Vue代码学习笔记(3)

本文深入探讨Vue的渲染函数RenderFunction,它是Vue响应式系统的重要组成部分。Vue将template编译成渲染函数,利用虚拟DOM进行高效更新。当数据变化时,响应式系统触发渲染函数生成新的虚拟DOM,通过差异比较更新真实DOM。文章还介绍了动态渲染组件的实现,包括函数式组件的使用。
摘要由CSDN通过智能技术生成

Render Function

渲染函数是组成完整的响应性渲染系统的另外一半,Vue的template实际上是通过渲染函数渲染出来的。

在Vue上下文中,当我们第一次渲染一个Vue应用时,会将template放到渲染函数进行编译。Render函数实际上是一个返回虚拟DOM的函数,然后Vue基于虚拟DOM生成真实DOM。

在后续的产生虚拟DOM的过程本质上是调用渲染函数,因为渲染函数和所有的data属性有依赖关系,在Vue中,这些data属性是具有响应性的,所有这些data属性会帮助这个组件的渲染函数收集依赖,如果这些依赖关系中的任何一个发生变化,将会再次调用渲染函数,它会返回一个新的虚拟DOM,新的虚拟DOM会和旧的虚拟DOM进行比较,最后,把最少量的更改应用到真实DOM中。

Render Function API

export default {
    render (h) {
        return h('div', {}, [...])
    }
}

 render函数接收一个参数h(hyper script超文本脚本),h需要三个参数,第一个参数是元素类型,第二个参数是数据对象(可选),第三个参数是一个数组表示子节点。它会返回这个虚拟DOM树的顶层元素,以便返回整个树。

虚拟DOM(Virtual DOM)

 通常情况下,调用真实DOM的开销十分的大,因为真实DOM具有非常多的属性,并且它在底层实现也十分的复杂。所以很多时候我们会说直接修改DOM是比较缓慢的。虚拟DOM的结构十分简单,因此它的开销会比真实DOM小很多

Virtual DOM

//tag标签名;data数据对象,如果没有数据对象可以忽略
//children可以拥有子虚拟节点,这样就构成了一个虚拟DOM树
{ tag: 'div', data:{ attrs:{}, ...}, children:[] }

虚拟DOM的本质是用轻量的Javascript数据格式来表示真实DOM在特定时间的外在表现,我们每次更新信息,先构造一个新的虚拟DOM,我们先计算差异,然后将这些更改应用到DOM上。

响应性和Render Function整合

 每一个组件都有一个渲染函数,它实际上包装在我们之前实现的autorun函数(笔记1)中,当数据发生变化的时候,观察者(wathcer)通过调用data属性中的getter收集依赖项,并监听setter,将收集到的改变通知到渲染函数,渲染函数再生成一个新的虚拟DOM。只要我们依赖的渲染属性发生变化,就会不断循环上述步骤。

每个组件都有自己的自动循环渲染,组件树有许多组件构成,每个组件都只负责自己的依赖。

由于你可以更改数据依赖关系,你的数据可以再任何地方发生改变,但是因为么个组件都只负责自己的依赖,再整个组件树中我们确切的知道哪些组件受到哪些数据的影响,所以,它有一个精确的依赖跟踪系统,不会造成过多的组件发生不必要的重新渲染。

相比于React自上而下的渲染模型,Vue可以说省去了一部分优化的工作。但是也付出了一部分将数据转换为getter和setter的开销。因此有了React上限高(优化做得好的话),下限低(优化不好),而Vue下限高(一部分优化工作自动完成),上限低(相比React开销更大一些)。但是在实际应用中,两者的差距其实很小,只有在极端情况下才会出现较大偏差。

动态渲染标签

<div id="app">
  <example :tags="['h1', 'h2', 'h3']"></example>
</div>

//定义一个新的Vue组件,组件就是自定义元素,
//第一个参数为组件名,第二个参数为可选的函数部分,第三个参数为template写组件内容
Vue.component('example', {
    //表示组件是纯函数式组件,只根据props计算渲染输出,本身不包含不改变仍和state
  functional: true,
  props: {
    tags: {
      type: Array,
      //自定义验证函数
      validator (arr) {return !!arr.length }
    }
  },
  //渲染函数
  render: (h, context) => {
    const tags = context.props.tags
    //hyper script,接受第一个参数为tagname标签名
    //第二个参数为数据对象(可选),第三个参数为子节点
    return h('div', context.data, tags.map((tag, index) => h(tag, index)))
  }
})

new Vue({ el: '#app' })

 更进一步,动态渲染组件

<div id="app">
  <example :ok="ok"></example>
  <button @click="ok = !ok"></button>
</div>

<script>
const Foo = {
  functional: true,
  render: h => h('div', 'foo')
}

const Bar = {
  functional: true,
  render: h => h('div', 'bar')
}

Vue.component('example', {
  functional: true,
  props: {
    ok: Boolean
  },
    //由于函数式组件中没有this,参数需要靠context来传递;
  render: (h, context) => h(context.props.ok ? Foo : Bar)
})

new Vue({
  el: '#app',
  data: {
    ok: true
  }
})
</script>

context 参数如下:

props:提供所有 prop 的对象
children: VNode 子节点的数组
slots: 一个函数,返回了包含所有插槽的对象
scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
parent:对父组件的引用
listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是data.on 的一个别名。
injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

 参考视频:尤雨溪教你写vue 高级vue教程 源码分析 中文字幕翻译完毕_哔哩哔哩_bilibili

参考github:GitHub - zhengguorong/vue-advanced-workshop: Vue.js workshop hosted by Evan You through Frontend Masters

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值