Building a fully functional mobile terminal rack based on vue-cli 3.0

Build a fully functional mobile terminal rack based on vue-cli3.0. The main functions include

  1. webpack packaging extension
  2. CSS: sass support, normalize.css_ mixin.scss,_ variables.scss
  3. vw, rem layout
  4. Cross domain settings
  5. eslint settings
  6. cdn introduction
  7. Routing design, login interception
  8. axios, api design
  9. vuex status management

Project address: vue-cli3-H5

demo address: https://zhouyupeng.github.io/vuecli3H5/#/

webpack packaging extension

After vue-cli3. *, the directory structure has been greatly changed. The previous build and config folders are removed. To realize the configuration change, vue.config.js is added in the root directory for configuration

CSS: sass support, normalize.css_ mixin.scss,_ variables.scss

The css preprocessor used is sass. For css mixin, the variable is introduced globally and normalize.css Make HTML element styles highly consistent across browsers
vue.config.js configuration

css: {
        // Whether to use the css separation plug-in ExtractTextPlugin
        extract:isProduction ? true:false,
        // Open CSS source maps?
        sourceMap: false,
        // css preset configuration item
        // Enable CSS modules for all CSS / pre processor files
        modules: false,
            sass: {
                data: '@import "style/_mixin.scss";@import "style/_variables.scss";' // Global import
            }
        }
    }

vw, rem layout

For the mobile terminal adaptation scheme, the NetEase News The method of,
Use vw + rem layout

/**
750px Design draft
    Taking 1rem=100px as the reference, the width of html element can be set as width: 7.5rem, so the font size of html = devicewidth / 7.5
**/
html {
    font-size: 13.33333vw
}

