Vue3.0學習筆記
Vue3.0學習筆記
2020年9月18日,尤雨溪大神終於推出了Vue3.0的正式版本,一時間眾多前端碼農頂禮膜拜之。餘亦景從,學習之。常言道:好記性不如一個爛筆頭。然也。現將筆記記錄如下:
新的改變
Vue3.0相對於2.x的版本,底層響應式資料本質發生了變化。在Vue2.x中是通過Object.defineProperty來實現響應式資料的,而在Vue3.0中是通過new Proxy來實現響應式資料的。此外,Vue3.0還新增了Composition API來增加對大型專案的更好的適配。
詳情
Vue3.0 響應式資料本質
示例如下
let obj = {name:"lnj",age:17};
// let arr = [1,4,7];
let state = new Proxy(obj,{
// let state = new Proxy(arr,{
get(obj,key){
console.log(obj,key);
return obj[key];
},
set(obj, key, value){
// [ 1, 4, 7 ] '3' 9
console.log(obj, key, value);
// [ 1, 4, 7, 9 ] 'length' 4
obj[key] = value;
console.log("更新介面");
return true;
}
});
console.log(state.name);
state.name = "zdf";
console.log(state);
// console.log(state[1]);
// state.push(9);
組合API–1
<template>
<div>
<ul>
<li v-for="(stu, index) in state.stus" :key="stu.id" @click="remStu(index)">{{ stu.name }}---{{ stu.age }}</li>
</ul>
</div>
</template>
<script>
import { reactive } from 'vue';
export default {
name: 'App',
// setup函式是Composition API的入口函式
setup() {
/*
// ref函式注意點:
// ref函式只能監聽簡單型別的變化,不能監聽複雜型別的變化(物件,陣列)
let state = reactive({
stus: [{ id: 1, name: 'zs', age: 10 }, { id: 2, name: 'ls', age: 20 }, { id: 3, name: 'ww', age: 30 }]
});
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx != index);
}
*/
let { state, remStu } = useRemoveStudent();
return { state, remStu };
},
data() {
return {};
},
methods: {}
};
function useRemoveStudent() {
let state = reactive({
stus: [{ id: 1, name: 'zs', age: 10 }, { id: 2, name: 'ls', age: 20 }, { id: 3, name: 'ww', age: 30 }]
});
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx != index);
}
return { state, remStu };
}
</script>
新建add.js和remove.js如下
//add.js
import { reactive } from 'vue';
function useAddStudent(state) {
let state2 = {
stu: { id: '', name: '', age: '' }
};
function addStu(e) {
e.preventDefault();
const stu = Object.assign({}, state2.stu);
state.stus.push(stu);
state2.stu.id = '';
state2.stu.name = '';
state2.stu.age = '';
}
return { state2, addStu };
}
export default useAddStudent;
//remove.js
import { reactive } from 'vue';
function useRemoveStudent() {
let state = reactive({
stus: [{ id: 1, name: 'zs', age: 10 }, { id: 2, name: 'ls', age: 20 }, { id: 3, name: 'ww', age: 30 }]
});
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx != index);
}
return { state, remStu };
}
export default useRemoveStudent;
組合API–2
<template>
<div>
<form action="">
<input type="text" v-model="state2.stu.id" />
<input type="text" v-model="state2.stu.name" />
<input type="text" v-model="state2.stu.age" />
<input type="submit" @click="addStu" />
</form>
<ul>
<li v-for="(stu, index) in state.stus" :key="stu.id" @click="remStu(index)">{{ stu.name }}---{{ stu.age }}</li>
</ul>
</div>
</template>
<script>
import useAddStudent from './add';
import useRemoveStudent from './remove';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
/*
// ref函式注意點:
// ref函式只能監聽簡單型別的變化,不能監聽複雜型別的變化(物件,陣列)
let state = reactive({
stus: [{ id: 1, name: 'zs', age: 10 }, { id: 2, name: 'ls', age: 20 }, { id: 3, name: 'ww', age: 30 }]
});
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx != index);
}
*/
let { state, remStu } = useRemoveStudent();
let { state2, addStu } = useAddStudent(state);
return { state, remStu, state2, addStu };
},
data() {
return {};
},
methods: {}
};
/*
function useRemoveStudent() {
let state = reactive({
stus: [{ id: 1, name: 'zs', age: 10 }, { id: 2, name: 'ls', age: 20 }, { id: 3, name: 'ww', age: 30 }]
});
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx != index);
}
return { state, remStu };
}
function useAddStudent(state) {
let state2 = {
stu: { id: '', name: '', age: '' }
};
function addStu(e) {
e.preventDefault();
const stu = Object.assign({}, state2.stu);
state.stus.push(stu);
state2.stu.id = '';
state2.stu.name = '';
state2.stu.age = '';
}
return { state2, addStu };
}
*/
</script>
setup執行時機和注意點
<template>
<div>
<p>{{name}}</p>
<button @click="myFn1">按鈕</button>
<p>{{age}}</p>
<button @click="myFn2">按鈕</button>
</div>
</template>
<script>
/*
1.Composition API和Option API混合使用
2.Composition API 本質(組合API/注入API)
3.setup執行時機
setup: Composition API 的入口函式,在beforeCreate之前執行
beforeCreate:表示元件剛剛被創建出來 ,組建的data和methods還沒有初始化好
created:表示元件剛剛被創建出來 ,並且組建的data和methods已經初始化好
4.setup注意點
- 由於在執行setup函式的時候,還沒有執行created生命週期方法
所以在setup函式中,是無法使用data和methods
- 由於我們不能再setup函式中使用data和methods,
所以Vue為了避免我們錯誤的使用,它直接將setup函式中的this修改成了undefined
- setup函式只能是同步的,不能是非同步的
*/
import {ref} from 'vue';
export default {
name: 'App',
data() {
return {
name:'sdf'
};
},
methods: {
myFn1(){
alert("asdf");
}
},
// setup函式是組合API的入口函式
setup() {
let age = ref(18);
function myFn2(){
alert("www.ksdf.com");
}
// console.log(this);//undefined
// console.log(this.name);
// this.myFn1();
return {age, myFn2};
},
};
</script>
reactive的理解
<template>
<div>
<!-- <p>{{state}}</p>
<p>{{state.age}}</p>
button @click="myFn">按鈕</button>
<p>{{state}}</p>
<button @click="myFn">按鈕</button> -->
<p>{{state.time}}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/*
1.什麼是reactive?
- reactive是vue3.0中提供的響應式資料的方法
- 在vue2.0中響應式資料是通過defineProperty來實現的
而在vue3.0中響應式資料是通過ES6的Proxy來實現的
2.reactive注意點:
- reactive引數必須是物件(json/arr)
- 如果給reactive傳遞了其他物件
+ 預設情況下修改物件,介面不會自動更新
+ 若想更新,可通過重新賦值的方式
*/
import {reactive} from 'vue';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
// let state = reactive(123);
// let state = reactive({
// age:123
// });
// function myFn(){
// state = 666;
// state.age = 666;
// console.log(state);
// }
// let state = reactive([1,2,4,5]);
// function myFn(){
// state[0] = 666;
// console.log(state);
// }
let state = reactive({
time: new Date()
});
function myFn(){
// state.time.setDate(state.time.getDate() + 1);
const newTime = new Date(state.time.getTime());
newTime.setDate(state.time.getDate() + 1);
state.time = newTime;
console.log(state.time);
}
return {state, myFn};
},
};
</script>
ref和reactive區別
<template>
<div>
<!--
ref和reactive區別
如果在template中使用的是ref型別的資料,那麼Vue會自動幫我們新增.value
如果在template中使用的是reactive型別的資料,那麼Vue不會自動幫我們新增.value
Vue是如何決定是否需要自動新增.value的?
Vue在解析資料之前,會自動判斷這個資料是否是ref型別的,
如果是就自動新增.value,若不是就不自動新增.value
Vue是如何判斷當前資料是否是ref型別的?
通過當前資料的__v_ref來判斷的
若有這個私有屬性,且取值為true,那麼當前資料為ref型別的
-->
<!-- <p>{{ state.age }}</p> -->
<!--
注意點:
如果是通過ref建立的資料,那麼在template中使用時不用通過.value的方式來獲取
因為Vue會自動給我們新增.value
-->
<p>{{ age }}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/*
1.什麼是ref?
- ref和reactive一樣,也是用來實現響應式資料的方法
- 由於reactive必須傳遞一個物件,所以導致在企業開發中
如果我們只想讓某個變數實現響應式的時候會非常麻煩,
所以Vue3為我們提供了ref方法,來實現對簡單值的監聽
2.ref本質:
- ref本質還是一個reactive
當我們給ref函式傳遞一個值後,ref函式底層會自動將ref轉換成reactive
ref(18) -> reactive({value: 18})
3.ref注意點:
- 在Vue中使用ref的值不用通過value獲取
- 在Js中使用ref的值必須通過value獲取
*/
// import { reactive } from 'vue';
import { ref } from 'vue';
import {isRef, isReactive} from 'vue';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
// let state = reactive({
// age: 18
// });
// function myFn() {
// state.age = 666;
// }
// return { state, myFn };
/**
*ref本質:
* ref本質還是一個reactive
* 當我們給ref函式傳遞一個值後,ref函式底層會自動將ref轉換成reactive
* ref(18) -> reactive({value: 18})
*/
let age = ref(18);
// let age = reactive({value: 18});
function myFn() {
// age = 666;
age.value = 666;
console.log(age);
console.log(isRef(age));
console.log(isReactive(age));
}
return { age, myFn };
}
};
</script>
遞迴、非遞迴監聽
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.gf.b }}</p>
<p>{{ state.gf.f.c }}</p>
<p>{{ state.gf.f.s.d }}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/**
1.遞迴監聽
預設情況下,無論通過ref還是通過reactive都是遞迴監聽
2.遞迴監聽存在的問題
如果資料量比較大,非常消耗效能
3.非遞迴監聽
shallowRef / shallowReactive
4.如何觸發非遞迴監聽屬性更新介面?
如果是shallowRef型別的資料,可以通過triggerRef來觸發
5.應用場景
一般情況下,我們使用ref和reactive就可以了
只有在監聽的資料量比較大的時候,我們才使用shallowRef和shallowReactive
*/
// import { reactive } from 'vue';
// import { shallowReactive } from 'vue';
import { ref, shallowRef, triggerRef } from 'vue';
// import {isRef, isReactive} from 'vue';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
// let state = reactive({
// let state = shallowReactive({
// a: 'a',
// gf: {
// b: 'b',
// f: {
// c: 'c',
// s: {
// d: 'd'
// }
// }
// }
// });
// function myFn() {
// state.a = '1';
// state.gf.b = '2';
// state.gf.f.c = '3';
// state.gf.f.s.d = '4';
// console.log(state);
// console.log(state.gf);
// console.log(state.gf.f);
// console.log(state.gf.f.s);
// }
// shallowRef -> shallowReactive
// shallowRef(10) -> shallowReactive({value: 10})
// let state = ref({
let state = shallowRef({
a: 'a',
gf: {
b: 'b',
f:{
c: 'c',
s: {
d:'d'
}
}
}
});
function myFn() {
// state.value.a = '1';
// state.value.gf.b = '2';
// state.value.gf.f.c = '3';
// state.value.gf.f.s.d = '4';
// state.value = {
// a: '1',
// gf: {
// b: '2',
// f:{
// c: '3',
// s: {
// d:'4'
// }
// }
// }
// };
state.value.gf.f.s.d = '4';
// 注意點:Vue3只提供了triggerRef,沒有提供triggerReactive方法
triggerRef(state);
// 注意點:如果是通過shallowRef建立的資料,
//那麼Vue監聽的是.value的變化,並不是第一層的變化
console.log(state);
console.log(state.value);
console.log(state.value.gf);
console.log(state.value.gf.f);
console.log(state.value.gf.f.s);
}
return { state, myFn };
}
};
</script>
toRaw
<template>
<div>
<p>{{ state }}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/**
* 1.toRaw
* 從Reactive或Ref中得到原始資料
*
* 2.toRaw作用
* 做一些不想被監聽的事情(提升效能)
*/
import { reactive, toRaw, ref } from 'vue';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
let obj = { name: 'asdf', age: 18 };
/**
* ref/reactive資料型別的特點:
* 每次修改都會被追蹤,都會更新UI介面,如此是非常消耗效能的,
* 所以若我們有一些操作不需要被追蹤,不需要更新UI介面,那麼此時,
* 我們可以使用toRaw方法拿到它的原始資料,對原始資料進行修改,
* 這樣就不會被追蹤,不會更新UI介面,這樣效能就好了
*/
// let state = reactive(obj);
// let obj2 = toRaw(state);
let state = ref(obj);
//注意點:如果想通過toRaw拿到ref型別的原始資料
// 那麼必須明確的告訴toRaw方法,要獲取的是.value的值
// 因為進過Vue處理之後,.value中儲存的才是當初建立時傳入的那個原始資料
let obj2 = toRaw(state.value);
console.log(obj === obj2);//true
// console.log(obj === state);//false
// state和obj的關係
// 引用關係,state的本質是一個Proxy物件,在這個Proxy物件中引用了obj
function myFn() {
// 如果直接修改obj,是無法觸發介面更新的
// 只有通過包裝之後的物件來修改,才會觸發介面更新
// obj2.name = 'zs';
// console.log(obj2); //{name: "zs", age: 18}
// console.log(state); //{name: "zs", age: 18}
// state.name = 'zs';
// console.log(state);
}
return { state, myFn };
}
};
</script>
markRaw
<template>
<div>
<p>{{ state }}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/**
* 1.markRaw
* 資料永遠不會被追蹤
*/
import { reactive, markRaw } from 'vue';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
let obj = { name: 'asdf', age: 18 };
obj = markRaw(obj);
let state = reactive(obj);
function myFn() {
state.name = 'zs';
}
return { state, myFn };
}
};
</script>
toRef
<template>
<div>
<p>{{ state }}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/**
* 1.toRef
* 響應式資料和原始資料產生關聯,資料變化,且不會更新UI介面
*/
import { ref, toRef } from 'vue';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
let obj = { name: 'asdf', age: 18 };
/*
ref(obj.name) -> ref('asdf') -> reactive({value:'asdf'})
*/
// ref -> 複製
// let state = ref(obj.name);
//toRef -> 引用
/**
ref和toRef區別:
ref->複製,修改響應式資料不會影響以前的資料
toRef->引用,修改響應式資料會影響以前的資料
ref:資料發生改變,介面自動更新
toRef:資料發生改變,介面不會自動更新
應用場景:
如果想讓響應式資料和以前的資料關聯起來,並且更新響應式資料之後還不想更新UI介面,那麼就可以使用toRef
*/
let state = toRef(obj,'name');
function myFn() {
state.value = 'zs';
/**
* 結論:如果利用ref將某一個物件中的屬性變成 響應式的資料
* 我們修改響應式的資料是不會影響到原始資料的
*/
/**
* 結論:如果利用ToRef將某一個物件中的屬性變成 響應式的資料
* 我們修改響應式的資料是會影響到原始資料的
* 但是如果相應式資料是通過toRef建立的,那麼修改了資料是不會觸發UI介面的更新的
*/
console.log(obj);
console.log(state);
}
return { state, myFn };
}
};
</script>
toRefs
<template>
<div>
<p>{{ state.name }}</p>
<p>{{ state.age }}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/**
* 1.toRefs
* 響應式資料和原始資料產生關聯,資料變化,且不會更新UI介面
*/
import { ref, toRef, toRefs } from 'vue';
export default {
name: 'App',
// setup函式是組合API的入口函式
setup() {
let obj = { name: 'asdf', age: 18 };
// let name = toRef(obj,'name');
// let age = toRef(obj,'age');
let state = toRefs(obj);
function myFn() {
// name.value = 'zs';
// age.value = 666;
console.log(obj);
state.name.value = "zs";
state.age.value = 666;
console.log(state);
// console.log(name);
// console.log(age);
}
// return { name,age, myFn };
return { state, myFn };
}
};
</script>
customRef上
<template>
<div>
<p>{{age}}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/**
* 1.customRef
* 返回一個ref物件,可以顯式的控制依賴追蹤和觸發響應
*/
import { ref, customRef } from 'vue';
function myRef(value){
return customRef((track,trigger)=>{
return {
get(){
track();// 告訴Vue這個資料是需要追蹤的
console.log("get",value);
return value;
},
set(newValue){
console.log("set",newValue);
value = newValue;
trigger();//告訴Vue觸發介面更新
}
}
});
}
export default {
name: 'App',
setup() {
// let age = ref(18);
let age = myRef(18);
function myFn(){
age.value +=1;
}
return {age,myFn};
}
};
</script>
customRef下.vue
<template>
<ul>
<li v-for="(item,index) in state"
:key="index">{{item.name}}</li>
</ul>
</template>
<script>
/**
* 1.customRef
* 返回一個ref物件,可以顯式的控制依賴追蹤和觸發響應
*/
import { ref, customRef } from 'vue';
function myRef(value){
return customRef((track,trigger)=>{
fetch(value)
.then((res)=>{
return res.json();
})
.then((data)=>{
console.log(data);
value = data;
trigger();
})
.catch((error)=>{
console.log(error);
});
return {
get(){
track();// 告訴Vue這個資料是需要追蹤的
console.log("get",value);
// 注意點
// 不能再get方法中傳送網路請求
// 渲染介面 -> 呼叫get -> 傳送網路請求
// 儲存資料 -> 更新介面 -> 呼叫get
return value;
},
set(newValue){
console.log("set",newValue);
value = newValue;
trigger();//告訴Vue觸發介面更新
}
}
});
}
export default {
name: 'App',
setup() {
let state = myRef("../public/data.json");
return {state};
}
};
</script>
ref獲取元素
<template>
<div ref="box">
我是一個div
</div>
</template>
<script>
/**
* 1.獲取元素
* 在Vue2.x中我們可以通過給元素新增ref="xxx",
* 然後在程式碼中通過ref.xxx的方式來獲取元素
* 在Vue3.x中我們可以通過ref來獲取元素
*/
/**
*
* setup
* beforeCreate
* created
*/
import { ref,onMounted } from 'vue';
export default {
name: 'App',
setup() {
let box = ref(null);
onMounted(()=>{
console.log("onMounted",box.value);
});
console.log(box.value);//null
return {box};
}
};
</script>
readonly家族
<template>
<div>
<p>{{state.name}}</p>
<p>{{state.attr.age}}</p>
<p>{{state.attr.height}}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
import {readonly, isReadonly, shallowReadonly} from "vue";
export default {
name: 'App',
setup() {
// readonly 用於建立一個只讀的資料,並且是遞迴只讀
// let state = readonly({
// name:"lng",
// attr:{
// age:18,
// height:1.88
// }
// });
// shallowReadonly 用於建立一個只讀的資料,但是不是遞迴只讀
let state = shallowReadonly({
name:"lng",
attr:{
age:18,
height:1.88
}
});
// const和readonly的區別
// const:賦值保護,不能給變數重新賦值
// readonly:屬性保護,不能給屬性重新賦值
// const value = 123
const value = {name:"ls",age:123};
function myFn(){
state.name = "sdlkfj",
state.attr.age = 14;
state.attr.height = 1.22;
console.log(state);
console.log(isReadonly(state));
// value = 456;
// console.log(value);
value.name = "zs";
value.age = 456;
console.log(value);
}
return {state,myFn};
}
};
</script>
手寫reactive、ref、isRef、isReactive
const { createBaseRollupPlugins } = require("vite");
function isRef(state){
return state.__v_ref||false;
}
function isReactive(state){
return state.__v_reacitve||false;
}
function ref(val){
return reactive({value:val},{__v_ref:true});
}
function reactive(obj,tem){
if(typeof obj === 'object' && obj != null){
if(obj instanceof Array){
// 如果是一個數組,那麼取出陣列中每個元素,
// 判斷每個元素是否又是一個物件,如果又是一個物件,那麼也需要包裝成一個Proxy
obj.forEach((item, index)=>{
if(typeof item === 'object'){
obj[index] = reactive(item,tem);
}
});
}else{
// 如果是一個物件,那麼取出物件屬性的值
// 判斷物件屬性的取值是否又是一個物件,如果又是一個物件,那麼也需要包裝成一個Proxy
for(let key in obj){
let item = obj[key];
if(typeof item === 'object'){
obj[key] = reactive(item,tem);
}
}
}
if(tem){
obj = Object.assign(tem,obj);
}else{
obj = Object.assign({ __v_reacitve:true},obj);
}
return new Proxy(obj,{
get(obj,key){
return obj[key];
},
set(obj,key,val){
obj[key] = val;
console.log("更新介面");
return true;
}
});
}else{
console.log(`${obj} is not object`);
}
}
let obj = {
a: 'a',
gf: {
b: 'b',
f:{
c: 'c',
s: {
d:'d'
}
}
}
};
let state = ref(obj);
state.a = "1";
state.gf.b = "2";
state.gf.f.c = "3";
state.gf.f.s.d = "4";
console.log("isRef",isRef(state));
console.log("isReactive",isReactive(state));
/*
let arr = [{id:"1", name:"魯班"},{id:"2", name:"虞姬"}];
let state = reactive(arr);
state[0].name = "zs";
state[1].name = "ls";
console.log("isReactive",isReactive(state));
console.log("isRef",isRef(state));
*/
手寫readonly、shallowReadonly、isReadonly
function shallowReadonly(obj){
return new Proxy(obj,{
get(obj,key){
return obj[key];
},
set(obj,key,value){
console.warn(`${key} 是隻讀的,不能賦值`);
}
});
}
/*
let obj = {
a:1,
gf:{
b:2
}
}
let state = shallowReadonly(obj);
state.a = 2;
*/
function readonly(obj){
if(typeof obj === 'object' && obj != null){
if(obj instanceof Array){
// 如果是一個數組,那麼取出陣列中每個元素,
// 判斷每個元素是否又是一個物件,如果又是一個物件,那麼也需要包裝成一個Proxy
obj.forEach((item, index)=>{
if(typeof item === 'object'){
obj[index] = readonly(item);
}
});
}else{
// 如果是一個物件,那麼取出物件屬性的值
// 判斷物件屬性的取值是否又是一個物件,如果又是一個物件,那麼也需要包裝成一個Proxy
for(let key in obj){
let item = obj[key];
if(typeof item === 'object'){
obj[key] = readonly(item);
}
}
}
obj = Object.assign({ __v_readonly:true},obj);
return new Proxy(obj,{
get(obj,key){
return obj[key];
},
set(obj,key,val){
console.warn(`${key} 是隻讀的,不能賦值`);
}
});
}else{
console.log(`${obj} is not object`);
}
}
function isReadonly(state){
return state.__v_readonly||false;
}
let obj = {
a:1,
gf:{
b:2
}
}
let state = readonly(obj);
state.a = 2;
state.gf.b = 4;
console.log("isReadonly",isReadonly(state));
手寫shallowReactive、shallowRef
function shallowRef(val){
return shallowReactive({value:val});
}
function shallowReactive(obj){
return new Proxy(obj,{
get(obj,key){
return obj[key];
},
set(obj,key,value){
obj[key] = value;
console.log("更新UI介面")
return true;
}
});
}
let obj = {
a: 'a',
gf: {
b: 'b',
f:{
c: 'c',
s: {
d:'d'
}
}
}
};
/*
let state = shallowReactive(obj);
state.a = "1";
state.gf.b = "2";
state.gf.f.c = "3";
state.gf.f.s.d = "4";
*/
let state = shallowRef(obj);
state.value = {
a: '1',
gf: {
b: '2',
f:{
c: '3',
s: {
d:'4'
}
}
}
}
生命週期
對於生命週期函式這塊,Vue3.0和2.x是有差異的。Vue3.0中,在 setup 中使用的 hook 名稱和原來生命週期的對應關係:
beforeCreate -> 不需要
created -> 不需要
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeUnmount -> onBeforeUnmount
unmounted -> onUnmounted
errorCaptured -> onErrorCaptured
renderTracked -> onRenderTracked
renderTriggered -> onRenderTriggered
此外,被替換的生命週期函式若要在setup函式中使用須提前從vue中匯入,如:
import { onMounted } from ‘vue’;
Vue Router 的安裝使用
安裝新版的 vue router
npm install vue-[email protected]
// 保證安裝完畢的版本是 4.0.0 以上的
vue-router 新增路由
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import Login from './views/Login.vue'
const routerHistory = createWebHistory()
const router = createRouter({
history: routerHistory,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: Login
}
]
})
使用 vue-router 獲取引數和跳轉路由
import { useRoute } from 'vue-router'
// 它是一個函式,呼叫後可以返回對應的物件。
const route = useRoute()
// 我們返回出去,在頁面中把它全部顯示出來看看
return {
route
}
// 對於一個object,如果我們想再頁面顯示它的全部內容,除了在 js 中使用 console,也可以使用 pre 標籤包裹這個變數。
// pre 標籤可定義預格式化的文字。在pre元素中的文字會保留空格和換行符。文字顯現為等寬字型
<pre>{{route}}</pre>
// 替換 URL 為比較豐富的地址
http://localhost:8080/column?abc=foo#123
router-link 元件跳轉的方式
我們第一種方法可以將 to 改成不是字串型別,而是 object 型別,這個object 應該有你要前往route 的 name ,還有對應的 params。
:to="{ name: 'column', params: { id: column.id }}"
第二種格式,我們可以在裡面傳遞一個模版字串,這裡面把 column.id 填充進去就好。
:to="`/column/${column.id}`"
使用 useRouter 鉤子函式進行跳轉
const router = useRouter()
// 特別注意這個是 useRouter 而不是 useRoute,差一個字母,作用千差萬別,那個是獲得路由資訊,這個是定義路由的一系列行為。在這裡,我們可以掉用
router.push('/login')
// router.push 方法跳轉到另外一個 url,它接受的引數和 router-link 的 to 裡面的引數是完全一致的,其實router link 內部和這個 router 分享的是一段程式碼,可謂是殊途同歸了。
新增導航守衛
vue-router 導航守衛文件 : https://router.vuejs.org/zh/guide/advanced/navigation-guards.html.
router.beforeEach((to, from, next) => {
if (to.name !== 'login' && !store.state.user.isLogin) {
next({ name: 'login' })
} else {
next()
}
})
新增元資訊完成許可權管理
vue-router 元資訊文件 : https://router.vuejs.org/zh/guide/advanced/meta.html.
新增元資訊:
{
path: '/login',
name: 'login',
component: Login,
meta: { redirectAlreadyLogin: true }
},
{
path: '/create',
name: 'create',
component: CreatePost,
meta: { requiredLogin: true }
},
更新路由守衛
router.beforeEach((to, from, next) => {
console.log(to.meta)
if (to.meta.requiredLogin && !store.state.user.isLogin) {
next({ name: 'login' })
} else if (to.meta.redirectAlreadyLogin && store.state.user.isLogin) {
next('/')
} else {
next()
}
})
Vuex 的安裝使用
新版 Vuex 安裝
npm install [email protected] --save
// 保證安裝完畢的版本是 4.0.0 以上的
測試 Vuex store
import { createStore } from 'vuex'
// 從 vuex 匯入 createStore 這個函式,我們發現 vue3 以後,這些第三方的官方庫,名字出奇的相似,vue-router 也是以create 開頭的,看起來非常的清楚。
const store = createStore({
state: {
count: 0
},
})
// createStore 接受一個物件作為引數,這些物件中包含了 vuex 的核型概念,第一個概念稱之為 state,這裡麵包含的是我們想放入的在全域性共享的資料,這裡我們放入一個簡單的 count。
// 現在我們已經可以直接訪問這個值了,我們可以直接使用 store.state.count 來訪問它。
console.log('store', store.state.count)
// 接下來我們來更改狀態,更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字串的 事件型別 (type) 和 一個 回撥函式 (handler)。這個回撥函式就是我們實際進行狀態更改的地方,並且它會接受 state 作為第一個引數:
mutations: {
add (state) {
state.count++
}
}
// 有了 mutations 以後,讓我們來觸發它,要喚醒一個 mutation handler,你需要以相應的 type 呼叫 store.commit 方法:
store.commit('add')
console.log('count', store.state.count)
Vuex 整合當前應用
定義 store 檔案
import { createStore } from 'vuex'
import { testData, testPosts, ColumnProps, PostProps } from './testData'
interface UserProps {
isLogin: boolean;
name?: string;
id?: number;
}
export interface GlobalDataProps {
columns: ColumnProps[];
posts: PostProps[];
user: UserProps;
}
const store = createStore<GlobalDataProps>({
state: {
columns: testData,
posts: testPosts,
user: { isLogin: false }
},
mutations: {
login(state) {
state.user = { ...state.user, isLogin: true, name: 'viking' }
}
}
})
export default store
使用
import { useStore } from 'vuex'
import { GlobalDataProps } from '../store'
...
const store = useStore<GlobalDataProps>()
const list = computed(() => store.state.columns)
Vuex getters
vuex getters 文件 :https://vuex.vuejs.org/zh/guide/getters.html.
Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被快取起來,且只有當它的依賴值發生了改變才會被重新計算。
getters: {
biggerColumnsLen(state) {
return state.columns.filter(c => c.id > 2).length
}
}
// 定義完畢,就可以在應用中使用這個 getter 了
// Getter 會暴露為 store.getters 物件,你可以以屬性的形式訪問這些值:
const biggerColumnsLen =computed(()=>store.getters.biggerColumnsLen)
getColumnById: (state) => (id: number) => {
return state.columns.find(c => c.id === id)
},
getPostsByCid: (state) => (id: number) => {
return state.posts.filter(post => post.columnId === id)
}
// 定義完畢以後就可以在應用中使用 getter 快速的拿到這兩個值了
const column = computed(() => store.getters.getColumnById(currentId))
const list = computed(() => store.getters.getPostsByCid(currentId))