Vue基于snabbdom做了哪些事

更新日期: 2019-06-22 阅读: 3k 标签: vue

前言

之前有简单看过 vue patch 部分的源码,了解了是基于 Snabbdom 库实现的。最近想详细了解下 Vue 处理 vnode patch 的整个过程,想知道它在 Snabbdom 之上做了哪些事情?所以带着这个问题,写了这篇文章来记录。


Snabbdom 做了哪些事?

A virtual DOM library with focus on simplicity, modularity, powerful features and performance. (一个虚拟 DOM 库,专注于简单性,模块化,强大的功能和性能。)

Snabbdom 核心代码大约只有 200 行。它提供了模块化架构,具有丰富的功能,可通过自定义模块进行扩展。在了解核心 patch 前,需要先了解 snabbdom 的模块化架构思想。

modules

在节点的生命周期里做一些任务,来扩展 Snabbdom ,就可以称之为模块。
Snabbdom 在 patch 的过程中会注入很多钩子(hooks)。模块实现就是基于这些钩子,钩子可以理解为 vnode 节点的生命周期。

比如 eventlisteners 模块:

  1. create: 节点创建时添加时间监听(addEventListener)
  2. update: 节点更新时移除老节点事件,添加新事件
  3. destroy: 节点销毁时,移除老节点事件

Hooks

Hooks 是一种挂载 vnode 生命周期的方式。软件开发领域有类似这样的设计思想,比如版本管理工具 git。Snabbdom 中的模块就是基于此来实现扩展的。当然,也可以传递 hook 配置,实现在 vnode 生命周期里做一些事(这种只针对单个节点,而模块针对所有节点),例如:

h('div.row', {
  key: movie.rank,
  hook: {
    insert: vnode => {
      movie.elmHeight = vnode.elm.offsetHeight
    }
  }
})

Vue 中的指令就是基于此实现的。具体的生命周期可以参考官方文档:https://github.com/snabbdom/snabbdom#hooks

patch(oldVnode, newVnode) 函数

Snabbdom 的核心部分,patch 函数由 snabbdom.init 创建,初始化时提供绑定 hook 的模块。
如果 oldVnode 是具有父节点的 DOM 元素,则 newVnode 将变为 DOM 节点,并且传递的元素将被创建的 DOM 节点替换。如果传递的是个 vnode 实例,则会比对此节点和递归对比它的子节点,并做 dom 更新。这一块网上的源码解析文章也比较多,这里就不多介绍了。

snabbdom/h

h 函数用来创建 vnode 实例,类比 Vue render 函数中的参数 h,Vue 中扩展了入参。比如第一个参数 tag,Vue 可以是对象或函数;第二个参数 data,Vue 增加了一些特有的功能比如:scopedSlots, slot, directive, ref... 等。


Vue 相比 Snabbdom,patch 过程中有哪些不一样的地方?

组件

  • Vue 中有组件的概念,组件同样是可以嵌套的,所有就存在父组件和子组件。父组件 render 函数执行后,子组件并没有被初始化,仅仅是创建了一个特殊的 vnode 节点,而这个节点上会绑定一些 hooks: init, prepatch, insert, destroy。当父组件 patch 执行的过程中,遇到子组件的话,会调用 hook: init 方法,此方法中才会初始化组件实例。后续 insert hook 触发时,相应的会调用组件 mounted 生命周期钩子。这一块源码可以查看 https://github.com/vuejs/vue/blob/dev/src/core/vdom/create-component.js#L36-L97
  • setScope 也是在 patch 执行过程中调用的,它会往 dom 节点上增加 ${scopedId} 属性,用来实现 scoped 样式功能。
  • patch 过程中会更新 ref 到上下文实例上。
  • 因为有组件概念,所以 Vue 中创建 vnode 第一个参数 tag 可以是对象或函数,Snabbdom 上只能是字符串。

生命周期

  • Vue 去掉了 pre 和 post 2 个钩子,这 2 个钩子分别用来在 patch 执行前后调用的
  • Vue 中 init 钩子只对组件节点生效,而 Snabbdom 中所有节点都可定义 init hooks
  • Vue 新增了 active 钩子,用来处理 <keep-alive> 嵌套 <transition> 组件产生的边界问题

静态节点优化

Vue 会对静态节点做性能优化。

  • 编译时:Vue compiler 会对 template 中的静态节点特殊处理:创建静态节点的方法会存放在 staticRenderFns 属性里。除了纯 html 语法产生的静态节点外,v-once, v-pre 也会产生静态节点。
  • 运行时:当组件初始化时,静态节点会被保存到实例 _staticTrees 数组中。组件更新重新触发 render 时,不会重新创建 vnode 节点,直接使用之前已有的静态节点。进而不会触发 patchVnode 操作。

hydrate

对于 Vue 服务端渲染输出的 html,客户端初始化挂载节点时,会把已经渲染好的 dom 和 vnode 一一绑定,以达到同构的效果,hydrate 函数就做了这个任务。


