插件安装 及 注入
# 安装插件
npm install @vue/composition-api
yarn add @vue/composition-api
# 在 main.js 文件中导入并注册使用
import VueCompositionApi from '@vue/composition-api';
Vue.use(VueCompositionApi);
setup 介绍 及 基础使用
setup 函数是一个新的组件选项,作为在组件内使用 Composition API 的入口
创建组件实例,然后初始化 props,紧接着就调用 setup 函数。
setup 返回一个对象,暴露给模板使用,对象的属性将会被合并到组件模板的渲染上下文
注意:setup 返回的 ref 在模板种会自动解开,不需要写 .value
<script>
<template>
<div>
{{ count }} - {{ object.foo }}
</div>
</template>
// 引入插件
import { ref, reactive } from '@vue/composition-api'
export default {
setup() {
const count = ref(0)
const object = reactive({ foo: 'bar' })
// 暴露给模板使用
return {
count,
object
}
}
}
</script>
参数 props
setup 函数接收 props 作为第一个参数,props 对象是响应式的,watchEffect 或 watch 会观察和响应 props 的更新。
注意:
- 不能在 setup 函数的形参内解构 props 对象,否则会失去响应性
- 在开发过程种,props 对象的值是不能改变的,否则会触发警告
<script>
export default {
props: {
name: String
},
// 注意:不能在直接解构第一个参数,可以在函数内部进行解构
setup(props) {
watchEffect(() => {
console.log(props.name)
})
}
}
</script>
setup 函数第二个参数 context 上下文对象
setup 函数的第二个参数是一个上下文对象,注意:在 setup() 函数种无法访问到 this
export default {
setup(props, context) {
// 注意,context 可以在行上进行结构, 也可以在函数内进行结构
const { root, emit, refs, attrs, slots, parent } = context
}
}
使用 defineComponent 使 setup() 参数获得类型推断的传递
export default defineComponent({
name: '组件名称',
props: {}
setup(props, { root, emit, refs }) {
return {}
}
})
响应式系统 API - reactive
import { reactive } from '@vue/composition-api'
export default {
setup(props, context) {
// 创建响应式数据对象
const state = reactive({ count: 0 })
// 返回响应式对象,提供给 template 使用
// 模板使用方式, <p> state.count: {{state.count}} </p>
return state
}
}
响应式系统 API - ref
ref() 函数用来根据给定的值创建一个响应式的数据对象,返回值是一个对象,这个对象上只包含一个 .value 属性
import { ref } from '@vue/composition-api'
export default {
setup(props, context) {
// 创建响应式数据
const count = ref(0) // 数字类型
// 在setup 内部使用时候,使用.vaue 进行访问更改 count.value = 100
const strVal = ref('default') // 字符串
// strVal.value = 'update'
const arrVal = ref(['小明', '小米']) // 数组
// arrVal.value = ['新东西', '新世界']
const objVal = ref({
num: 10, title: '结算'
})
// objVal.value.name = 100; objVal.value.title = '合计'
# 在reactive 对象种访问 ref创建的响应式数据
# ref挂载到reactive上时,会自动把响应式数据根据对象展开为原始的值, 不需要通过 .value 就可以直接被访问
const state = reactive({ count })
console.log(state.count) // 输出 0
state.count++ // 不需要通过 .value 就能直接访问原始值
console.log(count) // 输出 1
return {
count
}
}
}
isRef
# 检查某个值是否 ref() 创建出来的对象。
import { isRef } from '@vue/composition-api'
const unerapped = isRef(foo) ? foo.value : foo
toRefs
toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据
import { toRefs } from '@vue/composition-api'
export default {
setup(props, context) {
// 定义响应式数据 对象
const state = reactive({
count: 0
})
return {
// 将 state 上的每一个属性,都转化为 ref 形式的响应式数据
...toRefs(state)
}
}
}
计算属性 computed
computed() 用来创建计算属性,函数返回值是一个 ref 的实例
const count = ref(1)
# 1. 创建只读的计算属性
// 根据 count 的值,创建一个响应式的计算属性 setOne
// 根据依赖的 ref 自动计算并返回新的 ref
const setOne = computed(() => count.value + 1)
console.log( setOne.value ) // 输出 2
setOne.value++ // 报错, 计算属性不能直接修改
# 2. 创建可读可写的计算属性
// 在调用 computed() 函数期间, 传入一个包含 get 和 set 函数的对象. 可以得到一个可读写的计算属性
const setTwo = computed({
// 取值函数
get: () => count.value + 1,
// 赋值函数
set: val => {
count.value = val - 1
}
})
// 为计算属性赋值的操作,会触发 set 函数
setTwo.value = 100
console.log( count.value ) // 99, 触发set后, count的值会被更新
watch 监听
可以实现在一个属性变更的时候,去执行我们想要的行为。
比如:
- 当ID改变的时候,从数据库里面获取新的数据
- 当属性变换的时候执行一个动画
- 当搜索条件变更的时候,更新查询到的数据
const state = reactive({ count: 0, title: 'xiaoming' })
const count = ref(0)
const title = ref('xiaoming')
# 监听指定的数据源
watch(
() => state.count, // 监听 reactive 类型的数据源
count, // 监听 ref 类型的数据源
(val, oldVal) => {
/* ... */
}
)
# 监听多个数据源 ------
watch(
// 监听 reactive 类型的数据源
[() => state.count, () => state.title],
// 监听 ref 类型的数据源
[count, title],
([count, title], [oldCount, oldTitle]) => {
console.log(count) // 新的 count 值
console.log(title) // 新的 title 值
console.log('-----------')
console.log(oldCount) // 旧的 count 值
console.log(oldTitle) // 旧的 title 值
},
{
lazy: true // 在 watch 被创建的时候,不执行回调函数中的代码
}
)
# 清除监听
// 在 setup 含税内创建的 watch 监听,会在当前组件被销毁的时候自动停止。 如果想要明确地停止某个监听, 可以调用 watch 函数的返回值即可
// 创建监听,并得到 停止函数
const stopWatch = watch(() => {})
// 调用停止函数,清除对应的监听
stopWatch()
# 在 watch 中清除无效的异步任务
// 有时候,当被 watch 监听的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除哪些无效的异步任务,此时,watch 回调函数中提供了一个 cleanup registrator function 函数来执行清除的工作。这个清除函数会在如下情况下被调用: 1. watch 被重复执行了 2. watch 被强制 stop 了
const keyVal = ref('')
// 异步任务
const asyncPrint = val => {
return setTimeout(() => {
console.log(val)
}, 1000)
}
watch(keyVal, (val, oldVal, onCleanup) => {
// 执行异步任务,并得到关闭异步任务的 函数
const timeId = asyncPring(val)
// 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务
onCleanup(() => clearTimeout(timeId))
}, {
lazy: true // watch 刚被创建的时候不执行
})
watchEffect 特点 及 原理
- 不需要指定监听的属性,会自动收集依赖,只要我们在回调中引用到了响应式的属性,那么当这些属性变更的时候,回调都会执行。而 watch 只能监听指定的属性而做出变更
- watch 可以获取到新值和旧值(更新前的值), 而watchEffect 是拿不到的
- watchEffect 如果存在的话,在组件初始化的时候就会执行一次用来收集依赖(与computed同理), 而后收集的依赖发生变化,这个回调才会再次执行,而watch不需要,因为一开始就指定了依赖。
import { watchEffect, ref } from 'vue'
setup () {
const userID = ref(0)
watchEffect(() => console.log(userID))
setTimeout(() => {
userID.value = 1
}, 1000)
return { userID }
}
停止监听
watchEffect 会返回一个用于停止这个监听的函数
如果 watchEffect 是在 setup 或者 生命周期里面注册的话,在组件取消挂载的时候会自动的停止掉
const stop = watchEffect(() => {
// ...
})
stop()
新版本的生命周期函数 - 且只能在 setup 函数中使用
import { onMounted, onUpdated, onUnmounted } from '@vue/composition-api'
export default {
setup(props, context) {
onMounted(() => {
console.log('mounted')
})
onUpdated(() => {
console.log('updated')
})
onUnmounted(() => {
console.log('unmounted')
})
}
}
模板 refs
通过 ref() 还可以引用页面上的元素或组件
<template>
<div>
<h1 ref="titleRef">通过ref() 函数来引用元素</h1>
</div>
</template>
<script>
import { ref, onMounted } from '@vue/composition-api'
export default {
setup() {
// 创建一个 DOM 引用
const titleRef = ref(null)
// 在 DOM 首次加载完毕之后,才能获取到元素的引用
onMounted(() => {
// titleRef.value 是原生DOM对象
titleRef.value.style.color = 'red'
})
return {
titleRef
}
}
}
</script>
createComponent
这个函数不是必须的,除非你想要完美结合 TypeScript 提供的类型推断来进行项目的开发。
函数仅仅提供了类型推断,方便在结合 TypeScript 书写代码时,能为 setup 中的 props 提供完整的类型推断
import { createComponent } from '@vue/composition-api'
export default createComponent({
props: {
foo: String
},
setup(props, context) {
props.foo // type: string
}
})