基础
通过vite创建项目并本地启用
1 2 3 4 5 6 7 8
| npm init vue@latest
npm install
npm run dev
npm run build
|
使用全局构建版本 cdn引用
1 2 3 4 5 6 7 8 9 10 11 12
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <div id="app">{{ message }}</div> <script> const { createApp } = Vue createApp({ data() { return { message: 'Hello Vue!' } } }).mount('#app') </script>
|
使用ES模块构建版本
注意我们使用了 <script type="module">
,且导入的 CDN URL 指向的是 Vue 的 ES 模块构建版本
1 2 3 4 5 6 7 8 9 10 11
| <div id="app">{{ message }}</div> <script type="module"> import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js' createApp({ data() { return { message: 'Hello Vue!' } } }).mount('#app') </script>
|
导入映射表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script type="importmap"> { "imports": { "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js" } } </script> <div id="app">{{ message }}</div> <script type="module"> import { createApp } from 'vue' createApp({ data() { return { message: 'Hello Vue!' } } }).mount('#app') </script>
|
拆分模块
1 2 3 4 5 6 7
| <div id="app"></div> <script type="module"> import { createApp } from 'vue' import MyComponent from './my-component.js' createApp(MyComponent).mount('#app') </script>
|
1 2 3 4 5 6 7
| export default { data() { return { count: 0 } }, template: `<div>count is {{ count }}</div>` }
|
应用配置
1 2 3 4 5
| app.config.errorHandler = (err) => { }
app.component('TodoDeleteButton', TodoDeleteButton)
|
动态参数
1 2 3
| <a v-on:click="doSomething"> ... </a>
<a @click="doSomething"> ... </a>
|
动态表达式
1 2 3 4 5 6 7
|
<a v-bind:[attributeName]="url"> ... </a>
<a :[attributeName]="url"> ... </a>
|
内联事件处理
1 2 3 4 5 6 7 8
| <button @click="warn('Form cannot be submitted yet.', $event)"> Submit </button>
<button @click="(event) => warn('Form cannot be submitted yet.', event)"> Submit </button>
|
1 2 3 4 5 6 7 8 9
| methods: { warn(message, event) { if (event) { event.preventDefault() } alert(message) } }
|
动态事件
1 2 3
| <a v-on:[eventName]="doSomething"> ... </a>
<a @[eventName]="doSomething">
|
事件修饰符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <a @click.stop="doThis"></a>
<form @submit.prevent="onSubmit"></form>
<a @click.stop.prevent="doThat"></a>
<form @submit.prevent></form>
<div @click.self="doThat">...</div>
<div @click.capture="doThis">...</div>
<a @click.once="doThis"></a>
<div @scroll.passive="onScroll">...</div>
|
按键修饰符
.enter
.tab
.delete
(捕获“Delete”和“Backspace”两个按键)
.esc
.space
.up
.down
.left
.right
1 2 3
| <input @keyup.enter="submit" /> <input @keyup.page-down="onPageDown" />
|
系统按键修饰符
.ctrl
.alt
.shift
.meta
1 2 3 4
| <input @keyup.alt.enter="clear" />
<div @click.ctrl="doSomething">Do something</div>
|
.exact
修饰符
.exact
修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符。1 2 3 4 5 6
| <button @click.ctrl="onClick">A</button>
<button @click.ctrl.exact="onCtrlClick">A</button>
<button @click.exact="onClick">A</button>
|
鼠标按键修饰符
- .left
- .right
- .middle
你不应该在定义 methods 时使用箭头函数,因为箭头函数没有自己的 this 上下文。
若要等待一个状态改变后的 DOM 更新完成,你可以使用 nextTick()
这个全局 API:
1 2 3 4 5 6 7 8 9 10 11
| import { nextTick } from 'vue' export default { methods: { increment() { this.count++ nextTick(() => { }) } } }
|
有状态方法
在某些情况下,我们可能需要动态地创建一个方法函数,比如创建一个预置防抖的事件处理器:
1 2 3 4 5 6 7 8 9
| import { debounce } from 'lodash-es' export default { methods: { click: debounce(function () { }, 500) } }
|
要保持每个组件实例的防抖函数都彼此独立,我们可以改为在 created 生命周期钩子中创建这个预置防抖的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export default { created() { this.debouncedClick = _.debounce(this.click, 500) }, unmounted() { this.debouncedClick.cancel() }, methods: { click() { } } }
|
计算属性
1 2
| <p>Has published books:</p> <span>{{ publishedBooksMessage }}</span>
|
1 2 3 4 5 6 7
| computed: { publishedBooksMessage() { return this.author.books.length > 0 ? 'Yes' : 'No' } }
|
可写计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| export default { data() { return { firstName: 'John', lastName: 'Doe' } }, computed: { fullName: { get() { return this.firstName + ' ' + this.lastName }, set(newValue) { [this.firstName, this.lastName] = newValue.split(' ') } } } }
|
表单输入绑定
1 2
| <input :value="text" @input="event => text = event.target.value"> <input v-model="text">
|
复选框
1 2
| <input type="checkbox" id="checkbox" v-model="checked" /> <label for="checkbox">{{ checked }}</label>
|
多个复选框
1 2 3 4 5 6 7
| <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>
|
单选框
1 2 3 4 5
| <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>
|
下拉框 选择器
1 2 3 4 5 6 7
| <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>
|
多选
1 2 3 4 5 6
| <div>Selected: {{ selected }}</div> <select v-model="selected" multiple> <option>A</option> <option>B</option> <option>C</option> </select>
|
值绑定
对于单选按钮,复选框和选择器选项,v-model
绑定的值通常是静态的字符串 (或者对复选框是布尔值):
1 2 3 4 5 6 7 8
| <input type="radio" v-model="picked" value="a" />
<input type="checkbox" v-model="toggle" />
<select v-model="selected"> <option value="abc">ABC</option> </select>
|
复选框
true-value
和 false-value
是 Vue 特有的 attributes,仅支持和 v-model
配套使用。这里 toggle
属性的值会在选中时被设为 ‘yes’,取消选择时设为 ‘no’。你同样可以通过 v-bind
将其绑定为其他动态值:
1 2 3 4 5 6 7 8 9 10
| <input type="checkbox" v-model="toggle" true-value="yes" false-value="no" /> <input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue" />
|
单选
1 2
| <input type="radio" v-model="pick" :value="first" /> <input type="radio" v-model="pick" :value="second" />
|
选择器选项
v-model
同样也支持非字符串类型的值绑定!在上面这个例子中,当某个选项被选中,selected
会被设为该对象字面量值 { number: 123 }
。
1 2 3 4
| <select v-model="selected"> <option :value="{ number: 123 }">123</option> </select>
|
修饰符
生命周期
1 2 3 4 5
| export default { mounted() { console.log(`the component is now mounted.`) } }
|
侦听器watch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| export default { data() { return { question: '', answer: 'Questions usually contain a question mark. ;-)' } }, watch: { question(newQuestion, oldQuestion) { if (newQuestion.includes('?')) { this.getAnswer() } } }, methods: { async getAnswer() { this.answer = 'Thinking...' try { const res = await fetch('https://yesno.wtf/api') this.answer = (await res.json()).answer } catch (error) { this.answer = 'Error! Could not reach the API. ' + error } } } }
|
watch
选项也支持把键设置成用 . 分隔的路径:
1 2 3 4 5 6 7 8
| export default { watch: { 'some.nested.key'(newValue) { } } }
|
深层侦听器
1 2 3 4 5 6 7 8 9 10 11 12
| export default { watch: { someObject: { handler(newValue, oldValue) { }, deep: true } } }
|
模板引用
访问模板引用
1 2 3 4 5 6 7 8 9 10
| <script> export default { mounted() { this.$refs.input.focus() } } </script> <template> <input ref="input" /> </template>
|
注意,你只可以在组件挂载后才能访问模板引用。如果你想在模板中的表达式上访问 $refs.input,在初次渲染时会是 null。这是因为在初次渲染前这个元素还不存在呢!
v-for
中的模板引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <script> export default { data() { return { list: [] } }, mounted() { console.log(this.$refs.items) } } </script> <template> <ul> <li v-for="item in list" ref="items"> {{ item }} </li> </ul> </template>
|
应该注意的是,ref 数组并不保证与源数组相同的顺序。
函数模板的引用
1
| <input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
|
组件上的 ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script> import Child from './Child.vue' export default { components: { Child }, mounted() { } } </script> <template> <Child ref="child" /> </template>
|
expose
选项可以用于限制对子组件实例的访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| export default { expose: ['publicData', 'publicMethod'], data() { return { publicData: 'foo', privateData: 'bar' } }, methods: { publicMethod() { }, privateMethod() { } } }
|
在上面这个例子中,父组件通过模板引用访问到子组件实例后,仅能访问 publicData
和 publicMethod
。
组件
定义组件
1 2 3 4 5 6 7 8 9 10 11 12
| <script> export default { data() { return { count: 0 } } } </script> <template> <button @click="count++">You clicked me {{ count }} times.</button> </template>
|
1 2 3 4 5 6 7 8 9 10 11
| export default { data() { return { count: 0 } }, template: ` <button @click="count++"> You clicked me {{ count }} times. </button>` }
|
使用组件
1 2 3 4 5 6 7 8 9 10 11 12
| <script> import ButtonCounter from './ButtonCounter.vue' export default { components: { ButtonCounter } } </script> <template> <h1>Here is a child component!</h1> <ButtonCounter /> </template>
|
父传子
1 2 3 4 5 6 7 8 9
| <script> export default { props: ['title'] } </script> <template> <h4>{{ title }}</h4> </template>
|
子传父 通过监听来实现
1 2 3 4 5 6
| //父 <BlogPost ... @enlarge-text="postFontSize += 0.1" />
|
1 2 3 4 5 6 7 8
| //子
<template> <div class="blog-post"> <h4>{{ title }}</h4> <button @click="$emit('enlarge-text')">Enlarge text</button> </div> </template>
|
通过emits
来声明需要抛出的事件
1 2 3 4 5 6 7
| <script> export default { props: ['title'], emits: ['enlarge-text'] } </script>
|
插槽传递
1 2 3 4
| //父 <AlertBox> Something bad happened. </AlertBox>
|
1 2 3 4 5 6 7
| //子 <template> <div class="alert-box"> <strong>This is an Error for Demo Purposes</strong> <slot /> </div> </template>
|
动态组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <script> import Tab1 from "./Tab1.vue" import Tab2 from "./Tab2.vue" import Tab3 from "./Tab3.vue" export default { components: { Tab1, Tab2,Tab3 }, data(){ return { tab:"Tab1", count:0, list:["Tab1","Tab2","Tab3"] } } } </script> <template> <div> <button v-for="index in list" :key='index' @click="tab = index"> {{index}} </button> <div> <component :is="tab"></component> </div> </div> </template>
|
元素位置限制
1 2 3
| <table> <tr is="vue:blog-post-row"></tr> </table>
|
组件注册
全局注册
1 2 3 4 5 6 7 8 9 10
| import { createApp } from 'vue' const app = createApp({}) app.component( 'MyComponent', { } )
|
单文件导入
1 2
| import MyComponent from './App.vue' app.component('MyComponent', MyComponent)
|
链式调用
1 2 3 4
| app .component('ComponentA', ComponentA) .component('ComponentB', ComponentB) .component('ComponentC', ComponentC)
|
1 2 3 4
| <ComponentA/> <ComponentB/> <ComponentC/>
|
局部注册
1 2 3 4 5 6 7 8 9 10 11
| <script> import ComponentA from './ComponentA.vue' export default { components: { ComponentA } } </script> <template> <ComponentA /> </template>
|
props
1 2 3 4 5 6 7
| export default { props: ['foo'], created() { console.log(this.foo) } }
|
1 2 3 4 5 6
| export default { props: { title: String, likes: Number } }
|
1 2 3 4 5
| export default { props: { greetingMessage: String } }
|
1
| <MyComponent greeting-message="hello" />
|
静态
1
| <BlogPost title="My journey with Vue" />
|
动态
1 2 3 4
| <BlogPost :title="post.title" />
<BlogPost :title="post.title + ' by ' + post.author.name" />
|
不同的值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <BlogPost :likes="42" />
<BlogPost is-published />
<BlogPost :is-published="false" />
<BlogPost :comment-ids="[234, 266, 273]" />
<BlogPost :author="{ name: 'Veronica', company: 'Veridian Dynamics' }" />
|
使用一个对象绑定多个 prop
1 2 3 4 5 6 7 8 9 10
| export default { data() { return { post: { id: 1, title: 'My Journey with Vue' } } } }
|
1 2 3
| <BlogPost v-bind="post" /> or <BlogPost :id="post.id" :title="post.title" />
|
单向数据流
1 2 3 4 5 6 7
| export default { props: ['foo'], created() { this.foo = 'bar' } }
|
想要更改最好初始化一个新的变量
1 2 3 4 5 6 7 8 9 10
| export default { props: ['initialCounter'], data() { return { counter: this.initialCounter } } }
|
做进一步转换
1 2 3 4 5 6 7 8 9
| export default { props: ['size'], computed: { normalizedSize() { return this.size.trim().toLowerCase() } } }
|
prop验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| export default { props: { propA: Number, propB: [String, Number], propC: { type: String, required: true }, propD: { type: Number, default: 100 }, propE: { type: Object, default(rawProps) { return { message: 'hello' } } }, propF: { validator(value) { return ['success', 'warning', 'danger'].includes(value) } }, propG: { type: Function, default() { return 'Default function' } } } }
|
- 所有 prop 默认都是可选的,除非声明了
required: true
。
- 除
Boolean
外的未传递的可选 prop 将会有一个默认值 undefined
。
Boolean
类型的未传递 prop
将被转换为 false
。这可以通过为它设置 default 来更改——例如:设置为 default: undefined
将与非布尔类型的 prop
的行为保持一致。
- 如果声明了
default
值,那么在 prop
的值被解析为 undefined
时,无论 prop
是未被传递还是显式指明的 undefined
,都会改为 default
值。
注意 prop 的校验是在组件实例被创建之前,所以实例的属性 (比如 data、computed 等) 将在 default 或 validator 函数中不可用。
type的类型
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
自定义类或构造函数
1 2 3 4 5 6
| class Person { constructor(firstName, lastName) { this.firstName = firstName this.lastName = lastName } }
|
1 2 3 4 5
| export default { props: { author: Person } }
|
bool类型转换
1 2 3 4 5
| export default { props: { disabled: Boolean } }
|
1 2 3 4
| <MyComponent disabled />
<MyComponent />
|
1 2 3 4 5
| export default { props: { disabled: [Boolean, Number] } }
|
组件事件
1 2
| <button @click="$emit('someEvent')">click me</button>
|
1 2 3 4 5 6 7
| export default { methods: { submit() { this.$emit('someEvent') } } }
|
父组件监听
1 2
| <MyComponent @some-event="callback" /> <MyComponent @some-event.once="callback" />
|
事件参数
1 2 3
| <button @click="$emit('increaseBy', 1)"> Increase by 1 </button>
|
1
| <MyButton @increase-by="(n) => count += n" />
|
或者
1
| <MyButton @increase-by="increaseCount" />
|
1 2 3 4 5
| methods: { increaseCount(n) { this.count += n } }
|
所有传入 $emit() 的额外参数都会被直接传向监听器。举例来说,$emit(‘foo’, 1, 2, 3) 触发后,监听器函数将会收到这三个参数值。
声明触发的事件
1 2 3
| export default { emits: ['inFocus', 'submit'] }
|
1 2 3 4 5 6 7 8
| export default { emits: { submit(payload) { } } }
|
事件校验
和对 props
添加类型校验的方式类似,所有触发的事件也可以使用对象形式来描述。
要为事件添加校验,那么事件可以被赋值为一个函数,接受的参数就是抛出事件时传入 this.$emit
的内容,返回一个布尔值来表明事件是否合法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| export default { emits: { click: null, submit: ({ email, password }) => { if (email && password) { return true } else { console.warn('Invalid submit event payload!') return false } } }, methods: { submitForm(email, password) { this.$emit('submit', { email, password }) } } }
|
组件v-model
1 2 3 4 5 6 7 8 9 10 11
| <input v-model="searchText" />
<input :value="searchText" @input="searchText = $event.target.value" />
<CustomInput :modelValue="searchText" @update:modelValue="newValue => searchText = newValue" />
|
原理
- 将内部原生
<input>
元素的 value
attribute
绑定到 modelValue
prop
- 当原生的
input
事件触发时,触发一个携带了新值的 update:modelValue
自定义事件1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> export default { props: ['modelValue'], emits: ['update:modelValue'] } </script> <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template>
|
另一种在组件内实现 v-model
的方式是使用一个可写的,同时具有 getter
和 setter
的 computed
属性。get
方法需返回 modelValue
prop,而 set
方法需触发相应的事件:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script> export default { props: ['modelValue'], emits: ['update:modelValue'], computed: { value: { get() { return this.modelValue }, set(value) { this.$emit('update:modelValue', value) } } } } </script> <template> <input v-model="value" /> </template>
|
v-model
的参数
我们可以通过给 v-model
指定一个参数来更改这些名字:1
| <MyComponent v-model:title="bookTitle" />
|
子组件应声明一个 title prop,并通过触发 update:title 事件更新父组件值:1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script> export default { props: ['title'], emits: ['update:title'] } </script> <template> <input type="text" :value="title" @input="$emit('update:title', $event.target.value)" /> </template>
|
多个v-model
绑定
组件上的每一个 v-model
都会同步不同的 prop
,而无需额外的选项:1 2 3 4
| <UserName v-model:first-name="first" v-model:last-name="last" />
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script> export default { props: { firstName: String, lastName: String }, emits: ['update:firstName', 'update:lastName'] } </script> <template> <input type="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)" /> <input type="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)" /> </template>
|
又有参数又有修饰符的 v-model
绑定
arg + "Modifiers"
1
| <MyComponent v-model:title.capitalize="myText">
|
1 2 3 4 5 6 7
| export default { props: ['title', 'titleModifiers'], emits: ['update:title'], created() { console.log(this.titleModifiers) } }
|
attributes继承1
| <MyButton class="large" />
|
1
| <button class="large">click me</button>
|
禁用继承
组件选项配置中
访问1
| <span>Fallthrough attribute: {{ $attrs }}</span>
|
1 2 3
| <div class="btn-wrapper"> <button class="btn" v-bind="$attrs">click me</button> </div>
|
多节点继承1 2 3
| <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer>
|
js访问穿透1 2 3 4 5
| export default { created() { console.log(this.$attrs) } }
|
插槽1 2 3
| <FancyButton> Click me! </FancyButton>
|
1 2 3
| <button class="fancy-btn"> <slot></slot> </button>
|
不仅仅是文本可以当做插槽1 2 3 4
| <FancyButton> <span style="color:red">Click me!</span> <AwesomeIcon name="plus" /> </FancyButton>
|
默认内容1 2 3 4 5
| <button type="submit"> <slot> Submit </slot> </button>
|
具名插槽1 2 3 4 5
| <NewComVue title="abcdefg"> <template #header>header demo</template> <template #default>未命名插槽</template> </NewComVue>
|
1 2 3 4 5 6 7 8 9
| <template> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> </template>
|
v-slot
有对应的简写 #,因此 <template v-slot:header>
可以简写为 <template #header>
。其意思就是“将这部分模板片段传入子组件的 header
插槽中”。
动态插槽名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script> import NewComVue from './components/NewCom.vue' export default{ components:{ NewComVue }, data(){ return { cc1:'header', cc2:'default' } } } </script> <NewComVue title="abcdefg"> <template v-slot:[cc1]>header demo</template> <template #[cc2]>未命名插槽</template> </NewComVue>
|
作用域插槽
1 2 3 4
| <div> <slot :text="greetingMessage" :count="1"></slot> </div>
|
1 2 3
| <MyComponent v-slot="slotProps"> {{ slotProps.text }} {{ slotProps.count }} </MyComponent>
|
具名作用域插槽
1 2 3 4 5
| <MyComponent> <template #header="headerProps"> {{ headerProps }} </template> </MyComponent>
|
向具名插槽中传入props
1
| <slot name="header" message="hello"></slot>
|
混用具名插槽和默认插槽
1 2 3 4 5 6 7 8 9 10 11 12
| <template> <MyComponent> <template #default="{ message }"> <p>{{ message }}</p> </template>
<template #footer> <p>Here's some contact info</p> </template> </MyComponent> </template>
|
高级列表组件示例
1 2 3 4 5 6 7 8 9 10 11
| <FancyList :api-url="url" :per-page="10"> <template #item="{ body, username, likes }"> <div class="item"> <p>{{ body }}</p> <p> by {{ username }} | {{ likes }} likes </p> </div> </template> </FancyList>
|
1 2 3 4 5
| <ul> <li v-for="item in items"> <slot name="item" v-bind="item"></slot> </li> </ul>
|
无渲染组件
1 2 3
| <MouseTracker v-slot="{ x, y }"> Mouse is at: {{ x }}, {{ y }} </MouseTracker>
|
MouseTracker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <script> export default { data(){ return { x:0, y:0 } }, methods:{ update(event){ this.x = event.pageX this.y = event.pageY } }, mounted(){ window.addEventListener('mousemove',this.update) }, unmounted(){ window.removeEventListener('mousemove',this.update) } } </script> <template> <slot :x="x" :y="y"></slot> </template>
|
依赖注入
解决props逐级传递问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| export default{ provide:{ message:"root" } }
export default{ data(){ return { message:"hello" }, provide(){ return { message:this.message } } } }
|
应用层provide
1 2 3 4
| import {createApp} from "vue" const app = createApp() app.provide("message,'hello')
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| export default{ inject:["message"], created(){ console.log(this.message) } } export default{ inject:["message"], data(){ return { rullMessage:this.message } } }
|
注入别名
1 2 3 4 5 6 7
| export default{ inject:{ localMessage:{ from :"message" } } }
|
注入默认值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| export default{ inject:{ message:{ from : "message", default:"default value } }, user:{ // 对于非基础类型数据,如果创建开销比较大,或是需要确保每个组件实例 // 需要独立数据的,请使用工厂函数 default:()=>({name:"john}) } }
|
和响应式数据配合
1 2 3 4 5 6 7 8 9 10 11 12 13
| import {computed} from "vue" export default{ data(){ return { message:"hello" } }, provide(){ return { message:computed(()=>this.message) } } }
|
使用Symbol注入名
解决大型项目包含过多依赖提供,或者提供给其他开发者使用的组件库,最好使用symbol注入以免冲突
1 2
| export const myInjectionKey = Symbol()
|
1 2 3 4 5 6 7 8 9 10 11
| import {myInjectionKey} from "./keys.js" export default{ provide(){ return { [myInjectionKey]:{ } } } }
|
1 2 3 4 5 6 7
| import {myInjectionKey} from './keys.js' export default{ inject:{ injected:{from:myInjectionKey} } }
|
异步组件
Vue 提供了 defineAsyncComponent 方法来实现此功能
1 2 3 4 5 6 7 8
| import {defineAsyncComponent} from 'vue' const AsyncComp = defineAsyncComponent(()=>{ return new Promise((resolve,reject)=>{ resolve("返回获取的组件") }) })
|
ES模块动态导入
1 2 3 4
| import {defineSyncComponent} from 'vue' const AsyncComp = defineSyncComponent((=>{ import('./components/MyCom.vue) }))
|
全局注册
1 2 3
| app.component('MyComponent', defineAsyncComponent(() => import('./components/MyComponent.vue') ))
|
局部注册
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> import { defineAsyncComponent } from 'vue' export default { components: { AdminPage: defineAsyncComponent(() => import('./components/AdminPageComponent.vue') ) } } </script> <template> <AdminPage /> </template>
|
加载与错误状态
1 2 3 4 5 6 7 8 9 10 11 12 13
| const AsyncComp = defineAsyncComponent({ loader: () => import('./Foo.vue'), loadingComponent: LoadingComponent, delay: 200, errorComponent: ErrorComponent, timeout: 3000 })
|
组合式函数
mouse.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import {ref,onMounted,onUnmounted} from 'vue' export function useMouse(){ const x = ref(0) const y = ref(0) function update(e){ x.value = e.pageX y.value = e.pageY } onMounted(()=>window.addEventListener('mousemove',update)) onUnmounted(()=>window.removeEventListener('mousemove',update)) return { x,y } }
|
调用
1 2 3 4 5 6 7
| <script setup> import {useMouse} from '../use/mouse.js' const {x,y} = useMouse() </script> <template> {{x}} --- {{y}} </template>
|
异步状态示例