As the saying goes [to do good work, we must first sharp tools], in our front-end field is the same, want to do a good project, must have a complete project structure as a support, in order to better team cooperation, for the business.

Every time we create a new project, we need to do some basic configuration, we need to copy from other projects when we don’t have a basic template, and we even need to rewrite some of our own utility classes.

The source address

👩🎓 for new projects, and want to try out the available templates for Vue + TypeScript developmentCopy the code

List of Basic features

  • Partition of directory structure
  • Differentiation of environments (development, test, production)
  • Automatic route management and on-demand route loading
  • Page loading progress
  • API management
  • Vuex/Custom state management
  • Encapsulation of AXIos (repeated requests cancelled, multiple requests sent with only one loading, token invalid and refreshed)
  • General utility functions (anti-shake, choke, etc.)
  • Encapsulation of common instructions (animation instructions, lazy loading of pictures, copy instructions, etc.)
  • Introduction of Web Workers (to start a thread, share the computational burden of the main thread, and be especially useful for particularly time-consuming tasks)
  • WebSocket embedding (two-way communication)
  • Multi-page configuration
  • Element-ui(table, search, encapsulation of paging components, themes, internationalization, etc.)
  • Git commit record optimization
  • Mobile and PC adaptation
  • Handling permissions (button permissions,Dynamically add routes based on permissions)
  • Automated testing
  • Buried point

[The red part is not finished yet]

Create a project

