Vue3 数据响应式

224 阅读2分钟
  1. options写法:

data选项

 createApp({
            data(){
                return {
                    foo:1
                }
            },
        }).mount('#demo');        

初始化流程:

  1. 创建renderer:{render,createApp}
  • 其中createApp内部定义了mount()/use()/mixin()/directive()/unmount()/provide()等app实例方法;
  • render方法内部会根据vnode执行patch(container._vnode || null, vnode, container) 新增还是更新;还是unmount(container._vnode, null, null, true)销毁方法。
  1. 执行mount()方法[mount('#demo')], 在执行mount方法
  • 内部创建vnode --createVnode()

  • 并执行render(vnode, rootContainer)方法 --这个方法就是renderder内定义的方法

  • 执行 patch()方法,初始化的时候执行内部的processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized)方法,然后再执行mountComponent( n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized )方法;

  • 在mountComponent方法内部会const instance= ComponentInternalInstance() 通过ComponentInternalInstance()方法获取当前组件实例instance;然后再调用setupComponent(instance)方法进行初始化props和slot -- initProps()/initSlots(),由于我这个例子属于有状态的组件,会执行setupStatefulComponent(instance, isSSR)方法.

  • setupStatefulComponent()方法,由于此例子中没有setup就执行finishComponentSetup(instance, isSSR)方法.

  • finishComponentSetup(instance, isSSR)方法先const Component = instance.type ;Component.render = compile(Component.template);instance.render = (Component.render)通过编译器获取render方法,然后if (__FEATURE_OPTIONS_API__) { currentInstance = instance; applyOptions(instance, Component); currentInstance = null}判断是否有optionsapi执行 applyOptions(instance, Component)方法。 * applyOptions(instance,options)方法,其中options api有 const { // composition mixins, extends: extendsOptions, // state data: dataOptions, computed: computedOptions, methods, watch: watchOptions, provide: provideOptions, inject: injectOptions, // assets components, directives, // lifecycle beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy, beforeUnmount, destroyed, unmounted, render, renderTracked, renderTriggered, errorCaptured } = options;

    1. 其中beforeMount()执行 onBeforeMount;
    2. mounted执行 onMounted(mounted.bind(publicThis));
    3. beforeUpdate执行onBeforeUpdate(beforeUpdate.bind(publicThis));
    4. updated() 执行onUpdated(updated.bind(publicThis));
    5. activated() 执行onActivated(activated.bind(publicThis));
    6. deactivated() 执行onDeactivated(deactivated.bind(publicThis));
    7. errorCaptured() 执行onErrorCaptured(errorCaptured.bind(publicThis));
    8. renderTracked() 执行onRenderTracked(renderTracked.bind(publicThis)); 9.renderTriggered()执行onRenderTriggered(renderTriggered.bind(publicThis));
    9. beforeDestroy() 改成beforeUnmount(); 11.destroyed()改成unmount();
    10. beforeUnmount()执行onBeforeUnmount(beforeUnmount.bind(publicThis));
    11. unmounted()执行onUnmounted(unmounted.bind(publicThis));
  • 如果有data选项 (dataOptions) { resolveData(instance, dataOptions, publicThis); }在resolveData()方法内const data = dataFn.call(publicThis, publicThis);先执行data函数,if (!isObject(data)) { __DEV__ && warn(data() should return an object.) }判断执行的data返回值是否为对象;再执行reactive(), createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers ); 然后instance.data = reactive(data);

    总结

    1. createApp()方法内的选项可以存在options api,支持vue2的options api 风格;

    2. createApp()内的data选项必须是个函数并且返回值一定为对象,在源码中的resolveData方法中可以获取得到答案,不再支持data为对象的写法;

    3. 即使写data选项了,内部还是通过reactive(data)方法,把data创建为响应式数据,跟在setup内直接使用reactive(data)一样;

    4. 通过调用 applyOptions(instance, Component)方法进行处理之前的options api 风格的方法。

接下来从开始研究 reactive(data)
  1. 内部执行createReactiveObject(target, true,shallowReadonlyHandlers, readonlyCollectionHandlers)方法;

  2. 在createReactiveObject方法内会判断target是否为对象,是否为只读对象。const proxyMap = isReadonly ? readonlyMap : reactiveMap; 所以最外层是proxyMap;

  3. 是否存在,已经存在就return; 先获取target类型,getTargetType(target),内部执行targetTypeMap(toRawType(value))方法 function targetTypeMap(rawType: string) { switch (rawType) { case 'Object': case 'Array': return TargetType.COMMON case 'Map': case 'Set': case 'WeakMap': case 'WeakSet': return TargetType.COLLECTION default: return TargetType.INVALID } }

  4. const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy