In my last article, I wrote about the integration of Vue and typescript. I found that many people are interested in the vuE-CLI project, so TODAY I am going to write about how to further improve the vue-CLI3.0 shelf and integrate the front-end shelf with basic functions, including the following features:

  1. Webpack packages extensions
  2. CSS: SASS support, normalize.css
  3. Rem layout
  4. Route design: lazy loading, pre-check, validity check
  5. API design
  6. Request body design – Prevent duplicate submission
  7. Vuex status management

Webpack packages extensions

The most important feature of VUE-CLI3 is zero configuration. The scaffolding hides webpack-related configuration in @vue-preload-webpack-plugin. The default configuration can meet most application scenarios, and the advantage is that we can save a lot of time to fiddle with configuration. There is a bit of a threshold, so that newcomers can focus more on vUE coding. The disadvantages are also obvious, for want of their own custom configuration, it will be a little more troublesome.

View the detailed configuration of the current WebPack

A detailed list of configurations can be viewed using Vue Inspect

Extend the WebPack configuration

When we want to modify or extend the Webpack configuration item, we can add the vue.config.js file in the root directory

/ / webpack extension
module.exports = {
    baseUrl: 'production' === process.env.NODE_ENV ?
        '/production-sub-path/' :
        '/'.chainWebpack: config= > {
        config.module
            .rule('images')
            .use('url-loader')
            .tap(options= > Object.assign(options, { limit: 500 }));
    },
    devServer: {
        open: 'darwin' === process.platform,

        / / host: '0.0.0.0',
        port: 8088.https: false.hotOnly: false.// proxy: 'https://api.douban.com' // string | Object 
        proxy: 'http://localhost:3000' // string | Object 
    },
    lintOnSave: false
};
Copy the code

The official website vue.js development standard tool introduction is very detailed, and there are Chinese versions, very easy to understand,

Sass support

  1. Write this in the component<style lang="scss"></style>SCSS syntax is supported
  2. Import other SCSS files from the component as follows
<style lang="scss">
@import "./assets/style/app";
</style>
Copy the code
  1. Use custom functions and mixins in components. I have not found a way to reference them globally, so I can only reference them manually in the component files that need to be used, as shown below
