Summarize some basic configuration package sharing developed by the Vue team

Summarize some basic configuration package sharing developed by the Vue team

Introduction

This article is mainly brought about by vue 基础架构chapter, as we all know, Vue3.0after Vue2.0there will be a final version came out, it means that Vueushered in a new era, but not all projects are able to move towards a new era of ship together. This article is mainly to make a sequel to the last optimized technical article. This sequel is mainly for team development related things. Related plug-ins and libraries are only slightly introduced. If this article can promote your production line, please like it.

For configuration related categories, you can search for the corresponding sharing posts, or read my previous articles. The content of this article is more suitable for team development, and most of the non-toolchain sharing articles can trigger some technical thinking.

Preface

In many cases, for the vueproject, many just getting started, or is subject to compromise business friends are from Baidu CVset of shelves seen in the past, such as the common D2Admin, , vue-element-adminto develop a second iteration of its project itself is very quality, and in its templategoing be able to make changes in a project at the outset there is a good foundation for the environment, but if you do not take the time to Zhuomo Tou one-third of details. In the follow-up demining it is undoubtedly very difficult, so most of the front-end team will reconstruct the basis of their own set of scaffolding packages, there by webpackprocessing, but also based on VueClibuild, and ultimately are the property of their team, from Technology sharing and subdivided practices can bring more or less development convenience to the team's friends. Changes to follow-up team personnel can also be quickly put into production.

What have you done?

  • Basic HTTP request encapsulation
  • Conventional HTTP request management
  • Mmixin data management
  • jsdoc project documentation
  • log exception handling
  • Component and page management
  • Commonly used commands
  • Use sass or scss?
  • @mixin and %placeholder
  • eslint

What do basic HTTP requests do?

Error handling

