Build a fully functional mobile shelf based on vue-cli3.0

Build a fully functional mobile shelf based on vue-cli3.0

Vue-cli4 has been upgraded, click me to view

Based on vue-cli3.0 to build a fully functional mobile terminal shelf, the main functions include

  1. webpack package extension
  2. css: sass support, normalize.css, _mixin.scss, _variables.scss
  3. vw, rem layout
  4. Cross-domain settings
  5. eslint settings
  6. cdn introduced
  7. Routing design, login interception
  8. axios, api design
  9. Vuex state management

Project address: vue-cli3-H5

Demo address: zhouyupeng.github.io/vuecli3H5/#...

webpack package extension

After vue-cli3.*, the directory structure has been greatly changed, and the previous build and config folders have been removed. To achieve configuration changes, add vue.config.js to the root directory for configuration

css: sass support, normalize.css, _mixin.scss, _variables.scss

The css preprocessor used is sass. For css mixin, variables are introduced globally, and normalize.css is introduced to make the style of HTML elements appear to be highly consistent across browsers. vue.config.js configuration

css: {
       // css  ExtractTextPlugin
        extract:isProduction ? true:false,
       //  CSS source maps?
        sourceMap: false,
       //css 
       //  CSS modules for all css/pre-processor files.
        modules: false,
            sass: {
                data: '@import "style/_mixin.scss";@import "style/_variables.scss";'// 
            }
        }
    }
 

vw, rem layout

For the mobile terminal adaptation scheme, the NetEase News method is used, using the vw + rem layout

/**
750px 
     1rem=100px html width: 7.5rem html font-size=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: {
       // rem 
        plugins: [
            require('postcss-px2rem')({
                remUnit: 100
            })
        ]
    }
}
 

Cross-domain settings during development

devServer: {
        open: true,// 
        host: '127.0.0.1',
        port: 8088,// 
        https: false,
        hotOnly: false,
        proxy: 'https://easy-mock.com/'// 
    }
 

After configuration, the baseUrl of axios of the local development environment should be written as'', which is an empty string. When publishing online, if the front-end code is not placed under the same source as the back- end api , the back-end needs to do cross-domain processing.

eslint standard setting

The JavaScript standard code specification is used. A good coding style can help reduce friction between teams, and the code will be more refreshing to read and more readable. Don't feel annoying, just say it if you use it. This is the full text of the JavaScript standard code specification

Custom configuration, modify in .eslintrc.js, here is the configuration I gave, 4 spaces indented, do not check the ending semicolon, close the single var statement, you can configure it 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,// ,
   // 
    quotes: ['error', 'single'],
   // 
    'space-before-function-paren': 0,
   // var  
    'one-var': 0
    }
 

cdn introduced

For vue, vue-router, vuex, axios and other libraries that are not frequently changed, we let webpack not package them. By introducing cdn, the code size can be reduced, and the bandwidth of the server can also be reduced. The 360 cdn is used here. , accompanied by a public review article cdn point I

vue.config.js configuration

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

}

const cdn = {
   // 
    dev: {
        css: [
            'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
        ],
        js: []
    },
   // 
    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) {
           //externals 
            Object.assign(config, {
                externals: externals
            })
       
        } else {
           // ...
        }
    },
chainWebpack: config => {
   // vue-cli  webpack  
   // CDN htmlWebpackPlugin   public/index.html  
    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  -->
	<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"/>
	<!--  CDN CSS vue.config.js  -->
	<% 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>
	<!--  CDN JS vue.config.js  -->
	<% 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,// 
                keepAlive: true// 
            }
        },
        {
            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: '/',
            meta: {
               //auth: true,
               //keepAlive: true
            }
        }
    ]
})

//   
router.beforeEach((to, from, next) => {
    let auth = to.meta.auth
    let token = store.getters['login/token'];

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

 

Set whether you need to log in and whether to cache the current component in the meta. The login permission is judged in the router.beforeEac routing hook function. If there is no login, jump to the login page, and pass the current page to the past, and then jump back to this page after logging in.

The page cache is processed in app.vue

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

axios, api design

The design of axios is mainly request interceptor, respone interceptor, and secondary encapsulation of get and post

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

axios.defaults.headers.post['Content-Type'] =
    'application/x-www-form-urlencoded;charset=UTF-8'//post 
//axios  
axios.interceptors.request.use(
    config => {
       // token
        let token = store.getters['login/token'];
        token && (config.headers.token = token)
        Indicator.open(' ')
        return config
    },
    error => {
        return Promise.error(error)
    }
)
//axios respone 
axios.interceptors.response.use(
    response => {
       // 200 
       //   respone 
        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 
            case 401:
                break
           //404 
            case 404:
                Toast({
                    message: ' ',
                    position: 'middle',
                    duration: 2000
                });
                break
            default:
                Toast({
                    message: error.response.data.message,
                    position: 'middle',
                    duration: 2000
                });
        }
        return Promise.reject(error.response)
    }
)
/**
 *  get get 
 * @param {String} url [ url ]
 * @param {Object} params [ ]
 */
function get (url, params = {}) {
    return new Promise((resolve, reject) => {
        axios
            .get(url, {
                params: params
            })
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
   // return axios.get();
}
/**
 * post post 
 * @param {String} url [ url ]
 * @param {Object} params [ ]
 */
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)
            })
    })
   //  return axios.post();
}


 

In order to facilitate the management of the api path, 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 the uglifyjs-webpack-plugin plugin

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

Set alias directory alias

Files in various places are often quoted in the project, and it can be more convenient to import 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 undergo local development, test scripts, development self-test, test environment, and pre-launch environment before it can be officially released. Corresponding to each environment may be different, such as server address, interface address, websorket address... etc. When switching between environments, different configuration parameters are needed, so environment variables and modes can be used to facilitate our management.

.env                #  
.env.local          #   git  
.env.[mode]         #  
.env.[mode].local   #   git  
 

At the beginning of the custom variable VUE_APP_, 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 is running.
  2. BASE_URL-will match the baseUrl option in vue.config.js, which is the base path to which your application will be deployed.

As we defined .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/'
 

In the project, you can use process.env.VUE_APP_*, such as process.env.VUE_APP_BASE_API to get the defined value

Import filter globally

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

// 
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'
// 
Object.keys(filters).forEach(item => {
    Vue.filter(item, filters[item])
})
 

use

{{1555851774 | formatTimer()}}
 

Use mock.js in vue

To view the articles I wrote before, click on me

Wepback's visual resource analysis tool plug-in---webpack-bundle-analyzer

Used to analyze which modules introduce which codes, and optimize the code 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: zhouyupeng.github.io/vuecli3H5/#...

Children's shoes without server learning can look at Tencent Cloud's cloud server with 1 core 2G, 1M bandwidth, 50GB storage space, 120 a year.