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的重写使其更快
11
hahha