Vue面试题总结

1. 基本问题

1.1. Vue.js 双向绑定的原理

Vue.js 2.0 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。

Vue.js 3.0, 放弃了Object.defineProperty ,使用更快的ES6原生 Proxy (访问对象拦截器, 也称代理器)

步骤:

  1. 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
  2. compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
  3. Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器(dep)里面添加自己 ②自身必须有一个update()方法 ③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
  4. MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

1.2、vue是如何实现响应式数据的呢?(响应式数据原理)

Vue2:Object.defineProperty 重新定义data 中所有的属性,Object.defineProperty 可以使数据的获取与设置增加一个拦截的功能,拦截属性的获取,进行依赖收集。拦截属性的更新操作,进行通知。

具体的过程:首先Vue使用 initData 初始化用户传入的参数,然后使用 new Observer 对数据进行观测,如果数据是一个对象类型就会调用this.walk(value) 对对象进行处理,内部使用 defineeReactive 循环对象属性定义响应式变化,核心就是使用Object.defineProperty 重新定义数据。

那vue中是如何检测数组变化的呢?

数组就是使用object.defineProperty 重新定义数组的每一项,那能引起数组变化的方法我们都是知道的,pop 、push 、shift 、unshift 、splice 、sort 、reverse 这七种,只要这些方法执行改了数组内容,我就更新内容就好了,是不是很好理解。

是用来函数劫持的方式,重写了数组方法,具体呢就是更改了数组的原型,更改成自己的,用户调数组的一些方法的时候,走的就是自己的方法,然后通知视图去更新。

数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象才能进行观测,观测过的也不会进行观测)

vue3:改用proxy ,可直接监听对象数组的变化。


 

1.3. Vue.js 3.0 放弃defineProperty, 使用Proxy的原因

Object.defineProperty缺陷

  1. 监控到数组下标的变化时,开销很大。所以Vue.js放弃了下标变化的检测;
  2. Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象。Object.defineProperty需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。
  3. Object.defineProperty对新增属性需要手动进行Observe。vue2时需要使用 vm.$set 才能保证新增的属性也是响应式
  4. Proxy支持13种拦截操作,这是defineProperty所不具有的
  5. Proxy 作为新标准,长远来看,JS引擎会继续优化 Proxy,但 getter 和 setter 基本不会再有针对性优化
  6. Proxy的优势如下:

Proxy可以直接监听对象而非属性;

Proxy可以直接监听数组的变化;

Proxy有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;

Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;

Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;

Object.defineProperty的优势如下:

兼容性好,支持IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

1.4. Vue 2 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?

视图并未刷新。这是因为在Vue实例创建时,新属性并未声明,因此就没有被Vue转换为响应式的属性,自然就不会触发视图的更新,这时就需要使用Vue的全局 api $set():this.$set(this.obj, 'new_property', 'new_value')

三种方式

1、直接使用Object.assign()添加到对象的新属性不会触发更新。

应创建一个新的对象,合并原对象和混入对象的属性

data() {

return {

msg:{

name:'黑泽明',

hero:true

}

}

},

addProperty(){

this.msg = Object.assign({},this.msg,{sex:'name'});

},

2、Vue.set()

3、 this.msg.sex = '男';

this.$forceUpdate()

对象中添加少量的新属性,可以直接采用Vue.set()

对象中添加多个新属性,则通过Object.assign()创建新对象

如果你实在不知道怎么操作时,可采取$forceUpdate()进行强制刷新 (不建议)

1.5. Computed和Watch的区别

javascript - Vue的computed和watch的细节全面分析 - 个人文章 - SegmentFault 思否

computed 、watch、created 、mounted 的先后顺序

immediate 为false时: created => computed => mounted => watch

immediate 为true时: watch =>created=> computed => mounted

  1. computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的 属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
  2. watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每 当监听的数据变化时都会执行回调进行后续操作。

运用场景:

  1. 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。
  2. 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率, 并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
  3. 多个因素影响一个显示,用Computed;一个因素的变化影响多个其他因素、显示,用Watch;

computed用于处理复杂的逻辑运算,主要和methods储存方法来进行区分;methods储存方法,computed储存需要处理的数据值;methods每次都会调用,computed有缓存机制,只有改变时才执行,性能更佳;

