Vue.js 比较重要知识点总结三

news2024/11/22 17:24:20

概述

  • Vue 中 nextTick 的实现原理
  • v-if 和 v-show 的区别
  • Vue 中的 key 有什么作用
  • 如何理解ref toRef和toRefs
  • Composition API如何实现代码逻辑复用?

Vue 中 nextTick 的实现原理

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。

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

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

2.1.0 起新增:如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。

请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不原生支持 Promise (IE),你得自己提供 polyfill。

更多精彩内容,请微信搜索“前端爱好者戳我 查看

可能你还没有注意到,Vue 在更新 DOM 时是异步执行的

只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。

然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.thenMutationObserversetImmediate ,如果执行环境不支持,则会采用setTimeout(fn, 0) 代替。

例如,当你设置 vm.someData = ‘new value’ ,该组件不会立即重新渲染。

当刷新队列时,组件会在下一个事件循环“tick”中更新。

多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数将在 DOM 更新完成后被调用

案例

例如:

<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
})

在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue ,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:

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) // => '已更新 '
            })
        }
    }
})

因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2017 async/await 语法完成相同 的事情:

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

总结:

nextTick 方法主要是使用了宏任务和微任务,定义了一个异步方法,多次调用 nextTick 方法会将方法存 入队列中,通过这个异步方法清空当前队列。所以这个 nextTick 方法就是异步方法。

v-if 和 v-show 的区别

v-if 是“真正” 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和 重建。

  • true:渲染
  • false:销毁不渲染,元素就不存在了

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做 ——直到条件第一次变为真时,才会开 始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

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

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。

因此,如果需要非常频繁地 切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

特别注意

在 vue2 中 v-for 的优先级更高,但是在 vue3 中优先级改变了。v-if 的优先级更高。

在 vue2 中 v-for 和 v-if 同时出现时,可放在一个标签内,如下写法:

<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' },
        ]
    }
 }

在 vue3 中这样写会报错,就是因为 v-if 的优先级更高,所以 item.show 是未定义报错了。

Vue 中的 key 有什么作用

key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。

Vue 的 diff 过程可以概括为:

oldCh 和 newCh 各有两个头尾的变量 oldStartIndex 、oldEndIndexnewStartIndex 、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:

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

如果以上 4 种比较都没匹配

如果设置了key,就会用 key 再进行比较,在比较的过程中,遍历会往中间靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。

具体有无 key 的 diff 过程,可以查看作者写的另一篇详解虚拟 DOM 的文 章《深入剖析:Vue核心之虚拟DOM》

所以 Vue 中 key 的作用是:

key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以 更准确、更快速

更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地 复用的情况。所以会更加准确。
更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,源码如下:

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
}

如何理解ref toRef和toRefs

ref

  1. 生成值类型的响应式数据
  2. 可用于模板和reactive
  3. 通过.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. 针对一个响应式对象(reactive封装)的prop
  2. 创建一个ref,具有响应式
  3. 两者保持引用关系
<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> 

使用toRef将state的age属性变成一个响应式变量,然后在1秒后将state的age值变为20,此时ageRef也会变成20;在2秒后将ageRef的值变为21,此时state的age值也会变成21,说明了两者保持相互引用关系

toRef针对的是响应式,针对的不是普通对象,如果用于非响应式,产出的结果不具有响应式

toRefs,避免模板中导出都是state

  1. 将响应式对象(reactive封装)转换成普通对象
  2. 对象的每个prop都是对应的ref
  3. 两者保持引用关系
<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>

使用toRefs将state转变成一个普通对象,这时候就可以直接返回stateRefs,这时候在template就可以直接调用name和age。

最佳使用方式

  1. 用reactive做对象的响应式,用ref做值类型的响应式
  2. setup中返回toRefs(state),或者toRef(state, ‘prop’)
  3. ref的变量命名都用xxxRef
  4. 合成函数返回响应式对象时,用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>

上面的代码,test函数中定义了响应式对象state,并通过toRefs将其转为普通对象并返回,这时候可以结构赋值,并且值是响应式的

