vue3学习记录

vue3

main.js

import { createApp } from 'vue' //引入createApp工产函数
import App from './App.vue'

createApp(App).mount('#app')

setup

与vue2不同,vue3中的数据,方法都是写在setup函数中,通过return暴露出来

setup() {
    let name = 'test';
    function test() {
        console.log('test');
    };
    return {
        name,
        test
    }
}

setup不能是一个async函数,返回值不是return的对象而是promise,模板看不到return对象中的属性(若要使用async,需要Suspense和异步组件引入的配合

ref函数(refImpl reference implementation参考实例)

  1. 作用:定义一个响应式的数据
  2. 语法:const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
    • 改变该数据时用xxx.value
    • 在模板中读取数据不需要加.value,直接{{xxx}}即可
  3. 注:
    • 接收的数据可以是基本类型,也可以是对象类型
    • 基本类型的数据:响应式依然是靠Object.defineProperty()中的getset实现
    • 对象类型的数据:内部运用了vue3中的新函数——reactive函数

reactive函数

  1. 作用:定义一个对象类型的响应式数据(基本类型不要使用,用ref)
  2. 语法:const xxx = reactive(源对象)接收一个对象或数据,返回一个代理对象(proxy的实例对象,简称proxy对象)
  3. reactive定义的响应式数据是深层次的
  4. 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据

vue2和vue3响应式原理分析

vue2

  1. 实现原理:
    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)
    • 数组类型:通过重写更新数据的一系列方法来实现拦截(对数据的变更方法进行了包裹)
  2. 存在问题:
    • 新增属性、删除属性,界面不会更新
    • 直接通过下标修改数组,界面不会自动更新

vue3

实现原理:

  1. 通过Proxy(代理):拦截对象中任意属性的变化,包括属性值的读写、属性的添加、属性的删除等
  2. 通过Reflect(反射):对源对象的属性进行操作
  3. 实例:
    const p = new Proxy(person, {
         get(target, propName){
             console.log(`get,身上的${propName}属性`, target, propName);
             // return target[propName];
             return Reflect.get(target, propName);
         },
         set(target, propName, value) {
             console.log(`set,身上的${propName}属性修改为${value}`);
             // target[propName] = value;
             return Reflect.set(target, propName, value);
         },
         deleteProperty(target, propName) {
             console.log(`delete,身上的${propName}属性删除`);
             // return delete target[propName];
             return Reflect.deleteProperty(target, propName);
         }
     })
    

reactive对比ref

定义数据角度对比

  1. ref用来定义基本类型数据
  2. reactive用来定义对象(或数组)类型数据
  3. 注:ref也可以用来定义对象(或数组),会自动调用reactive

从原理角度对比

  1. ref通过Object.defineProperty()getset来实现响应式(数据劫持)
  2. reactive通过Proxy来实现响应式,并通过Reflect来操作源对象内部的数据

从使用角度对比

  1. ref定义的数据:操作数据需要.value,读取数据时不用.value
  2. reactive定义的数据:直接操作和读取即可

setup的两个注意点

setup执行的时机

在beforeCreate之前执行一次,this是undefined

setup的参数

props:值为对象,包含外部传递过来且内部声明接收了的属性
context:上下文对象

  1. attrs:值为对象,包含组件外部传递过来但没有在props中配置声明的属性,相当于this.$attrs
  2. slots:收到的插槽内容,相当于this.$slots
  3. emit:分发自定义事件的函数,相当于this.$emit

计算属性与监视

computed函数

  1. 与vue2中computed配置功能一致
  2. 写法:
       setup() {
        let person = reactive({
          firstName: '三',
          lastName: '张'
        });
        //计算属性——简写(没有考虑计算属性被修改的情况)
        /* person.fullName = computed(()=>{
          return person.lastName + '-' + person.firstName;
        }) */
        //计算属性——完整写法
        person.fullName = computed({
          get() {
            return person.lastName + '-' + person.firstName;
          },
          set(value) {
            const nameArr = value.split('-');
            person.firstName = nameArr[1];
            person.lastName = nameArr[0];
          }
        })
        return {
          person
        }
      }
    

watch函数

  1. 与vue2中watch配置功能一致
  2. 分为以下几种情况:
    • 情况一:监视ref所定义的一个响应式数据
    watch(sum, (newValue, oldValue)=>{
      console.log('sum变化了', newValue, oldValue);
    }, {immediate: true})
    
    • 情况二:监视ref所定义的多个响应式数据
    watch([sum, msg], (newValue, oldValue)=>{
      console.log('值发生改变', newValue, oldValue);
    }, {immediate: true})
    
    • 情况三:监视reactive定义的响应式数据时,oldValue无法正确获取,强制开启deep深度监视(deep配置无效)
    //此时deep无效
    watch(person, (newValue, oldValue)=>{
      console.log('person变化了',newValue, oldValue);
    }, {deep:false})
    
    • 情况四:监视reactive所定义的一个响应式数据中的某个属性
    watch(()=>person.name, (newValue, oldValue)=>{
      console.log('person变化了',newValue, oldValue);
    })
    
    • 情况五:监视reactive定义的响应式数据中的某个属性时,deep配置有效
    //此时不配置deep无法获取数据变化
    watch([()=>person.name, ()=>person.age], (newValue, oldValue)=>{
      console.log('person的name或age变化了',newValue, oldValue);
    })
    

watchEffect函数

  1. watch:既要指明监视的属性,也要指明监视的回调
  2. watchEffect:不用指明监视哪个属性,监视的回调中用到哪个属性就监视哪个属性
  3. watchEffect有点像computed,computed注重的是计算出来的值,需要返回值;watchEffect注重的是过程,不用返回值
     watchEffect(()=>{
       const x1 = sum.value;
       const x2 = person.job.j1.salary;
       console.log('watchEffect执行了');
     })
    