<style lang="scss"> @import ".. /assets/style/functions"; @import ".. /assets/style/mixin"; The rem {height: px2rem (p 187.5); // custom function}.mimi {@include clearfix(); </style>Copy the code
  1. To smooth out the differences between browsers, we need to introduce normalize.css
// app.scss
@import "./node_modules/normalize.css/normalize"; // Reference third-party normalize
@import "custom_normalize"; // Custom normalize
Copy the code

Rem layout

Using REM layout on mobile is a good choice. Since we use SCSS in mobile, we can use functions to simplify our work of double-counting. The design is usually given a 2x image, 750px wide, So we can set the benchmark for the document. The getElementsByTagName (‘ HTML ‘) [0]. Style. FontSize = window. The innerWidth / 10 + ‘px’; Then write a conversion function like this:

// _functions.scss
@function px2rem($px) {
    $rem: 75px;
    @return ($px/$rem) + rem;
}
Copy the code

We can write it this way when we’re using it

.rem {
    height: px2rem(300px); // The width is 300px.
}
Copy the code

Convert to CSS

.rem {
    height: 4rem;
}
Copy the code

Routing design

Mainly includes lazy route loading, route pre-check, validity check logic, the following is a simple route I wrote

import Vue from 'vue';
import Router from 'vue-router';

// Route lazy loading
const getComponent = (name: string) = >() = >import(`./views/${name}.vue`);

Vue.use(Router);

const router = new Router({
    routes: [{path: '/'.name: 'home'.component: getComponent('home')}, {path: '/about'.name: 'about'.component: getComponent('about'),
            meta: {
                auth: true}}, {path: The '*'.name: 'not_fount'.component: getComponent('notFount')}});/** * Route pre-check */
router.beforeEach((to, from, next) = > {
    // Verify the validity
    if (to.meta.auth) {
        console.log('into auth');
        next();
    }
    next();
});
export default router;
Copy the code

API design

The new service folder is used to store API scripts. The files are divided according to service modules, for example, one user-related API file and one purchase file. Api. ts is a collection of apis of each module, as shown in the following figure

// service/api.ts
export { userApi } from './user';
export { buyApi } from './buy';

// service/user.ts
export const userApi = {
    /** * Get user data */
    userInfo: '/node_api/read/userInfo'
};
// service/buy.ts
export const buyApi = {
    /** ** buy */
    shoping: '/node_api/shop/buy'
};
Copy the code

The purpose of this division is to make the project structure and business structure clear, and to avoid the problem of a single file being too long.

HTTP request secondary encapsulation

I’m using the very popular AXIos for sending HTTP, and I’ve built on that, wrapped it a little bit more simply, and then exposed the Request object for invocation. Secondary packaging is mainly to solve the following problems

  1. Simplify parameters, assign default values to some common parameters, simplify external use, make it more general and conducive to troubleshooting problems.

  2. Unified processing of returned packets, we usually need to do the same processing for some high-frequency scenarios, such as error codes, not logged in, etc., can be unified processing in the return response interceptor provided by it.

  3. To prevent double commits, because sometimes the interface is slow to respond because of the network, because of the backend processing, so the user can click the button repeatedly in a very short period of time, and then make a new request again after the first request is not returned, we can do something in the front interceptor provided by Axios. About preventing repeated requests this east east, I have written in a previous article, the front end to prevent users from repeatedly submitting -js interested partners can see.

According to the above points, the following is the request file I encapsulated, the idea is relatively simple, not to say more

import axios from 'axios';
import qs from 'qs';

const Axios = axios.create({
    baseURL: '/',
    timeout: 10000,
    responseType: 'json',
    withCredentials: true,
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}});const CancelToken = axios.CancelToken;
const requestMap = new Map();

// Request a pre-interceptor
Axios.interceptors.request.use(
    config= > {

        // Prevent duplicate submissions
        const keyString = qs.stringify(Object.assign({}, { url: config.url, method: config.method }, config.data));
        if (requestMap.get(keyString)) {
            // Cancel the current request
            config.cancelToken = new CancelToken((cancel) = > {
                cancel('Please slow down a little');
            });
        }
        requestMap.set(keyString, true);
        Object.assign(config, { _keyString: keyString });

        if (config.method === 'post' || config.method === 'put' || config.method === 'delete') {
            / / the serialization
            config.data = qs.stringify(config.data);
        }

        return config;
    },
    error= > {
        return Promise.reject(error); });// Returns the response interceptor
Axios.interceptors.response.use(
    res= > {
        / / reset requestMap
        const config: any = res.config;
        requestMap.set(config._keyString, false);

        if (res.status === 200) {
            return res.data;
        }
        // todo popup prompt etc
        console.log(` request error:${res}`);
    },
    error= > {
        return {
            code: - 1}; });/** * @description * @param URL * @param data * @param method */
const request = (url: string, data = {}, method = 'post') = > {
    return Axios({
        method,
        url,
        data,
        params: method.toUpperCase() === 'GET' && data
    });

};

export { request };

Copy the code

Vuex status management

Here I divide the file structure according to business modules, as shown below

It is divided into home page module and user module, and each module has its own independent state mutations, etc., and files of each module are introduced in Store. ts, as follows

import Vue from 'vue';
import Vuex from 'vuex';
import index from './indexModule/index';
import user from './userModule/user';

Vue.use(Vuex);

export default new Vuex.Store({
    modules: {
        user,
        index
    }
});
Copy the code

Notice that there is a file called store_types.ts, which is mainly used with TS. The file is as follows

export enum UserType {
    /** * Module name */
    'MODULE_NAME' = 'user'./** ** increment */
    'ADD_COUNT' = 'addCount'./** * Computes attributes - gets ten times the value */
    'GET_TEM_COUNT' = 'getTenCount'
}
Copy the code

Take a look at the usage in components:

<script lang="ts"> import { UserType } from '@/store/store_types'; import { Component, Prop, Vue, Watch,Emit } from 'vue-property-decorator'; import { Action, Getter, Mutation, namespace, State } from 'vuex-class'; @Component export default class Test extends Vue { @State(state => state[UserType.MODULE_NAME].count) public fff! : number; @Getter(`${UserType.MODULE_NAME}/${UserType.GET_TEM_COUNT}`) public tenCount! : number; @Mutation(`${UserType.MODULE_NAME}/${UserType.ADD_COUNT}`) public addCount! : any; } </script>Copy the code

Although this is a bit convoluted, it has the advantage of being able to make the method and attribute descriptions clear through annotations

summary

The above is designed by me according to the common scenes in my work. I hope it can be helpful to my friends. Welcome to discuss the improper design in the comment area