Section-3 webpack高级概念

section-3.1 Tree Shaking 概念详解

当引入一个模块的时候,只引入需要的代码,而不是所有的代码都打包进去,那么这时候就需要把不需要的代码“摇”掉,这个时候可以使用Tree Shaking

// match.js
export const add = (a, b) => {
    console.log(a + b);
}

export const minus = (a, b) => {
    console.log(a - b);
}
// index.js
import { add } from './match.js';
add(1, 2);

上面我们在index.js里只需要使用add这个方法,但是打包的时候会发现mian.js里minus依然被打包进去,这个就需要配置webpack对代码进行剔除,在webpack.config.js中添加 optimization(优化)配置,设置 usedExports 为 true

// webpack.config.js
// 为了容易阅读,其他的webpack配置、插件等都被我删除掉,但并非不需要,babel的options也被我挪到.babelre文件里,不贴出来

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    },
    optimization: {
         // 这里告知webpack,如果没有export导出,则可以移除未使用的导出(不是移除整个模块,只是移除没被用到的export代码)
        usedExports: true 
    }
}

在package.json中添加

"sideEffects": false

如果到这里还不知道package.json各个字段的配置,可以参考:npm的package.json中文文档
这里需要注意,Tree Shaking 只支持 ES Module 模块引入,也就是只支持静态引入,commonJS是不支持的,比如下面这样动态引入是不支持的

const add = require(./match.js)

这个时候看打包后的 main.js ,会发现代码还是被打包出来,这是因为在 development 环境中,代码是不会被剔除的,但会有提示

/*! exports provided: add, minus */
/*! exports used: add */

这个时候我们把配置改成 production ,需要进行下面的设置,把 optimization 配置删除即可

// package.json
// css 文件是 import './index.css',不需要剔除
// babel 也是没有 export 出任何东西的,但也不需要剔除
"sideEffects": ["@babel/polyfill", "*.css"]

section-3.2 Develoment 和 Production 模式的区分打包

对到开发中,需要对dev和prod环境分开处理打包,参考vue中的build,分别建立base/dev/prod三个配置来进行打包处理
webpack.base.js -- 公用的webpack配置
webpack.dev.js -- 开发环境webpack配置
webpack.prod.js -- 生产环境webpackp配置

|- build
 |- webpack.base.js
 |- webpack.dev.js
 |- webpack.prod.js
// webpack.base.config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    entry: {
        main: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, '../dist')
    },
    module: {
        rules: [  //模块打包规则
            {
                test: /\.js$/,
                exclude: /node_modules/,  // 如果代码是在node-modules,则排除
                loader: "babel-loader"   //webpack与babel的桥梁,并不会进行解析
            },{
                test: /\.(woff|eot|ttf|svg)$/,
                use: {
                    loader: 'file-loader',
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: 'icon-font/'
                    }
                }
            },{
                test: /\.(jpg|png|gif)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: 'images/',
                        limit: 2048  //是否小于2KB
                    }
                }
            },{
                test: /\.scss$/,
                use: [
                    'style-loader',
                    {
                        loader: "css-loader",
                        options: {
                            importLoaders: 2
                        }
                    },
                    'sass-loader',
                    'postcss-loader']
            },{
                test: /\.css$/,
                use: [
                    'style-loader',
                    "css-loader",
                    'postcss-loader'
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html'
        }),
        new CleanWebpackPlugin()
    ],
}

因为我们需要的不是某一个配置文件,而是该文件与base文件合并后的配置,所以需要安装webpack-merge,执行npm i webpack-merge -D ,在 dev 和 prod 两个配置里都加入webpack-merge

// webpack.dev.js
const webpack = require('webpack');
const merge = require('webpack-merge');  // 加入 merge 模块
const commonConfig = require('./webpack.base.js');  // 加入 base 配置

