整理-vue

130 阅读5分钟

数据绑定

1、实现原理
通过Object.defineProperty对data中的每项数据劫持getter和setter,在getter中收集依赖,在setter中触发更新。
如果是对象,会逐层进行以上处理。
如果是数组,会遍历进行以上处理,并另外修改了7个数组的原型方法,在里面增加了设置监听、触发更新的逻辑。

2、设计模式
观察者模式 / 发布订阅模式

3、缺点:

  • 初始化时会对所有data进行绑定,不管有没有用到。而且对象和数组需要逐层遍历处理。
  • 对象新增删除属性没法识别(所以vue提供了set方法)。数组索引和长度变更也没法识别
  • 无法监听set、map、class这些类型的数据

4、vue3更新
换用proxy方式

  • 直接代理对象而不是属性,无需逐层遍历;对象属性的新增和删除以及数组操作无需再额外处理。
  • 拦截方式有很多种;
  • 速度更快;

组件通信

1、父传子:props、$children、$ref

2、子传父:$emit+@事件名、$parent、slot

3、祖先-》子孙:provide + inject

4、跨层级:$attrs + $listeners

5、任意组件间:Vuex、bus(new Vue())

虚拟DOM

通过js对象来描述真实DOM,主要有节点名称、属性信息、孩子节点...

在浏览器端操作dom太耗性能,虚拟dom就是用来优化这个问题的。将dom更新放在js对象上去处理,完后再转成真实dom交给浏览器绘制。

diff

新旧vnode进行比较

  • 不同节点类型。删除老节点,将newVNode转成真实dom渲染
  • oldVNode有孩子而newVNode没有,删除老孩子
  • oldVNode没孩子而newVNode有,新增新孩子
  • 都有孩子,设置首尾游标,先老开始、老结束、新开始、新结束进行两两比较。然后首游标往后走,尾游标往前走,老的遍历完了新的还有,则新增;新的遍历完了老的还有,则删除。

computed和watch的区别

computed主要用于在data某数据项的基础上进行一个转换或简单处理,它有缓存,只有依赖的数据项发生变化时才重新渲染。
watch监听某个数据项,一般用于有大型操作或异步操作的时候。

优化

1、列表项设置唯一的key属性,以便高效的更新虚拟dom

1)比较新老vnode是不是同一个节点
2)在diff算法中,老开始、老结束、新开始、新结束两两比较没有一对相同的时候,将新开始的key在老孩子key列表中查找,如果存在,就将其对应的dom节点移动至开始位置,不存在则直接渲染新开始在开始位置。

2、设置缓存keep-alive

3、纯展示性的数据可以通过Object.freeze()冻结

4、v-for和v-if尽量不要一起使用(v-for优先级更高)

5、不频繁切换的优先选v-if而不是v-show,减少dom数

6、图片懒加载,可使用插件vue-lazyload

7、路由懒加载
const Foo = () => import('./Foo.vue')

8、第三方插件按需引入

9、无限列表参考vue-virtual-scroll-list 和 vue-virtual-scroller

10、SSR

生命周期

初始化事件、$parent、$root...

beforeCreate\color{red}{beforeCreate}

初始化data、props、computed、watch...

created\color{red}{created} (有data)

将template或outerHtml转成render函数

beforeMount\color{red}{beforeMount} (有$el)

渲染dom

mounted\color{red}{mounted}

data数据更新前

beforeUpdate\color{red}{beforeUpdate}

data数据更新后并渲染完

updated\color{red}{updated}

销毁前

beforeDestroy\color{red}{beforeDestroy}

销毁后(解绑、移除绑定事件、销毁子实例...)

destroyed\color{red}{destroyed}

还有activated\color{red}{activated}deactivated\color{red}{deactivated},组件被包裹在keep-alive里面时会被激活

还有errorCaptured\color{red}{errorCaptured},捕获来自子孙组件的错误

生命周期执行顺序:(P-父组件、C-子组件)

1、初始化:
P beforeCreate
P created
P beforeMount
C beforeCreate
C created
C beforeMount
C mounted
P mounted

