值得了解的webpack高级技能

3,941 阅读4分钟

只要这几步,webpack速成不是事儿一文中, 笔者简单介绍了webpack的常见用法。能满足最基本的开发需求。在这篇文章中,再来谈谈一些较高级的应用。

1. 配置不同环境的构建脚本

之前构建都是通过 npx webpack ... 这样的方式执行构建命令。可能你会觉得这样的方式不够高效。甚至在某些特定的情况下还需要设置 Node 的环境变量。根据环境变量值的不同,设置构建 --mode 的不同。

scripts 配置的入口在 package.json 中,另外再介绍下。通过执行 npm i 默认安装package.json中全部依赖。

 "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server"
  },

cross-env 设置node环境变量的插件 npm i cross-env -D

  • 开发环境构建: npm run dev

通过图中可以看到,npm run dev 相当于 npx webpack-dev-server

  • 生产环境构建: npm run build

1.1 设置node环境变量

  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server",
    "build": "cross-env NODE_ENV=production webpack"
  },

先安装插件cross-env

npm i cross-env -D

执行 npm run dev, 先设置环境变量 NODE_ENV=devlelopment, 然后执行 webpack 命令。

为什么要设置环境变量呢? 设置环境变量后,可以在打包构建时执行相应环境变量下的脚本。

在webpack.config.js中 process.env.NODE_ENV 拿到环境变量值。做你想做的事情。哈哈哈~

  • 通过 webpack 自带的插件定义全局变量 _DEV_ ,类似于全局变量 window
//应用: 在项目 src/index.js 文件中代码里区分开发或者是生产环境
 if(__DEV__){
     alert('开发环境')
 }else{
     alert('生产环境')
 }
  • 根据环境不同,来设置某些插件是否可用

在webpack.config.js文件中,设置变量 let isDev = process.env.NODE_ENV === 'development',然后定义插件的disable属性的值

  plugins: [
        new ExtractTextWebpackPlugin({
            filename: 'css/index.css',
            disable: isDev
        }),
    ],

1.2 构建 DLL 动态链接库

.dll 为后缀的文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据

  • 基础模块独立出来打包到单独的动态连接库里
  • 当需要导入的模块在动态连接库里的时候,模块不能再次被打包,而是去动态连接库里获取 dll-plugin
  • 提高构建效率

react, react-dom 为例

  • 创建 webpack.config.react.js
let path = require('path');
let webpack = require('webpack');

module.exports  = {
    entry: {
        vendor: ['react', 'react-dom']
    },
    output: {
        filename: '[name].js',
        path: path.join(__dirname, 'dist'),
        libraryTarget: "var",  //构建后输出js文件所属规范, 如果 设置为 commonjs, 则输出文件符合 commonjs规范
        library: '_dll_[name]', //构建后输出js库的名字
      
    },
    mode: 'development',
    plugins: [
        new webpack.DllPlugin({
            name: '_dll_[name]',
            path: path.join(__dirname, 'dist', '[name].manifest.json')
        })
    ],
};
  • 设置构建命令
 "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server",
    "react": "wepack --config webpack.config.react.js"
  },
  • 执行 npm run react

  • 输出的 vendor.js

1.3 使用动态链接库

在webpack.config.js中配置插件

plugins: [
    new webpack.DllReferencePlugin({
        manifest: path.resolve(__dirname, 'dist', 'vendor.manifest.json')
    })
]
  • react编译需要安装如下loader

npm i babel-core babel-loader babel-preset-env babel-preset-react babel-preset-stage-0 -D

  • 配置相关rules
   module: {
        rules: [
            {
                test: /\.jsx?/,
                use: 'babel-loader',
                exclude:/node_modules/,
                include:/src/
            },
        ]
    },
  • 在项目根目录下创建 .babelrc文件
{
    "presets": [
        "env",
        "stage-0",
        "react"
    ]
}

  • 执行构建命令 npm run build, 控制台打印:
[./node_modules/react-dom/index.js] delegated ./node_modules/react-dom/index.js from dll-reference _dll_vendor 42 bytes {index} [built]
[./node_modules/react/index.js] delegated ./node_modules/react/index.js from dll-reference _dll_vendor 42 bytes {index} [built]

由此可以看出项目构建时是使用的预先构建好的react库文件,从控制台打印的构建耗时也明显不同。

2. 抽离公共代码(库文件或组件)

  • 创建src/a.js, src/x.js 文件
  • 在src/index.js 和 src/x.js文件中都import a.js文件
  • 在src/index.js 和 src/x.js文件中都引入 react库文件

index.js

import './index.css';
// if(module.hot){
//    console.log('热更新');
// }
import React, { Component } from 'react'
import { render } from 'react-dom'
import a from './a.js'

render(<h1>hello zfpx</h1>, window.app)

x.js

import React, { Component } from 'react'
import { render } from 'react-dom'

import a from './a.js'

a.js

module.exports = {
    a: 'a.js'
}

在webpack.config.js中配置:

    optimization: {
        splitChunks: {
            cacheGroups: {
                commons: { //提供公共组件, 只要超出0字节就生产新的包
                    chunks: 'initial',
                    //miniChunks: 2,
                    //maxInitalRequest: 5,
                    name: 'commons',
                    minSize: 0 
                },
                vendor: {// 抽离第三插件
                    test: /node_modules/,
                    chunks: 'initial',
                    name: 'vender',
                    priority: 10,
                    enforce: true
                }
            }
        }
    },

执行构建脚本 npm run build, 控制台打印:

如果注释相关构建信息:

从二者控制台信息中可以看出,构建后的index.js 和 x.js文件体积大小和 dist目录中输出的文件数量是不一样的。

3. 设置全局变量

在index.js中引入 jquery.js后,在a.js文件中想不引入jquery.js直接使用jquery对象

webpack.config.js中配置

module: {
    rules: [
        {
        test: /jquery/,
        use:[{
            loader:'expose-loader',
            options:'$'
        }]
        },
    ]
}

或者使用webpack提供的内置插件

 // 提供全局变量插件
        new webpack.ProvidePlugin({
            $:'jquery'
        }),

二者的区别是神马呢

  • 使用插件时:
1. $不会定义到window上
2. 只要是用到jquery(或者 $)的地方,当前bundle.js都会把jquery打包进去

webpack.config.js 相关配置为:

entry: {
        index: './src/index.js',
        x: './src/x.js'
    },
 output: {
    filename: '[name].[hash].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    library: '_dll_[name]'
},
...
pugins: [
 new HtmlWebpackPlugin({
            template: 'src/index.html',
            hash: true
        }),
]

index.js

console.log($)

x.js

console.log($);

从构建的日志中可以看出,index.js构建后的文件大小和x.js构建的文件大小一致

  • 使用 expose-loader
1.导入一次,就会暴露出来
2.$会定义到在window上

index.js

import $ from 'jquery'
console.log('index.js');
console.log($);

x.js

console.log('x.js');
console.log($);
console.log(window.$);

构建日志:

浏览器打印日志:

从构建的日志中可以看出,index.js构建后的文件大小和x.js构建的文件大小不一致 从浏览器打印日志可以看出,x.js在没有直接import jquery时,也可以拿到jquery对象

4. 小结

webpack还有很多其他适用的功能,比如在使用vue时,常常会根据路由加载相应的组件js也就是我们所说的 按需加载。可以用到webpack代码分离中的 动态导入和懒加载(dynamic imports),优化代码加载,提升性能。