1. 安装postCss

npm install postcss-plugin-px2rem@0.8.1

  1. // 配置postcss
  2. const px2rem = require('postcss-plugin-px2rem')
  3. const postcss = px2rem({
  4. rootValue: 16, // 换算基数, 默认100 ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。
  5. exclude: /(node_module)/, // 默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)/ 。如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
  6. // selectorBlackList: [], //要忽略并保留为px的选择器
  7. // ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。
  8. // replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。
  9. mediaQuery: false, // (布尔值)允许在媒体查询中转换px。
  10. minPixelValue: 3 // 设置要替换的最小像素值(3px会被转rem)。 默认 0
  11. })
  12. css: {
  13. loaderOptions: {
  14. postcss: {
  15. plugins: [
  16. postcss // 配置postcss
  17. ]
  18. },
  19. scss: {
  20. // 全局使用的scss
  21. prependData: `@import "~@/styles/mixin.scss";`
  22. }
  23. }
  24. },

2. 单线程压缩js代码(速度慢)

“uglifyjs-webpack-plugin”: “^2.2.0”

  1. // 代码压缩
  2. const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
  3. configureWebpack: config => {
  4. // 代码压缩
  5. config.plugins.push(
  6. new UglifyJsPlugin({
  7. uglifyOptions: {
  8. output: {
  9. // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false
  10. beautify: false,
  11. // 是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
  12. comments: false
  13. },
  14. // 生产环境自动删除console
  15. compress: {
  16. // warnings: false, // 若打包错误,则注释这行
  17. drop_debugger: true,
  18. drop_console: true,
  19. pure_funcs: ['console.log']
  20. }
  21. },
  22. sourceMap: false,
  23. parallel: true
  24. })
  25. )
  26. }

3. 多线程js代码压缩(第三方不维护)

“webpack-parallel-uglify-plugin”: “^1.1.4”

  1. // 多线程js压缩
  2. const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
  3. configureWebpack: config => {
  4. // JS多线程压缩
  5. config.plugins.push(
  6. new ParallelUglifyPlugin({
  7. cacheDir: '.cache/',
  8. test: /.js$/,
  9. uglifyJS: {
  10. output: {
  11. beautify: false,
  12. comments: false
  13. },
  14. warnings: false,
  15. compress: {
  16. drop_console: true,
  17. collapse_vars: true,
  18. reduce_vars: true
  19. }
  20. },
  21. sourceMap: false
  22. })
  23. )
  24. }

4 terser JS多线程压缩(官方维护)

npm i terser-webpack-plugin@1.4.5

  1. // 多线程官方js压缩
  2. const TerserPlugin = require('terser-webpack-plugin')
  3. configureWebpack: config => {
  4. config.plugins.push(
  5. new TerserPlugin({
  6. terserOptions: {
  7. ecma: undefined,
  8. warnings: false,
  9. parse: {},
  10. compress: {
  11. drop_console: true,
  12. drop_debugger: false,
  13. pure_funcs: ['console.log'] // 移除console
  14. }
  15. }
  16. })
  17. )
  18. }

5. 图形化分析压缩体积

“webpack-bundle-analyzer”: “^4.5.0”,

  1. // 实时查看构建分析
  2. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  3. const analyzer = new BundleAnalyzerPlugin({
  4. analyzerPort: 9999
  5. })
  6. config.plugin('webpack-bundle-analyzer').use(analyzer)

6.0 压缩css代码

npm i optimize-css-assets-webpack-plugin@5.0.4

  1. // optimizeCssPlugin CSS文件压缩插件
  2. const OptimizeCssPlugin = require('optimize-css-assets-webpack-plugin')
  3. // 压缩css
  4. config.optimization.minimizer.push(
  5. new OptimizeCssPlugin({
  6. assetNameRegExp: /\.css$/g,
  7. cssProcessor: require('cssnano'),
  8. cssProcessorOptions: { discardComments: { removeAll: true }},
  9. canPrint: true
  10. })
  11. )

7.0 Gzip

npm i compression-webpack-plugin@5.0.1

  1. // gzip压缩
  2. const CompressionWebpackPlugin = require('compression-webpack-plugin')
  3. // gzip压缩
  4. // const productionGzipExtensions = ['html', 'js', 'css']
  5. config.plugins.push(
  6. new CompressionWebpackPlugin({
  7. filename: '[path].gz[query]',
  8. algorithm: 'gzip',
  9. test: /\.(js|css|json|txt|ico|svg)(\?.*)?$/i,
  10. threshold: 10240, // 只有大小大于该值的资源会被处理 10240
  11. minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
  12. deleteOriginalAssets: false // 删除原文件
  13. })
  14. )

