Reactive and ref of the new features of vue 3.0

Vue 3.0 new features

Reference:   https://www.cnblogs.com/Highdoudou/p/9993870.html

https://www.cnblogs.com/ljx20180807/p/9987822.html

Performance optimization

  • Changes in the observer mechanism : Proxy replaces object.defineProperty  

    • Object.defineProperty Getters and setters  used by Vue 2.x. However, Vue 3 will use ES2015 Proxy as its observer mechanism. This eliminates previous warnings, doubles the speed, and saves half of the memory overhead.
  • virtual DOM reconstruction (twice as fast as 2.5) [It is related to the size of the template --> It is related to the amount of dynamic content in the template]

    1.   Tradition: When the component is updated, the entire vdom tree needs to be recreated, and then traversed for diff, update

    2.   New update strategy: block tree

      1.  Distinguish between dynamic nodes and static nodes

      2. Update  based on dynamic node instructions (v-if, v-for, { {name }} etc.)

  • Compile-time optimization

  1. Slot is compiled as a function by default

  2. The creation function of vnode keeps the parameters consistent

  3. Generate vnode type tags at compile time

Added composition-api,

https://composition-api.vuejs.org/

You can use composition-api in the vue2.x project by installing the @vue/composition-api package.

  • Reactive: The role of reactive is to package objects into reactive objects -objects that are proxied through Proxy.
  • ref: Return a responsive, variable ref object with only one attribute of value from the incoming value.

  When ref is returned as a render context, and the ref object is used in the template, the internal value is automatically obtained, and the .value attribute is not required.

Copy code

<template>
  <div>{
  
  { count }}</div>
</template>

<script>
  export default {
    setup() {
      return {
        count: ref(0)
      }
    }
  }
</script>

Copy code

  When the ref object is used as a property and passed into the reactive object reactive, it will automatically get its internal value (behave like a normal property value, not an object).

Copy code

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1

Copy code

  After the reactive property is bound to the new ref object, the value of the original ref remains unchanged (disconnected).

const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1

  In other cases, you need to bring .value when obtaining ref

Copy code

const arr = reactive([ref(0)])
// need .value here
console.log(arr[0].value)

const map = reactive(new Map([['foo', ref(0)]]))
// need .value here
console.log(map.get('foo').value)

Copy code

    •   The difference between reactive objects and ref objects

    It can be compared by how to write standard JavaScript logic:

Copy code

// Style 1: Separate variables
let x = 0
let y = 0

function updatePosition(e) {
  x = e.pageX
  y = e.pageY
}


// Style 2: Single object
const pos = {
  x: 0,
  y: 0,
}

function updatePosition(e) {
  pos.x = e.pageX
  pos.y = e.pageY
}

Copy code

  • You can convert the style (1) to use ref writing (in order to make the basic type value responsive ).

  • Convert style (2) to the way of using  reactive objects.

      If you only use  reactive the problem, you must always keep a reference to the returned object when using the composite function to maintain responsiveness. This object cannot be deconstructed or expanded :

Copy code

// Combination function:
function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0,
  })

  // ...
  return pos
}

// Consumer components
export default {
  setup() {
    // Responsiveness will be lost here!
    const { x, y } = useMousePosition()
    return {
      x,
      Y,
    }

    // Responsiveness will be lost here!
    return {
      ...useMousePosition(),
    }

    // This is the only way to stay responsive!
    // You must return `pos` itself and reference x and y in the template in the same way as `pos.x` and `pos.y`.
    return {
      post: useMousePosition (),
    }
  },
}

Copy code

toRefs API is used to provide a solution to this constraint-it converts each property of the responsive object into a corresponding ref.

Copy code

function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0,
  })

  // ...
  return toRefs(pos)
}

// x & y are now in ref form and can be deconstructed by i!
const { x, y } = useMousePosition()

Copy code

 

  • computed: Pass in a getter() function and return an immutable responsive ref object.
const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2 count value changes, plusOne value changes accordingly

plusOne.value++ // error

  When an object with get and set functions is passed in, a writable ref object is returned.