const devConfig = {
    mode: "development",  // production
    devtool: 'cheap-module-eval-source-map',
    devServer: {
        contentBase: './dist',
        open: true,
        proxy: {  // 接口代理
            '/api': 'http://localhost:80'
        },
        hot: true,     //webpack-dev-server开启热更新
        hotOnly: true  //html没生效,浏览器不刷新
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    optimization: {
        usedExports: true
    }
}

module.exports = merge(commonConfig, devConfig);  // 合并两者配置并输出
// webpack.prod.js
const merge = require('webpack-merge');
const commonConfig = require('./webpack.base.js');

const prodConfig = {
    mode: "production",
    devtool: 'cheap-module-source-map'
}

module.exports = merge(commonConfig, prodConfig);

对应的修改package.json文件,来对不同环境执行不同命令,对到里面的--config指定配置文件,自行百度或查看我之前第一章第四节 section-1.4 使用webpack的配置文件

"scripts": {
    "start": "webpack-dev-server",
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
},

section-3.3&3.4 Webpack 和 Code Splitting

Code Splitting:代码分割,将页面逻辑和依赖分开打包,因为业务逻辑是多变的,依赖一般是不变的(比如使用了loadash/jQuery这些),而且这种做法虽然多了一个请求,但是现代浏览器是并行下载的,对到大的文件分开下载会比直接下载一个大型文件更快


这里以loadash为例(执行npm i loadash -D下载包)
逻辑代码(dafault),本节没写的index.js代码都是以下这段,以同步加载的形式,下面说的defalut都是这个

// index.js 未做代码分割,会将 loadash 和 业务逻辑打包到一个 js 里,
import _ from 'loadash';

console.log(_.join(['a', 'b', 'c'], '*'));
console.log(_.join(['a', 'd', 'z'], ','));

代码分割也可以手动制作

// loadash.js
import _ from 'loadash';
window._ = _;

// index.js
console.log(_.join(['a', 'b', 'c'], '*'));
console.log(_.join(['a', 'd', 'z'], ','));
// webpack.config.js

entry: {
    loadash: './src/loadash.js',
    main: './src/index.js'
}

逻辑代码为 dafault,webpack 4 默认支持代码分割配置,具体对到 splitChunks 推荐阅读一下这篇文章

// webpack.config.js

entry: {
    main: './src/index.js'
},
optimization: {
    splitChunks: {
        chunks: 'all'
    }
}

这样就完成了对同步代码的代码分割

|- dist
 |- index.html
 |- main.js
 |- vendors~main.js  //这个就是分割出来的 loadash

对到异步代码(import),无需做任何配置,会自动进行代码分割,也就是不需要写 optimization

// index.js
// /* webpackChunkName:"loadash" */ 是魔法注释,将这个分割出来的文件起名为ladash
function getComponent() {
    return import(/* webpackChunkName:"loadash" */ 'lodash').then(({ default: _}) => {  // 以异步的形式加载 loadash
        let elm = document.createElement('div');
        elm.innerHTML = _.join(['Dell', 'Lee'], '-');
        return elm
    })
}

getComponent().then(elm => {
    document.getElementById('root').appendChild(elm);
})

由于异步加载是实验性做法,babel并不支持,所以这里我们需要安装一个babel官方的代码分割插件 syntax-dynamic-import 的插件,还有一个非官方的代码分割插件“babel-plugin-dynamic-import-webpack”,个人尝试后不可用,或许是因为版本问题,这里不进行深究,执行npm i @babel/plugin-syntax-dynamic-import -D 进行安装

// .babelrc
{
    presets: [[
        "@babel/preset-env", {
            corejs: 3,
            useBuiltIns: "usage",
            targets: {
                chrome: "67"
            }
        }]
    ],
    plugins: ["@babel/plugin-syntax-dynamic-import"]
}

这样就完成了对异步代码的代码分割

|- dist
 |- index.html
 |- main.js
 |- 0.js  //这个就是分割出来的 loadash

对到异步的刚好自己粗心遇到了问题,但编译不会提示跟报错,注意 import('lodash').then(({ default: _})别写错,因为我把default拼错了,浏览器一直提示 _ 没有模块依赖,然后一直找不到问题,还以为是插件用的不对

section-3.5&3.6 SplitChunksPlugin 配置参数详解

这节主要说一下优化中的代码分割配置,webpack4是默认自带的,除非想用实验性的异步加载模块也被分割出来才需要安装上一节的babel插件
split-chunks-plugin 如果为空(也就是只有一个{}),则默认会有一段配置,具体看官网,这里主要对参数进行说明,例子就不搬了,看官网即可。默认配置主要是这四条规则

  1. New chunk can be shared OR modules are from the node_modules folder
    可共享(多处被引用)或来自 node_modules 中的模块
  2. New chunk would be bigger than 30kb (before min+gz)
    模块大于30kb
  3. Maximum number of parallel requests when loading chunks on demand would be lower or equal to 5
    加载模块时,最大的并行请求数大于等于5
  4. Maximum number of parallel requests at initial page load would be lower or equal to 3
    入口文件加载模块时,最大并行请求数大于等于3
optimization: {
    splitChunks: {       // 代码分割
        // 分割条件匹配
        chunks: "all",   // "async"只对异步代码进行打包 "initial"同步
        minSize: 30000,  // 大于30k,正确应该是30720才是30k,进行代码分割
        minChunks: 1,    // 当模块被引入至少一次
        maxAsyncRequests: 5,    // 只能同时加载5个代码块,大于5的时候,前5个会被分割出来
        maxInitialRequests: 3,  // 首页(入口文件)最多只能分割3个
        automaticNameDelimiter: '~',  // 组和文件名直接的拼接符
        name: true,     // 打包生成的文件名是否有效,如果true则走cacheGroups中的规则命名
       
         // 分组
        cacheGroups: {  // 缓存组,不会直接打包文件,会把符合组条件的文件先缓存起来,最后再打包成一个模块
            vendors: {  // 匹配组名,会给打包文件前面加 vendors 入口名
                test: /[\\/]node_modules[\\/]/,  // 检测是否是在node_modules中
                priority: -10,  // 优先级,当某个模块同时满足多个组的时候,按照优先级进行分配打包
                filename: 'vendors.js'  // 会让上面的 automaticNameDelimiter 失效
            },
            default: {
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true,  //如果一个模块已经被打包过,则不再打包
                filename: 'common.js'
            }
        }
    }
}

还有一点,上面的filename,如果是异步加载模块是会报错的,因为在运行中babel只能给分割出来的模块暂时命名为数字(runtime时),所以默认配置里并没有该属性

section-3.7 Lazy Loading 懒加载,Chunk 是什么?

vue中有路由,可以通过在路由中使用箭头函数,来让页面资源异步加载

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
// import City from '@/pages/city/City'
// import Detail from '@/pages/detail/Detail'

Vue.use(Router)

export default new Router({
    routes: [
        {
            path: '/',
            name: 'Home',
            component: Home  // 上面是import同步加载
        },{
            path: '/city',
            name: 'City',
            component: () => import('@/pages/city/City')
        },{
            path: '/detail/:id',  // router动态路由
            name: 'Detail',
            component: () => import('@/pages/detail/Detail')
        }
    ],
    scrollBehavior (to, from, savedPosition) {
        return { x: 0, y: 0 }
    }
})

其实本质依旧是代码分割,只是分割后的chunk只有在需要的时候才会去加载,而不是进入页面的时候并列下载

async function getComponent() {
    const { default: _ } = await import(/* webpackChunkName:"loadash" */ 'lodash');
    const elm = document.createElement('div');
    elm.innerHTML = _.join(['Dell', 'Lee'], '-');
    return elm
}

document.addEventListener('click', () => {
    getComponent().then(elm => {
        document.getElementById('root').appendChild(elm);
    })
})

上面这个例子中,只有点击页面才会去请求分割出来的loadash.js。
对到chunk是什么,说实话我自己都不知道它是什么(应该说我不知道怎么用中文来描述它)
chunk,就是webpack打包后的js文件,或许这叫代码块?

Built at: 2019-05-23 01:02:27
             Asset       Size           Chunks             Chunk Names
        index.html  349 bytes                   [emitted]
           main.js     35 KiB             main  [emitted]  main
vendors~loadash.js   1.35 MiB  vendors~loadash  [emitted]  vendors~loadash
Entrypoint main = main.js

chunk主要有以下三种:

  1. webpack.config.js 当中配置的入口文件(entry)是 chunk,可以理解为entry chunk
  2. 入口文件以及它的依赖文件通过code split (代码分割)出来的也是chunk,可以理解为children chunk
  3. 通过commonsChunkPlugin创建出来的文件也是chunk,可以理解为commons chunk
    webpack4默认自带code splitting 了,已经将CommonsChunkPlugin从webpack v4 legato 中移除,也就是上面优化当中的splitChunks配置

section-3.8 打包分析,Preloading, Prefetching

通过创建 analyze 来查看打包数据分析以及各文件之间的依赖关系
官方提供的三个可视化链接来查看 bundle 分析(bundle analysis),通过在package.json中添加--profile --json > stats.json来生成stats.json文件,这个文件放到官方提供的bundle分析网页中去查看

"scripts": {
    "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js",
    "start": "webpack-dev-server",
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
}

预加载(Preloading)和预提取(Prefetching)

// index.js
document.addEventListener('click', () => {
    import(/* webpackPrefetch: true, webpackChunkName: "click" */ './click.js').then(({default: func}) => {
        func();
    })
})

// click.js
function handleClick () {
    const elm = document.createElement('div');
    elm.innerHTML = "Prefetching Chunk"
    document.getElementById('root').appendChild(elm);
}

export default handleClick;

上面这个例子,在加载index.js的时候,页面并不会加载Prefetching Chunk这样的文字,只有在点击后才会出现文字。这样的点击事件理论上其实是不需要在页面加载的时候直接加载的,只有在点击后这个事件代码才有用(真实项目中比如登录框)
webpack的优化理念其实并非文件缓存,而是代码利用率(代码覆盖率),加载的页面只加载有需要的代码,剩余的代码可以在空闲期再去加载,这就是Prefetching。这也就是splitChunks为什么chunks的默认配置项是async而不是all,因为官方希望我们更多的去关注代码覆盖率的问题

Section-3 webpack高级概念_第1张图片
chrome查看代码覆盖率

因为例子太简单,所以无效代码占据比例较大(真实场景肯定不会是这样的),上图可以看到代码利用率是22.5,如果是直接写在click事件里,代码利用率只有6.6(当然还有一些注释,就不截图了)
Prefeching 其实重点理念是将来可能会用到的代码(也就是跨页面,预先加载后面的流程代码),它实际是在页面头部增加,指示浏览器在空闲时间去加载click.js,这样当需要使用的时候直接从 disk cache 里面取,既不影响当前页面的渲染,又提高了其他页面加载渲染的速度
而Preloading便是预加载,实际是在页面头部增加,错误地使用Preload实际上会损害性能,造成当前页面的阻塞。因为它是和当前页面并行下载文件的,所以官方推荐的是使用Prefeching
当我做demo的时候发现,preload并没有任何效果,页面头部也没任何挂载,也不会产生任何请求,只有在点击的时候才会加载click.js,也就是说 webpackPreload这个配置项可能已经被移除了,即使文档里依然有它的记录,如果有有误请大佬指正

section-3.9 CSS 文件的代码分割

我们对到css/sass/less/stylus,一般在开发环境里都是使用的style-loader,这样的loader会把css in js里的样式直接以style标签的形式插入到页面上。chunk有多少引用到css,则会有多少个style标签,很明显,这样的做法对到线上环境必然是不妥的,这个时候我们就需要把css in js中的样式抽离成单独的文件进行导入,这个时候我们需要使用 MiniCssExtractPlugin
它跟以前webpack3使用的 extract-text-webpack-plugin 相比,最主要的优势在于没有重复的编译(具体等我后面尝试了以后再来修改)
执行npm install --save-dev mini-css-extract-plugin进行安装,由于其hmr支持度不好(甚至需要reloadAll进行重载),所以这里只对生产环境进行配置,去除style-load,改成MiniCssExtractPlugin.loader,由于css不同于less/scss/stylus,并不需要配置importLoaders来进行loader流程重复,所以拆分成两块
当然你不在乎那点编译性能其实这样匹配也行/\.(sa|sc|c)ss$/。毕竟css在这些loader里也能正确识别

// webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');  // css拆分成单文件

module: {
    rules: [
        {
            test: /\.scss$/,
            use: [
                // 'style-loader',
                MiniCssExtractPlugin.loader,
                {
                    loader: "css-loader",
                    options: {
                        importLoaders: 2
                    }
                },
                'sass-loader',
                'postcss-loader'
            ]
        },{
            test: /\.css$/,
            use: [
                // 'style-loader',
                MiniCssExtractPlugin.loader,
                "css-loader",
                'postcss-loader'
            ]
        }
    ]
}

对到线上环境,我们需要的不仅是文件独立,还需要代码合并与压缩(默认会合并,但只是简单的写在同一个文件里,比如两个文件都有一个.style类名的样式,这个文件里会有两个.style的类名样式)

.style{
    background-color: darkseagreen;
}

.style{
    font-size: 18px;
}

这里需要使用 OptimizeCssAssetsPlugin 插件来对代码进行合并压缩
执行 npm install --save-dev optimize-css-assets-webpack-plugin 安装,添加配置,plugins里是为了配置出sourceMap,否则默认不会生成sourceMap,参考文章:mini-css-extract-plugin搭配optimize-css-assets-webpack-plugin不生成source-map问题,当然以个人经验来讲没必要,因为我们会对less/scss/stylus做sourceMap,但应该没见过有人为css做sourceMap吧

const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');  // css压缩

plugins: [
    new MiniCssExtractPlugin({
        filename: '[name].css',
        chunkFilename: '[name].chunk.css'
    }),
    new OptimizeCssAssetsPlugin({
        // 生成sourceMap
        cssProcessorOptions: {
            map: {
                // 不生成内联映射,生成sourceMap
                inline: false,
                // 向css文件添加source-map路径注释
                // 如果没有此项压缩后的css会去除source-map路径注释
                annotation: true
            }
        }
    })
],
optimization: {
    minimizer: [new OptimizeCssAssetsPlugin({})]  // 压缩代码
}

section-3.10 Webpack 与浏览器缓存( Caching )

我们在更改代码后,需要用户重新下载新的业务代码,但由于浏览器会有缓存问题,这样会导致用户的文件得不到更新,甚至出bug。在最早以前,是人为手工的为代码文件名添加后缀或时间戳,后来历史演变(原本想说一下,然后全部被我删掉了)现在是添加文件名加hash的形式
而因为我们做了代码分割,所以没更改到的文件不需要变,只有改变的才需要去重新下载,其他的走缓存即可,因此我们需要使用 contenthash 占位符来标识文件,如字面意思,内容哈希,内容不变哈希也不变,还有其他的占位符自行查看文档

// webpack.prod.js
output: {
    filename: '[name]_[contenthash].js',
    chunkFilename: '[name]_[contenthash].js'
},

webpack通过 runtime 和 manifest 来加载和解析模块,而对到如何清楚模块发生变化,这里我估计得去找--watch那里挖
毕竟,按照文档的说法:runtime负责的是连接模块所需的加载和解析逻辑, Manifest负责解析和加载模块,import 或 require 语句转换为 __webpack_require__方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够查询模块标识符,检索出背后对应的模块。
一个负责查找对应关系,一个负责转换引用方式来让runtime识别出对应的模块,这两个都不是监听文件变化的,所以是的,我看完了以后似懂非懂,还去查找一些博文看了一下,基本可以确定,这两个名词简单来说就是用来确定文件应该去加载哪些模块的,跟监听变化没有半毛钱关系,那块估计去找 --watch或hmr相关的应能找出点东西来
为什么扯这个,因为旧的webpack(不知道哪一版之前),即使文件没变化,hash也会发生变化,导致每次都得重新下载所有文件,这样缓存策略就不够好了,需要将manifest独立出一个文件来,没更改的文件hash才不会发生变化

// webpack.prod.js
optimization: {
    runtimeChunk: {
        name: "chunkName"
    }
}   

最近电脑坏了拿去修,需要15到20天…现在迫不得已拿出5年前的笔记本暂行扛着,打字显示出来的速度都跟不上我打字速度…好想买台19款 imac…

section-3.11 Shimming 的作用

Shimming,也就是垫片,其作用可以简单的理解为帮我们处理一些全局的东西。
假设我们的项目是基于jq和loadash写的业务逻辑,到处都需要用到,那么这个时候我们借助webpack自带的ProvidePlugin,便可以简单的在全局中引入对应的库文件了

// webpack.base.js
const webpack = require('webpack');

plugins: [
    new webpack.ProvidePlugin({
        _: 'lodash'
    })
]
// import _ from 'lodash';  //已经不需要在头部先引入loadash了,ProvidePlugin悄悄帮我们做这一步
console.log(_.join(['a', 'b', 'c'], '*'));
console.log(_.join(['a', 'd', 'z'], ','));

本质上,我们所做的,就是告诉 webpack

如果你遇到了至少一处用到 lodash 变量的模块实例,那请你将 lodash package 包引入进来,并将其提供给需要用到它的模块。

如果我们项目里多次用到某个库中的方法,我们甚至可以将这个方法也做成垫片

// webpack.base.js
const webpack = require('webpack');

plugins: [
    new webpack.ProvidePlugin({
        _: 'lodash',
        join: ['lodash', 'join']  // 这样就不需要写成 _.join()
    })
]

一些传统的模块依赖的 this 指向的是 window 对象。但是当模块运行在 CommonJS 环境下这将会变成一个问题,也就是说此时的 this 指向的是 module.exports。(看不懂的话随便在入口文件打印一下this,由于我们使用的都是import的方式,这些模块中的全局this都是指向的module.exports)
这个时候我们需要借助 imports-loader 来修改这个行为
执行npm i imports-loader -D

// webpack.base.js
{
    test: /\.js$/,
    exclude: /node_modules/,  // 如果代码是在node-modules,则排除
    use: [
        {
            loader: 'babel-loader'   //webpack与babel的桥梁,并不会进行解析
        },{
            loader: 'imports-loader?this=>window'
        }
    ]
}

// index.js
// 下面这两句能直接引发bug,this不指向moudule了,变成了undefined
// 特地记录一下,后面去查一下怎么解决,总不可能说一个文件为了this重新指向window就不去import任何东西吧
// import './style.css';  // css in js
// import _ from 'lodash';

console.log(_.join(['a', 'b', 'c'], '*'));
console.log(_.join(['a', 'd', 'z'], ','));
console.log(this)  // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

关于打包后 this 变成 undefined 种种问题:
https://blog.csdn.net/sinat_17775997/article/details/70495891
https://www.jianshu.com/p/a182d4f75e88

section-3.12 环境变量的使用方法

webpack 命令行 环境配置 中,通过设置 --env 可以使你根据需要,传入尽可能多的环境变量。在 webpack.config.js 文件中可以访问到这些环境变量。例如,--env.production--env.NODE_ENV=local
现在将原本的webpack.base.js/webpack.dev.js/webpack.prod.js三个进行改写,删除两个环境配置中的merge,将环境配置指向统一的webpack.base.js中,更改package.json(为了跟之前做区分,新增两条命令,原来命令行继续保留,原配置文件也保留,复制新增原配置目录commonbuild)

// package.json
"scripts": {
    "commondev": "webpack-dev-server --env.dev --config ./commonbuild/webpack.base.js",
    "commonprod": "webpack --env.prod --config ./commonbuild/webpack.base.js"
}
// webpack.prod.js
// const merge = require('webpack-merge');
// const commonConfig = require('./webpack.base.js');

module.exports = prodConfig;

// webpack.dev.js
// const merge = require('webpack-merge');
// const commonConfig = require('./webpack.base.js');

module.exports = devConfig;

// webpack.base.js
const merge = require('webpack-merge');
const devConfig = require('./webpack.dev.js');
const prodConfig = require('./webpack.prod.js');

const baseConfig = {
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [ 
            {
                test: /\.js$/,
                exclude: /node_modules/,  // 如果代码是在node-modules,则排除
                use: {loader: 'babel-loader'}   //webpack与babel的桥梁,并不会进行解析
            },{
                test: /\.(woff|eot|ttf|svg)$/,
                use: {
                    loader: 'file-loader',
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: 'icon-font/'
                    }
                }
            },{
                test: /\.(jpg|png|gif)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: 'images/',
                        limit: 2048  //是否小于2KB
                    }
                }
            }
        ]
    },
    performance: false,  // 不提示性能警告
    optimization: {
        usedExports: true,
        runtimeChunk: {
            name: "chunkName"
        },
        splitChunks: {
            chunks: "all",  // "async"只对异步代码进行打包 "initial"同步
            minSize: 30000,  // 大于30k,正确应该是30720才是30k,进行代码分割
            minChunks: 1,  // 当模块被引入至少一次
            maxAsyncRequests: 5,  // 只能同时加载5个代码块,大于5的时候,前5个会分割
            maxInitialRequests: 3,  // 首页(入口文件)最多只能分割3个
            automaticNameDelimiter: '~',  // 文件名,组和文件名直接的拼接符
            name: true,  // 打包生成的文件名是否有效,如果true则走cacheGroups中的规则命名
            cacheGroups: {  // 缓存组,不会直接打包文件,会把符合组条件的文件打包成一个模块
                vendors: {  // 匹配组名,会给打包文件前面加vendors~入口名
                    test: /[\\/]node_modules[\\/]/,  // 检测是否是在node_modules中
                    priority: -10,  // 优先级,当某个模块同时满足多个组的时候,按照优先级进行分配打包
                    name: "vendors"
                    // filename: 'vendors.js'  // 会让上面的 automaticNameDelimiter 失效
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true,  //如果一个模块已经被打包过,则不再打包
                    // filename: 'common.js'
                }
            }
        }
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html'  //猜测,这个是根据package.json目录?
        }),
        new CleanWebpackPlugin(),
        new webpack.ProvidePlugin({  // 自动加载模块,全局垫片
            _: 'lodash'
        })
    ],
}

module.exports = (env) => {
    // 这里的env.dev就是package.json里传进来的环境参数
    if(env.dev) return merge(baseConfig, devConfig);
    if(env.prod) return merge(baseConfig, prodConfig);
}
Section-3 webpack高级概念_第2张图片
--env参数语法

如果需要对环境进行复制的判断配置,这种全局环境的配置方法将会非常有用

你可能感兴趣的:(Section-3 webpack高级概念)