渐进式 JavaScript 框架 Vue 3 备忘清单的快速参考列表,包含常用 API 和示例
Vue 是一套用于构建用户界面的渐进式框架
注意:Vue 3.x 版本对应 Vue Router 4.x 路由版本
已安装 16.0
或更高版本的 Node.js
$ npm init vue@latest
指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具
✔ Project name: … <your-project-name>✔ Add TypeScript? … No/Yes✔ Add JSX Support? … No/Yes✔ Add Vue Router for Single Page Application development? … No/Yes✔ Add Pinia for state management? … No/Yes✔ Add Vitest for Unit testing? … No/Yes✔ Add Cypress for both Unit and End-to-End testing? … No/Yes✔ Add ESLint for code quality? … No/Yes✔ Add Prettier for code formatting? … No/YesScaffolding project in ./<your-project-name>...Done.
安装依赖并启动开发服务器
$ cd <your-project-name>$ npm install$ npm run dev
当你准备将应用发布到生产环境时,请运行:
$ npm run build
此命令会在 ./dist
文件夹中为你的应用创建一个生产环境的构建版本
import { createApp, ref } from 'vue'const app = createApp({setup() {const message = ref("Hello Vue3")return {message}}})app.mount('#app')
挂载应用
<div id="app"><button @click="count++">{{ count }}</button></div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script><div id="app">{{ message }}</div><script>const { createApp, ref } = VuecreateApp({setup() {const message = ref("Hello Vue3")return {message}}}).mount('#app')</script>
<div id="app">{{ message, ref }}</div><script type="module">import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'createApp({setup() {const message = ref("Hello Vue3")return {message}}}).mount('#app')</script>
<span>Message: {{ msg }}</span>
使用的是 Mustache
语法 (即双大括号),每次 msg
属性更改时它也会同步更新
<p>Using text interpolation: {{ rawHtml }}</p><p>Using v-html directive: <span v-html="rawHtml"></span></p>
双大括号{{}}
会将数据解释为纯文本,使用 v-html
指令,将插入 HTML
<div v-bind:id="dynamicId"></div>
简写
<div :id="dynamicId"></div>
<button :disabled="isButtonDisabled">Button</button>
通过不带参数的 v-bind
,你可以将它们绑定到单个元素上
<script setup>import comp from "./Comp.vue"import {ref} from "vue"const a = ref("hello")const b = ref("world")</script><template><comp v-bind="{a, b}"></comp></template>
如果你是使用的 setup
语法糖。需要使用 defineprops
声名(可以直接使用a
/b
)
const props = defineProps({a: String,b: String})
{{ number + 1 }}{{ ok ? 'YES' : 'NO' }}{{ message.split('').reverse().join('') }}<div :id="`list-${id}`"></div>
<!-- 这是一个语句,而非表达式 -->{{ var a = 1 }}<!-- 条件控制也不支持,请使用三元表达式 -->{{ if (ok) { return message } }}
<span :title="toTitleDate(date)">{{ formatDate(date) }}</span>
<p v-if="seen">Now you see me</p>
<a v-bind:href="url"> ... </a><!-- 简写 --><a :href="url"> ... </a>
<a v-on:click="doSomething"> ... </a><!-- 简写 --><a @click="doSomething"> ... </a>
<a v-bind:[attributeName]="url"> ... </a><!-- 简写 --><a :[attributeName]="url"> ... </a>
这里的 attributeName
会作为一个 JS 表达式被动态执行
<a v-on:[eventName]="doSomething"> ... </a><!-- 简写 --><a @[eventName]="doSomething">
<form @submit.prevent="onSubmit">...</form>
.prevent
修饰符会告知 v-on
指令对触发的事件调用 event.preventDefault()
v-on:submit.prevent="onSubmit"──┬─ ─┬──── ─┬───── ─┬──────┆ ┆ ┆ ╰─ Value 解释为JS表达式┆ ┆ ╰─ Modifiers 由前导点表示┆ ╰─ Argument 跟随冒号或速记符号╰─ Name 以 v- 开头使用速记时可以省略
<div>{{ state.count }}</div>
import { defineComponent, reactive } from 'vue';// `defineComponent`用于IDE推导类型export default defineComponent({// setup 用于组合式 API 的特殊钩子函数setup() {const state = reactive({ count: 0 });// 暴露 state 到模板return {state};},});
<button @click="increment">{{ state.count }}</button>
import { defineComponent, reactive } from 'vue';export default defineComponent({setup() {const state = reactive({ count: 0 });function increment() {state.count++;}// 不要忘记同时暴露 increment 函数return {state,increment};},})
<script setup>
setup语法糖<script setup>import { reactive } from 'vue';const state = reactive({ count: 0 })function increment() {state.count++}</script><template><button @click="increment">{{ state.count }}</button></template>
setup
语法糖用于简化代码,尤其是当需要暴露的状态和方法越来越多时
ref()
定义响应式变量reactive
只能用于对象、数组和 Map
、Set
这样的集合类型,对 string、number 和 boolean 这样的原始类型则需要使用ref
import { ref } from 'vue';const count = ref(0);console.log(count); // { value: 0 }console.log(count.value); // 0count.value++;console.log(count.value); // 1const objectRef = ref({ count: 0 });// 这是响应式的替换objectRef.value = { count: 1 };const obj = {foo: ref(1),bar: ref(2)};// 该函数接收一个 ref// 需要通过 .value 取值// 但它会保持响应性callSomeFunction(obj.foo);// 仍然是响应式的const { foo, bar } = obj;
在 html 模板中不需要带 .value
就可以使用
<script setup>import { ref } from 'vue';const count = ref(0);</script><template><div>{{ count }}</div></template>
import { reactive, defineComponent, onUnmounted } from 'vue';import { debounce } from 'lodash-es';export default defineComponent({setup() {// 每个实例都有了自己的预置防抖的处理函数const debouncedClick = debounce(click, 500);function click() {// ... 对点击的响应 ...}// 最好是在组件卸载时// 清除掉防抖计时器onUnmounted(() => {debouncedClick.cancel();});},});
<script setup>import { ref } from 'vue'const open = ref(false);</script><template><button @click="open = !open">Toggle</button><div>Hello Vue!</div></template><style scope>div{transition: height 0.1s linear;overflow: hidden;height: v-bind(open ? '30px' : '0px');}</style>
<script setup>import { ref, watch } from 'vue';const count = ref(0)const isEvent = ref(false)function increment() {state.count++}watch(count, function() {isEvent.value = count.value % 2 === 0})</script><template><button @click="increment">{{ count }}</button><p>is event: {{ isEvent ? 'yes' : 'no' }}</p></template>
watch(count, function() {isEvent.value = count.value % 2 === 0}, {// 上例中的 watch 不会立即执行,导致 isEvent 状态的初始值不准确。配置立即执行,会在一开始的时候立即执行一次immediate: true})
<template><h1> {{ count1 }} </h1><h1> {{ count2 }} </h1><button @click="count1++">count1</button><button @click="count2++">count2</button></template><script setup>import { watch, ref } from 'vue';const count1 = ref(0)const count2 = ref(0)watch(// 监听的表达式或函数() => ({count1: count1.value,count2: count2.value}),// 回调函数(newValue, oldValue) => {// 在这里执行需要的逻辑console.log('count1 或 count2 变化了:', newValue);},// immediate: true 表示在初始渲染时立即执行一次回调函数,以便处理初始的状态。// deep: true 表示深度监听,即对 newValue 和 oldValue 进行深层比较,而不是简单的引用比较。{ immediate: true, deep: true });</script><style scoped></style>
<script setup>import { ref, computed } from 'vue';const text = ref('')// computed 的回调函数里// 会根据已有并用到的状态计算出新的状态const capital = computed(function(){return text.value.toUpperCase();})</script><template><input v-model="text" /><p>to capital: {{ capital }}</p></template>
<script setup>import { defineProps } from 'vue';// 这里可以将 `username` 解构出来,// 但是一旦解构出来再使用,就不具备响应式能力defineProps({username: String})</script><template><p>username: {{ username }}</p></template>
子组件定义需要的参数
<script setup>const username = 'vue'</script><template><children :username="username" /></template>
父组件参入参数
<script setup>import { defineEmits, ref } from 'vue';const emit = defineEmits(['search'])const keyword = ref('')const onSearch = function() {emit('search', keyword.value)}</script><template><input v-model="keyword" /><button @click="onSearch">search</button></template>
子组件定义支持 emit
的函数
<script setup>const onSearch = function(keyword){console.log(keyword)}</script><template><children @search="onSearch" /></template>
父组件绑定子组件定义的事件
<script setup>import { defineExpose, ref } from 'vue';const keyword = ref('')const onSearch = function() {console.log(keyword.value)}defineExpose({ onSearch })</script><template><input v-model="keyword" /></template>
子组件对父组件暴露方法
<script setup>import { ref } from 'vue'const childrenRef = ref(null)const onSearch = function() {childrenRef.value.onSearch()}</script><template><children ref='childrenRef' /><button @click="onSearch">search</button></template>
父组件调用子组件的方法
import type { InjectionKey, Ref } from 'vue'export const ProvideKey = Symbol() as InjectionKey<Ref<string>>
在应用中使用 ProvideKey
<script setup lang="ts">import { provide, ref } from 'vue'import { ProvideKey } from './types'const text = ref<string>('123')provide(ProvideKey, text)</script><template><input v-model="text" /></template>
父组件为后代组件提供数据
<script setup lang="ts">import { inject } from 'vue'import { ProvideKey } from './types'const value = inject(ProvideKey)</script><template><h4>{{value}}</h4></template>
后代组件注入父组件提供的数据
当使用 <script setup>
时,defineProps()
宏函数支持从它的参数中推导类型
<script setup lang="ts">const props = defineProps({foo: { type: String, required: true },bar: Number})props.foo // stringprops.bar // number | undefined</script>
对同一个文件中的一个接口或对象类型字面量的引用:
interface Props {/* ... */}defineProps<Props>()
export interface Props {msg?: stringlabels?: string[]}const props = withDefaults(defineProps<Props>(), {msg: 'hello',labels: () => ['one', 'two']})
使用目前为实验性的响应性语法糖
<script setup lang="ts">interface Props {name: stringcount?: number}// 对 defineProps() 的响应性解构// 默认值会被编译为等价的运行时选项const {name, count = 100} = defineProps<Props>()</script>
<script setup lang="ts">// 运行时const emit = defineEmits(['change', 'update'])// 基于类型const emit = defineEmits<{(e: 'change', id: number): void(e: 'update', value: string): void}>()</script>
ref 会根据初始化时的值推导其类型:
import { ref } from 'vue'import type { Ref } from 'vue'const year: Ref<string | number> = ref('2020')year.value = 2020 // 成功!
import { reactive } from 'vue'interface Book {title: stringyear?: number}const book: Book = reactive({title: 'Vue 3 指引'})
你还可以通过泛型参数显式指定类型:
const double = computed<number>(() => {// 若返回值不是 number 类型则会报错})
<script setup lang="ts">function handleChange(event) {// `event` 隐式地标注为 `any` 类型console.log(event.target.value)}</script><template><inputtype="text"@change="handleChange" /></template>
显式地为事件处理函数的参数标注类型
function handleChange(event: Event) {const target = event.target as HTMLInputElementconsole.log(target.value)}
import { provide, inject } from 'vue'import type { InjectionKey } from 'vue'const key = Symbol() as InjectionKey<string>// 若提供的是非字符串值会导致错误provide(key, 'foo')// foo 的类型:string | undefinedconst foo = inject(key)
<script setup lang="ts">import { ref, onMounted } from 'vue'const el = ref<HTMLInputElement | null>(null)onMounted(() => {el.value?.focus()})</script><template><input ref="el" /></template>
<!-- MyModal.vue --><script setup lang="ts">import { ref } from 'vue'const isContentShown = ref(false)const open =() => (isContentShown.value = true)defineExpose({open})</script>
使用 TypeScript 内置的 InstanceType
工具类型来获取其实例类
<!-- App.vue --><script setup lang="ts">import MyModal from './MyModal.vue'type Modal = InstanceType<typeof MyModal>const modal = ref<Modal | null>(null)const openModal = () => {modal.value?.open()}</script>
import { defineComponent } from 'vue'export default defineComponent({// 启用了类型推导props: {name: String,id: [Number, String],msg: { type: String, required: true },metadata: null},mounted() {// 类型:string | undefinedthis.name// 类型:number|string|undefinedthis.id// 类型:stringthis.msg// 类型:anythis.metadata}})
使用 PropType 这个工具类型来标记更复杂的 props 类型
import { defineComponent } from 'vue'import type { PropType } from 'vue'interface Book {title: stringauthor: stringyear: number}export default defineComponent({props: {book: {// 提供相对 `Object` 更确定的类型type: Object as PropType<Book>,required: true},// 也可以标记函数callback: Function as PropType<(id: number) => void>},mounted() {this.book.title // stringthis.book.year // number// TS Error: argument of type 'string' is not// assignable to parameter of type 'number'this.callback?.('123')}})
import { defineComponent } from 'vue'type Payload = { bookName: string }export default defineComponent({emits: {addBook(payload: Payload) {// 执行运行时校验return payload.bookName.length > 0}},methods: {onSubmit() {this.$emit('addBook', {bookName: 123 // 类型错误})// 类型错误this.$emit('non-declared-event')}}})
计算属性会自动根据其返回值来推导其类型:
import { defineComponent } from 'vue'export default defineComponent({data() {return {message: 'Hello!'}},computed: {greeting() {return this.message + '!'}},mounted() {this.greeting // 类型:string}})
在某些场景中,你可能想要显式地标记出计算属性的类型以确保其实现是正确的:
import { defineComponent } from 'vue'export default defineComponent({data() {return {message: 'Hello!'}},computed: {// 显式标注返回类型greeting(): string {return this.message + '!'},// 标注一个可写的计算属性greetingUppercased: {get(): string {return this.greeting.toUpperCase()},set(newValue: string) {this.message = newValue.toUpperCase()}}}})
import { defineComponent } from 'vue'export default defineComponent({methods: {handleChange(event: Event) {console.log((event.target as HTMLInputElement).value)}}})
import axios from 'axios'declare module 'vue' {interface ComponentCustomProperties {$http: typeof axios$translate: (key: string) => string}}
我们可以将这些类型扩展放在一个 .ts
文件,或是一个影响整个项目的 *.d.ts
文件中
// 不工作,将覆盖原始类型。declare module 'vue' {interface ComponentCustomProperties {$translate: (key: string) => string}}
// 正常工作。export {}declare module 'vue' {interface ComponentCustomProperties {$translate: (key: string) => string}}
某些插件,比如 vue-router,提供了一些自定义的组件选项,比如 beforeRouteEnter:
import { defineComponent } from 'vue'export default defineComponent({beforeRouteEnter(to, from, next) {// ...}})
如果没有确切的类型标注,这个钩子函数的参数会隐式地标注为 any
类型。我们可以为 ComponentCustomOptions
接口扩展自定义的选项来支持:
import { Route } from 'vue-router'declare module 'vue' {interface ComponentCustomOptions {beforeRouteEnter?(to: Route,from: Route,next: () => void): void}}
:- | :- |
---|---|
createApp() | 创建一个应用实例 # |
createSSRApp() | 以 SSR 激活模式创建一个应用实例 # |
app.mount() | 将应用实例挂载在一个容器元素中 # |
app.unmount() | 卸载一个已挂载的应用实例 # |
app.provide() | 提供一个可以在应用中的所有后代组件中注入使用的值 # |
app.component() | 注册或获取全局组件 # |
app.directive() | 注册或获取全局指令 # |
app.use() | 安装一个插件 # |
app.mixin() | 全局注册一个混入 # |
app.version | 当前应用所使用的 Vue 版本号 # |
app.config | 获得应用实例的配置设定 # |
app.config.errorHandler | 为应用内抛出的未捕获错误指定一个全局处理函数 # |
app.config.warnHandler | 为 Vue 的运行时警告指定一个自定义处理函数 # |
app.config.performance | 在浏览器开发工具中追踪性能表现 # |
app.config.compilerOptions | 配置运行时编译器的选项 # |
app.config.globalProperties | 注册全局属性对象 # |
app.config.optionMergeStrategies | 定义自定义组件选项的合并策略的对象 # |
:- | :- |
---|---|
onMounted() | 组件挂载完成后执行 # |
onUpdated() | 状态变更而更新其 DOM 树之后调用 # |
onUnmounted() | 组件实例被卸载之后调用 # |
onBeforeMount() | 组件被挂载之前被调用 # |
onBeforeUpdate() | 状态变更而更新其 DOM 树之前调用 # |
onBeforeUnmount() | 组件实例被卸载之前调用 # |
onErrorCaptured() | 捕获了后代组件传递的错误时调用 # |
onRenderTracked() | 组件渲染过程中追踪到响应式依赖时调用 # |
onRenderTriggered() | 响应式依赖的变更触发了组件渲染时调用 # |
onActivated() | 若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用 # |
onDeactivated() | 若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用 # |
onServerPrefetch() | 组件实例在服务器上被渲染之前调用 # |
:- | :- |
---|---|
beforeCreate | 组件实例初始化完成之后立即调用 # |
created | 组件实例处理完所有与状态相关的选项后调用 # |
beforeMount | 组件被挂载之前调用 # |
mounted | 组件被挂载之后调用 # |
beforeUpdate | 状态变更而更新其 DOM 树之前调用 # |
updated | 状态变更而更新其 DOM 树之后调用 # |
beforeUnmount | 组件实例被卸载之前调用 # |
unmounted | 组件实例被卸载之后调用 # |
errorCaptured | 捕获了后代组件传递的错误时调用 # |
renderTracked Dev only | 组件渲染过程中追踪到响应式依赖时调用 # |
renderTriggered Dev only | 响应式依赖的变更触发了组件渲染时调用 # |
activated | 若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用 # |
deactivated | 若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用 # |
serverPrefetch SSR only | 组件实例在服务器上被渲染之前调用 # |
:- | :- |
---|---|
v-text | 更新元素的 textContent # |
v-html | 更新元素的 innerHTML # |
v-show | 切换元素的 display css 属性 # |
v-if | 有条件地渲染元素 # |
v-else | # |
v-else-if | # |
v-for | 多次渲染元素或模板块 # |
v-on | 绑定事件监听器 # |
v-bind | 动态地绑定一个或多个属性 # |
v-model | 创建双向绑定 # |
v-slot | 提供插槽或接收 props 的插槽 # |
v-pre | 跳过元素和它的子元素编译过程 # |
v-once | 只渲染元素和组件一次 # |
v-memo (3.2+) | 缓存一个模板的子树 # |
v-cloak | 保持在元素上直到实例结束编译 # |
serverPrefetch SSR only | 组件实例在服务器上被渲染之前调用 # |
:- | :- |
---|---|
createRenderer() | 创建一个自定义渲染器 # |