Copy code

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0 The value of count changes, and the value of plusOne changes accordingly; when the value of plusOne is changed, the value of count also changes accordingly

Copy code

  • readonly: Convert the received object (whether it is a responsive object, ref object or ordinary object) into a read-only object that is proxied by Proxy.

Copy code

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // works for reactivity tracking
  console.log(copy.count)
})

// mutating original will trigger watchers relying on the copy
original.count++

// mutating the copy will fail and result in a warning
copy.count++ // warning!

Copy code

  • watchEffect: accept a function, when the dependency changes, call the function again.

Copy code

const count = ref(0)

watchEffect(() => console.log(count.value))

setTimeout(() => {
  count.value++
}, 100)

Copy code

  When watchEffect() is called in setup() or life cycle hook, the monitor will always exist in the life cycle of the component until the component is unmount.

  Another case of unloading monitoring is that watchEffect() returns a stop handler, which can be called to stop the monitoring.

const stop = watchEffect(() => {
  /* ... */
})

// later
stop()

  When getting data from the background, watchEffect() accepts the async callback function.

const data = ref(null)
watchEffect(async () => {
  data.value = await fetchData(props.id)
})

  The update function of the component also has a watch effect. The user-defined watchEffect will be called after the component is updated .

Copy code

<template>
  <div>{
  
  { count }}</div>
</template>

<script>
  export default {
    setup() {
      const count = ref(0)

      watchEffect(() => {
        console.log(count.value)
      })

      return {
        count
      }
    }
  }
</script>

Copy code

In the above code, count.value will be printed synchronously in the first round (before the onmount life cycle); when the count changes, the component update will be performed first, and then log.

  If you want to execute the callback function in watchEffect for the first time, put it after onmount,

onMounted(() => {
  watchEffect(() => {
    // access the DOM or template refs
  })
})

  If you want the watchEffect() call to occur before the component update or re-run synchronization, you need to pass an option object with the flush attribute (the default value is post).

watchEffect(()=> {
//...
}, {
 flush:'sync' // trigger flush before update: "pre"
})

In addition, the option object has two function attributes, ontrack and ontrigger, which are used to debug the behavior of the watcher.

  • onTrack will be called when a reactive property or ref is tracked as a dependency
  • onTrigger will be called when the watcher callback is triggered by the mutation of a dependency

Copy code

watchEffect(
  () => {
    /* side effect */
  },
  {
    onTrigger (e) {
      debugger // Perform interactive debugging
    }
  }
)

Copy code

  • watch: equivalent to this.$watch in vue 2.x.

Compared to watchEffect(), watch() can help us achieve:

  1. Perform the side effect lazily;
  2. Be more specific about what state should trigger the watcher to re-run;
  3. Access both the previous and current value of the watched state.

  The data source of watch() can be a getter function that returns a value, or a ref object.

Copy code

// watching a getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// directly watching a ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

Copy code

  For monitoring of multiple data sources, an array can be used.

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})

 Life cycle api adjustment

Copy code

<template>
  <div>{
  
  {count}} {
  
  {obj.foo}}</div>
</template>
<script>
import { ref, reactive, watchEffect } from '@vue/composition-api'
export default {
  props: {
    name: String
  },
  setup (props) {
    const count = ref(0)
    const obj = reactive({ foo: 'abc' })
    watchEffect(() => {
      console.log(props.name)
    })
    return {
      count,
      obj
    }
  }
}
</script>

Copy code

The first parameter accepted by setup() is props. Props are responsive. Note: It cannot be deconstructed when passing parameters. The above code passes props to setup() and monitors them through watchEffect().

Setup() can also accept the second parameter context (equivalent to this in vue2.x,  setup() does not allow this ). When context is used as a parameter, it can be deconstructed. Commonly used ones are:

context.attrs, context.slots, context.emit

setup(props, {attrs}) {
    function onClick() {
      console.log(attrs.foo) // can be guaranteed to be the latest value
    }
 }