Composition API如何实现代码逻辑复用?

  1. 抽离逻辑代码到一个函数
  2. 函数命名约定为useXXX格式(React Hooks也是)
  3. 在setup中引用useXXX函数

案例:获取当前鼠标位置的方法

第一种,直接使用ref定义的useMousePosition–这种方式,导出和导入都可以随意解构

// 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

调用

<!-- 在任意一个组件,都可以调用这个方法 -->
 
<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>

第二种,使用reactive定义鼠标坐标对象 这种导出的方式,在组件中导入时是不能解构的

// 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>

第三种,使用toRefs 使用这种方式,可以将reactive对象,解构为ref对象

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)
}

调用

<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>

三种方式都可以实现,但是我们一般使用时,都会返回ref对象,所以比较建议使用第一种和第三种,尽量不使用第二种

参考地址:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/609906.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python在线聊天:实现即时通讯的快速解决方案

Python在线聊天&#xff1a;实现即时通讯的快速解决方案 在当今数字时代&#xff0c;在线聊天已经成为人与人之间交流的主流方式。Python在线聊天应用程序提供了一种快速且可定制的解决方案&#xff0c;使个人用户和企业可以进行互联网通信。本文将向您介绍Python在线聊天的基…

软考A计划-电子商务设计师-电子商务系统建设

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

深挖MYSQL大表加索引

深挖MYSQL大表加索引 起因是这样的&#xff0c;有一张表存在慢sql&#xff0c;查询耗时最多达到12s&#xff0c;定位问题后发现是由于全表扫描导致&#xff0c;需要对字段增加索引&#xff0c;但是表的数据量600多万有些大&#xff0c;网上很多都说对大表增加索引可能会导致锁…

垂直行业(新站)SEO流量快速起飞的核心思路

现在做站不比以前了&#xff0c;不管你是做百度也好&#xff0c;还是谷歌也罢&#xff0c;对于行业精准SEO流量来说肯定是没有以前那么容易做了。但是不容易做不代表没有机会做&#xff0c;机会一直还是有的&#xff0c;尤其是最近百度打击泛站&#xff0c;对于垂直行业来说其实…

chatgpt赋能python:Python图片尺寸大小修改指南

Python图片尺寸大小修改指南 在现代网站设计中&#xff0c;图像是非常重要的一部分。图片质量和大小是网站排名和用户体验的关键因素。一般来说&#xff0c;网站应该尽量避免使用过多的大图片&#xff0c;因为它们会使用户等待过长的时间&#xff0c;同时也会降低网站的加载速…

JAVA基础练习(6)

目录 1.冒泡排列学员成绩(降序) 2.常用Arrays类的应用 2.1.sort 2.2.equals 2.3.toString 2.4.fill 2.5.Arrays.copyOf 2.6.binarySearch 3.利用二维数组计算成绩 1.冒泡排列学员成绩(降序) package ch06;import java.util.Arrays; import java.util…

代码随想录算法训练营第四十一天|343. 整数拆分|96.不同的二叉搜索树

LeetCode343. 整数拆分 动态规划五部曲&#xff1a; 1&#xff0c;确定dp数组&#xff08;dp table&#xff09;以及下标的含义&#xff1a;dp[i]&#xff1a;分拆数字i&#xff0c;可以得到的最大乘积为dp[i]。 2&#xff0c;确定递推公式&#xff1a;可以想 dp[i]最大乘积…

下载安装微软office的详细步骤

目录 一、前言 二、下载路径 &#xff08;一&#xff09;wps office 办公软件下载地址 1.wps office办公软件下载地址 &#xff08;二&#xff09;微软office 办公软件下载地址--2021 1.专业增强版 2.专业版 3.家庭专业版 4.家庭企业版 &#xff08;三&#xff09;…

JAVA基础练习(1)

目录 1.练习一:使用变量存储数据&#xff0c;实现个人简历信息的输出 2.练习二:使用Scanner类获取键盘输入的会员卡号&#xff0c;并将该数据存储在变量中&#xff0c;输出这个变量的信息 3.练习三:键盘输入四位数字的会员卡号,使用“/”和“%”运算符分解获得会员卡各个位上…

(二)模拟实现 《资源发现》框架

