Vue3 que

Having spent a lot of time looking at the core source code for Vue2, it’s time to get to grips with Vue3. Vue3 has many new features, such as Composition Api, Teleport, Fragments, Emits Component Option, and createRenderApi for creating custom renderers. Today we’re going to take a look at the Componsition Api.

Composition API

Composition Api, which literally means Composition Api, was created to implement function-based logic reuse mechanisms. To get started using the Composition Api you first need a place to actually use it: —- Setup

The basic use

<div id="app">
  <h1>Composition API</h1>
  <div>count: {{ state.count }}</div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  const { createApp, reactive, computed } = Vue;
  const App = {
    beforeCreate() {
      console.log('beforeCreate');
    },
    setup() {
      console.log('setup');
      const state = reactive({
        count: 0.doubleCount: computed(() = > state.count * 2)})return { state }
    },
    created() {
      console.log('created');
    },
  }
  createApp(App).mount('#app')
</script>
Copy the code

Create a component instance with createApp, and setup is a new component option that is the entry point within the component to use the Composition API. Vue exposes Reactive, a property that handles reactive, receiving an object and returning a reactive proxy object. The final returned object will be merged with the render function context. Many articles say that the setup execution time is between beforeCreate and Created. After verification, the setup execution time is actually before beforeCreate and Created.

Calculate attribute
 <div>doubleCount: {{state.doubleCount}}</div>
Copy the code
const { computed } = Vue;
setup() {
  const state = reactive({
    count: 0.doubleCount: computed(() = > state.count * 2)})return {
    state,
  }
}
Copy the code

Passing a parameter to the computed function returns read-only responsive data.

The event processing
<div @click="add">count: {{ state.count }}</div>
Copy the code
const App = {
 setup() {
     Declare an add function in setup
     function add() {
         state.count++
     } 
 // Pass in the render function context
 return { state, add }
 }
}
Copy the code

The event definition is very different from the Options API. You only need to define it in setup and return

Listener:watch()
const { watch } = Vue;
const App = {
     setup() {
         // state.count Changes to cb will be performed
         watch(() = > state.count, (val, oldval) = > {
             console.log('the count has changed:+ val); }}})Copy the code
Reference object: single-valued responsivityref
<div>counter: {{ counter }}</div>
Copy the code
const { ref } = Vue;
const App = {
 setup() {
     // return a responsive Ref object
     const counter = ref(1)
     setTimeout(() = > {
     // Change the value of the object
         counter.value++
     }, 1000);
     / / add a counter
     return { state, add, counter }
 }
}
Copy the code

Demonstrate maintainability

<div>
    <h1>The logical combination</h1>
    <div id="app"></div>
  </div>
  <script>
    const { createApp, reactive, onMounted, onUnmounted, toRefs } = Vue;
    // mouse position listening
    function useMouse() {
      // Data reactivity
      const state = reactive({
        x: 0.y: 0
      })
      const update = e= > {
        state.x = e.pageX
        state.y = e.pageY
      }
      onMounted(() = > {
        window.addEventListener('mousemove', update)
      })
      onUnmounted(() = > {
        window.removeEventListener('mousemove', update)
      })
      // Convert all keys to responsive data
      return toRefs(state)
    }
    // Event monitoring
    function useTime() {
      const state = reactive({
        time: new Date()
      })
      onMounted(() = > {
        setInterval(() = > {
          state.time = new Date()},1000)})return toRefs(state)
    }
    // Logical combination
    const MyComp = {
      template: ` 
        
x: {{ x }} y: {{ y }}

time: {{time}}

`
.setup() { // Use mouse logic const { x, y } = useMouse() // Use time logic const { time } = useTime() // return to use return { x, y, time } } } createApp(MyComp).mount('#app')
</script>
Copy the code

As you can see from the above code, the useMouse,useTime functions are separated according to the business module we need, and finally combined into the Setup. If we use options API, if the amount of code is too much, the business logic will become extremely complicated, and our responsive data still needs to be defined in data and events need to be declared in methods, which will result in the phenomenon of “jumping up and down”. In addition, this situation makes understanding and maintenance more difficult. The Composition Api solves this problem.

responsive

We know that VUe2 uses Object.defineProperty to intercept data. But there are drawbacks to this approach.

  • Cannot listen for array changes
  • Each property of the object must be traversed
  • Nested objects must be traversed deeply
  • New or deleted attributes cannot be listened on

Vue2.0 takes care of arrays, overwriting their seven methods.

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  'push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse'
]

/** * Intercept mutating methods and emit events */
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (. args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})
Copy the code

Vue3.0 uses ES6 Proxy feature to solve these problems.

function reactive(obj) {
  if (typeofobj ! = ='object'&& obj ! =null) {
    return obj
  }
  const observed = new Proxy(obj, {
    get(target, key, receiver) {
      const res = Reflect.get(target, key, receiver)
      console.log(` access${key}:${res}`)
      return res
    },
    set(target, key, value, receiver) {
      const res = Reflect.set(target, key, value, receiver)
      console.log(` set${key}:${value}`)
      return res
    },
    deleteProperty(target, key) {
      const res = Reflect.deleteProperty(target, key)
      console.log(` delete${key}:${res}`)
      return res
    }
  })
  return observed
}

const state = reactive({
  foo: 'foo'
 })

 state.foo // yes
 state.foo = 'fooooooo' // yes
 state.dong = 'dong' // yes
 delete state.dong // yes
Copy the code

Nested object responsive

Nested objects cannot respond to the above treatment

const state = reactive({
 bar: { a: 1}})// Set nested object properties
state.bar.a = 10 // no
Copy the code

I’m going to do it recursively

function isObject(val) {
  if(! val &&typeof val === 'object') {
    return true}}function reactive(obj) {
  if (typeofobj ! = ='object'&& obj ! =null) {
    return obj
  }
  const observed = new Proxy(obj, {
    get(target, key, receiver) {
      const res = Reflect.get(target, key, receiver)
      console.log(` access${key}:${res}`)
       returnisObject(res) ? Reactive (RES) : RES}, ··· ··return observed
}
Copy the code

The above is a preliminary understanding of Vue3.0, and the follow-up will be a comprehensive study of Vue3.0. At present, we only have a superficial understanding of the source code of Vue3.0, and we hope to urge learning through writing articles. Welcome to leave a message ~~~