(一)響應式數據
1. 簡單例子
從最簡單的數據綁定開始,在 Vue 2.0 中,我們這樣將一個數據綁定到模板的指定位置:
在組件創建參數的 data 構造函數中返回一個用來綁定的數據對象,其中有個 now
字段,會被渲染到模板內的 .app > p
內。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< template > < div class = "app" > < h1 >Hello world!</ h1 > < p >Now is: {{now.toString()}}</ p > </ div > </ template > < script > // Vue 2.0 export default { data() { return { now: new Date(), }; }, }; </ script > |
用 Vue3 的組裝 API 實現的話,則是這樣:
1
2
3
4
5
6
7
8
|
// Vue 3.0 export default { setup() { return { now: new Date(), }; }, }; |
2. 更新數據
奇怪,看起來好像沒啥區別,只是把 data
改成了 setup
嗎?
并不是,假如我們現在對這個 DEMO 做個小改動,讓它每秒鐘刷新一次時間,用 Vue2 大概是這樣實現:
1
2
3
4
5
6
7
8
9
10
11
|
// Vue 2.0 export default { data() { return { now: new Date(), }; }, mounted() { setInterval(() => this .now = new Date(), 1000); }, }; |
而 Vue3 的等效實現則為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// Vue 3.0 import { ref, onMounted } from 'vue' ; export default { setup() { const now = ref( new Date()); onMounted(() => { setInterval(() => now.value = new Date(), 1000); }); return { now, }; }, }; |
3. 對比分析
寫了太多 Vue 的我們可能已經忘了,Vue2 的代碼從標準 JS 模塊的角度來看有多奇怪:
-
mounted
中修改的this.now
數據是在哪創建的?我們在模塊default
對象的成員里并沒有找到對應字段,倒是在data
內返回的另一個對象中有這個字段; -
而
data
中返回的now
也不是真正的this.now
,而是this.now
的初始值,在data
中setInterval
修改now
并不能更新渲染出來的時間; -
如果想復用這個數據和它的更新邏輯,你必須將這樣的結構單獨寫一份,然后通過特殊的
mixin
函數混入到當前組件的構造參數內。
這一切,是因為整個模塊 default
對象其實是 vm
對象的構造參數。其背后隱藏了對象的創建邏輯,在構造對象時構造參數中的一些不同層級的字段被綁定到了 vm
對象上。
不少新手可能都犯過一個錯誤,在 data
中返回的數據字段和 props
、methods
或者 computed
中的字段命名撞車(尤其是使用名為 data 的字段),在編碼階段并不能被 IDE 直接發現。就是因為上面的原因,這些字段創建時隸屬于不同的位置,在之后構造時才被綁在了同一個對象上,導致了運行時才能發現的沖突。
Vue3 中,改成提供 ref
、reactive
、toRef
、onMounted
等函數的形式實現,例子中:
-
在
setup
中看到的now
即是用于綁定的this.now
; -
修改
now.value
即可看到頁面狀態的更新; -
如果要封裝這份數據處理,只需要將
now
和onMounted
處理提取到同一個函數內,再將now
返回即可,不再需要黑盒的mixin
處理。
可以說 Vue3 是直接將響應數據的創建決定權、生命周期的通知回調,都通過 API 的形式交給了開發者,更直觀明了和可控。
4. API 說明
下面詳細說說常用的幾個響應式數據相關 API:ref
, reactive
和 toRefs
。
(1) ref
上面例子中使用到的 ref,可以將一個數據包裝成響應式數據代理對象。
1
2
3
4
5
|
const count = ref(0); console.log(count.value); // => 0 count.value++; console.log(count.value); // => 1 |
當你修改代理對象的 count.value 屬性時,模板中使用到 count 的位置將響應數據的變化,更新視圖中的數據狀態。
(2) reactive
對于對象的響應式封裝,使用 ref
稍顯麻煩:
1
2
3
4
5
6
7
|
const state = ref({ count: 0, }); console.log(state.value.count); // => 0 state.value.count++; console.log(state.value.count); // => 1 |
這時可以改為使用 reactive
,像操作普通對象的字段一樣修改 count
即可更新視圖:
1
2
3
4
5
6
7
|
const state = reactive({ count: 0, }); console.log(state.count); // => 0 state.count++; console.log(state.count); // => 1 |
對代理對象 state
添加新的字段也可觸發視圖更新。
(3) toRefs
有時候,對象的名字過長,我們想直接在模板內使用對象內部字段,直接使用解構是不行的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import { reactive } from 'vue' ; export default { setup() { const position = reactive({ x: 0, y: 0, }); return { // 錯誤,解構出來的 x, y 并沒有響應式代理。綁定到模板上后,數據變化無法觸發視圖更新 ...position, }; }, }; |
這個情況下,使用 toRefs
處理后再解構賦值即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import { reactive, toRefs } from 'vue' ; export default { setup() { const position = reactive({ x: 0, y: 0, }); return { ...toRefs(position), }; }, }; |
但需要注意,toRefs
只處理調用時 position
的現有字段,如果在之后對 position
增加新字段,將無法觸發視圖更新。
以上就是淺析 Vue 3.0 的組裝式 API(一)的詳細內容,更多關于Vue 組裝式 API的資料請關注服務器之家其它相關文章!
原文鏈接:http://blog.krimeshu.com/2020/08/30/vue-3-composition-api-introduction-1/?utm_source=tuicool&utm_medium=referral