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

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

我们发现,这次的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(() => {
/* ... */
}) // later
stop()

使 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

Coposition 详解的更多相关文章

  1. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  2. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  3. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  4. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  5. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  6. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  7. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  8. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  9. Node.js npm 详解

    一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...

随机推荐

  1. P3381 [模板] 最小费用最大流

    EK  + dijkstra (2246ms) 开氧气(586ms) dijkstra的势 可以处理负权 https://www.luogu.org/blog/28007/solution-p3381 ...

  2. java大数函数(附官方文档链接)

    java文档 字段摘要 static BigInteger ONE           BigInteger 的常量 1. static BigInteger TEN           BigInt ...

  3. cf-1230C Anadi and Domino

    题目链接:http://codeforces.com/contest/1230/problem/C 题意: 有21 个多米诺骨牌,给定一个无向图(无自环,无重边),一条边上可以放一个多米诺骨牌.如果两 ...

  4. 关于贪心算法的经典问题(算法效率 or 动态规划)

    如题,贪心算法隶属于提高算法效率的方法,也常与动态规划的思路相挂钩或一同出现.下面介绍几个经典贪心问题.(参考自刘汝佳著<算法竞赛入门经典>).P.S.下文皆是我一个字一个字敲出来的,绝对 ...

  5. Educational Codeforces Round 91 (Rated for Div. 2) B. Universal Solution (贪心)

    题意:石头剪刀布,bot有一串字符,表示他要出什么,你需要事先确定你的出招方案,然后遍历bot的字符串,从\(i\)位置开始跑一个循环,每次跑都要记录你赢的次数贡献给\(sum\),现要求\(\fra ...

  6. codeforces 868B

    B. Race Against Time time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  7. Leetcode(2)-两数相加(包含链表操作的注意事项)

    给定两个非空链表来表示两个非负整数.位数按照逆序方式存储,它们的每个节点只存储单个数字.将两数相加返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. 示例: 输入:(2 -& ...

  8. PHP7.1后webshell免杀

    严格的D盾 D盾说,我是个严格的人,看到eval我就报木马,"看着像"="就是"木马,宁可错杀一千,绝不放过一个.好了,多说无益,一起看看严格的D盾是如何错杀的 ...

  9. JavaScript 高级程序设计 (第4版) 思维导图/脑图 All In One

    JavaScript 高级程序设计 (第4版) 思维导图/脑图 All In One JavaScript 高级程序设计 (第4版) 思维导图下载 JavaScript 高级程序设计 (第4版) 脑图 ...

  10. js double 精度损失 bugs

    js double 精度损失 bugs const arr = [ 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 ]; // [ ...