Here the choices are now better compatibility axios, it can be said to be better request a library, compared to fetchit, both have their advantages, (I've been using fetch). This part is actually nothing more than a package of some behaviors need to be addressed when the public calls, such as: token, 请求拦截, 响应拦截, 错误码约定, 异常上报, 中间件and so on the basis of a series of behavior patterns.

The following examples, when HTTPthe time error of the request by first getEnvobtaining the current development environment, if it is dev(开发环境下)only a simple console.log, non-development environment monitor abnormality is reported, for both front and back ends for

function handleError (error, msg) {
  if (getEnv() ==='dev') {
    tools.log.danger('>>>>>> HTTP Error >>>>>>')
    console.log(error, msg)
  } else {
    Store.dispatch('logs/push', {
      message: msg,
      type:'danger'
    })
  }
}

RESTFul

Relative to the number of interceptors, it is very simple, nothing more than the need to pay attention to develop some rules, according to some norms of the team, such as the common codecode and other methods, in most cases, if nothing unexpected happens, 99 percent of the interfaces are requested success, but because of the particularity, there will be inside a codestate to define positive and negative. Similarly, when operating an interface, some states need to be synchronized with the request mode of the interface. Refer to the following:

  • GET: 200 OK
  • POST: 201 Created
  • PUT: 200 OK
  • DELETE: 204 No Content

Now most of the interface specification uses RESTful, if you do not know RESTfulwhat it is, you can take a look at @阮一峰the article to a preliminary understanding. RESTful API best practice @阮一峰's weblog

Status code mechanism

The same code, we also customize the daily development of some of the state code, when we need to use the 第三方APItime before and after the end of the need to quickly locate a problem of its own services, or other services (for example, in Taiwan) issue, therefore docking services we have a number of custom codeto present this type of error handling. You can refer to the following, these are actually created in an object:

Custom code

code

status

description

30000

invalid credential

Illegal certificate

30001

invalid connect_type

Illegal connect_type

30001

invalid group_conf_id

Invalid group_conf_id

......

......

......

When we refine some anomalies, it can be divided very carefully at this time. Here are some references for WeChat:

40039

invalid url size

Illegal URL length

40048

invalid url domain

Illegal url domain name

40054

invalid sub button url domain

Illegal submenu button url domain name

40055

invalid button url domain

Illegal menu button url domain name

40066

invalid url

Illegal url

41001

access_token missing

The access_token parameter is missing

41002

appid missing

Missing appid parameter

41003

refresh_token missing

The refresh_token parameter is missing

41004

appsecret missing

Missing secret parameter

41005

media data missing

Missing binary media file

41006

media_id missing

The media_id parameter is missing

41007

sub_menu data missing

Missing submenu data

41008

missing code

Missing code parameter

41009

missing openid

Missing openid parameter

41010

missing url

Missing url parameter

42001

access_token expired

access_token timeout

42002

refresh_token expired

refresh_token timeout

function createHttpService (settings) {
  const service = Axios.create(settings)
  service.interceptors.request.use(
    config => {
      
      const token = localStorage.getItem('access_token')
      config.headers.token = token
      return config
    },
    error => {
      return Promise.reject(error)
    }
  )
  
  service.interceptors.response.use(
    response => {
      console.log(response)
      const {code, message, data} = response.data
      
      if (code >= 30000) {
        console.log('>>> custom error message, global prompt processing', message)
        return data
      }
      
      if (code >= 200 && code <300) {
        return data
      }

      
      if (code >= 300 && code <600) {
        return Promise.reject(response.data)
      }
    },
    error => {
      const {status = 404} = error?.response
      if (Object.prototype.hasOwnProperty.call(codeMessage, status)) {
        handleError(error, codeMessage[status])
      }
      throw error
    }
  )
  return service
}

const http = createHttpService({
  withCredentials,
  timeout,
  baseURL
})

Conventional http request

After reading the articles in my last few articles, everyone is generally aware that the agreed request can simplify the complexity of request encapsulation. Similarly, when your company has a novice or an intern, the request is not considered for unpacking. Yes, when you deliver a task, completion does not mean better completion.

In addition to the agreed formula while reducing code for novice developers factors of instability in the team, but also reduces the number of write when developing a AxiosPromisefunction of repetitive behavior. Here is a basic interface to the agreement, in login-api.jscase of written documents, it will be mapped to请求函数

export default {
  getPerson:'GET/person',
  setPerson:'POST/person',
  updatePerson:'PUT/person/:id',
  deletePerson:'DELETE/person/:id'
}

As log.jsa result of printing, team developers do not need to concern itself function, only need to focus on calls. At the same time, 开发环境下all interface information will be through console.tableto the console, in the absence of a good type inference case, still you can quickly call the corresponding interfaces to back-end data acquisition.

The splitting of the api function itself is actually a repetitive work, which is meaningless for the development of developers.

Different calling methods

In order to make a unified call, two methods of use are appropriately given. In most scenarios, the use is common. The first method is more conservative. It is essentially left to members to handle tasks. Examples:

<script>
 
import {useServices} from'framework'


const {getPerson} = useServices()

export default {
  name:'home',
  created () {
    
    getPerson().then(res => {
      console.log(res)
      alert('Interface request is successful')
    }, err => {
      console.log(err)
      alert('Interface request failed')
    })
  }
}
</script>

The above example is very simple, I believe students with a little foundation can see that the first method is very common and suitable for most people, but the disadvantages are also obvious. If every interface needs to be done once then & catch & finally , then certainly a Disaster, so the second method was born, which is more friendly to novices. Examples are as follows:

<script>
 
import {useServices} from'framework'

const Admin = {
  created () {
    this.getPersonData()
  },
  methods: {
    
    async getPersonData () {
      const [, err] = await useServices('getPerson')
      if (err) {
        alert('Interface request failed')
      } else {
        alert('Interface request is successful')
      }
    }
  }
}
export default Admin
</script>

On the basis of the original api, useServicesto Promiseact wrap a layer of middleware, when you decide to use the very state request, this promise中间件behavior is triggered. And the Promiseresults form the abstract became an array returned it, then the logic block, we simply need to pass async & awaitthe data results are processed without concern meaningless try catchand then catch.

The reason for the compatibility of the two methods is that different developers have different habits. Sometimes developers believe that the handling of errors is left to the handler to solve them, so as to achieve the purpose of error resolution.

Mixin data management (model)

With the agreed request, the problem of our request is solved uniformly, but then comes the problem of asynchronous data management. For a long time, Vue developers have habitually placed interface requests and data logic processing. in vue file, such as the most common 分页表格数据, 基础表单显示each page of the statement are a lot of total, pageSize, currentPage, tableDataand other fields, and never tired of repeating CV, one day after the end of a busy day and feel flattered completed more than 10 pages. In fact, a careful student also found that, no matter how many times the code you CV, to enhance their own is limited, nothing more than Practice makes perfect, copy and paste more accelerated pace, enough to write 4,000 times than you hellodoes not mean you have 4000 words are average.

Therefore, a form component that encapsulates itself is produced, and only a small part of the parameters (such as HTTP request method) needs to be passed in to achieve the realization of rendering the form. Similarly, there are also small partners wraps Global Mixinto solve this part of the task. Similarly, in order to manage the data layer well, I am also trying different data management. As the business logic increases, the asynchronous data of most pages is difficult to manage, and it will be more or less confused with the logical data of the page. After a period of time, you need to$data data re-sort deconstruction to bubble through the logic.

Thus, after trying different solutions, mixinhas become the brunt of the program, it is not like vuexin general will take effect globally, but only works on mixed page, so after a simple attempt to mixin was packed, became abstract a modellayer, the modelactive layer of the main menu level is the flow of processing asynchronous data pages to create, view data in a page .vuedeclared, 后端数据inmodel.js middle.

A basic model.js it looks like this

export default {
  namespace:'Home',
  state: {
    move: {},
    a: 1,
    b: 2
  },
  actions: {
    
    async setUser (state, {payload, set }) {
      const data = await test()
      await set(state,'move', data)
      return state.move
    }
  }
}

const test = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        user:'wangly',
        age: 21
      })
    }, 2000)
  })
}

