Vue的使用总结(1)
1、新建Vue实例
每个 Vue 应用都是通过用 Vue
函数创建一个新的 Vue 实例开始的。
通过 Vue 函数新建一个 vue 应用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> {{message}} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) </script> </body> </html>
我们可以传入一个选项对象来新建一个 Vue 实例。上面的 vue 实例将会挂载到 #app 元素上,当 message 的值发生改变时,视图会重新渲染。
一个 Vue 应用由一个通过 new Vue
创建的根 Vue 实例,以及多个可选的嵌套的、可复用的组件组成。所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 (一些根实例特有的选项除外)。
1.1、template字符串模板
我们可以使用 template 选项来作为字符串模板,该模板将会替换挂载的元素,挂载元素的内容都将被忽略。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"></div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' }, template: ` <div id="newApp"> <h1>你好啊</h1> </div> ` }) </script> </body> </html>
渲染后的效果:
可以看到,#app 元素已经被 template 里面的模板完全替换掉。
1.2、将某个对象赋值给vue实例中的data属性时是浅拷贝
var obj= { a: 1 } var vm = new Vue({ data: obj }) obj.a = 2; console.log(vm.a) //2
2、vue上的字段:computed、method、watch
2.1、计算属性(computed)
计算属性作用一般是用来返回 data 属性里数据通过一系列计算过后返回的新数据,将会自动根据data属性里的数据发生改变而改变。computed里面的属性值重新进行计算的时机是方法体里面的任一响应式数据发生改变,无论该数据是否影响到该属性值。
var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { reversedMessage: function () { return this.message.split('').reverse().join('') } } })
计算属性有 getter 和 getter,但是默认只有 getter 。
当获取计算属性时,getter 被触发,返回计算值。
2.1.1、计算属性的 setter
计算属性有 getter 和 getter,但是默认只有 getter 。如果该计算属性也声明了 setter,当给计算属性赋值时(比如上面的 vm.reversedMessage = 'aaa'),计算属性值并不会被赋值为等号后面的值,而是执行里面的函数体。同时有getter和setter时,getter 的触发同默认情况(即没有setter时)一致。
要想修改计算属性的值,可以通过在 setter 方法里面响应式修改依赖的值(computed一般依赖于data里面的数据),由此触发计算属性的 getter ,计算属性的值得以更新。
注意,必须是响应式修改依赖的值。当修改数组某个索引的值时需要用 Vue.$set() 方法,当往对象里面添加属性时需要用 Vue.$set() 或者 Object.assign() 方法,删除时用 Vue.$delete() 方法。(请注意,使用Object.assign添加属性时,必须是往一个新对象里面添加属性,然后再将返回的新对象赋值给需要修改的对象)
data () { return { firstName: '张' } } computed: { name: { get () { return this.firstName + '三' }, set (newVal) { this.firstName = '李' //此处修改firstName,计算属性name的getter将会被触发,导致重新计算,name得以更新 } } }
不建议将 v-model 的值绑定到计算属性上。如果这样做了,你会发现输入框中的值改变但是计算属性的值并没有发生变化,而且此时你还要给计算属性添加 setter 属性才能编译成功,所以非常不建议将 v-model 绑定到计算属性上。
2.2、方法字段(methods)
方法字段里一般是定义一些dom可以调用的方法。
虽然方法也可以返回数据而达到跟计算属性一样的作用,不同的是计算属性基于它们的依赖而进行缓存,只在相关依赖发生改变时它们才会重新求值。这就意味着只要依赖的数据还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。而每当触发重新渲染时,调用方法将总会再次执行函数。
所以如果是为了返回跟data数据里依赖的数据的话最好还是用计算属性返回。
//index.html <p>Reversed message: "{{ reversedMessage() }}"</p> //index.js var vm = new Vue({ el: '#example', data: { message: 'Hello' }, methods: { show: function () { console.log('showtime') }, reversedMessage: function () { return this.message.split('').reverse().join('') } } })
2.3、侦听属性(watch)
侦听属性可以监听data属性里的数据变化,当数据改变时调用函数
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { console.log('firstname数据发生改变了') }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } })
watch 监听在 mounted 之前就已经绑定,即在 mounted 中触发数据改变,watch 也会执行。
watch 监听的数据在默认情况下,在 data 中给该数据绑定,即第一次绑定时,是不会触发 watch 的。
vue 中也可以监听 data 里的数据的属性的变化:
var vm = new Vue({ el: '#demo', data: { obj: { name: '张三' } }, watch: { //监听数据的属性要用引号 'obj.name'() { console.log(111) } } })
2.3.1、深度监听(deep)
在 vue 中监听数据,如果数据是一个对象,当对象的属性发生改变时,监听函数并不会被触发
<button @click="change">修改信息</button> var vm = new Vue({ el: '#demo', data: { obj: { name: '张三' } }, methods: { change() { this.obj.name = '李四' } } watch: { obj() { console.log(111) } } })
上面代码中改变 obj.name 后,监听函数并不会被触发。
此时就可以使用深度监听来解决这个问题,当然通过直接监听数据的属性也是可以的。
watch: { obj: { deep: true, handler() { console.log('深度监听修改'); } } }
此时修改 obj.name 监听函数就可以被触发。数组不需要使用 deep 属性也能监听到变化。
2.3.2、绑定时立即执行(immediate)
监听还有一个属性是 immediate。
在默认情况下,在 data 中给监听的数据绑定值时,即第一次绑定时是不执行函数的,但是有时候可能也需要在绑定时就执行函数,此时就可以使用 immediate 属性。通过该属性,在 data 中第一次给监听数据定义时,就会触发 watch 的执行。
比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将 immediate 设为 true。
watch: { firstName: { handler(newName, oldName) { console.log(newName) }, immediate: true } }
3、vue中模板语法
在vue中给绑定的特性赋值,应该写在双引号里面,双引号里面的是JavaScript表达式
//index.html <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }" ></div> //index.js data: { isActive: true, hasError: false }
4、vue中管理可复用元素
给每个元素定义一个唯一的 KEY ,这样 vue 将会把该元素视为独立唯一的,将不会简单地复用其他元素来渲染它。
参考:https://cn.vuejs.org/v2/guide/conditional.html 用key管理复用元素
5、Vue中响应式更改数组和对象
5.1、更改数组
以下方法更改数组可以触发视图更新:
不更改原始数组,返回新数组:filter()、concat()、slice(),可以用新数组取代原数组
直接更改数组某个索引的值并不会触发视图更新:
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的 vm.items.length = 2 // 不是响应性的
由于 Vue 只是监听了vue实例中的属性的根属性,比如上面 data 中的 items 数组,而并没有监听根属性内的数据的改变,所以对 items 进行修改会触发 items 属性的 set 方法,而对 items 属性中的某个值进行修改则不会触发 set 方法,因此也不会触发视图更新。
// JS代码模拟Vue中对数据的监听 let person = { get name() { return ['张三','李四'] }, set name(val) { console.log(111); } } person.name = [11,22] //会触发set方法 person.name[1] = 11 //不会触发set方法 person.name.splice(1,1) //不会触发set方法
Vue 中使用变异方法来修改根属性的值时可以触发视图更新,比如 pop、push等方法,值得注意的是Vue在底层中已经重写了这些方法
5.2、更改对象
由于 JavaScript 的限制,Vue 不能检测 data 属性里根级别的属性的添加或删除。比如:
var vm = new Vue({ data: { a: 1 } }) // `vm.a` 现在是响应式的 vm.b = 2 // `vm.b` 不是响应式的
5.2.1、响应式添加对象属性
对于已经创建的实例,Vue 不能动态添加 data 属性里的根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向根级别里的属性的对象属性添加响应式属性。 比如:
var vm = new Vue({ data: { userProfile: { name: 'Anika' } } }) Vue.set(vm.userProfile, 'age', 27) //可以添加一个新的 age 属性到嵌套的 userProfile 对象: this.$set(vm.userProfile, 'age', 27) //还可以使用实例的 $set 实例方法,它只是全局 Vue.set 的别名:
一次性往嵌套对象添加多个属性可以使用 Object.assign() 方法。注意,使用 Object.assign() 方法时必须新生成一个对象,然后往该对象里添加属性,再将该对象赋值给需要修改的对象。不能使用下面的第二、三种方法,修改对象属性时可以使用这两种方法。
// 应该使用的方法 vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' }) // 错误的使用方法,下面并不会响应式修改或者添加userProfile的属性 Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green' }) //这样也不行 vm.userProfile = Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green' })
5.2.2、响应式删除对象属性
响应式删除对象属性可以使用 Vue.delete(obj, 属性) 或者 this.$delete(obj, 属性) 方法。
6、Vue中的事件及修饰符
使用 v-on 为DOM元素绑定的事件相当于使用 onclick 方式,绑定的事件是在冒泡阶段触发的。
6.1、事件修饰符(stop、prevent、capture、self、once、passive)
6.2、修饰符串联使用
//在没有默认行为的元素里看不出区别 <div @click.prevent.self="show('aaa')">点击</div> //点击后将输出aaa //在有默认行为的元素里就能看出区别 //点击百度,先是判断是否自身行为,结果不是,所以不会触发click事件,也不会执行后面的阻止默认行为,所以 a 标签会跳转
//输出:3、1、然后跳转 <div @click="show(1)"> <a href="http://www.baidu.com" @click.self.prevent="show(2)"> <div @click="show(3)">百度</div> </a> </div>
//点击百度,先阻止了默认行为,所以 a 标签不会跳转。然后判断是否自身行为,结果不是,所以不会输出 2
//输出:3、1,不跳转 <div @click="show(1)"> <a href="http://www.baidu.com" @click.prevent.self="show(2)"> <div @click="show(3)">百度</div> </a> </div> show: function(str){ console.log(str); }
6.3、sync修饰符
官方文档:https://cn.vuejs.org/v2/guide/components-custom-events.html#.sync修饰符
sync 只是一个语法糖:
//父组件代码 <testComponent :title.sync="title"></testComponent> //等价于 <testComponent :title="title" @update:title="updateTitle"></testComponent> //子组件代码 <template> <div> {{dateMsg}} <button @click="updateTitle">修改title</button> </div> </template> props: { title: { type: Date } }, data() { return { dateMsg: this.title } }, methods: { updateTitle() { let newVal = new Date(); this.dateMsg = newVal; this.$emit('update:title',newVal); //当数据更新时传递给父组件,实现数据同步 } },
sync 修饰符的功能只是为了在父组件给子组件传 props 值时,可以实现父子组件的数据同步更新。
sync 修饰符只是一个语法糖,它的扩展有点像 v-model 在组件上使用时的情况。
7、Vue生命周期与Vue.nextTick()
7.1、vue的生命周期
Vue 生命周期有八个:beforeCreate、created、beforeMount、mounted、beforeUpdate、update、beforeDestroy、destroyed
- beforeCreate:创建实例之前。
- created:实例创建完成(执行new Vue(options)),可访问data、computed、watch、methods上的方法和数据,可进行数据请求。但是还未挂载到DOM结构上,不能进行dom操作。在生命周期中,要想访问 methods 或者 data 中的方法和数据,最早需要在 created 钩子中操作。
- beforeMount:在挂载开始之前被调用,此时只是完成了模板的编译,但是编译完成的 HTML 还没挂载到页面中,此时页面并没有内容。
- mounted:此时编译好的 HTML 已经挂载到了页面中,要想操作页面中的 DOM 节点,最早得在 mounted 钩子中操作。
- beforeupdate:响应式数据更新时调用,发生在给虚拟DOM打补丁之前,适合在更新之前访问现有的DOM,比如用于手动移除已添加的事件监听器。
- updated:虚拟 DOM 重新渲染和打补丁之后调用,此时组件DOM已经更新。
- beforeDestroy:实例销毁之前调用,this 仍能获取到实例,常用于销毁定时器、解绑全局事件、销毁插件对象等操作。
- destroyed: 实例销毁之后
参考:https://blog.csdn.net/mqingo/article/details/86031260
7.1.1、父子组件生命周期执行顺序
加载渲染过程:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子组件更新过程:
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程:
父beforeUpdate->父updated
销毁过程:
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
7.2、Vue.nextTick() 方法
Vue 更新 DOM 时是异步执行的。(在更新 DOM 时,vue 会尝试使用原生的 Promise.then、MutationObserver 和 setInmmediate,如果执行环境不支持这些方法,则会采用 setTimeout 方法代替)
Vue.nextTick() 接受一个回调函数作为参数,它的作用是将回调函数延迟到一次DOM更新之后的操作。如果没有提供回调函数参数且在支持Promise的环境中,nextTick将返回一个Promise。
注意:在组件中应该使用 this.$nextTick() 来代替,并且 this.$nextTick() 里面的回调函数的 this 自动绑定到该组件实例上。
使用 Vue.nextTick() 方法的场景:
1、在数据更新之后
vm.someData = 'new value'
,DOM
并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的 DOM 更新。如果你不用这个方法而是直接想要根据更新的 DOM
状态去做某些事情,就会出现问题,因此直接操作时 DOM 还未更新。为了在数据变化之后等待 Vue
完成更新 DOM
,可以在数据变化之后立即使用 Vue.nextTick(callback)
。这样回调函数在 DOM
更新完成后就会调用。
<div id="example">{{message}}</div> let vm = new Vue({ el: "#example", data: { message: '123' } }) vm.message = 'new message' console.log(vm.$el.textContent) //输出 'message' 直接获取时,此时DOM还未更新 Vue.nextTick(function (){ console.log(vm.$el.textContent) //输出 'new message' 使用该方法,会在 DOM 更新后才触发,获取到的是 DOM 更新后的数据 })
2、在 created() 生命周期进行 DOM 操作
Vue
生命周期的created()钩子函数进行的DOM
操作一定要放在Vue.nextTick()
的回调函数中。原因是什么呢,原因是在created()
钩子函数执行的时候DOM
其实并未进行任何渲染,而此时进行DOM
操作无异于徒劳,所以此处一定要将DOM
操作的js
代码放进Vue.nextTick()
的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM
挂载和渲染都已完成,此时在该钩子函数中进行任何DOM
操作都不会有问题 。参考:https://www.jianshu.com/p/46c9d777cab1
7.3、Vue.nextTick 和 setTimeout 的执行顺序
Vue 中 nextTick 比 setTimeout 先执行
可以参考:https://segmentfault.com/q/1010000018001188
8、v-cloak避免页面出现{{}}
某些情况下浏览器可能会在系统未解析完数据时就已经显示页面,此时就会显示出一些Vue的标签,比如可能会显示出双大括号 {{}},此时可以使用 v-cloak 来避免这种情况。
将 v-cloak 添加至元素中,通过 css 控制 v-cloak 属性的元素为隐藏,因为当 Vue 解析完后一些指令包括 v-cloak 不会出现在元素内,所以当 Vue 解析完后 css 将不会起作用,该元素也将显示出来,这样就避免了先显示然后再闪烁的情况。
参考:https://cn.vuejs.org/v2/api/#v-cloak