Vue设计与实现 笔记 组件

87 阅读2分钟

组件的实现原理

引入组件实例概念

  • state:组件自身的状态数据,即data
  • isMounted: 一个布尔值,用来表示组件是否被挂载
  • subTree: 存储组件的渲染函数返回的虚拟DOM,即组件的子树(subTree)

组件调用生命周期原理:

function mountComponent(vnode, container, anchor) {
  const componentOptions = vnode.type
  // 从组件选项对象中取得组件的生命周期函数
  const { render, data, beforeCreate, created, beforeMount, mounted, beforeUpdate, updated } = componentOptions

  // 在这里调用 beforeCreate 钩子
  beforeCreate && beforeCreate()

  const state = reactive(data())

  const instance = {
    state,
    props: shallowReactive(props),
    isMounted: false,
    subTree: null
  }
  vnode.component = instance
  // 创建渲染上下文对象,本质上是组件的代理
  const renderContext = new Proxy(instance, {
    get(t, k, r) {
      // 取得组件自身状态与props数据
      const { state, props } = t
      // 先尝试读取自身状态数据
      if(state  && k in state) {
        return state[k]
      } else if(k in props) {
        return props[k]
      } else {
        console.log('不存在')
      }
    },
    set(t, k, v, r) {
      const { state, props } = t
      if(state  && k in state) {
        state[k] = v
      } else if(k in props) {
        console.warn('props 应该是只读的')
      } else {
        console.log('不存在')
      }
    }
  })


  // 在这里调用 created 钩子
  created && created.call(state)

  effect(() => {
    const subTree = render.call(state, state)
    if(!instance.isMounted) {
      // 调用 beforeMount 钩子
      beforeMount && beforeMount.call(state)
      patch(null, subTree, container, anchor)
      instance.isMounted = true
      // 在这里调用 Mounted 钩子
      mounted && momunted.call(state)
    } else {
      // 在这里调用 beforeUpdate 钩子
      beforeUpdate && beforeUpdate.call(state)
      patch(istance.subTree, subTree, container, anchor)
      // 在这里调用 updated 钩子
      updated && updated.call(state)
    }
    instance.subTree = subTree
  }, { scheduler: queueJob })
}

渲染上下文对象: 用于拦截数据状态的读取和设置操作,每当在渲染函数或生命周期钩子中通过this读取数据时,都应该在渲染上下文对象中处理。

setup函数: setup函数的返回值可以是两种类型,如果返回函数,则将该函数作为组件的渲染函数,如果返回数据对象,则将该对象暴露到渲染的上下文中。

组件事件与emit的实现: 检测propsData的key值,判断是否以字符串'on'开头, 如果时,则认为该属性时组件的自定义事件,即使组件没有显示的声明props,也要讲它添加到props中,而不是attrs对象。

插槽: 我们讨论了组件的插槽。它借鉴了 Web Component 中<slot> 标签的概念。插槽内容会被编译为插槽函数,插槽函数的返回值就是向槽位填充的内容。<slot> 标签则会被编译为插槽函数的调用,通过执行对应的插槽函数,得到外部向槽位填充的内容(即虚拟DOM),最后将该内容渲染到槽位中。

注册组件生命周期钩子: 通过 onMounted 注册的生命周期函数会被注册到当前组件实例的 instance.mounted 数组中。为了维护当前正在初始化的组件实例,我们定义了全局变量 currentInstance,以及用来设置该变量的 setCurrentInstance 函数。

异步组件与函数式组件

内建组件和模块