Other life cycle functions can be registered synchronously in setup(). When the life cycle hooks are executed synchronously, the renderContext, watcher and computed properties of the component instance are also established synchronously. When the component is uninstalled, the internal lifecycle hooks will also be removed synchronously.

Copy code

setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }

Copy code

  1. <template> Same as before, vue-next also supports handwritten render. If template and render exist at the same time, render takes precedence.
  2. setup() is the new major change. As the name implies, the setup function will run once before the component is mounted . Similar to the initialization of the component, setup needs to return an object or function. The returned object will be assigned to the renderContext of the component instance , which can be accessed in the template scope of the component, similar to the return value of data. The return function will be treated as the render of the component.

Organize code based on logical concerns

In vue 2.x, the option api is used. To complete the data or computed attribute distance used by a function, the definition of this function may have many lines (code is scattered). This fragmentation makes code maintenance become difficult.

This problem has been resolved in the composition api. The code for each logical concern is now combined into a combinatorial function (named starting with use ). This greatly reduces the need to constantly "jump" when dealing with large components. At the same time, the combined function can also be folded in the editor to make the components easier to browse.

Copy code

  setup() {
    // ...
  },
}

function useCurrentFolderData(networkState) {
  // ...
}

function useFolderNavigation({ networkState, currentFolderData }) {
  // ...
}

function useFavoriteFolder(currentFolderData) {
  // ...
}

function useHiddenFolders() {
  // ...
}

function useCreateFolder(openFolder) {
  // ...
}

Copy code

setup() The function is now simply used as the entry point for calling all combined functions. The final return statement serves as a single exit to confirm the content exposed to the template.

Copy code

export default {
  setup() {
    // network status
    const { networkState } = useNetworkState()

    // folder status
    const { folders, currentFolderData } = useCurrentFolderData(networkState)
    const folderNavigation = useFolderNavigation({
      networkState,
      currentFolderData,
    })
    const { favoriteFolders, toggleFavorite } = useFavoriteFolders(
      currentFolderData
    )
    const { showHiddenFolders } = useHiddenFolders()
    const createFolder = useCreateFolder(folderNavigation.openFolder)

    // current working directory
    resetCwdOnLeave()
    const { updateOnCwdChanged } = useCwdUtils()

    // Utilities
    const { slicePath } = usePathUtils()

    return {
      networkState,
      folders,
      currentFolderData,
      folderNavigation,
      favoriteFolders,
      toggleFavorite,
      showHiddenFolders,
      createFolder,
      updateOnCwdChanged,
      slicePath,
    }
  },
}

Copy code

 

 

 

 Logic extraction and reuse

A composite function only depends on its parameters and  Vue's globally exported API , not on the  this context. You can export any piece of logic in the component as a function for reuse.

Copy code

import { ref, onMounted, onUnmounted } from 'vue'

export function useMousePosition() {
  const x = ref(0)
  const y = ref(0)
   // Extract as a function
  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    window.addEventListener('mousemove', update)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })

  return { x, y }
}

Copy code

Call the above combination function in a component

Copy code

import { useMousePosition } from './mouse'

export default {
  setup() {
    const { x, y } = useMousePosition()
    // Other logic...
    return { x, y }
  },
}

Copy code

Cooperate with existing API

  • The combined API will be resolved before the 2.x options ( data, computed and  methods), and the properties defined in these options cannot be accessed in advance.

  • setup() The property returned by the function will be exposed to  this. They can be accessed in the 2.x options.

 

Comparison of vue3.0 and react:

    1. Same logic combination and reuse ability

    2. The setup() of the  composition api  will only be called once , compared with react hooks

      • In line with js intuition

      • No closure variable problem

      • Will not be repeated every time rendering, reducing the pressure of garbage collection;

      • There is no problem that inline callbacks cause subcomponents to be updated forever

      • There is no problem of forgetting to record dependencies, and there is no need to "useEffect" and "useMemo" and pass in the dependency array to capture outdated variables. Vue's automatic dependency tracking can ensure that listeners and calculated values ​​are always accurate.

Guess you like

Origin blog.csdn.net/weixin_44273311/article/details/107688858