1 vue初识
1.1 学习vue需要改变关注点
-
jQuery和原生js的关注点:节点
-
Vue的关注点:数据
1.2 如何使用vue?
1.2.1 实例化 new Vue()
001 配置选项
-
el:关联Vue实例与页面元素
-
data:将data中属性添加到响应式系统中
-
methods:编写方法、函数,方法中的this指向实例
002 指令
- v-on:绑定事件
var data = { name: 'helloword' }
var vm = new Vue({
el: "#app",
data: data
});
1.2.2 在被接管的html标签中使用数据{{ }}
1.3 框架模式
1.3.1 复杂的软件必须有清晰合理的架构,更容易开发、维护和测试
1.3.2 MVVM
001 Model模型:数据处理
002 View视图:数据展示
003 ViewModel采用双向绑定
- View的变动自动反映在ViewModel,反之亦然
004 核心思想
- 关注Model的变化,让MVVM框架利用自己的机制去自动更新DOM,从而把开发者从操作DOM的繁琐中解脱
005 双向绑定model的原理:单向绑定+事件
-
Model -> View:
v-bind:value="val"
-
View -> Model:
v-on:input="val=$event.target.value"
-
<input type="text" v-model="scole.English">
2 配置参数
2.1 el 关联视图层 -- 类型为Selector | ELement
- Vue实例的挂载目标(实例中的所有属性|方法可直接在el中使用)挂载元素会被Vue生成的DOM替换
el:"#app"
2.2 data 关联数据层
- data下的属性会自动成为vm下的属性:Vue在实例化时,会遍历data下所有属性,把他们变成响应式属性。
data: {
title: '',
datalist: [{
id: Date.now(),
title: '年前月薪过万',
done: false,
selected: false
}]
}
2.3 methods 用于方法定义和事件处理函数
methods: {
addItem() {
let data = {
id: Date.now(),
title: this.title,
done: false,
selected: false
}
this.datalist.unshift(data);
this.title = "";
this.$refs.title.focus();
}
}
2.4 computed 写在该属性内的属性为存储器属性
computed: {
selectAll: {
get() {
return this.datalist.every(item => item.selected)
},
set(val) {
this.datalist.forEach(item => {
item.selected = val
})
}
}
}
2.5 template 模板
-
类型:String
-
如果不指定则以ele所在元素作为模板
2.6 render 渲染函数
-
类型:Function
-
template的代替方案
-
该渲染函数接收一个createElement(target, props, children)方法作为第一个参数用来创建VNode
2.7 watch 监听属性
- 监听的值被修改时会自动调用函数,当需要在数据变化时执行异步或者开销较大的操作时,这个方法是最有用的
3 实例属性
-
vm下的属性|方法
-
可以写在视图层的数据是vm实例的属性|方法
3.1 如何设置一个不可修改的属性?
-
属性特性:一种更深入控制属性的方式
-
设置属性特性:
Object.defineProperty(obj, key, descriptor)
3.1.1 值属性(有自己的值的属性)
-
configurable:可配置性 -- 属性特性的总开关
-
enumerable:可枚举性
-
writable:可写性
-
value:值
3.1.2 存储器属性(本身没有值,一般用于代理其他数据)
-
configurable:可配置性
-
enumberable:可枚举性
-
get:监听读取操作
-
set:监听写入操作
传统方式设置的属性,所有的属性特性默认为true;而通过
Object.defineProperty()
设置的属性特性默认为__false__
3.2 数据改变ui更新的原理:getter & setter
-
通过
Object.defineproperty()
把data下的属性变为存储器属性,并写入vue实例。 -
当这些属性值发生改变时,视图将会产生“响应”,及匹配更新为新的值。
001 从new Vue开始,首先通过get、set监听Data中的数据变化,同时创建Dep用来搜集使用该Data的Watcher;
002 编译模板,创建Watcher,并将Dep.target标识为当前Watcher;
003 编译模板时,如果使用到了Data中的数据,就会触发Data的get方法,同时调用Dep.addSub将Watcher搜集起来;
004 数据更新时,会触发Data的set方法,然后调用Dep.notify通知所有使用到该Data的Watcher去更新DOM。
3.3 如何设置响应式属性?
第一种:初始化时写入data属性
第二种:Vue.set(target, key, val) | 实例对象.$set()
- tip: target对象不能是vue实例,或者vue实例的根数据对象
第三种:数值变异方法
push() | unshift()
pop() | shift()
splice()
sort() | reverse()
4 指令(html的属性:v-*)
4.1 v-for 可以遍历数组|对象
- 格式
v-for="item in namelist"
* v-for="item of namelist"
4.2 v-bind(简写 : )
- 在属性中添加变量要使用指令v-bind,可以用在任何属性上,对style和class属性进行增强
4.3 v-on(简写 @ )
- 本质上使用了事件委托
001 event对象的获取:在事件触发时自动写入vm.$event
-
不传参时默认第一个参数为event对象;
-
传参必须手动传递$event;
002 阻止事件冒泡:v-on:click.stop=""
4.4 v-show(频繁显示隐藏)
- 通过display属性控制元素的显示隐藏
4.5 v-if | v-else-if | v-else(不频繁显示隐藏)
- 通过创建|移除的方式控制元素的显示隐藏
影响页面性能的几大因素
1 节点的频繁操作
- 判断一段js代码所花的时间console.time(name); | console.timeEnd(name);
2 事件绑定数量
5 虚拟DOM(Virtual DOM)一个结构类似于真实DOM节点的js对象
5.1 优化方法:优化节点操作 | 优化事件处理
5.2 虚拟DOM是怎样优化性能的?
001 背后有一套强大的算法:diff算法
002 只改变对象初始 | 结束状态不同部分
-
diff算法是进行新旧虚拟节点元素的对比并返回一个用来存储两个节点不同的地方的patchs对象,最后再用patchs记录消息去局部的更新视图层的DOM。
-
diff算法的本质就是判断两个虚拟节点的差异,并将差异更新到真实DOM中。
5.3 diff算法如何比较同级同类标签的不同?
001 复用原则 -- 默认
002 key:唯一且稳定
- 是否使用key取决于实际开发中是否有节点的增加、删除(结构变动)
// 虚拟DOM大概的样子
// 初始状态
{
type: 'div',
attrs: {},
children: [{
type: 'h1',
children: '暴富'
},{
type: 'ul',
children: [{
type: 'li',
children: '养生'
},{
type: 'li',
children: '早睡'
}]
}]
}
// 结束状态
{
type: 'div',
attrs: {},
children: [{
type: 'h1',
children: '暴富22'
},{
type: 'ul',
children: [{
type: 'li',
children: '养生22'
},{
type: 'li',
children: '早睡22'
}]
}]
}
6 数据绑定
6.1 单向数据绑定(Model -> View)
-
{{ }}
-
v-bind: attr
-
v-text
-
v-html
6.2 双向数据绑定
-
Model -> View: 单向绑定
-
View -> Model: 事件
-
v-model 原理: v:bind:value + v-on:input
bind: value = 'val';
v-on: input = '$val=$event.target.value';
例:Todolist
// 01 先完成简单的结构 + 初始数据(实例对象)
// 02 点击添加 View-> Model
// 03 点击完成按钮,切换完成状态
// 04 点击删除按钮,删除该数据
// 05 点击全选复选框,实现全选|全不选
// 06 用户体验优化
7 组件->模块开发-> 组件component-> 好维护、复用
7.1 如何组件化开发?
7.1.1 为什么要组件化开发:复用、维护
7.1.2 如何定义组件?
- 定义组件->相当于定义了一个html标签
001 全局组件:Vue.component(组件名, {})
-
配置参数:
- data: function() {} // 对象->引用类型->复用?->地址共享->不可复用
// 函数->方法->复用
- template:
存放组件内容
// 无el,由template关联页面,每个组件只能存放一个根元素
002 局部组件:components配置参数(定义在全局组件中,局部组件的标签名写在全局组件的template中)
-
component: {组件名: {}}
-
配置参数:template: ``
7.1.3 如何让template中字符串有高亮?
001 在body创建标签
002 template中的字符串存放在新创建的标签内
003 template: #组件名
004 如何解决body中不允许自定义标签?
- 利用特殊的is属性实现
7.2 怎样把vue实例中定义的数据渲染到组件中?【组件中是如何实现通讯的?】
- 组件通讯:谁的数据谁修改--子组件中不能直接修改父组件传来的数据
7.2.1 父传子:props
001 给子组件定义属性,并传递数据
002 在子组件中通过props配置参数接收数据
- 接收到的数据自动成为子组件的属性;如果子组件中不接收,定义的属性会自动成为子组件根节点的属性
<blog-post :msg="message"></blog-post>
<!--传入一个对象-->
<blog-post :author="{name: 'David', age: 18}"></blog-post>
Vue.component('blog-post', {
props: ['msg', 'author'],
template: '<h3>{{msg}}</h3>'
})
7.2.2 子传父:自定义事件 + $emit()
001 在子组件上自定义一个事件,把父组件的方法作为事件处理函数
-
v-on: 事件名=事件处理函数
-
子组件实例.$on(事件名,事件处理函数)
002 在子组件内部触发这个自定义事件
`this.$emit('eventName',参数)`
<mycom @show="handler"/>
this.$emit('show', 100)会触发父组件的事件处理函数handler,从而实现数据修改
7.2.3 多层级组件传递
方法一: 层次传递 -> 繁琐,不靠谱
方法二: 事件总线
001 接收方:自定义事件,绑定父组件事件处理函数:vm.$on('事件')
// 创建一个vue实例,用来进行多层级组件通讯
let Bus = new Vue();
// 生命周期函数
created() {
// 自定义事件
Bus.$On('complete', this.completeItem)
Bus.$on('remove', this.removeItem)
}
002 发送方:触发自定义事件:vm.$emit('事件',传参)
methods: {
completeItem(id) {
Bus.$emit('complete', id)
},
removeItem(idx) {
Bus.$emit('remove', idx)
}
}
```
* tip: 给组件绑定事件会不生效,通过.native修饰符绑定根元素,通过事件冒泡触发事件--native主要是给自定义组件添加原生事件
### 7.3 动态组件<component is="">【string配置参数| ComponentDefinition组件配置】
```js
components: {
// 多个局部组件
// home: {
// template: ``
// },
// list: {
// template: ``
// }
}
- 配置参数 $options 实例化时的配置参数
created() {
this.menu.forEach(item => {
this.$options.component[item.name + 'page'] = {
template: `<div class= "${item.name}">${item.text}</div>`
}
})
}
created() {
this.menu.forEach(item => {
this.$options.component[item.name + 'page'] = {
data() {
return { qty: 1}
},
template: `<div class= "${item.name}">
<h4> ${item.text} </h4>
<button @click="changQty">{{qty}}</button>
</div>`,
created() {
console.log(item.name + 'create')
},
destoryed() {
console.log(item.name + 'destory')
},
methods: {
changQty() {
this.qty++;
}
}
}
})
}
```
* 为什么每次切换后会重置为1:因为每次切换,之前的组件都被销毁了。
## 8 组件缓存
### 8.1 把切换出去的组件保留在内存中,可以保留它的状态或者避免重新渲染 可以添加一个<keep-alive>
### 8.2 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,主要用于保留组件状态或者避免重新渲染
### 8.3 选择性缓存
#### 001 include(String |Regexp) 缓存指定组件名
#### 002 exclude(String |Regexp) 不缓存指定组件名
```js
<keep-alive :include="/^l/">
<component :is="currentComponent"></component>
</keep-alive>
003 缓存多个:逗号|数组 --> 数组要配合v-bind + ''
9 生命周期(钩子函数)
9.1 生命周期的各个阶段
001 创建阶段 creating
- beforeCreate
- created
002 挂载阶段 mounting
- befoteMount
- mounted
003 更新阶段 updating
- beforeUpdate
- updated
004 销毁阶段 destorying
- beforeDestroy
- destroyed
9.2 每个阶段Vue做了什么?
9.3 在每个生命周期适合什么操作?
001 beforeCreate():loading事件
002 created(): 结束loading事件;处理响应式属性
003 beforeMount():发送ajax请求,拿回数据,配合路由钩子
004 mounted():节点操作(实现挂载到DOM)
005 beforeUpdate():数据有更新但未更新节点
006 Updated():更新节点完毕
007 beforeDestory()
008 destoryed():之后不会改变已生成的DOM,不再受vue控制
-
tip: vue.nextTick()
- 因为DOM至少会在当前tick里面的代码全部执行完毕再更新。所以不可能做到在修改数据后并且DOM更新后再执行,要保证在DOM更新以后再执行某一块代码,就必须把这块代码放在下一次事件循环里面,比如setTimeout(fn, 0),这样DOM更新后,就会立即执行这块代码。
10 内容通讯 -- 插槽slot--内置组件
10.1 组件外 ->组件内
001 默认插槽
002 具名插槽
-
模板上:给组件添加name属性 -------
-
组件上:v-slot:name值 --------
10.2 组件内 -> 组件外:作用域插槽
001 把需要传递的参数写入slot属性
002 v-slot="scope" (scope为写入slot的所有属性组成的对象)
<todo-button class="btn-success" idx="10" v-on:click.native="addItem">
<template v-slot:cn="mycn">添加{{mycn.username}}</template>
<template v-slot:en="myen">Add{{myen.age}}</template>
</todo-button>
Vue.component('todoButton',{
data(){
return {
username:'dingding',
age:32
}
},
template:`<button class="btn">
<slot name="cn" :username="username" idx="10"/>
<slot name="en" :age="age"/>
<slot/>
</button>`
})
11 过渡动画transition
11.1 前提条件(缺一不可)
001 状态改变 --> 通过样式 | js改变
002 使用 | 单元素 | 单组件
003 动画元素必须有进入状态、离开状态
11.2 如何让组件发生进入状态、离开状态?
001 条件渲染(v-if)
002 条件展示(v-show)
003 动态组件
004 组件根节点
11.3 标签有默认类名
-
v-enter
-
v-enter-active
-
v-enter-to
-
v-leave
-
v-leave-active
-
v-leave-to
11.4 name 过渡类名前缀(默认为v)
-
name="slide"
-
slide-enter
-
slide-enter-active
-
slide-enter-to
-
slide-leave
-
slide-leave-active
-
slide-leave-to
11.5 js过渡
- 通过内置事件实现过渡动画效果,可以利用第三方动画库实现动画效果(animation.css)
#组件名
<blog-post :msg="message"></blog-post>
<!--传入一个对象-->
<blog-post :author="{name: 'David', age: 18}"></blog-post>
Vue.component('blog-post', {
props: ['msg', 'author'],
template: '<h3>{{msg}}</h3>'
})
v-on: 事件名=事件处理函数
子组件实例.$on(事件名,事件处理函数)
`this.$emit('eventName',参数)`
<mycom @show="handler"/>
this.$emit('show', 100)会触发父组件的事件处理函数handler,从而实现数据修改
// 创建一个vue实例,用来进行多层级组件通讯
let Bus = new Vue();
// 生命周期函数
created() {
// 自定义事件
Bus.$On('complete', this.completeItem)
Bus.$on('remove', this.removeItem)
}
methods: {
completeItem(id) {
Bus.$emit('complete', id)
},
removeItem(idx) {
Bus.$emit('remove', idx)
}
}
```
* tip: 给组件绑定事件会不生效,通过.native修饰符绑定根元素,通过事件冒泡触发事件--native主要是给自定义组件添加原生事件
### 7.3 动态组件<component is="">【string配置参数| ComponentDefinition组件配置】
```js
components: {
// 多个局部组件
// home: {
// template: ``
// },
// list: {
// template: ``
// }
}
created() {
this.menu.forEach(item => {
this.$options.component[item.name + 'page'] = {
template: `<div class= "${item.name}">${item.text}</div>`
}
})
}
created() {
this.menu.forEach(item => {
this.$options.component[item.name + 'page'] = {
data() {
return { qty: 1}
},
template: `<div class= "${item.name}">
<h4> ${item.text} </h4>
<button @click="changQty">{{qty}}</button>
</div>`,
created() {
console.log(item.name + 'create')
},
destoryed() {
console.log(item.name + 'destory')
},
methods: {
changQty() {
this.qty++;
}
}
}
})
}
```
* 为什么每次切换后会重置为1:因为每次切换,之前的组件都被销毁了。
## 8 组件缓存
### 8.1 把切换出去的组件保留在内存中,可以保留它的状态或者避免重新渲染 可以添加一个<keep-alive>
### 8.2 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,主要用于保留组件状态或者避免重新渲染
### 8.3 选择性缓存
#### 001 include(String |Regexp) 缓存指定组件名
#### 002 exclude(String |Regexp) 不缓存指定组件名
```js
<keep-alive :include="/^l/">
<component :is="currentComponent"></component>
</keep-alive>
tip: vue.nextTick()
- 因为DOM至少会在当前tick里面的代码全部执行完毕再更新。所以不可能做到在修改数据后并且DOM更新后再执行,要保证在DOM更新以后再执行某一块代码,就必须把这块代码放在下一次事件循环里面,比如setTimeout(fn, 0),这样DOM更新后,就会立即执行这块代码。
模板上:给
组件上:v-slot:name值 --------
<todo-button class="btn-success" idx="10" v-on:click.native="addItem">
<template v-slot:cn="mycn">添加{{mycn.username}}</template>
<template v-slot:en="myen">Add{{myen.age}}</template>
</todo-button>
Vue.component('todoButton',{
data(){
return {
username:'dingding',
age:32
}
},
template:`<button class="btn">
<slot name="cn" :username="username" idx="10"/>
<slot name="en" :age="age"/>
<slot/>
</button>`
})
v-enter
v-enter-active
v-enter-to
v-leave
v-leave-active
v-leave-to
name="slide"
slide-enter
slide-enter-active
slide-enter-to
slide-leave
slide-leave-active
slide-leave-to