Select [Vue Cli] (https://cli.vuejs.org/zh/guide/) scaffold to quickly create ` Vue ` create XXXCopy the code

The directory structure

Install BREW on MACCopy the code

/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"

Install the treeCopy the code

brew install tree

Tree Lists the directory structure

├── Public │ ├── ├.js ├── App.config.js Unit Test ├─ Package.json Project Information File ├─ Public │ ├── Favicon. Ico │ ├─ index.html │ ├─ other.html │ ├─static│ ├─ ├─ ├─ CSS │ ├─ ├─ ├─ ├─ CSS │ ├─ ├─ test.work.js ├── SRC │ ├─ API API Management, separate folders by multiple pages │ ├─default- page │ │ │ ├ ─ ─ but ts export API │ │ │ └ ─ ─ testModule. API. Ts page under small module API │ │ └ ─ ─ other - page │ │ ├ ─ ─ but ts │ │ └ ─ ─ NewsModule. Api. ts │ ├─ Assets static resource file, compiled by WebPack. Don't need to compile can into the public directory │ │ └ ─ ─ styles common style (style, other common style) │ │ ├ ─ ─ common. SCSS │ │ └ ─ ─ pageAnimate. SCSS │ ├ ─ ─ components │ │ ├ ─ ─ business business component │ │ │ └ ─ ─ xw - list │ │ │ ├ ─ ─ but ts │ │ │ ├ ─ ─ but the ts │ │ │ └ ─ ─ index. The vue │ │ ├ ─ ─ common basic components │ │ │ ├ ─ ─ xw - pagination │ │ │ │ ├ ─ ─ but the ts │ │ │ │ └ ─ ─ index. The vue │ │ │ ├ ─ ─ xw - search │ │ │ │ ├ ─ ─ generateEl. Vue │ │ │ │ ├ ─ ─ index. The ts │ │ │ │ └ ─ ─ index. The vue │ │ │ └ ─ ─ xw - table │ │ │ ├ ─ ─ coustomColumn. Vue │ │ │ ├ ─ ─ GenerateElTable. Ts │ │ │ ├ ─ ─ generateElTableColumn. Ts │ │ │ ├ ─ ─ but the ts │ │ │ └ ─ ─ index. The vue │ │ └ ─ ─ example │ example file │ ├─ ├─ Exercises. Vue │ ├─ Exercises. Vue │ ├─ Exercises. Vue │ ├─ exercises Vue │ ├─ Directive │ ├─.directive. Ts │ │ ├─.directive │ ├─ Draggable.directive. Ts │ │ ├── │ ├─ longpress.directive Permissions. Directive. Ts │ ├ ─ ─ i18n internationalization │ │ ├ ─ ─ but ts │ │ └ ─ ─ lang │ │ ├ ─ ─ en. Ts │ │ └ ─ ─ useful. Ts │ ├ ─ ─ layout project layout │ │ ├ ─ ─ base. Layout. Vue │ │ └ ─ ─. Other layout. The vue │ ├ ─ ─ the mock mock data │ │ └ ─ ─ index. The js │ ├ ─ ─ the plugins project plug-in │ │ ├ ─ ─ config. Ts │ │ ├ ─ ─ index. Ts │ │ └ ─ ─ the lazyLoad. Plugin. Ts │ ├ ─ ─ the router routing management, according to the folder page more point │ │ ├ ─ ─ config. Ts │ │ ├ ─ ─default│ │ ├─ ├─ other │ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ D.ts │ ├── Flag School - Shims-Vue. D.ts │ ├─ Flag School - Shims-Vue. According to the folder page more point │ │ ├ ─ ─ Vuex module of common foundation │ │ │ ├ ─ ─ permissions. Vuex. Ts │ │ │ └ ─ ─ user. Vuex. Ts │ │ ├ ─ ─default│ │ │ └ ─ ─ home. Vuex. Ts │ │ └ ─ ─ but ts export base vuex module │ ├ ─ ─ the theme topics │ │ ├ ─ ─ fonts │ │ │ ├ ─ ─ element - the ICONS. The vera.ttf │ │ │ └ ─ ─ Element - the ICONS. Woff │ │ └ ─ ─ index. The CSS │ ├ ─ ─ types type control file (grammar tips) │ │ └ ─ ─ vue. Which s │ ├ ─ ─ utils tool function folder │ │ ├ ─ ─ common. Ts │ │ ├─ common js functions │ │ ├─ eventCenter. Ts progressBar. ProgressBar │ │ ├─ ReadyLocalStorage. Ts read local store data (user information, token, permissions, etc) coexist in the Vuex │ │ ├ ─ ─ request an Ajax request packaging │ │ │ ├ ─ ─ index. The ts │ │ │ ├ ─ ─ Index. The ts │ │ │ └ ─ ─ request. Ts │ │ ├ ─ ─ requestInstance. Ts Ajax example │ │ ├ ─ ─ useElement. Ts according to the need to use the Element - the UI │ │ └ ─ ─ ├─ ├─ ws. Ts WebSocket Newsletter │ ├─404.Vue │ ├ ─ ─default- page │ │ ├ ─ ─ App. Vue │ │ ├ ─ ─ main. Ts │ │ └ ─ ─ the test -moduleSmall module │ │ ├ ─ ─ home page specific │ │ │ └ ─ ─ index. The vue │ │ └ ─ ─ home2 │ │ └ ─ ─ index. The vue │ ├ ─ ─ the login. The vue │ └ ─ ─ other - page │ ├ ─ ─ App. Vue │ ├── ├─ press -module│ ├─ ├─ ├─ news1 │ ├─ ├─ vue │ ├─ ├─ news2 │ ├─ vue │ ├─ news2 │ ├─ Components │ │ └ ─ ─ coustomColumnHeader. Vue │ └ ─ ─ index. The vue ├ ─ ─ tests │ └ ─ ─ unit │ └ ─ ─ example. The spec. The ts ├ ─ ─ tsconfig. Json ├ ─ ─ vue. Config. Js webpack configuration file ├ ─ ─ yarn - error. The log └ ─ ─ yarn. The lock └ ─ ─ the env. The development of the local environment configuration └ ─ ─ the env. Production production environment configuration └─.env.staging test environment configurationCopy the code

Environment to distinguish the

Different environment variables are implemented through the patterns provided by Webpack

  • The root directoryCreate the following three files respectively

.env.development

NODE_ENV ="development"VUE_APP_REQUEST_URL ='http://localhost:8080'
Copy the code

.env.production

NODE_ENV = "production"
VUE_APP_REQUEST_URL = 'http://prod.com'
Copy the code

.env.staging

NODE_ENV = "production"
VUE_APP_REQUEST_URL = 'http://staging.com'
Copy the code
  • Adding a compile command

package.json

"scripts": {
  "serve": "vue-cli-service serve --mode development"./ / development
  "build:stage": "vue-cli-service build --mode staging"./ / test
  "build": "vue-cli-service build"							/ / production
}
Copy the code

Automatic route management and on-demand route loading

According to the need to load

Import ()

const App = () => import(/* webpackChunkName: app */ './app.vue')

/* webpackChunkName: app */ component block

Asynchronous component approach

Asynchronous components

const App = resolve => require(["./app.vue"], resolve)

Route lazy to load official documents

Automated routing

Recursive module introduction via Webpack’s require.context

require.context(
  directory: String.includeSubdirs: Boolean /* Optional, the default is true */.filter: RegExp /* Optional, default is /^\.\/.*$/, all files */.mode: String  / * optional, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy - once, the default value is' sync' * /
)
Copy the code
Parameters to require.context cannot accept variablesCopy the code

[Benefits: Reduce conflicts in multiplayer development and forget to introduce new modules]

API management

API unified management, in the project, the [add, delete, change and check] URL of a page is unified, which will only change the request mode, so the URL is centralized management

Vuex/Custom state management

Vuex

  • The namespace

    Resolve naming conflicts between Actions mutions between different modules

  • Dynamic registration module in addition to the basic module, other modules dynamic registration and uninstall

Custom state management (publish subscriber pattern)

A collection of different types of event functions is stored in the collection. Methods such as listening, canceling, dispatching, etc

Closures are used to handle single listeners, which store whether or not they have been executed

once(eventName: string, cb: CbType) {
    const { eventStack } = this
    const eventValue = eventStack[eventName]
    const tempCb = () = > {
      let isOutOfDate = false

      return (data: object) = > {
        if (isOutOfDate) return
        cb(data)
        isOutOfDate = true
      }
    }

    eventValue ? eventValue.push(tempCb()) : eventStack[eventName] = [tempCb()]
  }
Copy the code

Axios encapsulation

[Function List]

  • Request address processing

    It mainly deals with the standardization of routing

    /** * Processing path *@param The url path *@param IsBaseURL Is the root path */
    private transformUrl(url = "", isBaseURL = false) {
      if(! url)return url;
    
      if (isBaseURL) {
        if (!/ / / $/.test(url)) {
          return `${url}/ `;
        }
    
        return url;
      }
    
      if (/ ^ / / /.test(url)) {
        return `${url.substr(1)}`;
      }
    
      return url;
    }
    Copy the code
  • Whether loading is required? Only one loading occurs when multiple requests are serialized

    Use a variable to record the number of requests. When there are new requests, the number +1; when the number is 0 and loading needs to be started; when the request is completed, -1, and loading is disabled

      /** * To enable or disable Loading *@param CustomConfig Indicates a custom configuration item *@param IsOpen Whether to enable */
      private handleLoading(customConfig: CustomConfigType, isOpen: boolean) {
            if(! customConfig.isNeedLoading)return;
            // Do not start Loading repeatedly
            if (this.requestCount ! = =0) return;
    
            if (isOpen) {
                console.log("Open Loading");
                return
            }
    
            console.log("Loading off");
      }
    Copy the code
      /** * initiate a request *@param Config Indicates the configuration item *@param CustomConfig Custom configuration */
    private async transfromRquest(
      config: AxiosRequestConfig,
      customConfig: CustomConfigType = {}
    ): Promise<AxiosResponse> { customConfig = { ... this.defaultCustomConfig, ... customConfig };this.transformUrl(config.url);
      this.handleLoading(customConfig, true);
      this.addToken(config, customConfig);
      this.requestCount++
    
      try {
        const result = await this.axios.request(config);
        return result;
      } catch (error) {
        // ...
      } finally {
          this.requestCount--
    		this.handleLoading(customConfig, false); }}Copy the code
  • Whether token is required

  /** * token processing *@param Config Indicates the configuration item *@param CustomConfig Indicates a custom configuration item */
  private addToken(config: AxiosRequestConfig, customConfig: CustomConfigType) {
    if (customConfig.isNeedToken) {
      config.headers = {
        token: store.getters['userStore/getToken'] | |' '
      };
    } else{ config.headers = {}; }}Copy the code
  • Request error handling when presenttokenWhen invalid, refreshtokenSend the failed request again
  /** * initiate a request *@param Config Indicates the configuration item *@param CustomConfig Custom configuration */
  private async transfromRquest(
    config: AxiosRequestConfig,
    customConfig: CustomConfigType = {}
  ): Promise<AxiosResponse> { customConfig = { ... this.defaultCustomConfig, ... customConfig };this.transformUrl(config.url);
    this.handleLoading(customConfig, true);
    this.addToken(config, customConfig);
		this.requestCount++

    try {
      const result = await this.axios.request(config);
      return result;
    } catch (error) {
      const { code, config } = error

      if (code === 401) {
        // Resolve token invalidation

        // Jump to the login page
        // store.commit('userStore/setToken', '')
        // store.commit('permissionsStore/setPermissions', {})
        // router.replace({ path: '/login', query: {
        // redirectUrl: router.currentRoute.fullPath
        / /}})

        // Method 2 automatically refreshes the token and resends the failed request
        const res = await this.transfromRquest({
          method: 'post'.url: '/refresh-token'
        })
        console.log(res, '/refresh-token')
        store.commit('userStore/setToken', res.data.token)
        return this.transfromRquest(config)

        // In request interception, verify whether the token expires before initiating a request
      }

      this.handleError(customConfig, error);
      return Promise.reject(error);
    } finally {
			this.requestCount--
      this.handleLoading(customConfig, false); }}Copy the code
  • Cancel the request

    The CancelToken provided by Axios is combined with queues to achieve this. (The queue stores information about the current request (some custom rules to determine if it is the same request) and the cancel function)