watch顾名思义,用于监听数据变化,其中可以监听的数据来源有三部分:props、data、computed内的数据;watch提供两个参数(newValue,oldValue),第一个参数是新值,第二个参数保存旧值;

区别:

计算属性computed :

支持缓存,只有依赖数据发生改变,才会重新进行计算

不支持异步,当computed内有异步操作时无效,无法监听数据的变化

computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过的数据通过计算得到的

如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed

如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。

侦听属性watch:

不支持缓存,数据变,直接会触发相应的操作;

watch支持异步;

监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;

当一个属性发生变化时,需要执行对应的操作;一对多;

computed: {

fullName () {

return `${this.firstName} ${this.lastName}`;

}

}

复制代码这里的 fullName 事实上可以写为一个 Object,而非 Function,只是 Function 形式是我们默认使用它的 get 方法,当写为 Object 时,还能使用它的 set 方法:

computed: {

fullName: {

get () {

return `${this.firstName} ${this.lastName}`;

},

set (val) {

const names = val.split(' ');

this.firstName = names[0];

this.lastName = names[names.length - 1];

}

}

}

复制代码计算属性大多时候只是读取用,使用了 set 后,就可以写入了,比如上面的示例,如果执行 this.fullName = 'Aresn Liang',computed 的 set 就会调用,firstName 和 lastName 会被赋值为 Aresn 和 Liang。

1.7. 虚拟DOM,diff算法

首先需要了解Vue的渲染逻辑:

将模板template解析成AST;

再将AST转化为Render函数;

通过Watcher监听数据的变化;

当数据发生变化时,Render函数执行生成虚拟VNode节点,该节点包含创建DOM节点所需信息;

通过patch方法,对比新旧VNode对象,通过DOM diff算法,添加、修改、删除真实DOM节点。

————————————————

(1)让我们不用直接操作DOM元素,只操作数据便可以重新渲染页面
(2)虚拟dom是为了解决浏览器性能问题而被设计出来的
当操作数据时,将改变的dom元素缓存起来,都计算完后再通过比较映射到真实的dom树上
(3)diff算法比较新旧虚拟dom。如果节点类型相同,则比较数据,修改数据;如果节点不同,直接干掉节点及所有子节点,插入新的节点;如果给每个节点都设置了唯一的key,就可以准确的找到需要改变的内容,否则就会出现修改一个地方导致其他地方都改变的情况。比如A-B-C-D, 我要插入新节点A-B-M-C-D,实际上改变的了C和D。但是设置了key,就可以准确的找到B C并插入

diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。

diff算法的本质是找出两个对象之间的差异,目的是尽可能复用节点。此处说到的对象其实就对应 vue中的 virtual dom,即使用 js对象来表示页面中的 dom 结构。

diff算法比较新旧虚拟dom

如果节点类型相同,则比较数据,修改数据

如果节点不同,直接干掉节点及所有子节点,插入新的节点

如果给每个节点都设置了唯一的key,就可以准确的找到需要改变的内容,否则就会出现修改一个地方导致其他地方都改变的情况。比如A-B-C-D, 我要插入新节点A-B-M-C-D,实际上改变的了C和D。但是设置了key,就可以准确的找到B C并插入。


diff算法的优化策略:四种命中查找,四个指针

旧前与新前(先比开头,后插入和删除节点的这种情况)

旧后与新后(比结尾,前插入或删除的情况)

旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)

旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)

你怎么理解Vue中的diff算法?

在js中,渲染真实DOM的开销是非常大的, 比如我们修改了某个数据,如果直接渲染到真实DOM, 会引起整个dom树的重绘和重排。那么有没有可能实现只更新我们修改的那一小块dom而不要更新整个dom呢?此时我们就需要先根据真实dom生成虚拟dom, 当虚拟dom某个节点的数据改变后会生成有一个新的Vnode, 然后新的Vnode和旧的Vnode作比较,发现有不一样的地方就直接修改在真实DOM上,然后使旧的Vnode的值为新的Vnode。

