1. Vue3.0六大亮点
特性 | 解析 |
---|---|
Performance | 性能比Vue 2.x快1.2~2倍 |
Tree shaking support | 按需编译,体积比Vue2.x更小, Composition API:组合API(类似React Hooks) |
Better TypeScript support | 更好的 Ts支持 |
Custom Renderer API | 暴露了自定义渲染API |
Fragment,Teleport (Protal),Suspense | 更先进的组件 |
注:具体可以参考github中Vue3.0的相关源文件https://github.com/vuejs/vue-next/tree/master/packages
2. Vue3.0是如何变快的?
- diff方法优化:
- Vue2中的虚拟dom是进行全量的对比
- Vue3新增了静态标记(PatchFlag),
在与上次虚拟节点进行对比时候,只对比带有patch flag的节点
并且可以通过flag的信息得知当前节点要对比的具体内容
例子:
<div>Hello World!</div>
<div>你好呀</div>
<div>{{msg}}</div>
import { createVNode as _createVNode, toDisplayString as _toDisplayString, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode("div", null, "Hello World!"),
_createVNode("div", null, "你好呀"),
_createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
], 64 /* STABLE_FRAGMENT */))
}
// //编译网址---> https://vue-next-template-explorer.netlify.app/
标记查询列表
TEXT = 1,// --取值是1---表示具有动态textContent的元素
CLASS = 1 << 1, // --取值是2---表示有动态Class的元素
STYLE = 1 << 2, // --取值是4---表示动态样式(静态如style="color: pink",也会提升至动态)
PROPS = 1 << 3, // --取值是8--- 表示具有非类/样式动态道具的元素。
FULL_PROPS = 1 << 4, // --取值是16---表示带有动态键的道具的元素,与上面三种相斥
HYDRATE_EVENTS = 1 << 5, // --取值是32---表示带有事件监听器的元素
STABLE_FRAGMENT = 1 << 6, // --取值是64---表示其子顺序不变,不会改变自顺序的片段。
KEYED_FRAGMENT = 1 << 7, // --取值是128---表示带有键控或部分键控子元素的片段。
UNKEYED_FRAGMENT = 1 << 8, // --取值是256---子节点无key绑定的片段(fragment)
NEED_PATCH = 1 << 9, // --取值是512---表示只需要非属性补丁的元素,例如ref或hooks
DYNAMIC_SLOTS = 1 << 10, // --取值是1024---表示具有动态插槽的元素
- hoistStatic静态提升
- Vue2中无论元素是否参与更新,每次都会重新创建
- Vue3中对于不参与更新的元素,只会被创建一次,之后会在每次渲染时候被不停的复用即可
静态提升之后
import { createVNode as _createVNode, toDisplayString as _toDisplayString, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "Hello World!", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createVNode("div", null, "你好呀", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock(_Fragment, null, [
_hoisted_1,
_hoisted_2,
_createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
], 64 /* STABLE_FRAGMENT */))
}
- cacheHandlers事件侦听器缓存
- 默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化
但因为是同一个函数,所以没有追踪BIANHUA1,直接缓存起来复用即可
- 默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化
<button @click="onClick"></button>
开启事件监听缓存之前
import { openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("button", { onClick: _ctx.onClick }, null, 8 /* PROPS */, ["onClick"]))
}
开启事件监听缓存之后
import { openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("button", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick && _ctx.onClick(...args)))
}))
}
- ssr渲染
- 当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面,即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。
- 当静态内容大到一定量级时候,会用_createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
组合api
<template>
<div id="nav">
<div
@click="remStu(index)"
v-for="(item, index) in state.stus"
:key="item.key"
>
{{ item.name }}-{{ item.age }}
</div>
</div>
</template>
<script>
import { reactive, ref } from "vue";
export default {
name: "App",
setup() {
let { state, remStu } = useRemoveStudent();
return { state, remStu };
},
};
function useRemoveStudent() {
let state = reactive({
stus: [
{ id: 1, name: "zs", age: 10 },
{ id: 1, name: "ls", age: 20 },
{ id: 1, name: "ws", age: 30 },
],
});
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx !== index);
}
return { state, remStu };
}
</script>
- composition API 和 Option API混合使用
- CompositionAPI 本质(组合API/注入API)
- setup执行时机
beforCreate 组件刚刚创建出来,组件的data和methods没有初始化好
setup
Created 组件刚刚被创建出来,并且组件的data和methods已经初始化好了 - setup注意点
- 由于在执行setup函数的时候,还没有执行Created生命周期方法
所以在setup中不能使用data和methods - 由于不能再setup中使用data和methods
所以Vue为了避免我们错误的使用,它直接将setup函数中this修改成了undefined - setup只能是同步的不能是异步的
reactive
- 什么是reactive?
- reactive是V娿中提供的视线响应式数据的方法
- 在vue2.0中响应式数据是通过
defineProperty
来实现的 - 而在V3.0中响应式数据时通过ES6的
Proxy
来实现的
- reactive注意点;
- reactive参数必须是对象(
json/arr
) - 如果给reactive传递了其他对象
- 默认情况下修改对象,界面不会更新
- 如果想更新,可以通过重新赋值的方式
<template>
<div id="nav">
<div>
{{ state }}
</div>
<div>
{{ state1 }}
</div>
<button @click="myFun">按钮</button>
<button @click="myFun1">按钮1</button>
</div>
</template>
<script>
import { reactive, ref } from "vue";
export default {
name: "App",
setup() {
let state = reactive({ name: "zpp", age: 12 });
let state1 = reactive({ time: new Date() });
function myFun() {
state.age = 18;
}
function myFun1() {
state1.time.setDate(state1.time.getDate() + 1);
console.log(state1);
}
return { state, myFun, myFun1, state1 };
},
};
</script>
ref
- 什么是ref?
- ref和reactive一样,也是用来实现响应式数据的方法
- 由于reactive必须传递一个对象,所以导致在企业开发者如果我们只想让某个变量实现响应式的时候会非常麻烦,所以vue3就提供了ref方法,实现简单值的监听
- ref本质
- ref底层本质其实还是reactive
- 系统会自动根据我们传入的值将它转化成
ref(xx) ---> reactive({value:xx})
- ref注意点:
- 在vue中使用ref的值不通过value获取
- 在js中使用ref的值必须通过value获取
ref和reactive的区别
-
ref和reactive的区别
如果在template里使用的是ref类型的数据,那么Vue会自动帮我们添加.value
如果在template里使用的是reactive类型的数据,那么Vue不会自动帮我们添加.value
-
Vue是如何决定是否需要自动添加.value的
Vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是就自动添加.value,如果不是就不自动添加.value -
Vue是如何判断当前的数据是否是ref类型的
通过当前数据的__v_ref来判断的
如果有这个私有的属性,并且取值为true,那么就代表是一个ref类型的数据
vue中提供了判断方法
import {isRef,isReactive} from 'vue'
let age = ref(18);
console.log(isRef(age)); // 判断是否是ref数据类型
console.log(isReactive); // 判断是否是reactive数据类型
递归监听
- 递归监听
默认情况下,无论是通过ref还是reactive都是递归监听 - 递归监听存在的问题
如果数据量较大,非常消耗能量 - 非递归监听
shallowReactive, shallowRef
<template>
<div id="nav">
<div>
{{ state.a }}
{{ state.gf.b }}
{{ state.gf.f.c }}
{{ state.gf.f.s.d }}
</div>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { reactive, ref } from "vue";
import { shallowReactive, shallowRef } from "vue";
export default {
name: "App",
setup() {
// let state = shallowReactive({
let state = shallowRef({
a: "a",
gf: {
b: "b",
f: {
c: "c",
s: {
d: "d",
},
},
},
});
// function myFun() {
// // 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);
// }
function myFun() {
// 注意点:如果是通过shallRef创建数据
// 那么Vue监听的是.value的变化,并不是第一层的变化
// state.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",
},
},
},
};
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, myFun };
},
};
</script>
shallowReactive
shallowRef
注意点:如果是通过shallowRef创建数据
那么Vue监听的是.value的变化,并不是第一层的变化
调用triggerRef
可以主动更新视图,triggerRef(state)
注意点:Vue3只提供了triggerRef
方法,没有提供triggerReactive
方法
所以如果是reactive类型的数据,那么是无法主动触发界面更新的
- 如何触发非递归数据监听属性更新界面
如果是shallRef
类型数据,可以通过triggerRef
来触发 - 应用场景
一般情况下我们使用ref和reactive即可
只有在需要监听大量数据时,我们才使用shallowReactive, shallowRef
shallowRef本质
toRow
toRaw
- 从Reactive或Ref中获得到原始数据
- 做一些不想被监听的事情(提升性能)
<script>
import { reactive, ref } from "vue";
export default {
name: "App",
setup() {
let obj = {
name: "zpp",
age: 18,
};
let state = reactive(obj);
function myFun() {
console.log(state);
// state.name = "lll";
obj.name = "111";
let obj2 = toRow(state);
console.log(obj === obj2); //true
console.log(state);
}
return { state, myFun };
},
};
</script>
注意点:如果通过toRow
拿到ref类型的原始数据(创建时传入的那个数据)
那么久必须明确的告诉toRow方法,要获取的值是.value
的值
因为经过vue处理后,.value
中保存的才是当初创建时传入的那个原始数据
let state = ref(obj);
let obj2 = toRow(state.value);
console.log(obj);
console.log(state);
markRaw
标记某个数据永远不会追踪
let obj = {name:'lnj',age:18};
obj = markRaw(obj);
let state = reactive(obj);
function myFun(){
state.name = 'lll'; // 界面不会改变
}
toRef
会修改原始数据但是不会更新视图
let obj2 = {
name: "zpp",
age: 18,
};
let state2 = toRef(obj2, "name");
function myFun2() {
state2.value = "111";
console.log(obj2);
console.log(state2);
}
toRefs
let obj2 = {
name: "zpp",
age: 18,
};
let state2 = toRef(obj2, "name");
let state3 = toRefs(obj2);
function myFun2() {
state2.value = "111";
state3.name.value = "123"; //需要通过,.name.value 访问
console.log(obj2);
console.log(state2);
console.log(state3);
}
customRef
track();
// 告诉vue这个数据是需要追踪变化的
trigger();
// 告诉Vue触发界面更新
<template>
<div id="nav">
<h1>ref</h1>
<div>
{{ state }}
</div>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { customRef, reactive, ref, toRef, toRefs } from "vue";
function mRef(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 state = mRef(12);
function myFun() {
state.value += 1;
}
return { state, myFun };
},
};
</script>
不能再get() 方法中发送网络请求
ref获取元素
<template>
<div id="nav">
<div ref="box">nihao</div>
</div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
name: "App",
setup() {
let box = ref(null);
onMounted(() => {
console.log("onMounted", box.value);
});
console.log(box.value);
return { box };
},
};
</script>
readonly
创建一个只读的对象,并且是递归只读
let state = readonly({name:'lnj,attr:{age:18,height:1.88}});
function myFun()
{
state.name = 'zpp';
state.attr,age = 12;
state.attr.height = 1.7;
console.log(state);
}
shallowReadonly
:用于创建一个只读对象,不是递归只读,只有第一层只读;
isReadonly
:判断是否是一个只读对象
const 和 readonly的区别
const :赋值保护,不能给变量重新赋值,不能重新赋值
readonly:属性保护,不能给属性重新赋值,不能重新赋值
v3响应式数据理解
let obj = { name: 'zpp', age: 18 };
let state = new Proxy(obj, {
get(obj, key) {
console.log(obj, key);
return obj[key];
},
set(obj,key,value) {
console.log(obj, key, value);
obj[key] = value;
console.log('页面更新')
}
})
// console.log(state.name) // 输出zpp
state.name = 'lll'
console.log(state);
在set中return true表示更新成功,可以进行下一次的更新
let arr = [1,2,3]
let state = new Proxy(arr, {
get(obj, key) {
console.log(obj, key);
return obj[key];
},
set(obj,key,value) {
console.log(obj, key, value);
obj[key] = value;
console.log('页面更新')
return true;
}
})
// state[1] = 0;
state.push(7);
console.log(state);
shallowRef,shallowReactive理解
function shallowRef(val){
return shallowReactive({value:val})
}
function shallowReactive(obj) {
return new Proxy(obj,{
get(obj,key){
return obj[key];
},
set(obj,key,val){
obj[key] = val;
console.log('更新界面')
return true;
}
})
}
let obj = {
a: "a",
gf: {
b: "b",
f: {
c: "c",
s: {
d: "d",
},
},
},
};
/*
let state = shallowReactive(obj);
// state.a = '1'; //只有第一层触发 set方法
state.gf.b = '2';
state.gf.c = '3';
state.gf.f.s.d = '4';
*/
let state = shallowRef(obj);
state.value = { //只会监听value的变化,因为value才是第一层
a: "1",
gf: {
b: "2",
f: {
c: "3",
s: {
d: "4",
},
},
},
};
state.value.a = '1';
state.value.gf.b = '2';
state.value.gf.c = '3';
state.value.gf.f.s.d = '4';
ref和reactive理解
使用递归的方法逐步递归
readonly和shallowReadonly的理解
let obj = { name: 'zpp', age: 18 };
function shallowReadonly(obj) {
return new Proxy(obj,{
get(obj,key){
return obj[key];
},
set(obj,key,val){
// obj[key] = val;
// console.log('更新界面')
// return true;
console.warn(`${key}是只读的,不能赋值`)
}
})
}
let state = shallowReadonly(obj)
state.name = 'lll';
console.log(state);