vue的核心
- 组件化
- 数据驱动
虚拟dom
- 本质是一个js对象
- 2.0才有
虚拟DOM原理
虚拟DOM实现的原理主要包含三部分:
- 使用JavaScript对象模拟真实的DOM,对真实的DOM进行抽象;
- diff算法计算两棵虚拟DOM树的差异;
- patch算法将两个虚拟DOM对象差异应用到真正的DOM上。 或者这样说:
- 用JS对象模拟DOM(虚拟DOM)
- 把此虚拟DOM转成真实DOM并插入页面中(render)
- 如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff)
- 把差异对象应用到真正的DOM树上(patch) 优点:
- 保证性能下限;
- 无需手动操作DOM;
- 跨平台 缺点:
- 尺寸: 更多的功能意味着更多的代码;
- 内存: 虚拟DOM需要在内存中的维护一份DOM的副本。在DOM更新速度和使用内存空间之间取得平衡;
- 不是适合所有情况:如果虚拟DOM大量更改,这是合适的。但是少量的,频繁的更新的话,虚拟DOM将会花费更多的时间处理计算的工作。所以,如果一个DOM节点相对较少页面,用虚拟DOM,它实际上有可能会更慢 如何设计一个虚拟DOM:
- 遍历newVdom的节点,找到他在oldVdom的位置,找到就移动对应的dom元素,没找到就说明是新节点,则创建一个节点插入。遍历完后如果oldVdom中还有没有处理过的节点,说明这些节点在newVdom中被删除了,删除他们即可。
虚拟Dom做了什么
- 将真实的dom转化为虚拟dom
- 更新的时候做对比
虚拟dom如何提升vue的渲染效率
- 局部更新(节点数据)
- 将直接操作dom的地方拿到两个js对象中做比较,找出差异项去更新
虚拟dom的diff算法中的patch方法
- 初始化: patch(container, vnode)
- 更新: update(vnode, newVode)
const createElement = vnode => {
let tag = vnode.tag;
let attrs = vnode.attrs || {};
let childrens = vnode.children || [];
if (!tag) {
return null;
}
let elem = document.createElement(tag);
for (let attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
elem.setAttribute(attrName, attrs[attrName]);
}
}
childrens.forEach(item => {
elem.appendChild(!item.children ? item : createElement(item));
});
}
const updateElement = (vnode, newVnode) => {
let children = vnode.children || [];
let newChildren = newVnode.children || [];
children.forEach((childrenVnode, index) => {
let newChildrenVnode = newChildren[index];
if (childrenVnode === newChildrenVnode) {
updateElement(childrenVnode, newChildrenVnode);
} else {
// 替换
// replace(childrenVnode, newChildrenVnode);
}
})
}
proxy应用
- 通过拦截set,可以做数据的校验;
- 通过拦截apply,可以统计函数调用次数、实现普通函数和构造函数的简单处理;
- 通过拦截set、get,可以实现简单断言;
- 通过拦截get,可以实现自定义报错信息、实现隐藏属性;
// 数据校验
let handler = {
set: function(target, prop, value){
if(prop==='age'){
if(value > 200){
throw new RangeError('超过200岁了!')
}
}
}
}
let person = new Proxy({},handler)
person.age = 300 //超过200岁了!
// 统计函数调用次数
function orginFunction () {}
let handler = {
apply: function(target, thisObj, argumentsList){
console.log('xxx')
return target.apply(thisObj, argumentsList)
}
}
let proxyFunction = new Proxy(orginFunction, handler)
模板编译到DOM整个流程
Vue模板编译原理(生成渲染函数):
- 第一步解析器:将模板字符串转换成AST对象;
- 第二步优化器:对AST进行静态节点标记,主要用来做虚拟DOM的渲染优化;
- 第三步代码生成器:使用AST生成render函数代码字符串;
渲染过程:
- 调用编译comlile函数,生成render函数字符串;
- 通过调用new Watcher()函数,监听数据的变化,当数据发生变化时,然后更新render函数生成vnode;
- 最后调用patch()方法,对比新旧节点vnode对象,通过DOM的diff算法,将vnode生成真正的DOM,去更新视图;
什么是AST
- AST是对源代码的抽象语法结构的树状表现形式,在不同的场景下,会有不同的解析器将源码解析成抽象语法树。
vuex的原理
核心原理:
- vuex本质是一个对象;
- vuex对象有两个属性,一个是install方法,一个是store类;
- install方法的作用是将同一个store实例挂载到所有组件上,用Vue(vuex)方法挂载,其实就是调用了Vue.mixin,在所有组件的beforeCreate生命周期注入了设置this.$store这样一个对象;
- store这个类拥有commit,dispatch这些方法,store类里将用户传入的state包装成data,作为new Vue的参数,从而实现了state的响应式; 分析:
- 本质上vuex内部其实是new Vue实现,vuex中的state放到vue的data中的$$state 中,getters是通过new Vue的computed实现,getters的取值本质上是将所有的方法放到computed中,getters取值的时候取的就是computed
Vue的双向数据绑定原理
vue采用数据劫持结合发布者-订阅者的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤:
- 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加setter和getter。这样的话给这个对象的某个值赋值,就会触发setter,就能监听到数据变化。
- compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图;
- Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
a、在自身实例化时往属性订阅器(dep)里面添加自己;
b、自身必须有一个update()方法;
c、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调; - MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化->视图更新;视图交互变化(input)->数据model变更的双向数据绑定效果。 简单的理解有四个核心:
- 一个监听器observer,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;
- 一个订阅器Dep,用来收集订阅者。,对监听器Observer和订阅者Watcher进行统一管理;
- 一个订阅者Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图;
- 一个解析器Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化。
理解xss、csrf、ddos原理及避免方式
-
XSS(Cross-Site Scripting)跨站脚本攻击: 是一种代码注入攻击。攻击者在目标网站上注入恶意的代码,当被攻击者登录网站时就会执行这些恶意代码,这些脚本可以读取cookie、session、tokeb,或者其他敏感的网站信息,对用户进行钓鱼欺诈,甚至发起蠕虫攻击等;
-
CSRF(Cross-Site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击者网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
-
XSS避免方式:
1、url参数使用encodeURIComponent方法转义;
2、尽量不要用InnerHtml插入HTML内容;
3、使用特殊符号、标签转义符号。 -
CSRF避免方式:
1、添加验证码;
2、使用token:服务端给用户生成一个token,加密后传递给用户;用户在提交申请时需要携带这个token服务端验证token是否正确。 -
DDoS又叫分布式拒绝服务,全称Distributed Denial of Service,其原理就是利用大量的请求造成资源过载,导致服务不可用。
-
DDoS避免方式:
1、限制单IP请求频率;
2、防火墙等防护设置禁止ICMP包等;
3、检查特权端口的开放。
VUE2跟VUE3有什么不一样
VUE3.0的目标是让vue的核心变得更小、更快、更强大;因此增加了一些特性:
-
监测机制的改变:vue3将带来基于代理Proxy的observer的实现,提供了全语言覆盖式的反应性跟踪。这消除了vue2当中基于Object.defineProperty的实现所带来的的很多限制。
-
生命周期的不同:如果要想在页面中使用生命周期函数,以往vue2的操作是直接在页面中写入生命周期,而vue3是需要去引用的,这就是为什么3能够将代码压缩到更低的原因;
-
默认项目目录结构的不同:vue3移除了配置文件目录,config 和 build 文件夹,移除了 static 文件夹,新增 public 文件夹,并且 index.html 移动到 public 中,在 src 文件夹中新增了 views 文件夹,用于分类视图组件和公共组件;
-
模板:模板方便没有多大的变更,只改变了作用域和插槽,vue2的机制导致作用域插槽变了,父组件会重新渲染,而vue3把作用域插槽改成函数的方式,这样只影响了子组件的渲染,提升了渲染的性能;
-
对象式的组件声名方式:vue2的组件是通过声名的方式传入一系列的option,和TypeScript的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。vue3修改了组件的声明方式,改成了类式的写法,这样使得和TypeScript的结合变得很容易;
-
此外,vue的源码也改用了TypeScript来写。其实当代码的功能复杂之后,必须有一个静态的类型系统来做一些辅助管理。
-
其他方面的更改:
a、支持自定义渲染器,从而使得weex可以通过自定义渲染器方式来扩展,而不是直接fork源码来改动方式;
b、支持Fragment(多个根节点)和Protal(在dom其他部分渲染组件内容)组件,针对一些特殊的场景做了处理;
c、基于treeshaking优化,提供了更多的内置功能;
Proxy与Object.defineProperty的优劣对比
Proxy的优势如下:
- Proxy可以直接监听对象而非属性;
- Proxy可以直接监听数组的变化;
- Proxy有多大13中拦截方式,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的;
- Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能对象属性直接修改; Object.defineProperty的优势:
- 兼容性好,支持IE9,而Proxy存在浏览器兼容问题,而且无法用polyfil磨平;
v-model的原理
在vue项目中主要使用v-model指令在表单input、textarea、select等元素上创建双向数据绑定,其实v-model本质上不过是语法糖[便捷写法],v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text和textarea元素使用value属性和input事件;
- checkbox和radio使用checked属性和change事件;
- select字段将value作为prop并将传个作为事件。
computed与watch
- watch:属性监听,更多的是观察的作用,类似于某些数据的监听回调,每当监听的数据发生改变时都会执行回调进行后续操作;
- computed:计算属性,依赖其他属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值才会重新计算computed的值; 适用场景:
- 当我们需要进行数值计算,并且依赖于其他数据时,应该使用computed,因为可以利用computed的缓存属性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化执行异步操作或者开销较大的时,应该使用watch,使用watch选项允许我们执行异步操作,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。
- computed: 当一个属性受多个属性影响时使用,例: 购物车商品结算功能
- watch:当一条数据影响多条数据的时候使用,例: 搜索数据
watch 和 watchEffect有什么差异 - 1、watch需要依赖,watchEffect 不需要依赖,且会自动跟踪依赖
- 2、watch不会立即执行,watchEffect会立即执行,在首次加载
keep-alive
- keep-alive是vue内置的一个组件,可以使被包含的组件保留状态,避免重新渲染;
- 一般结合路由和动态组件使用,用于缓存组件;
- 提供include和exclude属性,两者都支持字符串或正则表达式,include表示只有名称匹配的组件会被缓存,exclude表示任何名称匹配的组件都不会被缓存,其中exclude的优先级比include高;
- 对应两个钩子函数activated和deactivated,当组件被激活时,触发钩子函数activated,当组件被移除时,触发钩子函数deactivated。