全网最详细——基于企业级vue项目webpack配置和优化实践

本文是基于vue-cli3企业级web项目的webpack配置和优化实践。我们知道vue-cli3没有了config目录,那该怎么配置webpack呢?如果你想对webpack相关内容进行配置,需要自己在根目录下(与package.json同级)创建一个vue.config.js文件,这个文件一旦存在,那么它会被 @vue/cli-service 自动加载。(但需要我们自己手动在项目的根目录下创建vue.config.js)

一、vue.config.js中常用的配置

1、publicPath 部署应用包的Url路径

部署应用包的基本Url,默认为/, 可以设置为相对路径./, 这样打出来的包,可以部署到任意路径上:

ini 复制代码
let developmentPath='./'; // 开发环境 npm run serve 时引用文件路径
let productionPath='./'; // 生产环境 npm run build 打包后引用文件路径
module.exports = {
    // 基本路径-引用文件的路
    publicPath: process.env.NODE_ENV === 'production' ? productionPath : developmentPath
}

2、outputDir 输出文件目录

代码构建后输出文件目录(打包后生成的目录,默认dist)

java 复制代码
module.exports = {
  outputDir: __dirname + '/server/dist', // build之后静态文件输出路径,可以自己定义路径
}

3、assetsDir 打包后生成的静态资源目录

打包后生成的静态资源目录,默认 " " ,也就是我们打包后的css,js等存放的位置

java 复制代码
module.exports = {
   assetsDir: 'static',
}

4、lintOnSave

用于指定是否在保存文件时执行代码检查,在实际项目中,你可以根据项目需求和团队规范来决定是否启用代码检查

arduino 复制代码
module.exports = {
    lintOnSave: process.env.NODE_ENV !== 'production', // eslint-loader 
}

5、productionSourceMap 生产环境的 source map

生产环境的 source map,可以将其设置为 false 以加速生产环境构建,默认值是true

java 复制代码
module.exports = {
    productionSourceMap: false,
}

6、devServer

可通过 devServer.proxy解决前后端跨域问题(反向代理)。hothotOnly 的区别是在某些模块不支持热更新的情况下,前者会自动刷新页面,后者不会刷新页面

arduino 复制代码
module.exports = {
  // 反向代理
  devServer: {
    index: '/login.html',   // 默认打开文件
    open: true,             // 自动打开浏览器
    host: 'localhost',      // 默认打开域名
    port: 8080,             // 默认打开端口号
    https: false,           // 开启关闭https请求
    hotOnly: false,         // 热更新
    proxy: {
      // 配置跨域
      '/api': {
        target: 'http://dev.aabb.cn:8082/', //代理地址,会代替axios中设置的baseURL
        ws: true, // proxy websockets
        changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
        pathRewrite: {                // pathRewrite方法重写url
          '^/api': '/',
        },
      },
    },
  },
} 

7、chainWebpack 配置

php 复制代码
module.exports = {
  chainWebpack: (config) => {
    config.plugins.delete('preload')
    config.plugins.delete('prefetch')

    config.module
      .rule('svg')
      .exclude.add(resolve('src/assets/icons'))
      .end()

    config.module
      .rule('icons')
      .test(/.svg$/)
      .include.add(resolve('src/assets/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]',
      })
      .end()

    const imagesRule = config.module.rule('images')
    imagesRule.uses.clear() // 清除原本的images loader配置
    imagesRule
      .test(/.(jpg|gif|png|svg)$/)
      .exclude.add(path.join(__dirname, '../node_modules')) // 不对node_modules里的图片转base64
      .end()
      .use('url-loader')
      .loader('url-loader')
      .options({ name: 'img/[name].[hash:8].[ext]', limit: 6000000 })
    
    // 代码切割处理
    config.optimization.splitChunks({
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          minChunks: pageNum,
          test: /node_modules/,
          priority: -10,
          chunks: 'initial',
        },
        elementUI: {
          name: 'chunk-elementUI', // 将 elementUI 提取成独立的包
          priority: 20,
          test: /[\/]node_modules[\/]_?element-ui(.*)/, // in order to adapt to cnpm
        },
        commons: {
          name: 'chunk-commons',
          test: resolve('src/components'),
          minChunks: 3,
          priority: 5,
          reuseExistingChunk: true,
        },
      },
    })
  },
}

