組合式 API 基礎
(就是将一個元件中的不同邏輯分離開來,放在不同的js檔案中,再引入,使用者不需要知道怎麼實作,隻需要知道提供了哪些功能)
- setup 元件選項
- 新的 setup 選項在元件建立之前執行,一旦 props 被解析,就将作為組合式 API 的入口。
- setup 中避免使用
, setup的調用 在this
、data
、methods
解析之前computed
- setup 選項是一個接收
和props
的函數context
- 将setup 傳回的函數 暴露給 元件的其餘部分 (計算屬性、方法、生命周期)
- 帶 ref 的響應式變量
- 在 Vue 3.0 中,我們可以通過一個新的 ref 函數使任何響應式變量在任何地方起作用
-
對toRefs(props)
屬性的響應式引用props
import { ref } from 'vue'
const counter = ref(0)
- ref 接收參數并将其包裹在一個帶有 value property 的對象中傳回,然後可以使用該 property 通路或更改響應式變量的值:
import { ref } from 'vue'
const counter = ref(0)
console.log(counter) // { value: 0 }
console.log(counter.value) // 0
- 在 setup 内注冊生命周期鈎子
- 為了使組合式 API 的功能和選項式 API 一樣完整,我們還需要一種在 setup 中注冊生命周期鈎子的方法。
- 這些函數接受一個回調,當鈎子被元件調用時,該回調将被執行。
import {ref, onMounted} from 'vue'
setup(){
const count = ref(0);
const getCount = function(){
return count + 6;
}
onMounted(getCount);
return {
count,
getCount
}
}
- watch 響應式更改
- 接收三個參數
- 一個想要偵聽的響應式引用或 getter 函數
- 一個回調
- 可選的配置選項
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
- 獨立的 computed 屬性
- 與 ref 和 watch 類似,也可以使用從 Vue 導入的 computed 函數在 Vue 元件外部建立計算屬性。
import { onMounted , ref , toRefs , computed , watch } from 'vue'
setup(props){
// 将 name 變成 props name 屬性的引用
const { name } = toRefs(props);
// 将 username 變成響應式的
const username = ref('');
// 方法
const getUserName = function(){
username.value ='ch' + name;
}
// 生命周期函數
onMounted(getUserName);
// 監聽 username 變化 觸發 getUserName方法
watch(username,getUserName);
// 計算屬性
const enName = computed(()=>'en' + name.value)
return {
username,
enName,
getUserName
}
}
Setup
- 接收兩個參數
- props
- context
- props
- props 是響應式的
(不要使用 ES6解構 會去掉它的響應式)
- 如果需要解構
,可以在prop
函數中使用setup
函數來完成此操作:toRefs
import { toRefs } from 'vue';
setup(props){
// name 不再是響應式
// let { name } = props;
// 這裡的name是響應式的
let { name } = toRefs(props);
}
- 對于非必填(可選)的props 不可以使用 toRefs() 結構
- toRefs 将不會為 title(可選參數) 建立一個 ref ,需要使用 toRef 替代它
import { toRefs , toRef } from 'vue';
setup(props){
let { name } = toRefs(props);
// title 非必填(可選)
const title = toRef(props, 'title')
}
- Context
- 對象格式,暴露元件的三個屬性
-
不是響應式的可以使用 ES6 安全解構Context
export default {
setup(props, { attrs, slots, emit }) {
...
}
}
-
: Attribute 非響應式對象attrs
-
: 插槽 非響應式對象slots
-
: 觸發事件 方法emit
-
和attrs
是有狀态的對象,它們總是會随元件本身的更新而更新。這意味着你應該避免對它們進行解構,并始終以 attrs.x 或 slots.x 的方式引用 property。 請注意,與 props 不同,attrs 和 slots 是非響應式的。如果你打算根據 attrs 或 slots 更改應用副作用,那麼應該在 onUpdated 生命周期鈎子中執行此操作slots
- 通路元件的 property
-
的執行是在 元件建立之前,無法通路setup
、methonds
、data
。computed
-
可以通路setup
及props
context(attrs,slots,emit)
-
- 結合模闆使用
- 如果 setup 傳回一個對象,那麼該對象的 property 以及傳遞給 setup 的 props 參數中的 property(原來就可以通路到 props) 就都可以在模闆中通路到
- 注意,從
傳回的setup
在模闆中通路時是被自動淺解包的,是以不應在模闆中使用refs
。.value
- 使用渲染函數
-
還可以傳回一個渲染函數,該函數可以直接使用在同一作用域中聲明的響應式狀态setup
傳回對象的響應式副本reactive
-
import { h , reactive } form 'vue'
setup(){
const img = reactive({ src:"https:xxxxxx" });
return () => h('img', { src:img.src })
}
- 使用 this
- 在
中setup
不是該活躍執行個體的引用,因為他是在解析其它元件選項之前被調用的this
- 在
生命周期鈎子
- 通過在生命周期鈎子前面加上 “on” 來通路元件的生命周期鈎子。
- 接收一個回調函數,當鈎子被元件調用的時候執行
setup(){
onMounted(()=>{})
}
Provide/Inject
-
Provide/Inject
- 需要先引入 import { provide , inject } form ‘vue’
- provide 接收兩個參數
- name
- value
- inject 兩個參數
- name
- 預設值(可選)
import { provide , inject } form 'vue'
// parent.vue
setup(){
provide('name','xiaohei');
provide('addr',{city:'',town:''});
}
//child.vue
setup(){
let cname = inject('name','hah');
let caddr = inject('addr');
}
-
響應性
- 為了增加 provide 值和 inject 值之間的響應性,我們可以在 provide 值時使用 ref 或 reactive。
import { provide , ref , reactive } form 'vue';
// parent.vue
setup(){
let name = ref('xiaohei');
let addr = reactive({city:'',town:''});
provide('name',name);
provide('addr',addr);
}
-
修改響應式 property
- 修改響應式 屬性的 時候,最好是在定義響應式的元件内進行修改
- 有時我們需要在注入元件的内部修改
中的值,可以通過inject
一個方法來修改這個值provide
import { provide , ref , reactive } form 'vue';
// parent.vue
setup(){
let age = ref(1);
let addr = reactive({city:'',town:''});
const updateAge = function(){
age.value +=1;
}
provide('name',name);
provide('addr',addr);
provide('updateAge',updateAge);
}
// child.vue
setup(){
let age = inject('age');
let addr = inject('addr');
const updateAge = inject('updateAge');
return{
age,
addr,
updateAge
}
},
methods:{
update(){
this.updateAge();
}
}
- 不允許 inject 的元件 修改 provide 傳遞的屬性 添加
readonly
readonly 接受一個對象 (響應式或純對象) 或 ref 并傳回原始對象的隻讀代理。隻讀代理是深層的:任何被通路的嵌套 property 也是隻讀的。
與 reactive 一樣,如果任何 property 使用了 ref,當它通過代理通路時,則被自動解包:
import { readonly , provide , reactive } from 'vue'
setup(){
let age = ref(1);
let work = reactive({money:'',addr:''}) ;
provide('age' , readonly(age)); // 隻讀屬性
provide('work' , readonly(work)); // 隻讀屬性
}
模闆引用
- 在使用組合式 API 時,響應式引用和模闆引用的概念是統一的。為了獲得對模闆内元素或元件執行個體的引用,我們可以像往常一樣聲明 ref 并從 setup() 傳回:
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() => {
// DOM元素将在初始渲染後配置設定給ref
console.log(root.value) // <div>這是根元素</div>
})
return {
root
}
}
}
</script>
- 這裡我們在渲染上下文中暴露
,并通過root
,将其綁定到 div 作為其ref="root"
。在ref
更新檔算法中,如果虛拟 DOM
的VNode
鍵對應于渲染上下文中的ref
,則ref
的相應元素或元件執行個體将被配置設定給該VNode
的值。這是在虛拟 DOM 挂載/打更新檔過程中執行的,是以模闆引用隻會在初始渲染之後獲得指派。ref
大緻意思就是 模闆裡面聲明的 ref='root' 如果與下文中暴露的 ref 所指派的 變量名一樣,就會把這個模闆中的ref 與下文中的那個ref指派的變量關聯起來
-
JSX 中的用法
export default {
setup() {
const root = ref(null)
return () =>
h('div', {
ref: root
})
// with JSX
return () => <div ref={root} />
}
}
-
v-for 中的用法
<template>
<div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
{{ item }}
</div>
</template>
<script>
import { ref, reactive, onBeforeUpdate } from 'vue'
export default {
setup() {
const list = reactive([1, 2, 3])
const divs = ref([])
// 確定在每次更新之前重置ref
onBeforeUpdate(() => {
divs.value = []
})
return {
list,
divs
}
}
}
</script>
-
偵聽模闆引用
- 但與生命周期鈎子的一個關鍵差別是,
和watch()
在 DOM 挂載或更新之前運作副作用,是以當偵聽器運作時,模闆引用還未被更新。watchEffect()
- 是以,使用模闆引用的偵聽器應該用
選項來定義,這将在flush: 'post'
更新後運作副作用,確定模闆引用與DOM
保持同步,并引用正确的元素。DOM
- 但與生命周期鈎子的一個關鍵差別是,
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, watchEffect } from 'vue'
export default {
setup() {
const root = ref(null)
watchEffect(() => {
console.log(root.value) // => <div></div>
},
{
flush: 'post'
})
return {
root
}
}
}
</script>