1.vue的生命周期有哪些
beforeCreate/Create --- 实例创建前后
beforeMount/Mount --- 实例挂在前后(Mount周期下适合做数据渲染)
beforeUpdate/Update --- 数据更新前后
beforeDestory/Destory --- 实例销毁前后
activeted --- keep-alive组件激发时候调用
deactiveted --- keep-alive组件被停用时候调用
2.vue双向数据绑定的原理
vue双向数据绑定的实现是通过Object.defineProperty()方法来劫持data中声明的对象中各个属性的getter/setter。
在数据变动时,发布消息给订阅者,触发相应的监听回调。
当把一个JavaScript对象传给Vue实例来作为data选项时,Vue将遍历它的属性,用Object.defineProperty()将它们
转为getter/setter。用户看不到getter/setter,但是内部它们让Vue追踪依赖,在属性被访问和修改时通知变化。
3.vue组件间的通信
(1)props,$emit // 适用于父子组件通信
(2)refs和$parent和$children // 适用于父子组件
注:
// 父组件向子组件通信
props着重数据的传递,它并不能调用子组件的属性和方法
refs着重于索引,主要用来调用子组件的属性和方法。不擅长数据的传递,而且ref用在dom元素的时候,能使到
选择器的作用
// 子组件向父组件通信
$emit绑定一个自定义事件(event),当这个语句被执行到的时候,就会将参数传递,父组件通过@event监听
并接收参数
(3)$attrs/$listeners(vue 2.4中提出) // attrs传递的是没被props声明的对象。listener是父组件v-on监听的
(4)provide和inject(vue 2.4中提出) // 在祖先组件用provide声明,在后代组件中用inject去注入所需变量
(5)EventBus(事件中心)// 常用于兄弟组件之间通信
注:
创建一个公用的bus.js,到处bus实例,在A,B组件中同时引用,A组件$emit事件及参数,B组件$on接收
(6)Vuex // 一般用于大型项目上的数据仓库,对数据状态进行统一管路。较小型项目用了反而感觉麻烦
4.钩子函数
在生命周期执行的流程中,预留一个能让我们执行自己代码的地方,叫钩子函数。即挂载我们自己的东西,而钩子函数的
实现基本原理就是callback。
vue中的生命周期函数就是最常用的钩子函数。
5 .vue-router中常用的钩子函数
(1)全局钩子
主要包括beforeEach和afterEach
beforeEach参数:
~ to:router // 即将进入的路由对象
~ from // 当前导航即将离开的路由
~ next:Function // 如果执行完,则导航状态就是confirmed(确认的);否则为false,终止导航
afterEach不需要传入next()函数
// 这类钩子主要作用于全局,一般用来判断权限,以及页面丢失时候需要执行的操作
(2)单个路由里面的钩子
beforeEnter和afterEnter
参数:
~ to
~ from
~next:Function
// 这类钩子主要用于写某个指定路由跳转时需要执行的逻辑
(3)组件内路由的钩子
主要包括beforeRouteEnter,beforeRouteUpdate和beforeRouteLeave
参数:
~ to
~ from
~ next:Function
6.v-for循环中key的作用
存在key的值,可以更加高效的渲染虚拟Dom。其中会涉及到虚拟Dom的diff算法,当页面数据发生变动时,diff算法只会
比较同一层级的节点。当某一层级存在很多相同的节点时,则可以通过key的值快速定位到需要变动的位置,提升更新虚拟
Dom的渲染过程。(在vue中使用v-for循环时,不绑定key也不会提示报错是因为如若你没给定key值,vue自己会自动
生成一个key。但官方是推荐使用自己给定的key值。)
7.vue组件中的data为什么必须是一个函数
因为组件都是会被重复调用的。如果data是一个单纯的对象形式,当被多组件调用时,修改同一个data则会发生相互覆
盖。但是data是一个函数的话,这样没复用一次组件,就会返回一个新的data。类似于给每个组件实例创建了一个私有
的数据空间,让每个组件实例维护各自的数据。
当使用new Vue()的方式的时候,无论我们将data设置为对象还是函数都是可以的,因为new Vue()的方式是生成一个跟
组件,该组件不会被复用,也就不存在共享data的情况了。
8.$route和 $router的区别
$route是"路由信息对象",包括path,params,hash,query,name等的一些路由信息参数
$router是"路由实例"对象,包括了路由的跳转方法和区别
9.vue路由的实现:hash模式和history模式
hash模式:在浏览器中符号"#",#以及#后面的字符统称为hash,用window.locaation.hash来读取。
特点:hash虽然在url中,但不被包括在http请求中,用来指导浏览器动作,对服务端不产生影响,hash不会重载页面
history模式:history采用HTML5新特性,且提供了两个新方法:pushState(),replaceState()可以对浏览器历史
记录栈进行修改,以及popState事件的监听到状态变更。
10. methods,watch和computed的区别和用法
computed属性的结果会被缓存,除非所依赖的响应式属性变化才会重新渲染计算。主要是当作属性来使用。计算属性是
取返回值作为最新结果,不能写异步逻辑。
watch是用来监听属性,监听的值发生改变会重新执行函数,将一个值重新赋值为最新结果,所以赋值的时候可以进行一
些异步操作。
methods方法属性,没有缓存机制,当数据发生变动,重新渲染页面时,会重新执行页面调用的方法。
11.vue computed的实现
1.建立与其他属性(data,store)的联系
2.属性发生改变时,通知计算属性重新计算
实现如下:
- 初始化data,使用Object.defineProperty把这些属性全部转为getter/setter。
- 初始化computed,遍历computed里的每个属性,每个computed属性都是一个watch实例。每个属性提供的函数作
为属性的getter,使用Object.defineProperty转化。
- Object.defineProperty.getter依赖收集,用于依赖发生变化时,触发属性重新计算。
- 若当前出现computed计算属性嵌套其他computed计算属性时,先进行其他的依赖收集
12.vue引入的虚拟Dom
12.1.Virtual Dom是什么
虚拟Dom其实就是一颗以JavaScript对象作为基础的树。用对象属性来描述节点,最终通过一系列的操作使这颗树映射到
真实环境上,可以把虚拟Dom理解为一个简单的JS对象,并且最少包含了标签名,属性和子元素对象三个属性。
12.2.虚拟Dom的作用
虚拟Dom的最终目标是将虚拟节点渲染到视图上。但是如果使用虚拟节点直接覆盖旧节点的话,会有很多不必要的Dom操
作。例如,一个ul标签下有很多标签,其中只有一个li标签发生变化,这种情况下,如果使用新的ul去替换旧的ul,就会造
成一些不必要的性能浪费。
为了避免不必要的操作,虚拟Dom在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所用到的虚拟节点进行
对比,找到真正需要更新的节点来进行Dom操作,从而避免操作其他无需更改的Dom。
其实虚拟Dom在vue.js中主要做了两件事
1.提供与真实Dom节点所对应的虚拟节点vnode
2.将虚拟节点vnode与旧的虚拟节点vnode进行对比,然后更新视图
12.3.为何需要虚拟Dom
- 具备跨平台优势
由于虚拟Dom是由Javascript对象为基础而不依赖真实平台环境,所以它具备跨平台优势
- Do,m操作慢,js运行效率高
在虚拟Dom中进行了大量JS计算比对,减少了大量的Dom操作
- 提升渲染性能
根据dif算法,虚拟Dom可以快速定位到需要更新的数据上。可以实现高效的Dom操作
12.4.vue的diff算法
vue的diff算法 --- 仅在同级的vNode间做diff算法,递归的进行同级的vNode的diff,最终实现整个Dom树的更新
diff算法步骤:
- 用JavaScript对象结构表示Dom树的结构;然后用这个树构建一个真正的Dom树,插到文档中
- 当对象变更的时候,重新构造一棵新的对象树,跟旧的树进行比对,记录差异
- 把所记录的差异应用到所构建真正的Dom树上, 更新视图
13.vuex是什么
当涉及多个组件共用一个状态,或由多个组件共同更新一个状态时,则采用vuex进行统一的数据状态管理。
vuex实现了一个单向数据流,在全局拥有state对象用来存放数据,当state里的数据需要修改的时候,则所有修改的操
作需要通过mutations来进行提交。mutations则对外提供了订阅者模式供外部组件使用来获取state的变化。所以的异
步接口需要在actions里去执行,例如在服务端进行数据获取,在actions中可以通过提交mutations来实现数据渲染。
vuex的运行依赖于vue的双向数据绑定的特性来实现,需要new一个vue的实例来实现数据响应。
14.vuex属性都有哪些
state:为单一状态树,在state中定义所需要管理的数据 (类似于data)
Getter:当我们需要在state的store中派生出一些状态,那么就要用到Getter。Getter会作为接收state作为第一个
参数,而且Getter的返回值会作为依赖缓存起来,只有在Getter中的依赖值发生改变时才会重新计算(类似
于计算属性 computed)
Mutations:作为修改state状态的唯一方法。如若需要修改state中的值则通过$store.commit()方法
actions:在actions中可以执行异步操作,并提交mutations。如页面调用actions则需要使用$store.dispatch()
Module:可以解决state中数据复杂臃肿时,利用module分割出模块,每个模块都有自己的state,Getter,
Mutations,actions
15.vue的编译过程
vue会通过编译器将模板通过几个阶段最终编译为render函数,然后通过执行render函数生成虚拟Dom最终映射成真实
Dom。
这个过程其实分为三个阶段
- 将模板解析为 AST (抽象语法树)
- 优化 AST
- 将 AST 转换为 render 函数
在第一阶段中,最主要的事情还是通过各种各样的正则去匹配模板中的内容,然后将内容提取出来做各种逻辑操作,接
下来会生成一个最基本的AST对象,然后根据这个基本的AST对象中的属性,进一步扩展AST(在这一阶段还会进行一
些逻辑判断。比如判断根组件是否只存在一个,判断标签是否闭合,判断是否符合H5规范等等)。接下来就是优化AST
阶段。最后一个阶段就是通过AST生成render函数。根据render函数生成的虚拟Dom最终映射成真实的Dom
16.Object.defineProperty的缺陷
如果通过下标的方式去修改数组数据或者给对象新增属性并不会触发组件的重新渲染。因为Object.defineProperty不
能拦截到这些操作。更准确的说是,对于数组而言,大部分操作都是拦截不到的,只是vue内部通过重写函数的方式解决
了这一问题。
- 对于解决对象新增属性可通过this.$set API 来解决
- vue监听数组的方法,是通过获得数组的原型,对push,pop,shift,等等的数组方法进行重写,监听重写
方法,进行手动派发更新。
17.nextTick的原理分析
nextTick可以让我们在下次Dom更新循环结束之后执行延迟回调,用于获取更新后的Dom
原理分析:
- vue用异步队列的方式来控制Dom的更新和nextTick回调的先后执行顺序
- microtask(微任务)因为其较高的优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕
- 因为兼容性问题,vue不得不做了microtask(微任务)向macrotask(宏任务)的降级方案