diff的过程就是调用patch函数,比较新旧节点,一边比较一边给真实的DOM打补丁。在采取diff算法比较新旧节点的时候,比较只会在同层级进行。 在patch方法中,首先进行树级别的比较 new Vnode不存在就删除 old Vnodeold Vnode 不存在就增加新的Vnode 都存在就执行diff更新 当确定需要执行diff算法时,比较两个Vnode,包括三种类型操作:属性更新,文本更新,子节点更新 新老节点均有子节点,则对子节点进行diff操作,调用updatechidren 如果老节点没有子节点而新节点有子节点,先清空老节点的文本内容,然后为其新增子节点 如果新节点没有子节点,而老节点有子节点的时候,则移除该节点的所有子节点 老新老节点都没有子节点的时候,进行文本的替换

updateChildren 将Vnode的子节点Vch和oldVnode的子节点oldCh提取出来。 oldCh和vCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和vCh至少有一个已经遍历完了,就会结束比较。

虚拟 DOM 的优缺点?

优点:

  • 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
  • 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
  • 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

缺点:

  • 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
虚拟 DOM 实现原理?

虚拟 DOM 的实现原理主要包括以下 3 部分:

  • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
  • diff 算法 — 比较两棵虚拟 DOM 树的差异;
  • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。


 

1.8. 为何需要Virtual DOM?

  1. 具备跨平台的优势
  2. 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。
  3. 提升渲染性能

1.9. 过滤器 (Filter)

在Vue中使用filters来过滤(格式化)数据,filters不会修改数据,而是过滤(格式化)数据,改变用户看到的输出(计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示。
使用场景: 比如需要处理时间、数字等的的显示格式;

1.10. 常见的事件修饰符及其作用

  1. .stop:等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;
  2. .prevent :等同于 JavaScript 中的 event.preventDefault() ,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);
  3. .capture :当元素发生冒泡时,先触发带有该修饰符的元素。若有多个该修饰符,则由外而内触发。如 div1中嵌套div2中嵌套div3.capture中嵌套div4,那么执行顺序为:div3=》div4=》div2=》div1
  4. .self :只会触发自己范围内的事件,不包含子元素;
  5. .once :只会触发一次。

1.11. v-show指令和v-if指令的区别是什么?

当v-show值为false时,绑定DOM的 display:none 当v-show值为true时,绑定DOM会 移除display:none ,此时并不是把display变为block,而是保持元素style的原始性,也就是说,不管初始条件是什么,元素总是会被渲染。;从实现效果可以看出DOM元素始终是存在的,v-show只是利用元素的display属性控制着元素的显示隐藏。

  • v-show指令设置隐藏是给绑定的DOM元素添加CSS样式:display:none,但是DOM元素仍然存在;
  • v-if指令设置隐藏是将DOM元素整个删除,此时DOM元素不再存在。
  • 2、编译过程不同

v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;而 v-show 只是简单的基于CSS切换,不管初始条件是什么,元素总是会被渲染。

3、编译条件不同

v-show 由false变为true时不会触发组建的生命周期

v-if 由false变为true时,触发组件的beforeCreate、create、beforeMount、mounter钩子,由true变为false时,触发组件的beforeDestory、destoryed方法。v-if 是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;v-if 也是惰性的,如果初始渲染时条件为假,则什么也不做——直到为真时才开始渲染条件块。

4、性能消耗不同

v-show 由更高的初始渲染消耗, v-if 有更高的切换消耗。

1.12. v-model 是如何实现的,语法糖实际是什么

  1. 作用在表单元素上v-model="message"等同于v-bind:value="message" v-on:input="message=$event.target.value"
  2. 作用在组件上, 本质是一个父子组件通信的语法糖,通过prop和$.emit实现, 等同于:value="message" @input=" $emit('input', $event.target.value)"

1.13. data为什么是一个函数而不是对象

JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。

而在Vue中,我们更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。

所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当我们每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。

1.14. Vue template 到 render 的过程

  1. 调用parse方法将template转化为AST(抽象语法树, abstract syntax tree)
  2. 对静态节点做优化。如果为静态节点,他们生成的DOM永远不会改变,这对运行时模板更新起到了极大的优化作用。
  3. 生成渲染函数. 渲染的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名,子节点,文本等等)

1.15. Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?

不会立即同步执行重新渲染。
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。
然后,在下一个的事件循环"tick"中,Vue 刷新队列并执行实际(已去重的)工作。

1.16. axios是什么