Then in the page, declare the components will be passed to useModelsthe intermingled, mixed after Mixinnaming format has been more complicated, it is not current at this time to use this.xxx, but the uniform implementation of this.useDispatchprocessing, and not directly to the trigger model methods, the same for all modelare changed state, have different internal loading.

A simple example

To simulate a server through a simple example of load data, the process from scratch, the pure model.jscontrol data and status by the following test simulated a data interface, in getDessertsfor acquisition, in order to ensure the quality of reading, the data is truncated analog, can Refer to vuetifyUI Table Demo data.

export default {
  namespace:'Admin',
  state: {
    mockTabData: [],
    headers: [
      {text:'Dessert (100g serving)', value:'name' },
      {text:'Calories', value:'calories' },
      {text:'Fat (g)', value:'fat' },
      {text:'Carbs (g)', value:'carbs' },
      {text:'Protein (g)', value:'protein' },
      {text:'Iron (%)', value:'iron'}
    ]
  },
  actions: {
    
    async getDesserts (state, {payload, set }) {
      const data = await test()
      console.log(data)
      if (data) {
        state.mockTabData = data
      }
    }
  }
}

const test = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([
        {
          name:'Frozen Yogurt',
          calories: 159,
          fat: 6.0,
          carbs: 24,
          protein: 4.0,
          iron: '1%'
        },
        
      ])
    }, 2000)
  })
}

In the page, in createdthe hook by calling this.useDispatch('Admin/getDesserts')for data acquisition, and then Admin.headers, Admin.mockTabDataassign the parameters to the corresponding component up, through 当前modela method for controlling the side effects of the screen skeleton.

All modelmethods are the datageneration state of a side effect, in order to avoid conflicts, data defined avoid model, to avoid being model.jscovered.

<template>
  <div>
    <v-data-table
      v-if="!model['Admin/getDesserts']"
      :headers="Admin.headers"
      :items="Admin.mockTabData"
    ></v-data-table>
    <v-sheet v-else>
      <v-skeleton-loader :boilerplate="false" type="table"></v-skeleton-loader>
    </v-sheet>
  </div>
</template>

<script>
import {useModels} from'framework'

const Admin = {
  created () {
    this.useDispatch('Admin/getDesserts')
  }
}
export default useModels(Admin, ['Admin'])
</script>

Look at the effect, it is very simple to control the data response to changes:

In this characteristic experiment, it is only for reference. The performance pressure test is still in progress QAQ.

jsDoc project documentation

Project documentation is a very important thing. Don’t trust your own code too much. If your business is large, you may lose some of your intuitive memory in the project you are dealing with in about 3 months. At this time, whether you continue to maintain it or a newcomer continue to maintain a very difficult thing is, at the same time need to consider is when the project to the general, followed by a time of new entrants, huge components, utils,api will make the couple feel feeling unable to start, so it is particularly valuable a document Up. Then a classmate asked, my business is too busy, and I still need to spend time organizing documents, what is the business of other people to me?

A good project is bound to have a good document, based on this kind of problem, so it introduced a document tool to generate documents, during this period, and also to the document has been improved, more fit vueitself, the first is to document syntax @modulehas been transformed, through @pageto page statement by @componenta statement of public assembly, the following example:

<template>
  <div>2222</div>
</template>

<script>
import {useModels, useServices} from'framework'

const Admin = {
  created () {
    this.getPersonData()
  },
  data: () => ({
    
    discount: false,
    
    currentTab: 1
  }),
  methods: {
    
    async getPersonData () {
      const [, err] = await useServices('getPerson')
      if (err) {
        alert('Interface request failed')
      } else {
        alert('Interface request is successful')
      }
    }
  }
}
export default useModels(Admin, ['Admin'])
</script>

