Vue3 笔记
1. 环境搭建
-
安装 18.0 或更高版本的 Node.js
-
创建项目
# 在指定目录下执行以下命令, 用来创建Vue项目 npm create vue@latest
-
运行项目
# 进入Vue项目的根目录 cd <your-project-name> # 安装依赖 npm install # 运行项目 npm run dev
-
打包Vue项目,此命令会在
./dist
文件夹中为你的应用创建一个生产环境的构建版本npm run build
2. Vue3目录结构
project/
|—— public/ # 于存放静态资源文件的目录,这些文件不会经过打包处理
| |—— index.html
|—— src/
| |—— assets/ # 放置静态资源文件,如图片、样式等
| |—— components/ # 放置可复用的 Vue 组件
| |—— views/ # 放置页面级组件
| |—— router/ # 放置路由配置文件
| |—— store/ # 放置 Vuex 状态管理文件
| |—— services/ # 放置与后端交互的服务文件
|—— ApiService.js # 用于定义后端 API 请求的方法,包括 GET、POST..
|—— AuthService.js # 用于处理用户认证和授权相关的操作,比如登录、注册
| |—— utils/ # 放置工具函数
| |—— App.vue # 根组件
| |—— main.js # 项目入口文件
|—— package.json # 项目配置文件
|—— babel.config.js # Babel 配置文件
|—— vue.config.js # Vue CLI 配置文件
|—— index.html # 整个应用的入口文件
3. idnex.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>
<!-- 引入js入口文件 -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
4. main.js
// 从静态资源文件夹中导入了一个名为 main.css 的样式
import './assets/main.css'
// 从 Vue 库中导入了 createApp 函数,用于创建一个 Vue 应用实例
import { createApp } from 'vue'
// 从 Pinia 库中导入了 createPinia 函数,用于创建一个 Pinia 应用状态管理实例
import { createPinia } from 'pinia'
// 导入了根组件 App.vue,这是整个应用程序的根组件
import App from './App.vue'
// 导入了路由配置文件,用于设置 Vue Router (页面跳转)
// 导入router 文件夹中 export default router 暴露出来的实例对象
import router from './router'
// 使用 createApp 函数创建了一个 Vue 应用实例对象
const app = createApp(App)
// 调用 app.use 方法来安装并使用 Pinia 应用状态管理实例
app.use(createPinia())
// 调用 app.use 方法来安装并使用 Vue Router
app.use(router)
// 将 Vue 应用实例挂载到index.html页面 具有 id="app" 的 HTML 元素上
app.mount('#app')
5. App.vue
<!-- 根组件JS脚本 -->
<script setup>
// 引入 vue-router 中的路由组件,用于页面跳转和显示
import {
// 它可以被用作普通的 <a> 标签来进行页面之间的导航
RouterLink,
// 用于渲染当前路由匹配到的组件的组件
// 当用户访问不同的 URL 时,RouterView 会根据当前路由匹配到的组件来动态地渲染对应的内容
RouterView
} from 'vue-router'
// 导入可复用组件 HelloWorld
import HelloWorld from './components/HelloWorld.vue'
</script>
<!-- 根组件模板内容 -->
<template>
<!-- 使用自定义的组件 -->
<HelloWorld msg="dd did it!" />
<!-- 使用路由跳转组件,相当于a标签 -->
<!-- to属性的值为router文件夹中index.js注册的对应的组件路由-->
<RouterLink to="/">Home</RouterLink>
<!-- 使用路由跳转组件 -->
<RouterLink to="about">About</RouterLink>
<!-- 用于显示 RouterLink 跳转的页面内容 -->
<RouterView />
</template>
<!-- 根组件样式 -->
<style scoped>
</style>
6. router/index.js
// 从 vue-router 包中导入了 createRouter 和 createWebHistory 函数
import { createRouter, createWebHistory } from 'vue-router'
// 导入自定义视图组件,用于在页面中显示一个区域的内容
import HomeView from '../views/HomeView.vue'
// 使用 createRouter 函数创建了一个新的路由实例对象
const router = createRouter({
// 指定了路由的历史模式为基于浏览器 history 的模式
// 并且传入了 import.meta.env.BASE_URL 作为基础 URL
// 这将影响路由的根路径和路由的匹配规则
history: createWebHistory(import.meta.env.BASE_URL),
// 定义了应用程序中的路由规则
routes: [
{
// RouterLink 的 to 属性对应的url,会匹配到 component 对应的组件
path: '/',
// 路由名称,RouterLink 的 to 属性也可以使用name属性作为值跳转到对应的组件
// 但是必须这样写 :to="{ name: 'home' }"
name: 'home',
// 绑定路径对应的组件
component: HomeView
},
{
path: '/about',
name: 'about',
// 绑定组件的另一种方式,箭头函数体 里面返回需要导入的组件
component: () => import('../views/AboutView.vue')
}
]
})
// 导出了这个创建好的路由实例对象,以便在应用程序的其他地方进行使用
export default router
7. stores/counter.js
// 引入了 ref 和 computed 这两个用于定义响应式数据和计算属性的函数
import { ref, computed } from 'vue'
// 引入了 Pinia 库中的 defineStore 方法,用于定义一个状态管理模块
import { defineStore } from 'pinia'
// 定义了一个名为 useCounterStore 的状态管理模块,
// 并使用 defineStore 方法对其进行定义
// 实例化一个状态管理对象,里面的属性和方法,会保存数据状态
// 最后通过 export 直接导出这个状态管理对象
// 其他地方可以通过 import { useCounterStore } from '...' 来引入这个模块
export const useCounterStore = defineStore('counter', () => {
// 定义一个常量引用,他的值是一个对象,初始化了一个数据 (类似于浏览器session)
// ref函数可以传递任意类型的数据,比如传递对象初始化数据
// const person = ref({ name: 'Alice', age: 30 })
// 可以通过访问 count.value 属性来获取或修改这些引用的值
const count = ref(0)
// 定义一个常量引用,用于计算响应式引用的值
// 当 count 方法变化时,computed() 函数会自动自行,并将值返回给计算常量引用
// computed() 方法接收一个函数,这个函数的返回值会赋值给 doubleCount 常量
const doubleCount = computed(() => count.value * 2)
// 定义一个方法,方调用这个方法时 count对象里属性的值加1
function increment() {
count.value++
}
// 将这些属性和方法返回
return { count, doubleCount, increment }
})
// // 其他页面导入时 import { useCounterStore } from './counter.js'
// export { useCounterStore }
// // 其他页面导入时 import useCounterStore from './counter.js'
// export default useCounterStore
8. views/AboutView.vue
<!-- 使用 <script setup> 时,你可以直接声明变量、引入组件选项,定义响应式状态等 -->
<script setup>
import { useCounterStore } from '../stores/counter.js'
import { ref } from 'vue'
// 实例化状态管理对象
const constStore = useCounterStore()
// 将状态里面的值再次封装为常量引用
const counts = ref(constStore.count)
// 增加一个方法,按钮点击事件
const increment = () => {
// 调用状态管理对象的累加方法,对状态里面的 count值进行修改
constStore.increment()
// 将状态管理对象中的常量引用,赋值给本地的常量引用,以响应式更新页面的数据
counts.value = constStore.count
}
</script>
<template>
<div class="about">
<h1>This is an about page</h1>
<!-- 页面中可以直接获得常量引用的值不用加 .value -->
<h2>{{ counts }}</h2>
<!-- 添加Vue单击事件 -->
<button @click="increment">加一</button>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>
9. main.js 中实例化一个Vue应用
-
每个 Vue 应用都是通过
createApp
函数创建一个新的 应用实例 -
传入
createApp
的对象实际上是一个组件 -
createApp
API 允许你在同一个页面中创建多个共存的 Vue 应用import { createApp } from 'vue' // 导入了根组件 App.vue,这是整个应用程序的根组件 import App from './App.vue' // 实例化vue应用并传参 const app = createApp(App) // 将 Vue 应用实例挂载到index.html页面 具有 id="app" 的 HTML 元素上 app.mount('#app')
-
根组件的模板通常是组件本身的一部分
但也可以直接通过在挂载容器内编写模板来单独提供
<div id="app"> <button @click="count++">{{ count }}</button> </div>
import { createApp } from 'vue' const app = createApp({ data() { return { count: 0 } } }) app.mount('#app')
10. 模版语法
-
最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号)
-
双大括号标签会被替换为相应组件实例中
msg
属性的值。同时每次msg
属性更改时它也会同步更新<span>Message: {{ msg }}</span>
-
属性名也可以是动态的 只需要加上中括号接口绑定为动态的变量
<!-- 绑定事件名称 --> <a @[eventName]="doSomething"> ... </a> <!-- 绑定属性名称 --> <a :[attributeName]="url"> ... </a>
11. 响应式基础
-
声明响应式状态
ref()
接收参数,并将其包裹在一个带有.value
属性的 ref 对象中返回 -
Ref 可以持有任何类型的值,包括深层嵌套的对象、数组等
import { ref } from 'vue' const count = ref(0)
-
示例
<script setup> // 使用 ref 创建响应式变量 import { ref, onMounted } from 'vue'; // 初始化响应数据 var count = ref(1); // 定义方法 相当于在vue2 的method里面编写 const increment = () => { count.value++; }; // 在 onMounted 生命周期钩子中执行 mounted 回调函数 onMounted(() => { // 打印 count 的值 console.log(count); // => 1 // 修改 count 的值 count.value = 3; // 调用方法 increment(); }); </script> <template> Count is: {{ count }} </template>
-
js文件中编写, 示例
import { ref } from 'vue' // 导入对象 export default { setup() { const count = ref(0) function increment() { // 在 JavaScript 中需要 .value count.value++ } // 不要忘记同时暴露 increment 函数 return { count, increment } } }
另一种声明响应式状态的方式,即使用 reactive()
API
-
reactive()
将使对象本身具有响应性 -
有限的值类型:它只能传入对象类型存储 (对象、数组和如
Map
、Set
这样的集合类型)<template> <button @click="state.count++"> {{ state.count }} </button> </template> <script setup> import { reactive } from 'vue' // 初始化响应式数据,和ref不同的是将使对象本身也具有响应性 const state = reactive({ count: 0 }) </script>
12. 计算属性
概念
-
如果在模板中写太多逻辑,会让模板变得臃肿,难以维护
-
所以引入一个计算属性用于计算响应式变量的变化
-
禁止改变其他状态、getter 中做异步请求或者更改 DOM!
-
一个计算属性的声明中描述的是如何根据其他值派生一个值
-
因此 getter 的职责应该仅为计算和返回该值
<script setup> import { reactive, computed } from 'vue' const author = reactive({ name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ] }) // 使用computed方法用于计算响应式数据的值返回不同的内容 // 只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果, 而不用重复执行 getter 函数 const publishedBooksMessage = computed(() => { return author.books.length > 0 ? 'Yes' : 'No' }) </script> <template> <p>Has published books:</p> <span>{{ publishedBooksMessage }}</span> </template>
-
可以通过同时提供 getter 和 setter 来创建 计算属性,以修改计算属性的值
-
现在当你再运行
fullName.value = 'John Doe'
时,setter 会被调用而firstName
和lastName
会随之更新<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 }, // 避免直接修改计算属性值 (只为了解) set(newValue) { // 注意:我们这里使用的是解构赋值语法 [firstName.value, lastName.value] = newValue.split(' ') } }) </script>
13. Class与Style绑定
概念
-
可以给
:class
(v-bind:class
的缩写) 传递一个对象来动态切换 class 样式<!-- :class="{ css样式名称: 是否使用这个css样式(布尔值) }" --> <div :class="{ active: isActive }"></div> <script setup> import { ref } from 'vue'; // 响应式属性 const isActive = ref(true) </script>
-
绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象
<div :class="classObject"></div> <script setup> import { reactive } from 'vue'; // 响应式对象 const classObject = reactive({ active: true, // 决定是否使用 css样式 active // 对与类名有特殊字符的需要加上引号 'text-danger': false // 不适用 css样式 text-danger }) </script>
-
可以绑定一个返回对象的计算属性
<div :class="classObject"></div> <script setup> import { ref,computed } from 'vue'; const isActive = ref(true) const error = ref(null) const classObject = computed(() => ({ // 当isActive的值为true,并且error的值为空时,使用这个css样式 active: isActive.value && !error.value, 'text-danger': error.value && error.value.type === 'fatal' })) </script>
-
可以给
:class
绑定一个数组来渲染多个 CSS class<div :class="[activeClass, errorClass]"></div> <script setup> import { ref } from 'vue'; const activeClass = ref('active') const errorClass = ref('text-danger') </script>
-
如果你也想在数组中有条件地渲染某个 class,你可以使用三元表达式
<div :class="[isActive ? activeClass : '', errorClass]"></div> <script setup> import { ref } from 'vue'; const isActive = ref(true) const activeClass = ref('active') const errorClass = ref('text-danger') </script>
Style绑定内联样式绑定
-
:style
支持绑定 JavaScript 对象值,对应的是 html的style属性<!-- :style="{ color: 响应式属性, fontSize: 响应式属性 + 'px' }" --> <div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> <script setup> import { ref } from 'vue'; const activeColor = ref('red') const fontSize = ref(30) </script>
-
直接绑定一个样式对象
<div :style="styleObject"></div> <script setup> import { reactive } from 'vue'; // 响应式对象 const styleObject = reactive({ color: 'red', fontSize: '13px' }) </script>
-
返回样式对象的计算属性
<div :style="styleObject"></div> <script setup> import {reactive,computed} form 'vue' // 计算属性返回的样式对象 const styleObject = computed(() => ({ color: 'red', fontSize: '13px' })) </script>
-
绑定数组 :
:style
绑定一个包含多个样式对象的数组<div :style="[baseStyles, overridingStyles]"></div> <script setup> import { ref } from 'vue'; const baseStyles = ref({color: 'red',fontSize: '16px'}) const overridingStyles = ref({fontWeight: 'bold' }) </script>
14. 条件渲染
概念
-
v-if
是一个指令,他必须依附于某个元素 -
v-if
指令用于条件性地渲染一块内容, 当表达式返回真值时才被渲染 -
注意事项:当
v-if
和v-for
同时存在于一个元素上的时候,v-if
会首先被执行 ,所有不推荐在同一个元素上使用这两个指令<h1 v-if="awesome">Vue is awesome!</h1> <script setup> import { ref } from 'vue'; const awesome = ref(true) </script>
-
可以使用
v-else
为v-if
添加一个“else 区块”<!-- 每当点击按钮时 awesome 的值取反 --> <button @click="awesome = !awesome">Toggle</button> <h1 v-if="awesome">Vue is awesome!</h1> <h1 v-else>Oh no</h1> <script setup> import { ref } from 'vue'; const awesome = ref(true) </script>
-
v-else-if
提供的是相应于v-if
的“else if 区块”<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div> <script setup> import { ref } from 'vue'; const type = ref('B') </script>
-
按条件显示一个元素的指令是
v-show
-
v-show
会在 DOM 渲染中保留该元素 -
v-show
仅切换了该元素上名为display
的 CSS 属性 -
v-show
不支持在<template>
元素上使用,也不能和v-else
搭配使用 -
无论
v-show
的条件是true
还是false
,元素都会被渲染到 DOM 中,只是通过display: none;
样式来控制显示与隐藏<h1 v-show="true">Hello!</h1>
15. v-for
概念
-
使用 v-for 对数据进行动态渲染
-
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
-
使用 v-for 的标签也会被循环生成
<!-- 循环生成多个 li --> <li v-for="item in items"> {{ item.message }} </li> <script setup> const items = ref([{ message: 'Foo' }, { message: 'Bar' }]) </script>
-
v-for
也支持使用可选的第二个参数表示当前项的位置索引<li v-for="(item, index) in items"> {{ index }} - {{ item.message }} </li> <script setup> const items = ref([{ message: 'Foo' }, { message: 'Bar' }]) </script>
-
对于多层嵌套的
v-for
,每个v-for
作用域都可以访问到父级作用域<li v-for="item in items"> <span v-for="childItem in item.children"> {{ item.message }} {{ childItem }} </span> </li>
-
循环渲染对象数据
<script setup> import { ref } from 'vue' const items = ref({message: 'Foo' , fdf: 'Bar'}) </script> <template> <ul> <li v-for="(value, key, index) in items"> {{ key }} - {{ value }} </li> </ul> </template>
-
v-for
可以直接接受一个整数值,注意此处n
的初值是从1
开始而非0
<span v-for="n in 10">{{ n }}</span>
通过 key 管理状态
-
给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素
-
需要为每个元素对应的块提供一个唯一的
key
属性<div v-for="item in items" :key="item.id"> <!-- 内容 --> </div>
16. 事件处理
概念
-
内联事件处理器:事件被触发时执行的内联 JavaScript 语句
<button @click="count++">Add 1</button> <p>Count is: {{ count }}</p> <script setup> const count = ref(0) </script>
-
绑定事件函数
<script setup> import { ref } from 'vue' const name = ref('Vue.js') const greet = (event) => { alert(`Hello ${name.value}!`) // `event` is the native DOM event if (event) { alert(event.target.tagName) } } </script> <template> <button @click="greet">Greet</button> </template>
-
在内联处理器中直接调用方法
<script setup> function say(message) { alert(message) } </script> <template> <button @click="say('hello')">Say hello</button> <button @click="say('bye')">Say bye</button> </template>
-
在内联事件处理器中访问原生 DOM 事件
<!-- 将DOM原生的时间对象作为参数传给 warn方法 --> <button @click="warn('Form cannot be submitted yet.', $event)"> Submit </button> <script setup> function warn(message, event) { // 这里可以访问原生事件 if (event) { event.preventDefault() } alert(message) } </script>
-
为
v-on
提供了事件修饰符,修饰符是用.
表示的指令后缀-
.stop
单击事件将停止传递 -
.prevent
提交事件将不再重新加载页面 .self
.capture
-
.once
一次性按钮 .passive
<!-- 单击事件将停止传递 --> <a @click.stop="doThis"></a> <!-- 提交事件将不再重新加载页面 --> <form @submit.prevent="onSubmit"></form> <!-- 修饰语可以使用链式书写 --> <a @click.stop.prevent="doThat"></a> <!-- 也可以只有修饰符 --> <form @submit.prevent></form> <!-- 仅当 event.target 是元素本身时才会触发事件处理器 --> <!-- 例如:事件处理器不来自子元素 --> <div @click.self="doThat">...</div> <!-- 添加事件监听器时,使用 `capture` 捕获模式 --> <!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 --> <div @click.capture="doThis">...</div> <!-- 点击事件最多被触发一次 --> <a @click.once="doThis"></a> <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 --> <!-- 以防其中包含 `event.preventDefault()` --> <div @scroll.passive="onScroll">...</div> <!-- 仅在 `key` 为 `Enter` 时调用 `submit` --> <input @keyup.enter="submit" />
-
17. 表单输入绑定
概念
-
实现表单元素与数据的双向绑定,简化数据状态管理
-
自动处理不同表单元素的输入事件,避免手动编写事件监听器
-
提高开发效率,减少重复的 DOM 操作
-
通过使用
v-model
,可以更轻松地管理表单数据和页面视图之间的关系<script setup> import { ref } from 'vue' const message = ref('') </script> <template> <p>Message is: {{ message }}</p> <!-- v-model 相当于value属性 --> <input v-model="message" placeholder="edit me" /> </template>
-
单一复选框绑定 决定是否选中
对于复选框而言,当复选框被选中时,
ss
数据属性的值会变为true
;当复选框未被选中时,ss
数据属性的值会变为false
<script setup> import { ref } from 'vue' const ss = ref('dd') </script> <template> <input type="checkbox" id="checkbox" v-model="ss" /> <label for="checkbox">{{ ss }}</label> </template>
-
多个复选框也可以绑定同一个数据,到的全选的效果
<script setup> import { ref } from 'vue' const checkedNames = ref([]) </script> <template> <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> </template>
-
单选框绑定数据
v-model绑定数据,当单选框被选中是,该单选框的value值会赋值为绑定的变量
<script setup> import { ref } from 'vue' const picked = ref('One') </script> <template> <div>Picked: {{ picked }}</div> <!-- v-model绑定数据,当单选框被选中是,该单选框的value值会赋值为绑定的变量 --> <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> </template>
-
下拉框绑定数据
当下拉选项被选中是,它的文本内容会赋值给绑定的变量
<script setup> import { ref } from 'vue' const selected = ref('') </script> <template> <span> Selected: {{ selected }}</span> <select v-model="selected"> <option disabled value="">Please select one</option> <option>A</option> <option>B</option> <option>C</option> </select> </template>
-
下拉框绑定数组 (多选)
<script setup> import { ref } from 'vue' const selected = ref([]) </script> <template> <div>Selected: {{ selected }}</div> <select v-model="selected" multiple> <option>A</option> <option>B</option> <option>C00</option> </select> </template> <style> select[multiple] { width: 100px; } </style>
-
值绑定
<!-- `picked` 在被选择时是字符串 "a" --> <input type="radio" v-model="picked" value="a" /> <!-- `toggle` 只会为 true 或 false --> <input type="checkbox" v-model="toggle" /> <!-- `selected` 在第一项被选中时为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select> <input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue" /> <input type="radio" v-model="pick" :value="first" /> <input type="radio" v-model="pick" :value="second" /> <select v-model="selected"> <!-- 内联对象字面量 --> <option :value="{ number: 123 }">123</option> </select>
-
修饰符
<!-- 在 回车 事件后同步更新而不是 输入时 --> <input v-model.lazy="msg" /> <!-- 输入自动转换为数字 --> <input v-model.number="age" /> <!-- 去除用户输入内容中两端的空格 --> <input v-model.trim="msg" />
18. 生命周期
-
onMounted
钩子可以用来在组件完成初始渲染并创建 DOM 节点后调用<script setup> import { ref, onMounted } from 'vue' const el = ref() onMounted(() => { // el.value 获得标签元素节点对象 el.value.innerHTML = "Hello:)" }) </script> <template> <!-- 将元素节点绑定到响应变量el中 --> <div ref="el"></div> </template>
-
onUpdated() 函数:组件因为响应式状态变更而更新其 DOM 树之后调用
<script setup> import { ref, onUpdated } from 'vue' const count = ref(0) onUpdated(() => { // 当组件的数据发生变化时执行一次该函数,打印改变后的响应变量值 console.log(count.value) }) </script> <template> <button @click="count++">{{ count }}</button> </template>
-
onUnmounted
钩子函数只会在整个组件被移除(从 DOM 中移除)时触发,而不是在组件中的某个 HTML 标签被移除时触发<script setup> import { onMounted, onUnmounted } from 'vue' let intervalId onMounted(() => { // 组件挂载时执行一个计时器 intervalId = setInterval(() => { // ... }) }) // 组件卸载时清除这个计时器对象 onUnmounted(() => clearInterval(intervalId)) </script>
-
onBeforeMount() 组件被挂载之前被调用
onBeforeMount(() => { ... })
-
onBeforeUpdate() 在组件即将因为响应式状态变更而更新其 DOM 树之前调用
onBeforeUpdate(() => { ... })
-
onBeforeUnmount() 在组件实例被卸载之前调用
onBeforeUnmount(() => { ... })
-
onErrorCaptured() 在捕获了后代组件传递的错误时调用
function onErrorCaptured(callback: ErrorCapturedHook): void type ErrorCapturedHook = ( err: unknown, instance: ComponentPublicInstance | null, info: string ) => boolean | void
-
onRenderTracked() 当组件渲染过程中追踪到响应式依赖时调用
function onRenderTracked(callback: DebuggerHook): void type DebuggerHook = (e: DebuggerEvent) => void type DebuggerEvent = { effect: ReactiveEffect target: object type: TrackOpTypes /* 'get' | 'has' | 'iterate' */ key: any }
19. 侦听器
概念
-
在状态变化时执行一些操作(计算属性中禁止的操作),例如更改 DOM,或是根据异步操作的结果去修改另一处的状态
-
watch
的第一个参数可以是不同形式的“数据源” (如ref) -
注意,你不能直接侦听响应式对象的属性值,因为 watch() 得到的参数是一个 具体值
<script setup> import { ref, watch } from 'vue' const question = ref('') const answer = ref('Questions usually contain a question mark. ;-)') const loading = ref(false) // 可以直接侦听一个 ref watch(question, async (newQuestion, oldQuestion) => { if (newQuestion.includes('?')) { loading.value = true answer.value = 'Thinking...' try { const res = await fetch('https://yesno.wtf/a