【前端】第12节:Vue3新特性

引入

说起 vue3 的新特性,就会不由自主想到 vue3 和 vue2 之间的差异,例如:双向绑定、根节点数量、生命周期、this 等等,详细可以见这篇文章(参考)——

vue2和vue3的差异整理(轻松过度到vue3)_vue2和vue3区别-CSDN博客

咱们今天就主要来大概梳理一下 vue3 的新特性。

1composition API
2生命周期
3异步组件
4自定义指令
5teleport
6自定义hooks

目录

引入

vue3 和 vue2 响应式原理的区别

vue2 : Object.defineProperty()

vue3 : proxy

vue3 代码实现

特性一:composition API

1. 创建

2. 添加响应式的变化

reactive

shallowReactive

ref


vue3 和 vue2 响应式原理的区别

vue2 : Object.defineProperty()

我们知道,vue2 是通过 Object.defineProperty 这个api,重新定义对象的 gettersetter 对数据进行劫持,以此监测数据的响应变更,触发视图的更新,从而实现双向绑定。

 Object.defineProperty(官网):Object.defineProperty() - JavaScript | MDN

数据劫持(参考): 第二十一章 javascript数据代理(数据劫持)_js 数据代理-CSDN博客

那具体怎么实现的呢?我们可以试着手写一个:

