指令将会安装并执行
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/Yes
Scaffolding project in ./<your-project-name>...
Done.
安装依赖并启动开发服务器
$ cd <your-project-name>
$ npm install
$ npm run dev
当你准备将应用发布到生产环境时,请运行:
$ npm run build
此命令会在 ./dist
文件夹中为你的应用创建一个生产环境的构建版本
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script>
const { createApp, ref } = Vue
createApp({
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>
<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
双大括号{{}}
会将数据解释为纯文本,使用 v-html
指令,将插入 HTML
如果你是使用的 setup
语法糖。需要使用 defineprops
声名(可以直接使用a
/b
)
const props = defineProps({
a: String,
b: String
<a v-bind:[attributeName]="url"> ... </a>
<a :[attributeName]="url"> ... </a>
这里的 attributeName
会作为一个 JS 表达式被动态执行
v-on:submit.prevent="onSubmit"
──┬─ ─┬──── ─┬───── ─┬──────
┆ ┆ ┆ ╰─ Value 解释为JS表达式
┆ ┆ ╰─ Modifiers 由前导点表示
┆ ╰─ Argument 跟随冒号或速记符号
╰─ Name 以 v- 开头使用速记时可以省略
export default defineComponent({
setup() {
const state = reactive({ count: 0 });
return {
state
});
import { defineComponent, reactive } from 'vue';
export default defineComponent({
setup() {
const state = reactive({ count: 0 });
function increment() {
state.count++;
return {
state,
increment
reactive
只能用于对象、数组和 Map
、Set
这样的集合类型,对 string、number 和 boolean 这样的原始类型则需要使用ref
import { ref } from 'vue';
const count = ref(0);
console.log(count);
console.log(count.value);
count.value++;
console.log(count.value);
const objectRef = ref({ count: 0 });
objectRef.value = { count: 1 };
const obj = {
foo: ref(1),
bar: ref(2)
callSomeFunction(obj.foo);
const { foo, bar } = obj;
在 html 模板中不需要带 .value
就可以使用
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
{{ 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();
});
});
transition: height 0.1s linear;
overflow: hidden;
height: v-bind(open ? '30px' : '0px');
</style>
isEvent.value = count.value % 2 === 0
}, {
immediate: true
const text = ref('')
const capital = computed(function(){
return text.value.toUpperCase();
</script>
<template>
<input v-model="text" />
<p>to capital: {{ capital }}</p>
</template>
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>
父组件绑定子组件定义的事件
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
props.bar
</script>
对同一个文件中的一个接口或对象类型字面量的引用:
interface Props {}
defineProps<Props>()
Props 解构默认值
export interface Props {
msg?: string
labels?: string[]
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
使用目前为实验性的响应性语法糖
<script setup lang="ts">
interface Props {
name: string
count?: number
const {
name, count = 100
} = defineProps<Props>()
</script>
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
</script>
<script setup lang="ts">
function handleChange(event) {
console.log(event.target.value)
</script>
<template>
<input
type="text"
@change="handleChange" />
</template>
显式地为事件处理函数的参数标注类型
function handleChange(event: Event) {
const target = event.target as HTMLInputElement
console.log(target.value)
import type { InjectionKey } from 'vue'
const key = Symbol() as InjectionKey<string>
provide(key, 'foo')
const foo = inject(key)
import { ref, onMounted } from 'vue'
const el = ref<HTMLInputElement | null>(null)
onMounted(() => {
el.value?.focus()
</script>
<template>
<input ref="el" />
</template>
const isContentShown = ref(false)
const open =
() => (isContentShown.value = true)
defineExpose({
</script>
使用 TypeScript 内置的 InstanceType
工具类型来获取其实例类
<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>
id: [Number, String],
msg: { type: String, required: true },
metadata: null
mounted() {
this.name
this.id
this.msg
this.metadata
使用 PropType 这个工具类型来标记更复杂的 props 类型
import { defineComponent } from 'vue'
import type { PropType } from 'vue'
interface Book {
title: string
author: string
year: number
export default defineComponent({
props: {
book: {
type: Object as PropType<Book>,
required: true
callback: Function as PropType<(id: number) => void>
mounted() {
this.book.title
this.book.year
this.callback?.('123')
methods: {
handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
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