vue 脚手架默认开启了 preload 与 prefetch,当项目很大时,这个就成了首屏加载的最大元凶

  • preload 与 prefetch 都是一种资源预加载机制;
  • preload 是预先加载资源,但并不执行,只有需要时才执行它;
  • prefetch 是意图预获取一些资源,以备下一个导航/页面使用;
  • preload 的优先级高于 prefetch。

8、configureWebpack 配置

javascript 复制代码
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']
const Version = 'V6.1'
const Timestamp = new Date().getTime()

module.exports = {
    webpack配置
    configureWebpack: (config) => {
        // 为生产环境修改配置
        if (process.env.NODE_ENV === 'production') {
            config.plugins.push(
                new UglifyJsPlugin({
                    uglifyOptions: {
                        compress: {
                            drop_debugger: true,// 生产环境自动删除debugger
                            drop_console: true, // 生产环境自动删除console
                        },
                        warnings: false,
                    },
                    sourceMap: false,   //关掉sourcemap 会生成对于调试的完整的.map文件
                    parallel: true, //使用多进程并行运行来提高构建速度。
                }),
                new CompressionWebpackPlugin({
                    filename: '[path].gz[query]',
                    algorithm: 'gzip',
                    test: new RegExp('\.(' + productionGzipExtensions.join('|') + ')$'),
                    threshold: 10240,
                    minRatio: 0.8,
                })
            )
        }
        // externals 配置后,减少了压缩的包内容,需要在 public/index.html 通过cdn方式再引入
        config.externals = { 
            vue: 'Vue',
            'vue-router': 'VueRouter',
            vuex: 'Vuex',
            axios: 'axios',
            jquery: '$',
            moment: 'moment',
            'mint-ui': 'MINT'
        },
        // 别名配置
        Object.assign(config, {
            // 开发生产共同配置
            resolve: {
                alias: {
                '@': path.resolve(__dirname, './src'),
                '@c': path.resolve(__dirname, './src/components'),
                '@p': path.resolve(__dirname, './src/pages')
                }
            }
        }),
        config.output.filename = `[name].${Version}.${Timestamp}.js`  //打包生成的文件
        config.output.chunkFilename = `[name].${Version}.${Timestamp}.js`
    },
 }

扩展:

  1. uglifyjs-webpack-plugin插件用来缩小(压缩优化)js文件,至少需要Webpack v4.0.0版本
  2. compression-webpack-plugin的配置实现Gzip压缩,需要nginx 配置相关参数生效
  3. chunkFilename和webpack.optimize.CommonsChunkPlugin插件的作用差不多,都是用来将公共模块提取出来,但是用法不一样

