Vue3

172 阅读2分钟

变化

  1. 性能提升

  2. 源码升级

    1. 使用proxy替代defineProperty实现响应式
    2. 重写虚拟DOM的实现和Tree-Shaking
  3. 更好的支持TypeScript

  4. 新特性

创建Vue3工程

使用vue-cli

  • 使用4.5版本以上vue-cli
  • vue creat vue3 projectName

使用vite

  • 开发环境无需打包,可以快速冷启动
  • 更轻量快速热重载
  • 按需编译
npm init vite-app projectName
//安装依赖
cd project
npm i
npm run dev

常用Composition API 组合式API

1.setup函数

  • 组件中用到的数据,方法都需要配置在setup中

  • 两种返回值

    1. 返回对象

      1. 返回内容可以在模版中直接使用
    2. 返回渲染函数

      //手动引入渲染函数
      import {h} from 'vue'
      //返回渲染函数
      setup(){
      	function fun(){
      		//此处内容会直接将原有模版字符串覆盖掉
      		return ()=>h('h1','text')
      	}
      }
      
  • 在vue3中无法读取到vue2配置内容

  • 若有重名, setup优先

  • setup不可以是async函数(后期配合suspense和异步组件可以使用async)

注意:

  • setup执行时间在beforeCreate之前,所以setup不含this(undefined)

  • setup参数

    • props //父组件向子组件传数据
    props: ["msg", "school"],
      setup(props) {
        console.log(props);
    }
    
    • context

      • attrs //捕获父组件传子组件但是子组件并没有使用props接收的值 this.$attrs

      • emit //触发自定义事件 相当于this.$emit

        • 在父组件中定义自定义事件后,子组件中使用context.emit(’事件’,value)触发
        • 要在子组件emits中注册自定义事件
      • slots //插槽 this.$slot

    2.ref函数

    //引入ref函数
    import {ref} from 'vue'
    let name = ref('章三')
    
    • 作用:定义一个响应式的数据

    • 语法:const xxx = ref(initValue)

      • 创建一个包含响应式数据的引用对象(reference对象)
      • js中操作数据 xxx.value
      • 模版中使用数据不需要value
    • 说明

      • 接受的数据类型可以是基本类型,也可以是对象类型

      • 基本类型数据:响应式依靠Object.defineProperty的get和set完成

      • 对象类型数据,内部借助了新函数 — reactive

        • reactive函数内部封装了Proxy函数
    • 实现

    // 引入ref函数
    import { ref } from "vue";
    export default {
      name: "App",
      setup() {
        // 使用ref函数将数据转化成引用对象实现响应式
        let name = ref("黎明");
        let age = ref(18);
    
        function changeName() {
          // 使用.value形式双向绑定
          (name.value = "王武"), (age.value = 20);
        }
    
        //使用setup返回对象t 
        return {
          name,
          age,
          changeName,
        };
      },
    

    3.reactive函数

    • 定义对象类型的响应式数据
    • 可以进行深层次的响应式数据
    • 面向对象式
    // 引入reactive函数
    import { reactive } from "vue";
    export default {
      name: "App",
      setup() {
        //使用reactive函数,将对象转化成响应式数据
        let person = reactive({
          name: "黎明",
          age: 18,
          job: {
            type: "UI设计师",
            salary: "30k",
          },
        });
    
        function changeName() {
          //通过reactive可以通过Proxy省略value
          (person.name = "王武"), (person.age = 30), (person.job.salary = "20k");
        }
    
        //使用setup返回对象
        return {
          person,
          changeName,
        };
    

    Vue3.0中响应式原理

    Vue2.x中响应式

    • 原理

      • 对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截实现数据劫持
      • 数组类型:通过重写数组的一系列方法来实现拦截
    • 不足

      • 在数组中直接通过数组下标修改数据数据不会发生更新
      • 添加,删除对象,数组身上属性时,页面不会刷新
      //直接向对象中添加属性并不会引起页面的更新
            // this.sex = "男";
            //通过下列方法
            /*
            ** 1.this.$set
            ** this.$set(this.person,'sex','男')
      
            ** 2.Vue.set
            **  Vue.set(this.person,'sex','男')
            */
      
    • 实现

    let person = {
                name: '章三',
                age: 20
            }
            let p = {}
            Object.defineProperty(p, 'name', {
                get() {
                    console.log("读取name属性");
                    return person.name
                },
                set(name) {
                    console.log("修改name属性")
                    person.name = name
                }
            })
    

    Vue3.0响应式

    • window.Proxy
    • 实现

    代理Proxy加反射Reflect

    • 通过代理拦截对象中属性的任意变化,如添加删除修改
    • 通过反射对源对象的属性进行操作
    let persons = {
                a: 1,
                b: 2,
            }
            let p1 = new Proxy(persons, {
                get(target, propName) {
                    console.log(`读取了${target}上的${propName}属性`);
                    return Reflect.get(target, propName)
                },
                set(target, propName, value) {
                    console.log(`修改了${target}上的${propName}属性,修改为${value}`);
                    console.log(target);
                    Reflect.set(target, propName, value)
                }
    						deleteProperty(target, propName){
                    console.log(`删除了${propName}`);
                    Reflect.deleteProperty(target, propName)
                }
            })
    

    Reflect函数

    • 方法

      • set
      • get
      • deleteProperty
      • defineProperty
    • 发生重复定义错误时,以布尔值的方式记录,减少try,catch的重复

    const x1 = Reflect.defineProperty(person, 'c', {
                get() {
                    return 3
                }
            })
            console.log(x1);   //true
            const x2 = Reflect.defineProperty(person, 'c', {
                get() {
                    return 4
                }
            })
            console.log(x2);  //false
    

    Ref函数和Reactive函数区别

    • 定义数据

      • ref定义基本类型数据
      • reactive定义对象(数组)类型数据。(引用类型数据)
      • ref也可以用来定义对象(数组)类型数据,内部会自动通过reactive转化为代理对象
    • 原理

      • ref仍旧使用Object.defineProperty的get和set来实现响应式(数据劫持)
      • reactive通过Proxy来实现响应式,并通过reflect操作源对象数据
    • 使用角度

      • ref:操作数据需要通过.value,在模版中读取不需要
      • reactive:操作读取均较为简洁

计算属性computed

//需要先引入computed
import { reactive, computed } from "vue";

//计算属性简写:
    // person.fullName = computed(() => {
    //   return `${person.firstName}-${person.lastName}`;
    // });
    //计算属性:
    person.fullName = computed({
      get() {
        return `${person.firstName}-${person.lastName}`;
      },
      set(value) {
        const personName = value.split("-");
        person.firstName = personName[0];
        person.lastName = personName[1];
      },
    });

watch监视

  • 注意:由于watch监视的是ref和reactive对象,所以普通类型的ref并不需要.value(.value会直接取到数值,watch不可以对数值进行监测)
  • 当watch监视ref对象定义的对象(数组)数据类型时,可以通过.value或是配置项中开启deep,来达到深度监视的目的(此时仍无法获取oldValue)

1.监视ref对象

//监视单个属性
    watch(
      num,
      (newVal, oldVal) => {
        console.log(newVal, oldVal);
      },
      { immediate: true, deep: true }
    );

    //监视多个属性
    watch([num, msg], (newVal, oldVal) => {
      console.log(newVal, oldVal);
    });

2.监视reactive对象

  • 当监视的是reactive定义的响应式数据时

    • 无法获取oldValue的值
    • 强制开启了深度监视
  • 当监视的是reactive中定义的某一个属性时

    • deep有效
  1. 监视reactive中某一个属性(此时可以正常获取到oldValue)
 //监视单个属性
    watch(
      () => {
        return person.job.salary;
      },
      (newValue, oldValue) => {
        console.log(newValue, oldValue);
      }
    );

watchEffect函数

  • 不需要指明监视的属性,会自动监视用到的属性

生命周期

名称变化

生命周期钩子

  • beforeDestroy → beforeUnmount
  • destroyed → unmounted

Composition API 钩子

  • beforeCreated , created → setup
  • 其余名称前面加on

自定义hook函数

  • 本质是一个函数,将setup中用到composition API的函数进行封装
  • 在使用过程中引入时直接引入不需要加大括号

toRef和toRefs函数

  • 作用:将某个数据变成ref对象

  • 使用:

    • toRef(obj,’atte’)

    • …toRefs(obj) //只能展开第一层

      • 用于reactive包裹的响应式对象

其他组合式API

shallowReactive

  • 只将第一层数据转化成响应式

shallowRef

  • 不会处理对象类型响应式
  • 对象数据后续功能不会修改对象中属性

readonly

  • 响应式对象只读(深层次)

shallowReadonly

  • 响应式对象只读(浅层次)

toRaw

  • 将reactive生成的响应式对象转化成普通对象
  • 转化后,对象内属性可以被修改,但是不会响应式刷新页面

markRaw

  • 标记一个对象,使其永远不会成为响应式

  • 应用:

    • 复杂的第三方类库永远不应该是响应式
    • 渲染具有不可变数据的大列表,跳过响应式优化性能

customRef

  • 自定义ref
//自定义ref
    function myRef(value) {
      //返回customRef
      return customRef((track,trigger) => {
        //重写get,set方法
        return {
          get() {
            console.log(`有人读取了${value}`);
            //告诉get监听set的通知
            track()
            return value;
          },
          set(newValue) {
            console.log(`有人修改了值,改为了${newValue}`);
            value = newValue;
            //通知get值已被修改
            trigger()
          },
        };
      });
    }
    let num = myRef("自定义customRef的使用");
  • 实现一个防抖
//自定义ref
    function myRef(value, delay) {
      //返回customRef
      return customRef((track, trigger) => {
        let timer;
        //重写get,set方法
        return {
          get() {
            console.log(`有人读取了${value}`);
            //告诉get监听set的通知
            track();
            return value;
          },
          set(newValue) {
            console.log(`有人修改了值,改为了${newValue}`);
            clearTimeout(timer);
            timer = setTimeout(() => {
              value = newValue;
              //通知get值已被修改
              trigger();
            }, delay);
          },
        };
      });
    }
    let num = myRef("qwe", 500);

provide和inject

  • 祖孙组件间通信
  • 父组件
import { provide } from 'vue'
provide ('car' , car )
  • 后代组件
import { inject } from 'vue'
let car = inject ('car')

响应式数据的判断

  • isRef
  • isReactive
  • isReadonly
  • isProxy

组合式API优势

配置式API (Options API)

  • 数据,方法分开存放,较为混乱

组合式API(composition API)

  • 更优雅的组织数据,方法,使代码更为有序

新的组件

Fragment

  • 不在需要根标签,组件内部会将多个标签包含在同一个fragment虚拟元素中
  • 减少层级标签,减少内存占用

Teleport

Suspense

  • 配合异步引入,实现骨架屏效果
import { defineAsyncComponent } from "vue";
const Demo = defineAsyncComponent(() => import("./components/Demo.vue")); //异步引入子组件

<Suspense>
      <template v-slot:default>
        <Demo />
      </template>
      <template v-slot:fallback>
        <h2>加载中。。。。。</h2>
      </template>
</Suspense>

其他改变

Untitled

  • 移除了keycode作为v-on的修饰符
  • 移除了click.native,若子组件中不声明emits:[click],默认click为原生事件
  • 移除了过滤器