超全的Vue3文檔【Vue2遷移Vue3】
關(guān)注 前端瓶子君,回復(fù)“交流”
加入我們一起學(xué)習(xí),天天進(jìn)步
看到掘金的這篇很全,就整理到自己的公眾號(hào)上來(lái),自己可以隨時(shí)復(fù)習(xí)。原文鏈接如下:
Vue2與Vue3的全局配置API變化區(qū)別
createApp
Vue2.x創(chuàng)建實(shí)例并且掛載DOM上
import Vue from "vue";
import App from './App.vue'
new Vue({
render: (h) => h(App)
}).$mount("#app");
Vue3新增api===>createApp創(chuàng)建實(shí)例
createApp 會(huì)產(chǎn)生一個(gè) app 實(shí)例,該實(shí)例擁有全局的可配置上下文
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
createApp 做了什么

ensureRenderer方法追溯過(guò)去底添加patchclass+patchStyle等跟操作DOM相關(guān)的方法
ensureRenderer(創(chuàng)建虛擬DOM)一直追溯到createRenderer以及baseCreateRenderer,baseCreateRenderer方法涉及了虛擬DOM的創(chuàng)建更新DIFF算法 之后就是檢查時(shí)候又mount是否掛載在DOM上
app對(duì)象上的方法:config、use、mixin、component、directive、mount、unmount、provide/inject
component
Vue2.x【注冊(cè)或獲取全局組件。注冊(cè)還會(huì)自動(dòng)使用給定的 id 設(shè)置組件的名稱(chēng)】
// 注冊(cè)組件,傳入一個(gè)選項(xiàng)對(duì)象 (自動(dòng)調(diào)用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 獲取注冊(cè)的組件 (始終返回構(gòu)造器)
var MyComponent = Vue.component('my-component')
Vue3【注冊(cè)或獲取全局組件. 注冊(cè)還會(huì)自動(dòng)使用給定的 name組件 設(shè)置組件的名稱(chēng)】
基本vue2寫(xiě)法一致
import { createApp } from 'vue'
const app = createApp({})
// 注冊(cè)組件,傳入一個(gè)選項(xiàng)對(duì)象
app.component('my-component', {
/* ... */
})
// 獲取注冊(cè)的組件 (始終返回構(gòu)造器)
const MyComponent = app.component('my-component', {})
config【app=createApp(App)】
devtools
配置是否允許 vue-devtools 檢查代碼。開(kāi)發(fā)版本默認(rèn)為 true,生產(chǎn)版本默認(rèn)為 false。生產(chǎn)版本設(shè)為 true 可以啟用檢查。
- Vue.config.devtools = true
+ app.config.devtools = true
errorHandler
- Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` 是 Vue 特定的錯(cuò)誤信息,比如錯(cuò)誤所在的生命周期鉤子
// 只在 2.2.0+ 可用
}
+ app.config.errorHandler = (err, vm, info) => {
// handle error
// `info` 是 Vue 特定的錯(cuò)誤信息,比如錯(cuò)誤所在的生命周期鉤子
// 這里能發(fā)現(xiàn)錯(cuò)誤
}
指定組件的渲染和觀察期間未捕獲錯(cuò)誤的處理函數(shù)。這個(gè)處理函數(shù)被調(diào)用時(shí),可獲取錯(cuò)誤信息和 Vue 實(shí)例。
錯(cuò)誤追蹤服務(wù) Sentry 和 Bugsnag 都通過(guò)此選項(xiàng)提供了官方支持。
warnHandler
- Vue.config.warnHandler = function (msg, vm, trace) {
// `trace` 是組件的繼承關(guān)系追蹤
}
+ app.config.warnHandler = function(msg, vm, trace) {
// `trace` 是組件的繼承關(guān)系追蹤
}
為 Vue 的運(yùn)行時(shí)警告賦予一個(gè)自定義處理函數(shù)。注意這只會(huì)在開(kāi)發(fā)者環(huán)境下生效,在生產(chǎn)環(huán)境下它會(huì)被忽略。
globalProperties 【新增屬性】
app.config.globalProperties.foo = 'bar'
app.component('child-component', {
mounted() {
console.log(this.foo) // 'bar'
}
})
添加可在程序內(nèi)的任何組件實(shí)例中訪問(wèn)的全局屬性。當(dāng)存在鍵沖突時(shí),組件屬性將優(yōu)先替代掉Vue2.x的 Vue.prototype屬性放到原型上的寫(xiě)法
isCustomElement 【新增屬性】
替代掉Vue2.x的ignoredElements
- Vue.config.ignoredElements = [
// 用一個(gè) `RegExp` 忽略所有“ion-”開(kāi)頭的元素
// 僅在 2.5+ 支持
/^ion-/
]
// 一些組件以'ion-'開(kāi)頭將會(huì)被解析為自定義組件
+ app.config.isCustomElement = tag => tag.startsWith('ion-')
指定一個(gè)方法來(lái)識(shí)別在Vue之外定義的自定義組件(例如,使用Web Component API)。如果組件符合這個(gè)條件,它就不需要本地或全局注冊(cè),Vue也不會(huì)拋出關(guān)于Unknown custom element的警告
注意,這個(gè)函數(shù)中不需要匹配所有原生HTML和SVG標(biāo)記—Vue解析器會(huì)自動(dòng)執(zhí)行此檢查
optionMergeStrategies
const app = Vue.createApp({
mounted() {
console.log(this.$options.hello)
}
})
app.config.optionMergeStrategies.hello = (parent, child, vm) => {
return `Hello, ${child}`
}
app.mixin({
hello: 'Vue'
})
// 'Hello, Vue
定義自定義選項(xiàng)的合并策略。合并策略接收在父實(shí)例options和子實(shí)例options和??子實(shí)例??options,分別作為第一個(gè)和第二個(gè)參數(shù)。上下文Vue實(shí)例作為第三個(gè)參數(shù)傳遞
【自定義選項(xiàng)合并策略】mixin
const app = Vue.createApp({
custom: 'hello!'
})
app.config.optionMergeStrategies.custom = (toVal, fromVal) => {
console.log(fromVal, toVal)
// => "goodbye!", undefined
// => "hello!", "goodbye!"
return fromVal || toVal
}
app.mixin({
custom: 'goodbye!',
created() {
console.log(this.$options.custom) // => "hello!"
}
})
optionMergeStrategies先獲取到子實(shí)例的$options的mixin而沒(méi)有父實(shí)例【custom第一次改變從undefined到goodbye--->打印"goodbye!", undefined】
父實(shí)例的options替換掉子實(shí)例的options替換掉子實(shí)例的options【custom第二次從goodbye到hello!--->打印了"hello", "goodbye!"】
最后在打印app.config.optionMergeStrategies.custom返回的父實(shí)例的$options
無(wú)論如何this.options.custom最后會(huì)返回合并策略的return的值【使用場(chǎng)景利用父子組件的options.custom最后會(huì)返回合并策略的return的值【使用場(chǎng)景利用父子組件的options.custom最后會(huì)返回合并策略的return的值【使用場(chǎng)景利用父子組件的options,然后返回計(jì)算等操作得到所需要的值】optionMergeStrategies合并$options變化
performance
- Vue.config.performance=true;
+ app.config.performance=true;
設(shè)置為 true 以在瀏覽器開(kāi)發(fā)工具的性能/時(shí)間線面板中啟用對(duì)組件初始化、編譯、渲染和打補(bǔ)丁的性能追蹤。只適用于開(kāi)發(fā)模式和支持 performance.mark API 的瀏覽器上。
directive
注冊(cè)或獲取全局指令。
import { createApp } from 'vue'
const app = createApp({})
// 注冊(cè)
app.directive('my-directive', {
// 指令的生命周期
// 在綁定元素的父組件被掛載之前調(diào)用
beforeMount(el, binding, vnode) {},
// 在掛載綁定元素的父組件時(shí)調(diào)用
mounted(el, binding, vnode) {},
// 在更新包含組件的VNode之前調(diào)用
beforeUpdate(el, binding, vnode, prevNode) {},
// 組件的VNode及其子組件的VNode更新之后調(diào)用
updated(el, binding, vnode, prevNode) {},
// 在卸載綁定元素的父組件之前調(diào)用
beforeUnmount(el, binding, vnode) {},
// 在卸載綁定元素的父組件時(shí)調(diào)用
unmounted(el, binding, vnode) {}
})
// 注冊(cè) (指令函數(shù))
app.directive('my-directive', (el, binding, vnode, prevNode) => {
// 這里將會(huì)被 `mounted` 和 `updated` 調(diào)用
})
// getter,返回已注冊(cè)的指令
const myDirective = app.directive('my-directive')

el
指令綁定到的元素。這可以用來(lái)直接操作DOM。
binding【包含下列屬性的對(duì)象】 instance:使用指令的組件的實(shí)例 value:指令的綁定值,例如:v-my-directive="1 + 1"中,綁定值為 2 oldValue:指令綁定的前一個(gè)值,僅在 beforeUpdate 和 updated 鉤子中可用。無(wú)論值是否改變都可用 arg:傳給指令的參數(shù),可選。例如 v-my-directive:foo 中,參數(shù)為 "foo" modifiers:一個(gè)包含修飾符的對(duì)象。例如:v-my-directive.foo.bar 中,修飾符對(duì)象為 { foo: true, bar: true } dir:一個(gè)對(duì)象,在注冊(cè)指令時(shí)作為參數(shù)傳遞; 舉個(gè)例子,看下面指令
app.directive('focus', {
mounted(el) {
el.focus()
}
})
dir就是下面的對(duì)象
{
mounted(el) {
el.focus()
}
}
vnode
編譯生成的虛擬節(jié)點(diǎn)
prevNode
前一個(gè)虛擬節(jié)點(diǎn),僅在beforeUpdate和updated鉤子中可用
tips:除了 el 之外,其它參數(shù)都應(yīng)該是只讀的,切勿進(jìn)行修改。如果需要在鉤子之間共享數(shù)據(jù),建議通過(guò)元素的 dataset 來(lái)進(jìn)行
mixin【基本Vue2.x一致】
全局注冊(cè)一個(gè)混入,影響注冊(cè)之后所有創(chuàng)建的每個(gè) Vue 實(shí)例。插件作者可以使用混入,向組件注入自定義的行為。不推薦在應(yīng)用代碼中使用。
mount【類(lèi)似Vue2.x】
在所提供的DOM元素上掛載應(yīng)用程序?qū)嵗母M件
import { createApp } from 'vue'
const app = createApp({})
// 做一些準(zhǔn)備
app.mount('#my-app')
provide/inject【Vue2.x一致】
該選項(xiàng)與inject一起使用,允許一個(gè)祖先組件作為其所有后代的依賴(lài)注入器,無(wú)論組件層次結(jié)構(gòu)有多深,只要它們位于同一父鏈中就可以。
provide 選項(xiàng)應(yīng)該是一個(gè)對(duì)象或返回一個(gè)對(duì)象的函數(shù)。該對(duì)象包含可注入其子孫的 property。在該對(duì)象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的環(huán)境下可工作。
如果在組件中兩者都只能在當(dāng)前活動(dòng)組件實(shí)例的 setup() 中調(diào)用,詳細(xì)請(qǐng)看依賴(lài)注入部分
import { createApp } from 'vue'
const app = createApp({
provide: {
user: 'John Doe'
}
})
app.component('user-card', {
inject: ['user'],
template: `
<div>
{{ user }}
</div>
`
})
unmount【新增屬性】
在所提供的DOM元素上卸載應(yīng)用程序?qū)嵗母M件
import { createApp } from 'vue'
const app = createApp({})
// 做一些必要的準(zhǔn)備
app.mount('#my-app')
// 應(yīng)用程序?qū)⒃趻燧d后5秒被卸載
setTimeout(() => app.unmount('#my-app'), 5000)
use【Vue2.x一致】
安裝 Vue.js 插件。如果插件是一個(gè)對(duì)象,必須提供 install 方法。如果插件是一個(gè)函數(shù),它會(huì)被作為 install 方法。install 方法調(diào)用時(shí),會(huì)將 Vue 作為參數(shù)傳入。
當(dāng) install 方法被同一個(gè)插件多次調(diào)用,插件將只會(huì)被安裝一次。
setup
setup 函數(shù)是一個(gè)新的組件選項(xiàng)。作為在組件內(nèi)使用 Composition API 的入口點(diǎn), 注意 setup 返回的 ref 在模板中會(huì)自動(dòng)解開(kāi),不需要寫(xiě) .value【setup 內(nèi)部需要.value】
調(diào)用時(shí)機(jī)
創(chuàng)建組件實(shí)例,然后初始化 props ,緊接著就調(diào)用setup 函數(shù)。從生命周期鉤子的視角來(lái)看,它會(huì)在 beforeCreate 鉤子之前被調(diào)用
如果 setup 返回一個(gè)對(duì)象,則對(duì)象的屬性將會(huì)被合并到組件模板的渲染上下文
參數(shù)
props 作為其第一個(gè)參數(shù)
注意 props 對(duì)象是響應(yīng)式的,watchEffect 或 watch 會(huì)觀察和響應(yīng) props 的更新不要解構(gòu) props 對(duì)象,那樣會(huì)使其失去響應(yīng)性。
export default {
props: {
name: String,
},
setup(props) {
console.log(props.name)
watchEffect(() => {
console.log(`name is: ` + props.name)
})
},
}
第二個(gè)參數(shù)提供了一個(gè)上下文對(duì)象【從原來(lái) 2.x 中 this 選擇性地暴露了一些 property(attrs/emit/slots)】
attrs 和 slots 都是內(nèi)部組件實(shí)例上對(duì)應(yīng)項(xiàng)的代理,可以確保在更新后仍然是最新值。所以可以解構(gòu),無(wú)需擔(dān)心后面訪問(wèn)到過(guò)期的值
為什么props作為第一個(gè)參數(shù)?
組件使用 props 的場(chǎng)景更多,有時(shí)候甚至只使用 props
將 props 獨(dú)立出來(lái)作為第一個(gè)參數(shù),可以讓 TypeScript 對(duì) props 單獨(dú)做類(lèi)型推導(dǎo),不會(huì)和上下文中的其他屬性相混淆。這也使得 setup 、 render 和其他使用了 TSX 的函數(shù)式組件的簽名保持一致
this 在 setup() 中不可用。由于 setup() 在解析 2.x 選項(xiàng)前被調(diào)用,setup() 中的 this 將與 2.x 選項(xiàng)中的 this 完全不同。同時(shí)在 setup() 和 2.x 選項(xiàng)中使用 this 時(shí)將造成混亂
setup(props, { attrs }) {
// 一個(gè)可能之后回調(diào)用的簽名
function onClick() {
console.log(attrs.foo) // 一定是最新的引用,沒(méi)有丟失響應(yīng)性
}
}


響應(yīng)式系統(tǒng) API
reactive
接收一個(gè)普通對(duì)象然后返回該普通對(duì)象的響應(yīng)式代理【等同于 2.x 的 Vue.observable()】
Proxy對(duì)象是目標(biāo)對(duì)象的一個(gè)代理器,任何對(duì)目標(biāo)對(duì)象的操作(實(shí)例化,添加/刪除/修改屬性等等),都必須通過(guò)該代理器。因此我們可以把來(lái)自外界的所有操作進(jìn)行攔截和過(guò)濾或者修改等操作
響應(yīng)式轉(zhuǎn)換是“深層的”:會(huì)影響對(duì)象內(nèi)部所有嵌套的屬性?;?ES2015 的 Proxy 實(shí)現(xiàn),返回的代理對(duì)象不等于原始對(duì)象。建議僅使用代理對(duì)象而避免依賴(lài)原始對(duì)象
reactive 類(lèi)的 api 主要提供了將復(fù)雜類(lèi)型的數(shù)據(jù)處理成響應(yīng)式數(shù)據(jù)的能力,其實(shí)這個(gè)復(fù)雜類(lèi)型是要在object array map set weakmap weakset 這五種之中【如下源碼,他會(huì)判斷是否是五類(lèi)以及是否被凍結(jié)】

因?yàn)槭墙M合函數(shù)【對(duì)象】,所以必須始終保持對(duì)這個(gè)所返回對(duì)象的引用以保持響應(yīng)性【不能解構(gòu)該對(duì)象或者展開(kāi)】例如 const { x, y } = useMousePosition()或者return { ...useMousePosition() }
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0,
})
return pos
}
toRefs API 用來(lái)提供解決此約束的辦法——它將響應(yīng)式對(duì)象的每個(gè) property 都轉(zhuǎn)成了相應(yīng)的 ref【把對(duì)象轉(zhuǎn)成了ref】。
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0,
})
return toRefs(pos)
}
// x & y 現(xiàn)在是 ref 形式了!
const { x, y } = useMousePosition()
ref
接受一個(gè)參數(shù)值并返回一個(gè)響應(yīng)式且可改變的 ref 對(duì)象。ref 對(duì)象擁有一個(gè)指向內(nèi)部值的單一屬性 .value
const count = ref(0)
console.log(count.value) // 0
如果傳入 ref 的是一個(gè)對(duì)象,將調(diào)用 reactive 方法進(jìn)行深層響應(yīng)轉(zhuǎn)換

陷阱
setup 中return返回會(huì)自動(dòng)解套【在模板中不需要.value】

ref 作為 reactive 對(duì)象的 property 被訪問(wèn)或修改時(shí),也將自動(dòng)解套 .value
const count = ref(0)
/*當(dāng)做reactive的對(duì)象屬性----解套*/
const state = reactive({
count,
})
/* 不需要.value*/
console.log(state.count) // 0
/*修改reactive的值*/
state.count = 1
/*修改了ref的值*/
console.log(count.value) // 1
注意如果將一個(gè)新的 ref 分配給現(xiàn)有的 ref, 將替換舊的 ref
/*創(chuàng)建一個(gè)新的ref*/
const otherCount = ref(2)
/*賦值給reactive的舊的ref,舊的會(huì)被替換掉*/
state.count = otherCount
/*修改reactive會(huì)修改otherCount*/
console.log(state.count) // 2
/*修改reactive會(huì)count沒(méi)有被修改 */
console.log(count.value) // 1
嵌套在 reactive Object 中時(shí),ref 才會(huì)解套。從 Array 或者 Map 等原生集合類(lèi)中訪問(wèn) ref 時(shí),不會(huì)自動(dòng)解套【自由數(shù)據(jù)類(lèi)型是Object才會(huì)解套,array map set weakmap weakset集合類(lèi) 訪問(wèn) ref 時(shí),不會(huì)自動(dòng)解套】
const arr = reactive([ref(0)])
// 這里需要 .value
console.log(arr[0].value)
const map = reactive(new Map([['foo', ref(0)]]))
// 這里需要 .value
console.log(map.get('foo').value)
心智負(fù)擔(dān)上 ref vs reactive
在普通 JavaScript 中區(qū)別聲明基礎(chǔ)類(lèi)型變量與對(duì)象變量時(shí)一樣區(qū)別使用 ref 和 reactive 所有的地方都用 reactive,然后記得在組合函數(shù)返回響應(yīng)式對(duì)象時(shí)使用 toRefs。這降低了一些關(guān)于 ref 的心智負(fù)擔(dān)
readonly
傳入一個(gè)對(duì)象(響應(yīng)式或普通)或 ref,返回一個(gè)原始對(duì)象的只讀代理。一個(gè)只讀的代理是“深層的”,對(duì)象內(nèi)部任何嵌套的屬性也都是只讀的【返回一個(gè)永遠(yuǎn)不會(huì)變的只讀代理】【場(chǎng)景可以參數(shù)比對(duì)等】
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 依賴(lài)追蹤
console.log(copy.count)
})
// original 上的修改會(huì)觸發(fā) copy 上的偵聽(tīng)
original.count++
// 無(wú)法修改 copy 并會(huì)被警告
copy.count++ // warning!
reactive響應(yīng)式系統(tǒng)工具集
isProxy
檢查一個(gè)對(duì)象是否是由 reactive 或者 readonly 方法創(chuàng)建的代理
isReactive
檢查一個(gè)對(duì)象是否是由 reactive 創(chuàng)建的響應(yīng)式代理
import { reactive, isReactive } from 'vue'
const state = reactive({
name: 'John'
})
console.log(isReactive(state)) // -> true
如果這個(gè)代理是由 readonly 創(chuàng)建的,但是又被 reactive 創(chuàng)建的另一個(gè)代理包裹了一層,那么同樣也會(huì)返回 true
import { reactive, isReactive, readonly } from 'vue'
const state = reactive({
name: 'John'
})
// 用readonly創(chuàng)建一個(gè)只讀響應(yīng)式對(duì)象plain
const plain = readonly({
name: 'Mary'
})
//readonly創(chuàng)建的,所以isReactive為false
console.log(isReactive(plain)) // -> false
// reactive創(chuàng)建的響應(yīng)式代理對(duì)象包裹一層readonly,isReactive也是true,isReadonly也是true
const stateCopy = readonly(state)
console.log(isReactive(stateCopy)) // -> true
isReadonly
檢查一個(gè)對(duì)象是否是由 readonly 創(chuàng)建的只讀代理
reactive高級(jí)響應(yīng)式系統(tǒng)API
toRaw
返回由 reactive 或 readonly 方法轉(zhuǎn)換成響應(yīng)式代理的普通對(duì)象。這是一個(gè)還原方法,可用于臨時(shí)讀取,訪問(wèn)不會(huì)被代理/跟蹤,寫(xiě)入時(shí)也不會(huì)觸發(fā)更改。不建議一直持有原始對(duì)象的引用【不建議賦值給任何變量】。請(qǐng)謹(jǐn)慎使用
被toRaw之后的對(duì)象是沒(méi)有被代理/跟蹤的的普通對(duì)象
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
console.log(toRaw(reactiveFoo) !== reactiveFoo) // true
markRaw
顯式標(biāo)記一個(gè)對(duì)象為“永遠(yuǎn)不會(huì)轉(zhuǎn)為響應(yīng)式代理”,函數(shù)返回這個(gè)對(duì)象本身。被 markRaw 標(biāo)記了,即使在響應(yīng)式對(duì)象中作屬性,也依然不是響應(yīng)式的
const foo = markRaw({
name: 'Mary'
})
console.log(isReactive(reactive(foo))) // false
markRaw 注意點(diǎn)
markRaw和 shallowXXX 一族的 API允許選擇性的覆蓋reactive或者readonly 默認(rèn)創(chuàng)建的 "深層的" 特性【響應(yīng)式】/或者使用無(wú)代理的普通對(duì)象
設(shè)計(jì)這種「淺層讀取」有很多原因
一些值的實(shí)際上的用法非常簡(jiǎn)單,并沒(méi)有必要轉(zhuǎn)為響應(yīng)式【例如三方庫(kù)的實(shí)例/省市區(qū)json/Vue組件對(duì)象】 當(dāng)渲染一個(gè)元素?cái)?shù)量龐大,但是數(shù)據(jù)是不可變的,跳過(guò) Proxy 的轉(zhuǎn)換可以帶來(lái)性能提升 這些 API 被認(rèn)為是高級(jí)的,是因?yàn)檫@種特性?xún)H停留在根級(jí)別,所以如果你將一個(gè)嵌套的,沒(méi)有 markRaw 的對(duì)象設(shè)置為 reactive 對(duì)象的屬性,在重新訪問(wèn)時(shí),你又會(huì)得到一個(gè) Proxy 的版本,在使用中最終會(huì)導(dǎo)致標(biāo)識(shí)混淆的嚴(yán)重問(wèn)題:執(zhí)行某個(gè)操作同時(shí)依賴(lài)于某個(gè)對(duì)象的原始版本和代理版本(標(biāo)識(shí)混淆在一般使用當(dāng)中應(yīng)該是非常罕見(jiàn)的,但是要想完全避免這樣的問(wèn)題,必須要對(duì)整個(gè)響應(yīng)式系統(tǒng)的工作原理有一個(gè)相當(dāng)清晰的認(rèn)知)。
const foo = markRaw({
nested: {},
})
const bar = reactive({
// 盡管 `foo` 己經(jīng)被標(biāo)記為 raw 了, 但 foo.nested 并沒(méi)有
nested: foo.nested,
})
console.log(foo.nested === bar.nested) // false
foo.nested沒(méi)有被標(biāo)記為(永遠(yuǎn)不會(huì)轉(zhuǎn)為響應(yīng)式代理),導(dǎo)致最后的值一個(gè)reactive

shallowReactive
只為某個(gè)對(duì)象的私有(第一層)屬性創(chuàng)建淺層的響應(yīng)式代理,不會(huì)對(duì)“屬性的屬性”做深層次、遞歸地響應(yīng)式代理,而只是保留原樣【第一層是響應(yīng)式代理,深層次只保留原樣(不具備響應(yīng)式代理)】
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
})
// 變更 state 的自有屬性是響應(yīng)式的【第一層次響應(yīng)式】
state.foo++
// ...但不會(huì)深層代理【深層次不是響應(yīng)式】(渲染性能)
isReactive(state.nested) // false
state.nested.bar++ // 非響應(yīng)式
shallowReadonly
類(lèi)似于shallowReactive,區(qū)別是:
第一層將會(huì)是響應(yīng)式代理【第一層修改屬性會(huì)失敗】,屬性為響應(yīng)式 深層次的對(duì)象屬性可以修改,屬性不是響應(yīng)式
const state = shallowReadonly({
foo: 1,
nested: {
bar: 2,
},
})
// 變更 state 的自有屬性會(huì)失敗
state.foo++
// ...但是嵌套的對(duì)象是可以變更的
isReadonly(state.nested) // false
state.nested.bar++ // 嵌套屬性依然可修改
ref 響應(yīng)式系統(tǒng)工具集
customRef
用于自定義一個(gè) ref,可以顯式地控制依賴(lài)追蹤和觸發(fā)響應(yīng),接受一個(gè)工廠函數(shù),兩個(gè)參數(shù)分別是用于追蹤的 track 與用于觸發(fā)響應(yīng)的 trigger,并返回一個(gè)一個(gè)帶有 get 和 set 屬性的對(duì)象【實(shí)際上就是手動(dòng) track追蹤 和 trigger觸發(fā)響應(yīng)】
以下代碼可以使得v-model防抖
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
/*初始化手動(dòng)追蹤依賴(lài)講究什么時(shí)候去觸發(fā)依賴(lài)收集*/
track()
return value
},
set(newValue) {
/*修改數(shù)據(jù)的時(shí)候會(huì)把上一次的定時(shí)器清除【防抖】*/
clearTimeout(timeout)
timeout = setTimeout(() => {
/*把新設(shè)置的數(shù)據(jù)給到ref數(shù)據(jù)源*/
value = newValue
/*再有依賴(lài)追蹤的前提下觸發(fā)響應(yīng)式*/
trigger()
}, delay)
},
}
})
}
setup() {
return {
/*暴露返回的數(shù)據(jù)加防抖*/
text: useDebouncedRef('hello'),
}
}
shallowRef
創(chuàng)建一個(gè) ref ,將會(huì)追蹤它的 .value 更改操作,但是并不會(huì)對(duì)變更后的 .value 做響應(yīng)式代理轉(zhuǎn)換(即變更不會(huì)調(diào)用 reactive)
前面我們說(shuō)過(guò)如果傳入 ref 的是一個(gè)對(duì)象,將調(diào)用 reactive 方法進(jìn)行深層響應(yīng)轉(zhuǎn)換,通過(guò)shallowRef創(chuàng)建的ref,將不會(huì)調(diào)用reactive【對(duì)象不會(huì)是響應(yīng)式的】
const refOne = shallowRef({});
refOne.value = { id: 1 };
refOne.id == 20;
console.log(isReactive(refOne.value),refOne.value);//false { id: 1 }
triggerRef 【與shallowRef配合】
手動(dòng)執(zhí)行與shallowRef相關(guān)的任何效果
const shallow = shallowRef({
greet: 'Hello, world'
})
// 第一次運(yùn)行打印 "Hello, world"
watchEffect(() => {
console.log(shallow.value.greet)
})
// 這不會(huì)觸發(fā)效果,因?yàn)閞ef是shallow
shallow.value.greet = 'Hello, universe'
// 打印 "Hello, universe"
triggerRef(shallow)
Computed and watch【監(jiān)控變化】
computed
傳入一個(gè) getter 函數(shù),返回一個(gè)默認(rèn)不可手動(dòng)修改的 ref 對(duì)象【默認(rèn)傳入的是get函數(shù)的對(duì)象】 傳入一個(gè)擁有 get 和 set 函數(shù)的對(duì)象,創(chuàng)建一個(gè)可手動(dòng)修改的計(jì)算狀態(tài)

const count = ref(1)
/*不支持修改【只讀的】 */
const plusOne = computed(() => count.value + 1)
plusOne.value++ // 錯(cuò)誤!
/*【可更改的】 */
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
},
})
plusOne.value = 1
console.log(count.value) // 0
watchEffect
立即執(zhí)行傳入的一個(gè)函數(shù),并響應(yīng)式追蹤其依賴(lài),并在其依賴(lài)變更時(shí)重新運(yùn)行該函數(shù)
computed與watchEffect區(qū)別:
computed 計(jì)算屬性可通過(guò)setup return,再模板中使用,watchEffect不能; computed可以使用多個(gè),并且對(duì)多個(gè)屬性進(jìn)行不同的響應(yīng)計(jì)算,watchEffect會(huì)存在副作用
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 打印出 0
setTimeout(() => {
count.value++
// -> 打印出 1
}, 100)
當(dāng)在組件的setup()函數(shù)或生命周期鉤子期間調(diào)用watchEffect時(shí),監(jiān)視程序會(huì)鏈接到組件的生命周期,并在卸載組件時(shí)自動(dòng)停止,一般情況下watchEffect返回可以stop 操作,停止監(jiān)聽(tīng)程序
const stop = watchEffect(() => {
/* ... */
})
// 停止監(jiān)聽(tīng)程序
stop()
副作用(函數(shù)式編程)
一個(gè)帶有副作用的函數(shù)不僅只是簡(jiǎn)單的返回一個(gè)值,還干了一些其他的事情,比如:
修改一個(gè)變量 直接修改數(shù)據(jù)結(jié)構(gòu) 設(shè)置一個(gè)對(duì)象的成員 拋出一個(gè)異?;蛞砸粋€(gè)錯(cuò)誤終止 打印到終端或讀取用戶(hù)的輸入 讀取或?qū)懭胍粋€(gè)文件 在屏幕上繪畫(huà)
如果一個(gè)函數(shù)內(nèi)外有依賴(lài)于外部變量或者環(huán)境時(shí),常常我們稱(chēng)之為其有副作用,如果我們僅通過(guò)函數(shù)簽名不打開(kāi)內(nèi)部代碼檢查并不能知道該函數(shù)在干什么,作為一個(gè)獨(dú)立函數(shù)我們期望有明確的輸入和輸出,副作用是bug的發(fā)源地,作為程序員開(kāi)發(fā)者應(yīng)盡量少的開(kāi)發(fā)有副作用的函數(shù)或方法,副作用也使得方法通用性下降不適合擴(kuò)展和可重用性
清除副作用
在一些時(shí)候監(jiān)聽(tīng)函數(shù)將執(zhí)行異步副作用【一個(gè)響應(yīng)式依賴(lài)被修改了,會(huì)做其他事情】,這些響應(yīng)需要在其失效時(shí)清除(例如在效果完成前狀態(tài)改變)。effect函數(shù)接收一個(gè)onInvalidate 函數(shù)作入?yún)ⅲ?用來(lái)注冊(cè)清理失效時(shí)的回調(diào)。這個(gè) invalidation函數(shù) 在什么時(shí)候會(huì)被調(diào)用:
監(jiān)聽(tīng)函數(shù)重新被執(zhí)行的時(shí)候【響應(yīng)式依賴(lài)的數(shù)據(jù)被修改】 監(jiān)聽(tīng)停止的時(shí)候(如果watchEffect在setup()或者生命周期函數(shù)中被使用的時(shí)候組件會(huì)被卸載)【停止觀察】
watchEffect(onInvalidate => {
/*這是個(gè)異步操作*/
const token = performAsyncOperation(id.value)//id依賴(lài)
onInvalidate(() => {
// id被修改了或者監(jiān)聽(tīng)停止了會(huì)觸發(fā)token.cancel()事件【這塊區(qū)域的代碼】.
// 這里是異步事件的話,前面的peding的異步操作無(wú)效【這里的異步事件只執(zhí)行一次】
token.cancel()/*異步操作*/
console.log('onInvalidate')
})
})
從上面看:我們之所以是通過(guò)傳入一個(gè)函數(shù)去注冊(cè)失效回調(diào),而不是從回調(diào)返回它(如 React `useEffect` 中的方式),是因?yàn)榉祷刂祵?duì)于異步錯(cuò)誤處理很重要
````js
const data = ref(null)
watchEffect(async onInvalidate => {
onInvalidate(() => {...}) // 我們?cè)赑romise的resolves之前注冊(cè)清理函數(shù)(cleanup function)
data.value = await fetchData(props.id)
})
我們知道異步函數(shù)都會(huì)隱式地返回一個(gè) Promise,但是清理副作用的函數(shù)必須要在 Promise 被 resolve 之前被注冊(cè)。另外,Vue 依賴(lài)這個(gè)返回的 Promise 來(lái)自動(dòng)處理 Promise 鏈上的潛在錯(cuò)誤
副作用刷新時(shí)機(jī)
Vue 的響應(yīng)式系統(tǒng)會(huì)緩存副作用函數(shù),并異步地刷新它們,這樣可以避免同一個(gè) tick 中多個(gè)狀態(tài)改變導(dǎo)致的不必要的重復(fù)調(diào)用。在核心的具體實(shí)現(xiàn)中, 組件的更新函數(shù)也是一個(gè)被偵聽(tīng)的副作用。當(dāng)一個(gè)用戶(hù)定義的副作用函數(shù)進(jìn)入隊(duì)列時(shí), 會(huì)在所有的組件更新后執(zhí)行
<template>
<div>{{ count }}</div>
</template>
<script>
export default {
setup() {
const count = ref(0)
watchEffect(() => {
console.log(count.value)
})
return {
count,
}
},
}
</script>
count 會(huì)在初始運(yùn)行時(shí)同步打印出來(lái) 更改 count 時(shí),將在組件更新后執(zhí)行副作用
初始化運(yùn)行是在組件 mounted 之前執(zhí)行的【你希望在編寫(xiě)副作用函數(shù)時(shí)訪問(wèn) DOM(或模板 ref),請(qǐng)?jiān)?onMounted 鉤子中進(jìn)行】
onMounted(() => {
watchEffect(() => {
// 在這里可以訪問(wèn)到 DOM 或者 template refs
})
})
如果副作用需要同步或在組件更新之前重新運(yùn)行,我們可以傳遞一個(gè)擁有 flush 屬性的對(duì)象作為選項(xiàng)(默認(rèn)為 'post')
// 同步運(yùn)行
watchEffect(
() => {
/* ... */
},
{
flush: 'sync',
}
)
// 組件更新前執(zhí)行
watchEffect(
() => {
/* ... */
},
{
flush: 'pre',
}
)
偵聽(tīng)器調(diào)試【響應(yīng)式調(diào)試用的】
onTrack 和 onTrigger 選項(xiàng)可用于調(diào)試一個(gè)偵聽(tīng)器的行為。
當(dāng)一個(gè) reactive 對(duì)象屬性或一個(gè) ref 作為依賴(lài)被追蹤時(shí),將調(diào)用 onTrack【調(diào)用次數(shù)為被追蹤的數(shù)量】 依賴(lài)項(xiàng)變更會(huì)導(dǎo)致重新追蹤依賴(lài),從而onTrack被調(diào)用【調(diào)用次數(shù)為被追蹤的數(shù)量】 的數(shù)量】 依賴(lài)項(xiàng)變更會(huì)導(dǎo)致重新追蹤依賴(lài),從而onTrack被調(diào)用【調(diào)用次數(shù)為被追蹤的數(shù)量】 依賴(lài)項(xiàng)變更導(dǎo)致副作用被觸發(fā)時(shí),將調(diào)用 onTrigger
這兩個(gè)回調(diào)都將接收到一個(gè)包含有關(guān)所依賴(lài)項(xiàng)信息的調(diào)試器事件。建議在以下回調(diào)中編寫(xiě) debugger 語(yǔ)句來(lái)檢查依賴(lài)關(guān)系:【onTrack 和 onTrigger 僅在開(kāi)發(fā)模式下生效】
watchEffect(
() => {
/* 副作用的內(nèi)容 */
},
{
onTrigger(e) {
/*副作用依賴(lài)修改*/
debugger
},
onTrack(e) {
/*副作用依賴(lài)修改*/
debugger
},
}
)

watch
watch API 完全等效于 2.x watch 中相應(yīng)的選項(xiàng)。watch 需要偵聽(tīng)特定的數(shù)據(jù)源,并在回調(diào)函數(shù)中執(zhí)行副作用【默認(rèn)情況是懶執(zhí)行的,也就是說(shuō)僅在偵聽(tīng)的源變更時(shí)才執(zhí)行回調(diào)】
watch允許我們:
懶執(zhí)行副作用 更明確哪些狀態(tài)的改變會(huì)觸發(fā)偵聽(tīng)器重新運(yùn)行副作用 訪問(wèn)偵聽(tīng)狀態(tài)變化前后的值
偵聽(tīng)單個(gè)數(shù)據(jù)源
偵聽(tīng)器的數(shù)據(jù)源可以是一個(gè)擁有返回值的 getter 函數(shù),也可以是 ref:
// 偵聽(tīng)一個(gè) getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接偵聽(tīng)一個(gè) ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
偵聽(tīng)多個(gè)數(shù)據(jù)源
watcher 也可以使用數(shù)組來(lái)同時(shí)偵聽(tīng)多個(gè)源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
與 watchEffect 共享的行為
watch 和 watchEffect 在停止偵聽(tīng), 清除副作用 (相應(yīng)地 onInvalidate 會(huì)作為回調(diào)的第三個(gè)參數(shù)傳入),副作用刷新時(shí)機(jī) 和 偵聽(tīng)器調(diào)試 等方面行為一致:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar],onInvalidate) => {
/* ... */
onInvalidate(() => {...})
},
{
onTrigger(e) {
/*副作用依賴(lài)修改*/
debugger
},
onTrack(e) {
/*副作用依賴(lài)修改*/
debugger
},
})
生命周期鉤子函數(shù)
與 2.x 版本生命周期相對(duì)應(yīng)的組合式 API
beforeCreate -> 使用 setup() created -> 使用 setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted errorCaptured -> onErrorCaptured
import { onMounted, onUpdated, onUnmounted } from 'vue'
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
這些生命周期鉤子注冊(cè)函數(shù)只能在 setup() 期間同步使用, 因?yàn)樗鼈円蕾?lài)于內(nèi)部的全局狀態(tài)來(lái)定位當(dāng)前組件實(shí)例(正在調(diào)用 setup() 的組件實(shí)例), 不在當(dāng)前組件下調(diào)用這些函數(shù)會(huì)拋出一個(gè)錯(cuò)誤。
組件實(shí)例上下文也是在生命周期鉤子同步執(zhí)行期間設(shè)置的,因此,在卸載組件時(shí),在生命周期鉤子內(nèi)部同步創(chuàng)建的偵聽(tīng)器和計(jì)算狀態(tài)也將自動(dòng)刪除。
新增的鉤子函數(shù)
除了和 2.x 生命周期等效項(xiàng)之外,組合式 API 還提供了以下調(diào)試鉤子函數(shù):
onRenderTracked onRenderTriggered
兩個(gè)鉤子函數(shù)都接收一個(gè) DebuggerEvent,與 watchEffect 參數(shù)選項(xiàng)中的 onTrack 和 onTrigger 類(lèi)似:
export default {
onRenderTracked(e){
debugger
// 檢查有響應(yīng)和追蹤的依賴(lài)性
},
onRenderTriggered(e) {
debugger
// 檢查哪個(gè)依賴(lài)性導(dǎo)致組件重新渲染
},
}
Vue提供的內(nèi)置組件
component 與Vue2.x一致
渲染一個(gè)“元組件”為動(dòng)態(tài)組件。依 is 的值,來(lái)決定哪個(gè)組件被渲染。
<!-- 動(dòng)態(tài)組件由 vm 實(shí)例的 `componentId` property 控制 -->
<component :is="componentId"></component>
<!-- 也能夠渲染注冊(cè)過(guò)的組件或 prop 傳入的組件 -->
<component :is="$options.components.child"></component>
transition 與 Vue2.x 【基本】 一致有差異
Props新增
persisted - boolean 如果為true,則表示這是一個(gè)轉(zhuǎn)換,實(shí)際上不會(huì)插入/刪除元素,而是切換顯示/隱藏狀態(tài)。transition 過(guò)渡掛鉤被注入,但會(huì)被渲染器跳過(guò)。相反,自定義指令可以通過(guò)調(diào)用注入的鉤子(例如v-show)來(lái)控制過(guò)渡
enter-class----->enter-from-class
~~leave-class----->leave-from-class
事件
before-appear
transition-group 與 Vue2.x 一致
**slot 與 Vue2.x 一致 **
teleport 【新增組件】
Props
to - string 必填屬性,必須是一個(gè)有效的query選擇器,或者是元素(如果在瀏覽器環(huán)境中使用)。中的內(nèi)容將會(huì)被放置到指定的目標(biāo)元素中
<!-- 正確的 -->
<teleport to="#some-id" />
<teleport to=".some-class" />
/*元素*/
<teleport to="[data-teleport]" />
<!-- 錯(cuò)誤的 -->
<teleport to="h1" />
<teleport to="some-string" />
disabled - boolean 這是一個(gè)可選項(xiàng) ,做一個(gè)是可以用來(lái)禁用的功能,這意味著它的插槽內(nèi)容不會(huì)移動(dòng)到任何地方,而是按沒(méi)有teleport組件一般來(lái)呈現(xiàn)【默認(rèn)為false】
<teleport to="#popup" :disabled="displayVideoInline">
<h1>999999</h1>
</teleport>
注意,這將移動(dòng)實(shí)際的DOM節(jié)點(diǎn),而不是銷(xiāo)毀和重新創(chuàng)建,并且還將保持任何組件實(shí)例是活動(dòng)的。所有有狀態(tài)HTML元素(比如一個(gè)正在播放的視頻)將保持它們的狀態(tài)。【控制displayVideoInline并不是銷(xiāo)毀重建,它保持實(shí)例是存在的,不會(huì)被注銷(xiāo)】
關(guān)于Teleport 其他內(nèi)容
Vue 鼓勵(lì)我們通過(guò)將UI和相關(guān)行為封裝到組件中來(lái)構(gòu)建UI。我們可以將它們彼此嵌套在一起,以構(gòu)建構(gòu)成應(yīng)用程序UI的樹(shù)
但是,有時(shí)組件模板的一部分邏輯上屬于這個(gè)組件,而從技術(shù)角度來(lái)看,最好將這一部分模板移到DOM中的其他地方,放到Vue應(yīng)用程序之外
一個(gè)常見(jiàn)的場(chǎng)景是創(chuàng)建一個(gè)包含全屏模態(tài)的組件。在大多數(shù)情況下,您希望模態(tài)的邏輯駐留在組件中,但是模態(tài)框的定位問(wèn)題很快就很難通過(guò)CSS解決,或者需要更改組件的組成
考慮下面的HTML結(jié)構(gòu):
<body>
<div style="position: relative;">
<h3>Tooltips with Vue 3 Teleport</h3>
<div>
<modal-button></modal-button>
</div>
</div>
</body>
讓我們看看 mode -button
該組件將有一個(gè)button元素來(lái)觸發(fā)模態(tài)的打開(kāi),還有一個(gè)div元素,其類(lèi)為.modal,它將包含模態(tài)的內(nèi)容和一個(gè)自關(guān)閉按鈕
const app = Vue.createApp({});
app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal!
</button>
<div v-if="modalOpen" class="modal">
<div>
I'm a modal!
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
`,
data() {
return {
modalOpen: false
}
}
})
當(dāng)在初始HTML結(jié)構(gòu)中使用這個(gè)組件時(shí),我們可以看到一個(gè)問(wèn)題——模態(tài)被呈現(xiàn)在深嵌套的div中,模態(tài)的絕對(duì)位置以父div相對(duì)位置作為參考。


