【Vue2】3. 简单组件渲染

204 阅读3分钟

概述

Vue 开发的一个基本思路就组件化,一切皆可通过组件实现。那Vue是怎么实现组件化渲染的呢?本文主要解决以下问题:

  1. 如何渲染一个简单自定义组件(包含 SFC编译概述)

  2. 父子组件的如何进行关联

实现代码为 简单组件渲染,实现和 Vue 的差异点见最后面的附录


DEMO展示

入口文件

import App from './App.vue'
import Vue from './src/vue'

new Vue({
  el: '#app',
  render(h) {
    return h(App)
  },
})

App.vue文件:

<template>
  <div>
    {{message}}
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      message: '我是一个组件'
    }
  }
}
</script>

初始效果为:


响应式更新:


原理解析

渲染一个简单组件

使用 SFC 形式编写组件,经过 vue-template-loader 编译解析后,生成一个对象,会将 SFC 中的 template模板编译生成一个 render 函数。编译之后会使用大量的快捷方式引用。上述 DEMO 中的App.vue编译之后的模板为:

function render () {
  var _vm = this
  var _h = _vm.$createElement
  var _c = _vm._self._c || _h
  return _c("div", [_vm._v("\n  " + _vm._s(_vm.message) + "\n")])
}

所以渲染组件第一步是,需要提供这些快捷访问方式,在Vue中定义为 renderHelpers。

其次渲染组件和渲染文本区别在于渲染时,传递的是一个对象或者是一个元素标签,故渲染是需要针对不同数据做不同的适配。适配过程为:

  1. createElement 里面对传入参数做判断,如果字符串类型,则渲染文本组件,否则渲染为组件

  2. 渲染组件时,先创建一个 VNode实例,tag 为自行拼接的一个字符串(渲染文本组件时tag为 div p 等html的tag)

  3. 在由 VNode生成真实Dom时,会先判断是否组件,针对组件会使用hook方式使用 Vue 的构造函数生成组件的实例,进行挂载。

整个流程为:


每一个组件其实都对应了一个 Vue 实例,所以所有组件内都可以共享全局的一些mixin,原型上面的方法、属性等。

组件VNode 会在 Patch过程中 通过hook方式创建相关的Vue实例,不太理解为什么需要使用 hook 方式渲染组件?

父子组件关联

每一个组件都是一个Vue实例,那父组件如何感知子组件存在呢?

在Vue中 还是借助了 JS 单线程结合全局变量这种思路实现的。


通过记录当前执行的组件,在创建子组件实例会会引用,从而创建连接,效果:


总结

可以简单理解Vue中一切皆组件,初始化时,是创建一个父组件实例(通过 new Vue 实现),然后可以递归生成子组件的实例(也是通过 new Vue 实现)。所以可以借助Vue的原型,Vue一些静态属性共享很多方法、属性等。

附录

代码实现和 Vue 实现源码有些出入,本着学习Vue思路,有些细节能简化简化。Vue中通过 Vue.extend 方法创建了一个继承自 Vue 的子类来渲染子组件,通过将初始化子组件所需要的参数都扩展到构造函数的options属性中。而自己实现的还是直接复用 Vue 的构造函数来创建子类。