配置的全部代码

  1. 'use strict'
  2. const path = require('path')
  3. // 配置postcss
  4. const px2rem = require('postcss-plugin-px2rem')
  5. const postcss = px2rem({
  6. rootValue: 16, // 换算基数, 默认100 ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。
  7. exclude: /(node_module)/, // 默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)/ 。如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
  8. // selectorBlackList: [], //要忽略并保留为px的选择器
  9. // ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。
  10. // replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。
  11. mediaQuery: false, // (布尔值)允许在媒体查询中转换px。
  12. minPixelValue: 3 // 设置要替换的最小像素值(3px会被转rem)。 默认 0
  13. })
  14. // cdn链接
  15. const cdn = {
  16. // cdn:模块名称和模块作用域命名(对应window里面挂载的变量名称)
  17. externals: {
  18. vue: 'Vue',
  19. vuex: 'Vuex',
  20. vant: 'vant',
  21. 'vue-router': 'VueRouter',
  22. axios: 'axios',
  23. moment: 'moment',
  24. 'vue-clipboard2': 'VueClipboard'
  25. },
  26. // cdn的css链接
  27. css: [
  28. 'https://cdn.jsdelivr.net/npm/vant@2.12/lib/index.css'
  29. ],
  30. // cdn的js链接
  31. js: [
  32. 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
  33. 'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js',
  34. 'https://unpkg.com/vue-router@3.2.0/dist/vue-router.min.js',
  35. 'https://cdn.jsdelivr.net/npm/vant@2.12/lib/vant.min.js',
  36. 'https://cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js',
  37. 'https://cdn.jsdelivr.net/npm/moment@2.18.1/min/moment.min.js',
  38. 'https://cdn.jsdelivr.net/npm/vue-clipboard2@0.3.1/dist/vue-clipboard.min.js'
  39. ]
  40. }
  41. // 单线程js代码压缩
  42. const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
  43. // gzip压缩
  44. const CompressionWebpackPlugin = require('compression-webpack-plugin')
  45. // 多线程js压缩
  46. const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
  47. // 多线程官方js压缩
  48. const TerserPlugin = require('terser-webpack-plugin')
  49. // 实时查看构建分析
  50. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  51. // optimizeCssPlugin CSS文件压缩插件
  52. const OptimizeCssPlugin = require('optimize-css-assets-webpack-plugin')
  53. function resolve(dir) {
  54. return path.join(__dirname, dir)
  55. }
  56. const isProduction = process.env.NODE_ENV === 'production'
  57. // const name = defaultSettings.title || 'vue Admin Template' // page title
  58. const port = process.env.port || process.env.npm_config_port || 9528 // dev port
  59. module.exports = {
  60. publicPath: process.env.NODE_ENV === 'production'
  61. ? '/production-sub-path/'
  62. : '/',
  63. outputDir: 'dist',
  64. assetsDir: 'static',
  65. lintOnSave: process.env.NODE_ENV === 'development',
  66. productionSourceMap: false,
  67. devServer: {
  68. open: true,
  69. host: '0.0.0.0', // localhost
  70. port: port,
  71. proxy: {
  72. // 写你的接口
  73. [process.env.VUE_APP_BASE_API]: {
  74. // target: `http://localhost:8081/ipamPortal`, //本机IP
  75. target: `http://0.0.0.0:8080/zoneportal/`, // 内网IP
  76. // target: `http://0.0.0.0:8080/zoneportal/`, // 外网
  77. changeOrigin: true,
  78. pathRewrite: {
  79. ['^' + process.env.VUE_APP_BASE_API]: ''
  80. }
  81. }
  82. },
  83. overlay: { // 设置让浏览器 overlay 同时显示警告和错误
  84. warnings: false,
  85. errors: true
  86. },
  87. before: require('./mock/mock-server.js')
  88. },
  89. css: {
  90. loaderOptions: {
  91. postcss: {
  92. plugins: [
  93. postcss // 配置postcss
  94. ]
  95. },
  96. scss: {
  97. // 全局使用的scss
  98. prependData: `@import "~@/styles/mixin.scss";`
  99. }
  100. }
  101. },
  102. configureWebpack: config => {
  103. // 生产环境
  104. if (isProduction) {
  105. // 为生产环境修改配置...
  106. config.mode = 'production'
  107. // 用cdn方式引入,则构建时要忽略相关资源
  108. config.externals = cdn.externals
  109. config.output.filename = `js/[name].[chunkhash:8].js`
  110. config.output.chunkFilename = `js/[name].[chunkhash:8].js`
  111. config.plugins.push(
  112. new UglifyJsPlugin({
  113. uglifyOptions: {
  114. output: {
  115. // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false
  116. beautify: false,
  117. // 是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
  118. comments: false
  119. },
  120. // 生产环境自动删除console
  121. compress: {
  122. // warnings: false, // 若打包错误,则注释这行
  123. drop_debugger: true,
  124. drop_console: true,
  125. pure_funcs: ['console.log']
  126. }
  127. },
  128. sourceMap: false,
  129. parallel: true
  130. }))
  131. // gzip压缩
  132. // const productionGzipExtensions = ['html', 'js', 'css']
  133. config.plugins.push(
  134. new CompressionWebpackPlugin({
  135. filename: '[path].gz[query]',
  136. algorithm: 'gzip',
  137. test: /\.(js|css|json|txt|ico|svg)(\?.*)?$/i,
  138. threshold: 10240, // 只有大小大于该值的资源会被处理 10240
  139. minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
  140. deleteOriginalAssets: false // 删除原文件
  141. })
  142. )
  143. // JS多线程压缩---缓存形式
  144. config.plugins.push(
  145. new ParallelUglifyPlugin({
  146. cacheDir: '.cache/',
  147. test: /.js$/,
  148. uglifyJS: {
  149. output: {
  150. beautify: false,
  151. comments: false
  152. },
  153. warnings: false,
  154. compress: {
  155. drop_console: true,
  156. collapse_vars: true,
  157. reduce_vars: true
  158. }
  159. },
  160. sourceMap: false
  161. })
  162. )
  163. // 压缩css
  164. config.plugins.push(
  165. new OptimizeCssPlugin({
  166. assetNameRegExp: /\.css$/g,
  167. cssProcessor: require('cssnano'),
  168. cssProcessorOptions: { discardComments: { removeAll: true }},
  169. canPrint: true
  170. })
  171. )
  172. // 多线程压缩js
  173. config.plugins.push(
  174. new TerserPlugin({
  175. test: /\.js(\?.*)?$/i, // 匹配参与压缩的文件
  176. parallel: true, // 使用多进程并发运行
  177. terserOptions: {
  178. output: { comments: false },
  179. ecma: undefined,
  180. warnings: false,
  181. parse: {},
  182. extractComments: false, // 将注释剥离到单独的文件中
  183. compress: {
  184. drop_console: true,
  185. drop_debugger: false,
  186. pure_funcs: ['console.log'] // 移除console
  187. }
  188. }
  189. })
  190. )
  191. }
  192. // 取消webpack警告的性能提示
  193. config.performance = {
  194. hints: 'warning',
  195. // 入口起点的最大体积
  196. maxEntrypointSize: 10000 * 1000,
  197. // 生成文件的最大体积
  198. maxAssetSize: 30000 * 1000,
  199. // 只给出 js 文件的性能提示
  200. assetFilter: function(assetFilename) {
  201. return assetFilename.endsWith('.js')
  202. }
  203. }
  204. },
  205. chainWebpack(config) {
  206. // 添加别名
  207. config.resolve.alias
  208. .set('@', resolve('src'))
  209. .set('assets', resolve('src/assets'))
  210. .set('api', resolve('src/api'))
  211. .set('views', resolve('src/views'))
  212. .set('components', resolve('src/components'))
  213. // 压缩图片
  214. const imagesRule = config.module.rule('images')
  215. imagesRule.uses.clear()
  216. imagesRule.use('file-loader')
  217. .loader('url-loader')
  218. .options({
  219. limit: 10240,
  220. fallback: {
  221. loader: 'file-loader',
  222. options: {
  223. outputPath: 'static/images'
  224. }
  225. }
  226. })
  227. // set svg-sprite-loader
  228. config.module
  229. .rule('svg')
  230. .exclude.add(resolve('src/icons'))
  231. .end()
  232. config.module
  233. .rule('icons')
  234. .test(/\.svg$/)
  235. .include.add(resolve('src/icons'))
  236. .end()
  237. .use('svg-sprite-loader')
  238. .loader('svg-sprite-loader')
  239. .options({
  240. symbolId: 'icon-[name]'
  241. })
  242. .end()
  243. config
  244. .when(process.env.NODE_ENV !== 'development',
  245. config => {
  246. config.plugins.delete('preload')
  247. config.plugins.delete('prefetch')
  248. const analyzer = new BundleAnalyzerPlugin({
  249. analyzerPort: 9999
  250. })
  251. config.plugin('webpack-bundle-analyzer').use(analyzer).end()
  252. // csshansh
  253. config.plugin('extract-css').tap(args => [{
  254. filename: `css/[name].[contenthash:8].css`,
  255. chunkFilename: `css/[name].[contenthash:8].css`
  256. }])
  257. // ============注入cdn start============
  258. config.plugin('html').tap(args => {
  259. // 生产环境或本地需要cdn时,才注入cdn
  260. args[0].cdn = cdn
  261. return args
  262. })
  263. config
  264. .plugin('ScriptExtHtmlWebpackPlugin')
  265. .after('html')
  266. .use('script-ext-html-webpack-plugin', [{
  267. inline: /runtime\..*\.js$/
  268. }])
  269. .end()
  270. config
  271. .optimization.splitChunks({
  272. chunks: 'all',
  273. maxInitialRequests: Infinity,
  274. minSize: 20000, // 依赖包超过20000bit将被单独打包
  275. cacheGroups: {
  276. vendor: {
  277. test: /[\\/]node_modules[\\/]/,
  278. name(module) {
  279. // get the name. E.g. node_modules/packageName/not/this/part.js
  280. // or node_modules/packageName
  281. const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]
  282. // npm package names are URL-safe, but some servers don't like @ symbols
  283. return `npm.${packageName.replace('@', '')}`
  284. }
  285. }
  286. }
  287. })
  288. config.optimization.minimize(true)
  289. config.optimization.runtimeChunk('single')
  290. }
  291. )
  292. }
  293. }