vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3组件初始化

Vue3组件渲染前的初始化过程

作者:F_Director

Vue3 中一个组件从创建到挂在,会经过3个重点步骤:创建组件实例,设置组件实例,创建并执行带副作用的渲染函数,本文将着重讲清 创建组件实例、设置组件实例 这两个过程都做了什么,这部分逻辑很简单,但你会从中学习到 Vue 优秀的实践技巧,需要的朋友可以参考下

Vue3 中一个组件从创建到挂在,会经过3个重点步骤:

本文将着重讲清 创建组件实例设置组件实例 这两个过程都做了什么。这部分逻辑很简单,但你会从中学习到 Vue 优秀的实践技巧。

一. 创建组件实例

这一步其实没太多可说的,只是实例化了一个初始状态的组件对象,并没有什么逻辑在里面,不过你千万别被这么庞大的信息量吓到,我们关注的是组件实例上有哪些常见的属性和方法,以及他们的维护方式。这能让我们对Vue组件信息有更多的了解。

下面是copy的源码:

function createComponentInstance (vnode, parent, suspense) {
  // 继承父组件实例上的 appContext,如果是根组件,则直接从根 vnode 中取。
  const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
  const instance = {
    // 组件唯一 id
    uid: uid++,
    // 组件 vnode
    vnode,
    // 父组件实例
    parent,
    // app 上下文
    appContext,
    // vnode 节点类型
    type: vnode.type,
    // 根组件实例
    root: null,
    // 新的组件 vnode
    next: null,
    // 子节点 vnode
    subTree: null,
    // 带副作用更新函数
    update: null,
    // 渲染函数
    render: null,
    // 渲染上下文代理
    proxy: null,
    // 带有 with 区块的渲染上下文代理
    withProxy: null,
    // 响应式相关对象
    effects: null,
    // 依赖注入相关
    provides: parent ? parent.provides : Object.create(appContext.provides),
    // 渲染代理的属性访问缓存
    accessCache: null,
    // 渲染缓存
    renderCache: [],
    // 渲染上下文
    ctx: EMPTY_OBJ,
    // data 数据
    data: EMPTY_OBJ,
    // props 数据
    props: EMPTY_OBJ,
    // 普通属性
    attrs: EMPTY_OBJ,
    // 插槽相关
    slots: EMPTY_OBJ,
    // 组件或者 DOM 的 ref 引用
    refs: EMPTY_OBJ,
    // setup 函数返回的响应式结果
    setupState: EMPTY_OBJ,
    // setup 函数上下文数据
    setupContext: null,
    // 注册的组件
    components: Object.create(appContext.components),
    // 注册的指令
    directives: Object.create(appContext.directives),
    // suspense 相关
    suspense,
    // suspense 异步依赖
    asyncDep: null,
    // suspense 异步依赖是否都已处理
    asyncResolved: false,
    // 是否挂载
    isMounted: false,
    // 是否卸载
    isUnmounted: false,
    // 是否激活
    isDeactivated: false,
    // 生命周期,before create
    bc: null,
    // 生命周期,created
    c: null,
    // 生命周期,before mount
    bm: null,
    // 生命周期,mounted
    m: null,
    // 生命周期,before update
    bu: null,
    // 生命周期,updated
    u: null,
    // 生命周期,unmounted
    um: null,
    // 生命周期,before unmount
    bum: null,
    // 生命周期, deactivated
    da: null,
    // 生命周期 activated
    a: null,
    // 生命周期 render triggered
    rtg: null,
    // 生命周期 render tracked
    rtc: null,
    // 生命周期 error captured
    ec: null,
    // 派发事件方法
    emit: null
  }
  // 初始化渲染上下文
  instance.ctx = { _: instance }
  // 初始化根组件指针
  instance.root = parent ? parent.root : instance
  // 初始化派发事件方法
  instance.emit = emit.bind(null, instance)
  return instance
}

二. 设置组件实例

这一步的重点在创建上下文代理,和执行处理setup函数的返回结果。

设置组件实例解决的问题,实际上都是组件渲染前所必须准备好的内容:

二.1 创建上下文代理

为什么需要上下文代理?可以举一个简单的例子:

<template>
  <p>{{ msg }}</p>
</template>
<script>
export default {
  data() {
    msg: 1
  }
}
</script>

为了方便维护,Vue 把组件中不同状态的数据存储到组件实例(instance)的不同的属性中,比如存储到 setupState、ctx、data、props 中。比如,msg 就被收拢到 instance.data中。

可是在模板渲染时,渲染逻辑不知道 msg 是来自 data 还是 props 亦或者是 setupState。供给用户的逻辑(比如, getCurrentInstance() api能在setup中访问到组件实例的变量和属性,但不推荐使用此api) 在访问 msg 时候也需要预先判断 msg 的溯源,然后才能决定是通过 instance.data 访问还是 instance.props 访问。这无疑增加了变量查询的负担。

为了方便使用,Vue 会直接访问渲染上下文 instance.ctx 中的属性,对instance.ctx做一层 proxy,对渲染上下文 instance.ctx 属性的访问和修改,代理到对 setupState、ctx、data、props 中的数据的访问和修改。

你需要学习的是访问组件变量时对数据集合的访问顺序,以及accessKey这样的柯里化思想。

值得注意的是,在set时,如果直接对props中的数据赋值,在非生产环境中就会产生一条警告。

创建accessKey的好处是节约了对不同上下文对象进行反复hasOwnPropety的性能损耗。

二.2 判断处理setup函数

这一步主要就是给setup注入参数,然后包了一层try...catch。然后处理setup的返回值。

为什么要包 try...catch?

笔者认为,setup 函数毕竟还是用户自己写的,难免出现逻辑错误,用 try...catch 不仅能防止程序被终止还能获取错误信息被Vue拦截处理。这是一种良好实践,在我们日常开发中,也应该考虑将暴露给用户书写的逻辑区域包一层try...catch来捕获不属于框架之内的错误。

二.3 完成组件实例设置

这一块就是在处理完setup返回结果后进行的,因为组件可能缺失render函数,用户的常用写法是template模板,且用户的编译环境可能并非是运行时编译(runtime-compiler)版本。所以这里需要统一转换为render函数的形式,以方便后面instance.update中方便拿到subTree。

这部分逻辑用一个脑图就能阐明:

以上就是Vue3组件渲染前的初始化过程的详细内容,更多关于Vue3组件初始化的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文