总结一下vue重点,做复习之用
- mvvm双向数据绑定原理
当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 都加上 setter和getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是: 1、在自身实例化时往属性订阅器(dep)里面添加自己 2、自身必须有一个update()方法 3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果
-
vue不能新增不存在的属性,不存在的属性没有getter和setter
-
vue的Virtual DOM
Virtual DOM这个概念产生前提示浏览器dom是很昂贵的,浏览器标准把dom设计的很复杂,我们频繁的更新dom,会产生一定的性能问题,Virtual dom就是用原生的JS对象描述DOM
1. 无论我们写.vue文件还是写了template属性,最终都会转化为render方法,调用原型上的$mount进行挂载
2. render函数用来创建VNode, render函数接受一个createElement方法作为第一个参数用来创建vnode
- DOM DIFF
对比算法
1. 当对比两棵树时,React首先比较两个根节点。根节点的type不同,其行为也不同。每当根元素有不同类型,React将卸载旧树并重新构建新树。从<a>到<img>或从<Article>到<Comment>,或从<Button> 到 <div>,任何的调整都会导致全部重建。当树被卸载,旧的DOM节点将被销毁。组件实例会产生
2. 当比较两个相同类型的React DOM元素时,React则会观察二者的属性,保持相同的底层DOM节点,并仅更新变化的属性。
3. React支持了一个key属性。当子节点有key时,React使用key来匹配原本树的子节点和新树的子节点。
- 请详细说下你对vue生命周期的理解?
创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在
- DOM 渲染在 哪个周期中就已经完成?
DOM 渲染在 mounted 中就已经完成了
- v-if和v-show区别
v-if是动态的向DOM树内添加或者删除DOM元素;
v-show是通过设置DOM元素的display样式属性控制显隐;
- 减小首屏代码体积,我们用异步组件
- 组件上监听原生事件native
- a: {name: {age: 123}} 改变age能触发视图更新是Object.defineProperty实现setter和getter
- 用key 管理可复用的元素
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
不加key替换时只会换placeholder
监听浏览器关闭事件
// 关闭页面提醒
export default {
created () {
// 监听浏览器关闭事件
window.onbeforeunload = function (e) {
var dialogText = '离开网站将不保存您所做的更改'
e.returnValue = dialogText
return dialogText
}
},
destroyed () {
// 解除监听浏览器关闭事件
window.onbeforeunload = null
}
}
class和style绑定
class绑定一个对象
1. <div v-bind:class="{ active: isActive }"></div>
2. <div v-bind:class="classes"></div>
data: {
isActive: true,
hasError: false
}
绑定一个数组,数组中可以加三木运算和对象
3. <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
4. <div v-bind:class="[{ active: isActive }, errorClass]"></div>
style绑定
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
数组语法可以将多个样式对象应用到同一个元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
watch高级用法
<div>
<p>FullName: {{fullName}}</p>
<p>FirstName: <input type="text" v-model="firstName"></p>
</div>
new Vue({
el: '#root',
data: {
firstName: 'Dawei',
lastName: 'Lou',
fullName: ''
},
watch: {
firstName(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
}
}
//
watch: {
firstName: {
handler(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
// 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
immediate: true
}
})
// watch还有deep
watch: {
obj: { // js的内存地址不变
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true // 深度监听
}
}
deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler。
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true
}
}
vue不能更改一下情况
数组:
当你利用索引直接设置一个项时
当你修改数组的长度时
对象:
Vue 不能检测对象属性的添加或删除:
v-model的修饰符:.lazy, .number, .trim
.sync 修饰符
过渡
Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。
插件
import Vue from 'vue'
function log (message) {
if (process.env.NODE_ENV !== 'production') {
window.console.log(message)
}
}
const ConsolePlugin = {
install(vue, options) {
Vue.prototype.$console = log
}
}
export default ConsolePlugin
new Vue的过程
Vue 初始化主要就干了几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等。
v-model实现
<input v-bind:value="ying" v-on:input="ying=$event.target.value">
Vue项目点击浏览器返回,返回至指定的页面
beforeRouteLeave(to, from, next){
if(to.name ==='D' ){
next({name: 'G'});
}else {
next(); // 注意:这边next必须要写
}
},
Object.defineProperty和Proxy区别
Object.defineProperty() 的问题主要有三个:
不能监听数组的变化
必须遍历对象的每个属性
必须深层遍历嵌套的对象
Proxy 在 ES2015 规范中被正式加入,它有以下几个特点:
针对对象:针对整个对象,而不是对象的某个属性,所以也就不需要对 keys 进行遍历。这解决了上述 Object.defineProperty() 第二个问题
支持数组:Proxy 不需要对数组的方法进行重载,省去了众多 hack,减少代码量等于减少了维护成本,而且标准的就是最好的。
除了上述两点之外,Proxy 还拥有以下优势:
Proxy 的第二个参数可以有 13 种拦截方法,这比起 Object.defineProperty() 要更加丰富
Proxy 作为新标准受到浏览器厂商的重点关注和性能优化,相比之下 Object.defineProperty() 是一个已有的老方法。
路由类型
全局守卫
路由独享守卫
路由组件内的守卫
beforeDestory和 destoryed
beforeDestory() 和 destoryed() 前者是销毁前执行(实例仍然完全可用),后者则是销毁后执行
this.$nextTick
因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2017 async/await 语法完成相同的事情:
async () => {
await this.$nextTick()
}
mixins
mixin的混入策略可以简单的理解为以下几点
1.数据对象,data中的属性,会进行递归合并,类比es6的展开运算符,如果mixin中存在的,泽一组件定义的数据优先。
2.同名的钩子函数会合并数组,如mounted ,如果组件和mixin都定义了,则都会执行,且组件内定义的后置。即混入的代码会优先执行。
3.同名的函数会覆盖。类似data中的属性,会将组件内函数和mixin中函数递归合并,同名覆盖,这点类似于es5的,同名函数覆盖,组件内函数覆盖混入函数。
Vue 组件 data 为什么必须是函数
每个组件都是 Vue 的实例。
组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一个会影响其他。
计算属性
计算属性是可以依赖另外一个计算属性的
new Vue 发生了什么
前面已经介绍的都是Vue源码的一下细节,这一篇是对前面的一个总结以及梳理。
具体的一下细节,可以看前面的文章。
在执行new Vue()后,Vue先初始化数据:
initLifucycle :规格化 option && 初始化属性
initEcents :把父组件在子组件上绑定的自定义事件传递到子组件中
beforeCreated :执行生命周期函数
initJections:读取 jections 如果有的话
initStates:初始化 props 、methods、data 、computed 、watch
initProvide:初始化 provide
created:执行生命周期函数
模板编译 :把模板编译成渲染函数
beforeMount :执行生命周期函数
Watcher 渲染函数:当渲染函数里面的数据变化会通知 Watcher 实例更新
mounted :挂载实例 替换 el
到此页面就已经渲染到页面上。接下来就是当数据更新时,更新DOM,以及卸载实例。