1、 cdn、全局cdn(所有的js、css都使用cdn)
2、Gzip压缩(压缩js、css)
3、去掉注释、去掉console.log
4、压缩图片
5、本地代理
6、环境变量和模式
基本配置
const path = require('path')
function resolve (dir) {
return path.join(__dirname, './', dir)
}
module.exports = {
publicPath: '/', // 默认'/',部署应用包时的基本 URL
outputDir: 'dist',
assetsDir: '', // 相对于outputDir的静态资源(js、css、img、fonts)目录
runtimeCompiler: true, // 是否使用包含运行时编译器的 Vue 构建版本
productionSourceMap: false, // 生产环境的 source map
configureWebpack: config => {},
chainWebpack: config => {}
}
vue.config
// cdn预加载使用
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'element-ui': 'ELEMENT',
'js-cookie': 'Cookies',
'nprogress': 'NProgress'
}
const cdn = {
// 开发环境
dev: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
],
js: []
},
// 生产环境
build: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.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://cdn.jsdelivr.net/npm/[email protected]/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js',
'https://unpkg.com/element-ui/lib/index.js',
'https://cdn.bootcss.com/js-cookie/2.2.0/js.cookie.min.js',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js'
]
}
}
configureWebpack: config => {
const myConfig = {}
//本地环境 线上环境
if(process.env.NODE_ENV === 'production'){
myConfig.externals = externals
}
if(process.env.NODE_ENV === 'development'){
myConfig.devServer = {
disableHostCheck: true
}
}
return myConfig
}
chainWebpack: config => {
// 使用cdn
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
})
}
public/index.html
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<% } %>
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<% } %>
vue-cli3
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<% } %>
npm run build
一下, 使用本地预览,serve -s dist
打开控制台,如下图
cdn
全局cdn
使用七牛云融合 CDN
1、安装Node.js SDK
$ npm install qiniu
2、获取密钥(Access/Secret Key)
Access/Secret Key
3、新建upcdn.js
const qiniu = require('qiniu')
const glob = require('glob')
const mime = require('mime')
const path = require('path')
const isWindow = /^win/.test(process.platform)
let pre = path.resolve(__dirname, './dist/') + (isWindow ? '\\' : '')
const files = glob.sync(
`${path.join(
__dirname,
'./dist/**/*.?(js|css|map|png|jpg|svg|woff|woff2|ttf|eot)'
)}`
)
pre = pre.replace(/\\/g, '/')
const options = {
scope: 'test2' // 空间对象名称
}
var config = {
qiniu: {
accessKey: '你的AccessKey', // 个人中心 秘钥管理里的 AccessKey
secretKey: '你的SecretKey', // 个人中心 秘钥管理里的 SecretKey
bucket: options.scope,
domain: 'http://ppqqg4jtj.bkt.clouddn.com' //你的域名
}
}
var accessKey = config.qiniu.accessKey
var secretKey = config.qiniu.secretKey
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey)
var putPolicy = new qiniu.rs.PutPolicy(options)
var uploadToken = putPolicy.uploadToken(mac)
var cf = new qiniu.conf.Config({
zone: qiniu.zone.Zone_z2
})
var formUploader = new qiniu.form_up.FormUploader(cf)
async function uploadFileCDN (files) {
files.map(async file => {
const key = getFileKey(pre, file)
try {
await uploadFIle(key, file)
console.log(`上传成功 key: ${key}`)
} catch (err) {
console.log('error', err)
}
})
}
async function uploadFIle (key, localFile) {
const extname = path.extname(localFile)
const mimeName = mime.getType(extname)
const putExtra = new qiniu.form_up.PutExtra({ mimeType: mimeName })
return new Promise((resolve, reject) => {
formUploader.putFile(uploadToken, key, localFile, putExtra, function ( respErr, respBody, respInfo) {
if (respErr) {
reject(respErr)
}
resolve({ respBody, respInfo })
})
})
}
function getFileKey (pre, file) {
console.log(pre, file);
if (file.indexOf(pre) > -1) {
const key = file.split(pre)[1]
return key.startsWith('/') ? key.substring(1) : key
}
return file
}
(async () => {
console.time('上传文件到cdn')
await uploadFileCDN(files)
console.timeEnd('上传文件到cdn')
})()
4、vue.config.js
const cdnDomian = 'http://ppqqg4jtj.bkt.clouddn.com' //静态文件cdn
module.exports = {
publicPath: cdnDomian, // 默认'/',部署应用包时的基本 URL
}
5、package.json
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"analyz": "vue-cli-service build --mode analyz",
"lint": "vue-cli-service lint",
"upcdn": "node ./upcdn.js",
"globalcdn": "rm -rf dist && echo --- 正在打包... --- && npm run build && echo --- 打包完成 --- && echo --- 正在上传到服务器... --- && npm run upcdn && echo -- 上传完成!!"
},
执行npm run globalcdn
效果如下图
效果1
效果2
1、安装compression-webpack-plugin
npm i compression-webpack-plugin
2、vue.config.js
// gzip --start
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzip = true // 是否使用gzip
const productionGzipExtensions = ['js', 'css'] // 需要gzip压缩的文件后缀
// gzip --end
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// gzip
// 构建时开启gzip,降低服务器压缩对CPU资源的占用,服务器也要相应开启gzip
productionGzip && myConfig.plugins.push(
new CompressionWebpackPlugin({
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 8192,
minRatio: 0.8
})
)
}
}
}
3、后台Nginx配置
gzip on;
gzip_static on;
gzip_min_length 1024;
gzip_buffers 4 16k;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
本地预览效果
带有.gz
后缀说明压缩成功了
线上在 response headers 里会有一个Content-Encoding:gzip
1、安装uglifyjs-webpack-plugin
npm i uglifyjs-webpack-plugin
2、vue.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 去掉注释
module.exports = {
configureWebpack: config => {
const myConfig = {}
if (process.env.NODE_ENV === 'production') {
myConfig.plugins = []
// 去掉注释
// 去掉注释
myConfig.plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注释
},
compress: {
warnings: false,
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log']//移除console
}
}
})
)
}
}
}
1、安装image-webpack-loader
npm i -D image-webpack-loader
2、vue.congin.js
module.exports = {
chainWebpack: config => {
// 压缩图片
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: false },
pngquant: { quality: '65-90', speed: 4 },
gifsicle: { interlaced: false },
webp: { quality: 75 }
})
}
}
.env.production文件 (线上)
NODE_ENV = 'production'
VUE_APP_SRC='https://www.google.com'
VUE_APP_V='https://www.google.com'
.env.development文件
//开发环境
NODE_ENV='development'
VUE_APP_SRC=''http://192.168.1.174:8003'
VUE_APP_V='http://192.168.1.173:8003'
module.exports = {
devServer: {
open: true, // 自动启动浏览器
host: '0.0.0.0', // localhost
port: 8080, // 端口号
https: false,
hotOnly: false, // 热更新
proxy: {
// 本地代理包含user的接口 如: /user/getUser
'^/user': {
target: process.env.VUE_APP_SRC,
ws: true, //开启WebSocket
changeOrigin: true
},
'^/v1': { //匹配包含 /v1的接口 如:v1/xxx/xx
target: process.env.VUE_APP_V,
ws: true,
changeOrigin: true
}
}
},
}
完整代码
const path = require('path')
const IS_PROD = ['production'].includes(process.env.NODE_ENV)
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin // 打包分析
const mockIndexData = require('./src/mock/index.json') // mock.js (传说的假数据)
// gzip --start
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzip = true // 是否使用gzip
const productionGzipExtensions = ['js', 'css'] // 需要gzip压缩的文件后缀
// gzip --end
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 去掉注释
function resolve (dir) {
return path.join(__dirname, './', dir)
}
// cdn预加载使用
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'element-ui': 'ELEMENT',
'js-cookie': 'Cookies',
'nprogress': 'NProgress'
}
const cdn = {
// 开发环境
dev: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
],
js: []
},
// 生产环境
build: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.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://cdn.jsdelivr.net/npm/[email protected]/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js',
'https://unpkg.com/element-ui/lib/index.js',
'https://cdn.bootcss.com/js-cookie/2.2.0/js.cookie.min.js',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js'
]
}
}
const cdnDomian = 'http://ppqqg4jtj.bkt.clouddn.com' //静态文件cdn
module.exports = {
// baseUrl: IS_PROD ? process.env.VUE_APP_SRC || '/' : './', // 默认'/',部署应用包时的基本 URL
publicPath: IS_PROD ? cdnDomian: '/', // 默认'/',部署应用包时的基本 URL
outputDir: 'dist',
assetsDir: '', // 相对于outputDir的静态资源(js、css、img、fonts)目录
runtimeCompiler: true, // 是否使用包含运行时编译器的 Vue 构建版本
productionSourceMap: false, // 生产环境的 source map
configureWebpack: config => {
const myConfig = {}
if (process.env.NODE_ENV === 'production') {
// 1. 生产环境npm包转CDN
myConfig.externals = externals
myConfig.plugins = []
// gzip
// 2. 构建时开启gzip,降低服务器压缩对CPU资源的占用,服务器也要相应开启gzip
productionGzip && myConfig.plugins.push(
new CompressionWebpackPlugin({
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 8192,
minRatio: 0.8
})
)
// 去掉注释
myConfig.plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注释
},
compress: {
warnings: false,
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log']//移除console
}
}
})
)
}
if (process.env.NODE_ENV === 'development') {
/**
* 关闭host check,方便使用ngrok之类的内网转发工具
*/
myConfig.devServer = {
disableHostCheck: true
}
}
return myConfig
},
chainWebpack: config => {
// 压缩图片
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: false },
pngquant: { quality: '65-90', speed: 4 },
gifsicle: { interlaced: false },
webp: { quality: 75 }
})
// 使用cdn
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
})
// 打包分析
if (process.env.IS_ANALYZ) {
config.plugin('webpack-report').use(BundleAnalyzerPlugin, [
{
analyzerMode: 'static'
}
])
}
// svg loader
const svgRule = config.module.rule('svg') // 找到svg-loader
svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
svgRule // 添加svg新的loader处理
.test(/\.svg$/)
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
// 修改images loader 添加svg处理
const imagesRule = config.module.rule('images')
imagesRule.exclude.add(resolve('src/icons'))
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
},
devServer: {
open: true, // 自动启动浏览器
host: '0.0.0.0', // localhost
port: 6060, // 端口号
https: false,
hotOnly: false, // 热更新
proxy: {
// 本地代理包含user的接口 如: /user/getUser
'^/user': {
target: process.env.VUE_APP_SRC,
ws: true, //开启WebSocket
changeOrigin: true
},
'^/v1': { //匹配包含 /v1的接口 如:v1/xxx/xx
target: process.env.VUE_APP_V,
ws: true,
changeOrigin: true
}
}
},
// devServer: {
// port: 8080,
// before(app) {
// app.get('/api/index', (req, res) => {
// res.json(mockIndexData)
// })
// }
// }
}
完!!