美文网首页
Vue3.0的变化

Vue3.0的变化

作者: 渚清与沙白 | 来源:发表于2023-12-17 16:59 被阅读0次
    setup函数
    • 执行时机:在beforeCreate函数之前,setup函数中的this是undefined
    • setup的参数
      1. props:值是一个对象。包含 组件外部传递过来,且组件内部声明接受了的属性。
      2. context上下文对象
        • attrs:值是一个对象。包含 组件外部传递过来,但组件内部没有声明接受了的属性。相当于vue2中this.$attrs
        • slots:收到的插槽内容。相当于vue2中this.$slots
        • emit:分发自定义事件的函数。相当于vue2中this.$emit

    RefImpl :引用对象,是一个对象,ref函数的返回值。


    image.png
    ref
    • ref:定义一个响应式的数据。可以处理基本数据类型的数据,也可以处理对象类型的数据
    • ref处理基本数据类型使用的是Object.defineProperty()getset来实现响应式,在处理对象和数组时使用的是es6中window的Proxy来实现响应式。reactive函数实现了Proxy的功能。
    reactive
    • reactive:定义一个对象类型的响应式数据
    • reactive定义的响应式数据是深层次的
    ref与reactive对比
    • 定义数据角度
      ref用来定义 基本数据类型
      reactive用来定义对象或数组
      ref也可以用来定义对象或数组,它内部会通过reactive自动转为代理对象
    • 原理角度
      ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
      reactive通过使用Proxy来实现响应式(数据劫持)。
    • 使用角度
      ref定义的数据:操作数据需要.value,模板中读取数据数据时不需要.value
      reactive定义的数据:操作数据与读取数据都不需要.value

    响应式原理

    vue2的响应式
    • 实现原理
      对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
      数组类型:通过重写更新数组的一系列方法来实现拦截。对数组的变更方法进行了包裹。
    // 只能进行读取和修改,无法进行新增和删除属性
    Object.defineProperty(data, 'count',{
      get(){},
      set(){}
    })
    
    • 存在问题
      1. 新增属性、删除属性,界面不会更新。
        this.$set(this.person,'sex','男')Vue.set(this.person,'sex','男')可以解决添加属性不是响应式的问题。
        this.$delete(this.person,'sex')Vue.delete(this.person,'sex')可以解决删除属性不是响应式的问题。
      2. 直接通过下标修改数组,界面不会自动更新。
        this.$set(this.list,0,'学习')Vue.set(this.list,0,'学习')slice方法可以解决该问题。(第二个参数是数组的下标)
    vue3的响应式
    • 实现原理
      通过Proxy(代理):拦截对象中任意属性的变化,包括属性值的读写、属性的添加、属性的删除等。
      通过Reflect(反射):对被代理对象的属性进行操作。
      模拟实现响应式
    <script>
      let person = { name: "张三", age: 10 };
      const p = new Proxy(person, {
            // 获取属性值
            get(target, key) {
              console.log(`监测到访问${key}属性`);
              // return target[key];
              return Reflect.get(target, key);
            },
            // 修改 或 新增 属性
            set(target, key, value) {
              console.log(`检测到 修改${key}属性`);
              // target[key] = value;
              Reflect.set(target, key, value);
            },
            // 删除属性
            deleteProperty(target, key) {
              console.log(`检测到 删除${key}属性`);
              // return delete target[key];
              return Reflect.deleteProperty(target, key);
            },
        });
    </script>
    
    计算属性 computed

    import { computed } from 'vue'

    export default{
        setup(){
          let person = reactive({
            firstName:'',
            lastName:'',
        });
        // 简写形式,只读  无法修改
        person.fullNmae = computed(()=>{
          return firstName + '-' + lastName;
        });
        // 完整写法 支持读和写
        person.fullNmae = computed(()=>{
          get(){
            return person.firstName + '-' + person.lastName;
          },
          set(value){
            const arr = value.split('-');
            person.firstName = arr[0];
            person.lastName = arr[1];
          }
        });
        return { person }
      }
    }
    
    监视属性 watch
    • 监视reactive定义的响应式数据时,oldValue无法正确获取,强制开启了深度监视(deep配置失效)
    • 监视reactive定义的响应式数据中某个属性时:deep配置有效
    • 监视ref定义的对象类型响应式数据,可以配置deep,可以传递person.value
    • watchEffect 回调函数中使用了谁就监视谁
      import { ref , watch } from 'vue'
    export default{
      setup(){
        let sum = ref(0);
        let msg = ref('xx');
        let person = reactive({
          name: '',
          age: 10,
          job: {
            j1: {
              salary: 20
            }
           }
        });
    
        // 情况一:监视ref所定义的一个响应式数据
        watch(sum,(newVal, oldVal)=>{  }, { immediate :true });// 监视多个 可以调用多次watch函数
       
        // 情况二:监视ref所定义的多个响应式数据
        watch([sum, msg],(newVal, oldVal) =>{  }); // 监视多个 可以调用多次watch函数
    
        /*
          情况三:监视reactive所定义的一个响应式数据的全部属性。
          注意:此处无法正确地获取到oldVal,oldVal与newVal的属性值【一样】
          注意:深度监视 deep 参数配置无效(reactive本身就支持深度响应式)
        */
        watch(person,(newVal, oldVal)=>{  }, { deep :false });// deep配置无效
        
        // 情况四:监视reactive所定义的一个响应式数据中的某个属性。传递一个函数。
        watch(()=>person.name,(newVal, oldVal)=>{  }, { immediate :true });
        
        // 情况五:监视reactive所定义的一个响应式数据中的多个属性。传递一个数组,元素是函数。
        watch([ ()=>person.name, ()=>person.age ],(newVal, oldVal)=>{  }, { immediate :true });
        
        // 情况六:监视的reactive所定义的对象中的某个属性(对象类型),所以deep配置有效
        watch(()=>person.job,(newVal, oldVal)=>{  }, { deep :true }); 
    
        // 情况七:监视ref所定义的对象类型响应式数据
        let student = ref({
          name: '',
          age: 20,
        });
        // 监视方式一  student是RefImpl类型,student.value的类型是Proxy类型
        watch(student.value, (newVal, oldVal)=>{});
        // 监视方式二
        watch(student, (newVal, oldVal)=>{}, {deep: true});
      
        // watchEffect  这里监视了salary和sum
        watchEffect(()=>{
            const salary = person.job.salary;
            const s = sum.value;
        });
        
        return { sum, msg, person};
      }
    }
    

    watch:要指明监视的属性,也要指明监视的回调
    watchEffect:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
    watchEffect类似computed:computed注重计算出来的值,必须要写 返回值;watchEffect注重的是过程,不必写返回值

    生命周期

    组合式API生命周期钩子,与Vue中钩子对应关系

    • beforeCreate ===> setup()
    • created ===> setup()
    • beforeMount ===> onBeforeMount
    • mounted ===> onMounted
    • beforeUpdate ===> onBeforeUpdate
    • updated ===> onUpdated
    • beforeUnmount ===> onBeforeUnmount
    • unmounted ===> onUnmounted

    引入
    import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'

    <script>
      export default{
        setup(){
          onBeforeMount(()=>{});
          onMounted(()=>{});
          onBeforeUpdate(()=>{});
          onUpdated(()=>{});
          onBeforeUnmount(()=>{});
          onUnmounted(()=>{});
        }
      }
    </script>
    
    自定义hook函数

    hook本质是一个函数,把setup函数中使用的组合式API进行了封装。类似与Vue2中的mixin。
    src/hooks/xxx.js创建一个JS文件,导出一个函数

    import { onBeforeMount, onMounted, reactive } from 'vue'
    export default function() {
        let point = reactive({
            x: 0,
            y: 0
        });
        function setPoint(event) {
            point.x = event.pageX;
            point.y = event.pageY;
        }
        onMounted(()=>{
            window.addEventListener('click', setPoint);
        })
        onBeforeMount(()=>{
            window.removeEventListener('click', setPoint);
        })
        return point;
    }
    

    在组件中使用hook

    <script>
        import { usePoint } from '../hooks/usePoint'
        export default {
            setup() {
                const point = usePoint();
                return { point }
            }
        }
    </script>
    
    toRef 和 toRefs
    • 作用:创建一个ref对象,其value值指向另一个对象的某个属性。
    • 语法:const name = roRef(person,'name')
    • 应用:要讲响应式对象的某个属性单独提供给外部使用时。
    • 扩展:toRefs 与 toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)
    <scrip> 
      setup(){
         let person = reactive({
          name: 'John Doe',
          job:{
            jQuery:{
            salary: 100000,
          },
        }
      })
    
      // 一次性处理多个属性
      return {
        person,
        ...toRefs(person)
      };
      
      // 每次处理一个
      return {
        person,
        name:toRef(person,'name'),
        salary:toRef(person.job.jQuery,'salary')
      };
    }
    </script>
    
    shallowReactive 和 shallowRef

    shallowReactive:浅层次的响应式,只处理对象最外层的响应式。其他层次的数据就不是响应式了
    shallowRef:只处理基本数据类型的响应式,不处理对象的响应式。shallowRef与ref传递的是基本数据类型,作用一样,如果传递的是对象类型,shallowRef不会把对象处理成响应式,ref则会将对象处理成响应式

    readonly 和 shallowReadonly
    • readonly:让一个响应式数据(ref、reactive)变为只读(深只读)
      person = readonly(person) person的数据无法修改
    • shallowReadonly:让一个响应式数据变为只读(浅只读)
      person = shallowReadonly(person) person的最外层数据无法修改,其他的数据可以修改
    toRaw 和 markRaw
    • toRaw:将一个由reactive生成的响应式对象转为普通对象(不具备响应式)
    • markRaw:标记一个对象,使其永远不会再成为响应式对象
    customRef
    <script> 
    setup(){
      function myRef(value,delay){
       let timer;
        return customRef((track, trigger) => {
          return {
            get() {
              track()// 追踪 数据变化
              return value
            },
            set(newValue) {
              clearTimeout(timer)
              timer = setTimeout(() => {
                value = newValue
                trigger()// 触发数据变化
              },delay)// 延时触发
            }
          }
        })
      }
      let key = myRef('hello');
      return { key };
    }
    
    </script>
    
    provide 与 inject

    组件间的通信方式,实现祖孙间的通信。父组件有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据。

    1. 祖组件
    setup(){
      let car = reactive({name:'ben', price:'40w'});
      provide('car',car)
    }
    
    1. 后代组件
    setup(){
      const car = inject('car');
      return { car }
    }
    
    响应式数据的判断
    • isRef:检查一个值是否为一个ref对象
    • isReactive:检查一个对象是否是由reactive创建的响应式代理
    • isReadonly:检查一个对象是否是由readonly创建的只读代理
    • isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理
    组合式api与选项式api

    新组件

    1. Fragment
    • 在vue2中:组件必须有一个根标签
    • 在vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素值
    • 好处:减少标签层级,减小内存占用
    1. Teleport
      Teleport是一种能够将我们的组件html结构移动到指定位置的技术
    <teleport to="body">
      <h3> title </h3>
    </teleport>
    
    1. Suspense
    • 等等异步组件时渲染一些额外内容,让应用有更好的用户体验
    • Suspense是用插槽实现的
    • 使用方法
      异步引入组件
    import { defineAsyncComponent } from 'vue'
    const Child = defineAsyncComponent(()=> import('./component/Child.vue'))
    

    使用Suspense 包裹组件,并配置好 default 与 fallback

    <template>
      <div class="app">
        <Suspense>
    
          <template v-slot:default>
            <Child/>
          </template>
    
          <template v-slot:fallback>
            <h3>加载中...</h3>
          </template>
    
        </Suspense>
      </div>
    </template>
    
    其他API的变化
    1. 全局API的转移
      Vue.config.xxx -> app.config
      Vue.config.productionTip -> 移除
      Vue.component -> app.component
      Vue.directive -> app.directive
      Vue.mixin -> app.mixin
      Vue.use -> app.use
      Vue.prototype -> app.config.globalProperties

    2. data选项应该始终被声明为一个函数

    3. 过渡类名的更改
      .v-enter -> .v-enter-from
      .v-leave -> .v-leave-from

    4. 移除keyCode作为v-on的修饰符,同时 不再支持config.keyCodes

    5. 移除v-on.native修饰符

    v-on:close=""
    v-on:click=""
    
    export default {
      emits: ['close']// 声明了close事件,是一个自定义事件。没有声明click事件,默认为原生事件
    }
    
    1. 移除过滤器
      建议使用计算属性或方法调用去实现

    相关文章

      网友评论

          本文标题:Vue3.0的变化

          本文链接:https://www.haomeiwen.com/subject/tzdmgdtx.html