@media screen and (max-width: 320px) {
    html {
        font-size: 42.667PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 321px) and (max-width:360px) {
    html {
        font-size: 48PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 361px) and (max-width:375px) {
    html {
        font-size: 50PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 376px) and (max-width:393px) {
    html {
        font-size: 52.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 394px) and (max-width:412px) {
    html {
        font-size: 54.93PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 413px) and (max-width:414px) {
    html {
        font-size: 55.2PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 415px) and (max-width:480px) {
    html {
        font-size: 64PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 481px) and (max-width:540px) {
    html {
        font-size: 72PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 541px) and (max-width:640px) {
    html {
        font-size: 85.33PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 641px) and (max-width:720px) {
    html {
        font-size: 96PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 721px) and (max-width:768px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;

        #app {
            margin: 0 auto
        }
    }


}

vue.config.js configuration

loaderOptions: {
    postcss: {
        // This is the configuration of rem adaptation
        plugins: [
            require('postcss-px2rem')({
                remUnit: 100
            })
        ]
    }
}

Development time cross domain settings

devServer: {
        open: true, // Do you want to open the browser after starting the service
        host: '127.0.0.1',
        port: 8088, // Service port
        https: false,
        hotOnly: false,
        proxy: 'https://easy-mock.com / '/ / set proxy
    }

After configuration, the baseUrl of axios in the local development environment should be written as' ', that is, an empty string.
When publishing online, if the front-end code is not placed with the background api Homology Cross domain processing is also required in the background,

eslint standard settings

Using JavaScript standard Code specification, a good coding style, can help reduce friction between teams. The code is more refreshing and readable to read. Don't feel bored. It's good to use it.
This is the full text of the JavaScript standard code specification

The user-defined configuration is modified in. eslintrc.js. Here is the configuration given by me. Four spaces are indented. The ending semicolon is not checked. The single var declaration is closed and can be configured by yourself

rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    indent: [
        'error',
        4,
        {
            SwitchCase: 1
        }
    ],
    semi: 0, // Do not check the ending semicolon,
    // Force single quotes
    quotes: ['error', 'single'],
    // Close the rule that there must be a space between the function name and the following parentheses
    'space-before-function-paren': 0,
    // Close the var declaration, and each declaration occupies one line of rules.
    'one-var': 0
    }

cdn introduction

For libraries that do not change frequently, such as vue, vue router, vuex, axios, etc., we let webpack not package them. Through the introduction of cdn, we can reduce the size of the code and the bandwidth of the server
360 cdn is used here, and a public cdn evaluation article is attached Point me

vue.config.js configuration

const externals = {
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    'mint-ui': 'MINT',
    axios: 'axios'

}

const cdn = {
    // development environment 
    dev: {
        css: [
            'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
        ],
        js: []
    },
    // production environment 
    build: {
        css: [
            'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
        ],
        js: [
            'https://lib.baomitu.com/vue/2.6.6/vue.min.js',
            'https://lib.baomitu.com/vue-router/3.0.1/vue-router.min.js',
            'https://lib.baomitu.com/vuex/3.0.1/vuex.min.js',
            'https://lib.baomitu.com/axios/0.18.0/axios.min.js',
            'https://lib.baomitu.com/mint-ui/2.2.13/index.js'
        ]
    }
}

configureWebpack: config => {
        if (isProduction) {
            // Modules in externals are not packaged
            Object.assign(config, {
                externals: externals
            })
       
        } else {
            // Modify configuration for development environment
        }
    },
chainWebpack: config => {
    // Make more fine-grained changes to the webpack configuration inside Vue cli.
    // Add the CDN parameter to the htmlWebpackPlugin configuration. See public/index.html for details
    config.plugin('html').tap(args => {
        if (process.env.NODE_ENV === 'production') {
            args[0].cdn = cdn.build
        }
        if (process.env.NODE_ENV === 'development') {
            args[0].cdn = cdn.dev
        }
        return args
    })
}
<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<!-- DNS Pre analysis -->
	<link rel="dns-prefetch" href="//lib.baomitu.com" />
	<meta name="viewport"
		content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=0,minimal-ui,viewport-fit=cover" />
	<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
	<!-- use CDN Accelerated CSS File, configuration in vue.config.js lower -->
	<% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
	<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
	<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
	<% } %>

	<title>vuedemo</title>
</head>

<body>
	<noscript>
		<strong>We're sorry but vuedemo doesn't work properly without JavaScript
			enabled. Please enable it to continue.</strong>
	</noscript>
	<div id="app"></div>
	<!-- use CDN Accelerated JS File, configuration in vue.config.js lower -->
	<% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
	<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
	<% } %>

	<!-- built files will be auto injected -->
</body>

</html>

Routing design, login interception

const router = new Router({
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home,
            meta: {
                auth: false, // Do you need to log in
                keepAlive: true // Cache components
            }
        },
        {
            path: '/about',
            name: 'about',
            component: () =>
                import(/* webpackChunkName: "about" */ './views/About.vue'),
            meta: {
                auth: true,
                keepAlive: true
            }
        },
        {
            path: '/login',
            name: 'login',
            component: () =>
                import(/* webpackChunkName: "login" */ './views/login.vue'),
            meta: {
                auth: false,
                keepAlive: true
            }
        },
        {
            path: '*', // Redirect on unmatched route
            redirect: '/',
            meta: {
                // auth: true,
                // keepAlive: true
            }
        }
    ]
})

// The global routing hook function is valid for the global
router.beforeEach((to, from, next) => {
    let auth = to.meta.auth
    let token = store.getters['login/token'];

    if (auth) { // Login required
        if (token) {
            next()
        } else {
            next({
                path: '/login',
                query: {
                    redirect: to.fullPath
                }
            })
        }
    } else {
        next()
    }
})

Set whether to log in and cache the current component in the meta,
Judge the login permission in the router.beforeEac routing hook function. If you do not log in, jump to the login page, pass the current page, and jump back to this page after logging in.

The cached pages are processed in app.vue

<keep-alive v-if="$route.meta.keepAlive" >
    <router-view class="router"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" class="router"></router-view>

axios, api design

The design of axios mainly includes request interceptor, response interceptor, and the secondary encapsulation of get and post

axios.defaults.timeout = 12000 // Request timeout
axios.defaults.baseURL = process.env.VUE_APP_BASE_API

axios.defaults.headers.post['Content-Type'] =
    'application/x-www-form-urlencoded;charset=UTF-8' // Setting of post request header
// axios request interceptor
axios.interceptors.request.use(
    config => {
        // You can set the token to be sent here
        let token = store.getters['login/token'];
        token && (config.headers.token = token)
        Indicator.open('Data loading')
        return config
    },
    error => {
        return Promise.error(error)
    }
)
// Axios response interceptor
axios.interceptors.response.use(
    response => {
        // If the returned status code is 200, the interface request is successful and the data can be obtained normally
        // Otherwise, an error will be thrown, and the response interceptor will be written according to its own business and the interface state returned by the background
        Indicator.close()
        if (response.status === 200 && response.data.code === 0) {
            return Promise.resolve(response)
        } else {
            Toast({
                message: response.data.msg,
                position: 'middle',
                duration: 2000
            });
            return Promise.reject(response)
        }
    },
    error => {
        Indicator.close()
        const responseCode = error.response.status
        switch (responseCode) {
            // 401: not logged in
            case 401:
                break
            // 404 request does not exist
            case 404:
                Toast({
                    message: 'The network request does not exist',
                    position: 'middle',
                    duration: 2000
                });
                break
            default:
                Toast({
                    message: error.response.data.message,
                    position: 'middle',
                    duration: 2000
                });
        }
        return Promise.reject(error.response)
    }
)
/**
 * Encapsulate the get method, corresponding to the get request
 * @param {String} url [Requested url address]
 * @param {Object} params [Parameters carried during request]
 */
function get (url, params = {}) {
    return new Promise((resolve, reject) => {
        axios
            .get(url, {
                params: params
            })
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
    // Or return axios.get();
}
/**
 * post Method, corresponding to the post request
 * @param {String} url [Requested url address]
 * @param {Object} params [Parameters carried during request]
 */
function post (url, params) {
    return new Promise((resolve, reject) => {
        axios
            .post(url, qs.stringify(params))
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
    //  Or return axios.post();
}


In order to facilitate the management of api paths, all requests are placed in the api folder, such as

import { get, post } from '@/axios/http.js'
function getIndex (params) {
    return get('/mock/5cb48c7ed491cd741c54456f/base/index', params)
}
function login(params) {
    return post('/mock/5cb48c7ed491cd741c54456f/base/login', params)
}
export {
    getIndex,
    login
}

other

Remove console.log

Install uglifyjs webpack plugin

 // Online compression removes console and other information
config.plugins.push(
    new UglifyJsPlugin({
        uglifyOptions: {
            compress: {
                warnings: false,
                drop_console: true,
                drop_debugger: false,
                pure_funcs: ['console.log'] // Remove console
            }
        },
        sourceMap: false,
        parallel: true
    })
)

Set alias directory alias

Files in various places are often referenced in the project, which can be introduced more conveniently after configuration

config.resolve.alias
            .set('assets', '@/assets')
            .set('components', '@/components')
            .set('view', '@/view')
            .set('style', '@/style')
            .set('api', '@/api')
            .set('store', '@/store')

Environment variables and patterns

In the front-end development process of a product, generally speaking, it will go through local development, test script, development self-test, test environment and pre online environment before it can be officially released. Each environment may be different, such as server address, interface address, websocket address, etc. When switching between environments, different configuration parameters are required, so we can use environment variables and modes to facilitate our management.

.env                # Loaded in all environments
.env.local          # Loaded in all environments, but ignored by git
.env.[mode]         # Is loaded only in the specified mode
.env.[mode].local   # It is only loaded in the specified mode, but it will be ignored by git

Custom variable VUE_APP_ At the beginning, there are two special variables:

  1. NODE_ENV - will be one of "development", "production" or "test". The specific value depends on the mode in which the application runs.
  2. BASE_URL - will match the baseUrl option in vue.config.js, that is, the basic path to which your application will be deployed.

As we define. env

NODE_ENV = 'development'
BASE_URL = '/'
VUE_APP_BASE_API = ''

.env.production

NODE_ENV = 'production'
BASE_URL = './'
VUE_APP_BASE_API = 'https://easy-mock.com/'

You can use process. Env. Vue in a project_ APP_*, Such as process.env.VUE_APP_BASE_API gets the defined value

Global import filter

Write the filters used in multiple places in one js and reuse the code.

// Filter the date format, pass in the timestamp, and return different formats according to the parameters
const formatTimer = function(val, hours) {
    if (val) {
        var dateTimer = new Date(val * 1000)
        var y = dateTimer.getFullYear()
        var M = dateTimer.getMonth() + 1
        var d = dateTimer.getDate()
        var h = dateTimer.getHours()
        var m = dateTimer.getMinutes()
        M = M >= 10 ? M : '0' + M
        d = d >= 10 ? d : '0' + d
        h = h >= 10 ? h : '0' + h
        m = m >= 10 ? m : '0' + m
        if (hours) {
            return y + '-' + M + '-' + d + ' ' + h + ':' + m
        } else {
            return y + '-' + M + '-' + d
        }
    }
}
export default {
    formatTimer
}

main.js introduction

import filters from './filters/index'
// Injection global filter
Object.keys(filters).forEach(item => {
    Vue.filter(item, filters[item])
})

use

{{1555851774 | formatTimer()}}

Use mock.js in vue

Check out my previous articles Click me

Webpack bundle analyzer, a visual resource analysis tool plug-in for webpack

It is used to analyze which modules introduce which codes and to optimize the codes purposefully

In the packaging environment, use the command npm run build --report

if (process.env.npm_config_report) {
    config.plugins.push(new BundleAnalyzerPlugin())
}

Code address

Project address: vue-cli3-H5

demo address: https://zhouyupeng.github.io/vuecli3H5/#/

Welcome to pay attention

Welcome to pay attention to the small program "advanced front-end", and more than 1000 front-end interview questions can be viewed online

Tags: Javascript Front-end Vue.js

Posted on Sun, 31 Oct 2021 23:33:03 -0400 by beselabios