Vue3.0理解

一、vue3.0亮点

  1. Performance:性能比Vue 2. x快1.2-2倍
  2. Tree shaking support:按需编译,体积比Vue2. x更小
  3. Composition API:组合API (类似React Hooks)
  4. Better TypeScript support:更好的Ts支持
  5. Custom Renderer API:暴露了自定义道染API
  6. Fragment, Teleport (Protal), Suspense:更先进的组件

 

二、Vue3.0是如何变快的

1.diff算法优化:

  • Vue2中的虚拟dom是进行全量的对比。第一次渲染的时候生成一颗dom树,数据发生改变时又生成一颗dom树,与之前的dom树进行全部比较,哪里不同更新哪里。
  • Vue3新增了静态标记(PatchFlag),在与上次虚拟节点进行对比时候,只对比带有patch flag的节点,并且可以通过flag的信息得知当前节点要对比的具体内容。

 

vue3.0标记: 

vue3.0源码中标记会改变的节点 :

https://vue-next-template-explorer.netlify.app/

 

2.hoistStatic 静态提升

  • Vue2中无论元素是否参与更新,每次都会重新创建,然后再渲染
  • Vue3中对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可

vue3.0源码将静态元素(即不会改变的元素)的创建提取为全局变量,渲染时复用,不会重新创建,性能提升:

 

3.cachellandlers事件侦听器缓存

  • 默认情况下onClick会被视为动态绑定,会添加静态标志,所以每次都会去追踪它的变化,但是因为是同一个函数,所以没有必要追踪变化,直接缓存起来复用即可

未启用侦听器缓存时,被添加了静态标记,在diff算法中会被进行比较、追踪:

启用侦听器缓存,没有添加静态标记,性能提升:

4.ssr渲染

  • 当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面
    即使存在动态的绑定,会通过模板插值嵌入进去,这样会比通过虚拟dmo来渲染的快上很多很多。
    当静态内容大到一定量级时候,会用createStatielode方法在客户增去生成一个static node这些静态node,会被直接innerltml,就不需要创建对象,然后根据对象渲染。

 

三、创建vue3

1.创建vue3的三种方式

  • Vue-Cli
  • Webpack
  • Vite

注意:vue版本应该在4.5.0以上

查看vue 版本号:

vue -V

 

2.Vite

Vite是Vue作者开发的意图取代webpack的工具。其实现原理是利用ES6的import会发送请求去加载文件的特性,拦截这些请求,做一些编译,省去webpack冗长的打包时间。

npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

 

3.vue/cli

//安装
npm i -g @vue/cli
vue create 项目名
cd <project-name>
npm run serve

 

四、组合API

组合API:将当前数据和当前业务逻辑放在一起,方便管理,会自动注入到option API中。

beforeCreate:组件刚被创建出来,组件的data和methods还没初始化好;

created:组件刚被创建出来,组件的data和methods已经初始化好;

setup()是在beforeCreate之前执行的。所以setup()中无法使用data和methods,所以setup()中将this改为undefined。

setup()返回对象中的属性与data函数返回对象的属性合并为组件对象的属性,返回对象的方法与methods中的方法合并为对象方法。如有冲突,setup优先。

setup参数:setup(props,{attrs, slots, emit})

注意:setup()只能是同步的,不能是异步的。想使用异步应使用async await。

 

1.初始化基本类型使用ref

ref本质:通过给属性添加getter/setter来实现对数据的监听。

其实还是reactive,给ref函数传递一个值之后,ref函数底层会自动将ref转换成reactive。ref(1) -> reactive({value:1})

注意:在template中使用ref的值不用太过value获取;在js中使用必须通过value获取。

<template>
  <div>
    <h1>{{count}}</h1>
    <button @click="add">增加</button>
  </div>
</template>

<script>
import {ref} from 'vue';
export default {
  name: 'App',
  // setup函数是组合API的入口函数
  setup() {
    // 定义变量count的value值为1
    let count = ref(1);
    // 在组API中定义的变量/方法
    function add()  {
      // count是一个对象
      console.log(count); // RefImpl {_rawValue: 1, _shallow: false, __v_isRef: true, _value: 1}
      // 改变count的值应该改变的是count的value属性值
      count.value ++ ;
    }
    // 要想在外界使用,必须通过return暴露出去
    return { count,add }
  }
}
</script>

2.监听对象使用reactive

<template>
  <ul>
    <li v-for="(stu, index) in state.stus" :key="stu.id" @click="remStu(index)">{{stu.id}} - {{stu.name}} - {{stu.age}}</li>
  </ul>
