Summary of important knowledge points of Vue.js three

overview

  • Implementation principle of nextTick in Vue
  • The difference between v-if and v-show
  • What does the key in Vue do?
  • How to understand ref toRef and toRefs
  • How does the Composition API implement code logic reuse?

Implementation principle of nextTick in Vue

Delayed callback to execute after the next DOM update cycle ends. Use this method immediately after modifying the data to get the updated DOM.

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})

// 作为一个  Promise 使用   (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
.then(function () {
// DOM 更新了
})

New since 2.1.0 : Returns a Promise if no callback is provided and in an environment that supports Promises.

Note that Vue doesn't come with a Promise polyfill, so if your target browser doesn't natively support Promise (IE), you'll have to provide the polyfill yourself.

For more exciting content, please search for " " on WeChat前端爱好者 , and click me to view .

In case you haven't noticed, Vue executes asynchronously when updating the DOM .

As long as it detects data changes, Vue will open a queue and buffer all data changes that occur in the same event loop.

If the same watcher is triggered multiple times, it will only be pushed into the queue once. This deduplication while buffering is very important to avoid unnecessary calculations and DOM manipulations.

Then, on the next event loop "tick", Vue flushes the queue and performs the actual (deduplicated) work. Vue internally tries to use native Promise.then , MutationObserver and setImmediate for asynchronous queues . If the execution environment does not support it, it will use setTimeout(fn, 0) instead.

For example, when you set vm.someData = 'new value' , the component will not re-render immediately.

When the queue is flushed, the component is updated on the next event loop "tick".

Most of the time we don't need to care about this process, but if you want to do something based on the updated DOM state, it can be tricky. While Vue.js generally encourages developers to use a "data-driven" way of thinking and avoid touching the DOM directly, sometimes we have to. To wait for Vue to finish updating the DOM after data changes, use Vue.nextTick(callback) immediately after data changes . like this 回调函数将在 DOM 更新完成后被调用.

the case

For example:

<div id="example">{
   
   {message}}</div>
var vm = new Vue({
el: '#example',
    data: {
     message: '123'
    }
})

vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false

Vue.nextTick(function () {
    vm.$el.textContent === 'new message' // true
})

It is particularly convenient to use the vm.$nextTick() instance method in the component , because it does not require the global Vue , and this in the callback function will be automatically bound to the current Vue instance:

Vue.component( 'example', {
    template: '<span>{
   
   { message }}</span>',
    data: function () {
        return {
            message: '未更新 '
        }
    },
    methods: {
        updateMessage: function () {
            this.message = '已更新 '
            console.log(this.$el.textContent) // => '未更新 '
            this.$nextTick(function () {
                console.log(this.$el.textContent) // => '已更新 '
            })
        }
    }
})

Since $nextTick() returns a Promise object, you can use the new ES2017 async/await syntax to accomplish the same thing:

methods: {
    updateMessage: async function () {
        this.message = '已更新 '
        console.log(this.$el.textContent) // => '未更新 '
        await this.$nextTick()
        console.log(this.$el.textContent) // => '已更新 '
    }
}

Summarize:

The nextTick method mainly uses macro tasks and micro tasks, and defines an asynchronous method. Calling the nextTick method multiple times will store the method in the queue, and clear the current queue through this asynchronous method. So this nextTick method is an asynchronous method.

The difference between v-if and v-show

v-if is "true" conditional rendering, as it will ensure that event listeners and child components inside the conditional block are properly destroyed and recreated during the transition.

  • true: render
  • false: Destroy without rendering, the element does not exist

v-if is also lazy: if the condition is false on initial render, do nothing - the conditional block won't start rendering until the condition becomes true for the first time.

In contrast, v-show is much simpler - no matter what the initial condition is, the element is always rendered, and is simply toggled based on CSS.

  • true:display: block
  • false: display: none

In general, v-if has higher switching overhead, while v-show has higher initial rendering overhead.

So, if you need to toggle very frequently, you're better off with v-show, and if your conditions rarely change at runtime, you're better off with v-if.

pay attention

In vue2 the priority of v-for is higher, but the priority has changed in vue3. v-if has higher precedence.

When v-for and v-if appear at the same time in vue2, they can be placed in one tag, as follows:

<div v-for="item in ss" v-if="item.show" :key="item.id">
 {
   
   { item.show }}
</div>
 
 data(){
    return {
        ss:[
        { id: 1, show: true, name: '1' },
        { id: 2, show: false, name: '2' },
        { id: 3, show: true, name: '3' },
        ]
    }
 }