// 初始数据
const initData = {value: 1
}
// 响应式数据
const data = {}Object.keys(initData).forEach(key => {Object.defineProperty(// 挂载对象data,// 挂载对象的 keykey,{// 挂载对象的 getter 和 setterget() {console.log("访问:", key)// 返回初始数据中的值return initData[key];},set(val) {console.log("修改", key)initData[key] = val;},})
})

在浏览器的控制台中测试:

因为添加的响应式数据 data 只能针对遍历后的 initData 进行监测,所以  data 无法获取到 initData 的新增属性,也就没法实现新增属性的双向绑定,这也是 vue2 的一个缺陷。

解决方法:$set

作用:可以对新增属性添加响应式的变化,并触发视图更新。

参考:

vue中$set用法详细讲解_vue $set-CSDN博客

这里简单介绍下 set ——

用法:Vue.set ( target , key , value )   

set 是如何实现的:

1. null undefined boolean 等这些类型会报错 因为只有 object 才可以设置属性 

2. 数组 : splice

        取两个数组的最大值,作为最新值,利用 splice 将更新值加入数组之中。

        因为 splice 是原型 (Array.prototype.splice) 上的一个方法 , 当 target 在收集依赖的时候,原型上的方法也会改变。此时会重新遍历数组,也就相当于是手动触发了一次更新~

3. 对象:判断 key 是否存在  ( target [ key ] = value )

        存在就替换

        不存在就判断target是否是响应式:是就替换,不是就报错。

        ( 如果是响应式的话,会用到 vue2 中的依赖收集,就会触发 defineReactive 进行更新 )

vue3 : proxy

proxy 是 js 提供的一种响应式的依赖,作用是作为一层拦截。当外界想要访问本身的对象时,需要通过一层拦截机制。所以 proxy 相当于是一层代理。

proxy(官网):Proxy() 构造函数 - JavaScript | MDN

使用:

// target : 需要拦截的目标
// handler : 拦截的方法
const proxy = new Proxy(target,handler)

代码实现 proxy :

const initData = {value: 1
}
const proxy = new Proxy(initData, {// receiver 用来接收数据get: (target, key, receiver) => {console.log("访问", key)// Reflect 针对当前数据进行拦截,是 proxy 默认提供的apireturn Reflect.get(target, key, receiver)},set: (target, key, value, receiver) => {console.log("修改", key)Reflect.set(target, key, value, receiver)}
})

 Reflect(官网):https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

浏览器控制台测试效果:

可以看到,proxy 实现了数据的双向绑定。无论是源数据还是新增属性,都可以监测到变化从而触发视图更新。

vue3 代码实现

现在正式开始讲 vue3 的新特性了~ 😜

首先创建一个项目:

// 创建项目
pnpm create vite-app demo// 进入文件
cd demo// 安装依赖
pnpm install

vue2 和 vue3 入口文件的差异:

vue2 中的声明只有一个 vue 的实例,因为 vue2 是单例模式,因此是用 this 上各种各样的属性去获取到内容。

new Vue({...
})this.xxx

vue3 通过 createApp 绑定到 App 实例上,再通过 mount 绑定到 app 这个节点上,来涵盖多种实例。 

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'createApp(App).mount('#app')

可以看到:

vue3 支持多实例,多实例之间相互隔离,互不影响。

特性一:composition API

vue2 的写法容易冗杂,很容易单页面就写出几千行代码的情况。就算有 mixin 但是容易因为变量命名而导致冲突。

vue3 则提供了 composition API ,把零散的逻辑耦合在一起,按照功能拆分成不同的子模块,利用 hooks,更方便代码阅读和后期维护。

由面向业务编程转为函数式编程,按照代码的本质进行操作,减少单文件的量级。

用代码示例 composition API:

1. 创建

main.js

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'// 引入
import Composition from './Compostion.vue'
// 挂载到创建的 composition 节点上
createApp(Composition).mount('#composition')createApp(App).mount('#app')

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vite App</title>
</head>
<body>
<div id="app"></div>
<!--composition-->
<div id="composition"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

Composition.vue (vue文件一般用大驼峰命名)

<template><div><div>count:{{ count }}</div></div>
</template><script>
import { defineComponent } from 'vue'
export default defineComponent({// setup:为 composition api 提供的新的属性,类似于 vue2 中的 beforeCreated 生命周期setup(){const count = 0return {count}}
})
</script><style scoped lang=scss></style>

页面效果:

当然,这只是一个非常简单的示例,也没有响应式,而且在项目中,更多的还是挂载到 #app 节点上。

2. 添加响应式的变化

reactive

是一个函数,接收一个对象,返回一个响应式的数据结构。

如果接收的对象层级很深(对象嵌套对象),则会通过递归去调用、关联。

官网:https://cn.vuejs.org/api/reactivity-core.html#reactive

创建个 reactive :

import {defineComponent, reactive} from 'vue'
export default defineComponent({setup() {const obj = {name: 'alikami',age: 24,contact: {phone: '1234567890',email: 'alikami@gmail.com'}}const reactiveObj = reactive(obj)console.log(reactiveObj, "<------reactiveObj-----------✨✨✨✨")return {obj}}
})

控制台打印结果:

 

说明,用 reactive 创建的对象是一个 proxy ,也就是响应式对象。

而且,reactiveObj 内部的对象也是响应式的,因为是递归关联的:

    const obj = {name: 'alikami',age: 24,contact: {phone: '1234567890',email: 'alikami@gmail.com'}}const reactiveObj = reactive(obj)const reactiveContact = reactiveObj.contactconsole.log(reactiveContact, "<----------reactiveContact-------✨✨✨✨")

打印结果:

shallowReactive

只针对表层内容做响应式创建,而不去递归。 

官网:https://cn.vuejs.org/api/reactivity-advanced.html#shallowreactive

reactive 与 shallowReactive (参考):Vue3中shallowReactive 与 shallowRef 的用法-CSDN博客

举个栗子:

const obj = {name: 'alikami',age: 24,contact: {phone: '1234567890',email: 'alikami@gmail.com'}
}const shallowReactiveObj = shallowReactive(obj)
const shallowReactiveContact = shallowReactiveObj.contactconsole.log(shallowReactiveObj, "<------shallowReactiveObj-----------✨✨✨✨")
console.log(shallowReactiveContact, "<------shallowReactiveContact-----------✨✨✨✨")

打印的 shallowReactiveObj 值为:

这时也是一个 proxy 对象,但是当我们打印其中的 contact 的时候,就会发现这只是个普通对象了:

所以 shallowReactive 只是实现浅层的响应式,对于深层次的对象不再具有响应性。

如果想转为响应式,就需要用到 toRef 或是 toRefs。要了解的话可以参考这篇——

vue3中的ref,toRef,toRefs三个是干嘛的,有什么作用呢。_vue3 ref torefs-CSDN博客

按照我们举的例子来说,如果想把 shallowReactiveContact 转化为响应式的,可以这样:

const obj = {name: 'alikami',age: 24,contact: {phone: '1234567890',email: 'alikami@gmail.com'}
}const shallowReactiveObj = shallowReactive(obj)
const shallowReactiveContact = shallowReactiveObj.contact
// 用 toRefs 进行转换
const toRefsShallowReactiveContact = toRefs(shallowReactiveContact)console.log(toRefsShallowReactiveContact, "toRefsShallowReactiveContact")

打印结果为:

ref

根据当前给定值,创建一个响应式对象,通过 .value 来获取。

官网:https://cn.vuejs.org/api/reactivity-core.html#ref

举个栗子:

const obj = {name: 'alikami',age: 24,contact: {phone: '1234567890',email: 'alikami@gmail.com'}
}const refObj = ref(obj)
console.log(refObj, "<-----------------✨✨✨✨")
console.log(refObj.value, "<-----------------✨✨✨✨")

打印结果:

ref 创建的也是深层次的响应。而且ref 对象是可更改的,也就是说你可以为 .value 赋予新的值。它也是响应式的,即所有对 .value 的操作都将被追踪,并且写操作会触发与之相关的副作用。

还需要注意,官网中有这样一段话:

① 如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。

② 若要避免这种深层次的转换,请使用 shallowRef() 来替代。

 我们先为 ① 举个栗子:

const contact = ref(0)
const obj = reactive({name: 'alikami',age: 24,contact
})// 结果为 0  因为会自动解包,不用.value来获取值,但是响应式还存在
console.log(obj.contact, "<-----contact.value------------✨✨✨✨")
// 结果为 true
console.log(obj.contact === contact.value, "<------------✨✨✨✨")contact.value = 1
// 结果为 1
console.log(contact.value, "<-----contact.value------------✨✨✨✨")
// 结果为 1
console.log(obj.contact, "<------obj.contact-----------✨✨✨✨")obj.contact = 2
// 结果为 2
console.log(contact.value, "<-----contact.value------------✨✨✨✨")
// 结果为 2
console.log(obj.contact, "<------obj.contact-----------✨✨✨✨")

响应式嵌套响应式,还是响应式的值。reactive 会把 ref 解包,这也是一种二者之间的关系。

未完待续。。。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 进程概念与进程状态

目录 1. 冯诺依曼体系结构2. 操作系统&#xff08;Operator System&#xff09;2.1 概念2.2 设计OS的目的2.3 系统调用和库函数概念 3. 进程概念3.1 描述进程 - PCB3.2 task_struct3.3 查看进程3.4 通过系统调用获取进程标识符PID&#xff0c; PPID3.5 通过系统调用创建fork 4.…

滑动窗口篇——如行云流水般的高效解法与智能之道(1)

前言&#xff1a; 上篇我们介绍了双指针算法&#xff0c;并结合具体题目进行了详细的运用讲解。本篇我们将会了解滑动窗口。滑动窗口是一种常用的算法技巧&#xff0c;主要用于处理子数组、子串等具有“窗口”特性的题目。柳暗花明&#xff0c;乃巧解复杂问题的高效之道。 一. …

网络安全-企业环境渗透2-wordpress任意文件读FFmpeg任意文件读

一、 实验名称 企业环境渗透2 二、 实验目的 【实验描述】 操作机的操作系统是kali 进入系统后默认是命令行界面 输入startx命令即可打开图形界面。 所有需要用到的信息和工具都放在了/home/Hack 目录下。 本实验的任务是通过外网的两个主机通过代理渗透到内网的两个主机。…

DB-GPT V0.6.2 版本更新:牵手libro社区、GraphRAG图谱构建能力增强等

DB-GPT V0.6.2版本现已上线&#xff0c;快速预览新特性&#xff1a; 新特性 1、DB-GPT 社区和 libro 社区共同发布 AWEL Notebook 功能 libro&#xff1a;灵活定制、轻松集成的 Notebook 产品方案。 社区地址&#xff1a;https://github.com/difizen/libro 使用教程&#xf…

GPT1.0 和 GPT2.0 的联系与区别

随着自然语言处理技术的飞速发展&#xff0c;OpenAI 提出的 GPT 系列模型成为了生成式预训练模型的代表。作为 GPT 系列的两代代表&#xff0c;GPT-1 和 GPT-2 虽然在架构上有着继承关系&#xff0c;但在设计理念和性能上有显著的改进。本文将从模型架构、参数规模、训练数据和…

本地部署与外部部署有何不同?

什么是本地部署&#xff1f; 本地部署&#xff08;通常缩写为“on-prem”&#xff09;是指在公司自己的设施或数据中心内托管的软件和基础设施。与基于云的解决方案不同&#xff0c;本地部署系统让企业对其数据、硬件和软件配置拥有完全的控制权。这种设置非常适合那些优先考虑…

游戏引擎学习第20天

解释 off-by-one 错误 从演讲者的视角&#xff1a;对代码问题的剖析与修复过程 问题的起因 演讲者提到&#xff0c;他可能无意中在代码中造成了一个错误&#xff0c;这与“调试时间标记索引”有关。他发现了一个逻辑问题&#xff0c;即在检查数组边界时&#xff0c;使用了“调试…

Android-如何实现Apng动画播放

01 Apng是什么 Apng&#xff08;Animated Portable Network Graphics&#xff09;顾名思义是基于 PNG 格式扩展的一种动画格式&#xff0c;增加了对动画图像的支持&#xff0c;同时加入了 24 位图像和8位 Alpha 透明度的支持&#xff0c;并且向下兼容 PNG。 Google封面图 02 A…

Linux下Intel编译器oneAPI安装和链接MKL库编译

参考: https://blog.csdn.net/qq_44263574/article/details/123582481 官网下载: https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit-download.html?packagesoneapi-toolkit&oneapi-toolkit-oslinux&oneapi-linoffline 填写邮件和国家,…

【Python系列】浅析 Python 中的字典更新与应用场景

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Matlab科研绘图:自定义内置多款配色函数

在Matlab科研绘图中&#xff0c;自定义和使用内置的多款配色函数可以极大地增强图表的视觉效果和数据的可读性。本文将介绍配色函数&#xff0c;共计带来6套配色体系&#xff0c;而且后续可以根据需要修改&#xff0c;帮助大家自定义和使用配色函数。 1.配色函数 可以根据个…

网络安全的学习方向和路线是怎么样的?

最近有同学问我&#xff0c;网络安全的学习路线是怎么样的&#xff1f; 废话不多说&#xff0c;先上一张图镇楼&#xff0c;看看网络安全有哪些方向&#xff0c;它们之间有什么关系和区别&#xff0c;各自需要学习哪些东西。 在这个圈子技术门类中&#xff0c;工作岗位主要有以…

JAVA八股与代码实践----JDK代理和CGLIB代理的区别

当spring发现该代理的类实现了接口会使用JDK代理&#xff0c;如果该类没有实现接口&#xff0c;会使用CGLIB代理 如果在springboot中要强制使用CGLIB代理&#xff0c;需要加上 EnableAspectJAutoProxy(proxyTargetClass true) // 强制使用 CGLIB SpringBootApplication Ena…

环境背景文本到语音转换

目录 概述演示效果核心逻辑使用方式 概述 本文所涉及的所有资源的获取方式&#xff1a;https://www.aspiringcode.com/content?id100000000027&uid2f1061526e3a4548ab2e111ad079ea8c 论文标题&#xff1a; 本文提出了 VoiceLDM&#xff0c;这是一种旨在生成准确遵循两种…

mac安装Pytest、Allure、brew

安装环境 安装pytest 命令 pip3 install pytest 安装allure 命令&#xff1a;brew install allure 好吧 那我们在安装allure之前 我们先安装brew 安装brew 去了官网复制了命令 还是无法下载 如果你们也和我一样可以用这个方法哦 使用国内的代码仓库来执行brew的安装脚本…

【Linux】重定向,dup

目录 文件描述符分配规则 重定向 dup ​编辑 输出重定向 追加重定向 输入重定向。 重定向会影响后面的程序替换吗&#xff1f; 1号文件和2号文件 2号文件输出重定向 下标之间的重定向 文件描述符分配规则 重定向 把显示器文件关闭后&#xff0c;本来应该写给显示器…

Vue实训---1-创建Vue3项目

1.创建项目&#xff08;项目名为my-vue-project&#xff09; npm create vitelatest my-vue-project -- --template vue 运行命令npm -v&#xff0c;查看npm版本号&#xff0c;如果是npm 7或更高版本运行以上命令即可。如果是npm 6或更低版本&#xff0c;使用npm create vite…

智慧社区方案提升居民生活质量与管理效率的创新实践

内容概要 智慧社区方案的背景与发展趋势指向了一个日益重要的方向&#xff0c;随着城市化进程的加快&#xff0c;传统的社区管理模式逐渐显得力不从心。在这个时候&#xff0c;智慧社区应运而生&#xff0c;它通过将现代信息技术与社区管理深度结合&#xff0c;为提升居民生活…

【IDER、PyCharm】免费AI编程工具完整教程:ChatGPT Free - Support Key call AI GPT-o1 Claude3.5

文章目录 CodeMoss 简介CodeMoss 的模型集成如何安装和配置 CodeMossIDER 插件安装步骤 CodeMoss 的实战使用AI 问答功能代码优化与解释优化这段代码解释这段代码 文件上传与对话联网查询与 GPT 助手联网查询GPT 助手 提升开发效率的最佳实践结语更多文献 CodeMoss 简介 CodeM…

Java安全—JNDI注入RMI服务LDAP服务JDK绕过

前言 上次讲到JNDI注入这个玩意&#xff0c;但是没有细讲&#xff0c;现在就给它详细地讲个明白。 JNDI注入 那什么是JNDI注入呢&#xff0c;JNDI全称为 Java Naming and Directory Interface&#xff08;Java命名和目录接口&#xff09;&#xff0c;是一组应用程序接口&…