前段时间学习之后总结的,最近有空码上来,过程中参考了别人的文章,总结错了请指出。
文章目录
诞生缘由
在 Vue2 中,随着功能的增长,复杂组件的代码变得越来越难以理解和维护。为解决这个问题,在vue3中引入了Composition API也称为组合式API,用于提取和重用多个组件之间的逻辑。
vue2.0与vue3.0区别
- 数据的双向绑定。
Vue2.0使用Object.defineProperty ,Vue 3.0使用ES6的新特性porxy。- defineProperty性能比proxy差
- defineProperty只能监听单一属性,需要把对象遍历一遍进行监听, proxy可以劫持整个对象。
- 完全ts支持。
3.0用ts重写,可使用ts强大的类型系统、类型提示。 - 兼容vue2.0。
- 性能快1~2倍。
- diff算法优化:引入了静态标记。当数据发生改变需要与之前的虚拟节点对比时,会通过静态标记进行对比。
- 静态提升:2.0中无论元素是否参与更新,每次渲染时都会重建,3.0中使用了静态提升,对于不参与更新的元素,只会被创建一次,在渲染时直接复用。
- porxy的使用。
- 更先进的组件。
例如fragment组件更简单,有一个虚拟的父级,不再限制最外层只有一个节点。 - 组合式API-Composition API。
类似React的Hooks。
2.0的Options API代码组织不灵活:
在 props 里面设置接收参数。
在 data 里面设置变量。
在 computed 里面设置计算属性。
在 watch 里面设置监听属性。
在 methods 里面设置事件方法。
一、setup
简介
- setup函数是一个新的组件选项。作为在组件内使用Composition API的入口点。
调用时机
- 创建组件实例,然后初始化props,然后调用setup函数。
- 从生命周期钩子的视角来看,它会在beforeCreate钩子之前被调用。
使用
- 如果setup返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文。
- 如果setup返回一个函数,也可以使用其中的响应式数据。
<template>
<div>
<p>{{ count }}</p>
</div>
</template>
<script>
export default {
name: "App",
props: {
msg: String,
default: () => ""
},
setup() {
const count = 0;
return { count };
//return () => h("div", [count.value]);
}
};
</script>
注意:
- 不用ref来定义count的初始值,在使用过程中无法响应式操作。
- setup()中this是undefined。
<template>
<div>
<p>{{ count }}</p>
<button @click="addCount">增加</button>
</div>
</template>
<script>
export default {
name: "App",
setup() {
let count = 0;
function addCount() {
console.log(count);
count += 1;
}
return { count, addCount };
}
};
</script>
二、ref
简介
- ref()函数用来根据给定的值创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个对象上只包含一个value属性。
使用
- 作为响应式数据对象。
<template>
<div>{{ count }}</div>
<button @click="addCount">增加</button>
</template>
<script>
import { ref } from "vue";
export default {
name: "App",
setup() {
const count = ref(0);
function addCount() {
console.log(count.value);
count.value += 1;
}
return { count, addCount };
}
};
</script>
- 调用原生DOM
<template>
<div ref="divBox">ref的调用用法</div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
name: "HelloWorld",
setup() {
const divBox = ref(null);
onMounted(() => {
const dom = divBox.value;
console.log("dom", dom);
console.log("parentElement", dom.parentElement);
});
return {
divBox
};
}
};
</script>
注意:
- 这里的onMounted与vue2中的mounted是一样的,是vue3的生命周期新用法。
三、reactive
简介
- reactive函数和ref作用非常接近,但是它的参数必须是一个对象。
使用
<template>
<div>{{ state.age }}</div>
<button @click="change">改变</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "HelloWorld",
setup() {
const state = reactive({
age: 18
});
function change() {
state.age = 20;
}
return {
state,
change
};
}
};
</script>
注意:
- reactive中传递的参数必须是json对象或者数组,如果传递了其他对象(比如new Date()),在默认情况下修改对象,界面不会自动更新,如果也需要具有响应式,可以通过重新赋值的方式实现。
不会改变的情况:
<template>
<div>{{ state.time }}</div>
<button @click="change">改变</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "HelloWorld",
setup() {
const state = reactive({
time: new Date()
});
function change() {
state.time.setDate(state.time.getDate() + 1);
console.log(state.time);
}
return {
state,
change
};
}
};
</script>
会改变的情况:
<template>
<div>
<p>{{ state.time }}</p>
<button @click="change">改变</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "HelloWorld",
setup() {
const state = reactive({
time: new Date()
});
function change() {
const newTime = new Date(state.time);
newTime.setDate(state.time.getDate() + 1);
state.time = newTime;
console.log(state.time);
}
return { state, change };
}
};
</script>
四、ref与reactive认识
-
ref本质是reactive,当我们给ref传递一个值后,ref函数底层会自动把ref转换成reactive,例如:
ref(18) -> reactive({value: 18})
-
在template模板里面,ref会自动加上.value, 但是如何判断的呢?
- 打印ref对象会发现,ref对象里面有一个私有属性__v_isRef: true,有这个属性就自动添加.value。
- 我们可以用isRef()、isReactive()来判断是什么类型。
初始化代码:
<template> <div> <p>{{ count }}</p> <button @click="addRef">加ref</button> <p>{{ state.age }}</p> <button @click="addReactive">加reactive</button> </div> </template> <script> import { ref, reactive } from "vue"; export default { name: "HelloWorld", setup() { const count = ref(0); function addRef() { count.value++; console.log("count", count); } const state = reactive({ age: 18 }); function addReactive() { state.age++; console.log("state", state); } return { count, addRef, state, addReactive }; } }; </script>
代码提取:
<template> <div> <p>{{ count }}</p> <button @click="addRef">加ref</button> <p>{{ state.age }}</p> <button @click="addReactive">加reactive</button> </div> </template> <script> import { ref, reactive } from "vue"; function addRef_() { const count = ref(0); function addRef() { count.value++; console.log("count", count); } return { count, addRef }; } function addReactive_() { const state = reactive({ age: 18 }); function addReactive() { state.age++; console.log("state", state); } return { state, addReactive }; } export default { name: "HelloWorld", setup() { const { count, addRef } = addRef_(); const { state, addReactive } = addReactive_(); return { count, addRef, state, addReactive }; } }; </script>
isRef()、isReactive()的使用:
<template> <div> <p>{{ count }}</p> <button @click="addRef">加ref</button> <p>{{ state.age }}</p> <button @click="addReactive">加reactive</button> </div> </template> <script> import { ref, reactive, isRef, isReactive } from "vue"; function addRef_() { const count = ref(0); function addRef() { count.value++; console.log("isRef", isRef(count)); console.log("isReactive", isReactive(count)); } return { count, addRef }; } function addReactive_() { const state = reactive({ age: 18 }); function addReactive() { state.age++; console.log("isReactive", isReactive(state)); console.log("isRef", isRef(state)); } return { state, addReactive }; } export default { name: "HelloWorld", setup() { const { count, addRef } = addRef_(); const { state, addReactive } = addReactive_(); return { count, addRef, state, addReactive }; } }; </script>
五、递归监听
- 默认情况下,无论ref还是reactive都是递归监听。
验证reactive:
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.a1.b }}</p>
<p>{{ state.a1.b1.c }}</p>
<button @click="change">改变</button>
</div>
</template>
<script>
import { reactive } from "vue";
function changeReactive_() {
const state = reactive({
a: "a",
a1: {
b: "b",
b1: {
c: "c"
}
}
});
function change() {
state.a = "1";
state.a1.b = "2";
state.a1.b1.c = "3";
}
return { state, change };
}
export default {
name: "HelloWorld",
setup() {
const { state, change } = changeReactive_();
return { state, change };
}
};
</script>
验证ref:
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.a1.b }}</p>
<p>{{ state.a1.b1.c }}</p>
<button @click="change">改变</button>
</div>
</template>
<script>
import { ref } from "vue";
function changeRef_() {
const state = ref({
a: "a",
a1: {
b: "b",
b1: {
c: "c"
}
}
});
function change() {
state.value.a = "1";
state.value.a1.b = "2";
state.value.a1.b1.c = "3";
}
return { state, change };
}
export default {
name: "HelloWorld",
setup() {
const { state, change } = changeRef_();
return { state, change };
}
};
</script>
六、非递归监听
-
shallowReactive()
<template> <div> <p>{{ state.a }}</p> <p>{{ state.a1.b }}</p> <p>{{ state.a1.b1.c }}</p> <button @click="change">改变</button> </div> </template> <script> import { shallowReactive } from "vue"; function changeReactive_() { const state = shallowReactive({ a: "a", a1: { b: "b", b1: { c: "c" } } }); function change() { state.a = "1"; state.a1.b = "2"; state.a1.b1.c = "3"; console.log(state); console.log(state.a1); console.log(state.a1.b1); } return { state, change }; } export default { name: "HelloWorld", setup() { const { state, change } = changeReactive_(); return { state, change }; } }; </script>
-
shallowRef()
- 不可递归:
<template> <div> <p>{{ state.a }}</p> <p>{{ state.a1.b }}</p> <p>{{ state.a1.b1.c }}</p> <button @click="change">改变</button> </div> </template> <script> import { shallowRef } from "vue"; function changeReactive_() { const state = shallowRef({ a: "a", a1: { b: "b", b1: { c: "c" } } }); function change() { state.value.a = "1"; state.value.a1.b = "2"; state.value.a1.b1.c = "3"; console.log(state); console.log(state.value.a1); console.log(state.value.a1.b1); } return { state, change }; } export default { name: "HelloWorld", setup() { const { state, change } = changeReactive_(); return { state, change }; } }; </script>
-
可以递归,但有缺陷:只能这样改,无法指定修改
<template> <div> <p>{{ state.a }}</p> <p>{{ state.a1.b }}</p> <p>{{ state.a1.b1.c }}</p> <button @click="change">改变</button> </div> </template> <script> import { shallowRef } from "vue"; function changeReactive_() { const state = shallowRef({ a: "a", a1: { b: "b", b1: { c: "c" } } }); function change() { state.value = { a: "1", a1: { b: "2", b1: { c: "3" } } }; console.log(state); console.log(state.value.a1); console.log(state.value.a1.b1); } return { state, change }; } export default { name: "HelloWorld", setup() { const { state, change } = changeReactive_(); return { state, change }; } }; </script>
-
强制修改:
<template> <div> <p>{{ state.a }}</p> <p>{{ state.a1.b }}</p> <p>{{ state.a1.b1.c }}</p> <button @click="change">改变</button> </div> </template> <script> import { shallowRef, triggerRef } from "vue"; function changeReactive_() { const state = shallowRef({ a: "a", a1: { b: "b", b1: { c: "c" } } }); function change() { state.value.a1.b1.c = "3"; triggerRef(state); /* state.value.a = "1"; state.value.a1.b = "2"; state.value.a1.b1.c = "3"; */ console.log(state); console.log(state.value.a1); console.log(state.value.a1.b1); } return { state, change }; } export default { name: "HelloWorld", setup() { const { state, change } = changeReactive_(); return { state, change }; } }; </script>
注意:
- vue3只提供了triggerRef(), 没有提供triggerReactive(),所以使用shallowReactive()无法主动更新界面。
- shallowRef的本质:
1. ref(18) -> reactive({value: 18}) 2. shallowRef(18) -> shallowReactive({value: 18})
shallowRef、shallowReactive的理解
- 在vue2.x中使用defineProperty来实现响应式数据的,在vue3.x中是通过proxy来实现。
- 以下代码我是在单独js文件中编写, 编辑器内运行的。
- 非源码,实现过程只是为了理解方法的原理
proxy简单实现:
const obj = {
name: "老大",
age: 20
};
const state = new Proxy(obj, {
get(obj, key) {
console.log("获取", obj[key]);
return obj[key];
},
set(obj, key, value) {
obj[key] = value;
console.log("赋值", obj);
return true;
}
});
state.name = "老小";
shallowRef()、shallowReactive() 简单实现:
function shallowReactive(obj) {
return new Proxy(obj, {
get(obj, key) {
return obj[key];
},
set(obj, key, value) {
obj[key] = value;
console.log(obj);
console.log("更新UI");
return true;
}
});
}
function shallowRef(val) {
return shallowReactive({
value: val
});
}
const obj = {
a: "a",
a1: {
b: "b",
b1: {
c: "c"
}
}
};
/* const state = shallowReactive(obj);
state.a = "1";
state.a1.b = "2";
state.a1.b1.c = "3"; */
const state = shallowRef(obj);
state.value = {
a: "1",
a1: {
b: "2",
b1: {
c: "3"
}
}
};
ref与reactive响应式理解
// let obj = {name: "老大", age: 20}
/* let arr = [
1,
{
name: "老大",
age: 20
}
]; */
function reactive(obj) {
if (typeof obj === "object") {
if (obj instanceof Array) {
// 如果是一个数组,那么每个元素是不是对象,是的话咱们是不是需要再把他包装成proxy
obj.forEach((item, index) => {
if (typeof item === "object") {
obj[index] = reactive(item);
}
});
} else {
// 如果是对象,那么没个属性是不是对象,是的话是不是要把它包装成proxy
for (let key in obj) {
const item = obj[key];
if (typeof item === "object") {
obj[key] = reactive(item);
}
}
}
return new Proxy(obj, {
get(obj, key) {
return obj[key];
},
set(obj, key, value) {
obj[key] = value;
// console.log(obj);
console.log("更新UI");
return true;
},
});
} else {
console.log(`${obj}: 不是对象`);
}
}
function ref(val) {
return reactive({
value: val
});
}
const obj = {
a: "a",
a1: {
b: "b",
b1: {
c: "c"
}
}
};
/* const state = reactive(obj);
state.a = "1";
state.a1.b = "2";
state.a1.b1.c = "3"; */
const arr = [
{
name: "老大",
age: 20,
},
{
name: "老二",
age: 18,
},
];
const state = reactive(arr);
state.push({
name: "健康",
age: 1000,
});
console.log(state.length);
放到vue项目中使用:
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.a1.b }}</p>
<p>{{ state.a1.b1.c }}</p>
<button @click="change">改变</button>
</div>
</template>
<script>
function reactive(obj) {
if (typeof obj === "object") {
if (obj instanceof Array) {
// 如果是一个数组,那么每个元素是不是对象,是的话咱们是不是需要再把他包装成proxy
obj.forEach((item, index) => {
if (typeof item === "object") {
obj[index] = reactive(item);
}
});
} else {
// 如果是对象,那么没个属性是不是对象,是的话是不是要把它包装成proxy
for (const key in obj) {
const item = obj[key];
if (typeof item === "object") {
obj[key] = reactive(item);
}
}
}
return new Proxy(obj, {
get(obj, key) {
return obj[key];
},
set(obj, key, value) {
obj[key] = value;
// console.log(obj);
console.log("更新UI");
return true;
}
});
} else {
console.log(`${obj}: 不是对象`);
}
}
function ref(val) {
return reactive({
value: val
});
}
function changeReactive_() {
const state = reactive({
a: "a",
a1: {
b: "b",
b1: {
c: "c"
}
}
});
function change() {
state.a = "1";
state.a1.b = "2";
state.a1.b1.c = "3";
console.log(state);
console.log(state.a1);
console.log(state.a1.b1);
}
return { state, change };
}
export default {
name: "HelloWorld",
setup() {
const { state, change } = changeReactive_();
return { state, change };
}
};
</script>