By then the final yarn docdocument generation command:

yarn doc

The effect looks like this

It can be seen now some of the comments have been very standardized, but still not perfect, the main factor is from the jsdocthematic issues of the document, if the team needs, you can set up their own reconstruction, and relatively simple.

The examples in the article are for reference only, please move to the annotation document: jsdoc

Custom development log log

For consoleuse when looking at D2Adminthe time to be a clone over, throwing logs for wrong, we do not need to own some conslealso collected, but consolebetween levels there, if by console.errorconduct, it may Will be captured and passed to the background, therefore, a rewrite of log.js for debugging and use of the development version and the test version.

const log = {}


function typeColor (type ='default') {
  let color =''
  switch (type) {
    case'default':
      color ='#303133'
      break
    case'primary':
      color ='#409EFF'
      break
    case'success':
      color ='#67C23A'
      break
    case'warning':
      color ='#E6A23C'
      break
    case'danger':
      color ='#F56C6C'
      break
    default:
      break
  }
  return color
}


log.capsule = function (title, info, type ='primary') {
  console.log(
    `%c ${title} %c ${info} %c`,
    'background:#35495E; padding: 1px; border-radius: 3px 0 0 3px; color: #fff;',
    `background:${typeColor(
      type
    )}; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;`,
    'background:transparent'
  )
}


log.colorful = function (textArr) {
  console.log(
    `%c${textArr.map(t => t.text ||'').join('%c')}`,
    ...textArr.map(t => `color: ${typeColor(t.type)};`)
  )
}

log.default = function (text) {
  log.colorful([{ text }])
}

log.primary = function (text) {
  log.colorful([{ text, type:'primary' }])
}

log.success = function (text) {
  log.colorful([{ text, type:'success' }])
}

log.warning = function (text) {
  log.colorful([{ text, type:'warning' }])
}

log.danger = function (text) {
  log.colorful([{ text, type:'danger' }])
}

export default log

The effect as shown below:

log.default('>>> I am some default prompt')
log.primary('>>> I am some markup hint')
log.success('>>> I am some success tips')
log.warning('>>> I am some warning notice')
log.danger('>>> I am some error message')

Component and page management

During the development process, develop some good habits for the same project experience will be a good help, write code and needlework, as carefully and deliberately, in order to learn more logically distinct, reduce P0 BUGthe appearance, if you do not rush the project, The same problem has always appeared, and the senses are very poor. Therefore, keep the following tips in mind, I hope it will help you

Page file

It is recommended that all page levels be placed under a tree, the directory menu uses folders and the default view is index.vue, the names are all lowercase camelcase, it is best to cover a sentence such as: home, user. Etc., components unified on the start page of componentsthe next, and the name for the big hump with the module name, such as Adminunder the Headercomponent is AdminHeader.vue, for the use: <admin-header/>When introducing uniform use @/views/admin/components/xxxintroduced. The menus are all things under the first-level menu, and the page name is to better distinguish them and prevent component confusion.

Method export

In many cases, different team members in writing utilswhen there use the arrow functions, are also used functionto declare, so here is recommended to use a unified export functionform of conductjs a declaration, as follows:

import asyncAxiosInstance from'@/plugin/createService'
import currentModels from'@/plugin/createModel'

export function getEnv () {
  return process.env.VUE_APP_MODE ||'dev'
}


export function useModels (component, models, isDispatch = true) {
  const current = []
  currentModels.forEach(item => {
    if (models.includes(item.name)) {
      current.push(item.mixin)
    }
  })
  if (isDispatch && current.length> 0) {
    current.push(currentModels[0].mixin)
  }
  console.log(current)
  if (component?.mixins) {
    const preMixin = component.mixins
    component.mixins = current.concat(preMixin)
    return component
  }
  component.mixins = [...current]
  console.log(component)
  return component
}

Commonly used commands

In daily development, some instructions can achieve a multiplier effect with half the effort, but the instructions need to be tailored to the business. At the same time, the designer also needs to mark the document when designing, so that team members can view it. The following commands are recommended to be registered:

  • v-click-outside external click instruction: when clicking on an unbound element, the element will be hidden
  • v-intersect element monitor: detect whether the element is visible in the user view
  • v-resize zoom monitor: monitor instructions when the window is zoomed
  • v-scroll scroll monitor: can flexibly observe the scroll changes of bound elements
  • v-touch touch monitor: can flexibly monitor the touch behavior in the mobile terminal and generate callbacks
  • v-auth rights monitors: rewrite from v-permissionthe main job button and page-level permissions check permissions check