Teleport提供了一種干凈的方式,允許我們控制DOM中希望在哪個(gè)父節(jié)點(diǎn)下呈現(xiàn)HTML片段,而不必訴諸全局狀態(tài)或?qū)⑵洳鸱譃閮蓚€(gè)組件。
讓我們修改我們的modal-button來(lái)使用并告訴Vue "teleport this HTML to the "body"標(biāo)簽"。
app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
`,
data() {
return {
modalOpen: false
}
}
})


與Vue組件一起使用
如果包含一個(gè)Vue組件,它將仍然是的父組件的邏輯子組件
const app = Vue.createApp({
template: `
<h1>Root instance</h1>
<parent-component />
`
})
app.component('parent-component', {
template: `
<h2>This is a parent component</h2>
<teleport to="#endofbody">
<child-component name="John" />
</teleport>
`
})
app.component('child-component', {
props: ['name'],
template: `
<div>Hello, {{ name }}</div>
`
})
在這種情況下,即使在不同的地方呈現(xiàn)child-component,它仍將是parent-componen的子組件【而不是爺爺組件】,并將從其父組件接收一個(gè)name 的props
這也意味著來(lái)自父組件的注入如預(yù)期的那樣工作,并且子組件將嵌套在Vue Devtools的父組件之下,而不是放在實(shí)際內(nèi)容移動(dòng)到的地方
對(duì)同一目標(biāo)使用多次teleports
一個(gè)常見(jiàn)的用例場(chǎng)景是一個(gè)可重用的組件,該組件可能同時(shí)有多個(gè)活動(dòng)實(shí)例。對(duì)于這種場(chǎng)景,多個(gè)組件可以將它們的內(nèi)容掛載到相同的目標(biāo)元素。這個(gè)順序?qū)⑹且粋€(gè)簡(jiǎn)單的附加—稍后的掛載將位于目標(biāo)元素中較早的掛載之后。
<teleport to="#modals">
<div>A</div>
</teleport>
<teleport to="#modals">
<div>B</div>
</teleport>
<!-- result-->
<div id="modals">
<div>A</div>
<div>B</div>
</div>
依賴(lài)注入Provide / Inject
provide 和 inject 提供依賴(lài)注入,功能類(lèi)似 2.x 的 provide/inject。兩者都只能在當(dāng)前活動(dòng)組件實(shí)例的 setup() 中調(diào)用。
例如,如果我們想在根組件上提供一個(gè)book name,并將其inject到子組件上
import { provide, inject } from 'vue'
const RootComponent = {
setup() {
provide('book', 'Vue 3 guide')
}
}
const MyBook = {
setup() {
const book = inject(
'book',
'Eloquent Javascript' /* 選項(xiàng)的默認(rèn)值,假如父組件不提供值就返回默認(rèn) */
)
return {
book
}
}
}
inject 接受一個(gè)可選的的默認(rèn)值作為第二個(gè)參數(shù)。如果未提供默認(rèn)值,并且在 provide 上下文中未找到該屬性,則 inject 返回 undefined。
如果我們需要提供或注入多個(gè)值,我們可以通過(guò)隨后分別調(diào)用provide或inject來(lái)實(shí)現(xiàn)【多次調(diào)用】
import { provide, inject } from 'vue'
const RootComponent = {
setup() {
provide('book', 'Vue 3 guide')
provide('year', '2020')
}
}
const MyBook = {
setup() {
const book = inject(
'book',
'Eloquent Javascript' /* 選項(xiàng)的默認(rèn)值,假如父組件不提供值就返回默認(rèn) */
)
const year = inject('year')
return {
book,
year
}
}
}
注入的響應(yīng)性
可以使用 ref 或 reactive 來(lái)保證 provided 和 injected 之間值的響應(yīng)
import { ref, reactive } from 'vue'
// 提供者
setup() {
const book = reactive({
title: 'Vue 3 Guide',
author: 'Vue Team'
})
const year = ref('2020')
/*提供reactive響應(yīng)式*/
provide('book', book)
/*提供ref響應(yīng)式*/
provide('year', year)
}
// 消費(fèi)者
setup() {
const book = inject('book')
const year = inject('year')
/*響應(yīng)式*/
return { book, year }
}
現(xiàn)在,當(dāng)提供者組件上的book或year發(fā)生變化時(shí),我們可以觀察到它們?cè)谧⑷氲慕M件上的變化。
警告:我們不建議改變一個(gè)被注入的反應(yīng)性屬性【子組件去修改數(shù)據(jù)流】,因?yàn)樗鼤?huì)破壞Vue的單向數(shù)據(jù)流。相反,嘗試在提供值【父組件去修改】的地方改變值,或者提供一個(gè)方法來(lái)改變值
import { ref, reactive } from 'vue'
// in provider
setup() {
const book = reactive({
title: 'Vue 3 Guide',
author: 'Vue Team'
})
function changeBookName() {
book.title = 'Vue 3 Advanced Guide'
}
provide('book', book)
provide('changeBookName', changeBookName)
}
// in consumer
setup() {
const book = inject('book')
const changeBookName = inject('changeBookName')
return { book, changeBookName }
}
指令
v-text 【Vue2.x一致】
v-html【Vue2.x一致】
v-show【Vue2.x一致】
v-if【Vue2.x一致】
v-else【Vue2.x一致】
v-else-if【Vue2.x一致】
v-for【Vue2.x一致】
v-on【Vue2.x一致】
v-bind 【Vue2.x 修飾符差異】
修飾符
.prop 去除.sync 去除.camel 將 kebab-case attribute 名轉(zhuǎn)換為 camelCase
v-model【Vue2.x一致】
v-slot【Vue2.x一致】
v-cloak【Vue2.x一致】
v-once 【Vue2.x一致】
v-pre【Vue2.x一致】
v-is【新增】
注意:本節(jié)只影響在頁(yè)面的HTML中直接編寫(xiě)Vue模板的情況
全局API
createApp
返回一個(gè)應(yīng)用程序?qū)嵗?提供了一個(gè)應(yīng)用程序上下文。應(yīng)用程序?qū)嵗龗燧d的整個(gè)組件樹(shù)共享相同的上下文。
const app = Vue.createApp({})
參數(shù):該函數(shù)接收一個(gè)根組件選項(xiàng)對(duì)象作為第一個(gè)參數(shù)
const app = Vue.createApp({
data() {
return {
...
}
},
methods: {...},
computed: {...}
setup(){...}
...
})
使用第二個(gè)參數(shù),我們可以將根組件props 傳遞給應(yīng)用
<div id="app">
<!-- 這里將會(huì)顯示 'Evan' -->
{{ username }}
</div>
const app = Vue.createApp(
{
props: ['username']
},
{ username: 'Evan' }
)

h
返回“虛擬節(jié)點(diǎn)”,通??s寫(xiě)為VNode:一個(gè)簡(jiǎn)單的對(duì)象,它包含描述Vue應(yīng)該在頁(yè)面上渲染何種類(lèi)型的節(jié)點(diǎn)的信息,包括對(duì)任何子節(jié)點(diǎn)的描述。你可以手動(dòng)閱讀render functions
render() {
return Vue.h('h1', {}, 'Some title')
}
參數(shù):接受三個(gè)參數(shù)tag, props and children
tag:
類(lèi)型:String | Object | Function | null 詳情:一個(gè)HTML標(biāo)簽名,一個(gè)組件,一個(gè)異步組件或null。使用null將渲染成注釋。此參數(shù)是必需的
props
類(lèi)型:Object 詳情:模板中使用的attributes、props 和events 對(duì)應(yīng)的對(duì)象??蛇x
children
類(lèi)型: String | Array | Object
詳情:Children VNodes,使用h()構(gòu)建,或使用字符串來(lái)獲取“text VNodes”或帶有槽的對(duì)象??蛇x
const aaa = {
props: {
someProp: String
},
setup(props) {
console.log(props, "dsadasdasddasds");
},
render() {
return h(
"h2",
// {Object}props
//與props,attributes和events相對(duì)應(yīng)的對(duì)象
//我們將在template中使用。
// 可選的。
{style: {"font-size": "20px",
color: "#136"}},
[this.someProp,this.$slots.default()]);
}
};
app.component("anchored-heading", {
render() {
return h(
/*
// {String | Object | Function | null}標(biāo)簽
// HTML標(biāo)記名稱(chēng),組件,異步組件或null。
//使用null將渲染注釋。
//必填
*/
"h" + this.level, // tag name
// {Object}props
//與props,attributes和events相對(duì)應(yīng)的對(duì)象
//我們將在template中使用。
// 可選的。
{},
// {String | Array | Object} children
//使用`h()`構(gòu)建的子級(jí)VNode,
//或使用字符串獲取“文本VNodes”或
//具有插槽的對(duì)象。
// 可選的。
[
"Some text comes first.",
h("h1", "A headline"),
h(aaa, {
someProp: "foobar"
})
] );},
});

Vue.h(
'a',
{
name: headingId,
href: '#' + headingId
},
this.$slots.default()
)
])
defineAsyncComponent 【異步組件】
創(chuàng)建只在必要時(shí)加載的異步組件
參數(shù)
對(duì)于基本用法,defineAsyncComponent可以接受返回Promise的工廠函數(shù)。當(dāng)您從serve檢索到組件定義時(shí),應(yīng)該調(diào)用Promise的解析回調(diào)。您還可以調(diào)用reject(reason)來(lái)指示加載失敗。
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
/*或者*/
import('./components/AsyncComponent.vue')
/*或者*/
new Promise((resolve, reject) => {
/*可以reject*/
resolve({
template: '<div>I am async!</div>'
})
})
)
app.component('async-component', AsyncComp)
在使用本地注冊(cè)時(shí),還可以直接提供返回Promise的函數(shù)
import { createApp, defineAsyncComponent } from 'vue'
createApp({
// ...
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
}
})
resolveDynamicComponent【解析活動(dòng)的組件active】
resolveDynamicComponent只能在render或setup函數(shù)中使用。允許使用與component:is=""相同的機(jī)制來(lái)解析組件。返回解析的組件或一個(gè)新創(chuàng)建的VNode以組件名稱(chēng)作為節(jié)點(diǎn)標(biāo)記的。如果沒(méi)有找到組件,會(huì)發(fā)出警告
resolveDirective
resolveDirective只能在render或setup函數(shù)中使用。允許通過(guò)名稱(chēng)解析指令,如果它在當(dāng)前應(yīng)用程序?qū)嵗锌捎?。返回一個(gè)Directive或 當(dāng)沒(méi)有找到的時(shí)候,返回undefined。
app.directive('highlight', {})
render(){
const highlightDirective = resolveDirective('highlight')
}
withDirectives
警告withDirectives只能在render或setup函數(shù)中使用。允許應(yīng)用指令到VNode。返回一個(gè)帶有應(yīng)用指令的VNode。
const bar = resolveDirective('bar')
return withDirectives(h('div'), [
[bar, this.y]
])
nextTick
將回調(diào)延遲到下一個(gè)DOM更新周期之后執(zhí)行。在更改了一些數(shù)據(jù)以等待DOM更新之后立即使用它
setup() {
const message = ref('Hello!')
const changeMessage = async newMessage => {
message.value = newMessage
/*等待DOM更新*/
await nextTick()
console.log('Now DOM is updated')
}
}