Vue3.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. 移除过滤器
    建议使用计算属性或方法调用去实现
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • Proxy API vue2.X 中的 defineProperty API优点:兼容性好,支持 IE9缺点:Ob...
    zhudying阅读 851评论 0 3
  • 9月30日,尤雨溪在medium个人博客上发布了vue3.0的开发思路,国内有翻译的版本,见文章最后的参考链接。3...
    指尖跳动阅读 417评论 0 1
  • 一、创建3.0项目 1. 2. main.js页面 注意:vue3.0不再支持2.0中引入、挂载Vue的方式。vu...
    大刀劈向鬼子阅读 1,485评论 0 1
  • 9月30日,尤雨溪在medium个人博客上发布了vue3.0的开发思路,国内有翻译的版本,见文章最后的参考链接。3...
    凌霄光阅读 11,213评论 3 29
  • 1.Vue3简介 2020年9月18日,Vue.js发布3.0版本,代号:One Piece(海贼王) 耗时2年多...
    洛梓煕阅读 271评论 0 1