</template>

<script>
import { reactive } from 'vue';
export default {
  name: 'HelloWorld',
  setup() {
    const stusWrap = {
      stus: [
        {id: 0, name: 'df0', age: 11},
        {id: 1, name: 'df1', age: 14},
        {id: 2, name: 'df2', age: 15},
      ]
    })
    // 监听对象或数组需要使用reactive
    const state = reactive(stusWrap);
    // 点击移除该项
    function remStu(index) {
      state.stus = state.stus.filter((stu, idx) => idx != index);
    }
    return { state, remStu };
  }
}
</script>

 reactive是Vue3中提供的实现响应式数据的方法,将传入的数据包装成Proxy对象。

  • 在Vueq2中响应式数据是通过defineProperty来实现的。
  • 在Vue3中响应式数据是通过RS6的Proxy来实现的。

stusWrap为目标对象,state为代理对象,

  • 当改变目标对象的值时,界面不更新;
  • 当改变代理对象的值时,界面更新,目标对象也更新

 

注意:reactive参数必须是对象(自定义对象/array)。如果给reactive传递了其他对象,默认情况下修改它,界面不会自动更新;需重新赋值才会更新。

<template>
    <div>
        <h1>{{state.time}}</h1>
        <button @click="myFn">修改</button>
    </div>
</template>
<script>
import { reactive } from 'vue'
export default {
    name: 'ReactiveCom',
    setup() {
        let state = reactive({
            time: new Date()
        });
        function myFn() {
            // time修改自身,界面不会更新
            // state.time.setDate(state.time.getDate() + 1);
            // console.log(state.time);

            // 需要给time重新赋值,界面才会更新
            const newTime = new Date(state.time.getTime());
            newTime.setDate(state.time.getDate() + 1);
            state.time = newTime;
            console.log(state.time);
        }
        return {state, myFn}
    }
}
</script>

 

3.ref和reactive区别

 (1)不同:

  • 在template中若为ref类型的数据,vue会自动添加.value;
  • 若为reactive类型,则不会自动添加.value。

通过当前数据是否有 __v_isRef 这个私有属性来判断,若 __v_isRef 为true,则为ref类型,则会自动添加.value。

我们可以通过isRef() 和 isReactive() 来判断。 

<template>
  <div>
    <h1>{{count}}</h1>
    <button @click="compare">比较</button>
  </div>
</template>

<script>
import { ref, isRef, isReactive } from 'vue';
export default {
  name: 'App',
  setup() {
    let count = ref(1);
    function compare()  {
      console.log(isRef(count)); // true
      console.log(isReactive(count)); // false
    }
    return { count,compare }
  }
}
</script>

 

(2)相同:都是递归监听,数据无论多少层都能监听到,每一层都被封装为Proxy对象。但当数据量大时,非常消耗性能。

ref:

<template>
  <div>
    <h1>{{state.a}}</h1>
    <h1>{{state.f.b}}</h1>
    <h1>{{state.f.t.c}}</h1>
    <h1>{{state.f.t.s.d}}</h1>
    <button @click="myFn">显示</button>
  </div>
</template>

<script>
import { ref } from 'vue';
export default {
  name: 'App',
  setup() {
    let state = ref({
        a: 'a',
        f: {
            b: 'b',
            t: {
                c: 'c',
                s: {
                    d: 'd'
                }
            }
        }
    });
    function myFn()  {
      console.log(state); // RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy}
      console.log(state.value); // Proxy {a: "a", f: {…}}
      console.log(state.value.f); // Proxy {b: "b", t: {…}}
      console.log(state.value.f.t); // Proxy {c: "c", s: {…}}
      console.log(state.value.f.t.s); // Proxy {d: "d"}

      state.value.a = 1;
      state.value.f.b = 2;
      state.value.f.t.c = 3;
      state.value.f.t.s.d = 4;
    }
    return { state, myFn }
  }
}
</script>

reactive: 

<template>
  <div>
    <h1>{{state.a}}</h1>
    <h1>{{state.f.b}}</h1>
    <h1>{{state.f.t.c}}</h1>
    <h1>{{state.f.t.s.d}}</h1>
    <button @click="myFn">显示</button>
  </div>
</template>

