vue3学习
1. Vue3.0是如何变快的?
- diff算法优化: https://vue-next-template-explorer.netlify.app/
+ Vue2中的虚拟dom是进行全量的对比
+ Vue3新增了静态标记(PatchFlag),
在与上次虚拟节点进行对比时候,只对比带有patch flag的节点
并且可以通过flag的信息得知当前节点要对比的具体内容
- hoistStatic 静态提升
+ Vue2中无论元素是否参与更新, 每次都会重新创建, 然后再渲染
+ Vue3中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可
- cacheHandlers 事件侦听器缓存
+ 默认情况下onClick会被视为动态绑定, 所以每次都会去追踪它的变化
但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可
- ssr渲染
+ 当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面,
即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。
+ 当静态内容大到一定量级时候,会用_createStaticVNode方法在客户端去生成一个static node,
这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
```javascript
// 附录: PatchFlags
export const enum PatchFlags {
TEXT = 1,// 动态文本节点
CLASS = 1 << 1, // 2 // 动态 class
STYLE = 1 << 2, // 4 // 动态 style
PROPS = 1 << 3, // 8 // 动态属性,但不包含类名和样式
FULL_PROPS = 1 << 4, // 16 // 具有动态 key 属性,当 key 改变时,需要进行完整的 diff 比较。
HYDRATE_EVENTS = 1 << 5, // 32 // 带有监听事件的节点
STABLE_FRAGMENT = 1 << 6, // 64 // 一个不会改变子节点顺序的 fragment
KEYED_FRAGMENT = 1 << 7, // 128 // 带有 key 属性的 fragment 或部分子字节有 key
UNKEYED_FRAGMENT = 1 << 8, // 256 // 子节点没有 key 的 fragment
NEED_PATCH = 1 << 9, // 512 // 一个节点只会进行非 props 比较
DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot
HOISTED = -1, // 静态节点
// 指示在 diff 过程应该要退出优化模式
BAIL = -2
}
```
2. 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:更先进的组件
3. setup 函数(只能同步,不能异步)
1. setup执行时机
- beforeCreate: 表示组件刚刚被创建出来, 组件的data和methods还没有初始化好
- setup
- Created : 表示组件刚刚被创建出来, 并且组件的data和methods已经初始化好
2. setup注意点
- 由于在执行setup函数的时候, 还没有执行Created生命周期方法
所以在setup函数中,是无法使用data和methods
- 由于我们不能在setup函数中使用data和methods,
所以Vue为了避免我们错误的使用, 它直接将setup函数中this修改成了undefined
- setup函数只能是同步的不能是异步的
4. reactive
```javascript
<template>
<div>
<p>{{state.time}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.什么是reactive?
- reactive是Vue3中提供的实现响应式数据的方法
- 在Vue2中响应式数据是通过defineProperty来实现的
而在Vue3中响应式数据是通过ES6的Proxy来实现的
2.reactive注意点:
- reactive参数必须是对象(json/arr)
- 如果给reactive传递了其它对象
+ 默认情况下修改对象, 界面不会自动更新
+ 如果想更新, 可以通过重新赋值的方式
* */
import {reactive} from 'vue';
export default {
name: 'App',
setup() {
// 创建一个响应式数据
// 本质: 就是将传入的数据包装成一个Proxy对象
// let state = reactive(123);
// let state = reactive({
// age: 123
// });
// let state = reactive([1, 3, 5]);
let state = reactive({
time: new Date()
});
function myFn() {
// state = 666; // 由于在创建响应式数据的时候传递的不是一个对象, 所以无法实现响应式
// state.age = 666;
// state[0] = 666;
// 直接修改以前的, 界面不会更新
// 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>
```
5. ref
```javascript
<template>
<div>
<!-- <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(xx) -> reactive({value:xx})
3.ref注意点:
- 在Vue中使用ref的值不用通过value获取
- 在JS中使用ref的值必须通过value获取
* */
// import {reactive} from 'vue';
import {ref} from 'vue';
export default {
name: 'App',
setup() {
// let state = reactive({
// age: 18
// })
/*
ref本质:
ref本质其实还是reactive
当我们给ref函数传递一个值之后, ref函数底层会自动将ref转换成reactive
ref(18) -> reactive({value: 18})
* */
let age = ref(18);
function myFn() {
// state.age = 666;
// age = 666;
age.value = 666;
console.log(age);
}
return {age, myFn}
}
}
</script>
```
6. reactive 和 ref 区别
```javascript
<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>{{age}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.ref和reactive区别
- 如果是reactive在template中不会自动添加.value
2.reactive为什么不会自动添加.value?
- 因为Vue在添加的时候首先会判断当前数据类型是ref还是reactive
3.Vue如何判断?
通过包装后的是有属性
__v-isRef
4.我们如何判断数据到底是ref还是reactive?
通过isRef / isReactive 方法
* */
import {isRef, isReactive} from 'vue';
import {reactive} from 'vue';
// import {ref} from 'vue';
export default {
name: 'App',
setup() {
// ref(18) -> reactive({value: 18})
// let age = ref(18);
let age = reactive({value: 18});
function myFn() {
console.log(isRef(age));
console.log(isReactive(age));
age.value = 666;
}
return {age, myFn}
}
}
</script>
```
7. 递归监听和非递归监听(shallowReactive,shallowRef)
```javascript
<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.递归监听存在的问题
如果数据量比较大, 非常消耗性能
2.非递归监听
shallowRef / shallowReactive
3.如何触发非递归监听属性更新界面?
如果是shallowRef类型数据, 可以通过triggerRef来触发
4.应用场景
一般情况下我们使用 ref和reactive即可
只有在需要监听的数据量比较大的时候, 我们才使用shallowRef/shallowReactive
* */
import {shallowReactive} from 'vue';
import {shallowRef, triggerRef} 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 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);
// state.value = {
// a:'1',
// gf:{
// b:'2',
// f:{
// c:'3',
// s:{
// d:'4'
// }
// }
// }
// }
// state.value.a = '1';
// state.value.gf.b = '2';
// state.value.gf.f.c = '3';
// state.value.gf.f.s.d = '4';
state.value.gf.f.s.d = '4';
// 注意点: Vue3只提供了triggerRef方法, 没有提供triggerReactive方法
// 所以如果是reactive类型的数据, 那么是无法主动触发界面更新的
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>
```
8. toRaw 做一些不想被监听的事情(提升性能)
```javascript
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.toRaw
从Reactive 或 Ref中得到原始数据
2.toRaw作用
做一些不想被监听的事情(提升性能)
* */
import {reactive, toRaw} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name:'lnj', age:18};
/*
ref/reactive数据类型的特点:
每次修改都会被追踪, 都会更新UI界面, 但是这样其实是非常消耗性能的
所以如果我们有一些操作不需要追踪, 不需要更新UI界面, 那么这个时候,
我们就可以通过toRaw方法拿到它的原始数据, 对原始数据进行修改
这样就不会被追踪, 这样就不会更新UI界面, 这样性能就好了
* */
let state = reactive(obj);
let obj2 = toRaw(state);
// 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>
```
9. markRaw 将数据标记为永远不能追踪的数据
```javascript
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.markRaw
将数据标记为永远不能追踪的数据
一般在编写自己的第三方库时使用
* */
import {reactive, markRaw} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name: 'lnj', age: 18};
obj = markRaw(obj);
let state = reactive(obj);
function myFn() {
state.name = 'zs';
}
return {state, myFn}
}
}
</script>
```
10. toRef 和 toRefs
```javascript
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.toRef
创建一个ref类型数据, 并和以前的数据关联
2.toRefs
批量创建ref类型数据, 并和以前数据关联
3.toRef和ref区别
ref-创建出来的数据和以前无关(复制)
toRef-创建出来的数据和以前的有关(引用)
ref-数据变化会自动更新界面
toRef-数据变化不会自动更新界面
* */
import {ref, toRef, toRefs} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name:'lnj', age:18};
// let name = toRef(obj, 'name');
// let age = toRef(obj, 'age');
let state = toRefs(obj);
function myFn() {
// name.value = 'zs';
// age.value = 666;
state.name.value = 'zs';
state.age.value = 666;
console.log(obj);
console.log(state);
// console.log(name);
// console.log(age);
}
return {state, myFn}
}
}
</script>
```
11. customRef 返回一个ref对象,可以显式地控制依赖追踪和触发响应
```javascript
<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); // reactive({value: 18})
let age = myRef(18);
function myFn() {
age.value += 1;
}
return {age, myFn}
}
}
</script>
```
```javascript
<template>
<ul>
<li v-for="item in state" :key="item.id">{{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((err)=>{
console.log(err);
})
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函数: 只能是一个同步的函数, 不能是一个异步的函数
setup() {
/*
let state = ref([]);
fetch('../public/data.json')
.then((res)=>{
return res.json();
})
.then((data)=>{
console.log(data);
state.value = data;
})
.catch((err)=>{
console.log(err);
})
*/
let state = myRef('../public/data.json');
return {state};
}
}
</script>
```
12. ref 获取dom元素
```javascript
<template>
<div ref="box">我是div</div>
</template>
<script>
/*
1.获取元素
在Vue2.x中我们可以通过给元素添加ref='xxx',
然后再代码中通过refs.xxx的方式来获取元素
在Vue3.x中我们也可以通过ref来获取元素
* */
import {ref, onMounted} from 'vue';
export default {
name: 'App',
/*
beforeCreate
setup
Created
* */
setup() {
// console.log(this.$refs.box);
let box = ref(null); // reactive({value: null})
onMounted(()=>{
console.log('onMounted',box.value);
});
console.log(box.value); // null
return {box};
}
}
</script>
```
13. readonly
```javascript
<template>
<div>
<p>{{state.name}}</p>
<p>{{state.attr.age}}</p>
<p>{{state.attr.height}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.readonly
- 只读数据
2.readonly和const
- const 赋值保护
- readonly 递归保护
3.isReadonly
- 判断是否是readonly
4.shallowReadonly
- 非递归保护
* */
import {readonly, isReadonly, shallowReadonly} from 'vue'
export default {
name: 'App',
setup() {
// readonly:用于创建一个只读的数据, 并且是递归只读
let state = readonly({name:'lnj', attr:{age:18, height: 1.88}});
// shallowReadonly: 用于创建一个只读的数据, 但是不是递归只读的
// let state = shallowReadonly({name:'lnj', attr:{age:18, height: 1.88}});
// const和readonly区别:
// const: 赋值保护, 不能给变量重新赋值
// readonly: 属性保护, 不能给属性重新赋值
// const value = 123;
const value = {name:'zs', age:123};
function myFn() {
state.name = '知播渔';
state.attr.age = 666;
state.attr.height = 1.66;
console.log(state);
console.log(isReadonly(state));
// value = 456;
// console.log(value);
value.name = 'ls';
value.age = 456;
console.log(value);
}
return {state, myFn};
}
}
</script>
```
13. 响应数据本质
```javascript
/*
1.Proxy注意点
- set方法必须通过返回值告诉Proxy此次操作是否成功
* */
// let obj = {name:'lnj', age:18};
let arr = [1, 3, 5]; // [1, 3, 5, 7]
let state = new Proxy(arr, {
get(obj, key){
console.log(obj, key); // [ 1, 3, 5 ] 1
return obj[key];
},
set(obj, key, value){
// [ 1, 3, 5 ] 3 7
// [ 1, 3, 5, 7 ] length 4
console.log(obj, key, value);
obj[key] = value;
console.log('更新UI界面');
return true;
}
});
// console.log(state[1]);
state.push(7);
```
15. shallowRef 和 shallowReactive实现原理
```javascript
/*
1.shallowReactive, shallowRef
2.shallowReadonly
3.reactive, ref
4.readonly
* */
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('更新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';
// 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
}
}
}
}
```
16. ref 和 reactive 原理
```javascript
/*
1.shallowReactive, shallowRef
2.shallowReadonly
3.reactive, ref
4.readonly
* */
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('更新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';
// 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
}
}
}
}
```
博客描述
vue2到vue3速度有一个显著提升,虚拟dom的重写使其更快
hahha