作者提供api的动机是逻辑重用与代码组织,由于vue当前的api所带来编程限制,基本分为两种:
1.随着功能的增长,复杂组件中的代码越来越难以推理,这种情况发生在开发人员阅读自己编写的代码时,根本原因是因为vue现有的api通过强制执行代码组织,在某些情况下,通过逻辑考虑来组织代码更有意义.
2.缺乏用于在多个组件之间提取和重用逻辑
- 首先利用cli4创建一个demo下载composition-api在main.js中引入使用.
//下载
npm install --save @vue/composition-api
//main.js
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
//引入官方提供的vue-composition-api库
import VueCompositionApi from '@vue/composition-api';
Vue.use(VueCompositionApi)
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
- setup函数
vue3中专门为组件提供的新属性.它为我们使用vue3的Composition API 新特新提供了统一的入口.之前定义的data,methods等都统一放入setup中实现。
- 执行时机
setup函数会在beforeCreate之后、created之前
export default {
setup(){
console.log('setup执行')
},
beforeCreate(){
console.log('beforeCreate执行')
},
created(){
console.log('created执行')
}
}
//输出顺序
//beforeCreate执行
//setup执行
//created执行
- 接收props
接收外界传入的props,接受的组件必须props里需要声明否则拿不到传值.
//home.vue里
<setup :data="123"></setup>
//setup.vue里
export default {
setup(props){
console.log(props.data)
},
props:{
data:Number
}
}
- 第二个形参context
指的是上下文对象,在这个对象中包含了一些书信,这些属性在2.x中通过this才能访问到,3.0访问方式如下,3.0中无法访问到this
export default {
name:'Setup',
setup(props,context){
console.log(context)
},
props:{
data:Number
}
}
//输出结果
{
root: (...),
parent: (...),
refs: (...),
attrs: (...),
listeners: (...),
isServer: (...),
ssrContext: (...),
emit: (...),
slots: {},
}
- reactive
reactive()函数接受一个普通对象,返回一个响应式的数据对象.类似于2.x的组件里的data()函数,想要使用必须先引入reactive,模板中想要使用该数据必须return出去,此函数只能在setup函数里使用.
<template>
<div>
<span style="margin-right:10px">{{state.count}}</span>
<el-button type="primary" @click="changeCount">点击累加</el-button>
</div>
</template>
<script>
import { reactive } from '@vue/composition-api';
export default {
name:'Setup',
setup(){
//state响应式的数据对象
const state= reactive({
count:1
})
function changeCount() {
state.count++;
}
return{
state,
changeCount
}
}
}
//也可以return state 如这样返回在模板直接{{count}}就可以取到值
结果
fehelper-localhost-8081-1587627070334.png
- ref
ref()函数用来根据给定制创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个队形上只包含一个.value的属性.与reactvie类似,但是比reactive合适.
//注意在setup内部访问值需要.value,在模板直接拿值可以使用
<template>
<div>{{count}}---{{name}}</div>
</template>
<script>
import {ref} from '@vue/composition-api';
export default {
name:'Ref',
setup(){
const count=ref(0);
//ref会返回一个对象,并且只有value属性
console.log(count.value)//0
console.log(name.value) //undefined
return {
count,
name:ref('zdb')
}
}
}
</script>
效果 fehelper-localhost-8081-1587630856799.png
两者区别 reactive创建一个集合响应式数据,ref创建单一响应式数据.
- 在reactive对象中访问ref创建的响应式数据
当把ref()创建出来的响应式数据对象,挂载到reactive()上是,会自动把响应式数据对象展开为原始值,不需要通过.value访问
<template>
<div>{{state.count}}</div>
</template>
<script>
import { ref ,reactive } from '@vue/composition-api';
export default {
name:'Ref',
setup(){
const count=ref(0);
//ref会返回一个对象,并且只有value属性
const state=reactive({
count
})
state.count++;
return {
state
}
}
}
</script>
- 新的ref会覆盖旧的ref
<script>
import { ref ,reactive } from '@vue/composition-api';
export default {
name:'Ref',
setup(){
const c1=ref(0)
const state=reactive({
c1
})
const c2=ref(9)
state.c1=c2;
state.c1++;
console.log(c1.value) //0
console.log(c2.value) //10
console.log(state.c1) //10
}
}
</script>
- isRef
isRef()用来判断某个值是否是ref()创建出来的,应用场景当展开某个可能为ref创建出来的值:例如:
import {isRef, ref ,reactive} from '@vue/composition-api';
export default {
name:'isRef',
setup(){
const count=reactive({
name:'zdb'
})
const name=ref('zdb')
console.log(isRef(name)) //true
const data=isRef(name) ? name.value :name;
console.log(data) //zdb
}
}
- toRef
toRef()函数可以将reactive()创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每一个属性节点,都是ref()类型的响应式数据:最长见的引用场景.(reactive创建之后的数据如果返回使用扩展运算符(...),它创建的所有属性不再是响应式的.这是需要toRefs)
<template>
<div>
<p>count值为{{count}}</p>
<el-button type="primary" @click="changeCount">点击累加</el-button>
</div>
</template>
<script>
import {reactive ,toRefs} from '@vue/composition-api';
export default {
name:'isRef',
setup(){
const state=reactive({
count:0,
name:'zdb'
})
//定义自增
const changeCount=()=>{
state.count++;
}
return {
...state, //此时页面点击state里的属性不会有任何改变,不再是响应式
...toRefs(state),//要想变成响应式需要把state里的每个属性用toRefs转换成响应式
changeCount
}
}
}
</script>
- computed
computed()用来创建计算属性,computed()函数的返回值是一个ref的实例,使用computed之前需要按需导入
1.创建只读计算属性
<script>
import {ref , computed} from '@vue/composition-api';
export default {
name:'Computed',
setup(){
const count = ref(1);
const plusOne=computed(()=>{ return count.value++ })
console.log(plusOne.value) //1
plusOne.value++ //error
}
}
</script>
2.创建可读可写的计算属性
<template>
<div>
<div>count:{{count}}</div>
<div>computed:{{countComputed}}</div>
<el-button type="primary" @click="changeCount">点击累加</el-button>
</div>
</template>
<script>
import {ref , computed} from '@vue/composition-api';
export default {
name:'Computed',
setup(){
const count = ref(1);
const countComputed=computed({
//读取函数
get:()=> count.value+1,
//赋值函数
set:(val)=>count.value=val
})
const changeCount=()=>{
count.value++;
}
return{
count,
countComputed,
changeCount
}
}
}
</script>
效果
fehelper-localhost-8080-1587645005972.png
- Watch
watch()函数用来监视某些数据项的变化,从而触发某些特定的操作.
<template>
<div>
<span>count:{{count}}</span>
<el-button type="primary" @click="changeCount">点击累加</el-button>
</div>
</template>
<script>
import { watch ,ref } from '@vue/composition-api';
export default {
name:'Watch',
setup(){
const count=ref(0)
watch(()=>{console.log(count.value)}) //会根据按钮点击count的值每次改变输出新的值
const changeCount=()=>{
return count.value++;
}
return {
count,
changeCount
}
},
}
</script>
watch监听指定数据源
1.监听reactive类型的数据源
<template>
<div>
<span>count:{{count}}</span>
<el-button type="primary" @click="changeCount">点击累加</el-button>
</div>
</template>
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
name:'Watch',
setup(){
const state=reactive({count:0})
const changeCount=()=>{
state.count++;
}
//必须指定监听属性,不能监听整个state
watch(
()=>state.count,
(count,preCount)=>{
console.log(`新的count${count}----旧的count${preCount}`)
},
{
lazy:true //默认情况下逐渐初始化会执行一次watch,lazy设置为true初始化不会调用watch
})
return{
// state,
//或者
...toRefs(state),
changeCount
}
},
}
</script>
效果
image.png
2.监听ref类型的数据源
<template>
<div>
<span>count:{{count}}</span>
<el-button type="primary" @click="changeCount">点击累加</el-button>
</div>
</template>
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
name:'Watch',
setup(){
const count=ref(0);
const changeCount=()=>{
count.value++;
}
watch(
()=>count.value,
(count,preCount)=>{
console.log(`新的count${count}----旧的count${preCount}`)
})
return{
count,
changeCount
}
},
}
</script>
监视多个数据源
//reactvie
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
name:'Watch',
setup(){
const state=reactive({
count:0,
name:'zdb'
})
watch(
[()=>state.count,()=>state.name], //Object.values(toRefs(state))
([newCount,newName],[oldCount,oldName])=>{
console.log(`新的count${newCount}`)
console.log(`新的name${newName}`)
console.log(`旧的count${oldCount}`)
console.log(`旧的name${oldName}`)
},
//去掉此项页面会报错
{
lazy:true
}
)
setTimeout(()=>{
state.count+=15;
state.name+='zs'
},2000)
return{
state
}
},
}
</script>
//ref
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
name:'Watch',
setup(){
const count=ref(0);
const name=ref('zdb');
watch(
[count,name],
([newCount,newName],[oldCount,oldName])=>{
console.log(`新的count${newCount}`)
console.log(`新的name${newName}`)
console.log(`旧的count${oldCount}`)
console.log(`旧的name${oldName}`)
},
{
lazy:true
}
)
setTimeout(()=>{
count.value+=15;
name.value+='zs'
},2000)
return{
count,
name
}
},
}
</script>
效果
image.png
- 清除watch
在setup()函数内创建watch监听,会在当前组件被销毁才会停止,如果想明确的停止某个监听,可以掉用watch的返回值即可.
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
name:'Watch',
setup(){
const count =ref(0);
const stop=watch(count,(newVal,oldVal)=>{
console.log(newVal)
console.log(oldVal)
})
setInterval(()=>{
count.value++;
// stop清除对应的监视
count.value>10 ? stop(): count.value
},1000)
return{
count
}
},
}
</script>
- 在watch中清除无效的异步任务
有时候,当贝watch监视的值发生变化时,或watch本身被stop后,我们希望能够清除那些无效的异步任务(例如当组件调用一个请求,请求结果没有返回,这时用户已经跳转到别的页面,请求没有意义,这时候需要清除异步任务),此时watch回调中提供了一个cleanup registrator function 来执行清除工作,这个清除函数在如下的情况被调用:
1.watch被重复执行了
2.watch被强制stop了
<template>
<div>
<el-input v-model="value"></el-input>
</div>
</template>
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
name:'Watch',
setup(){
let value=ref('');
//异步 1秒后输出用户的输入
const asyncPrint=val=>{
return setTimeout(()=>{
console.log(val)
},1000)
}
watch(value,(newVal,oldVal,onCleanup)=>{
//执行异步任务,并得到关闭异步任务的timerId
const timerId=asyncPrint(newVal);
//如果watch舰艇被重复执行,则会先清除上次未完成的异步任务.
onCleanup(()=>clearTimeout(timerId))
},
{
lazy:true
}
)
return {
value
}
},
}
</script>
- 新版的生命周期函数
2.x生命周期选项和Composition API之间的映射
beforeCreate ->使用 setup()
created ->使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
新钩子
除了2.x生命周期等效项之外,Composition API还提供了以下调试挂钩:
onRenderTracked
onRenderTriggered
两个钩子都收到DebuggerEvent类似于onTrack和onTrigger观察者的选项:
export default {
onRenderTriggered(e) {
debugger
// inspect which dependency is causing the component to re-render
}
}
示例
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
- provide&inject
provide和inject可以时间嵌套组件之间数据传递.这两个函数只能在setup中使用.父组件中使用provide()函数向下传递数据,子组件使用inject()获取上层传递来的数据
//父组件
<template>
<div class="home">
父组件:
<Provide></Provide>
</div>
</template>
<script>
import Provide from '@/components/provide.vue'
import {provide,ref} from '@vue/composition-api'
export default {
name: 'Home',
components: {
Provide
},
setup(){
const msg=ref('我是父组件传递来的数据');
//参数一:共享的名称 参数二:共享的数据
provide('sendMsg',msg)
return {
msg
}
}
}
</script>
//子组件
<template>
<div>子组件:{{msg}}</div>
</template>
<script>
import {inject,ref} from '@vue/composition-api'
export default {
name:'Provide',
setup(){
const msg=inject('sendMsg')
return {
msg
}
}
}
</script>
- ref
此功能与2.x中的ref类似
<template>
<div>
<h3 ref="el">template-ref</h3>
</div>
</template>
<script>
import {ref,onMounted} from '@vue/composition-api';
export default {
name:'tplRefs',
setup(){
//创建一个dom的引用
const el=ref(null)
onMounted(()=>{
el.value.style.color='red'
})
return {
el
}
}
}
</script>
网友评论