Vue 进阶系列(三)之Render函数原理及实现

栏目: JavaScript · 发布时间: 7年前

内容简介:根据第一篇文章介绍的响应式原理,如下图所示。在初始化阶段,本质上发生在在更新阶段,

根据第一篇文章介绍的响应式原理,如下图所示。

Vue 进阶系列(三)之Render函数原理及实现

在初始化阶段,本质上发生在 auto run 函数中,然后通过 render 函数生成 Virtual DOM , view 根据 Virtual DOM 生成 Actual DOM 。因为 render 函数依赖于页面上所有的数据 data ,并且这些数据是响应式的,所有的数据作为组件 render 函数的依赖。一旦这些数据有所改变,那么 render 函数会被重新调用。

在更新阶段, render 函数会重新调用并且返回一个新的 Virtual Dom ,新旧 Virtual DOM 之间会进行比较,把diff之后的最小改动应用到 Actual DOM 中。

Watcher负责收集依赖,清除依赖和通知依赖。在大型复杂的组件树结构下,由于采用了精确的依赖追踪系统,所以会避免组件的过度渲染。

Vue 进阶系列(三)之Render函数原理及实现

Actual DOM 和 Virtual DOM

Actual DOM 通过document.createElement('div')生成一个DOM节点。

document.createElement('div')

// 浏览器原生对象(开销大)
"[object HTMLDivElement]"
复制代码

Virtual DOM 通过 vm.$createElement('div')生成一个JS对象,VDOM对象有一个表示div的tag属性,有一个包含了所有可能特性的data属性,可能还有一个包含更多虚拟节点的children列表。

vm.$createElement('div')

// 纯JS对象(轻量)
{ tag: 'div', data: { attrs: {}, ...}, children: [] }
复制代码

因为Virtual DOM的渲染逻辑和Actual DOM解耦了,所以有能力运行在的非浏览器环境中,这就是为什么Virtual DOM出现之后混合开发开始流行的原因,React Native 和 Weex能够实现的原理就是这个。

JSX和Template

JSX和Template都是用于声明DOM和state之间关系的一种方式,在Vue中,Template是默认推荐的方式,但是也可以使用JSX来做更灵活的事。

JSX更加动态化,对于使用编程语言是很有帮助的,可以做任何事,但是动态化使得编译优化更加复杂和困难。

Template更加静态化并且对于表达式有更多约束,但是可以快速复用已经存在的模板,模板约束意味着可以在编译时做更多的性能优化,相对于JSX在编译时间上有着更多优势。

实例1:实现example组件

要求使用如下

<example :tags="['h1', 'h2', 'h3']"></example>
复制代码

要求输出如下

<div>
  <h1>0</h1>
  <h2>1</h2>
  <h3>2</h3>
</div>
复制代码

上面这个需求可以通过 render 函数来做,官方提供了 createElement 函数用来生成模板。 createElement('div', {}, [...]) 可接受的参数如下。

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一个 HTML 标签字符串,组件选项对象,或者
  // 解析上述任何一种的一个 async 异步函数。必需参数。
  'div',

  // {Object}
  // 一个包含模板相关属性的数据对象
  // 你可以在 template 中使用这些特性。可选参数。
  {
    
  },

  // {String | Array}
  // 子虚拟节点 (VNodes),由 `createElement()` 构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”。可选参数。
  [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)
复制代码

知道了用法之后,就可以在 render 中返回 createElement 生成的虚拟节点,外层是 div ,内层是三个锚点标题 h1 h2 h3 ,所以内层需要遍历下,使用两个 createElement 就可以完成了。

通常使用 h 作为 createElement 的别名,这是 Vue 的通用惯例,也是 JSX 的要求。

实现如下

<!--引用-->
<script src="../node_modules/vue/dist/vue.js"></script>

<!--定义template -->
<div id="app">
  <example :tags="['h1', 'h2', 'h3']"></example>
</div>

<script>
    // 定义example组件
    Vue.component('example', {
      props: ['tags'],
      render (h) {
        
        // 第二个参数是一个包含模板相关属性的数据对象,可选参数
        
        // 子虚拟节点(VNodes)参数可以传入字符串或者数字,
        // 通过createElement生成,可选参数
        return h('div', this.tags.map((tag, i) => h(tag, i)))
      }
    })
    
    // 实例化
    new Vue({ el: '#app' })
</script>
复制代码

实例2:实现动态的 <example> 组件

要求如下

  • 实现一个 Foo 组件渲染 <div>foo</div> ,实现一个 Bar 组件渲染 <div>bar</div> 。
  • 实现一个 <example> 组件,根据属性 ok 动态渲染 Foo 组件或者 Bar 组件。如果属性 ok 是 true ,那么最终的渲染应该是 <div>foo</div> 。
  • 实现一个按钮控制属性 ok ,通过这个属性让 <example> 在 Foo 或者 Bar 之间切换。

