keep-alive 中的生命周期哪些
keep-alive是 Vue 提供的一个内置组件,用来对组件进行缓存——在组件切换过程中将状态保留在内存中,避免反复渲染DOM。
如果为一个组件包裹了 keep-alive,那么它会多出两个生命周期:deactivated、activated。同时,beforeDestroy 和 destroyed 就不会再被触发了,因为组件不会被真正销毁。
当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期;当组件被切回来时,再去缓存里找这个组件、触发 activated钩子函数。
Vue组件如何通信?
Vue组件通信的办法如下:
props/$emit+v-on
: 通过props将数据自上而下传递,而通过$emit和v-on来向上传递信息。- EventBus: 通过EventBus进行信息的公布与订阅
- vuex: 是全局数据管理库,能够通过vuex治理全局的数据流
$attrs/$listeners
: Vue2.4中退出的$attrs/$listeners
能够进行跨级的组件通信- provide/inject:以容许一个先人组件向其所有子孙后代注入一个依赖,不管组件档次有多深,并在起上下游关系成立的工夫里始终失效,这成为了跨组件通信的根底
还有一些用solt插槽或者ref实例进行通信的,应用场景过于无限就不赘述了。
vue和react的区别
=> 相同点:
1. 数据驱动页面,提供响应式的试图组件2. 都有virtual DOM,组件化的开发,通过props参数进行父子之间组件传递数据,都实现了webComponents标准3. 数据流动单向,都反对服务器的渲染SSR4. 都有反对native的办法,react有React native, vue有wexx
=> 不同点:
1.数据绑定:Vue实现了双向的数据绑定,react数据流动是单向的 2.数据渲染:大规模的数据渲染,react更快 3.应用场景:React配合Redux架构适宜大规模多人合作简单我的项目,Vue适宜小快的我的项目 4.开发格调:react举荐做法jsx + inline style把html和css都写在js了 vue是采纳webpack + vue-loader单文件组件格局,html, js, css同一个文件
Vue模版编译原理晓得吗,能简略说一下吗?
简略说,Vue的编译过程就是将template
转化为render
函数的过程。会经验以下阶段:
- 生成AST树
- 优化
- codegen
首先解析模版,生成AST语法树
(一种用JavaScript对象的模式来形容整个模板)。 应用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相干解决。
Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变动,对应的DOM也不会变动。那么优化过程就是深度遍历AST树,依照相干条件对树节点进行标记。这些被标记的节点(动态节点)咱们就能够跳过对它们的比对
,对运行时的模板起到很大的优化作用。
编译的最初一步是将优化后的AST树转换为可执行的代码
。
参考:前端vue面试题具体解答
为什么在 Vue3.0 采纳了 Proxy,摈弃了 Object.defineProperty?
Object.defineProperty 自身有肯定的监控到数组下标变动的能力,然而在 Vue 中,从性能/体验的性价比思考,尤大大就弃用了这个个性。为了解决这个问题,通过 vue 外部解决后能够应用以下几种办法来监听数组
push();pop();shift();unshift();splice();sort();reverse();
因为只针对了以上 7 种办法进行了 hack 解决,所以其余数组的属性也是检测不到的,还是具备肯定的局限性。
Object.defineProperty 只能劫持对象的属性,因而咱们须要对每个对象的每个属性进行遍历。Vue 2.x 里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么须要深度遍历,显然如果能劫持一个残缺的对象是才是更好的抉择。
Proxy 能够劫持整个对象,并返回一个新的对象。Proxy 不仅能够代理对象,还能够代理数组。还能够代理动静减少的属性。
computed 的实现原理
computed 实质是一个惰性求值的观察者。
computed 外部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立即求值,同时持有一个 dep 实例。
其外部通过 this.dirty 属性标记计算属性是否须要从新求值。
当 computed 的依赖状态产生扭转时,就会告诉这个惰性的 watcher,
computed watcher 通过 this.dep.subs.length 判断有没有订阅者,
有的话,会从新计算,而后比照新旧值,如果变动了,会从新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 从新渲染,实质上是一种优化。)
没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其余数据时,属性并不会立刻从新计算,只有之后其余中央须要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)个性。)
理解nextTick吗?
异步办法,异步渲染最初一步,与JS事件循环分割严密。次要应用了宏工作微工作(setTimeout
、promise
那些),定义了一个异步办法,屡次调用nextTick
会将办法存入队列,通过异步办法清空以后队列。
Vue.js的template编译
简而言之,就是先转化成AST树,再失去的render函数返回VNode(Vue的虚构DOM节点),具体步骤如下:
首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的形象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创立编译器的。另外compile还负责合并option。
而后,AST会通过generate(将AST语法树转化成render funtion字符串的过程)失去render函数,render的返回值是VNode,VNode是Vue的虚构DOM节点,外面有(标签名、子节点、文本等等)
Vue 组件间通信有哪几种形式?
Vue 组件间通信是面试常考的知识点之一,这题有点相似于凋谢题,你答复出越多办法当然越加分,表明你对 Vue 把握的越纯熟。Vue 组件间通信只有指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,上面咱们别离介绍每种通信形式且会阐明此种办法可实用于哪类组件间通信。
(1)props / $emit
实用 父子组件通信 这种办法是 Vue 组件的根底,置信大部分同学耳闻能详,所以此处就不举例开展介绍。
(2)ref 与 $parent / $children
实用 父子组件通信
ref
:如果在一般的 DOM 元素上应用,援用指向的就是 DOM 元素;如果用在子组件上,援用就指向组件实例$parent / $children
:拜访父 / 子实例
(3)EventBus ($emit / $on)
实用于 父子、隔代、兄弟组件通信 这种办法通过一个空的 Vue 实例作为地方事件总线(事件核心),用它来触发事件和监听事件,从而实现任何组件间的通信,包含父子、隔代、兄弟组件。
(4)$attrs/$listeners
实用于 隔代组件通信
$attrs
:蕴含了父作用域中不被 prop 所辨认 (且获取) 的个性绑定 ( class 和 style 除外 )。当一个组件没有申明任何prop
时,这里会蕴含所有父作用域的绑定 ( class 和 style 除外 ),并且能够通过v-bind="$attrs"
传入外部组件。通常配合inheritAttrs
选项一起应用。$listeners
:蕴含了父作用域中的 (不含 .native 润饰器的)v-on
事件监听器。它能够通过v-on="$listeners"
传入外部组件
(5)provide / inject
实用于 隔代组件通信 先人组件中通过 provider
来提供变量,而后在子孙组件中通过 inject
来注入变量。 provide / inject API
次要解决了跨级组件间的通信问题,不过它的应用场景,次要是子组件获取下级组件的状态,跨级组件间建设了一种被动提供与依赖注入的关系。 (6)Vuex
实用于 父子、隔代、兄弟组件通信 Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。每一个 Vuex 利用的外围就是 store(仓库)。“store” 基本上就是一个容器,它蕴含着你的利用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地失去高效更新。
- 扭转 store 中的状态的惟一路径就是显式地提交 (commit) mutation。这样使得咱们能够不便地跟踪每一个状态的变动。
computed和watch有什么区别?
computed:
computed
是计算属性,也就是计算值,它更多用于计算值的场景computed
具备缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值扭转之后,下一次获取computed的值时才会从新调用对应的getter来计算computed
实用于计算比拟耗费性能的计算场景
watch:
- 更多的是「察看」的作用,相似于某些数据的监听回调,用于察看
props
$emit
或者本组件的值,当数据变动时来执行回调进行后续操作 - 无缓存性,页面从新渲染时值不变动也会执行
小结:
- 当咱们要进行数值计算,而且依赖于其余数据,那么把这个数据设计为computed
- 如果你须要在某个数据变动时做一些事件,应用watch来察看这个数据变动
Vue.extend 作用和原理
官网解释:Vue.extend 应用根底 Vue 结构器,创立一个“子类”。参数是一个蕴含组件选项的对象。
其实就是一个子类结构器 是 Vue 组件的外围 api 实现思路就是应用原型继承的办法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并
相干代码如下
export default function initExtend(Vue) { let cid = 0; //组件的惟一标识 // 创立子类继承Vue父类 便于属性扩大 Vue.extend = function (extendOptions) { // 创立子类的构造函数 并且调用初始化办法 const Sub = function VueComponent(options) { this._init(options); //调用Vue初始化办法 }; Sub.cid = cid++; Sub.prototype = Object.create(this.prototype); // 子类原型指向父类 Sub.prototype.constructor = Sub; //constructor指向本人 Sub.options = mergeOptions(this.options, extendOptions); //合并本人的options和父类的options return Sub; };}
computed 的实现原理
computed 实质是一个惰性求值的观察者。
computed 外部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立即求值,同时持有一个 dep 实例。
其外部通过 this.dirty 属性标记计算属性是否须要从新求值。
当 computed 的依赖状态产生扭转时,就会告诉这个惰性的 watcher,
computed watcher 通过 this.dep.subs.length 判断有没有订阅者,
有的话,会从新计算,而后比照新旧值,如果变动了,会从新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 从新渲染,实质上是一种优化。)
没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其余数据时,属性并不会立刻从新计算,只有之后其余中央须要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)个性。)
那vue中是如何检测数组变动的呢?
数组就是应用 object.defineProperty
从新定义数组的每一项,那能引起数组变动的办法咱们都是晓得的, pop
、 push
、 shift
、 unshift
、 splice
、 sort
、 reverse
这七种,只有这些办法执行改了数组内容,我就更新内容就好了,是不是很好了解。
- 是用来函数劫持的形式,重写了数组办法,具体呢就是更改了数组的原型,更改成本人的,用户调数组的一些办法的时候,走的就是本人的办法,而后告诉视图去更新。
- 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象能力进行观测,观测过的也不会进行观测)
vue3:改用 proxy
,可间接监听对象数组的变动。
Proxy 与 Object.defineProperty 优劣比照
Proxy 的劣势如下:
- Proxy 能够间接监听对象而非属性;
- Proxy 能够间接监听数组的变动;
- Proxy 有多达 13 种拦挡办法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
- Proxy 返回的是一个新对象,咱们能够只操作新的对象达到目标,而 Object.defineProperty 只能遍历对象属性间接批改;
Proxy 作为新规范将受到浏览器厂商重点继续的性能优化,也就是传说中的新规范的性能红利;
Object.defineProperty 的劣势如下:
- 兼容性好,反对 IE9,而 Proxy 的存在浏览器兼容性问题,而且无奈用 polyfill 磨平,因而 Vue 的作者才申明须要等到下个大版本( 3.0 )能力用 Proxy 重写。
Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
受古代 JavaScript 的限度 ,Vue 无奈检测到对象属性的增加或删除。因为 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在能力让 Vue 将它转换为响应式的。然而 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
来实现为对象增加响应式属性,那框架自身是如何实现的呢?
咱们查看对应的 Vue 源码:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any { // target 为数组 if (Array.isArray(target) && isValidArrayIndex(key)) { // 批改数组的长度, 防止索引>数组长度导致splcie()执行有误 target.length = Math.max(target.length, key) // 利用数组的splice变异办法触发响应式 target.splice(key, 1, val) return val } // key 曾经存在,间接批改属性值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ // target 自身就不是响应式数据, 间接赋值 if (!ob) { target[key] = val return val } // 对属性进行响应式解决 defineReactive(ob.value, key, val) ob.dep.notify() return val}
咱们浏览以上源码可知,vm.$set 的实现原理是:
- 如果指标是数组,间接应用数组的 splice 办法触发相应式;
- 如果指标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式解决,则是通过调用 defineReactive 办法进行响应式解决( defineReactive 办法就是 Vue 在初始化对象时,给对象属性采纳 Object.defineProperty 动静增加 getter 和 setter 的性能所调用的办法)
对SSR的了解
SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端实现,而后再把html间接返回给客户端
SSR的劣势:
- 更好的SEO
- 首屏加载速度更快
SSR的毛病:
- 开发条件会受到限制,服务器端渲染只反对beforeCreate和created两个钩子;
- 当须要一些内部扩大库时须要非凡解决,服务端渲染应用程序也须要处于Node.js的运行环境;
- 更多的服务端负载。
Vue中如何进行依赖收集?
- 每个属性都有本人的
dep
属性,寄存他所依赖的watcher
,当属性变动之后会告诉本人对应的watcher
去更新 - 默认会在初始化时调用
render
函数,此时会触发属性依赖收集dep.depend
- 当属性产生批改时会触发
watcher
更新dep.notify()
依赖收集简版
let obj = { name: 'poetry', age: 20 };class Dep { constructor() { this.subs = [] // subs [watcher] } depend() { this.subs.push(Dep.target) } notify() { this.subs.forEach(watcher => watcher.update()) }}Dep.target = null;observer(obj); // 响应式属性劫持// 依赖收集 所有属性都会减少一个dep属性,// 当渲染的时候取值了 ,这个dep属性 就会将渲染的watcher收集起来// 数据更新 会让watcher从新执行// 观察者模式// 渲染组件时 会创立watcherclass Watcher { constructor(render) { this.get(); } get() { Dep.target = this; render(); // 执行render Dep.target = null; } update() { this.get(); }}const render = () => { console.log(obj.name); // obj.name => get办法}// 组件是watcher、计算属性是watchernew Watcher(render);function observer(value) { // proxy reflect if (typeof value === 'object' && typeof value !== null) for (let key in value) { defineReactive(value, key, value[key]); }}function defineReactive(obj, key, value) { // 创立一个dep let dep = new Dep(); // 递归察看子属性 observer(value); Object.defineProperty(obj, key, { get() { // 收集对应的key 在哪个办法(组件)中被应用 if (Dep.target) { // watcher dep.depend(); // 这里会建设 dep 和watcher的关系 } return value; }, set(newValue) { if (newValue !== value) { observer(newValue); value = newValue; // 让key对应的办法(组件从新渲染)从新执行 dep.notify() } } })}// 模仿数据获取,触发getterobj.name = 'poetries'// 一个属性一个dep,一个属性能够对应多个watcher(一个属性能够在任何组件中应用、在多个组件中应用)// 一个dep 对应多个watcher // 一个watcher 对应多个dep (一个视图对应多个属性)// dep 和 watcher是多对多的关系
Vue.mixin的应用场景和原理
- 在日常的开发中,咱们常常会遇到在不同的组件中常常会须要用到一些雷同或者类似的代码,这些代码的性能绝对独立,能够通过
Vue
的mixin
性能抽离公共的业务逻辑,原理相似“对象的继承”,当组件初始化时会调用mergeOptions
办法进行合并,采纳策略模式针对不同的属性进行合并。当组件和混入对象含有同名选项时,这些选项将以失当的形式进行“合并”;如果混入的数据和自身组件的数据抵触,会以组件的数据为准 mixin
有很多缺点如:命名抵触、依赖问题、数据起源问题
根本应用
<script> // Vue.options Vue.mixin({ // 如果他是对象 每个组件都用mixin里的对象进行合并 data(){ return {a: 1,b: 2} } }); // Vue.extend Vue.component('my',{ // 组件必须是函数 Vue.extend => render(xxx) data(){ return {x:1} } }) // 没有 new 没有实例 _init() // const vm = this new Vue({ el:'#app', data(){ // 根能够不是函数 return {c:3} } })</script>
相干源码
export default function initMixin(Vue){ Vue.mixin = function (mixin) { // 合并对象 this.options=mergeOptions(this.options,mixin) };}};// src/util/index.js// 定义生命周期export const LIFECYCLE_HOOKS = [ "beforeCreate", "created", "beforeMount", "mounted", "beforeUpdate", "updated", "beforeDestroy", "destroyed",];// 合并策略const strats = {};// mixin外围办法export function mergeOptions(parent, child) { const options = {}; // 遍历父亲 for (let k in parent) { mergeFiled(k); } // 父亲没有 儿子有 for (let k in child) { if (!parent.hasOwnProperty(k)) { mergeFiled(k); } } //真正合并字段办法 function mergeFiled(k) { // strats合并策略 if (strats[k]) { options[k] = strats[k](parent[k], child[k]); } else { // 默认策略 options[k] = child[k] ? child[k] : parent[k]; } } return options;}
Vue为什么没有相似于React中shouldComponentUpdate的生命周期?
考点: Vue的变动侦测原理
前置常识: 依赖收集、虚构DOM、响应式零碎
根本原因是Vue与React的变动侦测形式有所不同
React是pull的形式侦测变动,当React晓得发生变化后,会应用Virtual Dom Diff进行差别检测,然而很多组件实际上是必定不会发生变化的,这个时候须要用shouldComponentUpdate进行手动操作来缩小diff,从而进步程序整体的性能.
Vue是pull+push的形式侦测变动的,在一开始就晓得那个组件产生了变动,因而在push的阶段并不需要手动管制diff,而组件外部采纳的diff形式实际上是能够引入相似于shouldComponentUpdate相干生命周期的,然而通常正当大小的组件不会有适量的diff,手动优化的价值无限,因而目前Vue并没有思考引入shouldComponentUpdate这种手动优化的生命周期.
Vue中的key到底有什么用?
key
是为Vue中的vnode标记的惟一id,通过这个key,咱们的diff操作能够更精确、更疾速
diff算法的过程中,先会进行新旧节点的首尾穿插比照,当无奈匹配的时候会用新节点的key
与旧节点进行比对,而后超出差别.
diff程能够概括为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量互相比拟,一共有4种比拟形式。如果4种比拟都没匹配,如果设置了key,就会用key进行比拟,在比拟的过程中,变量会往两头靠,一旦StartIdx>EndIdx表明oldCh和newCh至多有一个曾经遍历完了,就会完结比拟,这四种比拟形式就是首、尾、旧尾新头、旧头新尾.
- 精确: 如果不加
key
,那么vue会抉择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug. - 疾速: key的唯一性能够被Map数据结构充分利用,相比于遍历查找的工夫复杂度O(n),Map的工夫复杂度仅仅为O(1).