Composition API
also called combined API, which is a new feature of Vue 3.x.
By creating Vue components, we can extract the repeatable parts of the interface and its functions into reusable code segments. This alone can make our application go further in terms of maintainability and flexibility. However, our experience has proven that this alone may not be enough, especially when your application becomes very large—think hundreds of components. When dealing with such a large application, sharing and reusing code becomes particularly important
's terms:
Composition API
, the code of the vue-related business needs to be configured to the specific area of the option. Small and medium-sized projects are no problem, but in large-scale projects, the later maintenance will be more complicated, and the code reusability is not high. The composition-api in Vue3.x was born to solve this problem
compositon api provides the following functions:
setup
ref
reactive
watchEffect
watch
computed
toRefs
- The life cycle of
hooks
One, setup component options
The newsetup
component options in before creating a component execution, onceprops
is resolved, and acts as a syntheticAPI
entry point
prompt:
Since the component instance has not been created whensetup
is executed, there is nothis
setup
option. This means that, with the exception ofprops
, you will not be able to access any properties declared in the component-local state, calculated properties or methods.
When using the setup
function, it will accept two parameters:
props
context
Let's look deeper into how to use each parameter
1. Props
The first parameter in thesetup
props
. As a standard component as desired,setup
function ofprops
is responsive when a new incomingprop
, it will be updated
// MyBook.vue
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
Note:
However, becauseprops
is responsive, you cannot useES6
deconstruct because it will eliminate the responsiveness ofprop
If desired deconstruction prop, by using setup
function in toRefs
to safely complete the operation.
// MyBook.vue
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
2. Context
Passed tosetup
function of the second parameter iscontext
.context
is an ordinary JavaScript object that exposes the properties of three components
// MyBook.vue
export default {
setup(props, context) {
// Attribute (非响应式对象)
console.log(context.attrs)
// 插槽 (非响应式对象)
console.log(context.slots)
// 触发事件 (方法)
console.log(context.emit)
}
}
context
is a normal JavaScript object, that is, it is not reactive, which means you can safely use ES6 deconstructioncontext
// MyBook.vue
export default {
setup(props, { attrs, slots, emit }) {
...
}
}
attrs
andslots
are stateful objects, they are always updated with the update of the component itself. This means that you should avoid deconstructing them and always refer to propertiesattrs.x
orslots.x
Please note that, unlikeprops
,attrs
andslots
are not responsive. If you plan to change the application side effectsattrs
orslots
onUpdated
lifecycle hook.
3. The property of the setup component
When setup
is executed, the component instance has not been created yet. Therefore, you can only access the following properties:
props
attrs
slots
emit
In other words, your will not be able to access the following component options of
data
computed
methods
4. Use ref reactive and setup in combination with templates
Before looking at the setup
combination with the template, we must first know the ref
and reactive
methods.
If setup
returns an object, you can bind the properties and methods of the object in the template, but when you want to define responsive data, you can use the ref
, reactive
method to define the responsive data
wrong writing:
<template>
{{msg}}
<br>
<button @click="updateMsg">改变etup中的msg</button>
<br>
</template>
<script>
export default {
data() {
return {
}
},
setup() {
let msg = "这是setup中的msg";
let updateMsg = () => {
alert("触发方法")
msg = "改变后的值"
}
return {
msg,
updateMsg
}
},
}
</script>
<style lang="scss">
.home {
position: relative;
}
</style>
correct writing one:
ref used to define the string, number, array, Bool
type of response
import {
ref
} from 'vue'
<template>
{{msg}}
<br>
<br>
<button @click="updateMsg">改变etup中的msg</button>
<br>
<br>
<ul>
<li v-for="(item,index) in list" :key="index">
{{item}}
</li>
</ul>
<br>
</template>
<script>
import {
ref
} from 'vue'
export default {
data() {
return {
}
},
setup() {
let msg = ref("这是setup中的msg");
let list = ref(["马总", "李总", "刘总"])
let updateMsg = () => {
alert("触发方法");
msg.value = "改变后的值"
}
return {
msg,
list,
updateMsg
}
},
}
</script>
<style lang="scss">
.home {
position: relative;
}
</style>
correct writing two:
reactive used to define reactive objects
import {
reactive
} from 'vue'
<template>
{{msg}}
<br>
<br>
<button @click="updateMsg">改变setup中的msg</button>
<br>
<br>
<ul>
<li v-for="(item,index) in list" :key="index">
{{item}}
</li>
</ul>
<br>
{{setupData.title}}
<br>
<button @click="updateTitle">更新setup中的title</button>
<br>
<br>
</template>
<script>
import {
reactive,
ref
} from 'vue'
export default {
data() {
return {
}
},
setup() {
let msg = ref("这是setup中的msg");
let setupData = reactive({
title: "reactive定义响应式数据的title",
userinfo: {
username: "张三",
age: 20
}
})
let updateMsg = () => {
alert("触发方法");
msg.value = "改变后的值"
}
let updateTitle = () => {
alert("触发方法");
setupData.title = "我是改变后的title"
}
return {
msg,
setupData,
updateMsg,
updateTitle
}
},
}
</script>
<style lang="scss">
.home {
position: relative;
}
</style>
Description: To change the attribute name defined by ref, you need to modify the attribute name.value. To change the object name defined in
reactive
5. Use this
insetup()
inside,this
will not be a reference to the active instance of , becausesetup()
is being called before parsing other component options, sosetup()
internalthis
behavior with other optionsthis
completely different. This may cause confusion whensetup()
with other optional APIs
Two, toRefs-Deconstruct responsive object data
Convert a responsive object into an ordinary object. Eachproperty
the ordinary object is aref
, whichproperty
one-to-one
<template>
<div>
<h1>解构响应式对象数据</h1>
<p>Username: {{username}}</p>
<p>Age: {{age}}</p>
</div>
</template>
<script>
import {
reactive,
toRefs
} from "vue";
export default {
name: "解构响应式对象数据",
setup() {
const user = reactive({
username: "张三",
age: 10000,
});
return {
...toRefs(user)
};
},
};
</script>
When you want to return a responsive object from a combinatorial logic function, toRefs is very effective. This API allows the consumer component to deconstruct/expand the …
operator) without losing responsiveness:
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2,
})
// 对 state 的逻辑操作
// ....
// 返回时将属性都转为 ref
return toRefs(state)
}
export default {
setup() {
// 可以解构,不会丢失响应性
const { foo, bar } = useFeatureX()
return {
foo,
bar,
}
},
}
Three, calculated-calculated attributes
<template>
<div>
<h1>解构响应式对象数据+computed</h1>
<input type="text" v-model="firstName" placeholder="firstName" />
<br>
<br>
<input type="text" v-model="lastName" placeholder="lastName" />
<br>
{{fullName}}
</div>
</template>
<script>
import {
reactive,
toRefs,
computed
} from "vue";
export default {
name: "解构响应式对象数据",
setup() {
const user = reactive({
firstName: "",
lastName: "",
});
const fullName = computed(() => {
return user.firstName + " " + user.lastName
})
return {
...toRefs(user),
fullName
};
},
};
</script>
Four, readonly "deep" read-only proxy
Pass in an object (responsive or ordinary) or ref, and return a read-only proxy of the original object. A read-only proxy is "deep", and any nested properties within the object are also read-only
<template>
<div>
<h1>readonly - “深层”的只读代理</h1>
<p>original.count: {{original.count}}</p>
<p>copy.count: {{copy.count}}</p>
</div>
</template>
<script>
import { reactive, readonly } from "vue";
export default {
name: "Readonly",
setup() {
const original = reactive({ count: 0 });
const copy = readonly(original);
setInterval(() => {
original.count++;
copy.count++; // 报警告,Set operation on key "count" failed: target is readonly. Proxy {count: 1}
}, 1000);
return { original, copy };
},
};
</script>
Five, watchEffect
Run a function immediately when tracking its dependencies responsively, and re-run it when changing dependencies.
<template>
<div>
<h1>watchEffect - 侦听器</h1>
<p>{{data.count}}</p>
<button @click="stop">手动关闭侦听器</button>
</div>
</template>
<script>
import {
reactive,
watchEffect
} from "vue";
export default {
name: "WatchEffect",
setup() {
const data = reactive({
count: 1,
num: 1
});
const stop = watchEffect(() => console.log(`侦听器:${data.count}`));
setInterval(() => {
data.count++;
}, 1000);
return {
data,
stop
};
},
};
</script>
Six, the difference between watch, watch and watchEffect
Contrast watchEffect
, watch
allows us :
- Lazy execution, which means that the callback is executed only when the source of the listener changes;
- It is more clear which state changes will trigger the listener to re-run;
- Access the values before and after the listening state changes
More clearly which state changes will trigger the listener to re-run
<template>
<div>
<h1>watch - 侦听器</h1>
<p>count1: {{data.count1}}</p>
<p>count2: {{data.count2}}</p>
<button @click="stopAll">Stop All</button>
</div>
</template>
<script>
import {
reactive,
watch
} from "vue";
export default {
name: "Watch",
setup() {
const data = reactive({
count1: 0,
count2: 0
});
// 侦听单个数据源
const stop1 = watch(data, () =>
console.log("watch1", data.count1, data.count2)
);
// 侦听多个数据源
const stop2 = watch([data], () => {
console.log("watch2", data.count1, data.count2);
});
setInterval(() => {
data.count1++;
}, 1000);
return {
data,
stopAll: () => {
stop1();
stop2();
},
};
},
};
</script>
Access the value before and after the listening state change
<template>
<div>
<h1>watch - 侦听器</h1>
<input type="text" v-model="keywords" />
</div>
</template>
<script>
import {
ref,
watch
} from "vue";
export default {
name: "Watch",
setup() {
let keywords = ref("111");
// 侦听单个数据源
watch(keywords, (newValue, oldValue) => {
console.log(newValue, oldValue)
});
return {
keywords
};
},
};
</script>
is executed only when the source of the listening changes.
<template>
<div>
<h1>watch - 侦听器</h1>
<p>num1={{num1}}</p>
<p>num2={{num2}}</p>
</div>
</template>
<script>
import {
ref,
watch,
watchEffect
} from "vue";
export default {
name: "Watch",
setup() {
let num1 = ref(10);
let num2 = ref(10);
// 侦听单个数据源
watch(num1, (newValue, oldValue) => {
console.log(newValue, oldValue)
});
watchEffect(() => console.log(`watchEffect侦听器:${num2.value}`));
return {
num1,
num2
};
},
};
</script>
Seven, combined api life cycle hook
You can access the life cycle hook of the component by adding "on" in front of the life cycle hook.
The following table contains how to call life cycle hooks inside setup ()
Option API | Hook inside setup |
---|---|
beforeCreate | unnecessary* |
created | unnecessary* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
Becausesetup
around thebeforeCreate
andcreated
lifecycle hooks, there is no need to explicitly define them. In other words, any code written in these hooks should be writtensetup
export default {
setup() {
// mounted
onMounted(() => {
console.log('Component is mounted!')
})
}
}
8. Provider Inject
Usually, when we need to pass data from the parent component to the child component, we use props . Imagine a structure like this: you have some deeply nested components, and you only need some content from the parent component in the deeply nested child components. In this case, you still need to pass the prop to the entire component chain, which can be annoying
For this situation, we can useprovide
andinject
for the parent component to be the dependency provider for all its child components, no matter how deep the component hierarchy is. This feature has two parts: the parent component has aprovide
option to provide data, and the child component has ainject
option to start using this data
1. non-combined api writing
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
provide: {
location: 'North Pole',
geolocation: {
longitude: 90,
latitude: 135
}
}
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
export default {
inject: ['location', 'geolocation']
}
</script>
2. combined api in wording
Provider:
Insetup()
useprovide
, we first fromvue
explicitly importprovide
method. This allows us to callprovide
time to define eachproperty
provide
function allows you to define property
with two parameters:
property
ofname
(<String>
type)property
ofvalue
Using the MyMap
component, the value we provide can be reconstructed as follows:
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide } from 'vue'
import MyMarker from './MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
provide('location', 'North Pole')
provide('geolocation', {
longitude: 90,
latitude: 135
})
}
}
</script>
Inject:
Insetup()
useinject
when needed fromvue
it explicitly import. Once we do this, we can call it to define how to expose it to our components.
inject
function has two parameters:
- The name
property
to be injected - A default value ( optional )
Using the MyMarker
component, it can be refactored with the following code:
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>
Provider Inject Responsiveness
parent component:
import {
provide,
ref,
reactive
} from 'vue'
setup() {
const location = ref('北京')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = '上海'
}
provide('location', location);
provide('geolocation', geolocation);
return {
updateLocation
}
}
<button @click="updateLocation">改变location</button>
sub-component:
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。