2021年2月27日星期六

Coposition 详解

LifeCycle Hooks

在新版的生命周期函数,可以按需导入到组件中,且只能在setup()函数中使用.

import { onMounted, onUnmounted } from 'vue';export default { setup () {  onMounted(()=>{   //  });  onUnmounted(()=> {   //  }); }};



 

 

生命周期2.x与Composition之间的映射关系

  • beforeCreate -> use setup()
  • created -> use setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

 

setup

理解

setup()函数是vue3中专门新增的方法,可以理解为Composition Api的入口.

执行时机

在beforecreate之后,create之前执行.

接收props数据

export default { props: { msg: {  type: String,  default: () => {} } }, setup(props) { console.log(props); }}



context:

setup()的第二个参数是一个上下文对象,这个上下文对象大致包含了这些属性,注意:在setup()函数中无法访问this

const MyComponent = { setup(props, context) { context.attrs context.slots context.parent context.root context.emit context.refs }}



 

reactive

reactive是用来创建一个响应式对象,等价于2.x的Vue.observable,具体可以参考下面demo。

<template> <div>  <p @click="incment()">   click Me!  </p>  <p>   一:{{ state.count }} 二: {{ state.addCount }}  </p> </div></template><script>import { reactive } from 'vue';export default { setup () {  const state = reactive({//创建响应式数据   count: 0,   addCount: 0  });  function incment () {   state.count++;   state.addCount = state.count * 2;  }      return {   state,   incment  }; }};</script>



 

ref

基本语法

ref()函数用来给定的值创建一个响应式的数据对象,ref()的返回值是一个对象,这个对象上只包含一个.value属性.下面是基本数据类型创建步骤.

import { ref } from 'vue';export default { setup () {  const valueNumber = ref(0);  const valueString = ref('hello world!');  const valueBoolean = ref(true);  const valueNull = ref(null);  const valueUndefined = ref(undefined);  return {   valueNumber,   valueString,   valueBoolean,   valueNull,   valueUndefined  }; }};



image.png

 

在template中访问ref创建的响应式数据

import { ref } from 'vue';export default { setup () {  const value = ref(1);  return {   value,   msg: 'hello world!'  }; }};



<template> <p>  {{ value }} {{ msg }} </p></template>



将ref响应式数据挂载到reactive中

当把ref()创建出来值直接挂载到reactive()中时,会自动把响应式数据对象的展开为原始的值,不需要通过.value就可以直接访问到.

import { ref, reactive } from 'vue';export default { setup () {  const count = ref(1);  const state = reactive({   count  });      console.log(state.count);//1 可以直接访问到,不需要通过.value就可以直接访问到  state.count++;  console.log(count.value);//2 我们发现,最初count值也发生了改变  return {   count  }; }};



新的ref会覆盖旧的ref,实例如下:

import { ref, reactive } from 'vue';export default { setup () {  const count = ref(1);  const state = reactive({   count  });  const newCount = ref(9);  state.count = newCount;  state.count++;  console.log(state.count, newCount, count);// 10 10 1  return {   count  }; }};



image.png

我们发现,这次的count值却没有发生改变,还是原始值1,是因为新创建的newCount替换并覆盖了之前的count值,取代了它的位置.

 

 

isRef

用来判断某个值是否为ref创建出来的对象,场景:当需要展开某个值可能是ref()创建出来的对象时.

import { ref, isRef } from 'vue';export default { setup () {  const count = ref(1);  const unwrappend = isRef(count) ? count.value : count;  return {   count,   unwrappend  }; }};



 

toRefs

torefs()函数可以将reactive()创建出来的响应式对象转换为普通的对象,只不过这个对象上的每个属性节点都是ref()类型的响应式数据

<template> <p>   <!-- 可以不通过state.value去获取每个属性 -->  {{ count }} {{ value }} </p></template><script>import { ref, reactive, toRefs } from 'vue';export default { setup () {  const state = reactive({   count: 0,   value: 'hello',  })  return {   ...toRefs(state)  }; }};</script>



 

toRef

概念:为源响应式对象上的某个属性创建一个ref对象,二者内部操作的是同一个数据值,更新时二者是同步的。相当于浅拷贝一个属性.

区别ref: 拷贝的是一份新的数据单独操作,更新时相互不影响,相当于深拷贝。

场景:当要将某个prop的ref传递给某个复合函数时,toRef很有用.

import { reactive, ref, toRef } from 'vue'export default { setup () {  const m1 = reactive({   a: 1,   b: 2  })  const m2 = toRef(m1, 'a');  const m3 = ref(m1.a);  const update = () => {   // m1.a++;//m1改变时,m2也会改变   // m2.value++; //m2改变时m1同时改变   m3.value++; //m3改变的同时,m1不会改变  }  return {   m1,   m2,   m3,   update  } }}



 

computed

computed()用来创建计算属性,返回值是一个ref的实例。

 

创建只读的计算属性

import { ref, computed } from 'vue';export default { setup () {  const count = ref(0);  const double = computed(()=> count.value + 1);//1  double++;//Error: "double" is read-only  return {   count,   double  }; }};



 

创建可读可写的计算属性

在使用computed函数期间,传入一个包含get和set函数的对象,可以额得到一个可读可写的计算属性

// 创建一个 ref 响应式数据const count = ref(1)// 创建一个 computed 计算属性const plusOne = computed({ // 取值函数 get: () => count.value + 1, // 赋值函数 set: val => { count.value = val - 1 }})// 为计算属性赋值的操作,会触发 set 函数plusOne.value = 9// 触发 set 函数后,count 的值会被更新console.log(count.value) // 输出 8



 

watch

watch()函数用来监视某些数据项的变化,从而触发某些特定的操作,看下面这个案例,会实时监听count值的变化. 查看官方文档API

import { ref, watch } from 'vue';export default { setup () {  const count = ref(1);  watch(()=>{   console.log(count.value, 'value');  })  setInterval(()=>{   count.value++;  },1000);  return {   count,  }; }};



 

监听指定的数据源

监听reactive的数据变化

import { watch, reactive } from 'vue';export default { setup () {  const state = reactive({   count: 0  })  watch(()=>state.count,(count, prevCount)=>{   console.log(count, prevCount);//变化后的值 变化前的值  })  setInterval(()=>{   state.count++;  },1000);  return {   state  }; }};



监听ref类型的数据变化

import { ref, watch } from 'vue';export default { setup () {  const count = ref(0);  watch(count,(count, prevCount)=>{   console.log(count, prevCount);//变化后的值 变化前的值  })  setInterval(()=>{   count.value++;  },1000);  return {   count  }; }};



 

监听多个指定数据变化

监听reactive类型数据变化

import { watch, reactive } from 'vue';export default { setup () {  const state = reactive({   count: 0,   msg: 'hello'  })  watch([()=> state.count, ()=> state.msg],([count, msg], [prevCount, prevMsg])=>{   console.log(count, msg);   console.log('---------------------');   console.log(prevCount, prevMsg);  })  setTimeout(()=>{   state.count++;   state.msg = 'hello world';  },1000);  return {   state  }; }};



 

监听ref类型数据变化

import { ref, watch } from 'vue';export default { setup () {  const count = ref(0);  const msg = ref('hello');  watch([count, msg],([count, name], [prevCount, prevname])=>{   console.log(count, name);   console.log('---------------------');   console.log(prevCount, prevname);  })  setTimeout(()=>{   count.value++;   msg.value = 'hello world';  },1000);  return {   count,   msg  }; }};



 

清除监视

在setup()函数内创建的watch监视,会在当前组件被销毁的时候自动停止。如果想要明确的停止某个监视,可以调用watch()函数的返回值即可

// 创建监视,并得到 停止函数const stop = watch(() => { /* ... */})// 调用停止函数,清除对应的监视stop()



 

清除无效的异步任务

有时候watch()监视的值发生了变化,我们期望清除无效的异步任务,此时watch回调函数中提供了cleanup registrator function来执行清除工作

    • 场景
      • watch被重复执行了
      • watch被强制stop()
<template> <p>  <input type="text" v-model="keyword"> </p></template><script>import { watch, ref } from 'vue';export default { setup () {  const keyword = ref('');  const asyncPrint = val => {   return setTimeout(()=>{    console.log(val);   },1000);  }  watch(keyword, (keyword, prevKeyword, onCleanup) => {   const timeId = asyncPrint(keyword);   onCleanup(()=> clearTimeout(timeId));  }, {lazy: true})  return {   keyword  }; }};</script>



 

watchEffect

vue3中新增的api,用于属性监听.

与watch有什么不同?

      • watchEffect不需要指定监听属性,可以自动收集依赖,只要我们回调中引用了响应式的属性,那么这些属性变更的时候,这个回调都会执行,而watch只能监听指定的属性而做出变更(v3中可以同时监听多个)
      • watch可以获取到新值和旧值,而watchEffect获取不到
      • watchEffect会在组件初始化的时候就会执行一次与computed同理,而收集到的依赖变化后,这个回调才会执行,而watch不需要,除非设置了指定参数。

 

基础用法

import { watchEffect, ref } from 'vue'setup () { const userID = ref(0) watchEffect(() => console.log(userID)) setTimeout(() => {  userID.value = 1 }, 1000) /*  * LOG  * 0   * 1 */ return {  userID } }



停止监听

如果watchEffect是在setup或者生命周期里面注册的话,在取消挂在的时候会自动停止。

//停止监听const stop = watchEffect(() => { /* ... */})// laterstop()



使 side effect 无效

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

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

watchEffect(() => {  // 异步api调用,返回一个操作对象  const apiCall = someAsyncMethod(props.userID)  onInvalidate(() => {  // 取消异步api的调用。  apiCall.cancel()  })})



 

shallowReactive

概念:只处理对象最外层属性的响应式(也就是浅响应式),所以最外层属性发生改变,更新视图,其他层属性改变,视图不会更新.

场景:如果一个对象的数据结构比较深,但变化只是最外层属性.

import { shallowReactive } from 'vue'export default { setup() {  const obj = {   a: 1,   first: {    b: 2,    second: {     c: 3    }   }  }  const state = shallowReactive(obj)  function change1() {   state.a = 7  }  function change2() {   state.first.b = 8   state.first.second.c = 9   console.log(state);  }  return { state } }}



 

shallowRef

概念:只处理了value的响应式,不进行对象的reactive处理.

场景:如果有一个对象数据,后面会产生新的对象替换.

 

import { shallowRef } from 'vue'export default { setup () {  const m1 = shallowRef({a: 1, b: {c: 2}})  const update = () => {   m1.value.a += 1  }  return {   m1,   update  } }}



 

customRef

创建一个自定义的ref,并对其依赖跟踪和更新触发进行显式控制.

场景:使用customRef实现输入框防抖

<template> <div>  <input v-model="keyword" placeholder="搜索关键字"/>  <p>{{keyword}}</p> </div></template><script>import { customRef } from 'vue'export default { setup () {  const keyword = useDebouncedRef('', 500)  console.log(keyword)  return {   keyword  } }}function useDebouncedRef(value, delay = 200) { let timeout; return customRef((track, trigger) => {  return {   get() {    // 告诉Vue追踪数据    track()    return value   },   set(newValue) {    clearTimeout(timeout)    timeout = setTimeout(() => {     value = newValue     // 告诉Vue去触发界面更新     trigger()    }, delay)   }  } })}</script>



 

自定义Hook函数

自定义hook的作用类型于vue2中的mixin技术。

优势:清楚知道代码来源,方便复用

案例:收集用户点击的页面坐标

 

hook/useMousePosition.js

import { ref, onMounted, onUnmounted } from "vue";export default function useMousePosition() { // 初始化坐标数据 const x = ref(-1); const y = ref(-1); // 用于收集点击事件坐标的函数 const updatePosition = e => {  x.value = e.pageX;  y.value = e.pageY; }; // 挂载后绑定点击监听 onMounted(() => {  document.addEventListener("click", updatePosition); }); // 卸载前解绑点击监听 onUnmounted(() => {  document.removeEventListener("click", updatePosition); }); return { x, y };}



 

模版中使用hook函数

<template> <div>  <p>{{ x }}</p>  <p>{{ y }}</p> </div></template><script>import useMousePosition from '@/hook/useMousePosition'export default { setup () {  const {x, y} = useMousePosition();  return {   x,   y  } }}</script>



 

readonly与shallowReadonly

  • readonly:
    • 深度只读数据
    • 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
    • 只读代理是深层的:访问的任何嵌套 property 也是只读的。
  • shallowReadonly
    • 浅只读数据
    • 创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
  • 应用场景:
    • 在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除

 

<template> <div @click="update()">  <p>{{ a }}</p>  <p>{{ b }}</p> </div></template><script>import { reactive, readonly, shallowReadonly } from 'vue'export default { setup () {  const state = reactive({   a: 1,   b: {    c: 2   }  })  const m = readonly(state);  const m2 = shallowReadonly(state);  const update = () => {   m.a++ //无法修改 只读   m2.a++ //无法修改   m2.b.c++ //可以修改 视图层数据改变  }  return {   ...toRefs(state),   update  } }}</script>



 

Template refs

通过ref()还可以引用页面上的元素或者组件.

 

元素引用

使用ref()函数创建DOM引用,需在onMounted中获取.

<template> <div>  <p ref="dom">hello</p> </div></template><script>import { ref, onMounted } from 'vue';export default { setup () {  const dom = ref(null);  onMounted(()=> {   console.log(dom.value)//当前dom元素  });  return {   dom  } }};</script>



 

组件引用

<template> <div>  <Test ref="comRef"/> </div></template><script>import { ref, onMounted } from 'vue';import Test from "./test2";export default { components: { Test }, setup () {  const comRef = ref(null);  onMounted(()=> {   comRef.value.coun;//获取子组件值   comRef.value.Handle();//调用子组件函数  })  return {   comRef  } }};</script>



 

  • test2
<template> <p>  {{ count }} </p></template><script>import { ref } from 'vue';export default { setup () {  const count = ref('count');  const Handle = (()=> {   console.log('hello');  })  return {   count,   Handle  } }};</script>



 

createComponent

这个函数不是必须的,除非你想完美结合TypeScript提供的类型推断来进行项目开发

场景:这个函数仅仅提供了类型推断,能为setup()函数中的props提供完整的类型推断.

import { createComponent } from 'vue'export default createComponent({ props: { foo: String }, setup(props) { props.foo // <- type: string }})



 

getCurrentInstance

描述:可以获取当前组件的实例,然后通过ctx属性获取当前上下文,这样我们就可以在steup中使用router和vuex了.

<script>import { getCurrentInstance } from 'vue'export default { setup () { const { ctx } = getCurrentInstance() console.log(ctx.$router.currentRoute.value) //当前路径 //与以前this获取原型上东西一样 //ctx.$parent 父组件 // ctx.$nextTick 组件更新完毕  // ctx.$store VueX }}</script>



 

学习手册

https://vue3js.cn/vue-composition-api









原文转载:http://www.shaoqun.com/a/591582.html

跨境电商:https://www.ikjzd.com/

epa认证:https://www.ikjzd.com/w/1769

hts:https://www.ikjzd.com/w/525


LifeCycleHooks在新版的生命周期函数,可以按需导入到组件中,且只能在setup()函数中使用.import{onMounted,onUnmounted}from'vue';exportdefault{setup(){onMounted(()=>{//});onUnmounted(()=>{//});}};生命周期2.x与Composition之间的映射关系b
imgur:https://www.ikjzd.com/w/156
mile:https://www.ikjzd.com/w/1746
rakuten:https://www.ikjzd.com/w/2718
口述:妹妹想跟我一起嫁给老公:http://lady.shaoqun.com/m/a/108158.html
亚马逊自动定价逻辑更新,可在特定情况下提高商品价格!:https://www.ikjzd.com/home/107559
勤商网:https://www.ikjzd.com/w/2219

没有评论:

发表评论

注意:只有此博客的成员才能发布评论。