面试为何会考察原理?
- 之前然知其所以然
- 了解原理,才能应用的更好
- 大厂造轮子
面试中如何考察?以何种方式?
- 考察重点,而不是考察细节。掌握好2/8原则
- 和使用相关联的原理,如vdom、模板渲染
- 整体流程是否去全面?热门技术是否有深度?
Vue原理包括哪些?
一、 组件化和MVVM
概念介绍:组件化基础
面试官不会这么直接的问,他会问你什么是MVVM模型?
这道题这么回答:
第一:历史渊源: “很久以前”就有组件化
asp,jsp php 的最早的做前端的时候,就已经存在组件化了,
而且node.js 中也存在组件化,下面用nodejs 做的一个项目举例(布局如上,代码如下)
一块一块分别的导入一堆数据(或组件)
第二:Vue和React做了一个创新 数据驱动视图(MVVM setState)
- 在上一点中的 前端1.0 甚至是2.0 的时候,因为只是传统的组件化,只是静态渲染,更新还要依赖DOM,所以那个时候jQuery很火!!!
- 而 Vue ,React 引入了数据驱动视图的概念后,大大提高了效率。
Vue 2 采用的是 MVVM,React 采用的是 setState
数据驱动视图的概念这里就不过多赘述了。
算了还是一讲:就是我们想改什么数据,不用再去操作DOM,而是直接去Vue 或者React里面改数据就好了,框架会自动的将数据渲染到页面上!也就是因为如此,我们开发的时候,更多的时间可以去关注业务数据和业务逻辑!
第三:然后再开始将 MVVM 是什么?
画个图,一番解释就满分!
- 首先M 是Model ,V是View ,VM 是ViewModel
- 通过中间的 VM层,我们在Model 发生变化的时候立刻执行到View 层
- 同理,我们在View 层触发的事件的时候,我们也可以通过VM层直接去修改数据
二、 响应式原理
上题中,有一个问题:
就是当组件的data 的数据一旦改变,立刻就会触发视图的个更新。
下面从3分方面来回答这个问题链接
第一: 核心API-Object.defineProperty
这个方法就是在一个对象上定义一个新的属性,或者改变一个对象现有的属性,并且返回这个对象。里面有两个字段 set
,get
。顾名思义,set
是去设置属性的值,而get
就是获取属性的值。
举个栗子:
// 在对象中添加一个属性与存取描述符的示例
var bValue;
var o = {};
Object.defineProperty(o, "b", {
get : function(){
console.log('监听正在获取b')
return bValue;
},
set : function(newValue){
console.log('监听正在设置b')
bValue = newValue;
},
enumerable : true,
configurable : true
});
o.b = 38;
console.log(o.b)
最终打印
监听正在设置b
监听正在获取b
38
从在上述栗子中,可以看到当我们对 o.b 赋值38的时候,就会调用set函数,这时候给bValue赋值,之后我们就可以通过o.b来获取这个值,这时候,get函数被调用。
第二: 如何实现响应式,代码演示
① 监听对象(深度)
如果只是监听第一层级的属性(比如下面data 里面的name 和 age )很容易就实现了,
但是如果属性是一个对象呢(比如data 里面的 info),这时候怎么监听info.address
呢?能不能判断一下是不是对象呢?
图二中 observer()
函数就是干这个事的。如果传入这个函数的target(代表要被监听元素)是一个对象,那他就return
这个对象的属性,
就像他发现图一的
info
是一个对象,那么返回的就是address
这个对象或者属性!!!
图三中object defineReactive(target,key,value)
这个函数传入的这个target 就只是我们监听的这个对象,而不是这个对象的内层属性。
这时候就需要我们在 这个函数里面进行判断,如果传入不是对象或者数组,就返回他本身,那么就返回他所指向的内容。
就像传入的是 图一中 data里面的 name属性,那么返回的就是name 属性
如果传入的是 data里面的info 这个对象,那么就便利这个对象,找到他的所有的属性,然后就找到了adress ,然后再去对他进行这样一个检查,最终得到所有的属性。
② 监听数组
data(){
nums:[10,20,30]
}
监听数组我们按照上面监听对象的方式尝试,发现并不能成功!
这里我们需要 去重新定义数组的原型:我这里就大致的解释一番!
第三: Object.defineProperty的一些缺点(Vue3.O启用Proxy)这个了解一下就好了
- 深度监听,需要递归到底,一次性计算量大
如果data里面的info 是一个层级很省的对象的时候,那我们岂不是要一直递归(上文中的observe方法)
- 无法监听新增属性/删除属性(Vue.set Vue.delete)
- 无法原生监听数组,需要特殊处理(需要修改数组原型)
第四: Proxy兼容性问题
- Proxy兼容性不好,且无法polyfill(IE11,很多安卓浏览器的内核都不支持)
- Vue2.x还会存在一段时间,所以导学
- Vue3.O相关知识,下一章讲,这里只是先提一下
三、虚拟DOM(Virtual DOM )和diff
第一:背景了解
- DOM 操作非常耗费性能,耗时
- 相对来说js 的执行是比较快的,在用框架之前,是用jQuery的可以自行去控制DOM操作的时机手动调整
- Vue React 是数据驱动视图,如何有效控制DOM操作?
他们采用虚拟DOM
- 虚拟DOM是React提出来的,提出之后的几年里得到了大量的普及,像Vue 从2.0 之后就用了虚拟DOM
- 用控制变量的方法,
业务复杂度+执行速度=DOM操作最终速度
- 业务复杂度这个我们根本就没发控制,需求那么多,哪个保留哪个删去都不合适?
- 那我们能不能提高DOM操作的执行速度呢?
- 我们把更多的计算转移为JS计算,JS的计算快啊(特别是Chrome的V8引擎发布之后),和DOM速度就不是一个数量级的!
那虚拟DOM是咋做的呢?
第二:虚拟DOM的原理
它先用JS来模拟DOM结构,然后去进行一系列的计算,计算出最小的变更,然后再去操作DOM(这样就最大程度上避免一些无用功的操作),这就虚拟DOM的原理!
第三:原理整明白了,到你咋做的呢?
1. 用JS模拟DOM结构 (暂时整一个 JS模仿的DOM结构:左侧DOM结构,右侧JS的DOM结构)
大厂很可能会要求: 可以把以下DOM机构用JS表示以下吗?仅供参考,其实表达方式因人而异
2.Vue,React参考snabbdom ,通过snabbdom 学习 虚拟DOM
虚拟DOM用到了diff算法,首先来简单了解一下diff算法
- 同样diff算法也不是虚拟DOM独创的,
- diff即对比的意思,是一个广泛的概念,比如linux的diff命令,git diff 等等
- 两个js 对象也可以做diff,引入一个GitHub的 jiff库
- 两棵树可做diff,ru 虚拟DOM的diff ,就是DOM树和JS树做对比
两棵树的diff 的时间吗复杂度是 O(n^3)
- 遍历 treel
- 遍历tree2
- 排序
1000个节点,要计算1亿次啊,算法不可用
React 大牛想:这不行啊,然后把树的diff 的时间复杂度优化到了O(n)
- 只比较同一层级,不跨级比较
- tag不相同,则直接删掉重建不再深度比较
- tag和key,两者都相同,则认为是相同节点,不再深度比较
四、 组件渲染和更新过程
面试官不会直接问什么是模板边缘,但是会通过“组件渲染和更新过程”考察?
Vue template complier
将模板编译为 render 函数- 执行
render
函数生产vnode
(vnode
其实是虚拟DOM源码
中的一个重要关键词,理解为虚拟DOM的总节点)
① 前置知识:with语法 (只需要了解就好)
- 正常我们访问一个对象的某一个属性
console.log(obj.a)
- 但是使用
with
语法
with(obj){
console.log(a)
}
这两种方式结果是一样的,只是当我们在用with 语法的时候,需要传入这个对象,然后在{}就可以直接使用它的 某一个属性。
② Vue template 为什么要编译呢?
因为我们在 <template>
标签里面写的内容,其实并不是html,而是Vue定义的一种格式。
你想想。正常html 里面可以写 指令,插值,js表达式吗,能实现判断和循环吗?
但是JS可以呀,由此,Vue的模板一定转换为某种JS代码,即使编译模板!
③ 组件 渲染/更新 过程
初次渲染过程
- 解析模板为 render函数(在开发环境或者在浏览器完成,vue-loader)
- 触发响应式,监听data属性 getter setter
- 执行render函数,生产vnode,patch(elem,vnode)
更新过程
- 修改data,触发setter(判断此前在getter中已经被监听)
- 重新执行render 函数,生成newVnode
- patch(vnode,newVnode),path的diff算法会帮我算出这个新旧DOM节点的最小差异
五、 渲染过程
整个流程介绍
- 黄色区域是我们的render函数,这个时候模板已经编译完成了(Vue template里面的 => render函数)
- render 函数生产了虚拟DOM的树(绿色的树),同时render函数会 Touch(触发)Data(紫色区域的getter);
- 触发的时候同时也会收集依赖(就我在模板里面触发了哪个变量的getter,我就会把哪个变量Watch(蓝色区域)起来)
- 然后我们再去setter的时候,先进行Notify 的判断,判断setter的这个变量是不是我们曾经收集过依赖的那些值,如果是就去通知那些我们收集的依赖,然后再去触发 trigger re-render(重新渲染)
异步渲染
- ① 首先回顾一下 $nextTick和Vue渲染的方式
- ② 汇总data的修改,然后一次性更新视图,减少DOM操作次数,提高性能
6. 前端路由
前端路由的原理,这里讲的不是具体哪一个框架的路由原理,而是通用的前端路由原理。
Vue-router :
2种模式,h5 history和hash模式
hash 模式特点
通过 window.onhashchange
事件
(代码展示) 点击按钮,用js 去改变url值
- hash变化会触发网页跳转,即浏览器的前进、后退
- hash变化不会刷新页面,SPA必需的特点
- hash永远不会提交到server端(自生自灭)
H5 history 模式特点
(代码展示) history.pushState和window.onpopstate
总结:
- hash模式 –
window.onhashchange
- H5 history模式 –
history.pushState和window.onpopstate
Vue原理总结
大厂面试必考原理:所以我觉得需要把所有原理的重点讲清楚,然后热门技术挖一挖深度!
首先Vue原理的三大块是:响应式,虚拟DOM和模板编译
- 渲染过程 是对以上三个的总结
- 组件化 是对Vue整个的事件理念的一个通讲
- 前端路由 是对Vue router 的一个讲解
由于本人水平有限,如有描述不准确的地方请给我留言,欢迎交流~