第一章 初识VUE
1.vue中的常用指令
-
v-bind: 该指令绑定过的属性 可以在属性中编写js语句
v-bind: 简写 :
-
v-on: 该指令用于绑定事件,事件会执行methods对象里面定义的方法。 监听事件,添加一个事件监听器
v-on: 简写 @ tips: 当事件代码比较简单时,可以直接写在标签里。 例:
<button @click="test = !test">按钮</button>
当事件代码比较复杂时,最好写到方法中,然后调用。
-
v-model 该指令是对 v-bind: 和 v-on: 的封装。使用该指令可以实现数据的双向绑定。
2.vue响应式原理
-
知识铺垫。
-
在js中给对象(object)添加属性(property)的几种常用方式
(1) 对象名.属性
object.property
(2)对象名['属性']
object['property']
(3)Object.defineProperty() 定义属性
这种方法可以给对象属性添加更多的设置,例如能否删除,能否修改,是否可被枚举。该方法可以更详细的限制对象
Object.defineProperty(object,'property',{ value:'value', //表示该属性允许删除,默认不可以被删除 configurable:true,//可删除 writable:true,//可重写 get(){ //当访问该属性(property)时,会调用此函数 }, set(param){ //当属性的值被修改时,会调用此函数 //该方法接收一个参数(param) —— 即属性的新值 } }) //删除对象上的某个属性 delete object.property
-
-
响应原理
-
用代理对象操作原始对象。
-
操作原始对象,数据无法被监听。通过 代理对象 来监听数据。
//原始对象 let obj = { prop:'value' } //代理对象 let _obj = {}; //通过 代理对象 去监听数据的变化 并且把变化传递给 原始数据 Object.defineProperty(_obj,'prop',{ get(){ //通过 代理对象 访问 原始对象 return obj.prop; }, set(param){ //通过 代理对象 修改 原始对象 obj.prop = param; //当数据变化时,调用 渲染函数 重新渲染页面。 renderDOM(); } });
-
-
3.条件渲染
-
v-if
v-if 指令用于条件渲染,表达式返回true,渲染;表达式返回false,则不会生成对应的元素。
-
v-show
v-show 指令通过样式display 来显示隐藏元素。表达式true,显示;表达式false,隐藏。
-
v-if v-else-if v-else
v-if 、v-else-if 、v-else可以配合使用,中间不能有断层。
-
v-if & v-show如何选择?
如果只需要判断一次确定显示还是隐藏,使用 v-if。
如果需要反复切换显示和隐藏,使用v-show。
v-if 用于判断是否渲染,如果反复的渲染,会造成资源浪费。
v-show则只是在切换样式。
4.列表渲染
-
v-for
v-for 指令,用于遍历数组。
通常需要绑定key属性,并且key属性是唯一值(unique value),不推荐绑定 index
<!-- item 数组的每一项 index 数组下标 --> <div v-for="(item,index) in array" :key="unique-id"> {{item}} </div>
5.Tips
-
v-if 不能和 v-for 同时使用。
-
通常情况下,我们会在列表的最外层,再包一层元素,用以控制显示与隐藏。这样做的问题是最外层的元素,往往是没有意义的。此时我们可以通过Vue的内置组件template来包裹,该组件在渲染时不会显示。
例:
<template v-if="true"> <div v-for="(item,index) in array" :key="unique-id"> {{item}} </div> </template>
-
第二章 VUE的生命周期
1.生命周期
(1)知识铺垫
-
钩子(HOOK)函数。 钩子函数是在事件触发的时候,在系统层级捕获到了他,然后做一些操作。
用以处理系统消息的程序。就是在某个阶段给你做某些处理的机会。
-
(1)是个函数,在系统消息触发时被系统调用
(2)不是用户自己触发的
-
钩子函数的名称是确定的,当系统消息触发时,自动调用。
(2)vue生命周期及其钩子函数
-
new Vue() 创建实例
-
Init Events & Lifecycle 初始化 事件&生命周期
-
beforeCreate()
(1) 数据初始化之前。此阶段正在对data中的数据做代理。(此时只能操作实例的原型 __ proto __ )
-
-
Init injections & reactivity 初始化 注入&校验
-
created()
(1) 数据初始化完成。此阶段已完成对data数据代理,此时可以操作数据。(例如发送ajax请求,获取需要的数据)
-
-
Has 'el' option? 有 'el' 选项吗?
NO
when vm.$mount(el) is called 当指定挂载容器后继续,否则无限等待。
YES
继续 jump to next step.
-
Has 'template' option? 有 ”模板“ 吗?
YES
Compile template into render function 编译模板 放入 渲染函数
NO
Compile el's outerHTML as template 将容器编译为模板 放入 渲染函数 (outerHTML包含innerHTML 及其 标签本身)
分析: 渲染函数在内存中生成虚拟DOM(伪DOM)
-
beforeMount()
(1) 页面挂载之前。虚拟DOM尚未转成真实DOM。
-
-
Create vm.$el and replace 'el' with it 创建vm. $el并替换 'el'
-
mounted() *****
(1) 页面挂载完成(真实DOM替换了虚拟DOM,并插入页面。)
,此时可以操作DOM。
分析: 此时内存中虚拟DOM ”应该“尚未销毁。
-
-
Mounted
when data changes 当数据变化时
-
beforeUpdate()
(1) 数据更新后,页面重新挂载之前。
Virtual DOM re-render and patch 虚拟DOM重新渲染并更新。
分析: 新的虚拟DOM与内存中”旧虚拟DOM“比较,最终完成页面更新。Model ==> View 更新完成
-
updated()
(1) 数据更新后,页面重新挂载完成。
新模板渲染完成,
when vm.$destroy() is called
$destroy() 方法,用于销毁当前实例。销毁后,当前实例对象就没有办法继续操作页面。
注意:是不在具备渲染页面的能力,实例对象身上的数据和方法仍然存在。
分析: 此时销毁的是内存中的伪DOM。因为没有了比较的对象,所有页面无法再重新渲染更新。
-
beforeDestroy()
(1) 实例销毁之前。一般在此阶段做一些首尾工作。例如:关闭定时器、清空数据、取消订阅消息、解绑自定义事件etc..
-
-
Teardown watchers,child components and event listeners 卸载 监视器 、子组件 和 事件监听器
-
destroy()
实例销毁完成。
-
2.生命周期图
第三章 VUE基础
1. 绑定样式
-
class
铺垫
<style> .color{ color:red; } .back{ background-color:blue; } .d-f{ display:flex; } </style>
new Vue({ el:'#app', data(){ return { controlBack:true, controlColor:false, controlDF:true, /**写法一之2的obj**/ obj:{ back:true, color:false, 'd-f':true, }, /***************/ bgcolor:'back', fontcolor:'color', /***************/ bg:'red', fc:'blue' } } })
-
写法一
:class 指定一个对象,对象的属性名是样式名 (style name)。属性值返回boolean值
<div :class="{back:controlBack,color:controlColor}"> HELLO </div> <!--或者。对象写在data中,(见铺垫),绑定到class上--> <div :class="obj"> HELLO </div>
或者 使用计算属性 返回对象的计算属性(据官方文档,这种方法比较常用)
computed:{ obj(){ return { //可以写很多条件 back:this.controlBack /**&& 条件etc.**/, color:this.controlColor, 'd-f':this.controlDF, } } }
<div :class="obj"> HELLO </div>
-
写法二
:class 指定一个数组。数组的每一项,在data中定义时,其值是指定的样式名称(style name)。
<div :class="[bgcolor,fontcolor]"> HELLO </div>
-
写法三
:class 简单表达式法 。
<div :class="controlBack ? 'back' : '' "> HELLO </div> <!--绑定多个表达式,可以指定一个含有多个表达式的 数组 --> <div :class="[controlBack ? 'back' : '' , controlColor ? 'color' : '']"> HELLO </div>
-
-
Tips: 在组件中使用时。
Vue.component('dl-tagname'/**组件标签名字**/,{ template:` <div class="box"> <div>示例</div> </div> ` })
当在自定义组件上使用 class 属性时,这些样式将会被添加到 组件的根元素上,根元素上已有的class不会被覆盖。
<dl-tagname class="back color"></dl-tagname> <!--HTML将被渲染成:--> <div class="box back color"> <div>示例</div> </div> <!--带数据绑定的class亦。--> <dl-tagname :class="{back:controlBack,color:controlColor}"></dl-tagname> <!--HTML将被渲染成:--> <div class="box back "> <div>示例</div> </div>
-
style
-
绑定内联样式
<div :style="{backgroundColor:bg,color:fc}"> HELLO </div> <div :style="{'background-color':bg}"> HELLO </div>
-
etc...
-
2. 计算属性 computed
-
优雅进化之路。(?)
-
(1)模板中的表达式过于复杂时,会让模板过重难以维护。
{{z=x+y}}
-
(2)通过定义一个方法,返回结果。
{{calc()}}
methods:{ calc(){ return this.z=this.x+this.y; } }
-
(3) 计算属性:计算属性里面,定义的是方法,而这些方法在容器中,是当作属性来使用的。
注意: 在定义计算属性时,不要和methods选项中的方法同名。
-
计算属性缓存 VS 方法
计算属性是基于它们的响应式依赖进行缓存的 (官方文档)。 响应式依赖即依赖的数据发生改变才会重新返回新结果。
只要计算属性中的属性(变量)值(依赖的数据)没有改变,多次访问则会立即返回之前的计算结果(即缓存的结果)。当依赖的数据改变时才会重新求值。
而方法,每当调用,总会重复执行函数。(当不需要缓存时,使用方法)
{{calc}}<input type="text" v-model="calc">
通常情况下,计算属性只负责返回结果,不需要修改内容,所以大多使用简写形式定义计算属性。
computed:{ //简写形式: /* calc(){ //简写形式,默认是get方法,当在页面中修改返回值时,会报错,提示没有setter。 return this.z=this.x+this.y; } */ //完整形式: calc:{ get(){ //当调用时,get方法会先执行一次(,即先计算一次并返回结果,结果存储在缓存中,当重复访问时,如果依赖的数据没有改变,立即返回缓存的结果)。 //当get中使用的属性(变量)的值(即依赖的数据)发生变化时,执行一次get方法 return this.z=this.x+this.y; }, set(value){ //当get中的**返回值**在页面中发生改变,传回新值给value,进行其他操作 this.y = value-this.x; } } }
-
-
3.侦听器 watch
-
与计算属性 computed 的差异:计算属性只对结果负责,而侦听器可以做更多的事情。(例如:当数据变化时,重新请求后台,获取最新数据)
-
-
当数据发生变化而且需要执行某些操作时,使用watch。
watch:{ //侦听谁,以谁为名 //被侦听的属性含有两个参数,分别是新值和旧值。 x(newValue,oldValue){ console.log(newValue,oldValue) }, y(){ } }
-
深度侦听
data:{ return { params:{ a:'a', b:'b', c:{ d:'d', }, } } } watch:{ //如果需要深度侦听某个属性,就要采用完整的形式,定义侦听器 params:{ //开启深度侦听 deep:true, //开启立即侦听 immediate:true, //handler 方法侦听数据变化。 handler(newValue,oldValue){ //引用数据类型,对象没有改变,只是对象的属性发生了改变(此时oldValue并无意义) } } }
-
4.过滤器 filters
-
定义过滤器。(当全局过滤器和局部过滤器重名时,会采用局部过滤器。)
局部过滤器:实例或组件的选项中定义局部过滤器。
//... filters:{ //... filterA(value){ //... } } //...
全局过滤器:Vue实例之前定义全局过滤器。
##所有的实例都会拥有该过滤器。 Vue.filter('filterA',function(value){ //... }); new Vue({ //... }); new Vue({ //... });
-
过滤器中定义的方法只能在 插值表达式和v-bind: 表达式中使用。
通过 | 管道符 指示。将管道符左边的值,作为参数传递给管道符右边的方法,并返回新的结果。过滤器可以串联使用,将前一个过滤的结果,继续传给下一个过滤器继续过滤。
{{PI | PI5 | PI2}}<input :value="PI | PI5">
data:{ return { PI:3.1415926, } }, filters:{ PI5(value){ return value.toFixed(5); }, PI2(value){ return value.toFixed(2); } }
5. v-cloak 优化模板显示问题
-
解决页面挂载之前会显示页面模板的问题。
只需要在容器中添加一个v-cloak 属性,然后在样式中定义该属性选择器的样式为none。
注意: 当vue将真实的DOM挂载到容器中时,就会去掉 v-cloak 属性
<style> /* 属性选择器*/ [v-cloak]:{ display:none; /*使用visibility:hidden亦可*/ } </style> <div id="app" v-cloak> ... </div>
6. v-model 表单输入绑定
-
基础用法
data:{ return { txt:'txt', boo:true, arr:[], pick:'', op:'1', ops:[], id:1337 } }
-
文本
<input v-model="txt"/>
-
多行文本
不能用插值语句写在标签中,只能使用v-model写在属性中
<textarea cols="30" rows="5" v-model="txt"></textarea>
-
复选框
单个复选框,绑定布尔值(boolean)
<input type="checkbox" v-model="boo">
多个复选框,绑定同一个数组
<input type="checkbox" v-model="arr"> <input type="checkbox" v-model="arr"> <input type="checkbox" v-model="arr">
-
单选框
一组单选框,绑定同一份数据。
<input type="radio" value="1" v-model="pick"> <input type="radio" value="2" v-model="pick">
-
下拉框
单选时,绑定的是option中的value值
<select v-model="op"> <!--当option未设置value时,value默认是文本值--> <option value="1">op1</option> <option value="2">op2</option> <option value="3">op3</option> </select>
多选时,绑定一个数组,
<!--multiple 属性为多选--> <select multiple v-model="ops"> <!--当option未设置value时,value默认是文本值--> <option value="1">op1</option> <option value="2">op2</option> <option value="3">op3</option> </select>
-
-
值绑定
-
修饰符
-
.lazy
在文本框失去光标后,再更新数据。
<input type="text" v-model.lazy="id"> <!--配合change使用类似于onblur,失去光标后更新数据触发change绑定的事件--> <input type="text" v-model.lazy="id" @change="">
-
.number
输入的数字文本(String)转为Number类型。
<input type="text" v-model.number="id">
-
.trim
去除输入的文本两端空格。
<input type="text" v-model.trim="id">
-
修饰符可以串联使用 例如:
<input type="text" v-model.trim.number.lazy="id">
-
-
在组件中使用v-model
此处省略,后续组件中有涉及。
第五章 事件处理
1. 监听事件
-
简单操作,可以将处理代码放到模板中
new Vue({ //... data(){ return { id:1337, name:'example', } } });
{{id}} {{name}} <button @click="id=1338;name='for example'">修改</button>
-
复杂操作,定义事件方法并调用
方法调用不传参时,默认把事件对象(e)作为第一个参数传入,
//... methods:{ changeExample(/*e*/){ this.id = 1338; this.name = 'for example'; } } //...
<!--...--> <button @click="changeExample">修改</button>
-
复杂操作,通过事件方法传递参数
如果传参了,默认的事件对象就不传了,如果还想使用事件对象,需要在调用时传递事件对象 $event
//... methods:{ //... changeExampleWithParams(/*e,*/id,name){ this.id = id; this.name = name; } }
<!--...--> <button @click="changeExampleWithParams(/*$event,*/1339,'for for example')">修改</button>
-
键盘事件修饰符
按下指定的按键后触发绑定的事件(v-on: || @)
-
.enter
回车 -
.tab
制表 -
.delete
(捕获“删除”和“退格”键) -
.esc
退出 -
.space
空格 -
.up
上 -
.down
下 -
.left
左 -
.right
右
<!--...--> <!--亦可以使用 按键码 ,不推荐--> <!--<intput type="text" :value="name" @keyup.13="changeName">--> <intput type="text" :value="name" @keyup.enter="changeName">
//... methods:{ //... changeName(e){ // 获取当前元素值 this.name = e.target.value } } //...
-
-
-
.stop
----->event.stopPropagation() 阻止冒泡 -
.prevent
----->event.preventDefault() 阻止默认事件 -
.capture
-
.self
子元素不再继承事件 -
.once
只触发一次 -
.passive
滚动实时触发
<!--...--> <a href="https://google.com" @click.prevent="changeId">阻止默认事件</a> <div style="width:200px;height:200px;background-color:red" @click="redOne"> <div style="width:100px;height:100px;background-color:green" @click.stop="greenOne"> 阻止事件冒泡 </div> </div>
//... methods:{ //... changeId(/*e*/){ //e.preventDefault(); //阻止默认方法 this.id = 1340; }, redOne(){ alert('I am the red one'); }, greenOne(/*e*/){ //e.stopPropagation();//组件事件冒泡 alert('I am the green one'); } } //..
-
2.响应式原理-----非响应式情况
(Vue不支持IE8以及更低版本浏览器)
-
对象 (Object)
添加或移除属性(property)时不会触发响应式
通过Vue.set(object,propertyName,item)或者 this.$set(object,propertyName,item) 设置响应式
//... data(){ return { // obj:{ id:1337, name:'example', } } }, methods:{ // changeObj(){ this.obj = { id:1339, name:'for example', } }, // addProperty(){ //this.obj.prop = 'A Property';//直接添加对象属性无法实现动态响应 //给对象新添加的属性添加响应式 Vue.set(this.obj,'prop','A Property'); //或者 this.$set(this.obj,'prop','A Property'); }, deleteProperty(){ //删除指定对象的属性,并去除该属性的响应式 Vue.delete(this.obj,'name'); //或者 this.$delete(this.obj,'name'); } } //...
{{obj}} <!-- --> <button @click="obj.id = 1338">修改</button> <!-- --> <button @click="changeObj">修改obj对象</button> <!-- --> <button @click="addProperty">给obj添加属性</button> <!-- --> <button @click="deleteProperty">删除obj的属性</button>
-
数组 (Array)
根据索引(index)修改元素,修改数组长度 不会触发响应式
通过Vue.set(arr,index,item) 设置响应式
数组的响应式方法:push、pop、unshift、shift、reverse、splice、sort
//... data(){ return { arr:[233,2333,23333], } }, methods:{ pushOne(){ //响应式 //this.arr.push(233333); //非响应式 //1.使用索引直接设置数组项时 this.arr[3] = 233333; }, popOne(){ //响应式 //this.arr.pop(); //非响应式 this.arr.length = 2; } } //...
{{arr}} <button @click="pushOne">添加一个元素</button> <button @click="popOne">删除最后一项</button>
第七章 自定义组件
1.自定义组件
//...
//注册全局组件
Vue.component('dl-box',{
template:`<div>
<div>这是一个自定义组件<div>
</div>`,
})
new Vue({
//...
data(){
return {
example:'example'
}
},
components:{
//注册局部组件
//定义一个dl-box组件
//组件中可以写除 el 选项外其他选项,为避免数据污染,组件中的data选项必须是一个返回值为对象的方法
'dl-box':{
//template选项设置组件的内容,注意:template中的模板 只能包含一个 根标签
template:`<div>
<div>这是一个自定义组件{{example}}<div>
</div>`,
data(){
return {
}
},
//props选项,用于接收外部传递给组件的数据
//props简写形式是一个字符串数组,里面定义接收的属性名称,props接收到的数据可以直接在模板中使用
//自定义属性
props:['propertyName']
//...
},
}
//...
})
<dl-box propertyName="example"></dl-box>
2. 组件的传参
-
参数的传递、修改、回传
//... Vue.component('dl-example',{ template:`<div> <h1>{{backupData}}</h1> <button @click="backupData='for for example'"></button> </div>`, //props的完整形式,传递一个对象,对象的属性,对应一个属性名称。 //特别注意,props中的数据是只读的,不能修改 props:{ propertyName:{ //指定属性类型 type:String,// //该属性不能为空 //required:true, //属性的默认值 //default:'example' } }, data(){ return { //将props传递的数据,在组件的data中备份,修改data中的数据 backupData:this.propertyName } }, //监听数据变化 watch(){ //当bacupkData 发生变化 backupData(){ //通过$emit触发自定义事件,事件名称 xxx 。自定义事件调用 xxxFn 方法 this.$emit('xxx'/*自定义事件*/,this.backupData/*事件对象*/) } } }) //... new Vue({ //... data(){ example:'for example' }, methods:{ xxxFn(eventObject/*即组件中的backupData值*/){ //重新赋值,即完成了 参数的回传 this.example = eventObject } } })
<dl-example :propertyName="example" @xxx="xxxFn"></dl-example> {{example}}
实例中的数据传递路径: 组件自定义属性>>>>>组件备份属性值>>>>>监听备份数据>>>>>作为自定义事件对象回传>>>>>原数据重新赋值 等于数据回传。
3.组件插槽
-
在组件的模板中,使用slot标签定义插槽
Vue.component('dl-example',{ template:`<div> <slot></slot> </div>`, //... })
自定义组件标签之间的所有html内容会在组件中slot标签的位置显示
<dl-example> <img src="" alt=""> </dl-example>
4.组件的生命周期 与vue实例生命周期的执行顺序
-
Vue实例的数据准备完成后,才会去准备组件的数据
-
Vue实例从开始渲染,到渲染完成,中间要先渲染完组件。
-
Vue实例的生命周期
-
beforeCreate
-
created
-
beforeMount
-
组件的生命周期
-
-
mounted
-
5.$parent 、$children
-
$parent 属性,返回的是当前组件的父级组件实例
可以反复 .$parent 例如
this.$parent.$parent
-
$children 属性 ,返回的是 当前组件的所有子组件实例 返回值是一个数组,根据数组的下标可以获取指定的组件
注意:根据下标获取指定的组件信息是不可靠的,因为位置可能会发生变化。
6.$refs 、$root
-
$refs属性
在组件标签上添加一个ref属性,通过不同的属性值,给组件定义唯一的身份
<dl-example ref="exampleRef"></dl-example>
//$refs 用于获取所有带ref属性的组件,返回值是一个对象。对象中保存的是,所有带ref属性的组件
this.$refs.exampleRef
-
$root属性
$root 返回根组件
7.第三方UI组件库
-
Element UI
-
iView
8.深入v-model
-
v-bind: 绑定value属性 v-on: 绑定input事件 实现双向绑定
<input type="text" :value="name" @input="name=$event.target.value">
-
v-model 指令是 v-bind: 和 v-on: 的语法糖
当只需要给组件传递一个数据时,并且实现,数据的双向绑定时,可以使用v-model,
<input type="text" v-model="name">
-
自定义组件 实现 双向绑定
-
v-bind: v-on: $emit 回传数据
-
绑定的属性名改为 value 绑定的事件名改为input 此时可以使用v-model 简写
-
9 sync 修饰符
-
当给组件传递多份数据时,并且需要实现多份数据的双向绑定时,v-model就难以胜任了,
-
此时就可以使用 .sync 修饰符
注意事件名称如果是以 update: propertyName的方式 定义的
this.$emit('update:propertyName',param)
在调用组件时,就可以省略事件的调用步骤,在属性后面 .sync 修饰符 即可
<input type="text" :propertyName.sync="name" :propertyName2.sync="age">
10.具名插槽
-
slot标签,用于在组件内部定义插槽,可以通过,name属性,给插槽定义名称这种插槽叫做 具名插槽
如果没有给插槽定义名字,默认 叫default
Vue.component('dl-example',{ template:`<div> <slot name="header"></slot> <div> <slot name="content"></slot> </div> <slot name="footer"></slot> </div>`, //... })
v-slot: 简写 #
<template #header> </template> <template #content> </template> <template #footer> </template>
11.作用域插槽
-
作用域插槽必须是具名插槽。
所谓作用域插槽,其实就是在插槽身上绑定自己的属性
Vue.component('dl-example',{ template:`<div> <div v-for="(item,index) in list" :key="item.id"> {{item}} <slot name="header" :abc="item" :def="index" :ghi="list"></slot> </div> </div>`, //... })
使用 template标签的 v-slot指令,指定一个具体的插槽
如果该插槽身上有数据返回,可以通过赋值语句指定一个对象,对象名是自定义的,通常取名 scoped。scoped就表示作用域
<template #header="scoped"> {{scoped}} <button @click="scoped.abc.name='hhhhhh'">修改</button> <button @click="scoped.ghi.splice(scoped.def,1)">删除</button> </template>
12.mixin 混入
-
全局混入
Vue.mixin({}) 方法,用于给Vue全局混入成员,如:数据(data),方法(methods),生命周期函数etc.
混入之后所有的Vue实例都会拥有这些成员
例如混入 封装的请求方法
-
局部混入 (一般很少使用局部混入,意义不大,除非是从外部导入)
mixin:[{}]