Writing this way in vue3 will report an error, because v-if has a higher priority, so item.show is undefined and an error is reported.

What does the key in Vue do?

The key is for vnode in Vue唯一标记 , through this key, our diff operation can be more accurate and faster.

Vue's diff process can be summarized as:

oldCh and newCh each have two head and tail variables oldStartIndex, oldEndIndex and newStartIndex, newEndIndex , they will compare the new node and the old node in pairs, that is, there are 4 comparison methods in total:

  • newStartIndex 和oldStartIndex
  • newEndIndex and oldEndIndex
  • newStartIndex and oldEndIndex
  • newEndIndex 和 oldStartIndex,

If none of the above 4 comparisons match

If the key is set, the key will be used for comparison again . During the comparison, the traversal will move towards the middle. Once StartIdx > EndIdx indicates that at least one of oldCh and newCh has been traversed, the comparison will end.

For the diff process of whether there is a key or not, you can check another article written by the author to explain the virtual DOM in detail " In-depth analysis: virtual DOM at the core of Vue "

So the function of key in Vue is:

key is the unique mark of vnode in Vue, through this key, our diff operation can be more accurate and faster

More accurate : because it is not in-place multiplexing with key, in-place multiplexing can be avoided in the sameNode function a.key === b.key comparison. So it will be more accurate.
Faster : Use the uniqueness of the key to generate a map object to obtain the corresponding node, which is faster than the traversal method. The source code is as follows:

function createKeyToOldIdx (children, beginIdx, endIdx) {
    let i, key
    const map = {}
    for (i = beginIdx; i <= endIdx; ++i) {
        key = children[i].key
        if (isDef(key)) map[key] = i
    }
    return map
}

How to understand ref toRef and toRefs

ref

  1. Generating Reactive Data of Value Types
  2. Can be used for templates and reactive
  3. Modify the value by .value
<template>
	<div>{
   
   { ageRef }}</div>
</template>

<script>
import { ref } from 'vue'
export default {
	setup() {
		const ageRef = ref(20)

		setInterval(() => {
			ageRef.value += 1
		}, 1000)
		
		return {
			ageRef
		}
	},
}
</script>

toRef

  1. A prop for a reactive object (reactive wrapper)
  2. Create a ref with responsive
  3. Both maintain a reference relationship
<template>
	<div>{
   
   { state.age }} --- {
   
   { ageRef }}</div>
</template>

<script>
import { toRef, reactive } from 'vue'
export default {
	setup() {
		const state = reactive({
			name: 'JL',
			age: 18
		})
		const ageRef = toRef(state, 'age')
		setTimeout(() => {
			state.age = 20
		}, 1000)
		
		setTimeout(() => {
			ageRef.value = 21
		}, 2000)
		
		return {
			state,
			ageRef
		}
	},
}
</script> 

Use toRef to change the age attribute of the state into a responsive variable, and then change the age value of the state to 20 after 1 second, and ageRef will also become 20 at this time; change the value of ageRef to 21 after 2 seconds, this At this time, the age value of the state will also become 21, indicating that the two maintain a mutual reference relationship

toRef is for responsiveness, not for ordinary objects. If it is used for non-responsiveness, the output result will not be responsive.

toRefs, to avoid all state exports in the template

  1. Convert responsive objects (reactive encapsulation) into ordinary objects
  2. Each prop of the object is the corresponding ref
  3. Both maintain a reference relationship
<template>
	<div>{
   
   { name }}---{
   
   { age }}</div>
</template>

<script>
import { reactive, toRefs } from 'vue'
export default {
	setup() {
		const state = reactive({
			name: 'JL',
			age: 18
		})

		const stateRefs = toRefs(state)

		setTimeout(() => {
			state.age = 20
		}, 1000)

		setTimeout(() => {
			stateRefs.age.value = 21
		}, 2000)

		return stateRefs
	},
}
</script>

Use toRefs to convert the state into an ordinary object. At this time, you can directly return stateRefs. At this time, you can directly call name and age in the template.

Best way to use

  1. Use reactive to respond to objects, and use ref to respond to value types
  2. Return toRefs(state) or toRef(state, 'prop') in setup
  3. The variables of ref are named with xxxRef
  4. When the composite function returns a responsive object, use toRefs
<template>
	<div>x:{
   
   {x}} y:{
   
   {y}}</div>
</template>

<script>
import { reactive, toRefs } from 'vue'
export default {
	setup() {
		function test() {
			const state = reactive({
				x: 1,
				y: 2
			})
			return toRefs(state)
		}
		const {x, y} = test()

		setTimeout(() => {
			x.value = 2
		}, 1000)

		return {
			x,
			y
		}
	}
}
</script>