[Disadvantages:]

This kind of cancellation request is actually received by the server, but the browser level has done a layer of processing and cannot wait for the response.

When it is necessary to prevent repeated submission of data, the implementation of this method is not accurate. You can consider anti-shake, execution of variable control function, and click state of variable control button

Introduction of Web Workers

PostMessage cannot send functions

WebSocket embedded

See Packaging a simple WebSocket library

Encapsulation of the element-UI list component

[选 自 :]

  • Divide components, header, content, bottom
    <header class="list-header animate__animated animate__fadeIn">
      <slot name="head" />
      <xw-search
        v-if="searchOption"
        :searchOption="searchOption"
        :searchParams="searchParams"
        @onSearch="getList"
      >
        <slot name="search" />
      </xw-search>
    </header>

    <main class="list-main">
      <slot name="main" />
      <xw-table :tableOption="tableOption" />
    </main>

    <footer class="list-footer">
      <slot name="footer" />
      <xw-pagination
        v-if="paginationOption"
        :paginationOption.sync="paginationOption"
        @onPagination="getList"
      />
    </footer>
Copy the code
  • The search results are held by the list component

searchParams: SearchParams = {};

  • Assembly of tabular data

    To reduce the need for templates during development, all operations related to the table are encapsulated as configuration items.

import { Component } from 'vue'