易用、简洁且高效的http库, 支持node端和浏览器端,支持Promise,支持拦截器等高级配置。

1.18. Vue.js页面闪烁

Vue. js提供了一个v-cloak指令,该指令一直保持在元素上,直到关联实例结束编译。当和CSS一起使用时,这个指令可以隐藏未编译的标签,直到实例编译结束。用法如下。

[v-cloak]{  display:none; } <div v-cloak>{ { title }}</div>

1.22. vue 的 nextTick 方法的实现原理

  1. vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行
  2. microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕

1.24. v-if和v-for一起使用的弊端及解决办法

由于v-for的优先级比v-if高,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。

解决办法:

  1. 在v-for的外层或内层包裹一个元素来使用v-if
  2. 用computed处理

1.25. vue常用指令

  1. v-model 多用于表单元素实现双向数据绑定(同angular中的ng-model)
  2. v-bind 动态绑定 作用: 及时对页面的数据进行更改
  3. v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面
  4. v-for 格式: v-for="字段名 in(of) 数组json" 循环数组或json(同angular中的ng-repeat)
  5. v-show 显示内容 (同angular中的ng-show)
  6. v-hide 隐藏内容(同angular中的ng-hide)
  7. v-if 显示与隐藏 (dom元素的删除添加 同angular中的ng-if 默认值为false)
  8. v-else-if 必须和v-if连用
  9. v-else 必须和v-if连用 不能单独使用 否则报错 模板编译错误
  10. v-text 解析文本
  11. v-html 解析html标签
  12. v-bind:class 三种绑定方法 1、对象型 '{red:isred}' 2、三元型 'isred?"red":"blue"' 3、数组型 '[{red:"isred"},{blue:"isblue"}]'

1.27. vue-loader是什么?使用它的用途有哪些?

一、vue-loader作用:

解析和转换.vue文件。提取出其中的逻辑代码 script,样式代码style,以及HTML 模板template,再分别把他们交给对应的loader去处理

二、用途

js可以写es6,style样式可以写scss或less、template可以加jade等

三、

css-loader:加载由vue-loader提取出的CSS代码

vue-template-compiler:把vue-loader提取出的HTML模板编译成可执行的jacascript代码

1.28、vue运行过程

首先写的这些模板的语句,html根本就不能识别,我们通过编译的过程,可以进行依赖的收集,进行依赖收集以后,我们就把data中的数据模型和视图之间产生了绑定关系产生了依赖关系,那么以后模型发生变化的时候,我们就会通知这些依赖的地方让他们进行更新,
这就是我们执行编译的目的,这样就做到了模型驱动视图的变化。

我想知道Vuejs 2的这个文档是否等同于angular2的AOT:

是.使用vue-loader或vueify将完成与AOT相同的操作:

1.29、前端编译

编译是一个语言到另一个语言表达的转变,这里面不仅仅是功能上的应用,比如说从ES6转化到ES5,还可以给程序带来性能上的优化。

目前所有的编译器都是先将源代码Parser成AST(抽象语法树),然后对AST进行分析,在这个分析过程中进行各种优化。

代码压缩其实就是一个构建时优化,我们通常使用的压缩器就相当于编译器,它将原生的代码压缩成更简洁、更轻量的形式。常见的压缩器有Closuer Compiler、UglifyJS、Babili、Butternut。这里面UglifyJS不仅仅是一个压缩器,它自己还实现了一套JS Parser,拥有一套代码生成系统,等于是构建了一个完整的编译工具链。

Bundlers(打包工具)

早先的代码维护是非常不方便的,所以就出现一些打包工具,倡导开发者使用模块,使得代码能够更好的维护。但是另一个问题出现了,打包后代码变得难以压缩。这是因为早期的打包工具每一个模块都是包含在一个函数作用域内的,对于压缩器来说每一个作用域都是分离的,在进行优化的时候很多部分都无法完成。

针对上面的问题Rollup这类的工具就诞生了,只要是使用了ES模块,它就可以让所有的模块都放在同一个作用域中,这样压缩器就有用武之地。

AOT VS JIT

在使用模板引擎的时候,通常都会将模板直接写在JavaScript里面,模板字符串会被编译成JavaScript代码,这个过程一般都是在浏览器上进行的,但是这样就会增加用户的等待时间。

