1. 最简单的vue
- el: dom节点
data: 数据
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <p> {{ message }}</p> <input type="text" v-model="message"> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) </script> </body> </html>
2. Vue 数据里的数组对象更新,但是视图不更新
2.1 问题
由于js的限制,Vue 不能检测以上数组的变动,以及对象的添加/删除,很多人会因为像上面这样操作,出现视图没有更新的问题。
2.2 解决办法
this.$set(你要改变的数组/对象,你要改变的位置/key,你要改成什么value)
this.$set(this.arr, 0, "aa"); // 改变数组 this.$set(this.obj, "c", "cc"); // 改变对象
数组原生方法触发视图更新:
Vue可以监测到数组变化的,数组原生方法:splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
2.3实例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <p>arr:{{arr}}</p> <p>obj:{{obj}}</p> <button @click="arrFn1">修改数组</button> <button @click="arrFn2">改变数组</button> <button @click="objFn1">增加和删除对象</button> <button @click="objFn2">修改对象</button> <button @click="this$set">this.$set</button> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data() { return { arr: [1, 2, 3], obj: { a: 1, b: 2 } } }, methods: { objFn1() { this.obj.c = 'c' //增加对象属性 delete this.obj.a; //删除对象属性 console.log(this.obj) //数据变化,视图没有变化 }, objFn2() { this.obj.a = 'aa' console.log(this.obj) //数据变化,视图变化 }, arrFn1() { this.arr[0] = '11'; //修改数组 this.arr.length = 1; //修改数组 console.log(this.arr) //数据变化,视图没有变化 }, arrFn2() { // splice()、 push()、pop()、shift()、unshift()、sort()、reverse() this.arr.push('3') console.log(this.arr) //数据变化,视图变化 }, this$set() { this.$set(this.arr, 0, "11"); // 改变数组 this.$set(this.obj, "c", "11"); // 改变对象 } } }) </script> </body> </html>
3. filter过滤器的作用
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <div>{{message | filterTest}}</div> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data() { return { message: 1 } }, filters: { filterTest(value) { // value在这里是message的值 // return value + '%'; return `${value}%`; } } }) </script> </body> </html>
4. v-for与v-if优先级
v-if尽量不要与v-for在同一节点使用,因为v-for 的优先级比 v-if 更高,如果它们处于同一节点的话,那么每一个循环都会运行一遍v-if
如果你想根据循环中的每一项的数据来判断是否渲染,那么你这样做是对的:<li v-for="todo in todos" v-if="todo.type===1"> {{ todo }} </li>
如果你想要根据某些条件跳过循环,而又跟将要渲染的每一项数据没有关系的话,你可以将v-if放在v-for的父节点:
// 数组是否有数据 跟每个元素没有关系 <ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
正确使用v-for与v-if优先级的关系,可以为你节省大量的性能。
5.vue生命周期
5.1 实例
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
message: "xuxiao is boy"
},
beforeCreate: function() {
console.group('beforeCreate 创建前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //undefined
console.log("%c%s", "color:red", "message: " + this.message)
},
created: function() {
console.group('created 创建完毕状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
beforeMount: function() {
console.group('beforeMount 挂载前状态===============》');
console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
mounted: function() {
console.group('mounted 挂载结束状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
beforeUpdate: function() {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
updated: function() {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
beforeDestroy: function() {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
destroyed: function() {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message)
}
})
</script>
</body>
</html>
5.2 create和mounted
- beforecreated:el 和 data 并未初始化
- created:完成了 data 数据的初始化,el没有
- beforeMount:完成了 el 和 data 初始化
- mounted :完成挂载
另外在标绿处,我们能发现el还是 {{message}},这里就是应用的 Virtual DOM(虚拟Dom)技术,先把坑占住了。到后面mounted挂载的时候再把值渲染进去。
5.3update 相关
5.4destroy
有关于销毁,暂时还不是很清楚。我们在console里执行下命令对 vue实例进行销毁。销毁完成后,我们再重新改变message的值,vue不再对此动作进行响应了。但是原先生成的dom元素还存在,可以这么理解,执行了destroy操作,后续就不再受vue控制了。
5.5 总结
- beforecreate : 举个栗子:可以在这加个loading事件(没有this)
- created :在这结束loading,还做一些初始化,实现函数自执行
- mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestroy: 你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容(没有this)
5.6 参考
https://segmentfault.com/a/1190000008010666
Vue的生命周期函数和beforeRouteEnter()/beforeRouteLeave()的函数6.vue 为什么采用Virtual DOM
- 创建真实DOM的代价高:真实的 DOM 节点 node 实现的属性很多,而 vnode 仅仅实现一些必要的属性,相比起来,创建一个 vnode 的成本比较低。
2.触发多次浏览器重绘及回流:使用 vnode ,相当于加了一个缓冲,让一次数据变动所带来的所有 node 变化,先在 vnode 中进行修改,然后 diff 之后对所有产生差异的节点集中一次对 DOM tree 进行修改,以减少浏览器的重绘及回流 - 虚拟dom由于本质是一个js对象,因此天生具备跨平台的能力,可以实现在不同平台的准确显示。
Virtual DOM 在性能上的收益并不是最主要的,更重要的是它使得 Vue 具备了现代框架应有的高级特性。
例子
{
tag: 'div', /*说明这是一个div标签*/
children: [ /*存放该标签的子节点*/
{
tag: 'a', /*说明这是一个a标签*/
text: 'click me' /*标签的内容*/
}
]
}
渲染后可以得到
<div>
<a>click me</a>
</div>
7. 组件data为什么必须是函数
- 因为不使用return包裹的数据会在项目的全局可见,会造成变量污染
- 使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件
当一个组件被定义, data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。
8. 组件style的scoped
为什么在组件中用js动态创建的dom,添加样式不生效
<template>
<div class="test"></div>
</template>
<script>
let a=document.querySelector('.test');
let newDom=document.createElement("div"); // 创建dom
newDom.setAttribute("class","testAdd" ); // 添加样式
a.appendChild(newDom); // 插入dom
</script>
<style scoped>
.test{
background:blue;
height:100px;
width:100px;
}
.testAdd{
background:red;
height:100px;
width:100px;
}
</style>
结果
// test生效 testAdd 不生效
<div data-v-1b971ada class="test"><div class="testAdd"></div></div>
.test[data-v-1b971ada]{ // 注意data-v-1b971ada
background:blue;
height:100px;
width:100px;
}
原因
当 <style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素。
它会为组件中所有的标签和class样式添加一个scoped标识,就像上面结果中的data-v-1b971ada。
所以原因就很清楚了:因为动态添加的dom没有scoped添加的标识,没有跟testAdd的样式匹配起来,导致样式失效。
解决办法:
去掉scoped即可
9. vue-router实现原理
9.1更新视图而不重新请求页面
SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是: 更新视图而不重新请求页面;vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据mode参数来决定采用哪一种方式。
9.2. Hash模式
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。 hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说hash 出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。hash 模式的原理是 onhashchange 事件(监测hash值变化),可以在 window 对象上监听这个事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
ul {
height: 1000px;
border-bottom: 1px solid #000;
}
</style>
</head>
<body>
<ul>
<li class="a">
<a href="#a">a</a>
</li>
<li class="b">
<a href="#b">b</a>
</li>
</ul>
<div>
<div id="a">a</div>
<div id="b">b</div>
</div>
</body>
</html>
9.3 History模式
由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入"mode: 'history'",这种模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈,在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求
const router = new VueRouter({
mode: 'history',
routes: [...]
})
当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,比较好看!
不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。
所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
export const routes = [
{path: "/", name: "homeLink", component:Home}
{path: "/register", name: "registerLink", component: Register},
{path: "/login", name: "loginLink", component: Login},
{path: "*", redirect: "/"}]
此处就设置如果URL输入错误或者是URL 匹配不到任何静态资源,就自动跳到到Home页面
10.vue自定义指令
10.1 全局注册指令
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
<title></title>
</head>
<body>
<div id="app" class="demo">
<!-- 全局注册 -->
<input type="text" placeholder="我是全局自定义指令" v-focus>
</div>
<script>
Vue.directive("focus", {
inserted: function(el) {
el.focus();
}
})
//new Vue要放在后面
new Vue({
el: "#app"
})
</script>
</body>
</html>
10.2 局部注册指令
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
<title></title>
</head>
<body>
<div id="app" class="demo">
<!-- 局部注册 -->
<input type="text" placeholder="我是局部自定义指令" v-focus2>
</div>
<script>
new Vue({
el: "#app",
directives: {
focus2: {
inserted: function(el) {
el.focus();
}
}
}
})
</script>
</body>
</html>
10.2 钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选)
- bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="container"> <!-- 准备实现需求: 在h1标签上面,加上一个按钮,当点击按钮时候,对count实现一次 自增操作,当count等于5的时候,在控制台输出‘it is a test’ --> <button @click="handleClick">clickMe</button> <h1 v-if="count < 6" v-change="count">it is a custom directive</h1> </div> <script> //directive new Vue({ el: '#container', data: { count: 0, color: '#ff0000' }, methods: { handleClick: function() { //按钮单击,count自增 this.count++; } }, directives: { change: { bind: function(el, bindings) { console.log('指令已经绑定到元素了'); console.log(el); console.log(bindings); //准备将传递来的参数 // 显示在调用该指令的元素的innerHTML el.innerHTML = bindings.value; }, update: function(el, bindings) { console.log('指令的数据有所变化'); console.log(el); console.log(bindings); el.innerHTML = bindings.value; if (bindings.value == 5) { console.log(' it is a test'); } }, unbind: function() { console.log('解除绑定了'); } } } }) </script> </body> </html>
10.3 参考
https://www.cnblogs.com/wangruifang/p/7765536.html
https://juejin.im/post/5a3933756fb9a045167d52b1
11.v-if 和 v-show 区别
v-if按照条件是否渲染,v-show是display的block或none
12.v-for 中 :key 到底有什么用
key的作用主要是为了高效的更新虚拟DOM。
12.1参考
https://www.zhihu.com/question/61064119
13. Vue.nextTick()
13.1什么是Vue.nextTick()
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
注意:重点是获取更新后的DOM 就是在开发过程中有个需求是需要在created阶段操作数据更新后的节点 这时候就需要用到Vue.nextTick()
$nextTick就是用来知道什么时候DOM更新完成
nexttick是怎么可以获取到更新后的dom的_深入浅出理解vm.$nextTick
13.2原因
在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以在created中一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted()钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题
13.3$refs获取dom节点属性
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
<title></title>
</head>
<body>
<div id="app">
<div ref="msg1">{{msg1}}</div>
<div>{{msg2}}</div>
<button @click="changeMsg">点击我</button>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
msg1: "11",
msg2: "22"
}
},
methods: {
changeMsg() {
this.msg1 = "33"
this.msg2 = this.$refs.msg1.textContent;
console.log('DOM并未渲染完成' + this.$refs.msg1.textContent) //11
this.$nextTick(function() {
console.log('DOM已经何渲染完成' + this.$refs.msg1.textContent) //33
})
}
}
})
</script>
</body>
</html>
13.4 document.getElementById获取节点
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
<title></title>
</head>
<body>
<div id="app">
<div ref="msg" id="msg" v-if="isShow">{{msg}}</div>
<button @click="changeMsg">点击我</button>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
msg: "aa",
isShow: false
}
},
methods: {
changeMsg() {
this.isShow = true
console.log(document.getElementById("msg")) //null
this.$nextTick(function() {
console.log(document.getElementById("msg").innerHTML) //aa
})
}
}
})
</script>
</body>
</html>
13.5 参考
https://juejin.im/post/5b6a60285188251af1221121
14.keep-alive
14.1实现页面缓存
14.1.1 方法一
首先在定义路由的时候配置 meta 字段,自定义一个KeepAlive字段作为该页面是否缓存的标记
routes:[{
path: '/search',
name: 'search',
component: search,
meta: {
title: '搜索列表页',
keepAlive: true // 标记列表页需要被缓存
}
},
{
path: '/detail',
name: 'detail',
component: detail,
meta: {
title: '详情页',
// 详情页不需要做缓存,所以不加keepAlive标记
}
}]
由于<keep-alive>组件不支持v-if指令,所以我们在App.vue中采用两个<router-view>的写法,通过当前路由的keepAlive字段来判断是否对页面进行缓存:
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
</div>
14.1.1 方法二
使用<keep-alive>提供的 exclude 或者 include 选项,此处我们使用 exclude ,在App.vue中:
<div id="app">
<keep-alive exclude="detail">
<router-view />
</keep-alive>
</div>
需要注意的是,一定要给页面组件加上相应的name,例如在detail.vue中:
<script>
<script>
export default {
name: 'detail', // 这个name要和keep-alive中的exclude选项值一致
...
}
</script>
这么写就代表了在项目中除了name为detail的页面组件外,其余页面都将进行缓存。
15 vue组件通信
常见使用场景可以分为三类:
父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
兄弟通信:
Bus;Vuex
跨级通信:
Bus;Vuex;provide / inject API、$attrs/$listeners
event-bus
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 任何一个页面,发送
import { EventBus } from '@/config/event-bus.js'
EventBus.$emit('keyDownEvent', params)
// 任何一个页面,接收
import { EventBus } from '@/config/event-bus.js'
EventBus.$on('keyDownEvent', params => {})
$attrs和$listeners
修改前
父组件
<template>
<div>
<custom-Image
:src="src"
style="height: 100px"
class="custom-images"
alt="图片报错"
@load="onload"
></custom-Image>
</div>
</template>
<script>
import customImage from "@/components/custom-Image.vue";
export default {
name: "TestView",
components: {
customImage,
},
data() {
return {
src: "https://upload.jianshu.io/users/upload_avatars/23533258/c72270da-b23c-4b67-b70d-3bd55305849a.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96",
};
},
methods: {
onload() {
console.log("parent");
},
},
};
</script>
<style scoped>
.custom-images {
width: 200px;
}
</style>
子组件
<template>
<div>
<el-image :src="src" @load="onload"> </el-image>
</div>
</template>
<script>
export default {
props: {
src: {
type: String,
default: "",
},
},
mounted() {
console.log("$attrs", this.$attrs);
console.log("$listeners", this.$listeners);
},
methods: {
onload() {
console.log("children");
this.$emit("load");
},
},
};
</script>
修改后
子组件
<template>
<div>
<el-image
v-bind="$attrs"
v-on="$listeners"
@load="onload"
style="width: 100px; height: 100px"
>
</el-image>
</div>
</template>
<script>
export default {
mounted() {
console.log("$attrs", this.$attrs);
console.log("$listeners", this.$listeners);
},
methods: {
onload() {
console.log("children");
},
},
};
</script>
参考
https://juejin.im/post/5bd97e7c6fb9a022852a71cf
https://github.com/ljianshu/Blog/issues/66
16 Vue 的响应式原理中 Object.defineProperty 有什么缺陷
Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
- Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
- Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
- Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性
16. Vue计算属性和侦听属性
16.1 计算属性
- 计算属性由两部分组成:get和set,分别用来获取计算属性和设置计算属性。默认只有get,如果需要set,要自己添加。另外set设置属性,并不是直接修改计算属性,而是修改它的依赖
computed 是可以缓存的,methods 不能缓存
data:{ //普通属性 msg:'aa', }, computed:{ //计算属性 reverseMsg:function(){ // 该函数必须有返回值,用来获取属性,称为get函数 //可以包含逻辑处理操作,同时reverseMsg依赖于msg,一旦msg发生变化,reverseMsg也会跟着变化 return this.msg.split(' ').reverse().join(' '); } }
16.2 监听属性
- handler 回调
- deep 设置为 true 用于监听对象内部值的变化
immediate 设置为 true 将立即以表达式的当前值触发回调
<template> <button @click="obj.a = 2">修改</button> </template> <script> export default { data() { return { obj: { a: 1, } } }, watch: { obj: { handler: function(newVal, oldVal) { console.log(newVal); }, deep: true, immediate: true } } } </script>
16.3 两者之间对比
- watch:监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操
- computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才会重新计算
16.4 总结
- 计算属性适合用在模板渲染中,某个值是依赖了其它的响应式对象甚至是计算属性计算而来;
- 侦听属性适用于观测某个值的变化去完成一段复杂的业务逻辑
- computed能做的,watch都能做,反之则不行,能用computed的尽量用computed
16.5 参考
17 vue组件三大核心概念
17.1 概括
18 keep-alive
19 Vue的钩子函数
Vue的钩子函数[路由导航守卫、keep-alive、生命周期钩子
20 全面解析Vue.nextTick实现原理
nextTick。它可以在DOM更新完毕之后执行一个回调
全面解析Vue.nextTick实现原理
nexttick是怎么可以获取到更新后的dom的_深入浅出理解vm.$nextTick
21 vue.mixin
22 vue.use
23 vue.extend
虚拟DOM与diff算法
比较规则
新的DOM节点不存在{type: 'REMOVE', index}
文本的变化{type: 'TEXT', text: 1}
当节点类型相同时,去看一下属性是否相同,产生一个属性的补丁包{type: 'ATTR', attr: {class: 'list-group'}}
节点类型不相同,直接采用替换模式{type: 'REPLACE', newNode}
整个DOM-diff的过程
- 用JS对象模拟DOM(虚拟DOM)
- 把此虚拟DOM转成真实DOM并插入页面中(render)
- 如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff)
- 把差异对象应用到真正的DOM树上(patch)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。