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