chunkFilename 用来打包 require.ensure 方法中引入的模块,如果该方法中没有引入任何模块则不会生成任何chunk块文件。比如在main.js文件中使用require.ensure([], function(require){ alert(11); })不会打包块文件; require.ensure([], function(require{ alert(11); require( './greeter') }) 或者require.ensure(['./greeter'],function(require){alert(11);})是可以的。chunk 的 hash值只有在require.ensure中引入的模块发生变化,hash值才会改变。

注意: 对于不是在ensure方法中引入的模块,此属性不会生效,只能用CommonsChunkPlugin插件来提取

css 复制代码
entry:{
    main: __dirname + '/app/main.js',
    index: __dirname + '/app/index.js'      
},
output:{
    path: __dirname + '/public', // 通过HtmlWebpackPlugin插件生成的html文件存放在这个目录下面
    filename: '/js/[name].js', // 编译生成的js文件存放到根目录下面的js目录下面,如果js目录不存在则自动创建
    chunkFilename: 'js/[chunkhash:8].chunk.js'
},

configureWebpack 和 chainWebpack区别 ------ 在这里configureWebpack 和 chainWebpack 的作用相同,唯一的区别就是他们修改webpack配置的方式不同。如果对一个loader或plugin修改的配置如果只是单项的话推荐 chainWebpack;如果是多项的话用configureWebpack直接覆写

  1. chainWebpack 通过链式编程的形式,来修改默认的webpack配置
  2. configureWebpack 通过操作对象的形式,来修改默认的webpack配置

9、css相关配置

这里配置了全局 sass 需要安装的依赖 sass-loader 、less-loader 等

css 复制代码
 css: {
        loaderOptions: {
            scss: {
                additionalData: `@import '@/styles/main.scss';` // 注意配置的键名
            },
            postcss: {
                plugins: [
                    require('postcss-px2rem')({
                        remUnit: 37.5
                    })
                ]
            }
        }
    },

remUnit这个配置项的数值是多少呢?通常我们是根据设计图来定这个值,原因很简单,便于开发。假如设计图给的宽度是750,我们通常就会把remUnit设置为75,这样我们写样式时,可以直接按照设计图标注的宽高来 1:1 还原开发。

由于 sass-loader 版本不同,loaderOptions 中的 additionalData 的键名也不同

  • sass-loader loader v8-, 这个选项名是 "data",
  • sass-loader loader v8中, 这个选项名是 "prependData",
  • sass-loader loader v10+, 这个选项名是 "additionalData",

10、配置 pages 多页面

vue-cli3 中的webpack与vue多页面应用开发的相关参数:

  • entry:page 的入口
  • template:模板来源
  • filename:在 dist/index.html 的输出
  • title:template 中的 title 标签需要是
  • chunks:在这个页面中包含的块,默认情况下会包含
less 复制代码
module.exports = {
   pages:{
       index: {
           entry: "src/pages/main.js", // page 的第一页面入口
           template: "public/index.html", // 模板来源
           filename: "index.html", // 在 dist目录下的输出文件
           title: "Index Page", // 当使用 title 选项时,template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
           
           chunks: ["chunk-vendors", "chunk-common", "index"] // 在这个页面中包含的块,默认情况下会包含。提取出来的通用 chunk 和 vendor chunk。
       },
       about: {
           entry: "src/pages/about.js", // page 的第二页面入口
           template: "public/about.html",  // 模板来源
           filename: "about.html", // 在 dist目录下的输出文件
           title: "About Page",
           outputPath: 'dist/about',
           chunks: ["chunk-vendors", "chunk-common", "about"]
       },
   },
}

上面的例子中,我们定义了两个页面:indexaboutpages配置可以用来设置多页应用程序的入口和输出路径。每个页面都会生成一个对应的HTML文件和JavaScript文件,以及其他可能的静态资源文件。上面的例子中index.html页面将被输出到 dist/ 目录下,about.html 将被输出到 dist/about/ 目录下。对于多页面打包我们进行如下封装:

javascript 复制代码
// 引入glob模块,用于扫描全部src/pages/**.js(返回的是一个数组)
const glob = require('glob') 

// 打包多入口文件基本配置
function getPagesInfo() {
    let pages = {}
    glob.sync('src/pages/**.js').forEach((entry, i) => {
        let name = entry.slice(10, -8)
        pages[name] = {
            entry: entry,
            template: 'public.index.html',
            filename: name + '.html',
            title: '',
            chunks: ["chunk-vendors", "chunk-common", name]
        }
    })
    return pages
}
module.exports = {
    pages: getPagesInfo(),
    publicPath: './',
    assetsDir: 'static',
};

11、其他配置

  • parallel: require('os').cpus().length > 1, // 是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
  • pwa: {}, // PWA 插件相关配置
  • pluginOptions: {} // 第三方插件配置

二、优化

1、优化打包chunk-vendors.js

当运行项目并且打包的时候,会发现 chunk-vendors.js 这个文件非常大,那是因为webpack将所有的依赖全都压缩到了这个文件里面,这时我们可以将其拆分,将所有的依赖都打包成单独的js;

javascript 复制代码
// 利用splitChunks将每个依赖包单独打包,在生产环境下配置,代码如下
configureWebpack: (config) => {
    if (process.env.NODE_ENV === 'production') {
      // 为生产环境修改配置...
      config.mode = 'production'
      // 将每个依赖包打包成单独的js文件
      let optimization = {
        runtimeChunk: 'single',
        splitChunks: {
          chunks: 'all',
          maxInitialRequests: Infinity,
          minSize: 20000, // 依赖包超过20000 将被单独打包
          cacheGroups: {
            vendor: {
              test: /[\/]node_modules[\/]/,
              name (module) {
                // 首先获取packageName
                const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1]
                return `npm.${packageName.replace('@', '')}`
              }
            }
          }
        }
      }
      Object.assign(config, {
        optimization
      })
    } 
  }

2、配置使用 CDN 方式引入资源库

对于一些第三方依赖,例如 elemet-ui 可以通过cdn 引入的方式,减少包体的体积:

js 复制代码
  // vue.config.js
  configureWebpack: config => {
    config.resolve = {
      // 使用 CDN 的包不用打包到文件中
      externals: {
        // 这里的 element-ui 是 import xx from yy 中的 yy 包名。ELEMENT 则是文件导出的全局变量名字
        'element-ui': 'ELEMENT',
      },
    },
  },

可以直接在public 下的 index.html 中直接引入 cdn 地址。也可以通过如下方式挂载:

js 复制代码
  // vue.config.js
  chainWebpack: config => {
    // 添加 CDN 参数到 htmlWebpackPlugin 配置中
    config.plugin('html').tap(args => {
      args[0].cdn = {
        js: [
          'https://xx.com/CDN/js/index-element-ui@2.13.0.js',
        ],
        css: [
          'https://xx.com/CDN/css/element-ui2.13.0/index.css',
        ],
      };
      return args;
    });
  },
js 复制代码
<!DOCTYPE html>
<html lang="zh">
  <head>
    <!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
    <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>" type="text/javascript"></script>
    <% } %>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

3、打包时去除打印信息

  1. 首先下载相关插件 uglifyjs-webpack-plugin

    npm i -D uglifyjs-webpack-plugin

  2. 在vue.config.js文件中引入,并在 configureWebpack 的 optimization中添加如下代码

javascript 复制代码
const UglifyPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
 configureWebpack: (config) => {
    if (process.env.NODE_ENV === 'production') {
      // 为生产环境修改配置...
      config.mode = 'production'
      // 将每个依赖包打包成单独的js文件
      let optimization = {
        /*以下代码适用于uglifyjs-webpack-plugin 2.1.1及以前的版本*/
        minimizer: [new UglifyPlugin({
          uglifyOptions: {
            compress: {
              warnings: false,
              drop_console: true, // console
              drop_debugger: false,
              pure_funcs: ['console.log'] // 移除console
            }
          }
        })]
      }
      Object.assign(config, {
        optimization
      })
    }
   
  }
}

新版uglifyjs-webpack-plugin需写成以下方式

yaml 复制代码
minimizer: [new UglifyPlugin({
    uglifyOptions: {
        warnings: false,
        compress: {
            drop_console: false, // console
            drop_debugger: false,
            pure_funcs: ['console.log'] // 移除console
        }
    }
})]

4、开启gizp压缩

gizp压缩是一种http请求优化方式,通过减少文件体积来提高加载速度。html、js、css文件甚至json数据都可以用它压缩,可以减小60%以上的体积。webpack在打包时可以借助 compression-webpack-plugin 实现gzip压缩。

  1. 安装依赖 compression-webpack-plugin

    npm i -D compression-webpack-plugin

  2. vue.config.js配置

arduino 复制代码
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
  configureWebpack: (config) => {
    if (process.env.NODE_ENV === 'production') {
      // 为生产环境修改配置...
      config.mode = 'production';
      config.plugins = [
        ...config.plugins,
        new CompressionPlugin({
          test:/.js$|.html$|.\css/, //匹配文件名
          threshold: 10240,//对超过10k的数据压缩
          deleteOriginalAssets: false //不删除源文件
        })
      ]
    } else {
      // 为开发环境修改配置...
      config.mode = 'development';
    }
    
  }
}
  1. 对应的服务器nginx 也要做相应的配置, 资源响应头中出现 Content-Encoding: gzip 则代表配置成功
sh 复制代码
http {
   gzip on; # 开启 gzip 压缩
   gzip_static on; # 若存在静态 gz 文件,则使用该文件
   gzip_min_length 10k; # 设置允许压缩的页面最小字节数
   gzip_buffers 16 8k; # 设置用于处理请求压缩的缓冲区数量和大小
   gzip_comp_level 1; # 设置压缩级别 1-9,数字越大,压缩后的大小越小,也越占用CPU,花费时间越长
   # 对特定的 MIME 类型生效, 其中'text/html'被系统强制启用
   gzip_types application/javascript text/css font/ttf font/x-woff;
   gzip_vary on; # 是否在 http header中 添加 Vary:Accept-Encoding, on | off
   gzip_http_version 1.1; # 在 http/1.1 的协议下不开启压缩
}

5、图片压缩 image-webpack-loader

  1. 下载image-webpack-loader

    npm install --save-dev image-webpack-loader

  2. 在vue.config.js中修改相关配置。4M的图片使用默认设置压缩成1.4M,自定义的设置可以更小

