Vue打包压缩模板

282 阅读1分钟

线上项目 欢迎STAR

github地址

前言

项目中使用到的库 vue element-ui vuex echarts vue-router

正常main.js中引入 基本都是1mb往上了, 如果网络环境不好,那基本就是卡个好几秒才能打开。

经过搜索学习 总结出当前的一套打包配置。基本秒开,完美上线。

如过您看出有什么不足,请私信或者评论提出。

先看效果

再上代码

vue.config.js

'use strict'
const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i

function resolve(dir) {
  return path.join(__dirname, dir)
}

// 输出 vue.config.js 配置 vue inspect --mode production > output.js
let _env = {}
if (process.env.NODE_ENV === 'production') {
  _env = {
    'vue': 'Vue',
    'vuex': 'Vuex',
    'vue-router': 'VueRouter',
    'echarts': 'echarts',
    'element-ui': 'ELEMENT'
  }
}
module.exports = {
  // build时构建文件的目录 构建时传入 --no-clean 可关闭该行为
  outputDir: 'dist',
  // build时放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录
  assetsDir: 'static',
  // 指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径
  publicPath: '/',
  // 默认在生成的静态资源文件名中包含hash以控制缓存
  filenameHashing: true,
  // 构建多页时使用
  pages: undefined,
  // eslint-loader是否在保存的时候检查
  lintOnSave: true,
  // 是否使用包含运行时编译器的Vue核心的构建
  runtimeCompiler: false,
  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
  productionSourceMap: false,
  // webpack-dev-server 相关配置
  devServer: {
    // ...
  },
  chainWebpack: (config) => {
    // 修复HMR
    config.resolve.symlinks(true)
    // 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
    config.resolve.alias
      .set('@', resolve('src'))
    if (process.env.NODE_ENV === 'production') {
      // 开启打包压缩
      config.plugin('CompressionWebpackPlugin')
        .use(new CompressionWebpackPlugin({
          filename: '[path][base].gz',
          algorithm: 'gzip',
          test: productionGzipExtensions,
          threshold: 10240,
          minRatio: 0.8,
          deleteOriginalAssets: false
        }))
      // 选择打包模板
      config.plugin('html').tap(args => {
        // 修复 Lazy loading routes Error
        args[0].template = resolve('templates') + '\\index.prod.html'
        args[0].chunksSortMode = 'none'
        console.log('当前打包模板=> ' + args[0].template)
        return args
      })
    } else {
      config.plugin('html').tap(args => {
        // 修复 Lazy loading routes Error
        args[0].template = resolve('templates') + '\\index.dev.html'
        args[0].chunksSortMode = 'none'
        console.log('当前打包模板=> ' + args[0].template)
        return args
      })
    }
  },
  configureWebpack: {
    name: 'loading',
    externals: _env
  },
  // 默认情况下 babel-loader 会忽略所有 node_modules 中的文件。如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来
  css: {
    // 启用 CSS modules
    requireModuleExtension: false,
    // 是否使用css分离插件
    extract: true,
    // 开启 CSS source maps?
    sourceMap: false,
    // css预设器配置项
    loaderOptions: {
      postcss: {
        plugins: [
          require('precss'),
          require('autoprefixer'),
          require('postcss-flexibility')
        ]
      }
    }
  },
  // 第三方插件配置
  pluginOptions: {
    // ...
  }
}

pageage.json

{
  "dependencies": {
    "@babel/polyfill": "7.10.4",
    "axios": "0.19.2",
    "vuex": "3.1.0",
    "vue": "2.6.11",
    "vue-router": "3.0.2",
    "echarts": "4.2.1",
    "element-ui": "2.13.2",
    "core-js": "3.6.5"
  },
  "devDependencies": {
    "@babel/cli": "7.10.5",
    "@babel/core": "7.10.5",
    "@babel/preset-env": "7.10.4",
    "@vue/cli-plugin-babel": "4.4.0",
    "@vue/cli-plugin-eslint": "4.4.0",
    "@vue/cli-service": "4.4.0",
    "autoprefixer": "9.8.5",
    "babel-eslint": "10.1.0",
    "babel-plugin-import": "1.13.0",
    "compression-webpack-plugin": "6.0.5",
    "eslint": "6.7.2",
    "eslint-plugin-vue": "6.2.2",
    "node-sass": "4.14.1",
    "postcss": "7.0.32",
    "postcss-flexibility": "2.0.0",
    "precss": "4.0.0",
    "qs": "6.9.4",
    "sass-loader": "9.0.2",
    "style-loader": "1.2.1",
    "vue-template-compiler": "2.6.11",
    "webpack-dev-server": "2.6.1"
  }
}

src/main.js

import Vue from 'vue'
import router from './router'
import store from './store'
import App from './App.vue'
import ELEMENT from 'element-ui'
const Echarts = require('echarts')
if (process.env.NODE_ENV === 'development') {
  require('element-ui/lib/theme-chalk/index.css')
}
Vue.config.productionTip = false
Vue.use(ELEMENT)
Vue.prototype.echarts = Echarts

路由 src/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '@/store/index.js'
if (process.env.NODE_ENV === 'development') {
  Vue.use(VueRouter)
}
...

VUEX src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
if (process.env.NODE_ENV === 'development') {
  Vue.use(Vuex)
}
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)

const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

const store = new Vuex.Store({
  modules,
  getters
})

export default store

.env.development

ENV = 'development'

.env.production

ENV = 'production'

打包模板

开发模板 templates index.dev.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script type="text/javascript">
      if(!window.console){
        window.console={
          log:function(msg){},
          err:function(msg){}
        }
      } 
    </script>
  </body>
</html>

打包模板 templates index.prod.html

使用cdn来分发,最好是有自己的服务器或者使用稳定的cdn服务商

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
    <link rel="stylesheet" href="//cdn.bootcss.com/element-ui/2.14.0/theme-chalk/index.css">
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->

    <script type="text/javascript">
      if(!window.console){
        window.console={
          log:function(msg){},
          err:function(msg){}
        }
      } 
    </script>
    
    <script src="//cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script src="//cdn.bootcss.com/element-ui/2.14.0/index.min.js"></script>
    <script src="//cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
    <script src="//cdn.bootcss.com/echarts/4.8.0/echarts.min.js"></script>
    <script src="//cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
  </body>
</html>

遇到相关的问题

  • compression-webpack-plugin 注意版本filename: '[path][base].gz',不同版本是不一样的写法

6.0 以下会有如下警告

Conflict: Multiple assets emit different content to the same filename static/js/.gz

  • requireModuleExtension: false

vue-cli的更新警告

WARN "css.modules" option in vue.config.js is deprecated now, please use "css.requireModuleExtension" instead.

  • productionSourceMap: false

设置为 false 不生成map文件

  • 版本号 一定要跟cdn完全一致