In the above code, the responsive object state is defined in the test function, and it is converted into a normal object through toRefs and returned. At this time, the structure can be assigned, and the value is responsive

How does the Composition API implement code logic reuse?

  1. Extract logic code into a function
  2. Function naming convention is useXXX format (React Hooks too)
  3. Reference the useXXX function in setup

Case: the method to get the current mouse position

The first one is to directly use the useMousePosition defined by ref – in this way, both export and import can be deconstructed at will

// useMousePosition.js
import { ref, onMounted, onUnmounted } from 'vue'
 
// 1. 定义一个函数,抽离逻辑,命名使用 useXXX
function useMousePosition() {
  // 使用ref定义
  const x = ref(0)
  const y = ref(0)
 
  function update(e) {
    console.log(x.value, y.value);
 
    x.value = e.pageX
    y.value = e.pageY
  }
 
  onMounted(() => {
    console.log('开始监听鼠标划动事件');
    window.addEventListener('mousemove', update)
  })
 
  onUnmounted(() => {
    console.log('解除监听鼠标划动事件');
    window.removeEventListener('mousemove', update)
  })

  return {
    x, 
    y
  }
}
 
 
// 导出这个函数
export default useMousePosition

transfer

<!-- 在任意一个组件,都可以调用这个方法 -->
 
<template>
  <p>mouse position: {
   
   {x}}, {
   
   {y}}</p>
</template>
 
<script>
import useMousePosition from './useMousePosition'
export default {
  name: 'MousePosition', 
  setup() {
    // useMousePosition是使用ref定义变量的,这种可以解构
    const { x, y } = useMousePosition()
    console.log(x, y)
    return {
      x, y
    }
  }
}
</script>

The second way is to use reactive to define the export method of the mouse coordinate object, which cannot be deconstructed when imported into the component

// useMousePosition.js'
import {  onMounted, onUnmounted, reactive } from 'vue'
 
export function useMousePosition2() {
  // 使用reactive定义
  const mouse = reactive({
    x: 0, 
    y: 0
  })
 
  function update(e) {
    mouse.x = e.pageX
    mouse.y = e.pageY
  }
 
  onMounted(() => {
    console.log('开始监听鼠标划动事件');
    window.addEventListener('mousemove', update)
  })
 
  onUnmounted(() => {
    console.log('解除监听鼠标划动事件');
    window.removeEventListener('mousemove', update)
  })
 
  return {
    mouse
  }
}
<template>
  <!-- 使用对象方式显示信息 -->
  <p>mouse position2: {
   
   {mouse.x}}, {
   
   {mouse.y}}</p>
</template>
<script>
import { useMousePosition2 } from './useMousePosition'
export default {
  name: 'MousePosition', 
  setup() {
    // useMousePosition2是使用reactive定义的,这种不可以解构
    const { mouse } = useMousePosition2()
    return {
      mouse
    }
  }
}
</script>

The third way is to use toRefs In this way, the reactive object can be deconstructed into a ref object

export function useMousePosition3() {
  // 使用reactive定义
  const mouse = reactive({
    x: 0, 
    y: 0
  })
 
  function update(e) {
    mouse.x = e.pageX
    mouse.y = e.pageY
  }
 
  onMounted(() => {
    console.log('开始监听鼠标划动事件');
    window.addEventListener('mousemove', update)
  })
 
  onUnmounted(() => {
    console.log('解除监听鼠标划动事件');
    window.removeEventListener('mousemove', update)
  })
  
  // 这里,使用toRefs解构成ref对象
  return toRefs(mouse)
}

transfer

<template>
  <p>mouse position: {
   
   {x}}, {
   
   {y}}</p>
</template>
 
<script>
import { useMousePosition3 } from './useMousePosition'
export default {
  name: 'MousePosition', 
  setup() {
    // 使用reactive定义鼠标坐标对象,然后通过toRefs将其解构成ref对象
    const { x, y } = useMousePosition()
    console.log(x, y)
    return {
      x, y
    }
  }
}
</script>

All three methods can be implemented, but we usually return ref objects when we use them, so it is recommended to use the first and third methods, and try not to use the second method

Reference address:

  • https://tangjiusheng.com/web/4935.html
  • https://blog.csdn.net/weixin_41759744/article/details/125305021
  • https://www.qetool.com/scripts/view/27412.html

Guess you like

Origin blog.csdn.net/BradenHan/article/details/131039578