vue3.0已經發布正式版,遲早都要學,趕早不趕晚。vue
本文全部文字出自個人vue3練手倉庫 GitHubreact
一組能夠將UI 和 數據處理邏輯 分離的API。 入口函數setup();git
這兩個函數,均可以把數據,變成響應式的。即,數據改變 驅動 視圖(UI)改變。 其中。ref通常對簡單數據類型進行包裝,reactive對引用數據類型進行包裝。github
先看下ref的例子api
<template>
<button @click="addCnt">count is: {{ cnt }}</button>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'Ref',
// setup函數 是組合API(composition API) 的入口函數
setup() {
// 定義 變量/state。 注意ref()首字母小寫,不是Ref。
let cnt = ref(0); // ref只能監聽簡單類型數據的變化,不能監聽複雜類型(對象/數組),複雜類型請用reactive()。
// 定義 函數(方法)
const addCnt = () => {
// 注意必須加value
cnt.value++;
};
// 在組合API中定義的變量/方法,想要在外部使用,必須return暴露出去
return { cnt, addCnt };
},
};
</script>
這是一個簡單的 加法器 demo。咱們用ref聲明一個可響應狀態cnt, 以及一個數據處理邏輯函數addCnt,
setup()要求將狀態和方法返回,才能在模板裏使用。
複製代碼
再來看reactive的例子,實現一個TODO list demo數組
<template>
<form>
姓名:<input type="text" v-model="state.name">
年齡:<input type="text" v-model="state.age">
<input type="submit" @click="submit"></input>
</form>
<ol class="ol">
<li v-for="(item, index) in state.students" :key="index" @click="delStu(index)">
{{ item.name }} -- {{ item.age }}
</li>
</ol>
</template>
<script>
import { reactive } from 'vue'; // 注意首字母小寫
export default {
name: 'Reactive',
// 入口函數
setup() {
// 聲明幾個變量,用reactive監聽變化。
let state = reactive({
students: [
{
name: '張三',
age: 11,
},
{
name: '李四',
age: 21,
},
],
name: '',
age: '',
});
// 添加一個學生
let submit = (e) => {
e.preventDefault();
state.students.push({
name: state.name,
age: state.age,
});
state.name = '';
state.age = '';
};
// 刪除一個學生
let delStu = (index) => {
state.students.splice(index, 1);
};
return { state, submit, delStu };
}
};
</script>
複製代碼
composition API 固然是能夠和之前vue2.x的Options API那種寫法混用的。 實際上,composition API也叫注入API,是將setup中暴露的變量和方法注入到data()和methods中去。markdown
<template>
<div>
{{ count }}
<button @click="logCount">點擊Count</button>
</div>
<div>
{{ cnt }}
<button @click="logCnt">點擊Cnt</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'Mix',
data() {
return {
count: 1,
};
},
methods: {
logCount() {
alert(this.count);
}
},
// Vue2.x的寫法叫作 Options API寫法
// 本🌰可見,組合API和Options API能夠混用。
// 且實際上,組合API(也叫注入API)是將其中的變量和方法注入到data()和methods中去
setup() {
let cnt = ref(5);
let logCnt = () => {
alert(cnt.value); // 注意ref監聽的對象,在js中使用要加.value。
};
return { cnt, logCnt };
}
};
</script>
複製代碼
如何區分一個數據或者狀態,是經過ref仍是reactive包裝的呢? 能夠經過isRef 和 isReactive 兩個方法進行判斷。函數
<template>
<button @click="log"> 按鈕 </button>
</template>
<script>
import { ref, reactive, isRef, isReactive } from 'vue';
export default {
name: 'Which',
setup() {
let age = ref(18);
let state = reactive({
age: 11
});
const log = () => {
console.log('age is ref ? ', isRef(age)); // true
console.log('age is reactive ? ', isReactive(age)); // false
console.log('state is ref ? ', isRef(state)); // false
console.log('state is reactive ? ', isReactive(state)); // true
};
return { age, log };
},
};
</script>
複製代碼
在這個例子裏,age是經過ref包裝的,state是經過reactive包裝的,能夠經過isRef和isReactive區分。oop
默認狀況下,ref和reactive包裝的數據都是 遞歸監聽的,於是能夠遞歸響應。 可是遞歸監聽對性能是有影響的。 那麼,假設要監聽的數據數據量很大,且嵌套結構特別深,若是想要只對第一層數據監聽,這種狀況下,就可使用shallowReactive()。性能
<template>
<div>
<div>{{state.age}}</div>
<div>{{state.a.v}}</div>
<div>{{state.a.b.v}}</div>
</div>
<button @click="log">按鈕</button>
</template>
<script>
import { reactive, shallowReactive, shallowRef } from 'vue';
export default {
name: 'Recurse',
setup() {
// 只須要將reactive改爲shallowReactive,就能夠非遞歸監聽
// 通常狀況下,使用默認的遞歸監聽便可,只有傳入的數據量較大的時候,才考慮非遞歸監聽
let state = shallowReactive({
a: {
v: 1,
b: {
v: 2,
}
},
age: 15
});
const log = () => {
// 注意觀察 註釋掉下面這句話 和 不註釋下面這句話 的區別。
// state.age = 16; // 只有age修改能夠引發UI變化,若age不修改,內層數據修改,UI不變;若age和內存數據都修改,UI都變,是由於age的變化帶動了內層數據UI的響應
state.a.v = 'a';
state.a.b.v = 'b';
console.log('111: ', state);
console.log('111: ', state.a);
console.log('111: ', state.a.b);
};
return { state, log };
},
};
</script>
複製代碼
仍是上面那個問題,ref和reactive包裝後,數據變動都是實時反映到UI上的,若是監聽的數據數據量很大,那麼,有可能 是對性能有所影響的。
若是不想對某些數據或狀態的修改,都進行UI上的實時響應變化,該怎麼辦呢? toRaw 和 markRaw也許能夠幫到你。
toRaw 和 markRaw的區別是:toRaw是能夠把某個可響應數據/狀態,反取到原始值,修改原始值,UI是不會實時變化的; 而markRaw則是直接將數據或狀態聲明爲不可響應的,則後面,即便加了ref、reactive這樣的包裝,UI也不會再響應數據的變動。
先來看看toRaw
<template>
<div>
{{ state.name}} -- {{ state.age }}
<button @click="log">按鈕</button>
</div>
</template>
<script>
import { reactive, toRaw } from 'vue'; // 注意首字母小寫
export default {
name: 'ToRaw',
// toRaw的做用:由於ref/reactive每次修改都更新UI,性能消耗大,若某些操做不須要UI及時更新,能夠經過toRaw拿到原始數據
// 對原始數據進行修改,這樣UI就不會更新,性能就行了。
// (固然也能夠像下面那樣,在定義ref/reactive的時候,就把原始變量user單獨拿出去聲明,直接修改user,跟修改userOld是同樣的))
setup() {
let user = {
name: 'zs',
age: 12,
};
let state = reactive(user);
console.log('user === reactive(user): ', user === state); // false。 他們是引用關係
let userOld = toRaw(state);
console.log('user === toRaw(state): ', user === userOld); // true.
let log = () => {
// 改變原始數據,UI天然不會響應。(不過由於是引用關係,state.name其實已經變成lisi了。)
user.name = 'lisi';
console.log('111: ', state);
};
return { state, log };
}
};
</script>
複製代碼
再來看看markRaw
<template>
<div>
{{ state.name}} -- {{ state.age }}
<button @click="log">按鈕</button>
</div>
</template>
<script>
import { reactive, markRaw } from 'vue'; // 注意首字母小寫
export default {
name: 'MarkRaw',
setup() {
let user = {
name: 'zs',
age: 12,
};
// 注意加 和 不加markRaw 區別。有了它,UI永不響應數據的變化。(雖然數據自己已經變了)
markRaw(user);
// 即便後面再用reactive包裝改變量,UI也不響應數據變動。
let state = reactive(user);
let log = () => {
state.name = 'lisi';
console.log('111: ', state);
};
return { state, log };
}
};
</script>
複製代碼
還有不少特性,要手擼, 該倉庫 持續更新中,歡迎star~~~