yaml 复制代码
module.exports = {
  ...
  // 默认设置
  const defaultOptions = {
      bypassOnDebug: true
  }
 //  自定义设置
  const customOptions = {
      mozjpeg: {
        progressive: true,
        quality: 50
      },
      optipng: {
        enabled: true,
      },
      pngquant: {
        quality: [0.5, 0.65],
        speed: 4
      },
      gifsicle: {
        interlaced: false,
      },
      // 不支持WEBP就不要写这一项
      webp: {
        quality: 75
      }
  }
  chainWebpack: config => {
    config.module.rule('images') 
        .test(/.(gif|png|jpe?g|svg)$/i)
        .use('image-webpack-loader')
        .loader('image-webpack-loader')
        .options(customOptions)
        .end() 
  }
}

6、移动端 .px2rem 响应样式

  1. 安装

npm i -S lib-flexible postcss-px2rem

  1. 引入 lib-flexible

在项目入口中main.js 中引入lib-flexible

arduino 复制代码
import 'lib-flexible' 

# 注意事项: 由于flexible会动态给页面header中添加<meta name='viewport' >标签,所以务必请把目录 public/index.html 中的这个标签删除!!
  1. 配置postcss-px2rem。在项目 vue.config.js 中进行如下的配置:
css 复制代码
module.exports = { 
    css: {
        loaderOptions: { 
            css: {}, 
            postcss: { 
                plugins: [ require('postcss-px2rem')({ remUnit: 37.5 }) 
                ] 
            } 
        } 
    } 
}

7、通过 DLLPlugin 和 DLLReferencePlugin 拆分 bundles,提升构建速度

DllPluginsplitChunks功能很像,也能将第三方组件拆分出来,打包成一个或几个包,用于长期缓存且能加速打包过程。其实这样看起来,splitChunks 就是 DllPlugin 的自动版本。那么它们的差异在于:

  1. DllPlugin 需要 设置打包的配置文件,并先于项目打包将第三方组件打包;
  2. Dllplugin 在拆分项目模块的时候,不经过打包过程。所以能加快打包速度。(这个其实可以使用 webpack 中的 external 来排除打包某些组件,然后通过cdn链接将对应的组件引入页面,达到相同效果);
  3. 如果库可以按需加载,Dllplugin 将不能按需加载,它方式是全量的引入的,而 splitChunks 可以按需加载地打包。

如下是 webpack.dll.config.js 文件中的配置,通过在 package.json 文件中增加如下执行命令并执行 npm run dll,可以生成 react-manifest.json(包含抽取的文件路径映射关系) 和 react.dll.js (抽离的所有依赖打包成的独立文件)

js 复制代码
...
"scripts": {
  ...
  "dll": "webpack --config config/webpack.dll.config.js",
}
js 复制代码
// webpack.dll.config.js
const webpack = require('webpack'),
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const resolvePath = path.join(__dirname, "../lib"); 

module.exports = {
    mode: "production",
    resolve: {
        extensions: [".js", ".jsx"]
    },
    entry: {
        react: ['react', 'react-dom', 'react-router-dom', 'redux', 'react-redux'],
    },
    output: {
        path: resolvePath, // 抽离的所有依赖打包成的独立文件路径
        filename: "[name].dll.js",
        library: "lib_[name]"
    },
    plugins: [
        new CleanWebpackPlugin(), // 清除之前生成的dll文件
        new webpack.DllPlugin({
            context: process.cwd(),
            path: path.resolve(resolvePath, "[name]-manifest.json"), // 包含依赖文件路径的映射关系
            name: 'lib_[name]'
        })
    ]
};

执行 npm run dll 之后,再运行项目打包命令,将对应的 dll 打包进去。

js 复制代码
// vue.config.js
module.export = {
  ...
  plugins: [
    ...
    new webpack.DllReferencePlugin({
        context: process.cwd(),
        manifest: require(path.join(__dirname, '../lib/react-manifest.json')) 
      }),
    ...
  ]
  ...
}

目前的项目开发中推荐使用通过webpack 中的 external 来排除打包某些组件,然后通过cdn链接将对应的组件引入页面,如上述的第2种优化方案。

三、多页面应用开发与配置

1. 创建项目

通过vue-cli3脚手架进行创建

vue create 项目name,选择自己需要的依赖并进行下一步