根据上面的要求,在模板中调用 <example> 组件,然后定义 <button> 组件,同时绑定属性 ok 。

实现如下

<!--引用-->
<script src="../node_modules/vue/dist/vue.js"></script>

<!--定义template -->
<div id="app">

  <!--绑定属性ok-->
  <example :ok="ok"></example>
  
  <!--绑定点击事件-->
  <button @click="ok = !ok">toggle</button>
</div>

<script>
    // 定义Foo
    const Foo = {
      render (h) {
        return h('div', 'foo')
      }
    }
    
    // 定义Bar
    const Bar = {
      render (h) {
        return h('div', 'bar')
      }
    }
    
    // 定义example组件
    // 根据ok属性动态切换
    Vue.component('example', {
      props: ['ok'],
      render (h) {
        return h(this.ok ? Foo : Bar)
      }
    })
    
    // 实例化
    new Vue({
      el: '#app',
      data: { ok: true }
    })
</script>
复制代码

实例3:实现组件

要求如下

  • 实现一个 withAvatarURL 函数,要求传入一个带有 url 属性的组件,返回一个接收 username 属性的高阶组件,这个高阶组件主要负责获取相应的头像URL。
  • 在API返回之前,高阶组件将占位符URL http://via.placeholder.com/200x200 传递给内部组件。

例子如下

const SmartAvatar = withAvatarURL(Avatar)

// 使用这个方式
<smart-avatar username="vuejs"></smart-avatar>

// 替换下面的方式
<avatar url="/path/to/image.png"></avatar>
复制代码

withAvatarURL 函数返回一个对象,接收 username 属性,在生命周期 created 获取头像URL。 Avatar 对象接收 src 属性, src 的内容从 withAvatarURL 中获取,然后展示在上。实例化的时候,传入新定义的组件名 SmartAvatar 。

实现如下

<!--引用-->
<script src="../node_modules/vue/dist/vue.js"></script>

<!--定义template-->
<div id="app">
  <smart-avatar username="vuejs"></smart-avatar>
</div>

<script>
    // 获取头像URL
    function fetchURL (username, cb) {
      setTimeout(() => {
        // 获取头像并回传
        cb('https://avatars3.githubusercontent.com/u/6128107?v=4&s=200')
      }, 500)
    }
    
    // 传递的InnerComponent
    const Avatar = {
      props: ['src'],
      template: `<img :src="src">`
    }
    
    function withAvatarURL (InnerComponent) {
      return {
        props: ['username'],
        inheritAttrs: false, // 2.4 only,组件将不会把未被注册的props呈现为普通的HTML属性
        data () {
          return { url: null }
        },
        created () {
          // 获取头像URL并回传给this.url
          fetchURL(this.username, url => {
            this.url = url
          })
        },
        render (h) {
          return h(InnerComponent, {
            attrs: this.$attrs, // 2.4 only,获取到没有使用的注册属性
            props: {
              src: this.url || 'http://via.placeholder.com/200x200'
            }
          })
        }
      }
    }
    
    const SmartAvatar = withAvatarURL(Avatar)
    
    // 实例化,新构造组件名为SmartAvatar或smart-avatar
    new Vue({
      el: '#app',
      components: { SmartAvatar }
    })
</script>

复制代码

本文内容参考自VUE作者尤大的付费视频

Vue官网之渲染函数 & JSX

交流

本人Github链接如下,欢迎各位Star

github.com/yygmind/blo…

我是木易杨,现在是网易高级前端工程师,目前维护了一个高级前端进阶群,欢迎加入。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!

Vue 进阶系列(三)之Render函数原理及实现

以上所述就是小编给大家介绍的《Vue 进阶系列(三)之Render函数原理及实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

增长的本质

增长的本质

凯萨·伊达尔戈 / 中信出版集团股份有限公司 / 2015-11-1 / CNY 49.00

这是一本物理学家撰写得跨经济学、社会、物理学等多学科的专著。在伊达尔戈之前,从来没有人以这样的方式研究经济增长。 什么是经济增长?为什么它只发生在历史长河的一些节点上?传统意义上的解释都强调了体制、地理、金融和心理因素。而塞萨尔?伊达尔戈告诉我们,想了解经济增长的本质,还需要走出社会科学的研究,在信息、关系网和复杂性这样的自然科学中寻求答案。为了认识经济发展,塞萨尔?伊达尔戈认为我们首先需要......一起来看看 《增长的本质》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具