Wang's blog Wang's blog
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Wang Mings

跟随大神,成为大神!
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Python爬虫

  • 前端教程

    • 团队规范

    • Project

    • JS

    • NodeJS

    • Vue

      • 个人理解Vue和React区别
      • Vue高级用法
      • Vue2.x源码分析 - 框架结构
      • Vue2.x源码分析 - 模版编译以及挂载
      • 虚拟dom算法库 - snabbdom
      • Vue2.x源码分析 - Virtual DOM实现
        • [#](#基本数据结构) 基本数据结构
        • [#](#源码笔记) 源码笔记
      • Vue2.x源码分析 - 事件系统
      • Vue2.x源码分析 - 组件系统
      • Vue2.x源码分析 - Vue.nextTick
      • Vue2.x源码分析 - inject provide
      • Vue2.x源码分析 - 解析Template模板
      • Vue2.x源码分析 - 响应式原理
      • Vue2.x源码分析 - v-model
      • Vue CLI3 插件系统原理
      • Vue Loader v15 源码解析
      • Vue3 设计思想
      • Vue3 RFCS导读
      • Vue3 响应式原理 - Ref Reactive Effect源码分析
      • Vue3 API 源码解析
      • 为何Vue3 Proxy 更快
      • Vue核心原理 - 笔记
    • React

    • 效率工具

    • 读书笔记

  • 教程
  • 前端教程
  • Vue
wangmings
2022-07-19
目录

Vue2.x源码分析 - Virtual DOM实现

# # Vue2.x源码分析 - Virtual DOM实现

Vue Virtual DOM实现是基于snabbdom库改造的,所以理解snabbdom源码有助于我们更好的看懂Vue源码(实际上Vue源码中有非常多相似的地方,比如diff算法、hooks应用等)。snabbdom源码更纯粹,也更容易理解,具体可以看笔者另外一篇文章:虚拟dom算法库 - snabbdom。以下我们对比看下Virtual DOM在Vue源码中的应用。

# # 基本数据结构

    VNode {
      tag: string | void; // 标签tag
      data: VNodeData | void; // 数据,包括事件监听、class、style等
      children: ?Array<VNode>; // 子VNodes
      text: string | void; // 文本
      elm: Node | void; // VNode对应的真实DOM
      ns: string | void;
      key: string | number | void; // 唯一key
    
      // 组件
      context: Component | void; // rendered in this component's scope
      functionalContext: Component | void; // only for functional component root nodes
      componentOptions: VNodeComponentOptions | void;
      componentInstance: Component | void; // component instance
    
      parent: VNode | void; // component placeholder node
      raw: boolean; // contains raw HTML? (server only)
      isStatic: boolean; // 标记为静态
      isRootInsert: boolean; // necessary for enter transition check
      isComment: boolean; // empty comment placeholder?
      isCloned: boolean; // is a cloned node?
      isOnce: boolean; // is a v-once node?
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    VNodeData {
      key?: string | number;
      slot?: string;
      ref?: string;
      is?: string;
      pre?: boolean;
      tag?: string;
      staticClass?: string;
      class?: any;
      staticStyle?: { [key: string]: any };
      style?: Array<Object> | Object;
      normalizedStyle?: Object;
      props?: { [key: string]: any };
      attrs?: { [key: string]: string };
      domProps?: { [key: string]: any };
      hook?: { [key: string]: Function };
      on?: ?{ [key: string]: Function | Array<Function> };
      nativeOn?: { [key: string]: Function | Array<Function> };
      transition?: Object;
      show?: boolean; // marker for v-show
      inlineTemplate?: {
        render: Function;
        staticRenderFns: Array<Function>;
      };
      directives?: Array<VNodeDirective>;
      keepAlive?: boolean;
      scopedSlots?: { [key: string]: Function };
      model?: {
        value: any;
        callback: Function;
      };
    };
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# # 源码笔记

  • src/core/vdom/vnode.js: 对应snabbdom/vnode.ts源码

    • 定义VNode数据结构,并有创建VNode实例基础方法。
  • src/core/vdom/create-element.js: 对应snabbdom/h.ts

    • 应用层新建一个VNode实例,包含一些数据预处理。
    • vue在snabbdom基础上,增加了Component System。
      // 应用层说明
      createElement(
        // {String | Object | Function}
        'div',
      
        // {Object}
        // 一个与模板中属性对应的数据对象。可选。
        {
          // (详情见下面)
        },
      
        // {String | Array}
        // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
        // 也可以使用字符串来生成“文本虚拟节点”。可选。
        [
          '先写一些文字',
          createElement('h1', '一则头条'),
          createElement(MyComponent, {
            props: {
              someProp: 'foobar'
            }
          })
        ]
      )
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
      // 源码解释
      export function createElement (
        context: Component,
        tag: any,
        data: any,
        children: any,
        normalizationType: any,
        alwaysNormalize: boolean
      ): VNode | Array<VNode> {
        // data可省略,会判断参数类型,data非object会被认为省略
        if (Array.isArray(data) || isPrimitive(data)) {
          normalizationType = children
          children = data
          data = undefined
        }
        if (isTrue(alwaysNormalize)) {
          normalizationType = ALWAYS_NORMALIZE
        }
        return _createElement(context, tag, data, children, normalizationType)
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • platforms/web/runtime/node-ops.js: 对应snabbdom/htmldomapi.ts源码

    • 封装DOM API
  • src/core/vdom/patch.js: 对应snabbdom/snabbdom.ts

    • vdom diff核心内容。diff算法与snabbdom大同小异
    • vue patch.js里面的createPatchFunction方法,等同于snabbdom.ts的init方法,会默认注入一些模块。
  • platforms/web/runtime/modules文件夹: 对应snabbdom/modules文件夹

    • 注入默认模块,在dom合适的时间调用这些模块的hook
    • attrs.js: 根据VNode.data.attrs值。设置html的属性,能反应到html元素上
    • dom-props.js 根据VNode.data.domProps值。设置dom的属性
    • class.js: 根据VNode.data.class值。 设置html className。Vue官方API (opens new window) (opens new window)
    • style.js: 根据VNode.data.style值。设置html style。Vue官方API (opens new window) (opens new window)
    • event.js: 根据VNode.data.on值。设置html event事件。Vue官方API (opens new window) (opens new window)
    • src/core/vdom/modules/directives.js 根据VNode.data.directives值。绑定指令(指令hook如bind、update回调应用也在这)。Vue官方API (opens new window) (opens new window)
    • src/core/vdom/modules/ref.js 根据VNode.data.ref/refInFor值。注册ref,使得通过vm.$refs[key]能拿到VNode.elm(VNode一般不会暴露给外面)
      {
        // 与 `v-bind:class` 的 API 相同,
        // 接受一个字符串、对象或字符串和对象组成的数组
        'class': {
          foo: true,
          bar: false
        },
        // 与 `v-bind:style` 的 API 相同,
        // 接受一个字符串、对象,或对象组成的数组
        style: {
          color: 'red',
          fontSize: '14px'
        },
        // 普通的 HTML 特性
        attrs: {
          id: 'foo'
        },
        // 组件 prop
        props: {
          myProp: 'bar'
        },
        // DOM 属性
        domProps: {
          innerHTML: 'baz'
        },
        // 事件监听器在 `on` 属性内,
        // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
        // 需要在处理函数中手动检查 keyCode。
        on: {
          click: this.clickHandler
        },
        // 仅用于组件,用于监听原生事件,而不是组件内部使用
        // `vm.$emit` 触发的事件。
        nativeOn: {
          click: this.nativeClickHandler
        },
        // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
        // 赋值,因为 Vue 已经自动为你进行了同步。
        directives: [
          {
            name: 'my-custom-directive',
            value: '2',
            expression: '1 + 1',
            arg: 'foo',
            modifiers: {
              bar: true
            }
          }
        ],
        // 作用域插槽的格式为
        // { name: props => VNode | Array<VNode> }
        scopedSlots: {
          default: props => createElement('span', props.text)
        },
        // 如果组件是其它组件的子组件,需为插槽指定名称
        slot: 'name-of-slot',
        // 其它特殊顶层属性
        key: 'myKey',
        ref: 'myRef',
        // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
        // 那么 `$refs.myRef` 会变成一个数组。
        refInFor: true
      }
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
编辑 (opens new window)
虚拟dom算法库 - snabbdom
Vue2.x源码分析 - 事件系统

← 虚拟dom算法库 - snabbdom Vue2.x源码分析 - 事件系统→

最近更新
01
theme-vdoing-blog博客静态编译问题
09-16
02
搜索引擎
07-19
03
友情链接
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2022 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式