Vuex有哪几种属性?
有五种,分别是 State、 Getter、Mutation 、Action、 Module
- state => 基本数据(数据源存放地)
- getters => 从基本数据派生出来的数据
- mutations => 提交更改数据的方法,同步
- actions => 像一个装饰器,包裹mutations,使之可以异步。
- modules => 模块化Vuex
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 中 computed 和 watch 有什么区别?
计算属性 computed:
(1)**支持缓存**,只有依赖数据发生变化时,才会重新进行计算函数;
(2)计算属性内**不支持异步操作**;
(3)计算属性的函数中**都有一个 get**(默认具有,获取计算属性)**和 set**(手动添加,设置计算属性)方法;
(4)计算属性是自动监听依赖值的变化,从而动态返回内容。
侦听属性 watch:
(1)**不支持缓存**,只要数据发生变化,就会执行侦听函数;
(2)侦听属性内**支持异步操作**;
(3)侦听属性的值**可以是一个对象,接收 handler 回调,deep,immediate 三个属性**;
(3)监听是一个过程,在监听的值变化时,可以触发一个回调,并**做一些其他事情**。
vue初始化页面闪动问题
使用vue开发时,在vue初始化之前,由于div是不归vue管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于{{message}}的字样,虽然一般情况下这个时间很短暂,但是还是有必要让解决这个问题的。
首先:在css里加上以下代码:
[v-cloak] { display: none;}
如果没有彻底解决问题,则在根元素加上style="display: none;" :style="{display: 'block'}"
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).
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重新执行
// 观察者模式
// 渲染组件时 会创建watcher
class 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、计算属性是watcher
new 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()
}
}
})
}
// 模拟数据获取,触发getter
obj.name = 'poetries'
// 一个属性一个dep,一个属性可以对应多个watcher(一个属性可以在任何组件中使用、在多个组件中使用)
// 一个dep 对应多个watcher
// 一个watcher 对应多个dep (一个视图对应多个属性)
// dep 和 watcher是多对多的关系
参考 前端进阶面试题详细解答
diff算法
时间复杂度: 个树的完全 diff
算法是一个时间复杂度为 O(n*3)
,vue进行优化转化成 O(n)
。
理解:
-
最小量更新,
key
很重要。这个可以是这个节点的唯一标识,告诉diff
算法,在更改前后它们是同一个DOM节点- 扩展
v-for
为什么要有key
,没有key
会暴力复用,举例子的话随便说一个比如移动节点或者增加节点(修改DOM),加key
只会移动减少操作DOM。
- 扩展
- 只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的。
- 只进行同层比较,不会进行跨层比较。
diff算法的优化策略:四种命中查找,四个指针
- 旧前与新前(先比开头,后插入和删除节点的这种情况)
- 旧后与新后(比结尾,前插入或删除的情况)
- 旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
- 旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
$nextTick 是什么?
Vue 实现响应式并不是在数据发生后立即更新 DOM,使用 vm.$nextTick
是在下次 DOM 更新循环结束之后立即执行延迟回调。在修改数据之后使用,则可以在回调中获取更新后的 DOM。
谈谈你对MVVM的理解
为什么要有这些模式,目的:职责划分、分层(将Model
层、View
层进行分类)借鉴后端思想,对于前端而已,就是如何将数据同步到页面上
MVC模式 代表:Backbone
+ underscore
+ jquery
- 传统的
MVC
指的是,用户操作会请求服务端路由,路由会调用对应的控制器来处理,控制器会获取数据。将结果返回给前端,页面重新渲染 -
MVVM
:传统的前端会将数据手动渲染到页面上,MVVM
模式不需要用户收到操作dom
元素,将数据绑定到viewModel
层上,会自动将数据渲染到页面中,视图变化会通知viewModel
层 更新数据。ViewModel
就是我们MVVM
模式中的桥梁
MVVM模式 映射关系的简化,隐藏了controller
MVVM
是Model-View-ViewModel
缩写,也就是把MVC
中的Controller
演变成ViewModel
。Model
层代表数据模型,View
代表UI组件,ViewModel
是View
和Model
层的桥梁,数据会绑定到viewModel
层并自动将数据渲染到页面中,视图变化的时候会通知viewModel
层更新数据。
-
Model
: 代表数据模型,也可以在Model
中定义数据修改和操作的业务逻辑。我们可以把Model
称为数据层,因为它仅仅关注数据本身,不关心任何行为 -
View
: 用户操作界面。当ViewModel
对Model
进行更新的时候,会通过数据绑定更新到View
-
ViewModel
: 业务逻辑层,View
需要什么数据,ViewModel
要提供这个数据;View
有某些操作,ViewModel
就要响应这些操作,所以可以说它是Model for View
.
总结 : MVVM
模式简化了界面与业务的依赖,解决了数据频繁更新。MVVM
在使用当中,利用双向绑定技术,使得 Model
变化时,ViewModel
会自动更新,而 ViewModel
变化时,View
也会自动变化。
我们以下通过一个 Vue
实例来说明 MVVM
的具体实现
{{message}}
// ViewModel 层
var app = new Vue({
el: '#app',
data: { // 用于描述视图状态
message: 'Hello Vue!',
},
methods: { // 用于描述视图行为
showMessage(){
let vm = this;
alert(vm.message);
}
},
created(){
let vm = this;
// Ajax 获取 Model 层的数据
ajax({
url: '/your/server/data/api',
success(res){
vm.message = res;
}
});
}
})
// Model 层
{
"url": "/your/server/data/api",
"res": {
"success": true,
"name": "test",
"domain": "www.baidu.com"
}
}
如何理解Vue中模板编译原理
Vue
的编译过程就是将template
转化为render
函数的过程
-
解析生成AST树 将
template
模板转化成AST
语法树,使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理 -
标记优化 对静态语法做静态标记
markup
(静态节点如div
下有p
标签内容不会变化)diff
来做优化 静态节点跳过diff
操作-
Vue
的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM
也不会变化。那么优化过程就是深度遍历AST
树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用 - 等待后续节点更新,如果是静态的,不会在比较
children
了
-
-
代码生成 编译的最后一步是将优化后的
AST
树转换为可执行的代码
回答范例
思路
- 引入
vue
编译器概念 - 说明编译器的必要性
- 阐述编译器工作流程
回答范例
-
Vue
中有个独特的编译器模块,称为compiler
,它的主要作用是将用户编写的template
编译为js
中可执行的render
函数。 - 之所以需要这个编译过程是为了便于前端能高效的编写视图模板。相比而言,我们还是更愿意用
HTML
来编写视图,直观且高效。手写render
函数不仅效率底下,而且失去了编译期的优化能力。 - 在
Vue
中编译器会先对template
进行解析,这一步称为parse
,结束之后会得到一个JS
对象,我们称为 抽象语法树AST ,然后是对AST
进行深加工的转换过程,这一步成为transform
,最后将前面得到的AST
生成为JS
代码,也就是render
函数
可能的追问
-
Vue
中编译器何时执行?
在
new Vue()
之后。Vue
会调用_init
函数进行初始化,也就是这里的 init
过程,它会初始化生命周期、事件、props
、methods
、data
、computed
与watch
等。其中最重要的是通过Object.defineProperty
设置setter
与getter
函数,用来实现「响应式」以及「依赖收集」
- 初始化之后调用
$mount
会挂载组件,如果是运行时编译,即不存在render function
但是存在template
的情况,需要进行「编译」步骤 -
compile
编译可以分成parse
、optimize
与generate
三个阶段,最终需要得到render function
-
React
有没有编译器?
react
使用babel
将JSX
语法解析
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net