Pick up the time code words…
[VuE-CLI3 upgrade] Old project 50% faster (part 1)
Last time I wrote about error handling of ESLint in the project, forgive me for not being much of an article writer, haha…
Continue to clarify that this article is only a summary of personal experience in the actual work…
In line with the principle of not affecting the business code and the original intention, continue the process of the upgrade and transformation project…
This paper is roughly divided into the following parts:
- Correlation of environment variables
- The mock integration
- npm script
- Vue.config. js: Webpack optimization, task execution, historical version processing, etc
- Deploying a remote server using a shell file: Handles historical versions of tasks and pushes them to the remote server
Correlation of environment variables
I have to say that without looking carefully at the documentation, this is a pit…
To view the document
In the VUE-cli3 project, the config directory used to store environment variables was deleted and replaced with:
.env Load in all environments
.env.local # is loaded in all environments, but ignored by Git
.env.[mode] Is loaded only in the specified mode
.env.[mode].local # is only loaded in the specified mode, but is ignored by Git
Copy the code
There are three environments dev beta prod in the original project, and file.env.dev.env.beta.env. prod is established successively, and environment variables are written in the form of key=value
Special note: Always remember to use
VUE_APP_
Name the variable at the beginning, otherwise it won’t be written toprocess.env
.build
Command of the time not affected, the building Lord this pit stepped on the very egg pain…
# .env.dev
VUE_APP_API_ENV=dev
VUE_APP_BASE_API=xxx
...
Copy the code
The variable VUE_APP_*, named starting with VUE_APP_, is joyfully accessible in the project using process.env.vue_app_ *.
# .env.beta
NODE_ENV=production
VUE_APP_API_ENV=beta
VUE_APP_BASE_API=xxx
...
Copy the code
# .env.prod
NODE_ENV=production
VUE_APP_API_ENV=pro
VUE_APP_BASE_API=xxx
...
Copy the code
The mock integration
API documentation is still a headache ah, business rapid development, serious document missing, document is still showdoc writing, not fun…
I was going to take the form of a native mock, but think about it, you need to write a bunch of files, and mock files get bigger and bigger as the version goes through iterations…
Finally consider the actual situation and use the form of easy-mock
Create a team project: Log in => My Project (Team Project) => Create team => Create Project
After creation, click to enter the project:
This is the end of the easy mock description. It is easy to use.
Copy the Base URL and write to the previous environment variable file.env.dev
VUE_APP_MOCK=false # Mock global switch
VUE_APP_MOCK_BASE_URL=https://www.easy-mock.com/mock/xxx # mock base url
Copy the code
VUE_APP_MOCK: As a global switch on whether to enable mock in project dev mode
VUE_APP_MOCK_BASE_URL: As the baseUrl of the requested URL in the project dev pattern
Next look at SRC/API to unify API requests in projects (modular, one-to-one counterpart to back-end microservices modules)
SRC/API /example.js
import { asyncAxios } from '@/plugin/axios'
export const exampleApi = {
baseUrl: 'example/',
list (params = {}) {
return asyncAxios(`The ${this.baseUrl}list`, params, {
isMock: true
})
},
detail (params = {}) {
return asyncAxios(`The ${this.baseUrl}detail`, params, {
isMock: true}}})Copy the code
The asyncAxios method is imported from @/plugin/axios.js. Here is the axios.js code:
import store from '@/store'
import axios from 'axios'
import { Toast } from 'vant'
import util from '@/libs/util'
// Create an error
const errorCreate = msg= > {
const err = new Error(msg)
errorLog(err)
throw err
}
// Record and display errors
const errorLog = err= > {
// Add to log
store.dispatch('xxx/log/add', {
type: 'error',
err,
info: 'Data request exception'
})
// Print to console
if (process.env.NODE_ENV === 'development') {
util.log.danger('>>>>>> Error >>>>>>')
console.log(err)
}
// Display a prompt
Toast({
message: err.message,
type: 'error'})}// Create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000 // Request timeout
})
// Request interceptor
service.interceptors.request.use(
config= > {
Do some processing before the request is sent
const token = util.cookies.get('token')
config.headers['X-Token'] = token
/ / handle the mock
if (process.env.VUE_APP_MOCK && config.isMock) {
config.url = `${process.env.VUE_APP_MOCK_BASE_URL}/${config.url}`
}
return config
},
error => {
// Failed to send
console.log(error)
Promise.reject(error)
}
)
// Response interceptor
service.interceptors.response.use(
response= > {
const dataAxios = response.data
const { code } = dataAxios
if(! code)return dataAxios
switch (code) {
case 0:
case 10000:
/ / success
return dataAxios.data
case 'xxx':
errorCreate(`[ code: xxx ] ${dataAxios.msg}: ${response.config.url}`)
break
default:
// Not the correct code
errorCreate(`${dataAxios.msg}: ${response.config.url}`)
break
}
},
error => {
if (error && error.response) {
switch (error.response.status) {
case 400: error.message = 'Request error'; break
case 401: error.message = 'Not authorized, please log in'; break
case 403: error.message = 'Access denied'; break
case 404: error.message = 'Error requesting address:${error.response.config.url}`; break
case 408: error.message = 'Request timed out'; break
case 500: error.message = 'Server internal error'; break
case 501: error.message = 'Service not implemented'; break
case 502: error.message = 'Gateway error'; break
case 503: error.message = 'Service unavailable'; break
case 504: error.message = 'Gateway timed out'; break
case 505: error.message = 'HTTP version not supported '; break
default: break
}
}
errorLog(error)
return Promise.reject(error)
}
)
export default service
Copy the code
The key code for mock is in the request interceptor:
if (process.env.VUE_APP_MOCK && config.isMock) {
config.url = `${process.env.VUE_APP_MOCK_BASE_URL}/${config.url}`
}
Copy the code
Determine the global Mock switch and the isMock field in the request configuration item to control whether the mock interface is enabled
npm script
Vue-cli-service For more information, see the documentation
Vue-cli-service serve [options] [Entry] Options: --open Open browser on server startup --copy Copy URL to clipped version on server startup --mode Specify environment mode (default: Development) --host specify host (default: 0.0.0.0) --port specify port (default: 8080) -- HTTPS use HTTPS (default: false)Copy the code
Vue cli - service build [options] [entry | pattern] options: - mode specified environment mode (default: production) - dest specifies the output directory (default: Dist) - modern geared to the needs of modern browsers with automatic back to build applications -- target app | lib | wc | wc - async (default: app) - library name or the name of the Web Components mode (default: "Name" field or entry filename in package.json) --no-clean Does not clean the target directory before building the project --report generates report-html to help analyze package contents --report-json generates report-.json To help analyze package contents -- Watch listens for file changesCopy the code
Script configuration in the previous project:
"scripts": {
"dev": "npm run serve",
"serve": "vue-cli-service serve --mode dev",
"build": "vue-cli-service build --no-clean --mode dev",
"build_app": "cross-env PAGE_ENV=app vue-cli-service build --no-clean --report --mode prod",
"build_beta": "vue-cli-service build --no-clean --report --mode beta",
"build_pro": "vue-cli-service build --no-clean --report --mode prod",
"lint": "vue-cli-service lint --fix"
}
Copy the code
–mode (specify the environment mode), –no-clean (do not clear dist files, will be pushed to the remote server in one click later instructions), –report (generate report. HTML analysis package content), command integration remains the same as the old project…
As if there is nothing left to say in this part, the principle is to maintain the same as the old project command ~~
vue.config.js
To view the document
Go straight to the full code, code words are tired
const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const assetsDir = 'static'
const resolve = dir= > path.join(__dirname, dir)
// PosiX-compatible processing path
const posixJoin = _path= > path.posix.join(assetsDir, _path)
const lastVersion = new Date().getTime()
const isProd = process.env.NODE_ENV === 'production'
/ / the CDN switch
const OPENCDN = true
const webpackHtmlOptions = {
// DNS preload, optimize interface request
dnsPrefetch: [
'https://aaa.exmaple.com'.'https://bbb.exmaple.com'.'https://ccc.exmaple.com'.'https://ddd.exmaple.com'.'https://eee.exmaple.com'.'https://fff.exmaple.com'].externals: {
'vue': 'Vue'.'vue-router': 'VueRouter'.'vuex': 'Vuex'.'js-cookie': 'Cookies'
},
cdn: {
// Production environment
build: {
css: [
'https://cdn.jsdelivr.net/npm/[email protected]/lib/index.css'].js: [
'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js'.'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js'.'https://unpkg.com/[email protected]/dist/vuex.min.js'.'https://cdn.jsdelivr.net/npm/[email protected]/lib/vant.min.js'.'https://cdn.jsdelivr.net/npm/[email protected]/src/js.cookie.min.js']}}}module.exports = {
publicPath: '/'.outputDir: 'dist',
assetsDir,
productionSourceMap: false.// Close the build environment sourceMap
devServer: {
open: false.host: '0.0.0.0'.port: 3900
},
css: {
// Add the version numberextract: ! isProd ?false : {
filename: posixJoin(`css/${lastVersion}-[name].[contenthash:8].css`),
chunkFilename: posixJoin(`css/${lastVersion}-[name].[contenthash:8].css`)}},configureWebpack: config= > {
config.resolve.extensions = ['.js'.'.vue'.'.json']
if (isProd) {
// The build environment executes the task and writes the version number
const task = require('./task')
task.run(lastVersion)
config.plugins.push(
/ / enable gzip
new CompressionWebpackPlugin({
test: new RegExp('\ \. (' + ['js'.'css'].join('|') + '$'),
threshold: 10240.minRatio: 0.8}))// Enable CDN status: externals does not enter webpack
if (OPENCDN) {
config.externals = webpackHtmlOptions.externals
}
}
},
chainWebpack: config= > {
/** * Remove lazy module prefetch preload, reduce bandwidth pressure */
config.plugins
.delete('prefetch')
.delete('preload')
config.resolve.alias
.set('vue$'.'vue/dist/vue.esm.js')
.set(The '@', resolve('src'))
// Clear the warning
config.performance
.set('hints'.false)
// Write the version number to the environment variable
config
.plugin('define')
.tap(args= > {
args[0] ['app_build_version'] = lastVersion
return args
})
config
.when(isProd, config =>
// Add version number to production js
config.output
.set('filename', posixJoin(`js/${lastVersion}-[name].[chunkhash].js`))
.set('chunkFilename', posixJoin(`js/${lastVersion}-[id].[chunkhash].js`)))/** * Add CDN parameters to htmlWebpackPlugin configuration, modify public/index.html */
config.plugin('html').tap(args= > {
// Write the CDN to webpackHtmlOptions in the production environment and apply it to public/index.html
if (isProd && OPENCDN) {
args[0].cdn = webpackHtmlOptions.cdn.build
}
// DNS is preloaded
args[0].dnsPrefetch = webpackHtmlOptions.dnsPrefetch
return args
})
}
}
Copy the code
Here will involve a lot of company business related, make do with it, specially added a note to illustrate… Interested comments discussion
WebpackHtmlOptions application in public/index. The HTML reflect (htmlWebpackPlugin. Read the options) :
<html>
<head>
<title>xxx</title>
<meta charset="utf-8">
<meta content="Width = device - width, initial - scale = 1.0, the minimum - scale = 1.0, the maximum - scale = 1.0, user - scalable = no" name="viewport">
<! -- dns-prefetch, configure in vue.config.js -->
<% for (var i in htmlWebpackPlugin.options.dnsPrefetch) { %>
<link rel="dns-prefetch" href="<%= htmlWebpackPlugin.options.dnsPrefetch[i] %>">
<%} % >
<meta name="msapplication-tap-highlight" content="no">
<meta content="telephone=no" name="format-detection" />
<meta content="email=no" name="format-detection" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="xxx">
<link rel="icon" href="<%= BASE_URL %>static/applogo.png" type="image/x-icon">
<! -- CDN CSS, in vue.config.js configuration -->
<% 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">
<%} % >
<! -- Use CDN accelerated JS file, configure in vue.config.js -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
<%} % >
</head>
<body>
<div id="app"></div>
<! -- < script charset = "utf-8" type = "text/javascript" SRC = "/ / g.alicdn.com/de/prismplayer/2.7.1/aliplayer-min.js" > < / script > -->
<! -- CDN js, config in vue.config.js -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<%} % >
</body>
</html>
Copy the code
Task Shell file package remote push
Task.js: Write the history.js version control file in the dist directory using nodejs
Run. Sh: Pull remote code => local package => delete historical files outside version control => Push remote
Why do version control? For users without perception, in order to release at any time, in order not to work overtime (released at any time also add what shift? Very honest, ha ha)…
During the release process, there was no error when users walked around our product.
Build –no-clean mode does not clear the dist folder, history.js stores 5 versions, and build.sh controls 5 versions of the remote repository
Go to the code:
// task.js
let fs = require('fs')
let path = require('path')
let endOfLine = require('os').EOL
module.exports = {
maxHistoryNum: 5.historyFile: path.resolve(__dirname, './dist/history.js'),
staticDir: path.resolve(__dirname, './dist/'),
creataHistoryIfNotExist () {
if(! fs.existsSync(this.historyFile)) {
this.storeHistory([], 'a+')}},// @done writes data to history.js
storeHistory (list, mode) {
let historyFile = this.historyFile
let outJson = 'module.exports = [' + endOfLine
let listLen = list.length
if (list && listLen > 0) {
list.forEach((item, index) = > {
if (index === listLen - 1) {
outJson += ` ${item}${endOfLine}`
} else {
outJson += ` ${item}.${endOfLine}`
}
})
}
outJson += '] ' + endOfLine
fs.writeFileSync(historyFile, outJson, {
flag: mode
})
},
// Recursively delete files in a directory
rmFiles (dirPath, regexp) {
let files
try {
files = fs.readdirSync(dirPath)
} catch (e) {
return
}
if (regexp && files && files.length > 0) {
for (let i = 0; i < files.length; i++) {
let filename = files[i]
let filePath = dirPath + '/' + files[i]
if (fs.statSync(filePath).isFile() && regexp.test(filename)) {
console.log('Delete expired historical versions ->(' + regexp + ') : + filename)
fs.unlinkSync(filePath)
} else {
this.rmFiles(filePath, regexp)
}
}
}
},
// @done
cleanOldVersionFilesIfNeed (version) {
let staticDir = this.staticDir
let maxHistoryNum = this.maxHistoryNum
let history = []
try {
history = require(this.historyFile)
} catch (e) {
console.log(e)
}
// Add the latest version, delete the old version
history.push(version)
// If the number of historical versions exceeds the limit, delete the old version
let len = history.length
if (len > maxHistoryNum) {
let oldVersions = history.slice(0, len - maxHistoryNum)
for (let i = 0; i < oldVersions.length; i++) {
let ver = oldVersions[i]
let reg = new RegExp(ver)
this.rmFiles(staticDir, reg)
}
// Update the history file
let newVersions = history.slice(len - maxHistoryNum)
this.storeHistory(newVersions)
} else {
// Write the history file
this.storeHistory(history)
}
},
/ / the entry
run (version) {
this.creataHistoryIfNotExist()
this.cleanOldVersionFilesIfNeed(version)
}
}
Copy the code
# run.sh
# desc: This script is used to build online code with one click and automatically commit it to a remote Git repository
initContext() {# Target file directory directory
source_dir=dist
Parameters for packaging the embedded version of the app
if [ $# -gt&& [0]The $1 = 'beta' ];then
Production code remote warehouse address
git_url=xx.git
# production code local root directory
dest=".deploy/beta"
# NPM script rank
node_script=build_beta
else
Production code remote warehouse address
git_url=xx.git
# production code local root directory
dest=".deploy/pro"
# NPM script rank
node_script=build_pro
fi
}
Initialize git directory and pull the latest code
init() {echo +++init start;
if[!-d $dest ]; then
git clone $git_url $dest
fi
# record the current directory location, the last to return
cur=`pwd`
Go to the git directory
cd $dest
# git checkout .
git add .
git stash
# reset is the latest version on the line, pull it before resetting it.
git pull origin master
git reset --hard origin/master
# Then pull again
git pull origin master
# return to the original directory
cd $cur
echo ---init end;
}
Reset the dist directory
resetDist() {echo +++resetDist start
rsync -a --delete --exclude='.git' $dest/. ./dist
echo ---resetDist end
}
# building
build() {echo +++build start
npm run $node_script
echo ---build end
}
Check whether it is successful
checkBuild() {if[[!-f $source_dir/index.html || ! -d $source_dir/static ]]; then
echo error
else
echo ok
fi
}
Copy the code to the $dest directory
cpCode() {echo +++cpCode start
# Copy code, all files contain hidden files
rsync -r --delete --exclude='.git' $source_dir/. $dest
echo ---cpCode end
}
Commit to a remote Git repository
commit() {echo +++commit start
# record the current directory location, the last to return
cur=`pwd`
Go to the git directory
cd $dest
# string submitted
commit_str="commited in `date '+%Y-%m-%d_%H:%M:%S'`"
git add .
git commit -am "${commit_str}"
git push origin master
# return to the original directory
cd $cur
echo ---commit end
}
# Display help information
help() {echo ./run.sh build "#"Building codeecho ./run.sh init "#"Initialize the Git repositoryecho ./run.sh commit "#"Submitted to the gitecho ./run.sh "#"Perform all missionsecho ./run.sh hello "#"hello
echo ./run.sh test "#"test
echo ./run.sh beta "#"One-click build and submit beta# App embedded version
echo---- App embedded version --------echo ./run.sh app "#"Build and submit app versions with one clickecho---- Help information --------echo ./run.sh help "#"Help}# For testing
test() {echo "a test empty task"
}
# entry
if [[ $# -lt1 | |The $1 = 'app' || The $1 = 'beta' || The $1 = 'beta1' || The $1 = 'beta2']].then
If there is no parameter, select pro package, otherwise select corresponding package
if [ $# -lt1];then
type=pro
else
type=The $1
fi
echo===\> Ready to build${type}Version initContext$type && init && resetDist
# Build code
buildRes=$(build)
Check the build results
echo -e "$buildRes"
if [[ $buildRes= ~"ERROR"]].then
echo "$(tput setaf 1)xxx\>build error,task abort$(tput sgr0)"
else
Continue until the code builds successfully.
checkRes=$(checkBuild)
if [ $checkRes= ="ok" ];then
cpCode && commit
echo "$(tput setaf 2)===\>task complete$(tput sgr0)"
else
echo "$(tput setaf 1)xxx\>build error,task abort$(tput sgr0)"
fi
fi
elif [ The $1 ]; then
The parameter is not of package type and is handled by the function
echo===\> Ready to executeThe ${1}InitContext beta func=The $1
$func
echo ===\>task complete
fi
Copy the code
Write version number of history.js:
Package target file DIST:
You can see many version files
This is the end of today’s code word
End of code word +1
(‘ end of today’s code word ‘).repeat(‘999’)
It’s 4:30. I have to catch the high-speed train to attend my cousin’s wedding
At the end
Almost, can end, next article to write about webpack4 things, after all, packaging optimization depends on the stuff ~, enough hard