export interface TableOption {
  // Element-ui table configuration properties
  tableAttribute: TableAttribute
  // The configuration properties of the column
  tableColumn: TableColumn[]
}

export interface TableAttribute {
  / / property
  props: {
    data: object[]
    [index: string]: any
  }
  / / event
  on: { [key: string]: Function | Function[]}}export interface TableColumn {
  / / property
  props: { label? : string prop? : string [index: string]: any },/ / slotsslots? : { [index: string]: {/ / propertyoptions? : object// Custom components
      component: Component
    }
  }
  // Multilevel headercolumnChild? : TableColumn[] }Copy the code

Complex example

Edit the current row, display different buttons according to permissions, and load and disable status of buttons

Git commit record optimization

Use Commitizen instead of your Git commit. Commitizen also requires an adapter. Cz-xcom – Changelog is recommended

  • The installation

npm install -D commitizen cz-conventional-changelog

  • configuration

Package. json configuration:

"scripts": {
    "commit": "git-cz"
  },
"config": {
    "commitizen": {
      "path": "node_modules/cz-conventional-changelog"}}Copy the code
  • use

npm run commit

  • Custom adapter

    • The installationnpm i -D cz-customizable @commitlint/config-conventional @commitlint/cli
    • configuration
"config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"}}Copy the code

At the same time, create the.cz-config.js.commitlintrc.js file in the project directory

The effect is as follows:

Instruction encapsulation

  • Custom animation directives with animate. CSS
  • Copy and paste instruction
  • If you order
  • Drag and drop the instructions
  • Prohibit expressions and special character commands
  • Long according to the instruction
  • Permission control instruction

The source address

The above is just a simple overview, please see the source address for details

Post to recommend

  • Realize project download, automatic routing and project release scaffolding based on Node
  • Encapsulate a simple WebSocket library
  • Note: Vue often meet questions summary and analysis
  • Proxy is an alternative to Object. DefineProperty in Vue3.0
  • Vue 3.0 – First experience 1
  • Figure out a prototype, a prototype object, a prototype chain
  • Promise Principles = Build a Promise from 0 to 1

[Notes are not easy, if it is helpful to you, please like, thank you]