<script>
import { reactive } from 'vue';
export default {
  name: 'App',
  setup() {
    let state = reactive({
        a: 'a',
        f: {
            b: 'b',
            t: {
                c: 'c',
                s: {
                    d: 'd'
                }
            }
        }
    });
    function myFn()  {
      console.log(state); // Proxy {a: "a", f: {…}}
      console.log(state.f); // Proxy {b: "b", t: {…}}
      console.log(state.f.t); // Proxy {c: "c", s: {…}}
      console.log(state.f.t.s); // Proxy {d: "d"}

      state.a = 1;
      state.f.b = 2;
      state.f.t.c = 3;
      state.f.t.s.d = 4;
    }
    return { state,myFn }
  }
}
</script>

 

 4.非递归监听

(1)shallowReactive:若更新数据第一层,则这个数据所有层的数据在界面都会更新,若没有更新第一层,则界面不会更新

<template>
  <div>
    <h1>{{state.a}}</h1>
    <h1>{{state.f.b}}</h1>
    <h1>{{state.f.t.c}}</h1>
    <h1>{{state.f.t.s.d}}</h1>
    <button @click="myFn">显示</button>
  </div>
</template>

<script>
import { shallowReactive } from 'vue';
export default {
  name: 'App',
  setup() {
    let state = shallowReactive({
        a: 'a',
        f: {
            b: 'b',
            t: {
                c: 'c',
                s: {
                    d: 'd'
                }
            }
        }
    });
    function myFn()  {
      console.log(state); // Proxy {a: "a", f: {…}}
      console.log(state.f); // {b: "b", t: {…}}
      console.log(state.f.t); // {c: "c", s: {…}}
      console.log(state.f.t.s); // {d: "d"}

      
      state.a = 1; // shallow更新第一层,界面所有数据随之更新
      state.f.b = 2; // 若没有更新第一层,则界面不更新
      state.f.t.c = 3;
      state.f.t.s.d = 4;
    }
    return { state,myFn }
  }
}
</script>

(2)shallowRef:因为shallowRef只监听第一层即state.value的变化

shallowRef(10)的本质是转化成shallowReactive({value: 10}),所以监听的第一层是.value

<template>
  <div>
    <h1>{{state.a}}</h1>
    <h1>{{state.f.b}}</h1>
    <h1>{{state.f.t.c}}</h1>
    <h1>{{state.f.t.s.d}}</h1>
    <button @click="myFn">显示</button>
  </div>
</template>

<script>
import { shallowRef } from 'vue';
export default {
  name: 'App',
  setup() {
    let state = shallowRef({
        a: 'a',
        f: {
            b: 'b',
            t: {
                c: 'c',
                s: {
                    d: 'd'
                }
            }
        }
    });
    function myFn()  {
      console.log(state); // RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy}
      console.log(state.value); // Proxy {a: "a", f: {…}}
      console.log(state.value.f); // Proxy {b: "b", t: {…}}
      console.log(state.value.f.t); // Proxy {c: "c", s: {…}}
      console.log(state.value.f.t.s); // Proxy {d: "d"}

      // 界面不改变,因为shallowRef只监听第一层即state.value的变化
      state.value.a = 1;
      state.value.f.b = 2;
      state.value.f.t.c = 3;
      state.value.f.t.s.d = 4;

      // 直接修改state.value,界面才会改变
      state.value = {
        a: '1',
        f: {
          b: '2',
          t: {
            c: '3',
            s: {
                d: '4'
            }
          }
        }
      }
    }
    return { state,myFn }
  }
}
</script>

 (3)triggerRef:指定监听某一层数据

<template>
  <div>
    <h1>{{state.a}}</h1>
    <h1>{{state.f.b}}</h1>
    <h1>{{state.f.t.c}}</h1>
    <h1>{{state.f.t.s.d}}</h1>
    <button @click="myFn">显示</button>
  </div>
</template>

<script>
import { shallowRef, triggerRef } from 'vue';
export default {
  name: 'App',
  setup() {
    let state = shallowRef({
        a: 'a',
        f: {
            b: 'b',
            t: {
                c: 'c',
                s: {
                    d: 'd'
                }
            }
        }
    });
    function myFn()  {
      // 指定只监听某一层数据
      state.value.f.t.s.d = 4;
      triggerRef(state);
    }
    return { state,myFn }
  }
}
</script>

 

5.模块分离

<template>
  <div>
    <form>
      <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="button" value="添加" @click="addStu">
    </form>
    <ul>
      <li v-for="(stu, index) in state.stus" :key="stu.id" @click="remStu(index)">{{stu.id}} - {{stu.name}} - {{stu.age}}</li>
    </ul>
  </div>
</template>

