• vue3--相对于vue2的改变-T0档次


    前言

      vue3相对于vue2做了不少的改变,个人对其进行了一些整理。并结合在日常中的使用,对这些新特性在开发时的影响度进行了分级。

    T0档次

      setup

        介绍:setup(props,content) objet

          官方文档给出的解释是,在目前vue在编写复杂组件的时候(组件内代码量的庞大),会导致本来是一个逻辑块的代码,分散开来。这样会增加后面接手的人阅读上的难度。所以提供了一个组合api,可以把相同逻辑的代码块放在一个地方,这样便于阅读。不过这个东西仁者见仁智者见智,本人就觉得把一个个逻辑块分块放好也挺好的,重点开发人员编码习惯,一个邋遢的开发人员,就算你给他提供再好的api,照样会给你写出奇形怪状的代码来。但话说回来,你可以不用,但我不能没有。vue3充分尊重了开发人员的选择,setup你可以用,也可以不用。就像vue2一样,在钩子函数和methods中编写逻辑块,也是一种正确的选择

        使用细节:

          相对于vue2的执行周期

             setup(beforeCreate,created)凡是在原来beforeCreate和create中执行的代码,你都可以放在setup中,而且setup执行的时机还在brforeCreate之前

          一些限制

            1.不能访问$data,this

            2.必须返回一个{},

          返回值

            如果存在返回值,那么它将会合并到$data中,如果$data已经存在相同的key,那么将会覆盖原有key值。值得一提的是,如果你返回的是一个普通对象,那么即便合并到$data之后,改变对应的值,也不会具有响应性。你需要使用vue3提供的响应式方法,将普通对象转化成响应式对象,这样合并在$data上的属性才会是响应式的

         

    响应式原理

      变化:

        vue3将原来vue2中对数据响应式处理方法,由es5的Object.defineProperty改为es6的Proxy

      不同的点:

        defineProperty:是对对象的属性定义拦截,如果被定义的obj对象存在多个属性或者是多嵌套结构,那么就要便利递归它所有的属性,挨个去使用Object.defineProperty定义属性拦截

        let value = "obj1 - prop1";
        let obj = {};
        Object.defineProperty(obj, "prop1", {
          get() {
            return value + " suffix";
          },
        });
    
        console.log(obj); //obj1-name suffix

        Proxy:是对obj整个对象进行代理,它返回一个新的代理对象,这意味着:

          1.当我们改变数组中某一项的值的时候再也不需要使用$set了,直接this.arr[1]=new value即可触发视图更新。

          2.我们在响应式对象上新增的属性时,依然会触发也会触发视图更新

        let obj = { prop1: "obj1 - prop1" };
        let objProxy = new Proxy(obj, {
          get(obj, prop) {
            return obj[prop] + " suffix";
          },
        });
        console.log(obj.prop1); //obj1-prop1
        console.log(objProxy.prop1); //obj1-prop1 suffix
        return reactive({ data: "9999" });

    生命周期函数钩子

      介绍:vue3新增一批生命周期钩子函数,其目的是为了解决在使用setup的时候,因为setup的执行周期是在beaforeCreate和ceated之间,一些需要在其他周期时机内执行的一些逻辑,比如获取dom。无法setup中执行。所以为了使在setup中的代码,能够在指定的声明周期内执行,vue3补充了一些周期钩子函数,它们的作用和vue的声明周期钩子完全一样。

      

      举例使用

    import { onMounted } from "vue"; //引入钩子函数,其他的声明周期钩子函数也是如此引入
    export default {
      setup(props, content) {
        //因为在setup中无法访问this,所以也无法访问到$data,代码中需要依赖到$data中的字段的话,可以在setup中定义data,setup return之后,会合并至$data
        const data = reactive({
          data: 1,
        });
        //所有的钩子函数都是接收一个方法作为参数
        onMounted(function() {
          console.log("参数方法,在对应的生命周期内执行 mounted", data.data);
        });
        console.log("在setup中的代码,在beforeCreate至created 生命周期内执行");
        return data;
      },

    响应式api

      简介vue3提供的响应式api可以使普通对象转化为响应式对象,这也是为了配合setup的使用提供的一个方法,在setup的执行时机是beforeCreate和created,并且无法访问到this和$data,而使用setup可能会使用到一些响应式的变量(比如说,请求一个接口,在接口响应后将数据渲染到页面上,因为接口响应是异步的,所以就必须要一个响应式的对象接收数据发生改变,从而触发视图更新),而setup中不能读取this,自然就无法获取到data(){}项中定义的响应式属性,所以只能通过return的方式,把一个响应式的对象合并到data中。

    <template>
      <div class="hello">
        {{ prop1 }}
      </div>
      <button class="but" @click="changeVal">改变值</button>
    </template>
    
    <script>
    export default {
      setup(props, content) {
        //setup中将一个普通对象return合并到$data上之只会在初次渲染的时候作用在视图上,后续再对prop1进行更改this.prop1=new value,是不会触发视图更新的.因为data中的prop1只是一个普通的对象,而非响应式对象
        const data = {
          prop1: "old value",
        };
        return data; //return对象中所有属性都会合并到$data上
      },
      methods: {
        changeVal() {
          this.data = "new value"; //不会触发视图更新
        },
      },
    };
    </script>
    
    //使用响应式api //引入响应式api import { ref } from "@vue/reactivity"; export default { //要想使setup中return的对象具有响应式那么只需将return的对象使用响应式钩子处理即可 setup(props, content) { const data = { prop1: ref("old value"),//使prop1属性变成响应式对象 }; return data; }, methods: { changeVal() { this.prop1 = "new value";//视图发生改变 }, }, };

     响应式api分类:

        1.reactive 将引用类型对象转化成响应式对象,(这样的好处是,遇到复杂的数据类型,不需递归去使用ref,不然又回到了vue2)

    reactive({} || []); //针对引用类型,将引用类型转化为响应式对象

        2.ref 适合将基本类型转化成响应式对象,但ref同样也能对引用类型进行转化,与reactive不同的是,你需要通过ref.value来使用属性。对于基本类型来说这是实现响应式的基本条件,但是对于引用类型来说已经可以通过Proxy来实现代理,没有必要在进行过渡封装本来可以prop直接使用,没有必要value.prop,所以为什么说,ref适合基本类型。值得一提的是ref在对引用类型处理的时候,也是将其转化成Proxy

    const obj = ref(1 || "1"); //针对基本类型,接受一个基本类型返回响应式对象
    console.log(obj.value); //返回的响应式对象,通过value获取值

    响应式api拓展

      为了让开发者在使用响应式api时,能够便捷,全面的适用场景,vue3,对响应式api进行了一些拓展,围绕reactive和ref提供了一些功能性的api

      1.reactive

        readonly:创建一个只读的响应式对象。ps:(都只读了还需要响应式吗?)

     import {readonly} from "vue";//引入方法

     const copy = readonly({ prop: "测试属性", level: { prop: "level 测试属性" }, }); //将普通对象的副本转化成一个只读的响应式对象 copy.prop = "1234"; //warning;在这里改变只读的copy响应式对象时,会出现警告,不允许改变响应式对象copy的prop属性; copy.level.prop = "9999"; //warning 只读属性是深层次的,不管嵌套了多少层,只要是属于copy内部的属性,都无发更改 },

        isProxy:判断响应式对象是否是由reactive或者readonly创建的Proxy

      import {readonly} from "vue" //引入方法

       const copy = reactive({ prop: "测试属性", }); const copyReadonly = readonly({ prop: "only 属性" }); console.log(isProxy(copy)); //true   console.log(isProxy(copyReadonly)); //true
    console.log(isProxy({copyReadonly,copy})); //false

        isReactive:判断响应式对象是否是由reactive创建响应式对象

      import {isReactive} from "vue"  

      const copy = reactive({ prop: "测试属性", }); const copyReadonly = readonly({ prop: "only 属性" }); console.log(isReactive(copy)); //true console.log(isReactive(copyReadonly)); //true console.log(isReactive(readonly(copy))); //true 如果readonly依赖与reactive创建,那么也返回true console.log(isReactive(readonly({ copy }))); //false

        isReadonly:判断是否是由readonly创建的响应式对象

      import {isReadonly} from "vue";//引入方法

      const copy = reactive({ prop: "测试属性", }); const copyReadonly = readonly({ prop: "only 属性" }); console.log(isReadonly(copy)); //false console.log(isReadonly(copyReadonly)); //true console.log(isReadonly(readonly(copy))); //true 如果readonly依赖于reactive创建,同样返回ture

        toRaw:返回 reactivereadonly 代理的原始对象。这是一个转义口,可用于临时读取而不会引起代理访问/跟踪开销,也可用于写入而不会触发更改。不建议保留对原始对象的持久引用。请谨慎使用。

      const copy = reactive({
          prop: "测试属性",
        });
    
        const copyReadonly = readonly({ prop: "only 属性" });
        console.log(toRaw(copy)); //{prop:"测试属性"}
        console.log(toRaw(copyReadonly)); //{prop:"only 属性"}
        console.log(toRaw(readonly(copy))); //{prop:"测试属性"} 多重嵌套同样可以获取到原始值

         markRaw:标记一个对象,使其永远不会被转还成代理

       const base = markRaw({ prop: "base prop属性" });
        const reactiveBase = reactive(base);
        const readonlyeBase = readonly(base);
        const proxyBase = reactive({ reactiveBase });
        const proxyBase2 = reactive({ reactiveBase, prop2: "1245" }); //这里多出一个普通属性prop2
        console.log(isReactive(reactiveBase)); //false
        console.log(isReadonly(readonlyeBase)); //false
        console.log(isReactive(proxyBase)); //true 但是改变属性reactiveBase中的prop,仍不会触发视图更新
        console.log(isReactive(proxyBase2)); //true 只改动属性reactiveBase中的prop不会触发视图更新,但同时改变prop2属性,因为prop2属性正常的属性,所以会触发视图更新,视图更新的时候发现reactiveBase中的prop也发生了改变,
    //所以即便它被设置成了markRaw,也一样会在视图上呈现改动后的内容,

        shallowReactive:只对自身的属性进行响应式的追踪,对于嵌套的属性,不做响应式处理

      const obj = {
          level1: "测试属性level1",
          sub: {
            subProp1: "测试属性subProp1",
          },
        };
        const shallowReactiveObj = shallowReactive(obj);
    const reactiveObj
    = reactive(obj);//reactive console.log(isReactive(shallowReactiveObj)); //true console.log(isReactive(shallowReactiveObj.sub)); //false console.log(isReactive(reactiveObj.sub)); //true // ... //改动自身属性,触发视图更新 shallowReactiveObj.level1 = "new level1"; // ... //改动嵌套属性,不触发视图更新 shallowReactiveObj.sub.subProp1 = "new sub subProp1"; // ... //同时改变嵌套属性和自身属性 shallowReactiveObj.level1 = "new level1"; shallowReactiveObj.sub.subProp1 = "new sub subProp1"; //和markRaw一样,因为改变自身属性时会造成视图更新,而更新的时候发现嵌套熟悉sub.subProp1也改变了,所以改变后的sub.subProp1也会渲染至最新视图 return shallowReactiveObj;

        shallowReadonly:使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)。

    const state = shallowReadonly({
      foo: 1,
      nested: {
        bar: 2
      }
    })
    
    // 改变状态本身的property将失败
    state.foo++
    // ...但适用于嵌套对象
    isReadonly(state.nested) // false
    state.nested.bar++ // 适用

      2.ref

        unref:如果参数为 ref,则返回内部值,否则返回参数本身

        let baseVal = 1;
        console.log(unref(ref(1))); //返回内部置 1
        console.log(unref(baseVal)); //number 1

        toRef:可以为响应式对象的其中property创建一个ref,然后将其独立使用

       let baseVal = {
          prop1: "baseVal.prop1",
        };
        let prop1 = toRef(reactive(baseVal), "prop1");
        ...
        prop1 = "new prop1"; //触发视图更新

        toRefs:将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的

      let state = {
          prop1: "baseVal.prop1",
          prop2: "baseVal.prop2",
          sub: {
            prop1: "axsxsxs",
          },
        };
        let stateRef = toRefs(reactive(state)); //将一个rective转化成普通对象,并将reactive的property转化成ref
        // ...
        stateRef.prop1 = "new value"; //触发试图更新
    
        console.log(stateRef.sub); //Proxy 如果reactive的peoperty中存在{}||[],那么则不做处理,只是简单的保留,换句话说,就是toRefs只对基本类型property处理,非基本类型的proper,就地保留

        isRef:检查变量是否是一个ref,使用方法和isReactive一致

        const state = "test";
        const refState = ref(state);
        console.log(isRef(refState)); //true;
        console.log(isRef(state)); //false;

        cutomRef:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 tracktrigger 函数作为参数,并应返回一个带有 getset 的对象。

        let state = "test";
        const refState = customRef((track, trigger) => {
          let timeout;
          return {
            get() {
              track(); //track方法在get必须要执行,它是用来搜集依赖元素(订阅者),
              return state;
            },
            set(newValue) {
              clearTimeout(timeout);
              //这里表示延迟两秒通知订阅者更新,使用延迟函数能够更好的体现变化的异步效果,在学习过程中更便于理解
              timeout = setTimeout(() => {
                state = newValue;
                trigger(); //trigger是必须要执行的,他表示通知vue,refState已经发生改变,通过track() 收集到的订阅者需要更新了
              }, 1000);
            },
          };
        });

        shallowRef:创建一个 ref,它跟踪自己的 .value 更改,但不会使其值成为响应式的。在改变ref.value的时候会发生试图更新,在改变ref.value.prop的时候则不会触发试图更新

     setup() {
        let stateRef = shallowRef({ prop: "old val" }); //这里之所以使用引用类型,是因为对于基本类型ref.value直接可以访问值,无法演示ref.value.prop
        setTimeout(() => {
          stateRef.value = { prop: "new val" }; //触发视图更新(副作用之一)
        }, 2000);
        setTimeout(() => {
          stateRef.value.prop = "new val2"; //无法触发视图更新
        }, 3000);
        return {
          stateRef,
        };
      }

        triggerRef:手动触发与shallowRef的关联效果

    setup() {
        const shallow = shallowRef({
          greet: "Hello, world",
        });
    
        // 第一次运行时记录一次 "Hello, world"
        watchEffect(() => {
          console.log(shallow.value.greet);
        });
    
        // 这不会触发作用,因为 ref 很浅层
        shallow.value.greet = "Hello, universe";
    
        // 记录 "Hello, universe"
        triggerRef(shallow);
        return { shallow };
      }

          

  • 相关阅读:
    [置顶] 【原创分享】嵌入式linux应用之U-BOOT移植定制篇--20130822
    [置顶] java 连接 mysql 数据库步骤
    [置顶] 【原创】无线LED条屏信息报警项目---2012.05
    用Python正则表达式搜索统计命令行管道中的所有数字
    从SharePoint 2013迁移到SharePoint Online
    SharePoint Framework 构建你的第一个web部件(一)
    SharePoint Framework 配置你的SharePoint客户端web部件开发环境
    SharePoint Framework 配置Office 365开发者租户
    SharePoint Framework 开发工具和库
    SharePoint Framework 概述
  • 原文地址:https://www.cnblogs.com/wrhbk/p/14700231.html
Copyright © 2020-2023  润新知