Instruction resource

Use SASS or SCSS?

Now the best CSS扩展语言is still SASSand LESS, the big difference between the two is not bad, according to the team may need to be replaced, what difference does not use. In a development project, for SASSI am the first recommendation of the (non-SCSS), if not familiar with SASSthe students will feel very humanity, but if your specification is good, I think you can look at this the following SASScodes:

@mixin flex ($mod: flex, $justifyContent: center, $alignItems: center, $direction: initial)
  display: $mod
  justify-content: $justifyContent
  align-items: $alignItems
  flex-direction: $direction
//end flex mixin ...

When writing CSS, we have suggested that in the end add some endcomments to a logic symbol is completed, the code used to distinguish the style block, prevent logical confusion, when a large number of poorly maintained style, I want to SCSSgive a sense of security is relatively high For the same reason, when the maintenance is good, it SASSis undoubtedly more concise. Of course it is also prone to errors.

The two have the same goal by different routes and can be selected according to the habits of team members.

@mixin and %placeholder

First of all, @mixinfor having to write some logical css, as the most basic flex, you can transfer paramsdifferent settings, it is %placeholderlacking, but %placeholderin succession static style can reduce repeat csscalls, reduce duplication of code, use most scenes are: 基本卡片样式, 统一的组件样式etc. unbiased when the draft design use, there is no need to use no brain @mixin, sometimes %placeholdereven more.

Use an example for comparison:

%demo
  color: red
  font-size: 20px
  border: 1px solid red
//end ...

.test1
  @extend %demo
//end ...

.test2
  @extend %demo
//end ...

.test3
  @extend %demo
Copy code

As the code, using %placeholderthe final pattern will be generated is such that the following:

.test1, .test2, .test3 {
  color: red
  font-size: 20px
  border: 1px solid red
}

And if replaced @mixin, their result is this:

@mixin demo()
  color: red
  font-size: 20px
  border: 1px solid red
//end ...

.test1
  @include demo()
//end ...

.test2
  @@include demo()
//end ...

.test3
  @@include demo()
//end ...

After compilation:

.test1 {
  color: red
  font-size: 20px
  border: 1px solid red
}
.test2 {
  color: red
  font-size: 20px
  border: 1px solid red
}
.test3 {
  color: red
  font-size: 20px
  border: 1px solid red
}

Needless to say, you should know how to use it.

ESLint

Ideally, most front-end teams will have Eslintit as a standard of code quality, but it is only a standard.Git Commit pre-action can execute code review is qualified to prevent sub-standard code submitted to the repository, This action requires developers to develop good coding habits and code quality awareness.

If you are using VS CODEthen you need to configure the compiler to open the rule checking, when contrary to the syntax warning at the same time, it prompts the following warning:

It is not recommended to directly compile the code when committing directly. Eslint is to help developers grow their code, not a superficial effort tool.

Source code and resources

  • jsDoc: Go now
  • Source address: Go now
  • vuetifyjs: Go now
  • model management data thinking: click to go
  • api convention: click to go

summary

Unconsciously, I haven’t written an article for two months. In addition to getting busier after changing jobs, I spend time with someone and reading literature books. I am very surprised to see many friends repost my articles. The quality of the article is more demanding. With the accumulation of work experience and technical learning, I hope that the content of the article will be technically improved for you.

This article should undertake on a Vue开发中的一些常见套路和技巧(上)lower volume, the relative terms, more and more things in this article, and more practical, in addition to promoting individual developers, but more of a thinking on team development. Front-end engineering has gradually become perfect, and team building has become crucial. A few years ago, there was no front-end architecture. Today, the front-end architecture has become crucial in development. It is the tide of the times. Only by continuous learning can thinking be able to produce a little sporadic spray on the surging waves.

Write an article late at night, as a Vue article shared by a small front-end of Vue + React, if it is helpful to you or your team, please give me a thumbs up and change clothes for the winter. Hangzhou is really cold in winter.

At last

If you think this content is very inspiring for you, I would like to invite you to do me a little favor: click " Looking ", so that more people can see this content (if you like it or not, it's all Playing hooligan-_-)

Reference: https://cloud.tencent.com/developer/article/1781004 summarizes some basic configuration package sharing developed by the Vue team-Cloud + Community-Tencent Cloud