<script>
import { reactive } from 'vue';
export default {
  name: 'HelloWorld',
  setup() {
    // 引入删除和添加模块
    let { state, remStu } = useRemoveStudent();
    let { state2, addStu } = useAddStudent(state);
    return  { state, remStu, state2, addStu  } 
  }
}

// 删除模块
function useRemoveStudent() {
  // 监听对象或数组需要使用reactive
  let state = reactive({
    stus: [
      {id: 0, name: 'df0', age: 11},
      {id: 1, name: 'df1', age: 14},
      {id: 2, name: 'df2', age: 15},
    ]
  })
  // 点击移除该项
  function remStu(index) {
    state.stus = state.stus.filter((stu, idx) => idx != index);
  }
  return { state, remStu };
}
// 添加模块
function useAddStudent(state) {
  let state2 = reactive({
    stu: {
      id: '',
      name: '',
      age:''
    }
  })
  function addStu(e) {
    e.preventDefault();
    console.log(state2.stu);
    const stu = Object.assign({}, state2.stu);
    state.stus.push(stu);
  }
  return {state2, addStu}
}
</script>

 

6.toRaw获取原始数据

toRaw():获取原始数据,修改原始数据界面不会更新。

作用:ref/reactive数据每次修改都会被追踪,都会更新ui界面,非常消耗向性能。当一些操作不需要追踪时,可以通过toRaw()获取并修改原始数据。

获取reactive类型的原始数据:

<template>
    <div>
        <h1>{{state.name}}</h1>
        <button @click="myFn">更改</button>
    </div>
</template>
<script>
import {reactive, toRaw} from 'vue'
export default {
    name: 'ToRaw',
    setup() {
        let state = reactive({
            name: 'jds',
            age: 14
        });
        let obj2 = toRaw(state);
        function myFn() {
            console.log(state); // Proxy {name: "jds", age: 14}
            console.log(obj2); // {name: "jds", age: 14}
            obj2.name = 'sds'; // 界面不更新
            console.log(state); // Proxy {name: "sds", age: 14}
            console.log(obj2); // {name: "sds", age: 14}
        }
        return { state, myFn }
    }
}
</script>

获取ref类型的原始数据要注意加上.value:

let obj2 = toRaw(state.value);

 

7.markRaw永远不被追踪

即便使用reactive追踪也无反应

 

8.toRef

ref:将对象中的属性变成响应式的数据,修改响应式数据不会影响到原始数据

<template>
    <div>
        <h1>{{state}}</h1>
        <button @click="myFn">按钮</button>
    </div>
</template>

<script>
import {ref} from 'vue';
export default {
    setup() {
        let obj = {name: 'fbdf'};
        let state = ref(obj.name);
        function myFn() {
            console.log(obj); // {name: "fbdf"}
            console.log(state); // RefImpl {_rawValue: "fbdf", _shallow: false, __v_isRef: true, _value: "fbdf"}
            state.value = 'qwe'
            console.log(obj); // {name: "fbdf"}
            console.log(state); // RefImpl {_rawValue: "qwe", _shallow: false, __v_isRef: true, _value: "qwe"}
        }
        return {state, myFn}
    }   
}
</script>

toRef:将对象中的属性变为响应式数据,当修改响应式数据时,会影响原始数据,但ui不会更新。

<template>
    <div>
        <h1>{{state}}</h1>
        <button @click="myFn">按钮</button>
    </div>
</template>

<script>
import {toRef} from 'vue';
export default {
    setup() {
        let obj = {name: 'fbdf'};
        let state = toRef(obj,'name');
        function myFn() {
            console.log(obj); // {name: "fbdf"}
            console.log(state); // ObjectRefImpl {_object: {…}, _key: "name", __v_isRef: true}
            state.value = 'qwe'
            console.log(obj); // {name: "qwe"}
            console.log(state); // ObjectRefImpl {_object: {…}, _key: "name", __v_isRef: true}
        }
        return {state, myFn}
    }   
}
</script>

ref 和toRef区别:

  • ref->复制,修改响应式数据不会影响原始数据,数据发生改变,界面会更新
  • toRef -> 引用,修改响应式数据会影响原始数据,数据发生改变,界面不会更新

toRef应用场景:想让响应式数据和原始数据关联起来,并且更新响应式数据之后还不想更新UI,就可使用。

 

9.toRefs

将对象中的所有属性变成响应式的数据,修改响应式数据会影响到原始数据。

let obj = {name: 'fhd', age: 12};
let state = toRefs(obj); //

// 相当于
let name = toRef(obj.name);
let age = toRef(obj.age);

 

10.生命周期

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值