This is the 8th day of my participation in the First Challenge 2022. For details: First Challenge 2022.

Vue2 Options API vs. Composition API

The Options API of Vue2 uniformly puts data in data and methods in methods, and each life cycle is also separated. Assume that a component is a large component with many processing logic concerns (no color is used for the following figure). When making modifications, Options API needs to repeatedly jump in each block, which is a little tedious for daily business development and not conducive to code reading. Vue3 introduces a Composition API that puts all the relevant business logic code in one block, making it more logical, more organized, and easier to read

The current version of Vue3 is ^3.2.x, yu Yuxi announced on Github that Vue3 will become the new default version on February 7, 2022. Currently, the name of Vue3 on Github has been changed from VUE-Next to Core, which may not be compatible with Vue2.0 Options API in the future

Options API and Composition API can coexist

<body>
  <div id="app">
    <p>{{ msgData }}</p>
    <p>{{ msgSetup }}</p>
  </div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          msgData: "I'm from data"}},setup(props, { emit, slots, attrs }) {
        const msgSetup = Vue.ref("I'm from setup")
        return {  
          msgSetup
        }
      }
    })
    app.mount('#app')
  </script>
</body>
Copy the code
  • You can seedataandsetupThe data is rendered on the pageOptions APIandComposition APIData can coexist in

How does Vue3 handle data conflicts between SETUP and data?

  • Change the key values of the two values in the previous example to be the same and see what happens
<body>
  <div id="app">
    <p>{{ msg }}</p>
    <p>{{ msg }}</p>
  </div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          msg: "I'm from data"}},setup(props, { emit, slots, attrs }) {
        const msg = Vue.ref("I'm from setup")
        return {  
          msg
        }
      }
    })
    app.mount('#app')
  </script>
</body>
Copy the code
  • Now what you see rendered issetupTherefore, it can be determined that if there is data conflict,setupIs of high priority

Find the answer in the source code

  • In the aboveVue3.0 source code learning – Composition APIIn the studyCompostion APIThe procedure executed is compatible with Vue2Options APII did it in the process

Review the process

  1. Component instance initializes executionsetupComponent
  2. performsetupStatefulComponentreturnThe setup option returns the value
  • Focus onsetupStatefulComponentFunction, positionpackages\runtime-core\src\component.ts
  • In the first1. create public instance / render proxyThis step toinstance.ctxMade a layer of proxy innew ProxyIs passed in as the second argument toPublicInstanceProxyHandlersIt is an interception of data
function setupStatefulComponent(instance: ComponentInternalInstance, isSSR: boolean) {
  const Component = instance.type as ComponentOptions
  ...
  // 0. create render proxy property access cache
  instance.accessCache = Object.create(null)
  // 1. create public instance / render proxy
  // Create a proxy for the public instance/render function
  // also mark it raw so it's never observed
  instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
  ...
  // 2. call setup()
  const { setup } = Component
  // If the user sets the setup function
  if (setup) {
    // Create a context object for the setup function
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)
    
    // Set the instance of the current component to vue.getCurrentInstance
    setCurrentInstance(instance)
    // Pause tracing to improve performance
    pauseTracking()
    Exceptions can be caught by calling setup() with callWithErrorHandling
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      // Here is the props for setup(), CTX context
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )
    resetTracking()
    unsetCurrentInstance()

    if (isPromise(setupResult)) {
      ...
    } else {
      If setup() does not return a Promise, the result handler is executed
      handleSetupResult(instance, setupResult, isSSR)
    }
  } else{... }}Copy the code

  • PublicInstanceProxyHandlersPosition,packages\runtime-core\src\componentPublicInstance.ts, the main parts are selected and annotated
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
  get({ _: instance }: ComponentRenderContext, key: string) {
    // CTX instance context
    // setupState setup returns a value
    // data Returns the value of the data function
    const { ctx, setupState, data, props, accessCache, type, appContext } =
      instance

    ...

    // data / props / ctx
    // This getter gets called for every property access on the render context
    // during render and is a major hotspot. The most expensive part of this
    // is the multiple hasOwn() calls. It's much faster to do a simple property
    // access on a plain object, so we use an accessCache object (with null
    // prototype) to memoize what access type a key corresponds to.
    let normalizedProps
    if (key[0]! = ='$') { // The key value does not start with $and is a user-set attribute
      constn = accessCache! [key]if(n ! = =undefined) { // There is a cache condition
        switch (n) {
          // Get it from setupState first
          case AccessTypes.SETUP:
            return setupState[key]
          // Get the second value from the data return value
          case AccessTypes.DATA:
            return data[key]
          // The component context again
          case AccessTypes.CONTEXT:
            return ctx[key]
          // Finally from the component properties
          case AccessTypes.PROPS:
            returnprops! [key]// default: just fallthrough}}else if(setupState ! == EMPTY_OBJ && hasOwn(setupState, key)) {// No cache priority setupStateaccessCache! [key] = AccessTypes.SETUP// add cache
        return setupState[key]
      } else if(data ! == EMPTY_OBJ && hasOwn(data, key)) {// No data is cachedaccessCache! [key] = AccessTypes.DATAreturn data[key]
      } else if (
        // only cache other properties when instance has declared (thus stable)
        // props
        (normalizedProps = instance.propsOptions[0]) && hasOwn(normalizedProps, key) ) { accessCache! [key] = AccessTypes.PROPSreturnprops! [key] }else if(ctx ! == EMPTY_OBJ && hasOwn(ctx, key)) { accessCache! [key] = AccessTypes.CONTEXTreturn ctx[key]
      } else if(! __FEATURE_OPTIONS_API__ || shouldCacheAccess) { accessCache! [key] = AccessTypes.OTHER } } ... },... }Copy the code

summary

  • Source code pairs the component instance contextinstance.ctxActing as an agent, inPublicInstanceProxyHandlersproxy getDuring interception, the slave is preferredsetupStateTo get fromThe return value of setupSecondly,dataAnd the lastprops