【Vue实战教程】之 Vue3 新特性详解

1 为什么要用Vue3

在学习Vue3的新特性之前,我们先来看一下Vue3设计的目的是什么,为什么要对Vue2做出很大的改变,以及Vue3到底解决了什么问题。像Vue这样全球闻名的前端框架,在任何一次改动时,设计者都是经过深思熟虑的权衡,所以,Vue3的出现肯定是解决了某些棘手的问题。下面介绍一下Vue2中遇到的问题。

1.1 Vue2对复杂功能的处理不友好

使用Vue2开发项目的过程中,随着更加复杂业务逻辑的增加,复杂组件的代码变得难以维护。尤其是一个开发人员从别的开发人员手中接过一个新项目时,这个问题更突出。究其根本原因,Vue2中的API是通过选项来组织代码,但是大部分情况下,通过逻辑来组织代码会更有意义。Vue2中缺少多个组件之间提取和复用逻辑的机制,现有的重用机制都有很多缺点。

假设我们有一个需求,要对所有的视频进行分类管理,点击不同的学科分类,可以查看不同的视频列表,如图1所示。针对这个需求,可以设置一个filter过滤功能,根据用户点击的学科分类,展示不同内容。
在这里插入图片描述
图 1 视频分类效果

在上面的案例中,用户点击不同的学科分类,展示的视频列表如果超出了当前页面展示的数量,例如,每个分页下展示40个视频,超出范围的要使用分页显示,效果如图2所示。
在这里插入图片描述
图 2 视频列表分页效果

上面的案例,如果使用Vue2.0开发,示例代码如下:

export default {data() {return {filter: {}, //处理过滤功能pagination: {} //处理分页功能}},methods: {filterMethod: () => {},  //处理过滤功能paginationMethod: () => {} //处理分页功能},computed: {...}
}

通过上面代码可以看到,在data中已经做了filter过滤和pagination分页的相关处理,但是在下面的methods和computed中还需要继续做相关的处理,这些功能会分散到好几个部分。如果仅仅是这两个处理功能,项目的逻辑看上去还不是特别复杂,但是如果再增加搜索、收藏、排序等功能,随着功能复杂度的上升,带来的问题也愈加明显。

1.2 Vue2中mixin存在缺陷

在上一小节中提到的案例,Vue2也给出了解决方案,那就是使用mixin混入的方式。对上面案例中遇到的问题,我们可以使用mixin重写编写代码,示例代码如下:

const filterMixin = {data() {return {}},methods: {}
}const  paginationMixin = {data() {return {}},methods: {}
}export default {mixins: [filterMixin, paginationMixin]
}

在上面代码中,创建了两个mixin混入对象,filterMixin和paginationMixin,然后在Vue组件对象中使用mixins选项属性引入这两个对象。虽然这样可以暂时解决一些按逻辑分类的问题,但是这样做也会带来一些其他问题。
首先是会产生多个混入对象的属性和方法名称的命名冲突,其次是mixin对象所暴露的变量有什么作用,第三是把混入对象中的逻辑复用到其他的组件中,还会出现一些不可预知的问题。

1.3 Vue2对TypeScript的支持有限

Vue框架的开发者都清楚,Vue2对TypeScript的支持并不友好,这是因为在Vue中是依赖this上下文对象向外暴露属性,但是在组件中的this与普通的JavaScript中的Object对象处理的方式不同。其实,在Vue2设计时就没有考虑对TypeScript的集成和强制类型的相关问题,所以才导致在Vue2中使用TypeScript有很多阻碍。

2 Vue3.0简介

众所周知,前端技术一直更新的很快,特别是前端框架,更新速度更是极快的。在2020年4月21日晚上,Vue的作者尤雨溪在B站上直播分享了Vue3.0 Beta最新进展,直到9月19日,Vue3.0正式版才发布。这个耗时两年,历经99为代码贡献者,2600多次代码提交的大版本更新终于和众多开发者见面了。
为了减少前端开发者的学习成本,Vue2的大部分特性都完全地保留到Vue3中,你可以像使用Vue2一样,原封不动地使用Vue3,这是遵循了渐进式的准则。如果你是一个保守派,就想使用Vue2的写法,也是完全没有问题的。
Vue3增加了以下的新特性。

2.1 Vue3在性能上有很大提升

没有哪一位开发者不想要更快、更轻的框架,Vue3给开发者带来了极致的开发体验。整个Vue3的代码库被重新编写成了一系列独立的,并且实现了不同功能的模块。据官方介绍,Vue3的代码打包大小减少了41%,初次渲染速度提升55%,更新效率提升33%,内存使用率减少54%。这些数据都得益于Vue3中重构了虚拟DOM的写法,提升渲染速度。

2.2 Vue3推出了新的API

在Vue2中遇到了一些问题,例如,复杂组件的代码变得越来越难以维护,缺少一种纯粹的多组件之间提取和复用逻辑的机制。虽然Vue2中也提供了相关的解决方案,但是在Vue2中对于重用机制这一部分也存在了一些弊端。所以,Vue3中设计了Composition API,这也是我们本章重点介绍和使用的Vue3的新特性。
Composition这个单词是“组合”的意思,是Vue3中新推出的一系列API的合集,主要包括了以下API。

  • ref
  • reactive
  • computed
  • watch
  • 新的生命周期函数
  • 支持自定义Hooks函数
  • Teleport
  • Suspense
  • 全局API的修改和优化

2.3 更好的TypeScript支持

如果有在Vue2中集成TypeScript的开发者应该都体会过其中的痛苦,因为Vue2在推出的时候没有把TypeScript作为一个考量范围,那么在设计Vue3的时候,设计者们就痛定思痛地考虑了这方面的问题。
Vue3的源代码全部都是使用TypeScript语法编写的,提供了非常完备的类型定义,在使用Vue3开发项目时,可以把TypeScript语法深入到各个大型项目中,让开发者更加方便的享受类型推论等一系列TypeScript的红利。同时,还可以在VSCode等编辑器中安装相关的插件,完美的使用TypeScript的各种功能。

3 Vue3.0项目搭建

3.1 Vue CLI脚手架简介

学习Vue3之前要先配置Vue3和TypeScript的开发环境,我们本章节中使用的是Vue开发团队推出的官方脚手架工具——Vue CLI。
Vue CLI是一个基于Vue.js进行快速开发的完整系统,它提供了一系列与Vue框架相关的功能,例如,启动一个本地服务器、静态校验代码格式、运行单元测试、构建生产环境等。
在安装Vue CLI脚手架工具之前,需要先检查一下Node.js的版本,推荐使用Node.js10以上的版本。在cmd命令行工具中运行命令查看Node.js的版本,命令如下:

node --version

命令运行效果如图3所示。

在这里插入图片描述
图3 查看Node.js版本

3.2 安装Vue CLI

配置Vue3和TypeScript的开发环境之前,要先安装Vue CLI脚手架工具,安装命令如下。

npm install -g @vue/cli
## OR
yarn global add @vue/cli

由于npm访问的是境外服务器,很多情况下会出现请求速度慢或请求不到服务器的问题,推荐使用 cnpm 的方式下载Vue CLI,cnpm安装的命令如下。

npm install -g cnpm --registry=https://registry.npm.taobao.org

使用cnpm安装Vue CLI,命令如下。

cnpm install -g @vue/cli

如果你之前安装过Vue CLI,建议执行上面的命令来更新版本。安装成功后需要查看一下当前的Vue CLI版本是否为4.x,命令如下。

vue --version

查看版本命令运行效果如图4所示。

在这里插入图片描述
图4 查看Vue CLI版本

本节中使用的Vue CLI的版本为4.5.11,如果你的版本与本节中的版本不一致,不会造成负面影响,只需要保证你的版本为4.5.0以上即可,因为只有4.5.0以上的版本才能创建支持最新版的Vue3的基础项目。

3.3 创建Vue3项目

Vue CLI提供了命令行和UI界面创建项目的两种方式,无论使用哪种方式,创建的流程是完全一样的,只是展示的形式不太一样而已。本节使用的是命令行的方式创建Vue3项目。
在本地硬盘中创建一个project的目录,在该目录下启动命令行工具,创建vue3-basic的项目,执行以下命令。

vue create vue3-basic

在命令行工具中输入上面的命令,按回车,会出现如图5所示的选项。

在这里插入图片描述
图5 选择创建Vue项目模式

在图5中有三种创建模式,前两种分别是使用Vue2和Vue3默认的模板创建,第三种是手动创建,通过键盘的上下键选择创建模式,本节使用第三种手动创建的模式。
如果在你的命令行中没有出现“Vue3 Preview”选项,说明现在使用的是旧版的Vue CLI脚手架工具,需要更新版本,更新版本的方法参考3.2章节中的安装Vue CLI命令。
由于前两项创建模式不支持TypeScript语法,所以在命令行中,使用上下键选择 “Manually select features”模式,按回车进入下一步操作。效果如图6所示。

在这里插入图片描述
图6 安装需要的模块

在图6中提供了一系列课插拔的支持,涵盖了Vue3项目开发中需要的各种各样的功能,充分体现了Vue CLI工具的建议式配置项目的特点。在这一步骤中同样使用上下键选择需要的功能,选中需要安装的功能后按空格键在选择和取消选择之间进行切换,选择完成后按回车键确认,然后进入下一步操作。功能选择的效果如图7所示。

在这里插入图片描述
图7 功能选择

在下一步操作中需要选择Vue的版本,选择Vue3的版本,按回车进入下一步。效果如图8所示。

在这里插入图片描述
图8 选择Vue的版本

在下一步操作的提示中,系统询问了是否需要class-style的组件来支持TypeScript。由于Vue3已经对底层代码进行了重写,不需要class也可以很方便的进行开发,而且无需额外的配置。在这一步操作中,输入英文字母“n”,然后按回车进入下一步操作。效果如图9所示。

在这里插入图片描述
图9 选择是否使用class-style

在下一步操作中提示是否需要Babel和TypeScript结合使用,Babel会自动添加polyfills,并转换JSX。因为我们创建的Vue3项目中没有使用到JSX,所以这一步仍然输入字母“n”,继续按回车进入下一步操作。效果如图10所示。
在这里插入图片描述
图10 选择是否使用Babel

下一步选择是否使用“history”路由模式,这里输入“y”,按回车继续下一步,效果如图11所示。

在这里插入图片描述
图11 选择路由模式

下一步是询问是将配置信息放到一个独立的文件中还是放到package.json文件中,这里选择的是放到package.js文件中,然后按回车继续进入下一步。效果如图12所示。

在这里插入图片描述
图12 选择配置文件的类型

最后一步是询问是否将前面步骤的选择保存为一个模板,方便在以后的项目创建中一键安装,这里选择“n”,按回车。效果如图13所示。

在这里插入图片描述
图13 是否保存模板

安装上面的操作步骤完成后,会进入安装流程,这个环境需要进行一段时间的等待。当命令行工具中显示如图14所示的效果,就表示项目创建成功。

在这里插入图片描述
图14 项目创建成功

项目创建成功后,在命令行工具中继续输入如下命令,启动本地服务器。

cd vue3-basic
npm run serve

服务器启动成功效果如图15所示。

在这里插入图片描述
图15 启动本地服务器

服务器启动成功后,在浏览器中访问 http://localhost:8080/ 访问项目,效果如图16所示。

在这里插入图片描述
图16 在浏览器中访问Vue3项目

如果你在浏览器中成功打开如图16所示的页面,Vue3的项目就搭建成功了。

4 Vue3项目的目录结构

Vue3项目的目录结构如下。

- node_modules 项目的依赖管理目录
- public 公共资源管理目录|-- favicon.ico 站点title的图标
|-- index.html 站点的静态网页
- src 源码管理目录|-- assets 静态资源管理目录|-- components 公共组件管理目录|-- router 路由管理目录|-- store 状态管理目录|-- views 视图组件管理目录|-- App.vue 项目根组件|-- main.ts 项目入口文件|-- shims-vue.d.ts 定义.vue类型的TypeScript配置文件
- package-lock.js 依赖管理配置文件
- package.js 依赖管理配置文件
- tsconfig.json TypeScript配置文件

Vue3的项目目录结构与Vue2的类似,唯一不同的是很多.js文件都改为了.ts文件。其中,src目录下的shims-vue.d.ts是用来定义vue类型的TypeScript配置文件。因为.vue结尾的Vue组件文件在TypeScript中是不能被直接识别的,所以需要使用该配置文件来说明.vue的类型,便于TypeScript进行解析。
Vue3中main.ts入口文件的源代码与Vue2也有很大的差别。我们先来看一下Vue2中的main.js的代码,示例代码如下:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')

在Vue2中使用new关键字实例化Vue对象,然后通过构造函数将选项属性传入到Vue实例中。而Vue3对main.js做了修改,在Vue3中使用的是.ts类型的文件编写的入口文件,示例代码如下:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'createApp(App).use(store).use(router).mount('#app')

Vue3的main.ts文件中引入了createApp函数,然后再引入App.vue组件,调用createApp函数来创建Vue实例,将所有的模块使用Vue实例对象进行调用,而不是像Vue2中直接传入到Vue对象的构造方法中,这是Vue3做的很大的改进。

5 Composition API 详解

Composition API是Vue的下一代主要版本中最常用的语法,它是一种全新的逻辑重用和代码组织的方法。在Vue2中使用的是Options API的方式构建组件,如果要向Vue组件中添加业务逻辑,需要先填充选项属性,例如data、methods、computed等。这种方式最大的缺点是,它本身并不是有效的JavaScript代码,需要先了解模板中可以访问哪些属性,然后再使用Vue的编译器将这些属性转换成可以执行的代码,这样做既消耗了性能,有无法让使用做更好的类型检查。
Composition API设计的目的通过将当前可用组件属性作为JavaScript函数暴露出来,这种机制可以基于功能的附加API灵活地组合组件逻辑,使Composition API编写的代码更易读。
下面我们来学习Composition API的语法。

5.1 setup函数

setup()函数是vue3中专门新增的方法,可以理解为Composition Api的入口。
Composition API的主要思想是,将Vue组件的选项属性定义为 setup 函数返回的JavaScript变量,而不是将组件的功能(例如state、method、computed等)定义为对象属性。
setup函数的执行时机是在beforeCreate之后,created之前。setup函数有两个参数,第一个参数是用于接收props数据。示例代码如下:

export default {props: {msg: {type: String,default: () => {}}},setup(props) {console.log(props);}
}

setup函数的第二个参数是一个上下文对象,这个上下文对象大致包含了一些属性,示例代码如下:

const MyComponent = {setup(props, context) {context.attrscontext.slotscontext.parentcontext.rootcontext.emitcontext.refs}
}

这里需要注意,在setup函数中是无法访问this的。

5.2 reactive函数

reactive是用来创建一个响应式对象,等价于2.x的Vue.observable,示例代码如下:

<template><div><p @click="incment()">click Me!</p><p>一:{{ state.count }} 二: {{ state.addCount }}</p></div>
</template><script>
import { reactive } from 'vue';
export default {setup () {const state = reactive({//创建响应式数据count: 0,addCount: 0});function incment () {state.count++;state.addCount = state.count * 2;}return {state,incment};}
};
</script>

5.3 ref函数

ref函数用来给定的值创建一个响应式的数据对象,ref函数的返回值是一个对象,这个对象上只包含一个.value属性。示例代码如下:

import { ref } from 'vue';
export default {setup () {const valueNumber = ref(0);const valueString = ref('hello world!');const valueBoolean = ref(true);const valueNull = ref(null);const valueUndefined = ref(undefined);return {valueNumber,valueString,valueBoolean,valueNull,valueUndefined};}
};

使用ref函数定义的响应式属性,在template中访问的方法和Vue2一样,可以直接使用模板语法的形式访问,示例代码如下:

import { ref } from 'vue';
export default {setup () {const value = ref(1);return {value,msg: 'hello world!'};}
};

在template使用模板语法直接访问响应式属性,示例代码如下:

<template><p>{{ value }} {{ msg }}</p>
</template>

下面来对比一下Vue3中的ref函数与Vue2中data的区别。先使用Vue2的语法编写一个计算器的案例,示例代码如下:

//Counter.vue
export default {data: () => ({count: 0}),methods: {increment() {this.count++;}},computed: {double () {return this.count * 2;}}
}

下面再使用Composition API定义一个完全相同功能的组件,示例代码如下:

// Counter.vue
import { ref, computed } from "vue";export default {setup() {const count = ref(0);const double = computed(() => count * 2)function increment() {count.value++;}return {count,double,increment}}
}

在上面的示例中,使用Composition API提供的ref函数定义了一个响应式变量,其作用与Vue2的data变量几乎相同。在Vue3的示例代码中,increment方法是一个普通的JavaScript函数,需要更改子属性count的value才能更改响应式变量,这是因为使用ref函数创建的响应式变量必须是对象,以便于在传递的时候保持一致。
Composition API提供了更方便的逻辑提取方式,我们还以上面的代码为例,使用Composition提取Counter.vue组件的代码,创建useCounter.js文件,示例代码如下:

//useCounter.js
import { ref, computed } from "vue";export default function () {const count = ref(0);const double = computed(() => count * 2)function increment() {count.value++;}return {count,double,increment}
}

如果要在其他组件中使用该函数,只需要将模块导入组件文件并调用它即可,导入的模块是一个函数,该函数将返回我们定义的变量,然后可以从setup函数中返回它们。示例代码如下:

// MyComponent.js
import useCounter from "./useCounter.js";export default {setup() {const { count, double, increment } = useCounter();return {count,double,increment}}
}

这种操作还可以解决Vue2中mixins命名冲突的问题,示例代码如下:

export default {setup () {const { someVar1, someMethod1 } = useCompFunction1();const { someVar2, someMethod2 } = useCompFunction2();return {someVar1,someMethod1,someVar2,someMethod2}}
}

Composition API也提供了一些其他的ref辅助操作的函数。

isRef

用来判断某个值是否为ref创建出来的对象,在需要展开某个值可能是ref()创建出来的对象时使用。示例代码如下:

import { ref, isRef } from 'vue';
export default {setup () {const count = ref(1);const unwrappend = isRef(count) ? count.value : count;return {count,unwrappend};}
};

toRefs

torefs函数可以将reactive()创建出来的响应式对象转换为普通的对象,只不过这个对象上的每个属性节点都是ref()类型的响应式数据。示例代码如下:

<template><p><!-- 可以不通过state.value去获取每个属性 -->{{ count }} {{ value }}</p>
</template><script>
import { ref, reactive, toRefs } from 'vue';
export default {setup () {const state = reactive({count: 0,value: 'hello',})return {...toRefs(state)};}
};
</script>

toRef

toRef函数为源响应式对象上的某个属性创建一个ref对象,二者内部操作的是同一个数据值,更新时二者是同步。但是与ref的区别是,使用toRef函数拷贝的是一份新的数据单独操作,更新时相互不影响,相当于深拷贝。当要将某个prop的ref传递给某个复合函数时,toRef很有用。示例代码如下:

import { reactive, ref, toRef } from 'vue'export default {setup () {const m1 = reactive({a: 1,b: 2})const m2 = toRef(m1, 'a');const m3 = ref(m1.a);const update = () => {// m1.a++;//m1改变时,m2也会改变// m2.value++; //m2改变时m1同时改变m3.value++; //m3改变的同时,m1不会改变}return {m1,m2,m3,update}}
}

5.4 computed计算属性

computed函数用来创建计算属性,返回值是一个ref的实例。创建只读的计算属性,示例代码如下:

import { ref, computed } from 'vue';
export default {setup () {const count = ref(0);const double = computed(()=> count.value + 1);//1double++;//Error: "double" is read-onlyreturn {count,double};}
};

在使用computed函数期间,传入一个包含get和set函数的对象,可以额得到一个可读可写的计算属性。示例代码如下:

// 创建一个 ref 响应式数据
const count = ref(1)// 创建一个 computed 计算属性
const plusOne = computed({// 取值函数get: () => count.value + 1,// 赋值函数set: val => {count.value = val - 1}
})// 为计算属性赋值的操作,会触发 set 函数
plusOne.value = 9
// 触发 set 函数后,count 的值会被更新
console.log(count.value) // 输出 8

5.5 Vue3中的响应式对象

Vue2中的data和Vue3中的ref一样,都可以返回一个响应式对象,但是Vue2中使用的是object.defineProperty()来实现响应式的,这就导致Vue2的响应式出现一些限制。对于Vue2中新增一个响应式属性就会变得很困难。
在Vue2中,无法检测property的添加或者移除,对于已经创建的实例,Vue是不允许动态添加根级别的响应式属性的。如果要动态添加响应式对象的属性,可以使用Vue.set(object, propertyName, value)方法向嵌套对象中添加响应式属性。还可以使用 vm. s e t 实例方法动态添加响应式属性,这也是全局 V u e . s e t ( ) 方法的别名。这种操作对于一个 V u e 的初学者来说,很多时候需要小心翼翼的去判断到底什么情况下需要用 set 实例方法动态添加响应式属性,这也是全局 Vue.set() 方法的别名。 这种操作对于一个Vue的初学者来说,很多时候需要小心翼翼的去判断到底什么情况下需要用 set实例方法动态添加响应式属性,这也是全局Vue.set()方法的别名。这种操作对于一个Vue的初学者来说,很多时候需要小心翼翼的去判断到底什么情况下需要用set,什么情况下可以直接触发响应式。这就对初学者带来了很多困扰。
在Vue3中,这些问题都将成为过去式。Vue3采用了ES6的一个新特性,使用Proxy来实现响应式。Proxy对象用于定义基本操作的一个自定义行为,简单来说,Proxy对象就是可以让你对一个JavaScript中一切合法对象的基本操作进行自定义,然后用自定义的操作去覆盖对象的一些基本操作。
我们可以通过下面的两段代码来学习Vue3中是如何使用Proxy进行优化的。
Vue2中的响应式处理,示例代码如下:

Object.defineProperty(data, 'count', {get() {},set() {}
})

Vue3中对于响应式的优化,示例代码如下:

new Proxy(data, {get(key) {},set(key, value) {}
})

通过上面两段代码可以看出,Proxy是在更高维度上进行一个属性拦截修改的。我们先看Vue2的代码示例。对于给定的data对象,date对象中有一个count属性,需要根据具体的count去修改set函数。所以,在Vue2中对于对象上的新增属性是无能为力的。
而Vue3中使用的是Proxy进行拦截的,这里无需知道具体的key是什么,拦截的是修改data上任意的key和读取data上任意的key的操作。所以,无论是已有的key还是新增的key都可以被拦截。
Proxy更加强大之处在于,除了getter和setter对属性的拦截外,还可以拦截更多的操作符。

5.6 生命周期的改变

在Vue3中的生命周期和Vue2中的生命周期的用法是一样的。所谓生命周期,就是在一个组件从创建到销毁的全过程,会暴露出一系列的钩子函数供开发者在对应阶段进行相关的操作。
除了Vue2中已有的一部分生命周期钩子,Vue3还增加了一些新的生命周期,可以直接导入 onXXX 一族的函数来注册生命周期钩子。示例代码如下:

import { onMounted, onUpdated, onUnmounted } from 'vue'const MyComponent = {setup() {onMounted(() => {console.log('mounted!')})onUpdated(() => {console.log('updated!')})onUnmounted(() => {console.log('unmounted!')})},
}

Vue3的生命周期钩子函数只能在 setup() 期间同步使用,因为它们依赖于内部的全局状态来定位当前组件实例,不在当前组件下调用这些函数会抛出一个错误。组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除。
Vue3中与Vue2的生命周期相对应的组合式API如下。

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

Vue2生命周期的初创期钩子beforeCreate和created,在Vue3中使用setup()替代了。除了和 2.x 生命周期等效项之外,组合式 API 还提供了以下调试钩子函数。

  • onRenderTracked
  • onRenderTriggered

两个钩子函数都接收一个 DebuggerEvent,与 watchEffect 参数选项中的 onTrack 和 onTrigger 类似。示例代码如下:

export default {onRenderTriggered(e) {debugger// 检查哪个依赖性导致组件重新渲染},
}

5.7 watch侦测变化

watch函数用来监视某些数据项的变化,从而触发某些特定的操作,看下面这个案例,会实时监听count值的变化。示例代码如下:

import { ref, watch } from 'vue';
export default {setup () {const count = ref(1);watch(()=>{console.log(count.value, 'value');})setInterval(()=>{count.value++;},1000);return {count,};}
};

watch还可以监听指定的数据源,例如监听reactive的数据变化。示例代码如下:

import { watch, reactive } from 'vue';
export default {setup () {const state = reactive({count: 0})watch(()=>state.count,(count, prevCount)=>{console.log(count, prevCount);//变化后的值 变化前的值})setInterval(()=>{state.count++;},1000);return {state};}
};

watch用于监听ref类型的数据变化,示例代码如下:

import { ref, watch } from 'vue';
export default {setup () {const count = ref(0);watch(count,(count, prevCount)=>{console.log(count, prevCount);//变化后的值 变化前的值})setInterval(()=>{count.value++;},1000);return {count};}
};

在setup()函数内创建的watch监视,会在当前组件被销毁的时候自动停止。如果想要明确的停止某个监视,可以调用watch()函数的返回值即可。示例代码如下:

// 创建监视,并得到 停止函数
const stop = watch(() => {/* ... */
})// 调用停止函数,清除对应的监视
stop()

5.8 Vue3更好的支持TypeScript

Vue2依赖于this上下文对象向外暴露属性,但是在设计Vue2的API时,并没有考虑到与TypeScript集成。如果在Vue2中想要使用TypeScript语法的话,需要使用vue class或者是vue extends的方式来集成对TypeScript的支持。到了Vue3,Vue官方团队推出了一个新的方式定义component,这个方式被称为 definedComponent。示例代码如下:

import { defineComponent } from 'vue';export default defineComponent({setup(){function demo(str: String){console.log(str)}return {demo}}
});

5.9 Teleport传送门

Teleport 是一种能够将我们的模板移动到 DOM 中 Vue app 之外的其他位置的技术。例如,在项目中,像modals、toast等这样的元素,很多情况下,需要将它完全的和Vue应用的DOM完全剥离,这样会更加便于项目的管理。如果类似于modals、toast这样的注解嵌套在Vue的某个组件内部时,那么处理嵌套组件的定位、z-index和样式就会变得很困难。
Teleport就很好的解决了这一类问题。下面用一个例子来说明Teleport的用法。
在index.html中添加一个div元素,并指定其id属性值。示例代码如下:

<div id="app"></div>
<div id="teleport-target"></div>

在HelloWorld.vue文件中,添加teleport的组件代码,teleport组件上的to属性要和index.html新增的div的id选择器保持一致。示例代码如下:

  <button @click="showToast" class="btn">打开 toast</button><!-- to 属性就是目标位置 --><teleport to="#teleport-target"><div v-if="visible" class="toast-wrap"><div class="toast-msg">我是一个 Toast 文案</div></div></teleport>

在HelloWorld.vue文件中添加script脚本,示例代码如下:

import { ref } from 'vue';
export default {setup() {// toast 的封装const visible = ref(false);let timer;const showToast = () => {visible.value = true;clearTimeout(timer);timer = setTimeout(() => {visible.value = false;}, 2000);}return {visible,showToast}}
}

在上面的示例中,使用teleport组件,通过to属性指定该组件渲染的位置与

同级,但是teleport的状态visible又是完全由内部Vue组件控制的。

5.10 Suspense异步请求

Vue3新增了Suspense组件,可以允许应用程序在等待异步组件时渲染一些后备内容,帮助开发者创建一个平滑的用户体验。Suspense组件非常容易理解,也不需要任何额外的导入。
例如,有一个异步的ArticleInfo.vue的组件,其中setup方法是异步的,用于返回加载用户的数据。示例代码如下:

async function getArticleInfo() { // 一些异步API调用 return { article } 
}export default { async setup () {    
var { article } = await getArticleInfo() return { article    
}  
}} 

再创建一个ArticlePost.vue组件,在该组件中包含ArticleInfo.vue组件。如果要在等待组件获取数据并解析时显示“正在加载…”的内容,只需要三步就可以实现Suspense。

  • 将异步组件包装在<template #default>标记中
  • 在我们的Async组件的旁边添加一个兄弟姐妹,标签为<template #fallback>
  • 将两个组件都包装在组件中

使用插槽,Suspense将渲染后备内容,直到默认内容准备就绪。然后,它将自动切换以显示我们的异步组件。示例代码如下:

<Suspense> <template #default> <article-info/> </template> <template #fallback> <div>正在拼了命的加载…</div> </template> 
</Suspense> 

5.11 全局API修改

Vue 2.x 有许多全局 API 和配置,这些 API 和配置可以全局改变 Vue 的行为。例如,要创建全局组件,可以使用 Vue.component 这样的 API。示例代码如下:

Vue.component('button-counter', {data: () => ({count: 0}),template: '<button @click="count++">Clicked {{ count }} times.</button>'
}) 

虽然这种声明方式很方便,但它也会导致一些问题。在Vue2中只能通过new Vue()创建根Vue实例,从同一个Vue构造函数创建的每个根实例共享相同的全局配置,因此,在测试期间,全局配置很容易意外地污染其他测试用例。
Vue3中增加了createApp这个新的全局API,调用createApp返回一个应用实例。示例代码如下:

import { createApp } from 'vue' 
const app = createApp({})

任何全局改变Vue行为的API在使用了createApp之后,都会转移到应用实例上。如下表所示,Vue2中的全局API转移到了Vue3中的实例API上面。

Vue2全局APIVue3实例API(app)
Vue.configapp.config
Vue.config.productionTipremoved
Vue.config.ignoredElementsapp.config.isCustomElement
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use
Vue.prototypeapp.config.globalProperties

所有其他不全局改变行为的全局 API 现在被命名为 exports。

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

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

相关文章

Kithara和Halcon (二)

Kithara使用Halcon QT 进行二维码实时识别 目录 Kithara使用Halcon QT 进行二维码实时识别Halcon 简介以及二维码检测的简要说明Halcon 简介Halcon的二维码检测功能 Qt应用框架简介项目说明关键代码抖动测试测试平台&#xff1a;测试结果&#xff1a; 开源源码 Halcon 简介以…

vue3+vite纯前端实现自动触发浏览器刷新更新版本内容,并在打包时生成版本号文件

前言 在前端项目中&#xff0c;有时候为了实现自动触发浏览器刷新并更新版本内容&#xff0c;可以采取一系列巧妙的措施。我的项目中是需要在打包时候生成一个version.js文件&#xff0c;用当前打包时间作为版本的唯一标识&#xff0c;然后打包发版 &#xff0c;从实现对版本更…

基于SpringBoot的矩形范围面时空分析-以震中附近历史地震为例

目录 前言 1、分析的必要性 2、分析的紧迫性 一、数据库物理模型及空间分析实现 1、数据库物理模型 2、空间数据库中的空间查询分析 二、Java后台程序开发 1、模型层设计 2、业务层的设计与实现 三、WebGIS功能设计与实现 1、同时展示4幅地图 2、初始化地图 3、展示…

动态创建标签jQuery效果

动态创建标签jQuery效果https://www.bootstrapmb.com/item/14832 使用jQuery来动态创建HTML标签并添加效果是一种常见的方法。以下是一个简单的示例&#xff0c;说明如何使用jQuery来动态创建<div>标签&#xff0c;并给它们添加一些基本的效果。 1. 创建一个新的<di…

go语言day17 通道channel

Golang-100-Days/Day16-20(Go语言基础进阶)/day18_channel通道.md at master rubyhan1314/Golang-100-Days (github.com) go语言day09 通道 协程的死锁-CSDN博客 channel for range 循环通道对象 单向通道 单项通道常用于函数参数&#xff0c;只是用来限定在函数中只能进行通道…

Langchain核心模块与实战[8]:RAG检索增强生成[loader机制、文本切割方法、长文本信息处理技巧]

Langchain核心模块与实战[8]:RAG(Retrieval Augmented Generation,检索增强生成) RAG(Retrieval-Augmented Generation)技术是一种结合检索和生成功能的自然语言处理(NLP)技术。该技术通过从大型外部数据库中检索与输入问题相关的信息,来辅助生成模型回答问题。其核心…

Mysql中(基于GTID方式)实现主从复制,单主复制详细教程

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

《深入探秘Java中的枚举:掌握Enum的魔力》

目录 &#x1f4dd; 枚举枚举的定义枚举的使用1、表示一组固定常量2、实现接口3、枚举与策略模式4、EnumSet5、EnumMap &#x1f4ce; 参考文章 &#x1f600; 准备好了吗&#xff1f;让我们一起步入这座Java神奇的城堡&#xff0c;探寻枚举&#xff08;Enum&#xff09;这个强…

grafana对接zabbix数据展示

目录 1、初始化、安装grafana 2、浏览器访问 3、安装zabbix 4、zabbix数据对接grafana 5、如何导入模板&#xff1f; ① 设置键值 ② 在zabbix web端完成自定义监控项 ③ garafana里添加nginx上面的的三个监控项 6、如何自定义监控项&#xff1f; 以下实验沿用上一篇z…

Python学习笔记44:游戏篇之外星人入侵(五)

前言 上一篇文章中&#xff0c;我们成功的设置好了游戏窗口的背景颜色&#xff0c;并且在窗口底部中间位置将飞船加载出来了。 今天&#xff0c;我们将通过代码让飞船移动。 移动飞船 想要移动飞船&#xff0c;先要明白飞船位置变化的本质是什么。 通过上一篇文章&#xff0…

vue上传Excel文件并直接点击文件列表进行预览

本文主要内容&#xff1a;用elementui的Upload 组件上传Excel文件&#xff0c;上传后的列表采用xlsx插件实现点击预览表格内容效果。 在项目中可能会有这样的需求&#xff0c;有很多种方法实现。但是不想要跳转外部地址&#xff0c;所以用了xlsx插件来解析表格&#xff0c;并展…

使用 vSphere vCenter 管理 ESXi

使用 vSphere vCenter 管理 ESXi 1、新建数据中心 在 vSphere Client 中&#xff0c;左上角图标&#xff0c;进入 “清单”&#xff0c;鼠标右键名称&#xff0c;新建数据中心。 输入数据中心名称&#xff0c;我这里直接使用默认值&#xff0c;点击确定。 2、往数据中心中添加…

Linux epoll 机制——原理图解与源码实现分析

epoll概述 epoll是Linux内核为处理大批量文件描述符而作了改进的poll&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。 epoll可以理解为event poll&#xff0c;它是一种事件驱动的I/O模型&#xff0c;可以用来替代传统的select和poll模型…

leetcode-98. 验证二叉搜索树

题目描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&…

功能性的安全性保障:TOKEN鉴权校验

1. 引言 在软件开发过程中&#xff0c;确保系统的安全性是至关重要的一环。它不仅关乎保护用户数据的完整性和隐私性&#xff0c;也是维护系统稳定运行的基石。我认为&#xff0c;从宏观角度审视&#xff0c;软件开发的安全性保障主要可分为两大类&#xff1a;功能性的安全性保…

Golang | Leetcode Golang题解之第275题H指数II

题目&#xff1a; 题解&#xff1a; func hIndex(citations []int) int {n : len(citations)return n - sort.Search(n, func(x int) bool { return citations[x] > n-x }) }

JDBC操作MySQL数据

一准备、 1、首先在IDEA中导入导入包&#xff1a;mysql-connector-java-8.0.23 2、写初始化语句 &#xff08;1&#xff09;在目录下找到driver类 &#xff08;2&#xff09;在JDBCUtil函数中把驱动器的类路径改掉 ①打开driver类 ②按住类名 Driver用快捷键 CtrlAltshiftC …

AIGC的神秘面纱——利用人工智能生成内容改变我们的生活

近年来&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;正在迅速改变我们与数字世界互动的方式。从自动写作到图像生成&#xff0c;AIGC正逐渐走进我们的日常生活。它不仅提高了效率&#xff0c;还为创意和商业活动带来了新的可能性。让我们一起来探索AIGC的世界&…

17.jdk源码阅读之LinkedBlockingQueue

1. 写在前面 LinkedBlockingQueue 是 Java 并发包中的一个重要类&#xff0c;常用于生产者-消费者模式等多线程编程场景。上篇文章我们介绍了ArrayBlockingQueue&#xff0c;并且与LinkedBlockingQueue做了简单的对比&#xff0c;这篇文章我们来详细分析下LinkedBlockingQueue…

从零开始构建你的第一个Python Web应用

在本文中&#xff0c;我们将带领你从零开始构建一个简单的Python Web应用。不需要任何先验知识&#xff0c;我们会一步步地指导你完成设置、框架选择、代码编写到部署的整个过程。无论你是Web开发新手还是希望扩展技能的老手&#xff0c;这篇文章都将为你提供一个实践操作的起点…