文章目录 前言资源发现《资源发现》概述技术难点 《资源发现》基本思想《资源发现》框架思考需求分析技术选择 《资源发现》技术难点实现《资源发现》框架实现资源发现基础类实现资源注册中心的实现资源持有者和资源请求者资源持有者和资源请求者功能具体实现 前言 《资源发现…

JAVA基础练习(4)

目录 1.利用循环打印九九乘法表 2.使用循环输出 100、95、90、85.......5 3.输入星期查看对应结果 4.几行数字展示 5.打印1-100之间13的倍数&#xff0c;使用for循环 6.用*来打印&#xff0c;根据用户输入rows和columns&#xff0c;来打印响应矩形 7.输入三个班&#xff…

YOLO8自定义检测实战

文章目录 资料模型介绍(或者叫weights)安装安装ultralytics&#xff08;yolo&#xff09;Torch测试命令 CLI命令行通过COCO128数据集体验yolov8标签predictsegment下载COCO 2017数据集ValTrain 自定义数据集标注标注软件labelimg分析训练结果 获得最佳训练结果提示 资料 Docs:…

docker学习记录

1.什么是docker&#xff1f; Docker是一个容器引擎&#xff0c;使用 Linux 内核功能&#xff08;如命名空间和控制组&#xff09;在操作系统之上创建容器。除了作为一种容器技术之外&#xff0c;Docker 还具有定义明确的包装器组件&#xff0c;这使打包应用程序变得十分容易&am…

Windows认证机制

windows认证基础 windows的认证包括三个部分&#xff1a; 本地认证&#xff1a;用户直接操作计算机登录账户网络认证&#xff1a;远程连接到工作组中的某个设备域认证&#xff1a;登录到域环境中的某个设备 本地认证 1、用户输入密码 2、系统收到密码后将用户输入的密码计…

LeetCode 周赛 348(2023/06/05)数位 DP 模板学会了吗

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 加入知识星球提问&#xff01; 往期回顾&#xff1a;LeetCode 单周赛第 347 场 二维空间上的 LIS 最长递增子序列问题 周赛 348 概览 T1. 最小化字符串长度&#xff08;Medium&…

chatgpt赋能python:Python基础教程:如何利用Python进行地区查询

Python基础教程&#xff1a;如何利用Python进行地区查询 在现代社会&#xff0c;人们越来越关注自己所处的地理位置和周边环境。这就导致了地区查询变得越来越流行&#xff0c;因为它可以让人们更加方便地获取自己想要的信息。 Python作为一门强大的编程语言&#xff0c;不仅…

chatgpt赋能python:Python在线模拟:提高编程技能的必备工具

Python在线模拟&#xff1a;提高编程技能的必备工具 Python是一种广泛应用于各行业的编程语言&#xff0c;也是许多工程师的选择。成为一名Python工程师意味着拥有高薪、稳定的职业和无尽的机会。但是如何成为一名高效的Python工程师&#xff1f;在线模拟器做到了提高技能和编…

Servlet与Mabatis-1

Web 应用开发 get 和 post 请求方法 &#xff08;重点&#xff09; http 协议中定义的请求方法有 DELETE、HEAD、GET、OPTIONS、POST、PUT、TRACE 在 http 协议中的两种常见的传参方法 get/post&#xff0c;例如 get 和 post 的共同点&#xff1a;Get 提交和 post 提交都是…

SpringBoot 3.x 新特性

SpringBoot 3.x 新特性 引用文章地址 SpringNative GraalVM 打包 SpringBoot 为 Linux 的 单文件应用程序 目录 JDK版本spring.factories废弃GraalVM — Spring Native三方包升级jakarta代替javax改进ConstructorBinding检测Log4j2增强杂七杂八 JDK版本 Springboot 3.x 基…

chatgpt赋能python:Python图片处理:让图像处理更简单

Python 图片处理&#xff1a;让图像处理更简单 作为一门强大的编程语言&#xff0c;Python 可以处理多种任务&#xff0c;其中之一是图形处理。Python 程序员可以使用各种库和工具&#xff0c;在不同的平台上进行图片处理、编辑和转换。在本文中&#xff0c;我们将讨论 Python…