其实这个编译的过程完全可以放在构建时进行,由此AOT和JIT出现了。JIT在构建时并不编译而是直接将模板发送到浏览器里,当需要使用的时候再进行编译。AOT则是在构建的时候提前进行编译。

Angular、Vue、Glimmer就是一个典型构建时编译的例子,编写的时候是模板而当编译完成后发送出去的却是JavaScript代码。Angular使用AOT达成这一目标,Vue在使用Vue-loader时候默认就是这样执行的。

2、如何编译template 模板?

compile编译会有三个过程

(1)parse 根据正则进行字符串解析,得到指令、class、style等数据,形成语法树(AST)

(2)对 parse 生成的 AST 进行静态内容的优化,标记静态节点(和数据没有关系不需要每次都刷新的内容),标记静态根节点。

(3)generate 生成 render

生成render 的 generate 函数的输入也是 AST,它递归了 AST 树,为不同的 AST 节点创建了不同的内部调用方法,等待后面的调用。

3、浅谈vue的两种编译模式---七日打卡

前言

vue的编译模式分为:

1.完整版(同时包含运行时版本和编译器)
2.只包含运行时版本(用来创建Vue实例,渲染并处理虚拟DOM等的代码,基本上就是除去编译器的其它一切)。

编译器: 用来将模板字符串编译为JS渲染函数的代码

用法:

一、完整版

在页面中直接通过script标签引入的vue.js文件就是完整版的。完整版既可以使用render 函数,也可以使用 template 模板。

二、只包含运行时版本

我们自己手动通过 webpack 配置的打包环境中,通过 import Vue from ‘vue’导入的Vue就是运行时版本,也就是:vue.runtime.esm.js,这种情况下只能使用 render 函数来渲染组件。

由vue的两种编译模式,可以引出vue的两种构建版本的区别:

比较维度

完整版

运行时版

特点

包含compiler

没有compiler

视图

写在HTML中或者写在template中

写在render函数里用h来创建标签

CDN用法

vue.js

vue.runtime.js

webpack用法

需要配置alias

默认使用运行时版本

@vue/cli用法

需要额外配置

默认使用运行时版本

示例:

1、需要编译器的写法

new Vue({ template: '<div>{ { hi }}</div>' }) 复制代码

2、运行时写法:

new Vue({ render (h) { return h('div', this.hi) } }) 复制代码

当使用 vue-loader 或 vueify 的时候,*.vue 文件内部的模板会在构建时预编译成 JavaScript。你在最终打好的包里实际上是不需要编译器的,所以只用运行时版本即可。(因为运行时版本相比完整版体积要小大约 30%,所以应该尽可能使用这个版本。)

如果你仍然希望使用完整版,则需要在打包工具里配置一个别名:
vue.config.js中配置如下:

module.exports = { configureWebpack:{ resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' } } } }

————————————————

4、vue常用的修饰符?

1.事件修饰符

.prevent: 阻止默认事件(在指定的事件后进行默认事件的阻止);

.stop: 阻止单击事件冒泡(在指定事件后阻止事件冒泡,阻止指定在最内层事件的标签里);

.self: 当事件发生在该元素本身而不是子元素的时候会触发;

.capture: 事件侦听,事件发生的时候会调用

.once:控制元素指定的事件只执行一次

2. 表单元素修饰符

.number:能够强制的指定表单元素的内容数据类型是number

.trim:能够去除输入内容左右两边的空格

.lazy:只有标签的change事件执行后才会执行数据的双向绑定


5、那么问题来了 为什么官方要说 Vue 没有完全遵循 MVVM 思想呢?

严格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 提供了$refs 这个属性,让 Model 可以直接操作 View,违反了这一规定,所以说 Vue 没有完全遵循 MVVM。

6、vue的优点

轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb;

简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;

双向数据绑定:保留了angular的特点,在数据操作方面更为简单;

组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;

视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;

7、Vue 中的 key 到底有什么用?

key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快,但会产生一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。)

diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.

更准确 : 因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug。

更快速 : key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1),源码如下:


8、v-model 的原理?

我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

以 input 表单元素为例:

<input v-model='something'> 相当于 <input v-bind:value="something" v-on:input="something = $event.target.value"> 复制代码

