const path = require('path')
const HtmlWebpackPlugin = require("html-webpack-plugin") // 自动生成index.html文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin") //用MiniCssExtractPlugin.loader 替代style-loader 将css合并并成一个 并统一用一个link标签加载
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); //压缩打包后的css
const TerserWebpackPlugin = require("terser-webpack-plugin");
const webpack = require("webpack")
// const { ModuleFederationPlugin } = webpack.container // 模块联邦
// const WorkBoxPlugin = require("workbox-webpack-plugin"); //离线运行
// const { BundleAnalyzerPlugin } = require("webapck-bundle-analyzer")
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
// 常用代码分离
// 入口起点:使用 entry 配置手动地分离代码。
// 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。
// 动态导入:通过模块的内联函数调用来分离代码。
// 1. 单入口,直接是个字符串
// entry:"./src/index.js",
//2. 配置入口节点 another,使用entry配置手动分离代码, 配合SplitChunksPlugin使用splitChunks自动all分隔
//缺点:多入口的话,多入口的共享文件会重复在每个包里打包 推荐使用dependOn
// entry:{
// index:"./src/index.js",
// another: "./src/index-another.js"
// },
// 3.防止重复分离
//1、 配置入口节点 another,使用entry配置手动分离代码,缺点:多入口的话,多入口的共享文件会重复在每个包里打包
//2. 防止重复分离: 使用entry dependence 或者optimization.splitChunks 去重和分离公共代码
//3. 动态导入 : 通过调用模块的内联函数 import() 分离代码 import(/* webpackChunkName: 'math', webpackPrefetch: true */
//4 webpackPrefetch:true预获取,空闲时加载 ;webpackPreload:true 类似懒加载 使用时候加载
// import(/* webpackChunkName: 'math', webpackPrefetch: true */
// './math.js').then(({ add }) => {
// console.log(add(4, 5))
// })
// })
// entry:{
// index:{
// import:"./src/index.js",
// dependOn:'shared' //dependOn当前入口的依赖项,shared可以把共享的文件定义出来 防止重复分离: 使用entry dependOn 可以在多个chuck直接共享模块
// },
// another:{
// import:"./src/index-another.js",
// dependOn:'shared'
// },
// shared: 'lodash' // 当上面两个模块有loadsh时 会抽离出来 并生成名为'shared'的chuck
// },
// 4.多页应用,按功能模块划分, 多入口文件打包
entry: {
main1: {
import: ["./src/app1.js", "./src/app2.js"],
dependOn: "dependName",
filename: "chanel1/[name].js"
},
main2: {
import: "./src/app3.js",
dependOn: "dependName",
filename: "chanel2/[name].js"
},
dependName: {
import: "lodash",
filename: "common/[name].js"
}
},
output: {
filename: 'scripts/[name].[contenthash].js', // [name] 可以拿到入口里面chuck的这个key的名字
path: path.resolve(__dirname, './dist'),
clean: true,
publicPath: '/', //通常是CDN地址 例如,你最终编译出来的代码部署在 CDN 上,资源的地址为: 'https://AAA/BBB/YourProject/XXX',那么可以将生产的 publicPath 配置为: //AAA/BBB/。
assetModuleFilename: 'image/[contenthash][ext]', //导出文件的文件名 hash .后缀
library: { // 配置支持amd commonJS esMoudle 开发npm包时使用
name: '[name]',
type: "umd"
},
globalObject: "globalThis" //出来library报错
},
mode: isDev ? 'development' : 'production', // mode 配置项,告知 webpack 使用相应模式的内置优化。
devtool: isDev ? 'cheap-module-eval-source-map' : 'source-map',
// devtool 中的一些设置,可以帮助我们将编译后的代码映射回原始源代码。不同的值会明显影响到构建和重新构建的速度。
//"inline-source-map"
// 能够定位到源码的行即可,因此,综合构建速度,在开发模式下,设置 devtool 的值是 cheap-module-eval-source-map。
// 生产环境可以使用 none 或者是 source-map,使用 source-map 最终会单独打包出一个 .map 文件,我们可以根据报错信息和此 map 文件,进行错误解析,定位到源代码。
// source-map 和 hidden-source-map 都会打包生成单独的 .map 文件,区别在于,source-map 会在打包出的js文件中增加一个引用注释,以便开发工具知道在哪里可以找到它。hidden-source-map 则不会在打包的js中增加引用注释。
// 但是我们一般不会直接将 .map 文件部署到CDN,因为会直接映射到源码,更希望将.map 文件传到错误解析系统,然后根据上报的错误信息,直接解析到出错的源码位置。
devServer: {
static: path.resolve(__dirname, './dist'), //指向服务的物理路径
port: '8888', //默认是8080
compress: true, //是否启用 gzip 压缩
hot: true, //热更新HMR 默认开启
liveReload: true, //热加载 默认开启
proxy: { //网络代理 处理本地请求跨域
'/api': {
target: "http://stvideo-test.kaixinkan.com.cn", // 后台接口域名
ws: true, //如果要代理 websockets,配置这个参数
secure: true, // 如果是https接口,需要配置这个参数
changeOrigin: true, //是否跨域
}
},
headers: { //请求头信息
"X-Access-Tonke": "abcde"
},
historyApiFallback: true //单页面应用路由history模式 浏览器把资源当静态资源请求导致页面404报错 时使用
// quiet: false, //默认不启用
// inline: true, //默认开启 inline 模式,如果设置为false,开启 iframe 模式
// stats: "errors-only", //终端仅打印 error
// overlay: false, //默认不启用
// clientLogLevel: "silent", //日志等级
},
// 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见 ,不建议开启
// stats: "errors-only" ,终端中仅打印出 error,注意当启用了 quiet 或者是 noInfo 时,此属性不起作用。
// 启用 overlay 后,当编译出错时,会在浏览器窗口全屏输出错误,默认是关闭的。
// asset module 资源模块 处理非js文件 引入外部资源 是一种模块类型
// 优化:将loader应用于最少数量的应用模块 使用includes和excludes
// 每个loader和plugin都有启动时间,尽量少的使用工具
module: {
rules: [
{
test: /\.js$/,
use: [{
loader: "babel-loader", // 将js转译为低版本 es6 =>es5
options: {
presets: [
[
'@babel/preset-env', //通过package.json的browserslist来转义浏览器不支持的特性 包含大多数程序转化 很多babel插件都在这里 如polyfill等等一些兼容es3等 低版本的插件
{
targets: [ //约定 浏览器的最后1个版本 且 世界上大于 >1的 用户使用的浏览器
"last 1 version",
"> 1%"
],
useBuiltIns: "usage", //默认false会将babel-polyfill全局导入
corejs: 3 // 添加依赖使用 v3版本的corejs 处理报错
}
]
],
plugins: [
[
"@babel/plugin-transform-runtime", // babel插件 ,处理regenertorRuntime报错。 兼容async/await
{
"corejs": 3
}
]
]
},
},
// {
// loader:"thread-loader", worker-pool worker池 针对比较大的loader 提升打包速度
// options:{
// workers:2
// }
// }
],
exclude: /node_modules/
},
{
test: /\.png$/, //识别出哪些文件
type: 'asset/resource',
// 资源模块类型
//asset/resource发送单独文件导出url asset/inline导出资源的Data URL asset/source 导出资源的源码 asset导出Data URL和发送单独一个文件间单独选择
generator: { //同output的assetModuleFilename ,但是比他优先级高
filename: 'image/[contenthash][ext]'
}
},
{
test: /\.png$/,
type: 'asset', //asset导出Data URL和发送单独一个文件间单独自动选择 默认临界值小于8k 走inline生成base64 否组导出文件url
parser: {
dataUrlCondition: { //修改自动选择临界值 由默认8k 修改为 4M
maxSize: 4 * 1024
}
}
},
{
test: /\.(css|less)$/,
// use:["style-loader","css-loader","less-loader"]
// 逆序调用 先加载less loade解析less文件 在用css-loader转换成css 最后再运行style-loader 将css文件插入页面header标签中
use: [
MiniCssExtractPlugin.loader, // 用MiniCssExtractPlugin.loader 替代style-loader 将css合并并成一个 并统一用一个link标签加载
{
loader: "css-loader",
options: {
modules: true, //css模块话 防止类名重复给类名转换为字符串,vue 中css模块使用该思想
}
},
"postcss-loader", //css兼容浏览器 给css自动加浏览器前缀 需要配合autoprefixer插件使用
"less-loader",
],
exclude: /node_modules/
},
{
test: /\.ts$/,
use: "ts-loader", //处理ts
exclude: /node_modules/
},
{
test: /\.(woff|woff2|ttf|otf)$/i, //处理字体font
type: "asset/resource"
}
]
},
// 加载webpack 插件
plugins: [
new HtmlWebpackPlugin({ //自动生成index.html文件
title: "我是title",
template: "./index.html", //打包后使用的模板文件
filename: "chanel1/app.html", // 打包生成的新文件名
inject: 'body', //script标签查入到body中
chunks: ["mian1", "mian2"], // 指定打包后 引入的entry模块
publicPath: 'http://www.a.com',
}),
// 通过new多个HtmlWebpackPlugin分包
new HtmlWebpackPlugin({ //自动生成index.html文件
title: "我是title",
template: "./index.html", //打包后使用的模板文件
filename: "chanel2/app2.html", // 打包生成的新文件名
inject: 'body', //script标签查入到body中
chunks: ["dependName"], // 指定打包后 引入的entry模块
publicPath: 'http://www.b.com',
}),
new MiniCssExtractPlugin({ //将所有的css文件抽离合并成一个单独的文件main.css 用一个link标签加载
filename: "styles/[contenthash].css" //修改打包后的main.css的文件名
}),
new webpack.ProvidePlugin({
_: "lodash" //全局引入lodash 不需要再在业务代码中import 可直接使用_
}),
// new ModuleFederationPlugin({ // 模块联邦
// name:"nav",
// filename:"remoteEntry.js",
// remotes:{}, //使用 其他人的模块
// exposes:{ //暴露模块给其他人使用
// "./Header": './src/index.js' //key为其他人的引用路径,value为本地项目路径
// }
// }),
// new WorkBoxPlugin.GenerateSW({ 离线运行
// clientsClaim:true,
// skipWaiting:true
// }),
// new BundleAnalyzerPlugin(), // 打包分析工具
require('autoprefixer'), // 配合postcss-loade使用给css 添加浏览器前缀
require('postcss-nested') //允许 css 样式嵌套 类似sass less
],
// 减少resolve.modules ,resolve.extensions ,resolve.mainFiles 中条目数量,因为他们会增加文件系统的调用次数
resolve: {
extensions: ['.json', '.js', '.vue'], // 设置查找的拓展名的优先级,引用模块出现同名文件时候,调用的文件后缀优先顺序.json>.js>.vue
alias: {
"@": path.resolve(__dirname, './src') //项目中可以用 @ 直接访问src根目录
}
},
externalsType: "script", // vue-router这种 value 为数组时候使用
externals: { //第三方资源库不打包 通过在html用cdn引入 减小打包体积
"vue": 'Vue', //key 为页面中from引入的值, value为第三库挂在在window上的对象
'vue-router': [
'https://h5.vivo.com.cn/shortVideo/vuerouter/vue-router.min.js', //这种value为数组写法 可以直接在html中生成 script链接,需配合 externalsType:"script" 告知已script标签插入
'VueRouter'
],
'axios': 'axios',
'vuex': 'Vuex',
'lottie-web': 'lottie',
'better-scroll': 'BetterScroll',
'swiper/swiper-bundle.min.js': 'Swiper'
},
// 用于优化打包结果的对象。
optimization: {
minimizer: [
new CssMinimizerPlugin(), //压缩打包后的css
new TerserWebpackPlugin() //压缩代码
],
usedExports: true, //开启 Tree Shaking 功能,用于删除未被使用的代码 package.json中设置sideEffects: true或指定文件sideEffects: ["*.css"]文件 智能删除
splitChunks: { //去重和分离公共代码
// async表示只从异步加载得模块(动态加载import())里面进行拆分
// initial表示只从入口模块进行拆分
// all表示以上两者都包括
// chunks:"all"
cacheGroups: { //缓存组 ,缓存一些不经常修改的第三方库
vendor: {
test: /[//\node_modules[//\]]/, //第三方组件库 都在node_modules里面
name: "vendor",
chunks: "all"
}
}
}
},
//性能方面的一些配置
performance: {
hints: 'warning',
//入口起点的最大体积
maxEntrypointSize: 50000000,
//生成文件的最大体积
maxAssetSize: 30000000,
//只给出 js 文件的性能提示
assetFilter: function (assetFilename) {
return assetFilename.endsWith('.js');
}
}
}