1.vue生命周期及各周期得特点
beforCreate
特点:
初始化实例,不能使用data和methods、ref
示例
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", "data中的msg : " + this.msg); //undefined console.log("%c%s", "color:red", "methods中的handleOpen方法 : " + this.handleOpen); //undefined },
结果
created
特点:
data和methods都已经被初始化好了,可以调用,但ref还是不行
示例
created: function () { console.group('------created创建完毕状态------'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 [object Object] console.dir(this.$data) console.log("%c%s", "color:red", "data中的msg : " + this.msg); //测试 console.log("%c%s", "color:red", "methods中的handleOpen方法 : " + this.handleOpen); //function () { [native code] } },
结果
beforeMount
特点:
开始挂载之前调用,相关的render函数首次被调用 此时的DOM是数据挂载前的DOM,数据还未挂载(将虚拟Dom转变成真实Dom的过程,所以在这之前,我们的el
当然还是拿不到的)虽然 $ref已被初始化,但还是拿不到值
示例
beforeMount: function () {
console.group('------beforeMount挂载前状态------');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "refs : " + this.$refs); //已被初始化
console.dir(this.$refs)
var _ref = JSON.parse(JSON.stringify(this.$refs))
console.log("%c%s", "color:red", "_ref : " + _ref); //已被初始化
console.dir(_ref)
console.log("%c%s", "color:red", "refs.myRef : " + this.$refs.myRef); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "data中的msg : " + this.msg); //已被初始化
console.log("%c%s", "color:red", "methods中的handleOpen方法 : " + this.handleOpen);//已被初始化
},
结果
跑题·······················
大家看小颖在打印 $ref 时 又重新定义了一个_ref ,并且这个变量是深拷贝了 $ref 的,这是为 了方便大家能够看到在该钩子函数中 $ref 的值,如果大家不明白为什么,可以看下下面的代码
var a = 1; var b = a; a = 3 console.log(a) console.log(b) var a2 = {} var b2 = a2 a2.c = '22' console.log(a2) console.log(b2)
看这个结果大家有没有发现变量 b
不会随着变量 a
的改变而改变,但是变量 b2
会随着变量 a2
的改变而改变,这是为什么呢?
因为 a
、 b
都是基本类型,而 a2
、b2
都是引用类型,基本类型存储的是值,引用类型存储的是一个指向对象真实内存地址的指针。在 js 中,对象包括Array Object Function RegExp Math等。
大家可以参考下:js对象赋值只保留存在的属性_夯实JS主要知识点
来来来,我们接着看vue的生命周期,不要被小颖带偏了哈哈哈
mounted
特点:
实例挂载完成,vue实例已经初始化完成,初始化的el被vm.$el替换(虚拟Dom已经挂载在了真实的元素上,所以从此开始我们就可以拿到el
)
示例
mounted: function () { console.group('------mounted 挂载结束状态------'); console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化 console.dir(this.$el) console.log("%c%s", "color:red", "refs : " + this.$refs); //已被初始化 console.log("%c%s", "color:red", "refs.myRef : " + this.$refs.myRef);//已被初始化 console.dir(this.$refs) console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "data中的msg : " + this.msg); //已被初始化 console.log("%c%s", "color:red", "methods中的handleOpen方法 : " + this.handleOpen);//已被初始化 },
结果
beforeUpdate
特点:
数据更新时调用,发生在虚拟DOM打补丁之前,这里适合更新之前访问现有的DOM,比如手动移除已添加的时间监听器,该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行 。
示例
beforeUpdate: function () { console.group('beforeUpdate 更新前状态===============》'); // 比如点击新增数组元素, vue会触发此生命周期函数, 但是此时页面并未更新, 所以获取不到新增的li标签 console.log(document.getElementById("myUl").children[2].innerHTML); console.log(document.getElementById("myUl").children[3].innerHTML); },
结果
updated
特点:
在数据驱动下导致的虚拟DOM重新渲染和打补丁之后调用,此时,组件DOM已经更新。只会在数据变更之后触发这个钩子函数。
示例
updated: function () { console.group('updated 更新完成状态===============》'); console.log(document.getElementById("myUl").children[2].innerHTML); console.log(document.getElementById("myUl").children[3].innerHTML); },
结果
beforeDestroy
特点:
实例销毁之前调用,此时实例还可用
示例:
(清除定时器 / 解绑js定义的事件)
destroyed
特点:
实例销毁后调用,实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁,
示例:
(清除定时器 / 解绑js定义的事件)
大家也可以看看:
Vue都使用那么久了,还不了解它的生命周期吗、了解Vue的生命周期、vue组件的生命周期讲解2.keep-alive相关问题
keep-alive是什么?
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
有什么用?
主要用于保留组件状态或避免重新渲染;在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。
参考:vue官网、 vue 使用keep-alive使用总结和进入缓存页需要再次更新数据
有哪些属性
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存。exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。max
- 数字。最多可以缓存多少组件实例。
生命周期执行顺序
- 初次进入时:created > mounted > activated;退出后触发 deactivated
- 再次进入:会触发 activated;事件挂载的方法等。
注意啦注意啦 `````````````````防坑指南
keep-alive
先匹配被包含组件的name
字段,如果name
不可用,则匹配当前组件components
配置中的注册名称。keep-alive
不会在函数式组件中正常工作,因为它们没有缓存实例。- 当匹配条件同时在
include
与exclude
存在时,以exclude
优先级最高(当前vue 2.4.2 version)。比如:包含于排除同时匹配到了组件A,那组件A不会被缓存。 - 包含在
keep-alive
中,但符合exclude
,不会调用activated
和deactivated
。
3.虚拟DOM相关问题
什么是虚拟DOM
JS
的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点。虚拟DOM实现原理是什么?
- 数据和模板相结合生成虚拟dom
- 虚拟dom转化成真是dom渲染到页面上
- 当数据发生改变时,新的数据和模板相结合会生成新的虚拟dom
- 新旧虚拟dom进行比对找差异
- 找到差异后根据差异改变dom
- 老的虚拟dom被清除,新的虚拟dom变成老的虚拟dom
虚拟dom相比真实dom的优势在哪里?
- 减少dom操作(虚拟dom可以将多次操作合并为一次操作。比如你添加1000个节点,传统方法是一个接一个的操作。虚拟dom借助dom diff可以把多余的操作步骤省略掉,比如你添加1000个节点,其实只有10个节点是新增的。)
- 跨平台渲染(虚拟 DOM 本质上是 JavaScript 对象,⽽ DOM 与平台强相关,相⽐之下虚拟 DOM 可以进⾏更⽅便地跨平台操作,例如
服务器渲染、weex 开发等等。)
模板和虚拟dom的关系
vue框架中有一个compile模块,它主要负责将模板转换为render函数,而render函数调用后将得到虚拟dom。
加考点:什么是diff算法
各种百度后小颖的理解是:在数据发生变化时对比新旧虚拟dom,更新试图的过程就叫diff算法。
网上答案小颖觉得比较的是:diff的过程就是调用名为patch
的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。
参考:详解vue的diff算法
4.双向绑定原理
Object.defineProperty()
方法来劫持(监听)各属性的 getter、setter,并在当监听的属性发生变动时通知订阅者Watcher,是否需要更新,若更新就会执行对应的更新函数。而Object.defineProperty 是有局限性的,他的拦截的 target 就是单纯的对象的key的值,所以当对象属性的删减,数组,数组长度的改变,它就没法进行劫持了,这时就需要调用 $set方法来更新数据。参考:vue的双向绑定原理与实现、Vue数据双向绑定的原理是什么?、记一次忏悔的前端面试经验(Vue 双向绑定原理)
5.v-model原理
v-bind
绑定一个value
属性v-on
监听当前元素的input
事件,当数据变化时,将值传递给value
实时更新数据<input :value="val" @input="val = $event.target.value">
6.父子组件生命周期钩子函数执行顺序
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
7.父组件可以监听到子组件的生命周期吗?
可以
使用$emit
// 父组件代码: <child @created="doSomething" /> // 子组件代码: created() { this.$emit('created') }
8.请谈谈vue-router路由模式有几种?及hash和history路由实现原理
参考小颖上一篇文章: hash和history路由的区别
后面的等有时间再写