如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:

父组件: <ModelChild v-model="message"></ModelChild>

子组件: <div>{ {value}}</div>

props:{

value: String

},

methods: {

test1(){

this.$emit('input', '小红')

}, },

9、vue 中使用了哪些设计模式

1.工厂模式 - 传入参数即可创建实例

虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode

2.单例模式 - 整个程序有且仅有一个实例

vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉

3.发布-订阅模式 (vue 事件机制)

4.观察者模式 (响应式数据原理)

5.装饰模式: (@装饰器的用法)

6.策略模式 策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略



10、nextTick 使用场景和原理

nextTick 中的回调是在下次 DOM 更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。主要思路就是采用微任务优先的方式调用异步方法去执行 nextTick 包装的方法



11、能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?

(1)hash 模式的实现原理

早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search':

https://www.word.com#search

hash 路由模式的实现主要是基于下面几个特性:

  • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;

hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;

  • 可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
  • 我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。
(2)history 模式的实现原理

HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:

window.history.pushState(null, null, path); window.history.replaceState(null, null, path);

history 路由模式的实现主要基于存在下面几个特性:

  • pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
  • 我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
  • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。


*12、vue与react对比

共同点:

a、都使用虚拟dom

b、提供了响应式和组件化的视图组件

c、注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。(vue-router、vuex、react-router、redux等等)

区别:

a、优化

b、HTML&CSS

c、构建工具

d、数据绑定

e、状态管理

f、路由

g、渲染性能

h、数据更新

i、开发模式及规模

j、使用场景

k、服务器端渲染(SSR)

l、扩展(高阶组件和mixin)


vue中的模板解析和渲染的核心就是:通过类似snabbdom的h()和patch()的函数,先将模板解析成vnode,如果是初次渲染,则通过patch(container,vnode)将vnode渲染至页面,如果是二次渲染,则通过patch(vnode,newVnode),先通过Diff算法比较原vnode和newVnode的差异,以最小的代价重新渲染页面。

1、优化:

react:

在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。

如要避免不必要的子组件的重渲染,

你需要在所有可能的地方使用 PureComponent,

或是手动实现 shouldComponentUpdate 方法。

react优化方法:

a、shouldComponentUpdate

b、immutable

可以看看:https://www.cnblogs.com/yangyangxxb/p/10104817.html

c、react-immutable-render-mixin,实现装饰器简化很多写法

d、无状态组件

e、高阶组件

vue:

在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,

所以系统能精确知晓哪个组件确实需要被重渲染。

可以理解为每一个组件都已经自动获得了 shouldComponentUpdate,并且没有上述的子树问题限制。

Vue 的这个特点使得开发者不再需要考虑此类优化,从而能够更好地专注于应用本身。


2、HTML&CSS

react:

在 React 中,一切都是 JavaScript。

在 React 中,所有的组件的渲染功能都依靠 JSX。

JSX 是使用 XML 语法编写 JavaScript 的一种语法糖。

3、数据绑定

vue:vue是双向绑定, Vue.js 最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统。所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据。这是通过设置属性访问器实现的。

Vue 的依赖追踪是【原理上不支持双向绑定,v-model 只是通过监听 DOM 事件实现的语法糖】

vue的依赖追踪是通过 Object.defineProperty 把data对象的属性全部转为 getter/setter来实现的;当改变数据的某个属性值时,会触发set函数,获取该属性值的时候会触发get函数,通过这个特性来实现改变数据时改变视图;也就是说只有当数据改变时才会触发视图的改变,反过来在操作视图时,只能通过DOM事件来改变数据,再由此来改变视图,以此来实现双向绑定

双向绑定是在同一个组件内,将数据和视图绑定起来,和父子组件之间的通信并无什么关联;

组件之间的通信采用单向数据流是为了组件间更好的解耦,在开发中可能有多个子组件依赖于父组件的某个数据,假如子组件可以修改父组件数据的话,一个子组件变化会引发所有依赖这个数据的子组件发生变化,所以vue不推荐子组件修改父组件的数据,直接修改props会抛出警告

思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听, 当属性变化的时候,响应式的更新对应的虚拟dom

vue:

