美文网首页
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的变化

    1、兼容除了渲染函数 API 和作用域插槽语法之外的所有内容都将保持不变,或者通过兼容性构建让其与 2.x 保持兼...

  • vue3.0 变化

    Proxy API vue2.X 中的 defineProperty API优点:兼容性好,支持 IE9缺点:Ob...

  • Vue3.0 API的使用

    Vue3.0 整体优势 proxy数据劫持代替object.defineProperty(特点:可以监听数组变化,...

  • Vue3新特性笔记

    vue3.0的主要变化 响应式基本原理:Object.defineProperty -> Proxy,提高性能 初...

  • 解读vue3.0的变化

    9月30日,尤雨溪在medium个人博客上发布了vue3.0的开发思路,国内有翻译的版本,见文章最后的参考链接。3...

  • vue3.0 变化点

    vue3.0 变化点: 1.源码体积优化: 引入tree-shaking:通过编译阶段的静态分析,找到没有引入的模...

  • vue3.0有哪些变化?

    ** 发展历程** vue.js1.x-----vue.js2.x 引入了虚拟DOM vue.js2.x问题 1)...

  • Vue3.0入门指南

    第一章、走进Vue3.0 2-1、下载Vue3.0的单文件核心库 vue3.0 源码下载地址: https://u...

  • 【vue3.0】vue3.0中监听路由router变化

    问题:vue3.0中的监听路由已经不能使用watch的方法 改进方式,使用onBeforeRouteUpdate ...

  • 全面改革:解读vue3.0的变化

    9月30日,尤雨溪在medium个人博客上发布了vue3.0的开发思路,国内有翻译的版本,见文章最后的参考链接。3...

网友评论

      本文标题:Vue3.0的变化

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