preface
Unconsciously, I wrote articles less frequently than before, mainly because AFTER I transferred to React, I spent some energy from using React to understanding it. In addition, I spent a lot of money on the Little Red Book, so I could not ignore it. So I spent all my working time reading the Little Red Book plan this month. Precipitate yourself first.
The content of this article is for reference only, this program is also an immature research, welcome friends to give me some optimization suggestions.
Asynchronous data issues
Why is there a management solution for asynchronous data? Why should we manage it? You know, a long time ago, when we used to write Api call methods on the top of the page, you could imagine how bad this was. Then the nwtWork is split, we methodically split the API interface, there are separate function files, there are direct to do the form, roughly unchanged is to remove this.$HTTP. Get….. on the page And so on.
But have we solved our problems?
In fact, not at all. In a recent visit to some open source projects, we have frequently seen similar business code
<template>
<view>demo</view>
</template>
<script>
import { couponListApi } from '@/api'
export default {
data: () = > ({
viewLoading: false.// Page loading event
buttonLoading: false.// Click the loading & Disable event
}),
methods: {
// Get the coupon list
getCouponSyncList() {
try {
this.viewLoading = true
const data = await couponListApi()
if (data) {
/ /... todo}}finally {
this.viewLoading = false}}}}</script>
Copy the code
The basic logic is to request a list of data, which triggers a viewLoading update, and then hides loading display after the update, so that when we have multiple asynchronous data to manage, it may be converted into loading and disable behavior control. As the volume of a single service increases, Over time, data and methods will disperse into reef code. When we add iterations again, it’s like being a ship on a reef, and there’s a risk of hitting a reef.
So, the question goes back to the title, what do you do with asynchronous code in option?
The official rules for mixing in mixins are provided to us, so when we can happily mix them in, does this solve the problem? It seems that mixins are a great solution to our form of code reuse, but they also present a new problem: because of the way mixins work, the code that gets mixed in is unknowable. So when you’re using mixins, you need to be aware that both sides are defining the same thing, and that there’s no overwriting conflict.
- Accidental code conflict
- Mixed ambiguity
- Mental burden
So we can think about doing more things on mixins, taking some of the burden out of mixins and making changes to them, so as to have a good effect on code management and maintenance.
How to solve the problem?
I looked at Umi’s DVA model to solve the problem, but it wasn’t a good idea to manage data in Vuex, so I pulled out the mixins. In the end, I changed mixins completely. Is the final result of the asynchronous data code alone split, forming a data access layer as a treatment, we here to deal with the data, when we these data or logic need to be changed, we just need to separate the data access layer of this model to modify, can quickly to replace logic.
What is the Data access layer?
Many people have a doubt, the article begins the data access layer is a what thing, most people get from literal meaning, you will know the function of this layer, it is used for the bridge of the layer Model and data Model, it can also be spun off as the controller of a writing mode, we’ll call it “the data access layer,” We just need to know that it’s a communication tool.
- JSON data retrieval
- Api data acquisition
- Third party data access
- other
All of this belongs to the data access layer. These codes are often linked to business logic and background data to act as a bridge between the preceding and the following.
Model design of Mixin
Since mixins are to be handled, we tried to make the syntax more team-friendly, so we created model.js in the project’s page path to declare the data access layer, and changed it to a Vuex Moudule class Api to allow for learning the Api.
example
export default {
namespace: 'test'.state: {
move: {},
a: 1.b: 2,},actions: {
/** * Obtain the current user identity information *@param { Object extends VueData } state
* @param { Object } payload
* @param { function } set
*/
async getUser(state, { payload, set }) {
const data = await test()
await set(state, 'move', data)
return state.move
}
}
}
Copy the code
state
The current module must specify the namespace, and the data in the state will eventually be converted to an object with the same name as the data in the mixin, instead of being flat in the data under our page (view) layer. We only need to pay attention to the introduced model namespace to avoid the mixins conflicts. Instead, we need to have a more strict understanding of the data needed to be reactive. Instead, we need to use $set for data that is not within the scope of observation.
actions
Actions will also have a layer of namespace wrapping, and they’ll be mixed in with methods under the view layer. Similarly, we’ll always mix in a dispatch method that serves as the entry point to the actions, and the methods that are mixed in have a namespace identifier on them, Avoid producing the same method resulting in being replaced.
This time the action takes several arguments:
state
Of the current namespace$data
Data, you can use it directly tostate
For the assignmentoption.payload
dispatch
The passed parameter object can be retrieved fromdispatch
The object passed in.option.set
Will the current instance$set
It comes in, and it comes inthis.$set
Equally, both can be used.
dispatch
As the entry point for all the execution of mixin methods, Dispatches are responsible for more than just parsing the mixins Model namespaces that have been mixed in. They are responsible for most of the unified processing, which is still the example above. Each of our actions actually changes the state of the corresponding Model action, which can be seen in the model variable in $data.
this.dispatch("test/setUser", {
user: "1111"
}, true).then((res) = > {
console.log(res);
});
Copy the code
The parameters of dispatch are very simple. In most cases, only two parameters are required. The following parameters can be customized according to the logic of the service, such as showLoading under uni-app.
type
The incomingtype
Is a dainty thing that you need to passnamespace/actionName
To specify which namespace you want to call the asynchronous method.payload
给action
Is similar to the normalargument
.
perform
How to inject
Introducing Model mixing
import { createModel } from "@/plugin/controller";
Copy the code
Introduce the corresponding model
export default createModel({
name: "componentName".methods: {
getUserData() {
this.dispatch("test/setUser", {
user: "1111"
}, true).then((res) = > {
console.log(res); }); }},},"test"."index"]);Copy the code
What does createModel do?
- Get the current directory
model.js
- Analysis and packaging
model.js
intomixins
- for
component
Inject or merge resolvedmixins
- Components parsed by Vue to produce a hybrid
Disassembled code
Code words written in a unified GenerateModel class, through this class can be unified model processing.
Find all model.js
GetCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel () getCurrentModel ();
getCurrentModel() {
const context = require.context('.. /.. /pages'.true./model.js$/)
context.keys().forEach(key= > {
const currentModel = context(key).default
if (this.modelNames.indexOf(currentModel.namespace) ! = = -1) {
this.resultStock.push(currentModel)
}
})
}
Copy the code
camouflagemixins
Using the generateModelMixinData method, you can convert the data stored in a resultStock into a conventional format to confuse names.
generateModelMixinData() {
const mixinQueue = []
if (this.resultStock.length > 0 ) {
this.resultStock.forEach(model= > {
const transformMethods = {}
const transformLoadingEffect = {}
const { namespace, state = {}, actions = {} } = model
Object.keys(actions).forEach(fun= > {
transformMethods[`${namespace}__${fun}`] = actions[fun]
transformLoadingEffect[`${namespace}/${fun}`] = false
})
mixinQueue.push({
data: () = > ({
model: {... transformLoadingEffect}, [namespace]: { ... state } }),methods: {
...transformMethods,
dispatch: this.dispatch
}
})
})
} else {
this.warning('No Mdoel data found')}return mixinQueue
}
Copy the code
Dispatch call
In dispatches, for its own part, actively exposing a Promise can inject some business logic before and after methods, such as state updates of async methods, loading, and hiding. Entrust the parameters we host at Dispatch to our action.
dispatch (modelCursor, payload, loading = false) {
const [ namespace, actionName ] = modelCursor.split('/')
return new Promise((success, fail) = > {
loading && uni.showLoading({
title: 'loading'
})
this.$set(this.model, `${namespace}/${actionName}`.true)
this[`${namespace}__${actionName}`] (this[namespace], {
payload,
set: this.$set
}).then(res= > success(res)).catch(err= > fail(err)).finally(() = > {
loading && uni.hideLoading()
this.$set(this.model, `${namespace}/${actionName}`.false)})})}Copy the code
Injection components
CreateModel will eventually be merged with the Mixins in the Component to become a new component, so it can coexist with the Mixins.
export function createModel (components, names = []) {
const createSetup = new GenerateModel(names)
createSetup.getCurrentModel()
const transFormModel = createSetup.generateModelMixinData()
if(components? .mixins) {const preMixin = components.mixins
components.mixins = transFormModel.concat(preMixin)
}
components.mixins = [...transFormModel]
console.log(components)
return components
}
Copy the code
The source code
Recently I just read some articles of industry bigwigs and have some new ideas. I will also post the article
Front-end architecture design for Model programming @ industrial clustering
The sample code
<template>
<view class="content">
<view>
<! -- <u-button type="error"> </u-button> -->
<text class="title">{{ JSON.stringify(test.move) }}</text>
<u-button
type="error"
:disabled="model['test/setUser']"
@click="getUserData"
></u-button ></view>
</view>
</template>
<script>
import { createModel } from ".. /.. /plugin/controller";
export default createModel(
{
name: "test".methods: {
getUserData() {
this.dispatch("test/setUser", {
user: "1111"
}, true).then((res) = > {
console.log(res); }); }},},"test"."index"]);</script>
Copy the code
The source code
class GenerateModel {
constructor (modelNames) {
this.modelNames = modelNames
this.resultStock = []
}
getCurrentModel() {
const context = require.context('.. /.. /pages'.true./model.js$/)
context.keys().forEach(key= > {
const currentModel = context(key).default
if (this.modelNames.indexOf(currentModel.namespace) ! = = -1) {
this.resultStock.push(currentModel)
}
})
}
generateModelMixinData() {
const mixinQueue = []
if (this.resultStock.length > 0 ) {
this.resultStock.forEach(model= > {
const transformMethods = {}
const transformLoadingEffect = {}
const { namespace, state = {}, actions = {} } = model
Object.keys(actions).forEach(fun= > {
transformMethods[`${namespace}__${fun}`] = actions[fun]
transformLoadingEffect[`${namespace}/${fun}`] = false
})
mixinQueue.push({
data: () = > ({
model: {... transformLoadingEffect}, [namespace]: { ... state } }),methods: {
...transformMethods,
dispatch: this.dispatch
}
})
})
} else {
this.warning('No Mdoel data found')}return mixinQueue
}
dispatch (modelCursor, payload, loading = false) {
const [ namespace, actionName ] = modelCursor.split('/')
return new Promise((success, fail) = > {
loading && uni.showLoading({
title: 'loading'
})
this.$set(this.model, `${namespace}/${actionName}`.true)
this[`${namespace}__${actionName}`] (this[namespace], {
payload,
set: this.$set
}).then(res= > success(res)).catch(err= > fail(err)).finally(() = > {
loading && uni.hideLoading()
this.$set(this.model, `${namespace}/${actionName}`.false)
})
})
}
warning (message) {
console.warn('model.js: ', message)
}
}
export function createModel (components, names = []) {
const createSetup = new GenerateModel(names)
createSetup.getCurrentModel()
const transFormModel = createSetup.generateModelMixinData()
if(components? .mixins) {const preMixin = components.mixins
components.mixins = transFormModel.concat(preMixin)
}
components.mixins = [...transFormModel]
console.log(components)
return components
}
Copy the code
Thinking and summarizing
Thinking is just a kind of thinking, this is just a kind of immature program. The problem is very obvious, either overhead or reporting rate is at a level to be fastidious, is that I don’t like before a mixin into form, but any proposed an API has its purpose, and we can do is think about API flexibility to meet our business needs, Technology is ultimately about serving the business. It’s just a bit of a nab here. When our business is big enough, if the project is not clear enough, then it will be a very bad problem to maintain.
Also read most articles do not recommend mixins for code mixing, this practice is in a toss of things. The hope is to separate the asynchronous code from our VUE pages and components into a single logical layer, rather than being stacked with the main view. Quickly locate the location of the code and make consistent changes without affecting each other.
Now most people are chasing Vue3, but it will take some time for Vue3 to actually be on the ground.
If this was useful to you, give it a like. If you have good suggestions, please leave them in the comments section.