当前位置:网站首页>Summary of the use of composition API in the project
Summary of the use of composition API in the project
2022-06-28 03:27:00 【jxderic】
background
For the first time composition api It's from vue3 Of RFC Heard from the proposal , What impresses me most is options api and composition api Comparison of the figure :

This picture clearly describes composition api The advantages of : Ability to group the same logical concerns , You don't have to scroll back and forth between different options , This can achieve better logic reuse effect . This is very attractive to me , In many complex businesses ,template There is little content , Most of the code is js Logic , The function is complex , For better code reuse , Adopted mixins Encapsulates a lot of common logic , but mixins It's obvious that ,mixins The variables or methods used in vue Reuse in components requires repeated horizontal jumps , But it was limited by vue2, Only to this extent , If you do it again now , Use composition api It can achieve better logic reuse .
composition api
First, familiarize yourself with composition api, What are they? api, At present, you only need to master the bold part , You can experience the combined api The charm of , Other api Wait until you need it vue3 Official website documentation Learn more .

reactive Used to turn an object into a responsive , And vue2 Of observable similar ,ref Used to obtain responsiveness individually or for the underlying data type . Why do two get responsive api Well , Later we will specify .computed、watch,provide、inject No doubt and vue2 Do the same thing in . You must have noticed the addition of on The life cycle hook function at the beginning , Yes, in the combination api in , That's how they register . But why is it gone beforeCreate and created Well ? because setup It is at this stage that the , and setup Is to open the combined api The door of the world . You can take setup Understood as a class Of constructor, stay vue The creation phase of the component , Execute our related logic , And register the relevant side effect functions .
Now let's go back to ref and reactive.
reactive Instructions on the official website , Accept an object , Returns a responsive copy of an object . ref Description on the official website " Accept an internal value and return a responsive and variable ref object .ref Object has a single... That points to an internal value property.value".
Sounds like a tongue twister , The short answer is reactive You can create a response for an object ; and ref Except for objects , You can also receive basic data types , such as string、boolean etc. . Then why is there such a difference ? stay vue3 The response is based on proxy Realized , and proxy Of target Must be a complex data type , That is, stored in heap memory , Object referenced by pointer . It's easy to understand , Because the basic data type , Each assignment is a new object , So I can't represent . So what if we want to get a simple type of response ? And that's where it comes in ref.
class RefImpl<T> {
private _value: T
public readonly __v_isRef = true
constructor(private _rawValue: T, public readonly _shallow = false) {
this._value = _shallow ? _rawValue : convert(_rawValue)
}
get value() {
track(toRaw(this), TrackOpTypes.GET, 'value')
return this._value
}
set value(newVal) {
if (hasChanged(toRaw(newVal), this._rawValue)) {
this._rawValue = newVal
this._value = this._shallow ? newVal : convert(newVal)
trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
}
}
}
...
const convert = <T extends unknown>(val: T): T =>
isObject(val) ? reactive(val) : val
ref By creating an internal state , Hang values on value On , therefore ref Generated objects , To pass the value Use . rewrite get/set Obtained listening , At the same time, the processing of objects , I also rely on reactive The implementation of the . thus ,ref Not just the ability to respond to basic data types , He can also deal with objects . So I think ref and reactive The distinction should not be simple / The distinction between complex objects , It should be distinguished by programming ideas . We should avoid , hold reactive As data The idea of declaring all variables at the top , It should be combined with specific logical functions , For example, a control gray Flag Then he should be a ref, And the page number in the page ,pageSize,total Wait should be a reactive Declared object . That is to say a setup There can be more declarations of response variables , And they should be closely combined with logic .
Next, I'll use a paging function , Use options and combinations api Let's compare :
// options api
<template>
<div>
<ul class="article-list">
<li v-for="item in articleList" :key="item.id">
<div>
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
</div>
</li>
</ul>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="pageSizes"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</div>
</template>
<script>
import { getArticleList } from '@/mock/index';
export default {
data() {
return {
articleList: [],
currentPage: 1,
pageSizes: [5, 10, 20],
pageSize: 5,
total: 0,
};
},
created() {
this.getList();
},
methods: {
getList() {
const param = {
currentPage: this.currentPage,
pageSizes: this.pageSizes,
pageSize: this.pageSize,
};
getArticleList(param).then((res) => {
this.articleList = res.data;
this.total = res.total;
});
},
handleSizeChange(val) {
this.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.currentPage = val;
this.getList();
},
},
};
</script>
The above is the paging process that we are familiar with , stay data Data stated in , stay method Provides a method for repairing pages in . When we use composition-api When it is realized, it looks like the following :
<script>
import { defineComponent, reactive, ref, toRefs } from "@vue/composition-api";
import { getArticleList } from "@/mock/index";
export default defineComponent({
setup() {
const page = reactive({
currentPage: 1,
pageSizes: [5, 10, 20],
pageSize: 5,
total: 0,
});
function handleSizeChange(val) {
page.pageSize = val;
getList();
}
function handleCurrentChange(val) {
page.currentPage = val;
getList();
}
const articleList = ref([]);
function getList() {
getArticleList(page).then((res) => {
articleList.value = res.data;
page.total = res.total;
});
}
getList();
return {
...toRefs(page),
articleList,
getList,
handleSizeChange,
handleCurrentChange,
};
},
});
</script>
This is because composition-api Paging implemented in a way , You'll find the original data,method, And the options such as declaration period are missing , All the logic is put in setup among . Through this simple example , We can find the logic scattered among the options , Here we get the aggregation . This change is more obvious in complex scenes . In complex components , This situation is more obvious . And when logic comes together completely , Now , Pull them out , And those separated from logic can be reused elsewhere , thus hook Formed .
hook Paging component of form :
// hooks/useArticleList.js
import { ref } from "@vue/composition-api";
import { getArticleList } from "@/mock/index"; // mock ajax request
function useArticleList() {
const articleList = ref([]);
function getList(page) {
getArticleList(page).then((res) => {
articleList.value = res.data;
page.total = res.total;
});
}
return {
articleList,
getList,
};
}
export default useArticleList;
// hooks/usePage.js
import { reactive } from "@vue/composition-api";
function usePage(changeFn) {
const page = reactive({
currentPage: 1,
pageSizes: [5, 10, 20],
pageSize: 5,
total: 0,
});
function handleSizeChange(val) {
page.pageSize = val;
changeFn(page);
}
function handleCurrentChange(val) {
page.currentPage = val;
changeFn(page);
}
return {
page,
handleSizeChange,
handleCurrentChange,
};
}
export default usePage;
// views/List.vue
import { defineComponent, toRefs } from "@vue/composition-api";
import usePage from "@/hooks/usePage";
import useArticleList from "@/hooks/useArticleList";
export default defineComponent({
setup() {
const { articleList, getList } = useArticleList();
const { page, handleSizeChange, handleCurrentChange } = usePage(getList);
getList(page);
return {
...toRefs(page),
articleList,
getList,
handleSizeChange,
handleCurrentChange,
};
},
});
Use in projects
stay vue2 Can also be used in composition api, Just import @vue/composition-api This package , And tested , about IE11 There is no problem with the compatibility of , Then you can safely use it in the project , So in the comprehensive financial security V1.7.0 In the second phase of , We introduced composition api.
// main.js
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
The first thought is that the addition, deletion, modification and query of a typical list page can be encapsulated into hooks To achieve the reuse effect , Every time I wrote this curd Business , Some logic is repeated , For example, delete 、 Paging jump, etc , Although the logic is simple , But it will take some time , This time, I'll just seal it well , There is no need to write such unskilled code in the future .
Here are some examples of curd The logic of :
1) Pagination of lists hook
Refer to the previous section
2) add to hook
Adding / Edit page , Verify the form before saving , Call the save interface , Cancel to return to the previous page , These are general logic , Involving data :
const loading = ref(false) // The saved loading identification
Functions involved :
// Save function , Check the form first , adopt hook Pass in the saved interface 、 The ginseng
const handleSave = () => {
form.value.validate(async valid => {
if (valid) {
loading.value = true
try {
await api.save(params && params.formModel)
root.$message.success(' Saved successfully ')
} finally {
loading.value = false
}
}
})
}
// Cancel
const handleCancel = () => {
root.$router.go(-1)
}
You only need to call hook function , Pass in the list saving interface 、 Save transfer parameters , Use the function with the same name in the template .
3) list hook
Generally, there are operation items in the list ( add to 、 edit 、 details 、 Delete ), The above filter items support query and reset , You can integrate the previous list pagination hook, To achieve a complete list of general functions , Functions involved :
// Reset filter items
const handleReset = () => {
search.value.getForm().resetFields()
handleSearch()
}
// Delete , Support single deletion and batch deletion , Different unique identification fields are supported , Whether you need a second prompt
const handleDeleteData = async (row, id = 'id', hintFlag = true) => {
let data = []
if (row) {
data = [row]
} else {
data = checkedList.value
}
const deleteAndUpdateData = async () => {
const ids = data.map(item => item[id])
await api.delete(ids)
root.$message.success(' Delete successful !')
handleSearch()
}
if (hintFlag) {
root.$confirm(' Are you sure to delete ?', {
confirmButtonText: ' determine ',
cancelButtonText: ' Cancel ',
onConfirm: () => {
deleteAndUpdateData()
},
onCancel: () => {
console.log(' Cancel deletion ')
}
})
} else {
deleteAndUpdateData()
}
}
// Jump to add page , Support different add page routes
const handleAdd = () => {
root.$router.push({
path: params.addPath
})
}
// Jump to the edit page , Different unique identifiers are supported , Routing is consistent with adding
const handleEdit = (row, id = 'id') => {
root.$router.push({
path: params.addPath,
query: {
[id]: row[id]
}
})
}
// Go to the details page , Support different routes and unique identities
const handleDetail = (row, id = 'id') => {
root.$router.push({
path: params.detailPath,
query: {
[id]: row[id]
}
})
}
// export
const handleExport = () => {
window.location.href = api.exportUrl + `?${qs.stringify(params.searchParam)}`
}
You only need to call hook function , Incoming delete interface 、 Unique field id 、 Whether secondary confirmation is required , Use the function with the same name in the template , At present, interface calls only follow most interface parameter transfer habits , If the parameters transmitted by the corresponding back-end interface are inconsistent , You can communicate to keep it consistent , Many of these common functions need to push the interface specification , Etc hooks After stabilization, the backend will be pushed to comply with this set of specifications .
4) details hook
Both the edit and detail pages exist to obtain a unique identifier from the routing parameters , Call the interface to get detailed data , Can be encapsulated into details hook:
const identifier = root.$route.query[id] // Unique identification
const getDetail = async () => {
const { data } = await api.detail(identifier)
return data
}
You only need to call hook function , Pass in a unique field id 、 Detail interface .
vue The official is also based on composition api Extracted the function set VueUse.
Of course composition api Not just for reusing common logic , It can also be used for better code organization to extract composite functions :
Composite functions are extracted not only for reuse , Also for code organization . As component complexity increases , You may eventually find that there are too many components to query and understand . combined API Will give you enough flexibility , It allows you to split component code into smaller functions based on logic problems :
<script setup>
import { useFeatureA } from './featureA.js'
import { useFeatureB } from './featureB.js'
import { useFeatureC } from './featureC.js'
const { foo, bar } = useFeatureA()
const { baz } = useFeatureB(foo)
const { qux } = useFeatureC(baz)
</script>
In a way , You can think of these extracted composite functions as component - wide services that can communicate with each other .
Best practices
name
The combined function convention is named by hump nomenclature , And “use” As the beginning .
Input parameters
Although its responsiveness does not depend on ref, Composite functions can still receive ref Parameters . If you write a composite function that will be used by other developers , You'd better be compatible with... When dealing with input parameters ref Not just the original value .unref() Tool functions can be very helpful for this :
import { unref } from 'vue'
function useFeature(maybeRef) {
// if maybeRef It's a ref, its .value Will be returned to
// otherwise ,maybeRef Will be returned as is
const value = unref(maybeRef)
}
If your composite function is receiving ref When it is a parameter, a response expression will be generated effect, Please make sure to use watch()
Listen explicitly for this ref, Or in watchEffect()
Call in unref()
For proper tracking .
Return value
You may have noticed , We have been using in combinatorial functions ref()
instead of reactive()
. Our recommended convention is that composite functions always return a ref object , This allows the function to remain responsive after being deconstructed in the component :
// x and y Are the two ref
const { x, y } = useMouse()
Returning a responsive object from a composite function will result in the loss of a responsive connection to the state in the composite function during object deconstruction . By contrast ,ref This responsive connection can be maintained .
If you prefer to object property Returns a state from a composite function in the form of , You can use the object to be returned as reactive()
packing , So one of them ref Will be automatically unpacked , for example :
const mouse = reactive(useMouse())
// mouse.x Link to the original x ref
console.log(mouse.x)
Mouse position is at: {{ mouse.x }}, {{ mouse.y }}
hook Principle of encapsulation function
hook The function of the package shall be as single as possible , To use in a component by combining , If hook The function logic inside is complex , Then there is no split into hook That's the meaning of , Use in combination to show the use process more clearly , Better look at the code and locate the problem , Expose directly to by deconstruction template The variables and methods of can only be viewed by searching .
side effect
Side effects can indeed be performed in composite functions ( for example : add to DOM Event listeners or request data ), But please note the following rules :
If you use... In an application ** Server-side rendering ** (SSR), Make sure to execute... On the post loaded declaration hook DOM Related side effects , for example : onMounted()
. These hooks will only be used in browsers , Therefore, it can be ensured that DOM.Make sure that the onUnmounted()
When cleaning up side effects . for instance , If a composite function has an event listener set up , It should be inonUnmounted()
Be removed in the ( It's like we're hereuseMouse()
As seen in the example ). Of course, you can also use a composite function to automatically help you do these things .
Limit
hook Asynchronous problems in
because hook It's essentially a function , So flexibility is very high , Especially in logic involving asynchrony , Incomplete consideration is likely to cause many problems . hook It can cover asynchronous situations , But it must be setup Valid objects returned during execution cannot be blocked . We summarized two asynchronous styles , Through a simple hook For example :
There are no other external dependencies , Just deliver the rendered response variables
In this case , You can declare 、 External exposure response variables , stay hook Asynchronous modification in
// hooks/useWarehouse.js
import { reactive,toRefs } from '@vue/composition-api';
import { queryWarehouse } from '@/mock/index'; // Request to query warehouse
import getParam from '@/utils/getParam'; // The method of obtaining some parameters
function useWarehouse(admin) {
const warehouse = reactive({ warehouseList: [] });
const param = { id: admin.id, ...getParam() };
const queryList = async () => {
const { list } = await queryWarehouse(param);
list.forEach(goods=>{
// Some logic ...
return goods
})
warehouse.warehouseList = list;
};
return { ...toRefs(warehouse), queryList };
}
export default useWarehouse;
// components/Warehouse.vue
<template>
<div>
<button @click="queryList">queryList</button>
<ul>
<li v-for="goods in warehouseList" :key="goods.id">
{{goods}}
</li>
</ul>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api';
import useWarehouse from '@/hooks/useWarehouse';
export default defineComponent({
setup() {
// Storekeeper
const admin = {
id: '1234',
name: ' Zhang San ',
age: 28,
sex: 'men',
};
const { warehouseList, queryList } = useWarehouse(admin);
return { warehouseList, queryList };
},
});
</script>
External has dependencies , To be machined on the use side
Through external exposure Promise The way , Enable external access to synchronous operations Expand on the original example , Add an update time attribute to be processed
// hooks/useWarehouse.js
function useWarehouse(admin) {
const warehouse = reactive({ warehouseList: [] });
const param = { id: admin.id, ...getParam() };
const queryList = async () => {
const { list, updateTime } = await queryWarehouse(param);
list.forEach(goods=>{
// Some logic ...
return goods
})
warehouse.warehouseList = list;
return updateTime;
};
return { ...toRefs(warehouse), queryList };
}
export default useWarehouse;
// components/Warehouse.vue
<template>
<div>
...
<span>nextUpdateTime:{{nextUpdateTime}}</span>
</div>
</template>
<script>
...
import dayjs from 'dayjs';
export default defineComponent({
setup() {
...
// Storekeeper
const admin = {
id: '1234',
name: ' Zhang San ',
age: 28,
sex: 'men',
};
const { warehouseList, queryList } = useWarehouse(admin);
const nextUpdateTime = ref('');
const interval = 7; // Suppose the time interval for updating the warehouse is 7 God
const queryHandler = async () => {
const updateTime = await queryList();
nextUpdateTime.value = dayjs(updateTime).add(interval, 'day');
};
return { warehouseList, nextUpdateTime, queryHandler };
},
});
</script>
this The problem of
because setup yes beforecreate Stage , Can't get this, Though through setup Second parameter of context You can get part of the ability , But if we want to operate something like routing ,vuex This ability is limited , Abreast of the times [email protected]、[email protected] Both provide combined api. because vue2 We have no way to use these hook, Though through getCurrentInstance You can get component instances , The object mounted on it , But because of composition-api Although the underlying principle of the response in vue The same is through object.defineproperty Override property implementation , However, there are differences in specific implementation methods , So setup Zhongyu vue Native responses are not interoperable . This also leads to even if we get the corresponding examples , There's no way to listen to their responses . If there is a need for this , Can only be used in option configuration .
Combinatorial functions in <script setup>
or setup()
In the hook , Should always be Synchronously call . In some cases , You can also be like onMounted()
Use them in such lifecycle hooks .
These are Vue To determine the conditions for the currently active component instance . The ability to access active component instances is essential , In order to :
You can register a lifecycle hook in a composite function Compute properties and listeners can connect to the current component instance , So that it can be disposed of when the component is unloaded .
<script setup>
Is the only one calling await
Where composite functions can still be called later . The compiler will automatically restore the currently active component instance for you after the asynchronous operation .
In option API Using composite functions in
If you are using the option API, Composite functions must be in setup()
Call in . And the binding returned must be in setup()
Back in , To expose to this
And its template :
import { useMouse } from './mouse.js'
import { useFetch } from './fetch.js'
export default {
setup() {
const { x, y } = useMouse()
const { data, error } = useFetch('...')
return { x, y, data, error }
},
mounted() {
// setup() exposed property You can go through `this` Access to the
console.log(this.x)
}
// ... The other options
}
Can only option api visit composition api Value thrown , Not vice versa , So I don't recommend composition api and options api A mixture of .
Cannot share an instance
Every call useMouse() The component instance of will create its own x、y State copy , So they don't affect each other . If you want to share state between components , Use state management (pinia)
template The variables or methods used
If you use ...toRef(useData()) This way of writing directly deconstructs and exposes template Variables and methods of , You can't click to jump directly , This mixins There are the same problems , You need to search to see , Here's the suggestion hook stay setup Used in combination , Don't go straight to return Deconstruct using , Even if there is no other hook Or logic used to , It is also recommended to deconstruct once ,return Back again .
summary
adopt vue3 combined 、 And hook The ability of . Our code style has changed a lot , Logic is more aggregated 、 pure . Reusability has been improved . The overall maintainability of the project has been significantly improved . That's what we do even when vue2 In the project , Also use composition-api introduce vue3 The reason for the new feature .composition api There are high requirements for the developed business logic splitting ability , If the split is not good , It's easy to write faceted code , It also reverses the need for front-end personnel to be familiar with the business .
Reference link
边栏推荐
- 空闲中断无法清除
- Etcd database source code analysis -- network layer server rafthandler between clusters
- Importer un fichier Excel, résoudre le problème de sauter les cellules vides et de ne pas lire, et avancer l'indice, et retourner Blank As NULL Red
- 如何给Eclips自动添加作者,时间等…
- 买股票通过券商经理的开户链接开户资金是否安全?想开户炒股
- Idea auto generate code
- Is it better for a novice to open a securities account? Is it safe to open a stock trading account
- What is the best and safest software to download when buying stocks?
- In the digital era, enterprises must do well in user information security
- Brief history and future trend of codeless software
猜你喜欢
用于 C# 的 SQL 基本语法总结
Artifact for converting pcap to JSON file: joy (installation)
Inference optimization implementation of tensorrt model
R language penalty logistic regression, linear discriminant analysis LDA, generalized additive model GAM, multiple adaptive regression splines Mars, KNN, quadratic discriminant analysis QDA, decision
Tardigrade:Trino 解决 ETL 场景的方案
View the SQL execution plan according to explain and optimize the SQL
Importer un fichier Excel, résoudre le problème de sauter les cellules vides et de ne pas lire, et avancer l'indice, et retourner Blank As NULL Red
collections.defaultdict()的使用
matlab习题 —— 矩阵的常规运算
ETCD数据库源码分析——集群间网络层服务端RaftHandler
随机推荐
Apache - about Apache
国泰君安证券靠谱吗?开证券账户安全吗?
建立自己的网站(17)
PPT制作小技巧
crond BAD FILE MODE /etc/cron.d
Apache——阿帕奇簡介
Sublime Text 3 基本配置教程
[games] Parkour
Embedded DSP audio development
導入Excel文件,解决跳過空白單元格不讀取,並且下標前移的問題,以及RETURN_BLANK_AS_NULL報紅
Is it safe to buy stocks and open an account through the account opening link of the broker manager? Want to open an account for stock trading
17 `bs对象.节点名h3.parent` parents 获取父节点 祖先节点
Object类,以及__new__,__init__,__setattr__,__dict__
[iptables & ICMP] description of ICMP Protocol in iptables default policy
猴子都会用的圆形滑动自动吸附UI工具
A16z: metauniverse unlocks new opportunities in game infrastructure
【PaddleDetection】ModuleNotFoundError: No module named ‘paddle‘
被校园暴力,性格内向的马斯克凄惨而励志的童年
MySQL错误
【小程序】使用font-awesome字体图标的解决文案(图文)