如何创建实例?
每一个实例都是通过 Vue 函数创建一个新的Vue实例开始的:
let vm = new Vue({//选项})
可以通过内存图来看待这个实例函数:
- 该函数把Vue的实例命名为
vm,vm对象封装了对视图的所有操作,包括数据读写、事件绑定、DOM更新。 vm的构造函数是Vue,按照ES6的说法,vm所属的类是Vue。optioins是new Vue的参数,一般称之为选项或构造选项。
options里面有什么?
官方文档列出了该选项有什么:文档
注意:
- 红色:必学、
- 黄色:高级需要费点脑子、
- 绿色:稍微说一下就记住、
- 紫色:比较特殊,重点讲、
- 蓝色:不常用,可学可不学、
- 灰色:不需要学习,用到再看文档
options的五类属性:
数据:
- data:内部数据,支持对象和函数,优先用函数。
- props:外部数据,可以是数组或对象,用于接收来自父组件的数据。
- methods:事件处理函数或者是普通函数。
- computed:Vue组件。所有
getter和setter的this上下文自动地绑定为Vue实例。 - watch:一个对象,键是需要观察的表达式,值是对应回调函数。值也是可以是方法名,或者包含选项的对象。
- propsData:创建实例时传递props。主要作用是方便测试。
DOM:
- el:挂载点,提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以用$mount代替。
- template:一个字符串模版作为Vue实例的标识使用。
- renderError:只在开发环境下工作。
- render:字符串模板的代替方案,允许你发挥JavaScript最大的编程能力。
生命周期钩子:
- created:实例创建完成之后被立即调用,但还没挂载到DOM节点。
- mounted:实例挂载到DOM节点后被调用。
- updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在之后会调用该钩子。
- destroyed:实例销毁后调用。
- activated:被keep-alive缓存的组件激活时调用。
- deactivated:被keep-alive缓存的组件停用时调用。
- beforeCreate:~之前调用
- beforeMount:同上
- beforeUpdate:同上
- beforeDestroy:同上
- errorCaptured
资源:
- components:包含Vue实例可用组件的哈希表。
- filters:包含Vue实例可用过滤器组件的哈希表。
- directives:指令。
组合:
- mixins:复制,减少data等操作。
- extends:也是复制,允许声明扩展另一个组件。
- provide/inject:需要两个一起使用
- provide:一个对象或者返回一个对象的函数。
- inject:一个字符串数组或者一个对象。
- parent
其它:
- 先不看
入门
el
想挂载到想挂载的节点,甚至可以替换挂载的节点。
如
public/index.html :
<div id="jeff">jeff</div>
src/main.js :
new Vue({el: `#jeff`,template: `<p>Vue成功</p>`})
最后在预览链接里可以看到,最后显示的是“Vue成功”。
或者也可以代替 el 属性,效果是一样的:
new Vue({template:`<p>Vue成功</p>`}).$mount('#jeff')
data
内部存放数据的,并且支持对象和函数:
new Vue({data: {n:0 //存放n属性为0的data对象。}})
函数的写法:
new Vue({data:function(){return {n: 0}}//上面可以缩写成:data(){return {n:0}}})
在组件(文件后缀为.vue)里必须使用函数的写法,当然也请尽量写函数的写法。
methods
可以写事件处理的函数:
new vue({data(){n:0,array: [1,2,3,4,5,6,7,8]},temoplate: `<div>{{n}}<button @click="add">+1</button><hr/>{{filter()}}</div>`,methods:{add(){this.n +=1},filter(){return this.array.filter(i = i % 2 === 0)}}})
components
使用vue组件
import Demo from './Demo.vue'const vm = new Vue({components:{jeff: Demo //把Demo组件命名为jeff//如果是下面组件名和命名是一致的,那么可以直接简写成:DemoDemo:Demo//简写:Demo},template:`<div><jeff/> 这里就使用了名为jeff的组件。</div>`})
上面使用了vue的components,还有一种方法是使用js的写法:
import Demo from './Demo.vue'vue.component('jeff',{data(){return {n:'jeff'}},template:`<div>{{n}}</div>`})const vm = new Vue({template:`<div><jeff/> 这里就使用了名为jeff的组件。</div>`})
组件:
顾名思义,就是可以和别的文件组合在一起的文件。
props
接受外部数据
src/Demo.vue:
<template><div class="red">{{message}}<button @click="fn">call fn</button>//可以接受props</div></template><script>export default{props:['message','fn'], //声明两个分别名为‘message’和'fn'的props。}</script><style>.red {border: 1px solid #42b983;}</style>
src/main.js
import Demo from './Demo.vue'new Vue({components:{Demo},//下面传了一个字符串“你好”template:`<div><Demo message="你好"></div>`})
如果我想传一个data里的数据n如下:
需要注意,如果message前面没有加上”:”,就会传字符串。
import Demo from './Demo.vue'new Vue({components:{Demo},data:{n:0},//下面传了一个字符串“n”,没错字符串"n"template:`<div><Demo message="n"></div>`,//传入一个数据的写法如下:template:`<div><Demo :message="n"></div>`,//但我又想冒号和字符串怎么办:template:`<div><Demo :message=" 'n' "></div>`,//当然也可以传一个函数,注意冒号:template:`<div><Demo :message="n"><Demo :fn="add"></div>`,methods:{add(){.....}}})
深入进阶
computed
计算属性,类似与公平过滤器,对于绑定到视图的数据进行处理,并监听变化而执行对应的方法。
栗子:
new Vue({data: {user: {email: "1234565@qq.com",nickname: "小明",phone: "13234567"}},// DRY don't repeat yourself// 不如用 computed 来计算 displayNametemplate: `<div>{{user.nickname || user.email || user.phone}}<div>{{user.nickname || user.email || user.phone}}</div>{{user.nickname || user.email || user.phone}}</div>`,}).$mount("#app");
上面的栗子里,template里的{{user.nickname || user.email || user.phone}} ,如果有 user.nickname 就展示,如果没有而 user.email 有,那么展示,同理 user.phone 。
假如,上面的栗子里,有100个这样的 {{user.nickname || user.email || user.phone}} ,然后需求变了,要求user.nickname 先展示,后展示user.phone !难道我要把改100次这样的代码吗?
因此计算属性就来了:
new Vue({data: {user: {email: "1234565@qq.com",nickname: "小明",phone: "13234567"}},computed: {displayName(){const user = this.user;return user.nickname || user.email || user.phone;}},// DRY don't repeat yourself// 不如用 computed 来计算 displayNametemplate: `<div>{{displayName}}<div>{{displayName}}<button @click="add">set</button></div></div>`}).$mount("#app");
添加100个 {{displayName}} 就可以了,当然可以配合v-for。
也可以使用函数来改变里面的计算属性(使用getter和setter):
new Vue({data: {user: {email: "1234567@qq.com",nickname: "小明",phone: "12345678"}},computed: {displayName: {get() {const user = this.user;return user.nickname || user.email || user.phone;},set(value) {console.log(value);this.user.nickname = value;}}},// DRY don't repeat yourself// 不如用 computed 来计算 displayNametemplate: `<div>{{displayName}}<div>{{displayName}}<button @click="add">set</button></div></div>`,methods: {add() {console.log("add");this.displayName = "圆圆";}}}).$mount("#app");
小结:计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。注意,“displayName”不能在组件的 props 和 data 定义,否则会报错。
watch 侦听
用途:watch是一个侦听的动作,用来观察和响应Vue数据的变化实例上的数据变动。当data发生变化的时候,就会发生一个回调,它有两个参数,一个newValue(修改后的data的数据),一个oldValue(原来的data数据)
简单例子:
new Vue({el: '#app',data:{cityName: 'shanghai'},watch: {cityName(){console.log('cityName改变了:'+ this.cityName)}},template:`<div><div id="app"><input type='text' v-model='cityName'/></div></div>`})
运行后发现,每改一次 cityName ,watch就执行一次。
注意:不应该使用箭头函数定义watcher函数。因为箭头函数没有this,它的this会继承它的父级函数,但它的父级函数是window,导致箭头函数的this指向window,而不是Vue实例。
可以写一个撤销功能的代码,如下:
new Vue({data: {n: 0,history: [],inUndoMode: false},watch: {n: function(newValue, oldValue) {console.log(this.inUndoMode);if (!this.inUndoMode) {this.history.push({ from: oldValue, to: newValue });}}},template: `<div>{{n}}<hr /><button @click="add1">+1</button><button @click="add2">+2</button><button @click="minus1">-1</button><button @click="minus2">-2</button><hr/><button @click="undo">撤销</button><hr/>{{history}}</div>`,methods: {add1() {this.n += 1;},add2() {this.n += 2;},minus1() {this.n -= 1;},minus2() {this.n -= 2;},undo() {const last = this.history.pop();this.inUndoMode = true;console.log("ha" + this.inUndoMode);const old = last.from;this.n = old; // watch n 的函数会异步调用this.$nextTick(() => {this.inUndoMode = false;});}}}).$mount("#app");
- 模拟
computed,但没有computed好用,建议能用computed的场景用computed。
注意:watch是异步操作!
怎么样才算变化?
watch 只要数据变化就会执行,那么,怎样才算变化?
例子:
new Vue({data: {n: 0,obj: {a: "a"}},template: `<div><button @click="n += 1">n+1</button>//属性的值改变<button @click="obj.a += 'hi'">obj.a + 'hi'</button>//对象的属性的值改变<button @click="obj = {a:'a'}">obj = 新对象</button>//对象的地址改变</div>`,watch: {n() {console.log("n 变了");},obj:{handler: function (val, oldVal) {console.log("obj 变了")},},"obj.a":{handler: function (val, oldVal) {console.log("obj.a 变了")},}}}).$mount("#app");
只要以下之一:
- 属性的值改变
- 对象的属性的值改变
- 对象的地址改变
就算是变化,简单类型看值,复杂类型看对象(地址),和 === 规则差不多。
handler方法和immediate属性
watch有个特点,最初绑定时是不会执行的,要等到 firstName 改变才执行监听计算,那么要是我想要一开始就可以执行监听,需要怎么做?
可以这样,如下代码:
watch: {firstName:{handler(newName, oldName){this.fulName = newName + ' ' this.lastName;},immediate: true}}
可以选择给 firstName 绑定一个 handler 方法,之前写的 watch 方法默认写这个,Vue自动去处理这个逻辑,最终编译出来其实就是这个 handler 方法。
而 immediate: true 代表如果在watch里面声明了 firstName 后,就会立刻先去执行里面的 handler 方法,相之在绑定时就不会执行!
watch 的 deep 有什么用?
按刚刚 obj.a 变了,那么 obj 不会变,但一个新的需求:要求 obj.a 变了,那么 obj 也要变!这时就可以使用 deep:true 了(默认:false),如下:
new Vue({data: {n: 0,obj: {a: "a"}},template: `<div><button @click="obj.a += 'hi'">obj.a + 'hi'</button>//对象的属性的值改变</div>`,watch: {"obj.a":{handler: function (val, oldVal) {console.log("obj.a 变了")},deep: true // 该属性设定在任何被侦听的对象的 property 改变时都要执行 handler 的回调,不论其被嵌套多深},}}).$mount("#app");
watch与computed总结
这两个区别是什么?
答:
computed是计算属性的意思,watch是监听的意思;- 其次computed调用时不需要加括号,可以当属性用,并根据依赖进行缓存,如果依赖没变就不需要重新计算;
- 而
watch有immdiate表示在第一次渲染的时候是否执行这个函数, 另一个是deep,如果监听的对象是否监听里面的属性的变化。
watch 和 computed 都是数据变化的时候去执行一个函数,面对不同的场景:
- 如果一个数据需要经过复杂的计算就用
computed - 如果一个数据需要被监听并且对数据做一些操作就用
watch