2. 搭建文件结构

  • 新建pages存放每个页面相关内容
  • pages---下创建相关页面存放文件
  • 页面文件内放入main.js 与 router.js

3. 配置路由

javascript 复制代码
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

const routes = [
    {
        path: '/login',
        name: 'login',
        component: () => import('./views/index.vue'),
        // component:()=>import('./reset.vue'),
        meta: {
            title: '这里是动态title'
        },
        children: [
            /*  
            单页面应用的写法     
            {
                path: 'reset',
                name: 'reset',
                component: () => import('./reset.vue')
            
            },
            */
          
            //  ***多页面的写法
            {
                path: 'editJobs',
                components: {
                    'editJobs': () => import('./views/reset.vue'),
                },
            },],

    }
]
export default new VueRouter({
    mode: 'hash',
    base: process.env.BASE_URL,
    routes
})

4. 配置vue.config.js

javascript 复制代码
// 打包多入口文件基本配置
function getPagesInfo() {
    let pages = {}
    const glob = require('glob') // 引入glob模块,用于扫描全部src/pages/**/main.js(返回的是一个数组)
    glob.sync('src/pages/**/main.js').forEach((entry, i) => {
        let name = entry.slice(10, -8)
        pages[name] = {
            entry: entry,
            template: 'public.index.html',
            filename: name + '.html',
            title: '',
            chunks: ["chunk-vendors", "chunk-common", name]
        }
    })
    return pages
}
module.exports = {
    pages: getPagesInfo(),
    publicPath: './kk',
    assetsDir: 'static',
};

四、vue.config.js 配置的完整代码

javascript 复制代码
// 打包多入口文件基本配置
let developmentPath = './'; // 开发环境-npm run serve时引用文件路径
let productionPath = './'; // 生产环境-npm run build打包后引用文件路径
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')//生产环境取消打印
const CompressionWebpackPlugin = require('compression-webpack-plugin')//gzip压缩
const productionGzipExtensions = ['js', 'css']
const Version = 'V6.1'
const Timestamp = new Date().getTime()
function getPagesInfo() {
    let pages = {}
    const glob = require('glob') // 引入glob模块,用于扫描全部src/pages/**/main.js(返回的是一个数组)
    glob.sync('src/pages/**/main.js').forEach((entry, i) => {
        let name = entry.slice(10, -8)
        pages[name] = {
            entry: entry,
            template: 'public.index.html',
            filename: name + '.html',
            title: '',
            chunks: ["chunk-vendors", "chunk-common", name]
        }
    })
    return pages
}

