Vue typescript 如何极限压缩编译静态资源
# Vue typescript 如何极限压缩编译后静态资源
# 前言
近期开发项目,由于资源有限,云服务器只有1m带宽。
vue初始打包的静态资源,通过浏览器加载需要近1分钟的时间。
所以需要将静态资源进行压缩及相应处理,最终浏览器访问时间为5秒钟。
# 一、安装依赖
首先安装依赖
compression-webpack-plugin
image-webpack-loader
- yarn
yarn add compression-webpack-plugin -D
yarn add image-webpack-loader -D
2
- npm
npm install compression-webpack-plugin --save-dev
npm install image-webpack-loader --save-dev
2
注意
记住,依赖一定要安装在 devDependencies
下,否则会增大你的打包体积
# 二、压缩图片
提示
图片处理其实有很多种方式,例如引入cdn服务器等。
此处介绍的方式为,需要将图片和静态资源打包在一起时的解决方案。
# 1. 处理图片
- 首先需要对图片进行处理,处理方式自行选择,例如使用ps将图片质量减小等。
- 此处推荐在线压缩图片,处理图片的网站https://imagecompressor.com/zh/
提示
此处再推荐一个其他的在线处理图片的网站https://www.websiteplanet.com/zh-hans/webtools/imagecompressor/
Thanks for Katherine Miller suggestion.
注意
不保证网站是否会收集图片数据,如果数据较为敏感,请不要上传,自行进行处理。
此步骤主要是为了将原始图片进一步压缩。
# 2. 打开vue.config.js
文件
打开vue的配置文件
# 3. 编写压缩图片配置
需要在文件中编写相应的压缩,可以对图片进行二次压缩。请根据需求自行配置。
详细使用说明可参考 https://www.npmjs.com/package/image-webpack-loader
module.exports = {
// 省略部分配置项
....
,
chainWebpack: config => {
// 省略部分配置项
...
// 判断环境不为开发环境
config
.when(process.env.NODE_ENV !== 'development',
config => {
// 压缩图片配置
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 65 }, // Compress JPEG images
optipng: { enabled: false }, // Compress PNG images
pngquant: { quality: [0.65, 0.9], speed: 4 }, // Compress PNG images
gifsicle: { interlaced: false } // Compress SVG images
})
.end()
}
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 三、压缩资源
# 1. 打开vue.config.js
文件
打开vue的配置文件
# 2. 编写压缩配置
配置压缩资源,具体配置项可参考https://www.npmjs.com/package/compression-webpack-plugin
// 引入压缩依赖
const CompressionPlugin = require('compression-webpack-plugin')
// 需要压缩的文件的正则
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i
module.exports = {
// 省略部分配置项
....
,
chainWebpack: config => {
// 省略部分配置项
...
// 判断环境不为开发环境
config
.when(process.env.NODE_ENV !== 'development',
config => {
// 打包成gzip压缩文件
config.plugin('compressionPlugin')
.use(new CompressionPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: productionGzipExtensions,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: true
}))
}
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 四、抽取公共代码打包
# 1. 打开vue.config.js
文件
打开vue的配置文件
# 2. 编写抽取公共代码配置
此处为将代码中的公共部分抽取出来,统一打包,可减小打包后的代码体积。
module.exports = {
// 省略部分配置项
....
,
chainWebpack: config => {
// 省略部分配置项
...
// 判断环境不为开发环境
config
.when(process.env.NODE_ENV !== 'development',
config
.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
elementUI: {
name: 'chunk-elementUI', // split elementUI into a single package
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
},
commons: {
name: 'chunk-commons',
test: path.resolve(__dirname, 'src/components'),
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
})
config.optimization.runtimeChunk('single')
}
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 五、引用CDN
注意
此处引用的js和css的公共cdn代码库。
引入cdn后可有效减少从服务获取的资源数量。
但是有风险,一旦cdn服务出问题,你的网站也将无法访问。
此步骤请谨慎选择或使用自己的cdn服务器。
# 1. 打开vue.config.js
文件
打开vue的配置文件
# 2. 配置CDN
const cdn = {
css: [
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/theme-chalk/index.min.css',
'https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css'
],
js: [
// vue必须在第一个
'https://cdn.bootcss.com/vue/2.6.11/vue.min.js',
'https://cdn.bootcss.com/vuex/3.2.0/vuex.min.js',
'https://cdn.bootcss.com/vue-router/3.4.3/vue-router.min.js',
'https://cdn.bootcss.com/axios/0.19.2/axios.min.js',
'https://cdn.bootcss.com/qs/6.5.1/qs.min.js',
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/index.min.js',
'https://cdn.bootcdn.net/ajax/libs/echarts/4.7.0/echarts-en.min.js',
'https://cdn.bootcdn.net/ajax/libs/dexie/3.0.3/dexie.min.js',
'https://cdn.bootcdn.net/ajax/libs/js-cookie/2.2.1/js.cookie.min.js',
'https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js'
]
}
module.exports = {
// 省略部分配置项
....
,
chainWebpack: config => {
config
.when(process.env.NODE_ENV !== 'development',
config => {
// 设置不打包的依赖,请根据自己需求进行修改
const externals = {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
axios: 'axios',
qs: 'Qs',
'element-ui': 'ELEMENT',
echarts: 'echarts',
dexie: 'Dexie',
'js-cookie': 'Cookies',
nprogress: 'NProgress'
}
config.externals(externals)
// 引入cdn
config.plugin('html').tap(args => {
args[0].cdn = cdn
return args
})
}
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 六、配置Nginx
提示
如果你使用的ngxin服务器当做静态资源服务,则可以开启gzip配置。
开启后访问速度会获得极大的提升
http {
# 省略部分配置
...
# 开启gzip
gzip on;
gzip_static on;
# 设置缓冲区大小
gzip_buffers 4 16k;
#压缩级别官网建议是6
gzip_comp_level 6;
#压缩的类型
gzip_types text/plain application/javascript text/css application/xml text/javascript application/x-httpd-php;
# 省略部分配置
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 七、完整的配置文件
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const CompressionPlugin = require('compression-webpack-plugin')
const name = 'xxxx系统'
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i
const cdn = {
css: [
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/theme-chalk/index.min.css',
'https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css'
],
js: [
// vue必须在第一个
'https://cdn.bootcss.com/vue/2.6.11/vue.min.js',
'https://cdn.bootcss.com/vuex/3.2.0/vuex.min.js',
'https://cdn.bootcss.com/vue-router/3.4.3/vue-router.min.js',
'https://cdn.bootcss.com/axios/0.19.2/axios.min.js',
'https://cdn.bootcss.com/qs/6.5.1/qs.min.js',
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/index.min.js',
'https://cdn.bootcdn.net/ajax/libs/echarts/4.7.0/echarts-en.min.js',
'https://cdn.bootcdn.net/ajax/libs/dexie/3.0.3/dexie.min.js',
'https://cdn.bootcdn.net/ajax/libs/js-cookie/2.2.1/js.cookie.min.js',
'https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js'
]
}
module.exports = {
publicPath: './',
outputDir: 'dist',
// 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。 资源放的目录
assetsDir: './static',
// 指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径 index的路劲和名字
indexPath: './index.html',
// publicPath: process.env.NODE_ENV === 'production' ? '/vue-typescript-admin-template/' : '/',
lintOnSave: process.env.NODE_ENV === 'development',
transpileDependencies: ['fuse.js', 'vuex-module-decorators', 'clipboard', 'markdown-it-vue'],
productionSourceMap: false,
devServer: {
port: devServerPort,
open: true,
overlay: {
warnings: false,
errors: true
},
progress: false,
proxy: {
'/': {
target: 'http://localhost:8082/',
withCredentials: true,
changeOrigin: true, // needed for virtual hosted sites
ws: true, // proxy websockets
}
}
},
pwa: {
name: name,
workboxPluginMode: 'InjectManifest',
workboxOptions: {
swSrc: path.resolve(__dirname, 'src/pwa/service-worker.js')
}
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: [
path.resolve(__dirname, 'src/styles/_variables.scss'),
path.resolve(__dirname, 'src/styles/_mixins.scss')
]
}
},
chainWebpack: config => {
// provide the app's title in webpack's name field, so that
// it can be accessed in index.html to inject the correct title.
config.set('name', name)
config
.when(process.env.NODE_ENV !== 'development',
config => {
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
// { bypassOnDebug: true }
mozjpeg: { progressive: true, quality: 65 }, // ∂Compress JPEG images
optipng: { enabled: false }, // Compress PNG images
pngquant: { quality: [0.65, 0.9], speed: 4 }, // Compress PNG images
gifsicle: { interlaced: false } // Compress SVG images
// webp: { quality: 75 }
})
.end()
const externals = {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
axios: 'axios',
qs: 'Qs',
'element-ui': 'ELEMENT',
echarts: 'echarts',
dexie: 'Dexie',
'js-cookie': 'Cookies',
nprogress: 'NProgress'
}
config.externals(externals)
// 引入cdn
config.plugin('html').tap(args => {
args[0].cdn = cdn
return args
})
// 打包成gzip压缩文件
config.plugin('compressionPlugin')
.use(new CompressionPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: productionGzipExtensions,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: true
}))
config
.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
elementUI: {
name: 'chunk-elementUI', // split elementUI into a single package
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
},
commons: {
name: 'chunk-commons',
test: path.resolve(__dirname, 'src/components'),
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
})
config.optimization.runtimeChunk('single')
}
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149