vue3生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4fXKFYNw-1658854391684)(https://v3.cn.vuejs.org/images/lifecycle.svg)]

自定义hook函数

  1. hook本质是一个函数,把setup函数中使用的Composition API进行了封装
  2. 类似于vue2中的mixin
  3. 自定义hook的优势:复用代码,让setup中的逻辑更加清晰易懂

toRef和toRefs

  1. 作用:创建一个ref对象,其value值指向另一个对象中的某个属性
  2. 语法:const name = toRef(person, 'name')
  3. 应用:要将响应式对象中的某个属性单独提供给外部使用
  4. 扩展:toRefstoRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)

其他Composition API

shallowReactive和shallowRef

  1. shallowReactive:只处理对象最外层属性的响应式(浅响应式);若有一个对象数据,结构比较深,但变化时只是外层属性变化可以使用shallowReactive
  2. shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理;若有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换,使用shallowRef

readonly和shallowReadonly

  1. readonly:让响应式数据变为只读(深层次)
  2. shallowReadonly:浅层次只读

toRow和markRow

  1. toRaw:将一个由reactive生成的响应式对象转为普通对象;用于读取响应式对象对应的普通对象,对这个普通对象的所有操作不会引起页面更新
  2. markRaw:标记一个对象使之不会变成响应式对象;当有些值不应被设置为响应式或者渲染具有不可变数据源的大列表时,跳过响应式转换以提高性能

customRef

  1. 自定义ref,控制依赖项的更新触发
  2. 实现防抖效果:
   <template>
     <input type="text" v-model="keyWord">
     <h3>{{keyWord}}</h3>
   </template>
   <script>
   import { ref, customRef } from 'vue';
   export default {
     name: 'App',
     setup() {
       //自定义ref:myRef
       function myRef(value, delay) {
         return customRef((track, trigger) => {
           let timer;
           return {
             get: function () {
               console.log('myRef数据被读取');
               track(); //通知vue追踪value的变化
               return value;
             },
             set: function (newValue) {
               console.log(`myRef数据被修改为${newValue}`);
               clearTimeout(timer);
               timer = setTimeout(() => {
                 value = newValue;
                 trigger(); //通知vue重新解析模板
               }, delay);
             }
           };
         });
       }

       // let keyWord = ref('hello');
       let keyWord = myRef('hello', 500);
       return { keyWord };
     }
   };
   </script>

provide与inject

  1. 作用:实现祖孙组件间通信
  2. 父组件用provide来提供数据,后代组件用inject来接收数据
  3. 使用:
  • 父组件中:
import { provide } from 'vue';
setup() {
  let car = { name: 'car', price: 100 };
  provide('car', car); //第一个为自定义的名称,第二个为所提供的数据
}
  • 子组件中:
import { inject } from 'vue';
setup() {
  let car = inject('car');
  return { car };
}

响应式数据的判断

  • isRef:检查是否为ref对象
  • isReactive:检查是否由reactive创建的响应式代理
  • isReadonly:检查是否由readonly创建的只读代理
  • isProxy:检查是否由reactive或者readonly创建的代理(readonly创建的也为代理

Composition API的优势

  1. 之前vue2的Option API中,若要新增或修改一个需求,要分别在data、methods和computed中进行修改
  2. 在vue3中,所有的配置放在setup中,可以更优雅的组织代码和函数,让相关代码更加直观有序

新的组件

Fragment

  1. 在vue2中必须要有跟标签
  2. 在vue3中可以没有跟标签,内部将多个标签包含在一个fragment虚拟元素中
  3. 好处是减少标签层级,减小内存占用

Teleport

可以将组件html结构移动到指定位置

<teleport to='body'>
      <div class="mask" v-if="isShow">
        <div class="dialog">
          <h3>弹窗</h3>
          <h4>内容</h4>
          <button @click="isShow = false;">关闭</button>
        </div>
      </div>
</teleport>

Suspense

  1. 作用:等待异步组件时渲染一些额外内容
  2. 使用:
    • 异步引入组件(defineAsyncComponent)
    import { defineAsyncComponent } from 'vue'; 
    const Son = defineAsyncComponent(() => import('./components/Son.vue')); //异步引入
    
    • 使用Suspense包裹组件,并配置好v-slot:defaultv-slot:fallback
     <div class="app">
       <h3>我是app</h3>
       <Suspense>
         <template v-slot:default> //默认展示的
           <Son></Son>
         </template>
         <template v-slot:fallback> //default展示出来前展示
           <h3>just wait a minute</h3>
         </template>
       </Suspense>
     </div>
    

其他

全局API的转移

vue2全局API(Vue)vue3全局API(app)
Vue.config.xxxapp.config.xxx
Vue.config.productionTip移除
Vue.componentapp.component
Vue.directapp.direct
Vue.mixinapp.mixin
Vue.useapp.use
Vue.prototypeapp.config.globalProperties

其他改变

  1. data选项应始终声明为函数形式
  2. 过渡类名的改变
    vue2vue3
    .v-enter.v-enter-from
    .v-enter-to.v-enter-to
    .v-leave.v-leave-from
    .v-leave-to.v-leave-to
  3. 移除keyCode作为v-on的修饰符,同时也不再支持config.keyCodes(自定义别名按键)
  4. 移除v-on.native修饰符(子组件中使用的从父组件接收的自定义事件需要emits:['xxx']进行声明)
  5. 移除过滤器filter
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值