// 打包相关
module.exports = {
    pages: getPagesInfo(),//多页面应用配置
    publicPath: process.env.NODE_ENV === 'production' ? productionPath : developmentPath, 
    assetsDir: 'static', // 静态资源大包位置
    outputDir: __dirname + '/server/dist', //build之后静态文件输出路径
    lintOnSave: process.env.NODE_ENV !== 'production', // 打包的时候eslint-loader检查 
    productionSourceMap: false, // source map 检查
    // 启动服务器
    devServer: {
        index: '/login.html',   //默认打开文件
        open: true,             //自动打开浏览器
        host: 'localhost',      //默认打开域名
        port: 8080,             //默认打开端口号
        https: false,           //开启关闭https请求
        hotOnly: false,         //热更
        // 反向代理
        proxy: {
            // 配置跨域
            '/api': {
                target: 'http://dev.aabb.cn:8082/', //代理地址,这里设置的地址会代替axios中设置的baseURL
                ws: true,   //// proxy websockets
                changeOrigin: true,// 如果接口跨域,需要进行这个参数配置
                pathRewrite: {                //pathRewrite方法重写url
                    '^/api': '/',
                },
            },
        },
    },
    
    // webpack 配置链式配置
    chainWebpack: (config) => {
        // 1、取消预加载增加加载速度
        config.plugins.delete('preload')
        config.plugins.delete('prefetch')

        // 2、vue中使用SVG图标,并且想批量导入,然后需要使用的时候直接添加就可以
        config.module
            .rule('svg')
            .exclude.add(resolve('src/assets/icons'))
            .end()
        config.module
            .rule('icons')
            .test(/.svg$/)
            .include.add(resolve('src/assets/icons'))
            .end()
            .use('svg-sprite-loader')
            .loader('svg-sprite-loader')
            .options({
                symbolId: 'icon-[name]',
            })
            .end()

        // 3、图片处理
        const imagesRule = config.module.rule('images')
        imagesRule.uses.clear() //清除原本的images loader配置
        imagesRule
            .test(/.(jpg|gif|png|svg)$/)
            .exclude.add(path.join(__dirname, '../node_modules')) //不对node_modules里的图片转base64
            .end()
            .use('url-loader')
            .loader('url-loader')
            .options({ name: 'img/[name].[hash:8].[ext]', limit: 6000000 })

        config.optimization.splitChunks({
            cacheGroups: {
                vendors: {
                    name: 'chunk-vendors',
                    minChunks: pageNum,
                    test: /node_modules/,
                    priority: -10,
                    chunks: 'initial',
                },
                elementUI: {
                    name: 'chunk-elementUI',
                    priority: 20, 
                    test: /[\/]node_modules[\/]_?element-ui(.*)/,
                },
                commons: {
                    name: 'chunk-commons',
                    test: resolve('src/components'),
                    minChunks: 3, //  minimum common number
                    priority: 5,
                    reuseExistingChunk: true,
                },
            },
        })
    },
    
    // webpack 配置
    configureWebpack: (config) => {
          // 为生产环境修改配置
        if (process.env.NODE_ENV === 'production') {
      
            config.plugins.push(
                  // 1、取消打印
                new UglifyJsPlugin({
                    uglifyOptions: {
                        compress: {
                            drop_debugger: true,// 生产环境自动删除debugger
                            drop_console: true, // 生产环境自动删除console
                        },
                        warnings: false,
                    },
                    sourceMap: false,   //关掉sourcemap 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
                    parallel: true, //使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
                }),

                // 2、gzip压缩
                new CompressionWebpackPlugin({
                    filename: '[path].gz[query]',
                    algorithm: 'gzip',
                    test: new RegExp('\.(' + productionGzipExtensions.join('|') + ')$'),
                    threshold: 10240,
                    minRatio: 0.8,
                })
            )
        }

        // 在这里配置后,减少了压缩的包内容,需要在public/index.html通过cdn方式再引入,注意对应的版本
        config.externals = { 
            vue: 'Vue',
            'vue-router': 'VueRouter',
            vuex: 'Vuex',
            axios: 'axios',
            jquery: '$',
            moment: 'moment',
            'mint-ui': 'MINT'
        },

        // 别名配置
        Object.assign(config, {
            // 开发生产共同配置
            resolve: {
                alias: {
                '@': path.resolve(__dirname, './src'),
                '@c': path.resolve(__dirname, './src/components'),
                '@p': path.resolve(__dirname, './src/pages')
                }
            }
        }),

        config.output.filename = `[name].${Version}.${Timestamp}.js`  //打包生成的文件
        config.output.chunkFilename = `[name].${Version}.${Timestamp}.js`
    },
    // css相关
    css: {
        loaderOptions: {
            // 配置全局sass
            scss: {
                additionalData: `@import "@/assets/css/reset.scss";@import "@/assets/css/globle.scss";`  //注意配置的键名
            },
            // lib-flexible 移动端响应式配置
            postcss: {
                plugins: [
                    require('postcss-px2rem')({
                        remUnit: 37.5
                    })
                ]
            }
        }
    },
    parallel: require('os').cpus().length > 1, // 是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
    pwa: {}, // PWA 插件相关配置 
    pluginOptions: {},  // 第三方插件配置
};
相关推荐
青云交1 小时前
大数据新视界 -- 大数据大厂之 Impala 性能优化:跨数据中心环境下的挑战与对策(上)(27 / 30)
大数据·性能优化·impala·案例分析·代码示例·跨数据中心·挑战对策
Winston Wood4 小时前
Perfetto学习大全
android·性能优化·perfetto
EterNity_TiMe_4 小时前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
程序猿进阶5 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
契机再现5 小时前
babel与AST
javascript·webpack·typescript
工业甲酰苯胺13 小时前
Redis性能优化的18招
数据库·redis·性能优化
前端李易安17 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
无尽的大道19 小时前
深入理解 Java 阻塞队列:使用场景、原理与性能优化
java·开发语言·性能优化
loey_ln19 小时前
webpack配置和打包性能优化
前端·webpack·性能优化
Amd7941 天前
Nuxt.js 应用中的 webpack:compile 事件钩子
webpack·自定义·编译·nuxt.js·构建·钩子·逻辑