創建
創建應用
const app = Vue.createApp({ /* 選項 */ })
鏈式
Vue.createApp({})
.component('SearchInput', SearchInputComponent)
.directive('focus', FocusDirective)
.use(LocalePlugin)
加載應用
const vm = app.mount('#app')
生命周期
動態綁定js表達式
綁定js表達式,根據表達式的值動態綁定
<a v-on:[js表達式]=""></a>
<a @[event]="doSomething"> ... </a>
約束
如空格和引號,放在 HTML attribute 名里是無效的
<a v-bind:['foo' + bar]="value"> ... </a>
變通的辦法是使用沒有空格或引號的表達式,或用computed替代這種復雜表達式
在 DOM 中使用模板時,避免使用大寫字符來命名鍵名(全部強制轉為小寫,相當於someattr的property
<a v-bind:[someAttr]="value"> ... </a>
data
命名
內置 API:$ 前綴
內部 property: _ 前綴
避免使用這兩個字符開頭的的頂級 data property 名稱。
method
Vue 自動為 methods 綁定 this,以便於它始終指向組件實例
在定義 methods 時應避免使用箭頭函數,因為這會阻止 Vue 綁定恰當的 this 指向
防抖和節流
防抖:對於短時間內連續觸發的事件,使某個時間期限內,事件處理函數只執行一次。(讀條)
節流:函數執行一次后,在某個時間段內暫時失效,過了這段時間后再重新激活(CD)
app.component('save-button', {
created() {
// 用 Lodash 的防抖函數
this.debouncedClick = _.debounce(this.click, 500)
},
unmounted() {
// 移除組件時,取消定時器
this.debouncedClick.cancel()
},
methods: {
click: _.debounce(function() {
// ... 響應點擊 ...
}, 500)
},
template: `
<button @click="debouncedClick">
Save
</button>
`
})
computed
Getter:
與methods的區別:
計算屬性是基於它們的反應依賴關系緩存的。計算屬性只在相關響應式依賴發生改變時它們才會重新求值。
Setter:
默認只有getter,默認時可以提供setter
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
watch
使用 watch 選項允許我們執行異步操作 (訪問一個 API),限制我們執行該操作的頻率,並在我們得到最終結果前,設置中間狀態
watch: {
xxx(new,old){
...
}
}
(同樣可以使用vm.$watch API
class
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
也可以綁定一個返回對象的computed
<div :class="classObject"></div>
...
computed: {
classObject() {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
//數組,三元表達式同樣允許
<div :class="[activeClass, errorClass]"></div>
<div :class="[isActive ? activeClass : '', errorClass]"></div>
組件中綁定class,可以選擇將傳進來的attr綁定到特定的tag上
app.component('my-component', {
template: `
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
`
})
style
直接綁定一個樣式對象,當然也可以綁定分散的data,語法類似css
<div :style="styleObject"></div> //數組語法可以將多個樣式對象應用到同一個元素上
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
自動添加前綴
在 :style 中使用需要 (瀏覽器引擎前綴) vendor prefixes 的 CSS property 時,如 transform,Vue 將自動偵測並添加相應的前綴。
多重值
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
只會渲染最后一個被瀏覽器支持的值
v-if & v-show
- 如果需要非常頻繁地切換,則使用 v-show 較好;
- 如果在運行時條件很少改變,則使用 v-if 較好。
當 v-if 與 v-for 一起使用時,v-if 具有比 v-for 更高的優先級,不推薦同時使用
修正方法:
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo }}
</li>
</template>
v-for
<li v-for="(item, index) in items"></li> <!-- 可以用 of 替代 in 作為分隔符 -->
<li v-for="(value, name) in myObject"> <!-- 遍歷對象 -->
{{ name }}: {{ value }}
</li>
<li v-for="(value, name, index) in myObject"> <!-- 第三個參數為索引 -->
{{ index }}. {{ name }}: {{ value }}
</li>
建議盡可能在使用 v-for 時提供 key attribute
除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴默認行為以獲取性能上的提升,用於強制替換元素/組件而不是重復使用它
更變或替換數組
更變:push() pop() shift() unshift() splice() sort() reverse()
替換:filter() concat() slice()
Vue 不會重新渲染整個列表
組件數據傳遞
組件中使用v-for不會將數據自動傳遞到組件里,因為組件有自己獨立的作用域。
為了把迭代數據傳遞到組件里,我們要使用 props:
<my-component
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
></my-component>
v-on
$event可以傳入方法,獲得原生DOMevent
- $event是指當前觸發的是什么事件(鼠標事件,鍵盤事件等)
- $event.target則指的是事件觸發的目標,即哪一個元素觸發了事件,這將直接獲取該dom元素
- $event.target.tagName指向事件發生目標的tag
<button @click="one($event), two($event)">
Submit
</button>
methods: {
one(event) {
// first handler logic...
},
two(event) {
// second handler logic...
}
}
事件修飾符
.stop .prevent .capture .self .once .passive
順序不同產生的效果不同
<!-- 阻止單擊事件繼續傳播 -->
<a @click.stop="doThis"></a>
按鍵、系統、鼠標修飾符
按鍵:.enter .tab .delete .esc .space .up .down .left .right
系統:.ctrl .alt .shift .meta
鼠標:.left .right .middle
.exact
精確控制按下的系統修飾符組合
v-model
v-model 會忽略所有表單元素的 value、checked、selected attribute的初始值而總是將當前活動實例的數據作為數據來源。你應該通過 JavaScript 在組件的 data 選項中聲明初始值。
多選綁定
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
值綁定
通過:value來綁定v-model的值
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
v-model
.lazy 當change而非input
.number 轉換輸入值為數值類型
.trim 過濾首尾空白字符
component
組件可以在Vue根實例中作為自定義元素使用
與new Vue接受相同的選項,例如 data、computed、watch、methods 以及生命周期鈎子等(無el)
<!-- Prop -->
通過 Prop 向子組件傳遞數據,被注冊之后,就可以作為自定義attribute傳遞
<!-- emit -->
$emit 附加參數都會傳給監聽器回調,可以附加額外參數
@click="$emit('enlarge-text')"
@click="$emit('enlarge-text',0.1)"
使用$event來訪問到被拋出的值
@enlarge-text="postFontSize += $event"
如果處理函數是一個方法,那么拋出的值會作為第一個參數來傳入這個方法
emits選項也可以拋出事件
app.component('blog-post', {
props: ['title'],
emits: ['enlarge-text']
})
這將允許你檢查組件拋出的所有事件(validate)
<!-- 組件下的v-model -->
<custom-input v-model="searchText"></custom-input>
app.component('custom-input', {
props: ['modelValue'],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
`
})
使用computed方法實現:
app.component('custom-input', {
props: ['modelValue'],
template: `
<input v-model="value">
`,
computed: {
value: {
get() {
return this.modelValue
},
set(value) { this.$emit('update:modelValue', value)
}
}
}
})
<!-- 動態組件 -->
<component :is="currentTabComponent"></component>
組件會在currentTabComponent改變時改變,cTC可以包括已注冊組件的名字,或一個組件選項對象
v-is 可以讓本來渲染出錯(不該出現在特定元素內部或外部的元素,比如ul只可以有li)的tag有了變通方法
<table>
<tr v-is="'blog-post-row'"></tr>
</table>
值應為js字符串文本
<!-- 命名法 -->
組件命名:(推薦kebab-case,避免與當前以及未來的 HTML 元素發生沖突)
(對於非字符串模板或單文件組件,即對於直接在DOM中使用組件時)
1.全部小寫
2.包含連字符 (及:即有多個單詞與連字符符號連接)
對於PascalCase(首字母大寫命名)
定義一個組件時,你在引用這個自定義元素時兩種命名法都可以使用
HTML 屬性名不區分大小寫,在JavaScript中的駝峰
app.component('blog-post', {
props: ['postTitle'],
template: `
<h3>{{ postTitle }}</h3>
`
})
在HTML則是橫線字符分割
<blog-post post-title="hello!"></blog-post>
???
需要注意的是如果我們從以下來源使用模板的話,這條限制是不存在的:
字符串模板 (例如:template: '...')
單文件組件
<script type="text/x-template"></script>
<!-- 注冊 -->
全局注冊:
app.component('component-a', {
/* ... */
})
局部注冊:(局部注冊的組件在其子組件中不可用
const ComponentA = {
/* ... */
}
const app = Vue.createApp({
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
Props
-- 字符串數組形式
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
-- 每個Prop指定類型
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其他構造函數
}
-- 傳遞靜態或動態的 Prop
任何類型的值都可以通過v-bind:的attribute傳給一個prop
-- 單向數據流
父子 prop 之間形成了一個單向下行綁定:
父級 prop 的更新會向下流動到子組件中,反之不行
每次父級組件發生變更時,子組件中所有的 prop 都將會刷新為最新的值
--> 不應該在一個子組件內部改變 prop,引用傳入將會影響到父組件的狀態
如果需要改變prop:
1.如果希望作為本地prop使用,最好定義一個本地data property接收父傳遞
2.如果要轉換,可以定義一個計算屬性,對原始的值進行轉換然后保存
<!-- 驗證 -->
propE: {
type: Object,
// 對象或數組默認值必須從一個工廠函數獲取
default: function() {
return { message: 'hello' }
}
},
propF: {
validator: function(value) {
// 這個值必須匹配下列字符串中的一個
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
注意:prop 會在一個組件實例創建之前進行驗證,所以實例的 property (如 data、computed 等) 在 default 或 validator 函數中是不可用的。
type 可以是String Number Boolean Array Object Date Function Symbol
<!-- 命名 -->
當你使用 DOM 中的模板時,
camelCase (駝峰命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名:
app.component('blog-post', {
// camelCase in JavaScript
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
<!-- kebab-case in HTML -->
<blog-post post-title="hello!"></blog-post>
非 Prop 的 Attribute
一個非 prop 的 attribute 定義:
傳向一個組件,但是該組件並沒有相應 props 或 emits 定義的 attribute
包括 class、style 和 id 等屬性。
<!-- 單個根節點上的 Attribute 繼承 -->
當組件返回單個根節點時,非 prop attribute 將自動添加到根節點的 attribute 中。
app.component('date-picker', {
template: `
<div class="date-picker">
<input type="datetime" />
</div>
`
})
<!-- 禁用 Attribute 繼承 -->
如果你不希望組件的根元素繼承 attribute,你可以在組件的選項中設置 inheritAttrs: false
可以訪問組件的 $attrs property獲取
app.component('date-picker', {
inheritAttrs: false,
template: `
<div class="date-picker">
<input type="datetime" v-bind="$attrs" />
</div>
`
})
<!-- 多個根節點上的 Attribute 繼承 -->
有多個根節點的組件不具有自動 attribute 回退行為
如果未顯式綁定 $attrs,將發出運行時警告。
自定義事件
不同於組件和 prop,事件名不存在任何自動化的大小寫轉換。
而是觸發的事件名需要完全匹配監聽這個事件所用的名稱
!觸發一個 camelCase 名字的事件,則監聽這個名字的 kebab-case 版本是不會有任何效果的:
this.$emit('myEvent')
<my-component @my-event="doSomething"></my-component>
推薦你始終使用 kebab-case 的事件名
<!-- 定義自定義事件 -->
通過 emits 選項在組件上定義已發出的事件。
app.component('custom-form', {
emits: ['in-focus', 'submit']
})
當在 emits 選項中定義了原生事件 (如 click) 時,將使用組件中的事件替代原生事件偵聽器。
建議定義所有發出的事件,以便更好地記錄組件應該如何工作。
-- 驗證拋出的事件
app.component('custom-form', {
emits: {
// 沒有驗證
click: null,
// 驗證submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm() {
this.$emit('submit', { email, password })
}
}
})
<!-- v-model -->
已在v-model中介紹過,回顧一下:(多個綁定)
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
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)">
`
})
<!-- 定義自定義修飾符 -->
v-model 有內置修飾符——.trim、.number 和 .lazy
3.x中,添加到組件 v-model 的修飾符將通過 modelModifiers prop 提供給組件
slot
<slot></slot>
組件渲染時替換內容
!template 中沒有包含一個 <slot> 元素,則該組件起始標簽和結束標簽之間的任何內容都會被拋棄
<!-- 作用域 -->
<todo-button action="delete">
Clicking here will {{ action }} an item
<!-- `action` 未被定義,因為它的內容是傳遞*到* <todo-button>,而不是*在* <todo-button>里定義的。 -->
</todo-button>
規則:
父級模板里的所有內容都是在父級作用域中編譯的;
子模板里的所有內容都是在子作用域中編譯的
<!-- 后備內容 -->
<button type="submit">
<slot>Submit</slot>
</button>
當slot不顯示時顯示
<!-- 具名插槽 -->
-定義:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一個不帶 name 的 <slot> 出口會帶有隱含的名字“default”。
-使用:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
!v-slot 只能添加在 <template> 上 (只有一種例外情況:
當被提供的內容只有默認插槽時,組件的標簽才可以被當作插槽的模板來使用)
<!-- 插槽 prop -->
要使 item 可用於父級提供的 slot 內容,我們可以添加一個 <slot> 元素並將其綁定為屬性
<ul>
<li v-for="( item, index ) in items">
<slot :item="item"></slot>
</li>
</ul>
-- 重命名
<todo-list v-slot="{ item: todo }">
<i class="fas fa-check"></i>
<span class="green">{{ todo }}</span>
</todo-list>
-- 后備內容
<todo-list v-slot="{ item = 'Placeholder' }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
<!-- 動態插槽名 -->
<template v-slot:[dynamicSlotName]>
<!-- 縮寫 -->
v-slot: --> #
<template #header>
<h1>Here might be a page title</h1>
</template>
提供 / 注入
父組件可以作為其所有子組件的依賴項提供程序,而不管組件層次結構有多深
-- 父組件有一個 provide 選項來提供數據
-- 子組件有一個 inject 選項來開始使用這個數據。
//provide
provide: {
xxx:
}
//inject
${this.xxx}
要訪問組件實例 property,我們需要將 provide 轉換為返回對象的函數
//返回對象
provide() {
return {
todoLength: this.todos.length
}
}
處理響應性
默認情況下,provide/inject 綁定不是被動綁定,無響應性。
-- 增加computed達到響應
provide() {
return {
todoLength: Vue.computed(() => this.todos.length)
}
}
-- ref property 或 reactive
import { provide, reactive, ref } from 'vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
}
動態組件 & 異步組件
keep-alive
<!-- 失活的組件將會被緩存!-->
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
defineAsyncComponent
?
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
}
模板引用???
返回對象
應該避免在模板或計算屬性中訪問 $refs
處理邊界情況
強制更新
大概率出現錯誤,非常罕見的情況使用$forceUpdate
強制更新
低級靜態組件與 v-once
在 Vue 中渲染純 HTML 元素的速度非常快,但有時你可能有一個包含很多靜態內容的組件。在這些情況下,可以通過向根元素添加 v-once
指令來確保只對其求值一次,然后進行緩存
Mixin
// define a mixin object
const myMixin = {
created() {
this.hello()
},
methods: {
hello() {
console.log('hello from mixin!')
}
}
}
// define an app that uses this mixin
const app = Vue.createApp({
mixins: [myMixin]
})
app.mount('#mixins-basic') // => "hello from mixin!"
選項合並
- 數據對象在內部會進行遞歸合並,並在發生沖突時以組件數據優先
- 同名鈎子函數將合並為一個數組,因此都將被調用;混入對象的鈎子將在組件自身鈎子之前調用。
自定義選項合並策略
const app = Vue.createApp({})
app.config.optionMergeStrategies.customOption = (toVal, fromVal) => {
// return mergedVal
}
customOption是沖突值的名字
?
toVal為Mixin提供
fromVal為原參數
合並策略接收在父實例和子實例上定義的該選項的值,分別作為第一個和第二個參數
自定義指令
除了核心功能默認內置的指令 (v-model
和 v-show
),Vue支持自定義指令。
全局指令
const app = Vue.createApp({})
// 注冊一個全局自定義指令 `v-focus`
app.directive('focus', {
// 當被綁定的元素插入到 DOM 中時……
mounted(el) {
// Focus the element
el.focus()
}
})
局部指令
directives: {
focus: {
// 指令的定義
mounted(el) {
el.focus()
}
}
}
使用方法:<input v-focus />
鈎子函數
一個指令定義對象可以提供如下幾個鈎子函數 (均為可選):
app.directive('my-directive', {
// 指令是具有一組生命周期的鈎子:
// 在綁定元素的父組件掛載之前調用
beforeMount() {},
// 綁定元素的父組件掛載時調用
mounted() {},
// 在包含組件的 VNode 更新之前調用
beforeUpdate() {},
// 在包含組件的 VNode 及其子組件的 VNode 更新之后調用
updated() {},
// 在綁定元素的父組件卸載之前調用
beforeUnmount() {},
// 卸載綁定元素的父組件時調用
unmounted() {}
})
el
指令綁定到的元素。這可用於直接操作 DOM。
binding
包含以下 property 的對象。
instance
:使用指令的組件實例。value
:傳遞給指令的值。例如,在v-my-directive="1 + 1"
中,該值為2
。oldValue
:先前的值,僅在beforeUpdate
和updated
中可用。值是否已更改都可用。arg
:參數傳遞給指令 (如果有)。例如在v-my-directive:foo
中,arg 為"foo"
。modifiers
:包含修飾符 (如果有) 的對象。例如在v-my-directive.foo.bar
中,修飾符對象為{foo: true,bar: true}
。dir
:一個對象,在注冊指令時作為參數傳遞。例如,在以下指令中
app.directive('focus', {
mounted(el) {
el.focus()
}
})
dir
將會是以下對象 { mounted(el) { el.focus() } }
vnode
上面作為 el 參數收到的真實 DOM 元素的藍圖。
prevNode
上一個虛擬節點,僅在 beforeUpdate
和 updated
鈎子中可用。
動態指令參數
v-mydirective:[argument]="value"
可以使用 binding.arg
來獲取 argument
可以使用 binding.value
來獲取 value
以此根據組件實例數據進行更新
函數簡寫
去掉 mounted(){}
等
app.directive('pin', (el, binding) => {
el.style.position = 'fixed'
const s = binding.arg || 'top'
el.style[s] = binding.value + 'px'
})
組件使用
在 3.0 中,有了片段支持,組件可能有多個根節點。如果在具有多個根節點的組件上使用自定義指令,則會產生問題。
Teleport
Teleport允許深度嵌套下直接控制DOM中哪個父節點下呈現HTML,而不必求助於全局狀態或將其拆分為兩個組件。
<teleport to="body">
...
</teleport>
Vue components 中使用
app.component('parent-component', {
template: `
<h2>This is a parent component</h2>
<teleport to="#endofbody">
<child-component name="John" />
</teleport>
`
})
即使在不同的地方渲染 child-component
,它仍將是 parent-component
的子級,並將從中接收 name
prop。
在同一目標上使用多個 teleport
多個 <teleport>
組件可以將其內容掛載到同一個目標元素,順序將是一個簡單的追加
渲染函數
當需要可變地渲染 template 的時候,就可以使用 render 函數
app.component('anchored-heading', {
render() {
const { h } = Vue
return h(
'h' + this.level, // tag name
{}, // props/attributes
this.$slots.default() // array of children
)
},
props: {
level: {
type: Number,
required: true
}
}
})
h()
h()
函數是一個用於創建 vnode 的實用程序
// @returns {VNode}
h(
// {String | Object | Function | null} tag
// 一個 HTML 標簽名、一個組件、一個異步組件,或者 null。
// 使用 null 將會渲染一個注釋。
//
// 必需的。
'div',
// {Object} props
// 與 attribute、prop 和事件相對應的對象。
// 我們會在模板中使用。
//
// 可選的。
{},
// {String | Array | Object} children
// 子 VNodes, 使用 `h()` 構建,
// 或使用字符串獲取 "文本 Vnode" 或者
// 有 slot 的對象。
//
// 可選的。
[
'Some text comes first.',
h('h1', 'A headline'),
h(MyComponent, {
someProp: 'foobar'
})
]
)
render下的v-model
v-model
指令擴展為 modelValue
和 onUpdate:modelValue
在模板編譯過程中,我們必須自己提供這些prop:
props: ['modelValue'],
render() {
return Vue.h(SomeComponent, {
modelValue: this.modelValue,
'onUpdate:modelValue': value => this.$emit('update:modelValue', value)
})
}
render下的v-on
我們必須為事件處理程序提供一個正確的prop名稱,例如,要處理 click
事件,prop名稱應該是 onClick
。
render() {
return Vue.h('div', {
onClick: $event => console.log('clicked', $event.target)
})
}
事件修飾符
修飾符 | 處理函數中的等價操作 |
---|---|
.stop |
event.stopPropagation() |
.prevent |
event.preventDefault() |
.self |
if (event.target !== event.currentTarget) return |
按鍵: .enter , .13 |
if (event.keyCode !== 13) return (對於別的按鍵修飾符來說,可將 13 改為另一個按鍵碼 |
修飾鍵: .ctrl , .alt , .shift , .meta |
if (!event.ctrlKey) return (將 ctrlKey 分別修改為 altKey , shiftKey , 或 metaKey ) |
插槽
props: ['message'],
render() {
// `<div><slot :text="message"></slot></div>`
return Vue.h('div', {}, this.$slots.default({
text: this.message
}))
}
子組件插槽???
render() {
// `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
return Vue.h('div', [
Vue.h('child', {}, {
// pass `slots` as the children object
// in the form of { name: props => VNode | Array<VNode> }
default: (props) => Vue.h('span', props.text)
})
])
}
響應式
reactice
返回對象的響應式副本
ref
ref
會返回一個可變的響應式對象,只包含一個名為 value
的 property
展開
Ref 展開僅發生在被響應式 Object
嵌套的時候:可以省略value
當從 Array
或原生集合類型如 Map
訪問 ref 時,不會進行展開
解構
普通的解構會使property的響應性丟失
let { author, title } = book
轉換為一組ref后保留響應式關聯:
let { author, title } = toRefs(book)
使用 readonly
防止更改響應式對象
const original = reactive({ count: 0 })
const copy = readonly(original)
…
Compositional API
setup
組件選項
新的 setup
組件選項在創建組件之前執行,一旦 props
被解析,並充當合成 API 的入口點。
(由於在執行 setup
時尚未創建組件實例,因此在 setup
選項中沒有 this
。這意味着,除了 props
之外,你將無法訪問組件中聲明的任何屬性——data
computed
method
)
Props
setup
函數中的第一個參數是 props
,是響應式的,當傳入新的 prop 時,它將被更新。
context
傳遞給 setup
函數的第二個參數是 context
。context
是一個普通的 JavaScript 對象,它暴露三個組件的 property,不是響應式的,因此可以安全地對 context
使用 ES6 解構。
// MyBook.vue
export default {
setup(props, context) {
// Attribute (非響應式對象)
console.log(context.attrs)
// 插槽 (非響應式對象)
console.log(context.slots)
// 觸發事件 (方法)
console.log(context.emit)
}
}
setup中的生命周期鈎子
選項式 API | Hook inside setup |
---|---|
beforeCreate |
Not needed* |
created |
Not needed* |
beforeMount |
onBeforeMount |
mounted |
onMounted |
beforeUpdate |
onBeforeUpdate |
updated |
onUpdated |
beforeUnmount |
onBeforeUnmount |
unmounted |
onUnmounted |
errorCaptured |
onErrorCaptured |
renderTracked |
onRenderTracked |
renderTriggered |
onRenderTriggered |
*Not needed:在這些鈎子中編寫的任何代碼都應該直接在 setup
函數中編寫。
Compositional API的提供/注入
我們也可以在組合式 API 中使用 provide/inject。兩者都只能在當前活動實例的 setup() 期間調用。
provide
函數允許你通過兩個參數定義 property:
- property 的 name (
<String>
類型) - property 的 value
inject
函數有兩個參數:
- 要注入的 property 的名稱
- 一個默認的值 (可選)
…
https://vue3js.cn/docs/zh/guide/change-detection.html#聲明響應式-property???