库和框架
库
- 别人写好的内容由我们主动调用
- 常见库:jQuery Underscore Zepto Animate.css
框架
- 拥有完整的解决方案,我们写好内容交给框架调用
- 常见框架:vue angular react backbone
架构模式
MVC模式 单项数据绑定 backbone
- Model 模型
- View 视图
- Controller 控制器
MVVM模式 双向数据绑定 vue,angular
- Model 模型
- View 视图
- ViewModel 视图模型
vue
vue的优点
- 只关注视图层,不需要操作DOM
- 轻量级,压缩后只有不到20k
- 渐进式框架,根据需求选择
vue的兼容性
Vue.js 不支持 IE8 及其以下版本,因为 Vue.js 使用了 IE8 不能模拟的 ECMAScript 5 特性。
vue的安装
1 2
| # 最新稳定版 $ npm install vue
|
{ { } }
{ { } }取值表达式,通过{ { } }来进行取值,默认可以不写this,支持表达式、赋值运算、计算和三元表达式 。尽量少写逻辑运算(computed)
指令 v-开头的行内属性
v-model 实现双向数据绑定(看到表单元素加v-model),忽略掉value,checked,selected,将数据绑定的视图上,视图修改后会影响数据的变化。
v-html 把html字符串渲染成html标签
v-text 把数据渲染成文本,{ { } }是v-text简写写法
v-for 循环(数组,对象,字符串,数字)
v-on 事件 v-on:click->@click 绑定给DOM元素,函数需要定义在methods中,不能和data中的内容重名,this指向实例,不能使用箭头函数,事件源对象如果不写括号,可以自动传入,否则只能手动传入$event
v-if 操作DOM
v-else-if 操作DOM
v-else 操作DOM
v-show 样式上的显示和隐藏 频繁的控制显示和隐藏
v-once 数据只渲染一次,数据再变化是不会导致视图刷新
v-cloak 相应比较慢的时候,会看到{ { } },出现闪烁的效果,防止闪烁效果出现
v-bind 动态绑定属性
1 2 3 4 5 6 7 8 9 10 11 12
| <div id="app"> <div class="x" :class="{z:flag,y:true}">啦啦啦</div> <div class="x" :class="['y','z']">哈哈哈</div> <div class="x" :class="[class1,{z:true}]">呵呵呵</div> <div class="x" :class="{true:'y',false:'z'}[true]">不怎么用</div> <div v-for="(a,index) in 10" :class="{x:index%2===0}">{{a}}</div> <div style="font-size: 30px;" :style="{backgroundColor:'red',color:'pink'}">嘿嘿嘿</div> <div :style="[sty1,sty2,{fontSize:'30px'}]">略略略</div> </div>
|
1 2 3 4 5 6 7 8 9
| let vm = new Vue({ el:'#app', data:{ flag:true, class1:'y', sty1:{backgroundColor:'red'}, sty2:{color:'pink'} } });
|
data
保存变量
1
| <div id="app">{{val}}</div>
|
1 2 3 4 5 6
| let vm = new Vue({ el:'#app', data:{ val:'Hello world!' } });
|
methods
定义需要执行的使用的函数方法
1
| <div id="app">{{'123'| my}}</div>
|
1 2 3 4 5 6 7 8
| let vm = new Vue({ el:'#app', filters:{ my(data,param1,param2){ } } });
|
filters 过滤器
对数据的显示效果进行处理
1
| <div id="app">{{'123'| my}}</div>
|
1 2 3 4 5 6 7 8
| let vm = new Vue({ el:'#app', filters:{ my(data,param1,param2){ } } });
|
computed 计算“属性”,不是方法
计算属性的值,不需要在data中再次声明
1 2 3 4
| <div id="app"> 全选<input type="checkbox" v-model="checkAll"><br> <input type="checkbox" v-for="product in products" v-model="product.isSelected"> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let vm = new Vue({ el: '#app', data: { products: [{isSelected: true}, {isSelected: true}, {isSelected: true}] }, computed: { checkAll: { get() { return this.products.every(item => item.isSelected); }, set(val) { this.products.forEach(item => { item.isSelected = val; }); } } } });
|
watch 监测属性变化
监测属性变化,属性变化则执行对应方法
1 2 3 4
| <div id="app"> <input type="text" v-model="a"> {{msg}} </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let vm = new Vue({ el: '#app', data: { a: '', msg: '' }, watch: { a(newVal, oldVal) { setTimeout(() => { if (newVal.length < 3) { this.msg = '太少'; return; } if (newVal.length > 6) { this.msg = '太多'; return; } this.msg = ''; }, 1000); } } });
|
生命周期:
beforeCreate()
触发时机:初始化内部方法和生命周期方法后
created()
触发时机:注入数据,完成双向数据绑定后
一般作用:获取ajax,初始化操作
beforeMount()
触发时机:挂载实例和编译模板后,要保证有编译的元素才能执行
mounted()
触发时机:真实DOM渲染完成
一般作用:DOM操作
tips:真实DOM的渲染是异步操作,需要等待DOM渲染完成后来获取。如果数据变化后,想获取真实DOM中的内容,需要等待页面渲染完成后再去获取,所有的DOM操作,最好放在nextTick中执行
beforeUpdate()
触发时机:页面依赖的数据需要变化时
一般作用:不用,用watch代替
updated()
触发时机:页面依赖的数据变化,虚拟DOM重新渲染后
一般作用:不用,用watch代替
beforeDestroy()
触发时机:实例销毁前,销毁的是监听
一般作用:清除定时器,清除事件绑定
destroyed()
触发时机:实例销毁后
实例常用方法
- this.$mount 挂载实例的另一种方法
- this.$data 实例上的数据
- this.$watch 监控
- this.$el 当前el元素
- this.$set 后加的属性实现响应式变化
- this.$options 实例上的其他属性
- this.$refs ref放在DOM上用来获取DOM元素;放在组件上获取的是组件的实例,并不是组件的DOM元素
- this.$nextTick 将回调延迟到下次 DOM 更新循环之后执行
组件
概念
vue把一个自定义标签看作一个组件,vue可以赋予自定义标签一些意义
优点
- 提高开发效率
- 方便重复利用
- 便于协同开发
- 更容易被管理和维护
用途
- 页面级组件:一个页面是一个组件
- 基础组件:将可用的部分抽离出来
用法
全局组件:可以声明一次在任何地方使用,写插件的时候用全局属性多一点
1 2 3 4 5 6 7 8
| Vue.component('my-component', { template: '<div>Component {{msg}}</div>', data() { return { msg: 'Test' }; } });
|
局部组件:必须告诉这个组件属于哪个实例
局部组件创建步骤:
1. 创建组件
2. 注册组件
3. 引用组件
组件是相互独立的,不能直接跨作用域,实力也是一个组件,组件中拥有生命周期函数
子组件不能直接使用父组件的使用(组件之间数据交互)
组件理论上可以无限嵌套
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let component = { template: '<div>Component {{msg}}</div>', data() { return { msg: 'Test' } } };
let vm = new Vue({ el: '#app', data: { msg: 'Test' }, components: { component } });
|
命名
- 组件名不用大写,多个单词用-连接
- 只要组件名和定义名字相同是可以的(首字母可以大写)
- html采用短横线隔开命名法,js中采用驼峰命名法
嵌套组件
如果要在一个组件中使用另一个组件,先保证使用的组件是真实存在的,在需要引用这个组件的实例上通过components注册这个组件,组件需要在父级的模板中通过标签的形式引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let grandson = {template: '<div>Grandson</div>'}; let son = { template: '<div>Son<grandson></grandson></div>', components: { grandson } }; let parent = { template: '<div>Parent<son></son></div>', components: { son } };
let vm = new Vue({ el: '#app', data: {}, template:'<parent></parent>', components:{ parent } });
|
组件间数据传输——父传子(props)
给子组件上添加一个属性,绑定对应的父组件,然后给子组件中增加props属性,把在子组件上添加的属性写进去。
1 2 3 4 5
| <div id="app"> 父亲:{{money}} <child :m="money"></child> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| let vm = new Vue({ el: '#app', data: {money: 400}, components: { child: { props: { m: { type: [String, Boolean, Number, Function, Object, Array],
required: true, validator(val) { return val > 300; } } },
template: '<div>儿子:{{m}}</div>' } } });
|
组件间数据传输——子传父(emit)
父组件绑定好一些事件,子组件触发这个事件,将这个参数传进去,单项数据流,父组件数据刷新,子组件数据刷新
1 2 3 4 5
| <div id="app"> 父亲:{{money}} <child :m="money" @child-msg="things"></child> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let vm = new Vue({ el: '#app', data: { money: 200 }, methods: { things(val) { this.money = val; } }, components: { child: { props: ['m'], template: '<div>儿子:{{m}} <br/><button @click="getMoney">多来点</button></div>', methods: { getMoney() { this.$emit('child-msg', this.m * 2); } } } } });
|
语法糖:
1 2 3 4 5 6
| <div id="app"> 父亲:{{money}} <child :m.sync="money"></child> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| let vm = new Vue({ el: '#app', data: { money: 200 }, components: { child: { props: ['m'], template: '<div>儿子:{{m}} <br/><button @click="getMoney">多来点</button></div>', methods: { getMoney() { this.$emit('update:m', this.m * 2); } } } } });
|
组件间数据传输——兄弟组件(EventBus)
通过新建一个vue实例传递事件
1 2 3 4
| <div id="app"> <brother1></brother1> <brother2></brother2> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| let EventBus = new Vue();
let brother1 = { template: '<div>{{color}}<button @click="change">变绿</button></div>', data() { return { color: '绿色', old: '绿色' } }, created() { EventBus.$on('changeRed', val => { this.color = val; }); }, methods: { change() { EventBus.$emit('changeGreen', this.old); } } }; let brother2 = { template: '<div>{{color}}<button @click="change">变红</button></div>', data() { return { color: '红色', old: '红色' } }, created() { EventBus.$on('changeGreen', val => { this.color = val; }); }, methods: { change() { EventBus.$emit('changeRed', this.old); } } }; let vm = new Vue({ el: '#app', data: {}, components: { brother1, brother2 } });
|
插槽slot
插槽slot可以在组件中通过给DOM元素赋slot属性,在模板中使用slot标签配合name属性把slot标签替换为对应slot属性的DOM元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <div id="app"> <modal> <a href="http://www.ryanshang.com">Ryan</a> <p slot="content">亲,确认删除么?</p> <h1 slot="title" @click="fn">是否删除?</h1> <a href="http://www.ryanshang.com">Ryan</a> </modal> </div>
<template id="modal"> <div> <slot name="title">默认标题</slot> <slot name="content">默认内容</slot> <slot name="default">这是一个默认标题</slot> </div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let modal={ template:'#modal', };
let vm = new Vue({ el:'#app', data:{}, components:{ modal }, methods:{ fn(){alert(1);} } });
|
keep-alive
一般用作缓存,如果已缓存则不会再执行created、mounted钩子函数
子组件和父组件同时拥有mounted方法,先走子组件
1 2 3 4 5 6 7
| <div id="app"> <input type="radio" v-model="radio" value="home">home <input type="radio" v-model="radio" value="list">list <keep-alive> <component :is="radio"></component> </keep-alive> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| let home={ template:'<div>home</div>', mounted(){ alert('home'); }, beforeDestroy(){ alert('销毁'); } }; let list={ template:'<div>list</div>', mounted(){ alert('list'); }, beforeDestroy(){ alert('销毁'); } };
let vm = new Vue({ el:'#app', components:{ home,list }, data:{ radio:home }, mounted(){ alert('vm'); } });
|
router
访问不同的路径,返回不同的结果,常用于单页面应用(spa,single page application),单页面应用常用开发模式:
- hash模式:开发时使用,不会导致404,但是不支持SEO
- h5的history.pushStatus:上线使用
使用
1 2 3 4 5 6
| <div id="app"> <router-link to="/home" tag="button">home</router-link> <router-link to="/list" tag="button">list</router-link> <router-view></router-view> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| let home = { template: '<div>首页</div>' };
let list = { template: '<div>列表页</div>' };
let routes = [ {path: '/home', component: home}, {path: '/list', component: list} ];
let router = new VueRouter({ routes, linkActiveClass:'active' });
let vm = new Vue({ el: '#app', router });
|
多级路由
1 2 3 4 5 6 7 8 9 10 11 12
| <div id="app"> <router-link to="/home">首页</router-link> <router-link to="/detail">详情</router-link> <router-view></router-view> </div> <template id="detail"> <div> <router-link to="/detail/profile">个人信息</router-link> <router-link to="/detail/about">关于</router-link> <router-view></router-view> </div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| let home = { template: '<div>home</div>' };
let detail = { template: '#detail' };
let profile = { template: '<div>profile</div>' };
let about = { template: '<div>about</div>' };
let routes = [ { path: '/home', component: home }, { path: '/detail', component: detail, children: [ {path: 'profile', component: profile}, {path: 'about', component: about}, ] } ];
let router = new VueRouter({ routes });
let vm = new Vue({ el: '#app', data: {}, router });
|
路由参数
1 2 3 4 5 6 7
| <div id="app"> <router-link :to="{name:'pro',params:{c:1,a:2}}">商品1</router-link> <router-link to="/article/2/b">商品2</router-link> <router-link to="/article/3/c">商品3</router-link> <router-view></router-view> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| let article = { template: '<div>第 {{$route.params.c}} 篇文章</div>', watch: { $route() { alert('发送ajax请求'); } } };
let routes = [ { path: '/article/:c/:a', component: article, name: 'pro' } ];
let router = new VueRouter({ routes });
let vm = new Vue({ el: '#app', data: {}, router });
|