美文网首页
composition

composition

作者: 没有昵_称 | 来源:发表于2021-05-20 17:19 被阅读0次

    mixin 缺点

    命名冲突

    mixin模式在运行时,会将mixin对象和组件对象合并,如果他们都有相同的属性名时,vue组件默认的合并策略是将本地选项覆盖mixin选项(可以配置合并策略),

    隐式依赖

    mixin和使用的他的组件之间没有层级关系,这就意味着组件可以使用mixin里定义的属性mixin也可以使用组件定义的属性,这样子可能辉导致一些问题,当我们要修一个组件时,修改mixin的属性名称,可能不会有什么问题,如果这个组件使用了大量的mixin时候,会不会破坏mixin?

    Composition Api

    mixin的缺点就是推动Composition Api的主要原因之一

    Composition Api的主要思想就是:我们将他们定义为从新的 setup函数返回的JavaScript变量,而不是将组件功能定义为对象属性

    setup

    setup函数是vue3新增的方法,可以理解为Composition Api入口,在beforeCreate之后create之前执行。

    参数有,propscontentprops是接受组件props数据,context是个上下文对象,注意setup中无法访问this
    context对象包含一下属性:attrsemitslots

    reactive

    reactive是用来创建个响应式对象的,相当于vue2observable

    export declare function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
    export declare type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
    export declare type UnwrapRef<T> = T extends Ref<infer V> ? UnwrapRefSimple<V> UnwrapRefSimple<T>
    泛型T继承了objec,所以reactive函数的参数是对象类型

    例子:

    export default {
      name: 'HelloWorld',
      setup() {
        const state = reactive({
          name: '',
          age: ''
        })
        return {
          state,
        }
      },
    }
    

    ref

    ref是给定植创建个响应式数据对象,ref()函数返回的是一个对象,该对象只包含一个属性value。ref函数的参数是任意类型

    export default {
      name: 'HelloWorld',
      setup() {
        const age = ref(18)
        return {
          age,
        }
      },
    }
    

    ⚠️注意
    如果将ref响应式数据挂在到reactive中,不用使用.value就可以访问,

    export default {
      name: 'HelloWorld',
      setup() {
        const num = ref (1)
        const obj = reactive({num})
        obj.num++
        console.log(obj.num) //2
        console.log(obj.num.value) //undefined
        console.log(obj.value) // 2 因为num是对象 对于挂在的时候是将地址赋值给objnum了
        return {
          obj
        }
      },
    }
    

    isRef

    isRef 用来判断某个值是否是ref创建出来的

    isRef<T>(r: Ref<T> | unknown): r is Ref<T> 返回值是个boolean类型
    unknown 类型,它是 any类型对应的安全类型
    isRef函数的参数是ref类型或者任意类型

    toRefs

    toRefs可以将reactive创建出来的响应式对象转化成普通对象,只不过这个对象的每个属性都是ref类型的响应式数据
    toRefs<T extends object>(object: T): ToRefs<T>
    接受参数是对象,返回值是 ToRefs<T>

    type ToRefs<T = any> = {
        [K in keyof T]: T[K] extends Ref ? T[K] : Ref<UnwrapRef<T[K]>>;
    };
    

    例子:

    <div>{{name}}{{age}}</div>
    export default {
      name: 'HelloWorld',
      setup() {
        const obj = reactive({name:'小明',age:18})
        return {
          ...toRefs(obj)
        }
      },
    }
    
    

    toRef

    为源响应式对象的某个属性创建一个ref对象,相当于浅拷贝
    区别ref,ref是深拷贝
    export declare function toRef<T extends object, K extends keyof T>(object: T, key: K): ToRef<T[K]>
    declare type ToRef<T> = [T] extends [Ref] ? T : Ref<UnwrapRef<T>>;
    第一个参数是对象类型,第二个参数要是第一个参数的属性,返回值是ref类型

    export default {
      name: 'HelloWorld',
      setup() {
         const obj = reactive({name:'小明',age:18})
         const age1 = toRef(obj, 'age')
         const age2 = ref(obj.age)
         const update = ()=> {
           obj.age ++ //obj会变,age1也会变
           age2.value ++ //obj和age1都不会变
         }
        return {
          update
        }
      },
    }
    
    

    computed

    computed,用来创建计算器属性,返回ref值

    export default {
      name: 'HelloWorld',
      setup() {
        const num = ref(1)
        const odd = computed(() => num.value * 2)
        //odd 只读
        return {
          num,
          odd
        }
      },
    }
    

    创建可读可写的 computed

    export default {
      name: 'HelloWorld',
      setup() {
        const num = ref(1)
        const odd = computed({
          // 取值
          get: () => num.value * 2,
          // 赋值
          set: val => num.value = val - 1
        })
        odd.value = 2 //触发之后num的值也会改变
        return {
          num,
          odd
        }
      },
    }
    

    watch

    监听数据的变化
    export declare function watch<T extends object, Immediate extends Readonly<boolean> = false>(source: T, cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>, options?: WatchOptions<Immediate>): WatchStopHandle

    第一个参数类型是:WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)

    第二个参数是回调函数

    第三个参数是可选的,类型是: WatchOptions?:{immediate?: Immediate;deep?: boolean;}

    返回值是个函数调用返回的函数会终止监听,类型:WatchStopHandle = () => void

    const state = ref(1)
    watch(()=>{
      console.log(state.value);
    })
    setTimeout(() => {
      state.value ++
    }, 1000)
    

    指定监听源

    const state = ref(1)
    watch(()=>state.value,(newVal,oldVal) => {
      console.log(oldVal,newVal);
    })
    watch(state,(newVal,oldVal) => {
      console.log(oldVal,newVal);
    })
    setTimeout(() => {
      state.value++
    }, 1000)
    

    监听多个

    const state = reactive({
      count: 0,
      name: '12'
    })
    watch([() => state.count,() => state.name], ([newCount,newName], [oldCount,oldName]) => {
      console.log('new',newCount,newName);
      console.log('old',oldCount,oldName);
    })
    
    setTimeout(() => {
      state.count++
      console.log(state.name )
      state.name = '121'
    }, 1000)
    
    

    在setup函数内创建的watch在组件销毁时,会自动停止监听,如果想明确的停止监听的话,可以调用watch函数的返回值watch函数返回值(WatchStopHandle = () => void)是一个函数

    const state = reactive({
      count: 0,
      name: '12'
    })
    const stop = watch([() => state.count,() => state.name], ([newCount,newName], [oldCount,oldName]) => {
      console.log('new',newCount,newName);
      console.log('old',oldCount,oldName);
    })
    setInterval(() => {
      state.count++
      state.name = '121'
    }, 1000)
    setTimeout(() => {
      stop()
    }, 2000);
    

    watchEffect

    vue3 新增的属性监听api

    watchEffect(effect: WatchEffect, options?: WatchOptionsBase): WatchStopHandl
    
    type WatchEffect = (onInvalidate: InvalidateCbRegistrator) => void;
    
    nterface WatchOptionsBase {
      flush?: 'pre' | 'post' | 'sync';
      onTrack?: ReactiveEffectOptions['onTrack'];
      onTrigger?: ReactiveEffectOptions['onTrigger'];
    }
    
    type WatchStopHandle = () => void;
    

    和watch的区别:
    1、watchEffect不需要指定监听属性,可以自动收集依赖,只要我们回调中引用了响应式的属性,那么这些属性变更的时候,这个回调都会执行,而watch只能监听指定的属性而做出变更(v3中可以同时监听多个)

    2、watch可以获取到新值和旧值,而watchEffect获取不到

    3、watchEffect会在组件初始化的时候就会执行一次与computed同理,而收集到的依赖变化后,这个回调才会执行,而watch不需要,除非设置了指定参数。

    setup(props, contex) {
      const state = reactive({
        num: '121',
        msg: '111'
      })
      const stop = watchEffect(()=>{
        console.log('watchEffect', state.num)
        console.log('watchEffect', state.msg)
      })
      stop()//停止监听
      return {
        state,
      }
    }
    

    假设我们现在用一个用户ID去查询用户的详情信息,然后我们监听了这个用户ID, 当用户ID 改变的时候我们就会去发起一次请求,这很简单,用watch 就可以做到。 但是如果在请求数据的过程中,我们的用户ID发生了多次变化,那么我们就会发起多次请求,而最后一次返回的数据将会覆盖掉我们之前返回的所有用户详情。这不仅会导致资源浪费,还无法保证 watch 回调执行的顺序。而使用watchEffect我们就可以做到.

    onInvalidate(fn)传入的回调会在watchEffect重新运行或者watchEffect停止的时候执行。

    watchEffect(()=>{
      //异步调用api
      const obj= getData(user.id)
      onInvalidate(()=>{
        // 取消API的调用
        obj.cancel()
      })
    })
    

    customRef

    创建一个自定义的ref,并对其依赖跟踪和更新触发进行显式控制
    场景:实现防抖输入框

    export declare function customRef<T>(factory: CustomRefFactory<T>): Ref<T>;
    
    declare type CustomRefFactory<T> = (track: () => void, trigger: () => void) => {
        get: () => T;
        set: (value: T) => void;
    };
    
    

    customRef参数类型是CustomRefFactory<T>,参数是个函数,返回值是个对象{get: () => T;set: (value: T) => void;}
    customRef 返回值是 Ref<T> 类型,说明返回值是个ref对象

    例子:

    let keyworld = useDebouncedRef('')
        function useDebouncedRef(value, t = 500) {
          let tims = null;
          return customRef((track, trigger) => {
            return {
              get: () => {
                track();
                return value;
              },
              set: (newValue) => {
                clearTimeout(tims);
                setTimeout(() => {
                  value = newValue;
                  trigger();
                }, t);
              },
            };
          });
        };
    

    自定义Hook函数

    相当于vue2的mixin,相对于vue2的mixin技术,自定义hook函数知道代码来源,方便复用
    例子:

    //hooks.js
    import { reactive } from 'vue';
    
    export default function () {
      const state = reactive({
        num: 1,
        msg: 'text'
      })
      return {
        state
      }
    }
    //引用
    import hooks from '../utils/hooks.js';
    export default {
      name: 'HelloWorld',
      props: {
        msg: String,
      },
      setup(props, contex) {
        const { state } = hooks();
        return {
          state,
        };
      },
    };
    
    

    readonly与shallowReadonly

    readonly 深度只读
    shallowReadonly 浅度只读

    template ref

    通过ref() 可以拿到页面上的元素和组件
    例子:

    <template>
      <button @click="tap">点击</button>
      <p ref="pRef">测试</p>
      <HelloWorld ref="helloRef"></HelloWorld>
    </template>
    
    setup(props, contex) {
        const pRef = ref(null)
        const helloRef = ref(null)
        const tap = ()=>{
          helloRef.value.divClick() //调用组件的方法
          helloRef.value.count //获取组件的值
          pRef.value //dom节点
        }
        return {
          pRef,
          helloRef,
          tap
        };
      },
    
    

    相关文章

      网友评论

          本文标题:composition

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