2、父更新传给子组件的值:
P beforeUpdate
C beforeUpdate
C updated
P updated

3、子更新传给父组件的值:
P beforeUpdate
C beforeUpdate
C updated
P updated

4、子组件销毁:
C beforeDestroy
C destroyed

5、子组件销毁:
P beforeDestroy
C beforeDestroy
C destroyed
P destroyed

6、keep-alive

A列表页、B详情页(url上通过id来区分每个页面),A 设置了keep-alive

A\color{red}{-》A}
-》全局守卫beforeEach
-》A路由独享钩子beforeEnter
-》A组件路由钩子beforeRouteEnter
-》 全局守卫beforeResolve
-》全局守卫afterEach
-》 (A-beforeCreate,A-created,A-beforeMount,A-mounted,A-activated)
-》 A组件路由钩子beforeRouteEnter的next回调

AB\color{red}{A-》B}
-》A组件路由钩子beforeRouteLeave
-》全局守卫beforeEach
-》B路由独享钩子beforeEnter
-》B组件路由钩子beforeRouteEnter
-》全局守卫beforeResolve
-》全局守卫afterEach
-》(B-beforeCreate,B-created,B-beforeMount,B-mounted,A-deactivated)

B也设置keep-alive的差别:
(B-beforeCreate,B-created,B-beforeMount, A-deactivated, B-mounted, B-activated)

-》 B组件路由钩子beforeRouteEnter的next回调

BA\color{red}{B-》A}
-》B组件路由钩子beforeRouteLeave
-》全局守卫beforeEach
-》A路由独享钩子beforeEnter
-》A组件路由钩子beforeRouteEnter
-》 全局守卫beforeResolve
-》全局守卫afterEach
-》(B-beforeDestroy,B-destroyed,A-activated)

B也设置keep-alive的差别:
(B-deactivated,B-beforeUpdate,A-activated, B-updated)

-》 A组件路由钩子beforeRouteEnter的next回调

Bid1Bid2\color{red}{B id1 -》 B id2}
-》全局守卫beforeEach
-》B组件路由钩子beforeRouteUpdate
-》全局守卫beforeResolve
-》全局守卫afterEach
-》(B-beforeUpdate,B-updated)

keep-alive取消缓存

离开时销毁
deactivated() {this.$destroy();},

keep-alive实现原理

  • 第一步:获取keep-alive包裹着的第一个子组件对象及其组件名;
  • 第二步:根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(VNode),否则执行第三步;
  • 第三步:根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在this.keys中的位置(更新key的位置是实现LRU置换策略的关键),否则执行第四步;
  • 第四步:在this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max的设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key)。
  • 第五步:最后并且很重要,将该组件实例的keepAlive属性值设置为true。

juejin.cn/post/684490…

vuex实现原理

vuex是专为vue应用程序实现状态管理的,采用集中式存储管理应用所有组件的状态。

vue插件都会通过install方法进行注册,调用的时候如下

Vue.use(Vuex)

use内部就调用了install方法。
install方法通过mixin的方式将vuex在beforeCreate阶段注入到vue根实例上,将store作为它的data,实现响应式,并能逐级传到子组件进行使用(this.$store)

vuex.vuejs.org/zh/ segmentfault.com/a/119000001…

vue用的虚拟dom,怎么做到事件绑定的?

  • 原生:addEventListener
<div @click="fn()"></div>
  • 组件:$on、$emit、$off
<my-component @click.native="fn" @click="fn1"></my- component>

blog.csdn.net/ichangebaob… zhuanlan.zhihu.com/p/68832362

vue组件的开发过程

数据驱动视图原理

每个组件实例都有相应的watcher实例 - 渲染组件的过程,会把属性记录为依赖 - 当我们操纵一个数据时,依赖项的setter会被调用,从而通知watcher重新计算,从而致使与之相关联的组件得以更新

juejin.cn/post/684490…

data中有些数据没用到,那更新的时候vue是怎么做到不渲染的?

从以上“数据驱动视图原理”可知,没用到的数据不会存在watcher上,不存在的自然也不会去更新了

欢迎指正!后续会继续补充~