这是我参与第五届青训营伴学笔记创作活动的第十七天
MVVM模型?
MVVM,是Model-View-ViewModel的简写,其本质是MVC模型的升级版。其中 Model 代表数据模型,View 代表看到的页面,ViewModel是View和Model之间的桥梁,数据会绑定到ViewModel层并自动将数据渲染到页面中,视图变化的时候会通知ViewModel层更新数据。以前是通过操作DOM来更新视图,现在是数据驱动视图。
Vue的生命周期
每个 Vue 组件实例在创建后都会经过一系列的初始化过程,这个过程中会运行叫做生命周期钩子的函数,以便于用户在特定的阶段有机会添加自己的代码。
Vue 的生命周期可以分为8个阶段:创建前后、挂载前后、更新前后、销毁前后,以及一些特殊场景的生命周期。Vue 3 中还新增了是3个用于调试和服务端渲染的场景。
| Vue 2中的生命周期 | Vue 3中的生命周期 | 描述 |
|---|---|---|
beforeCreate | beforeCreate | 创建前,此时data和 methods的数据都还没有初始化 |
created | created | 创建后,data中有值,尚未挂载,可以进行一些Ajax请求 |
beforeMount | beforeMount | 挂载前,会找到虚拟DOM,编译成Render |
mounted | mounted | 挂载后,DOM已创建,可用于获取访问数据和DOM元素 |
beforeUpdate | beforeUpdate | 更新前,可用于获取更新前各种状态 |
updated | updated | 更新后,所有状态已是最新 |
beforeDestroy | beforeUnmount | 销毁前,可用于一些定时器或订阅的取消 |
destroyed | unmounted | 销毁后,可用于一些定时器或订阅的取消 |
activated | activated | keep-alive缓存的组件激活时 |
deactivated | deactivated | keep-alive缓存的组件停用时 |
errorCaptured | errorCaptured | 捕获一个来自子孙组件的错误时调用 |
| — | renderTracked | 调试钩子,响应式依赖被收集时调用 |
| — | renderTriggered | 调试钩子,响应式依赖被触发时调用 |
| — | serverPrefetch | 组件实例在服务器上被渲染前调用 |
父子组件的生命周期:
加载渲染阶段:父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted更新阶段:父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated销毁阶段:父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
针对Vue 3中的生命周期做如下补充:
vue 3中的生命周期图示:
Vue 3中新引入了组合API,在组合API中,生命周期钩子通过调用onXxx()函数显式的进行注册。这些生命周期钩子注册函数只能在setup()期间同步使用,因为它们依赖内部全局状态定位当前活动实例【即其setup()正在被调用的组件实例】。在没有当前活动实例的情况下调用它们将导致错误。
组合API中对应的生命周期钩子注册函数的名字就是生命周期选项的名字首字母大写并添加前缀on。二者对应关系如下:
- beforeCreate 和 created 没有对应的onXxx()函数,取而代之使用setup()函数
- berforeMount —> onBeforeMount
- mounted —> onMounted
- beforeUpdate —> onBeforeUpdate
- updated —> onUpdated
- beforeUnmount —> onBeforeUnmount
- unmounted —> onUnmounted
- activated —> onActivated
- deactivated —> onDeactivated
- errorCaptured —> onErrorCaptured
- renderTracked —> onRenderTracked
- renderTriggered —> onRenderTriggered
Vue.$nextTick
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
nextTick 是 Vue 提供的一个全局 API,由于 Vue 的异步更新策略,导致我们对数据修改后不会直接体现在 DOM 上,此时如果想要立即获取更新后的 DOM 状态,就需要借助该方法。
Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue 将开启一个异步更新队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入队列一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的 DOM 操作完成后才调用。
使用场景:
- 如果想要在修改数据后立刻得到更新后的
DOM结构,可以使用Vue.nextTick() - 在
created生命周期中进行DOM操作
Vue 实例挂载过程中发生了什么?
挂载过程指的是 app.mount()过程,这是一个初始化过程,整体上做了两件事情:初始化和建立更新机制。
初始化会创建组件实例、初始化组件状态、创建各种响应式数据。
建立更新机制这一步会立即执行一次组件的更新函数,这会首次执行组件渲染函数并执行patch将vnode 转换为 dom; 同时首次执行渲染函数会创建它内部响应式数据和组件更新函数之间的依赖关系,这使得以后数据发生变化时会执行对应的更新函数。
Vue 的模版编译原理
Vue 中有个独特的编译器模块,称为compiler,它的主要作用是将用户编写的template编译为js中可执行的render函数。
在Vue 中,编译器会先对template进行解析,这一步称为parse,结束之后得到一个JS对象,称之为抽象语法树AST;然后是对AST进行深加工的转换过程,这一步称为transform,最后将前面得到的AST生成JS代码,也就是render函数。
Vue 的响应式原理
-
Vue 2 中的数据响应式会根据数据类型做不同的处理。如果是对象,则通过
Object.defineProperty(obj,key,descriptor)拦截对象属性访问,当数据被访问或改变时,感知并作出反应;如果是数组,则通过覆盖数组原型的方法,扩展它的7个变更方法(push、pop、shift、unshift、splice、sort、reverse),使这些方法可以额外的做更新通知,从而做出响应。
缺点:- 初始化时的递归遍历会造成性能损失;
- 通知更新过程需要维护大量
dep实例和watcher实例,额外占用内存较多; - 新增或删除对象属性无法拦截,需要通过
Vue.set及delete这样的 API 才能生效; - 对于
ES6中新产生的Map、Set这些数据结构不支持。
-
Vue 3 中利用
ES6的Proxy机制代理需要响应化的数据。可以同时支持对象和数组,动态属性增、删都可以拦截,新增数据结构均支持,对象嵌套属性运行时递归,用到时才代理,也不需要维护特别多的依赖关系,性能取得很大进步。
虚拟DOM
-
概念:
虚拟DOM,顾名思义就是虚拟的DOM对象,它本身就是一个JS对象,只不过是通过不同的属性去描述一个视图结构。 -
虚拟DOM的好处:
(1) 性能提升
直接操作DOM是有限制的,一个真实元素上有很多属性,如果直接对其进行操作,同时会对很多额外的属性内容进行了操作,这是没有必要的。如果将这些操作转移到JS对象上,就会简单很多。另外,操作DOM的代价是比较昂贵的,频繁的操作DOM容易引起页面的重绘和回流。如果通过抽象VNode进行中间处理,可以有效减少直接操作DOM次数,从而减少页面的重绘和回流。
(2) 方便跨平台实现
同一VNode节点可以渲染成不同平台上对应的内容,比如:渲染在浏览器是DOM元素节点,渲染在Native(iOS、Android)变为对应的控件。Vue 3 中允许开发者基于VNode实现自定义渲染器(renderer),以便于针对不同平台进行渲染。 -
结构:
没有统一的标准,一般包括tag、props、children三项。
tag:必选。就是标签,也可以是组件,或者函数。
props:非必选。就是这个标签上的属性和方法。
children:非必选。就是这个标签的内容或者子节点。如果是文本节点就是字符串;如果有子节点就是数组。换句话说,如果判断children是字符串的话,就表示一定是文本节点,这个节点肯定没有子元素。