patch 整体流程

我整理了一份比较详细的 patch 流程图。
一些细节没有写入,比如:updateChildren diff 过程、异步组件、keep-alive、hydrate...



Vue 中有哪些功能是基于 patch 中的 Hooks 实现的?

directives

Vue 2 中的指令就是基于 hooks 实现的,从 directive 的生命周期来看:

  1. bind:节点上绑定新的指令时调用 -> hooks: create, update
  2. inserted:节点上绑定新的指令,若 hooks == create ,则在节点被 insert 时调用;若 hooks == update,则直接调用
  3. update:节点上已存在指令时调用 -> hooks: update
  4. componentUpdated: 节点上已存在指令,且在 hooks: postpatch 触发时调用,此时指令所在组件的 VNode 及其子 VNode 已全部更新。
  5. unbind:节点被销毁时调用 -> hooks: destroy

ref

vnode 创建、更新、销毁时都会更新所在组件上的 $refs 属性。
patch 过程中处理了子组件为空时,父组件指向的 ref 为 undefined 的边界情况

style

对于动态绑定 style,Vue 还会智能的往不支持的属性前加厂商前缀。

const normalize = cached(function(prop) {
  emptyStyle = emptyStyle || document.createElement('div').style
  prop = camelize(prop)
  if (prop !== 'filter' && prop in emptyStyle) {
    return prop
  }
  const capName = prop.charAt(0).toUpperCase() + prop.slice(1)
  for (let i = 0; i < vendorNames.length; i++) {
    const name = vendorNames[i] + capName
    if (name in emptyStyle) {
      return name
    }
  }
})

Vue 还支持 value 数组写法。比如 {display: ["-webkit-box", "-ms-flexbox", "flex"]}

Snabbdom 中对 style 增加了 hooks: remove,这个是为了实现节点被移除时的过渡动效,而 Vue 对过渡动效的处理封装在了 <transition> 组件中。


写在后面

其实 Vue 中还有很多功能都依赖 vnode 节点 patch 的过程,transition 的功能也比较多,这里暂时不深入了。
由于本人理解有限,文中如有任何问题欢迎留言指正。


参考

来自:http://shuaizhang.top/2019/06/20/Vue基于snabbdom做了哪些事/


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/3816

相关推荐

基于vue移动端UI框架有哪些?vue移动端UI框架总汇

vue现在使用的人越来越多了,这篇文章主要整理一些比较优秀的移动端ui框架,推荐给大家,例如:mint UI、vux、vonic、vant、cube-ui、Muse-ui、Vue-Carbon、YDUI等

vue watch监听对象的使用_实现首次不触发、深度监听

vue中的watch是一个对象,所以一定要当成对象来用,它有键-值组成,其中键就是你要监控的那个数据。这篇文章介绍:vue如何实现首次不触发watch,vue如何实现数据的深度监听?

组件化的概念/特性/优点,Vue组件的使用

Web 中的组件其实就是页面组成的一部分,具有高内聚性,低耦合度,互冲突等特点,有利于提高开发效率,方便重复使用,简化调试步骤等。vue 中的组件是一个自定义标签形式,扩展原生的html元素,封装可重用的代码。

vue扩展——使用vue插件添加全局方法属性

在使用vue构建一些大型项目的时候,会发现许多组件会共用到一些函数或常量,我们需要把它提取出来,每次需要的时候调用一次就可以了,避免每个组件都重新写再一篇的麻烦。

Vue的href动态拼接绑定

:href前面要加“:”或者v-bind: 字符串要用单引号包住 加上了冒号是为了动态绑定数据,等号后面可以写变量。 如果不使用冒号,等号后面就可以写字符串等原始类型数据。这是就无法进行动态绑定数据了

vue引用js文件的多种方式

vue引用js文件的多种方式,这里以为引入jquery为例。js引入文件方式包括: vue-cli webpack全局引入jquery、vue组件引用外部js的方法、单vue页面引用内部js方法

Vue的理解:Vue.js新手入门指南

从一个从未接触过除HTML+CSS+JavaScript+JQuery以外的前端技术的人到现在可以独立使用Vue.js以及各种附属的UI库来开发项目,我总结了一些知识和经验想与大家分享。

vue与后台交互ajax数据

Vue.js是一套构建用户界面的渐进式的前端框架。 vueJS与后台交互数据的方法我所了解的有以下几种

Vue.js最佳实践:五招让你成为Vue.js大师

本文面向对象是有一定Vue.js编程经验的开发者。如果有人需要Vue.js入门系列的文章可以在评论区告诉我,有空就给你们写。对大部分人来说,掌握Vue.js基本的几个API后就已经能够正常地开发前端网站

Vuejs讲解之:响应式、过渡效果、过渡状态

Vue是一套构建用户界面的JS渐进式框架。 Vue 只关注视图层, 采用自底向上增量开发的设计。讲解js高级之响应式、过渡效果、过渡状态。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!