Vue3 基础使用
约 5919 字大约 20 分钟
2025-04-09
1.Vue3 的编码环境
1.1.在线环境
- 如果你更喜欢不用任何构建的原始
HTML,可以使用 JSFiddle 入门 - 如果你已经比较熟悉
Node.js和构建工具等概念,还可以直接在浏览器中打开 StackBlitz 来尝试完整的构建设置
1.2.脚架工具
在以前使用 Vue2 的时候,我们体验过原生的 Vue2.js 代码直接引入 .html 来使用,也使用过 vue 的第一款脚手架工具 vue-cli,这种工具的体验还是相当不错的。
不过我们仅仅需要有 Vue3.js 也同样是个 JS 库的概念即可,这里不再使用传统的源代码引入和使用老旧的 vue-cli 脚手架,而是使用较为现代的 create-vue,这个脚手架内部又会默认使用 vite 构建工具。
重要
补充:vue-cli 和 create-vue 的区别。
| 特性 | create-vue | Vite |
|---|---|---|
| 功能 | 项目初始化工具,生成 Vue3 项目模板 | 构建工具与开发服务器,提供构建和开发支持 |
| 目标 | 提供 Vue3 项目的基础模板和结构 | 提供快速的开发体验与高效的生产构建 |
| 作用 | 创建 Vue3 项目骨架(文件、配置等) | 处理项目的构建、开发、热重载等 |
| 使用场景 | 用于创建一个新的 Vue3 项目 | 用于开发和构建 Vue3 项目,并且也支持 React 项目 |
另外,在没有 Vite 之前,许多前端项目是依赖 Webpack 来进行模块打包、构建、优化的。
1.3.语法选择
本文档选择的是 vue3 的组合式语法。
2.Vue3 的首个应用
我们先使用 create-vue 来建立第一个应用,这个应用会打开一个关于 vue 的页面,首先需要下载脚手夹,然后进行配置。
# 安装脚手架并且配置应用
$ npm create vue@latest
Need to install the following packages:
create-vue@3.12.0
Ok to proceed? (y) y
> npx
> create-vue
Vue.js - The Progressive JavaScript Framework
✔ 请输入项目名称: … vue3-test
✔ 是否使用 TypeScript 语法? … 否 / 是
✔ 是否启用 JSX 支持? … 否 / 是
✔ 是否引入 Vue Router 进行单页面应用开发? … 否 / 是
✔ 是否引入 Pinia 用于状态管理? … 否 / 是
✔ 是否引入 Vitest 用于单元测试? … 否 / 是
✔ 是否要引入一款端到端(End to End)测试工具? › 不需要
✔ 是否引入 ESLint 用于代码质量检测? › 是
✔ 是否引入 Prettier 用于代码格式化? … 否 / 是
✔ 是否引入 Vue DevTools 7 扩展用于调试? (试验阶段) … 否 / 是
正在初始化项目 /home/ljp/test/vue3test/vue3-test...
项目初始化完成,可执行以下命令:
cd vue3-test
npm install
npm run format
npm run dev
npm notice
npm notice New minor version of npm available! 10.7.0 -> 10.9.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.9.0
npm notice To update run: npm install -g npm@10.9.0
npm notice按照上面的指令来初始化项目。
# 初始化项目并且运行
# 打开项目主目录
$ cd vue3-test
# 安装项目依赖
$ npm install
added 139 packages, and audited 140 packages in 13s
36 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
# 运行代码格式化
$ npm run format
> vue3-test@0.0.0 format
> prettier --write src/
src/App.vue 54ms (unchanged)
src/assets/base.css 7ms (unchanged)
src/assets/main.css 2ms (unchanged)
src/components/HelloWorld.vue 12ms (unchanged)
src/components/icons/IconCommunity.vue 2ms (unchanged)
src/components/icons/IconDocumentation.vue 1ms (unchanged)
src/components/icons/IconEcosystem.vue 2ms (unchanged)
src/components/icons/IconSupport.vue 1ms (unchanged)
src/components/icons/IconTooling.vue 2ms (unchanged)
src/components/TheWelcome.vue 8ms (unchanged)
src/components/WelcomeItem.vue 5ms (unchanged)
src/main.js 2ms (unchanged)
# 修改配置(可选, 如果您是直接在自己本机的桌面操作系统安装, 则可以跳过这个步骤, 因为我是使用云服务器, 必须更改部署地址和部署端口才能看到界面)
$ ls
eslint.config.js index.html jsconfig.json node_modules package.json package-lock.json public README.md src vite.config.js
$ vim vite.config.js
$ cat vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
host: '0.0.0.0', // 修绑定在任意 ip 地址
port: 8081, // 修改开放的 port 端口
}
})
# 开放端口号
$ sudo ufw allow 8081
规则已添加
规则已添加 (v6)
# 运行项目
$ npm run dev
VITE v5.4.10 ready in 154 ms
➜ Local: http://localhost:8081/
➜ Network: http://10.1.74.26:8081/
➜ press h + enter to show help在浏览器打开上面网址(本地开发使用第一个第二个都可以,远程开发只能使用第二个)。

