29-更新component组件

78 阅读2分钟

例子

查看例子

实现

新增$props

例子中用了this.props.xx,但是目前我们没有this.props.xx,但是目前我们没有this.props,我们把新增一个$props对象

componentPublicInstanceProxyHandlers.ts

/*
 * @Author: Lin zefan
 * @Date: 2022-03-23 17:52:57
 * @LastEditTime: 2022-04-09 10:43:19
 * @LastEditors: Lin ZeFan
 * @Description:
 * @FilePath: \mini-vue3\src\runtime-core\componentPublicInstanceProxyHandlers.ts
 *
 */
 
// 扩展的实例Map
const PublicInstanceMap = {
  $el: (i) => i.vnode.el,
  $slots: (i) => i.slots,
  $props: (i) => i.props,
};

实现

组件更新

思考

  1. 前面借助了effect,能够侦听响应式数据更新,并且判断了数据初始化或者更新
  2. 更新component依旧是需要借助effect来触发更新的
  3. 之前实现effect的时候,实现了runner功能,可以利用runner来触发更新
  4. 要拿到runner,得先在实例上存储runner
  5. 要拿到当前实例,还要储存当前的实例

render.ts

function mountComponent(n2, container, parentComponent, anchor) {
    const instance = (n2.component = createComponentInstance(
      n2,
      parentComponent
    ));
}
function setupRenderEffect(instance, container, anchor) {
    instance.runner = effect(() => {})
}

component.ts

export function createComponentInstance(initVNode, parent) {
  const component = {
    // other code
    
    runner: null,
    component: null
  };
  
  // other code
}

我们把runner和实例拿到后,可以开始写我们的逻辑了

  1. 获取当前组件实例
  2. 保存当前新的vnode,在触发更新时做数据更新
  3. 利用runner来触发更新

触发effect更新

// render.ts
function updateComponent(n1, n2) {
    const instance = (n2.component = n1.component);
    instance.next = n2;
    instance.runner();
}

在effect更新逻辑中,加上判断是否有新的vnode,有则更新

// render.ts
function setupRenderEffect(instance, container, anchor) {
    instance.runner = effect(() => {
        if (!instance.isMounted) {
        } else {
            const { proxy, next, vnode } = instance;
            if (next) {
              // 保存当前的dom节点,因为新vnode没有走创建流程,所以没有el
              next.el = vnode.el;
              updateComponentPreRender(instance, next);
            }
            // other code
        }
    })
}

function updateComponentPreRender(instance, nextVNode) {
    // 更新当前虚拟节点
    instance.vnode = nextVNode;
    // 更新当前的props
    instance.props = nextVNode.props;
    // 把next清空
    nextVNode = null;
}

优化更新逻辑

现在不管子组件需不需要更新,都会触发runner更新,有时候我们只是当前组件更新了,并没有涉及子组件的更新,所以需要做进一步优化判断

组件更新判断

function updateComponent(n1, n2) {
    /** 更新组件
     * 1. 获取当前组件实例
     * 2. 对比props数据,若有变更,则走更新逻辑;否则直接将vnode替换即可
     * 2. 保存当前新的vnode,在触发更新时做数据更新
     * 3. 利用runner来触发更新逻辑
     */
    const instance = (n2.component = n1.component);
    /** 需要更新props
     * 1. 保存新的vnode数据
     * 2. 触发runner再次调用更新逻辑
     */
    if (shouldUpdateComponent(n1, n2)) {
      instance.next = n2;
      instance.runner();
    } else {
      // 不需要更新props,更新当前vnode即可
      n2.el = n1.el;
      instance.vnode = n2;
    }
}

新增shouldUpdateComponent函数,判断props是否有差异

/*
 * @Author: Lin ZeFan
 * @Date: 2022-04-09 12:04:52
 * @LastEditTime: 2022-04-09 12:04:53
 * @LastEditors: Lin ZeFan
 * @Description:
 * @FilePath: \mini-vue3\src\runtime-core\componentUpdateUtils.ts
 *
 */

export function shouldUpdateComponent(prevVNode, nextVNode) {
  const { props: prevProps } = prevVNode;
  const { props: nextProps } = nextVNode;
  for (const key in nextProps) {
    if (nextProps[key] !== prevProps[key]) {
      return true;
    }
  }
  return false;
}