Vue 通过建立一个虚拟 DOM 对真实 DOM 发生的变化保持追踪。

vue渲染的过程如下:

new Vue,执行初始化


挂载$mount方法,通过自定义Render方法、template、el等生成Render函数


通过Watcher监听数据的变化


当数据发生变化时,Render函数执行生成VNode对象


通过patch方法,对比新旧VNode对象,通过DOM Diff算法,添加、修改、删除真正的DOM元素

4、数据更新

react:

React 元素都是immutable 不可变的。当元素被创建之后,你是无法改变其内容或属性的。一个元素就好像是动画里的一帧,它代表应用界面在某一时间点的样子。

根据我们现阶段了解的有关 React 知识,更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render() 方法

vue:

数据直接修改,响应式。

5、使用场景

react:

a、构建一个大型应用项目

同时用Vue和React实现的简单应用程序,可能会让一个开发者潜意识中更加倾向于Vue。这是因为基于模板的应用程序第一眼看上去更加好理解,而且能很快跑起来。

但是这些好处引入的技术债会阻碍应用扩展到更大的规模。

模板容易出现很难注意到的运行时错误,同时也很难去测试,重构和分解。


相比之下,Javascript模板可以组织成具有很好的分解性和干(DRY)代码的组件,干代码的可重用性和可测试性更好。

Vue也有组件系统和渲染函数,但是React的渲染系统可配置性更强,还有诸如浅(shallow)渲染的特性,和React的测试工具结合起来使用,使代码的可测试性和可维护性更好。

与此同时,React的immutable应用状态可能写起来不够简洁,但它在大型应用中意义非凡,因为透明度和可测试性在大型项目中变得至关重要。

b、同时适用于Web端和原生APP

React Native是一个使用Javascript构建移动端原生应用程序(iOS,Android)的库。 它与React.js相同,只是不使用Web组件,而是使用原生组件。 如果你学过React.js,很快就能上手React Native,反之亦然。
它的意义在于,开发者只需要一套知识和工具就能开发Web应用和移动端原生应用。如果你想同时做Web端开发和移动端开发,React为你准备了一份大礼。
阿里的Weex也是一个跨平台UI项目,目前它以Vue为灵感,使用了许多相同的语法,同时计划在未来完全集成Vue,然而集成的时间和细节还不清楚。因为Vue将HTML模板作为它设计的核心部分,并且现有特性不支持自定义渲染,因此很难看出目前的Vue.js的跨平台能力能像React和React Native一样强大。

vue:

a、期待模板搭建应用

  • Vue应用的默认选项是把markup放在HTML文件中。数据绑定表达式采用的是和Angular相似的mustache语法,而指令(特殊的HTML属性)用来向模板添加功能。
    相比之下,React应用不使用模板,它要求开发者借助JSX在JavaScript中创建DOM。

b、期待应用尽可能的小和快

当应用程序的状态改变时,React和Vue都将构建一个虚拟DOM并同步到真实DOM中。 两者都有各自的方法优化这个过程。
Vue核心开发者提供了一个benchmark测试,可以看出Vue的渲染系统比React的更快。测试方法是10000个项目的列表渲染100次。

从实用的观点来看,这种benchmark只和边缘情况有关,大部分应用程序中不会经常进行这种操作,所以这不应该被视为一个重要的比较点。

但是,页面大小是与所有项目有关的,这方面Vue再次领先,它目前的版本压缩后只有25.6KB。

React要实现同样的功能,你需要React DOM(37.4KB)和React with Addon库(11.4KB),共计44.8KB,几乎是Vue的两倍大。

双倍的体积并不能带来双倍的功能。

扩展(高阶组件和mixin)

react可以通过高阶组件(Higher Order Components--HOC)来扩展,而vue需要通过mixins来扩展

React刚开始也有mixin的写法,通过React.createClass的api,不过现在很少用了。

Vue也不是不能实现高阶组件,只是特别麻烦,因为Vue对与组件的option做了各种处理, 想实现高阶组件就要知道每一个option是怎么处理的,然后正确的设置。


小结
总结一下,我们发现,


- Vue的优势包括:
- 模板和渲染函数的弹性选择
- 简单的语法及项目创建
- 更快的渲染速度和更小的体积


- Rea

  • 34
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nicky_hb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值