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:

  • stateOf the current namespace$dataData, you can use it directly tostateFor the assignment
  • option.payload dispatchThe passed parameter object can be retrieved fromdispatchThe object passed in.
  • option.setWill the current instance$setIt comes in, and it comes inthis.$setEqually, 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.

  • typeThe incomingtypeIs a dainty thing that you need to passnamespace/actionNameTo specify which namespace you want to call the asynchronous method.
  • payloadactionIs 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 directorymodel.js
  • Analysis and packagingmodel.jsintomixins
  • forcomponentInject 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.