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))