这里稍微解释一些重要的文件。
首先是配置文件,由于我们的项目使用了 vite 构建工具,所以有这个配置文件。
// vite.config.js: vite 配置文件
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
alias: {
"@": resolve(__dirname, "./src"), // 设置 @ 缩写方便引入文件
},
},
server: {
host: "127.0.0.1", // 或指定为具体的 IP 地址
port: 8003, // 设置为所需的端口号
},
});挂载页面文件,需要定义挂载点和引入 JS 代码(创建了根组件),而 JS 代码内部引入了描述主页的 .vue 文件。
<!-- index.html: 应用挂载文件 -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div> <!--定义挂载点: 后续 Vue 应用会挂载到这里 -->
<script type="module" src="/src/main.js"></script> <!-- 设置入口文件: 一个 Vue 应用的所有 JS 代码 -->
</body>
</html>// @/main.js: 创建一个 Vue 应用实例
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
createApp(App).mount("#app");<!-- @/App.vue: 描述主页应用, 该页面通常设置页面路由组件 -->
<script setup></script>
<template>
<div>
<p>你好我是主页</p>
</div>
</template>
<style scoped></style>重要
补充:Vue2 和 Vue3 在 .vue 文件上的书写区别。
在 Vue3 中,setup 和 scoped 是两个重要的概念,分别与 Vue 组件的逻辑和样式作用域相关。
<script setup>是Vue3引入的一个编译时语法糖,用于简化组件的编写。它用于在<script>标签中声明组件的逻辑,且具有以下作用。- 简化组件编写:通过使用
<script setup>,Vue自动处理常见的功能,如响应式数据、生命周期钩子等,无需手动引入defineComponent组件。 - 更直接的组合式 API 使用:可以直接在
<script setup>中使用Vue3的组合式API(例如ref,reactive,computed,watch等),不需要额外的声明。
例如,在
<script setup>中声明一个响应式变量:<script setup> import { ref } from 'vue'; const message = ref('你好我是主页'); </script> <template> <div> <p>{{ message }}</p> </div> </template>简化后的
setup语法使得Vue组件更简洁且易于阅读和维护。- 简化组件编写:通过使用
<style scoped>是Vue3用于限定样式作用域的一个特性。它确保在该<style>标签中的样式只作用于当前组件的元素,而不会影响全局或其他组件。通过scoped,可以防止样式泄漏到外部,避免全局样式污染。例如,当您在组件中写CSS时,scoped会自动为每个元素添加一个独特的属性,确保它们仅在当前组件内生效。<style scoped> p { color: red; } </style>这个样式只会应用到当前组件的
<p>元素,而不会影响其他组件的<p>元素。
重要
补充:值的注意的是,在根组件自带的 innerHTML 也是组件模板的一部分,在组件没有设置 template 选项时,Vue 组件就会自动使用 innerHTML 作为模板。
重要
补充:这个配置指的是在 Vue 应用实例 (app) 创建后但挂载之前所进行的所有应用级配置。这种配置通常是用来设定一些全局性的选项,比如错误处理器、全局组件、插件、全局指令等,体现为创建好后的应用实例在没有挂载之前可以使用 app.use() 或 app.component() 或 app.config.errorHandler 等。要确保在调用 app.mount('#app') 之前完成这些配置,因为挂载后配置将不会生效。
重要
补充:服务端渲染可以考虑挂载多个 Vue 应用实例。
我们后面都通过 App.vue 文件来演示 Vu3 的语法。
3.Vue3 的模板语法
{{}}会将定义好的数据值解释为纯文本v-html给一个<div>或<span>定义一个则会渲染指定HTML文本(不过注意,为了避免XSS漏洞,慎用这个指令)<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 --> <script setup> import { ref } from "vue"; const rawHtml = ref("<strong>Bold text</strong>"); </script> <template> <div> <p>你好我是主页</p> <!-- 不会渲染 --> <p>{{ rawHtml }}</p> <!-- 会渲染 --> <p><span v-html="rawHtml"></span></p> </div> </template> <style scoped></style>v-bind由于双大括号不能在标签属性中使用,因此就用这个指令替代{{}}给组件的某些属性赋予数据值(简写语法为:,后面只需要带上参数即可),并且这个值了还可以绑定组合起来的数据(有点像类)<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 --> <script setup> const objectOfAttrs = { id: "container", class: "wrapper", style: "background-color:green", }; </script> <template> <div> <p>你好我是主页</p> <div v-bind="objectOfAttrs"></div> </div> </template> <style scoped></style>在
Vue模板内,JS表达式/JS函数可以被使用在如下场景上:在文本插值中 (双大括号)
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }}在任何
Vue指令 (以v-开头的特殊attribute)attribute的值中<div :id="`list-${id}`"></div>仅支持单一表达式 ,不支持语句(必须可以被求值的
JS代码)<!-- 这是一个语句,而非表达式 --> {{ var a = 1 }} <!-- 条件控制也不支持,请使用三元表达式 --> {{ if (ok) { return message } }}
v-if:"布尔变量"用于条件渲染元素(注意和用于禁用的v-bind:disabled="布尔变量"有区别)v-on:填入DOM事件="调用方法名称"将监听DOM事件,调用对应的事件方法(v-on缩写为@)前面我们提到过
v-bind:后是一个参数,不过这个参数是静态参数,我们还可以使用[]语法将参数包裹起来填写动态参数(类似v-bind:[]的其他指令也有类似的,比如v-on:[])- 动态参数值的限制:动态参数中表达式的值应当是一个字符串,或者是
null。特殊值null意为显式移除该绑定。其他非字符串的值会触发警告。 - 动态参数语法限制:动态参数表达式因为某些字符的缘故有一些语法限制,比如空格和引号,在
HTML attribute名称中都是不合法的。
- 动态参数值的限制:动态参数中表达式的值应当是一个字符串,或者是
修饰符...
4.Vue3 的响应基础
在组合式
API中,推荐使用ref()函数来声明响应式状态<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 --> <script setup> import { ref } from "vue"; const count = ref(0); console.log(count); // { value: 0 } console.log(count.value); // 0 count.value++; console.log(count.value); // 1 </script> <template> <div> <p>你好我是主页</p> </div> </template> <style scoped></style>// ref 实现的伪代码 const myRef = { _value: 0, get value() { track() return this._value }, set value(newValue) { this._value = newValue trigger() } }当修改了响应式状态时,
DOM会被自动更新。但是需要注意的是,DOM更新不是同步的。Vue会在"next tick"更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。要等待DOM更新完成后再执行额外的代码,可以使用nextTick()全局API<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 --> <script setup> import { ref } from "vue"; import { nextTick } from "vue"; const count = ref(0); async function increment() { count.value++; await nextTick(); // 现在 DOM 已经更新了... } </script> <template> <div> <p>你好我是主页</p> </div> <button @click="increment"> <!-- 注意不需要使用 count.value 会自动解包 --> {{ count }} </button> </template> <style scoped></style>reactive()可以用于将对象转换为响应式的API,我们之前定义的数据都是单个字段,而对象内部可以包含多个字段,当某一个字段发生变化时,Vue就会认为该对象发生变化,就会导致使用对象的组件重新渲染
5.Vue3 的计算属性
计算属性可以把复杂的响应计算逻辑分离出来,避免多个组件重复计算。
优化前。
<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 -->
<script setup>
import { reactive } from 'vue'
// 定义响应式数据 author
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
</script>
<template>
<p>Has published books:</p>
<!-- 直接在模板中使用表达式判断书籍数量 -->
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
</template>优化后。
<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 -->
<script setup>
import { reactive, computed } from "vue";
// 定义响应式数据 author 对象
const author = reactive({
name: "John Doe",
books: [
"Vue 2 - Advanced Guide",
"Vue 3 - Basic Guide",
"Vue 4 - The Mystery",
],
});
// 定义一个计算属性 publishedBooksMessage, 根据书籍数量决定显示信息
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? "Yes" : "No";
});
</script>
<template>
<p>Has published books:</p>
<!-- 使用计算属性来显示是否有书籍的信息, 避免多个组件重复计算 -->
<span>{{ publishedBooksMessage }}</span>
<span>{{ publishedBooksMessage }}</span>
<span>{{ publishedBooksMessage }}</span>
</template>
<style scoped></style>我们在这里定义了一个计算属性 publishedBooksMessage。computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 publishedBooksMessage.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value。
补充:计算属性缓存 vs 方法。你可能注意到我们在表达式中像这样调用一个函数也会获得和计算属性相同的结果,若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要
author.books不改变,无论多少次访问publishedBooksMessage都会立即返回先前的计算结果,而不用重复执行getter函数。
计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建:
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>现在当你再运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstName 和 lastName 会随之更新。
- 抽离计算逻辑,方便复用
- 具有计算缓存,减少计算
- 能够自动响应,重算数据
6.Vue3 的样式绑定
6.1.布尔绑定
数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 class 和 style 都是 attribute,我们可以和其他 attribute 一样使用 v-bind 将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 class 和 style 的 v-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。
<script setup>
import { ref } from 'vue'
const isActive = ref(true)
</script>
<template>
<!-- 根据 isActive 的布尔值结果决定是否拥有 active 类名 -->
<div :class="{ active: isActive }">This is active</div>
</template>你可以在对象中写多个字段来操作多个 class。此外,:class 指令也可以和一般的 class attribute 共存。举例来说,下面这样的状态。
<script setup>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
</script>
<template>
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
</template>渲染结果为 <div class="static active"></div>。
绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象,不过这个对象内部所有的字段都是布尔类型。
<script setup>
import { reactive } from 'vue'
const classObject = reactive({
active: true,
'text-danger': false
})
</script>
<template>
<div :class="classObject"></div>
</template>也可以绑定对象的计算属性做缓存...
<script setup>
import { ref, computed } from 'vue'
const isActive = ref(true)
const error = ref(null)
const classObject = computed(() => ({
active: isActive.value && !error.value,
'text-danger': error.value && error.value.type === 'fatal'
}))
</script>
<template>
<div :class="classObject"></div>
</template>6.2.列表绑定
我们可以给 :class 绑定一个数组来渲染多个 CSS class。
const activeClass = ref('active')
const errorClass = ref('text-danger')template
<div :class="[activeClass, errorClass]"></div>渲染的结果是。
<div class="active text-danger"></div>如果你也想在数组中有条件地渲染某个 class,你可以使用三元表达式。
<div :class="[isActive ? activeClass : '', errorClass]"></div>errorClass 会一直存在,但 activeClass 只会在 isActive 为真时才存在。
然而,这可能在有多个依赖条件的 class 时会有些冗长。因此也可以在数组中嵌套对象。
<div :class="[{ [activeClass]: isActive }, errorClass]"></div>补充:class 合并...
$attrs属性...
7.Vue3 的逻辑渲染
7.1.条件渲染
v-ifv-else-ifv-else
如果需要包含多个组件的条件判断,就需要使用 <template> 来进行包裹,再在 <template> 上使用上述三个指令。
v-show控制元素显隐,但是不支持上述的<template>
补充:
v-if是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。相比之下,
v-show简单许多,元素无论初始条件如何,始终会被渲染,只有 CSSdisplay属性会被切换。这里的渲染是指合成
HTML总的来说,
v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show较好;如果在运行时绑定条件很少改变,则v-if会更合适。
警告:当
v-if和v-for同时存在于一个元素上的时候,v-if会首先被执行,请避免这么做。
7.2.列表渲染
v-for支持数组,支持键值,支持范围
v-for 支持 <template>
key 状态管理,待补充...
变化监听,待补充...
计算属性过滤结果再列表渲染
8.Vue3 的事件处理
8.1.事件处理
内联事件处理器
const count = ref(0) <button @click="count++">Add 1</button> <p>Count is: {{ count }}</p>方法事件处理器
const name = ref('Vue.js') function greet(event) { alert(`Hello ${name.value}!`) // `event` 是 DOM 原生事件 if (event) { alert(event.target.tagName) } } <!-- `greet` 是上面定义过的方法名 --> <button @click="greet">Greet</button>
方法与内联事件判断,模板编译器会通过检查 v-on 的值是否是合法的 JavaScript 标识符或属性访问路径来断定是何种形式的事件处理器。举例来说,foo、foo.bar 和 foo['bar'] 会被视为方法事件处理器,而 foo() 和 count++ 会被视为内联事件处理器。
8.2.事件修饰
在处理事件时调用 event.preventDefault() 或 event.stopPropagation() 是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。
尤其是案件修饰符和鼠标修饰符的使用,待补充...
9.Vue3 的表单绑定
在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦:
<input
:value="text"
@input="event => text = event.target.value">v-model 指令帮我们简化了这一步骤:
<input v-model="text">另外,v-model 还可以用于各种不同类型的输入,<textarea>、<select> 元素。它会根据所使用的元素自动使用对应的 DOM 属性和事件组合:
<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 -->
<script setup>
import { ref, computed } from "vue";
const message = ref("Init");
const checkedNames = ref([]);
const picked = ref();
const selected = ref();
</script>
<template>
<p>Message is: {{ message }}</p>
<!-- 输入框 -->
<input v-model="message" placeholder="edit me" /><br />
<!-- 文本框 -->
<textarea v-model="message" placeholder="add multiple lines"></textarea><br />
<!-- 选择框 -->
<input type="checkbox" id="checkbox" v-model="message" /><br />
<!-- 复选框 -->
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
<!-- 单选框 -->
<div>Picked: {{ picked }}</div>
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
<!-- 选择器 -->
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</template>值绑定...
表单绑定修饰符...
10.Vue3 的生命周期
onMounted组件挂载时onUpdated组件更新时onUnmounted组件销毁时
<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 -->
<script setup>
import { ref, onMounted, onUpdated, onUnmounted } from "vue";
const message = ref("Home");
// 当组件挂载时调用
onMounted(() => {
console.log("App Component is mounted");
});
// 当组件更新时调用
onUpdated(() => {
console.log("App Component is updated");
});
// 当组件销毁时调用
onUnmounted(() => {
console.log("App Component is unmounted"); // 这个单个文件比较难演示
});
const clicked = () => {
message.value += "!";
};
</script>
<template>
<p>{{ message }}</p>
<button @click="clicked">添加感叹号</button>
</template>11.Vue3 的侦听观察
侦听器和计算属性很像,但是不一定返回一个数值,可以不返回,并且大部分都是在监听到数据改变后,执行异步请求等副作用(计算属性不适合副作用)
- 深层侦听
- 即时侦听
- 一次侦听
- 同步侦听
- 停止侦听,要手动停止一个侦听器,请调用 watch 或 watchEffect 返回的函数
watchvs.watchEffect- 过时请求
12.Vue3 的模板引用
这个非常有用。
<!-- @/App.vue: 描述主页应用, 这个页面通常会设置页面路由组件 -->
<script setup>
import { useTemplateRef, onMounted } from "vue";
// 第一个参数必须与模板中的 ref 值匹配
const input = useTemplateRef("my-input");
onMounted(() => {
input.value.focus();
});
</script>
<template>
<input ref="my-input" />
</template>v-for中的模板引用- 函数模板引用
- 子组件模板引用
13.Vue3 的组件封装
组件和组件之间的关系
13.1.父向子传递属性数据
父组件传递 props
<script setup>
import Child from './Child.vue';
const parentMessage = "Hello from parent!";
const parentCount = 5;
</script>
<template>
<Child :message="parentMessage" :count="parentCount" />
</template><script setup>
import { defineProps } from 'vue';
// 使用 defineProps 来定义多个 props
const props = defineProps({ // 其实可以直接使用 defineProps['parentMessage']; 并且后续自动解包
message: {
type: String,
required: true // 必填的意思
},
count: {
type: Number,
required: true
}
});
</script>
<template>
<p>{{ props.message }}</p>
<p>Count: {{ props.count }}</p>
</template>子组件让父组件触发事件。
<template>
<div :style="{ fontSize: postFontSize + 'em' }">
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
@enlarge-text="postFontSize += 0.1" <!-- 监听子组件的 enlarge-text 事件 -->
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import BlogPost from './BlogPost.vue';
// 存储博客文章的数组
const posts = ref([
{ id: 1, title: 'First Blog Post' },
{ id: 2, title: 'Second Blog Post' },
{ id: 3, title: 'Third Blog Post' }
]);
// 控制所有博客文章的字体大小
const postFontSize = ref(1);
</script><template>
<div class="blog-post">
<h4>{{ title }}</h4>
<button @click="$emit('enlarge-text')">Enlarge text</button> <!-- 点击按钮触发 enlarge-text 事件 -->
</div>
</template>
<script setup>
defineProps(['title']); <!-- 接收父组件传递的 title -->
defineEmits(['enlarge-text']); <!-- 声明子组件会触发 enlarge-text 事件 -->
</script>- 插槽
- 动态组件
- DOM 内模板解析
14.Vue3 的官方路由
npm install vue-router@4