Build a fully functional mobile terminal rack based on vue-cli3.0. The main functions include
- webpack packaging extension
- CSS: sass support, normalize.css_ mixin.scss,_ variables.scss
- vw, rem layout
- Cross domain settings
- eslint settings
- cdn introduction
- Routing design, login interception
- axios, api design
- 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:
- NODE_ENV - will be one of "development", "production" or "test". The specific value depends on the mode in which the application runs.
- 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