Vue3(Ⅰ)
- 1、 概述
-
- 1.1、概述
- 1.2、使用 Vite 创建工程
-
- 1.2.1 Vite 介绍
- 1.2.2 创建工程
- 1.3、项目文件结构
- 2、 基础
-
- 2.1、setup
-
- 2.1.1 初识 setup
- 2.1.2 setup 的返回值
- 2.1.3 setup 的触发时机
- 2.1.4 setup 语法糖
- 2.2、ref 函数
-
- 2.2.1 概述
- 2.2.2 创建单个基本类型的响应式数据示例
- 2.2.3 RefImpl
- 2.2.4 Proxy 类
- 2.2.5 创建对象类型的响应式数据示例
- 2.3、reactive 函数
-
- 2.3.1 概述
- 2.3.2 创建对象类型的响应式数据示例
- 2.3.3 Object.assign 解决 reactive 存在的问题
- 2.4、toRef 和 toRefs
- 2.5、计算属性
- 2.6、监视属性
-
- 2.6.1 监视 ref 定义的基本类型的数据及停止监视
- 2.6.2 监视 ref 定义的对象类型的数据
- 2.6.3 监视 reactive 定义的对象类型的数据
- 2.6.4 监视对象的属性值
- 2.6.5 同时监视多个数据
- 2.6.6 watchEffect
- 2.7、ref 属性及 defineExpose
- 2.8、props
- 2.9、Vue3 的生命周期
- 2.10、自定义 Hook
1、 概述
1.1、概述
Vue.js 是一个流行的前端 JavaScript 框架,用于构建交互式的 Web 用户界面。Vue3 是 Vue.js 的下一个主要版本,带来了许多令人期待的新特性和改进
特性 | 描述 |
---|---|
Composition API (组合式 API) |
Vue 3 引入了 Composition API,这是一种新的组件组织方式,使得组件更易于阅读、理解和维护。它允许将代码按逻辑功能组织,而不是按照生命周期函数 |
响应性系统的重写 | Vue 3 中的响应性系统进行了重写,性能得到了显著提升。新的响应性系统采用了 Proxy 来实现,相比之前的 Object.defineProperty,Proxy 具有更好的性能 |
Teleport (传送门) |
Vue 3 引入了 Teleport 组件,使得在 DOM 中的任意位置渲染组件变得更容易。这对于在应用中实现模态框、通知、弹出菜单等功能非常有用 |
Fragment (片段) |
Vue 3 支持使用片段(Fragment),这允许组件返回多个根节点而无需包装元素。这样可以使得组件更加灵活 |
Suspense (悬挂) |
Vue 3 中引入了 Suspense 组件,用于优雅地处理异步操作。它能够在等待异步组件加载时显示备用内容,提高了用户体验 |
性能优化 | Vue 3 在性能方面进行了多项优化,包括虚拟 DOM 的改进、Tree-shaking 支持、更高效的组件更新算法等,使得应用程序的性能得到了提升 |
TypeScript 支持 | Vue 3 对 TypeScript 的支持更加友好,提供了更好的类型推断和类型检查,使得开发者在使用 TypeScript 时更加流畅 |
1.2、使用 Vite 创建工程
1.2.1 Vite 介绍
Vite(法语意为“快速的”,发音 /it,发音同“veet”)是一个由 Vue.js 核心团队开发的新型前端构建工具(对标 Webpack)。它的目标是提供一种快速、简单的开发体验,特别是针对现代化的前端开发。它主要由两部分组成:① 开发服务器,它基于 原生 ES 模块 提供了丰富的内建功能,如速度快到惊人的模块热更新(HMR)。② 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。Vite 意在提供开箱即用的配置,同时它的 插件 AP 和 javaScriptAPI 带来了高度的可扩展性,并有完整的类型支持
主要特点 | 描述 |
---|---|
快速的冷启动 | Vite 利用了 ES Module(ESM)的特性,在开发模式下不需要打包,而是直接使用原生 ES Module,因此具有非常快速的冷启动时间。这意味着在开发过程中,你可以更快地看到修改后的效果 |
按需编译 | Vite 可以以模块为单位进行编译,只编译你实际需要的部分。这样,在开发过程中,不需要重新编译整个应用,而是只编译修改的部分,从而提高了开发效率 |
内置开发服务器 | Vite 内置了一个开发服务器,支持热模块替换(HMR),并且使用原生 ES Module 运行代码,从而使得开发过程更加流畅 |
支持 Vue 3 和 TypeScript | Vite 对 Vue 3 和 TypeScript 有很好的支持,可以让你在项目中轻松地使用它们 |
插件系统 | Vite 提供了一个灵活的插件系统,可以轻松地扩展其功能,满足项目的特定需求 |
Webpack 的构建:入口文件开始,然后分析路由、分析模块、打包,服务才可以启动,因此尝试过使用 webpack 去进行项目的构建时会感受到还是相对较慢的,特别是写的组件一多的时候这种感觉就更明显了
Vite 的构建:Vite 一上来就直接可以启动服务,然后按需地进行分析、打包,因此在构建项目的时候会感觉到比 Webpack 快一些
总的来说,Vite 的出现为前端开发提供了一种全新的开发体验,尤其适用于 Vue.js 项目,使得开发者可以更快、更简单地构建出现代化的 Web 应用程序
1.2.2 创建工程
首先执行nrm use npm
切换成官方镜像,这样下载貌似快一点,然后执行npm create vue@latest
创建项目(应该是要比 vue-cli 在创建 vue2 要快一些),执行完创建命令之后,可以进行自定义的配置如下
① JSX(JavaScript XML):是一种 JavaScript 的语法扩展,它允许在 JavaScript 代码中直接编写类似 XML 或 HTML 的标签。JSX 最初由 React 引入,并广泛用于 React 应用开发中,但它也可以在其他框架中使用
② Pinia 是一个用于 Vue.js 应用的状态管理库,被设计为 Vuex 的下一代替代品。Pinia 提供了一种更简单、更直观的方式来管理应用状态,同时保持与 Vue 生态系统的良好集成
然后按照下方的绿色字体来启动项目,分别是:进入刚创建的项目目录cd vue_test
、安装依赖npm i
、启动npm run dev
(Vite 魅力时刻,可以体验下其构建的速度多快),执行之后就可以看到项目跑起来了
访问蓝字指示的服务器地址,就可以访问到内置的示例了
1.3、项目文件结构
新创建的 Vue3 项目可以看到其大致有如下的文件结构
vue_test/ # 根目录
├── .vscode/ # 存放 VsCode 的配置文件
│ └── extensions.json # Vue3 推荐开发者在该项目中使用的 VsCode 扩展
├── node_modules/ # 包存放目录
├── public/ # 静态资源目录
│ └── favicon.ico # 浏览器标签页图标
├── src/ # 源代码目录
│ ├── assets/ # 静态资源文件,如样式表、图片等
│ ├── components/ # Vue 组件文件夹,存放项目的组件
│ ├── App.vue # 根组件,是整个应用的主入口
│ └── main.ts # 入口文件,初始化并挂载 Vue 实例
├── .gitignore # git忽略文件
├── env.d.ts # TS 的声明文件,相当于 Webpack 配置文件中 resolve.extensions的作用,让 TS 能够识别其它后缀的文件
├── index.html # 项目的入口文件
├── package.json # 项目的配置文件
├── README.md # 项目的说明文件(可删)
├── tsconfig.app.json # 该文件常用于 Angular 项目中,用于配置 Angular 应用的 TS 编译选项
├── tsconfig.json # TS 的主要配置文件
├── tsconfig.node.json # 该文件常用于 NodeJS 项目中,用于配置 Node.js 应用的 TS 编译选项
└── vite.config.ts # Vite 构建工具的配置文件,用于配置 Vite 项目的构建选项、插件、中间件等
2、 基础
2.1、setup
2.1.1 初识 setup
使用 setup 有如下一些基本的注意点
① setup 函数是组件内部的一个特殊选项,它用于替代 Vue 2 中的 data、computed、methods 等选项,用于配置组件的状态和行为
② vue2 中 data 配置中的变量在 vue3 中作为 setup 函数中的普通变量,同理 vue2 中 methods 配置中的函数在 vue3 中作为 setup 函数中的普通函数
③ setup 函数中的 this 是 undefined,弱化了 this 的使用
④ setup 函数的执行时机先于 beforeCreate 钩子函数
<!-- src/components/Person.vue -->
<template>
<h2>姓名:{
{ name }}</h2>
<h2>年龄:{
{ age }}</h2>
<button @click="changeName">改变名字</button>
<button @click="changeAge">改变年龄</button>
</template>
<script>
export default {
// 组件名
name: "Person",
// setup 函数
setup() {
console.log(this); // undefined
let name = "niki";
let age = 18;
function changeName() {
name = "pyy";
}
function changeAge() {
age += 1;
}
return {
name, age, changeName, changeAge};
},
};
</script>
<style scoped>
button {
margin-right: 10px;
}
</style>
Tip
1、Vue3 的模板做了改进,现在不需要在 template 标签中再套一层 div 了,可以直接将内容写在 template 下
2、上面给出的例子中,在数据在变更之后不会响应式地渲染到页面中
2.1.2 setup 的返回值
当 setup 的返回值不是一个对象,而是一个返回了字符串的函数时,这个字符串将作为该组件的模板,即替换掉 template 中所写的内容
<!-- src/components/Person.vue -->
<template>
<h2>姓名:{
{ name }}</h2>
<h2>年龄:{
{ age }}</h2>
</template>
<script>
export default {
name: "Person",
setup() {
let name = "niki";
let age = 18;
return () => "Hello World"; // 返回一个字符串
},
};
</script>
2.1.3 setup 的触发时机
在组件实例化时,setup 函数是最先执行的代码之一,它比 beforeCreate 生命周期钩子执行的还要更早
<!-- src/components/Person.vue -->
<template></template>
<script>
export default {
name: "Person",
beforeCreate() {
console.log("this is beforeCreate");
},
setup() {
console.log("this is setup");
},
};
</script>
我们知道数据代理、数据监测工作在 created 生命钩子调用时才完成,因此,如果 setup 函数和 OptionsAPI (data、methods这些配置项)同时出现时,OptionsAPI 可以访问 setup 的内容。相反,由于 setup() 在 beforeCreate 之前就已经解析完毕,它不可能知道 data、methods… 中有什么东西,因此 setup() 不能访问 OptionsAPI 中的数据
<!-- src/components/Person.vue -->
<template>
<h2>姓名:{
{ myname }}</h2>
<h2>年龄:{
{ getAge() }}</h2>
</template>
<script>
export default {
name: "Person",
data() {
return {
myname: this.name,
};
},
methods: {
getAge() {
return this.age;
},
},
setup() {
let name = "niki";
let age = 18;
return {
name, age};
},
};
</script>
Tip:setup 函数需要将数据、函数 return 出来算是被挂载到组件实例身上,此时 data、methods 中才可以通过 this.xxx 来访问 setup 中的内容,没有 return 的话是访问不到的
2.1.4 setup 语法糖
由于在 setup 函数中每次定义完要进行 return 才能使用,有时候可能会忘记直到项目报错才想起来是忘记 return 了,会比较烦,因此可以采用 setup 语法糖来简写
<!-- src/components/Person.vue -->
<template>
<h2>姓名:{
{ name }}</h2>
<h2>年龄:{
{ getAge() }}</h2>
</template>
<script>
export default {
name: "Person",
};
</script>
<script setup>
let name = "niki";
let age = 18;
function getAge() {
return age;
}
</script>
这样的写法会有一个普通的 script 用来进行组件的配置,还有一个带有 setup 字眼的 script 标签用来承载组合式 API 的(因此组件名要是在这里配置会出错),如果觉得这样写很难受,想合并到一起的话,可以借助插件来解决这个问题
安装:npm i vite-plugin-vue-setup-extend -D
// 修改 vite.config.ts 文件
// 引入
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
// 添加插件
plugins: [
... // 其它插件
VueSetupExtend() // 添加这个
]
然后现在组件的名字可以通过 name 属性来指明
<!-- src/components/Person.vue -->
<template>
<h2>姓名:{
{ name }}</h2>
<h2>年龄:{
{ getAge() }}</h2>
</template>
<script setup name="Person">
let name = "niki";
let age = 18;
function getAge() {
return age;
}
</script>
2.2、ref 函数
2.2.1 概述
在 Vue 3 中,ref 是一个用于创建响应式数据的 API,它主要用于定义基本类型(如数字、字符串、布尔值)和对象类型的响应式引用。与 reactive API 不同,ref 更适用于单个值或引用的场景
注意:ref 可以用于创建单个基本类型的响应式数据,也可以用于创建对象类型的响应式数据,只不过更适用于前者
2.2.2 创建单个基本类型的响应式数据示例
<!-- src/components/Person.vue -->
<template>
<h2>姓名:{
{ name }}</h2>
<h2>年龄:{
{ age }}</h2>
</template>
<script setup name="Person">
import {
ref } from "vue";
let name = ref("niki"); // 响应式数据,一个 RefImpl 实例
console.log(name);
let age = 18; // 普通字符串
console.log(age);
function changeName() {
name.value = "pyy";
}
</script>
运行结果如下,可见使用 ref 创建响应式数据得到的是一个 RefImpl 实例,RefImpl 是 Vue 内部实现的一种机制,用来管理响应式引用的行为
此时在 script 标签中要访问、修改响应式数据需要调用 RefImpl 实例的 value 属性,而在模板中可以直接使用变量名即可,因为在背后会自动帮我们去调取其 value 值
2.2.3 RefImpl
RefImpl 是 Vue 3 中用来实现 ref 的基础类。它的主要作用是封装一个值,并通过 value 属性来访问和修改这个值,同时确保这个值在修改时能够触发依赖它的响应式更新
主要特性 | 描述 |
---|---|
value 属性 | RefImpl 的核心特性是其 value 属性,通过这个属性可以访问和修改被封装的值 |
响应式更新 | 当 value 被修改时,会触发依赖于这个 ref 的响应式更新 |
RefImpl 类的简要实现有如
class RefImpl {
// 属性
public dep;
private _value; // 原始值
// 构造函数
constructor(value) {
this._value = value;
this.dep = new Map(); // 用来存储依赖
}
// 对 _value 的数据监视
get value() {
track(this, 'get', 'value'); // 收集依赖
return this._value;
}
set value(newValue) {
if (newValue !== this._value) {
this._value = newValue;
trigger(this, 'set', 'value', newValue); // 触发依赖更新
}
}
}
// 暴露
function ref