composition

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
    };
  },

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

推荐阅读更多精彩内容