webpack

184 阅读9分钟

一. WebPack


1.1. webpack简介


​ webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle

image.png

知识点:

image.png

1.2. webpack 核心概念


1.21. Entry

​ 入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。

1.22. Output

​ 输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。

1.23. Loader

​ Loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 能处理 js/json 资源)

1.24. Plugins

​ 插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,

一直到重新定义环境中的变量等。

1.25. Mode

​ 模式(Mode)指示 webpack 使用相应模式的配置

选项描述特点
development会将 DefinePlugin 中的process.env.NODE_ENV值设为development开发环境(能让代码本地调试 )
production默认值,会将 DefinePlugin 中的process.env.NODE_ENV值设为production生产环境(能让代码优化上线运行的环境 )
none不使用任何默认优化选项
1.26. 浏览器兼容性

​ webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import()require.ensure() 需要 Promise。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill

1.27. 环境

​ webpack 5 运行于 Node.js v10.13.0+ 的版本。

1.3. 初始化配置


  1. 初始化 package.json

    输入指令: 
    
    npm init
    
  2. 下载并安装 webpack

    输入指令: 
    
    npm install webpack webpack-cli -g 
    
    npm install webpack webpack-cli -D
    

1.4. 编译打包应用


  • 创建文件

  • 运行指令

    1. 开发环境指令:webpack src/js/index.js -o build/js/built.js --mode=development

      功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成 浏览器能识别的语法。

    2. 生产环境指令:webpack src/js/index.js -o build/js/built.js --mode=production

      功能:在开发配置功能上多一个功能,压缩代码。

  • webpack 能够编译打包 js 和 json 文件。 能将 es6 的模块化语法转换成浏览器能识别的语法。 能压缩代码。

  • webpack 不能编译打包 css、img 等文件。 不能将 js 的 es6 基本语法转化为 es5 以下语法。

1.5. webpack 开发环境的基本配置


1.51. 配置文件 webpack.config.js

运行指令: webpack

// loader 下载 配置
// plugins 下载 引入 配置

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
	// webpack配置
	// 入口
	entry: "./src/index.js",
	// 出口
	output: {
		// 输出文件名
		filename: "built.js",
		// 输出路径
		// __dirname 代表当前文件目录绝对路径
		path: resolve(__dirname, "build"),
	},
	// loader配置
	module: {
		rules: [
			// 匹配哪些文件
			{
				test: "/.css$/",
				// 使用哪些loader进行处理
				// style-loader 创建style标签,将js中的样式资源插入进行,添加到head中生效
				// css-loader 将css文件变成commonjs模块加载js中,里面内容是样式字符串
				// 执行顺序 从右向左
				use: ["style-loader", "css-loader"],
			},
			// 详细loader配置
		],
	},
	// plugins 的配置
	plugins: [
		new HtmlWebpackPlugin({
			// 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
			template: "./src/index.html",
		}),
	],
	// 模式
	// 开发模式
	// 生产环境 production
	mode: "development",
};
1.52. 打包样式资源
1. 下载安装 loader 包 npm i css-loader style-loader less-loader less -D

2. 修改配置文件
	module: {
		rules: [
			// 匹配哪些文件
			{
				test: "/.css$/",
				use: ["style-loader", "css-loader"],
			},
			{
				test: "/.less$/",
                use: ["style-loader", "css-loader","less-loader"],
			}
		],
	},
1.53. 打包HTML资源
1. 下载安装 plugin 包 npm install --save-dev html-webpack-plugin

2. 修改配置文件 

const HtmlWebpackPlugin = require('html-webpack-plugin');

{
	plugins: [
		new HtmlWebpackPlugin({
			// 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
			template: "./src/index.html",
		}),
	],
}
1.54. 打包图片资源
1. 下载安装 loader 包 npm install --save-dev html-loader url-loader file-loader

	// 问题:默认处理不了 html 中 img 图片 
  // 处理图片资源
  test: /\.(jpg|png|gif)$/,
  // 使用一个 loader 
  // 下载 url-loader file-loader
  loader: 'url-loader',
  options: {
  // 图片大小小于 8kb,就会被 base64 处理 
  // 优点: 减少请求数量(减轻服务器压力)
  // 缺点:图片体积会更大(文件请求速度更慢)
  limit: 8 * 1024,
  // 问题:因为 url-loader 默认使用 es6 模块化解析,而 html-loader 引入图片是 commonjs 
  // 解析时会出问题:[object Module] 
  // 解决:关闭 url-loader 的 es6 模块化,使用 commonjs 解析
  esModule: false,
  // 给图片进行重命名 
  // [hash:10]取图片的 hash 的前 10 位
  // [ext]取文件原来扩展名
  name: '[hash:10].[ext]'
  },
  {
    test: /\.html$/,
    // 处理 html 文件的 img 图片(负责引入 img,从而能被 url-loader 进行处理) 
    loader: 'html-loader'
  }
1.55. 打包其它资源
// 打包其他资源(除了 html/js/css 资源以外的资源) 
{ 
    // 排除 css/js/html 资源 
    exclude: /\.(css|js|html|less)$/, 
    loader: 'file-loader',
    options: {
        name: '[hash:10].[ext]' 
    }
}
1.56. devserver 自动化
const { resolve } = require("path");

module.export = {
   entry: '',
   output: {},
   ...
   // 安装 npm webpack-dev-server -g
   // 开发服务器 devServer :自动化编译,自动打开浏览器,自动刷新浏览器
   // 特点: 只会在内存中编译打包,不会有任何输出
   // 启动 devServer指令:npx webpack-dev-server
   devServer: {
     // 项目路径
     contentBase: resolve(__dirname, 'build'),
     // 启动gzip压缩
       compress: true,
     // 端口号
       port: 3000,
     // 自动打开浏览器
       open: true
   } 
 }

1.6. webpack 生产环境的基本配置


1.61. 提取 css 成单独文件
// 下载插件 npm install --save-dev mini-css-extract-plugin

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module: {
    rules: [
        { 
         test: /\.css$/,
         use: [ 
             // 创建 style 标签,将样式放入 
             // 'style-loader', 
             // 这个 loader 取代 style-loader。作用:提取js中的css成单独文件
             MiniCssExtractPlugin.loader, 
             // 将 css 文件整合到 js 文件中 
             'css-loader',
         	  ] 
        } 
    ] 
},

plugins: [
    new MiniCssExtractPlugin({ 
        // 对输出的 css 文件进行重命名 
        filename: 'css/built.css' 
    }) 
],
mode: 'development'
1.62. css 兼容性处理
// 安装 npm install --save-dev mini-css-extract-plugin

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 设置node.js 环境变量 决定使用browserlist的环境
process.env.NODE_ENV = development
module: {
    rules: [
        { 
         test: /\.css$/,
         use: [ 
             // 创建 style 标签,将样式放入 
             // 'style-loader', 
             // 这个 loader 取代 style-loader。作用:提取js中的css成单独文件
             MiniCssExtractPlugin.loader, 
             // 将 css 文件整合到 js 文件中 
             'css-loader',
             
             /*  
             css兼容性处理:postcss --> postcss-loader postcss-preset-env
             通过package.json中的 browserlist里面的配置,来配置css兼容性样式
             	browserlist: {
             	  "development": [
             	    "last 1 chrome version",
             	    "last 1 firefox version",
             	    "last 1 safari version"
             	  ],
             	  "production": [
             	    ">0.2%",
             	    "not dead",
             	    "not op_mini all"
             	  ]
             	}
             */
             {
                 loader: 'postcss-loader',
                 options: {
                     ident:'postcss',
                     plugins: () => [
                         require('postcss-preset-env')()
                     ]
                 }
             }
           ] 
        } 
    ] 
},

plugins: [
    new MiniCssExtractPlugin({ 
        // 对输出的 css 文件进行重命名 
        filename: 'css/built.css' 
    }) 
],
mode: 'development'
1.63. 压缩 css
// 下载插件 npm install --save-dev optimize-css-assets-webpack-plugin

const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

plugins: [
    new optimizeCssAssetsWebpackPlugin()
],
1.64. js 语法检查 eslint
/* 
安装: npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import

语法检查: eslint-loader eslint 
注意:只检查自己写的源代码,第三方的库是不用检查的 
设置检查规则:
	package.json 中 eslintConfig 中设置~ 
	"eslintConfig": {
    	"extends": "airbnb-base",
        "env": {
        "browser": true
        }
      } 
      
     airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint

*/


{ 
    test: /\.js$/, 
    exclude: /node_modules/,
    loader: 'eslint-loader', 
    options: { 
        // 自动修复 eslint 的错误 
        fix: true 
    } 
    
}
1.65. js 兼容性处理
/*
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js

*/

{ 
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: { // 预设:指示 babel 做怎么样的兼容性处理 
        presets: [
            [ '@babel/preset-env',
             { 
                 // 按需加载 
                 useBuiltIns: 'usage', 
                 // 指定 core-js 版本
                 corejs: { 
                     version: 3
                 },
                 // 指定兼容性做到哪个版本浏览器 
                 targets: { 
                     chrome: '60',
                     firefox: '60',
                     ie: '9',
                     safari: '10',
                     edge: '17' 
                 } 
             }
           ] 
        ] 
    } 
}
1.66. js 压缩
// 生产环境下会自动压缩 js 代码 

mode: 'production'
1.67. HTML压缩
plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html', 
        // 压缩 html 代码 
        minify: { 
            // 移除空格 
            collapseWhitespace: true, 
            // 移除注释 
            removeComments: true 
        } 
    }) 
],

二. WebPack 优化配置

2.1. HMR


HMR: hot module replacement 模块热替换

作用:一个模块发生变化,只会重新打包这一个模块,极大提升构建速度

devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true, 
    // 开启 HMR 功能 
    // 当修改了 webpack 配置,新配置要想生效,必须重新 webpack 服务 
    hot: true 
}

2.2. source-map


一种提供源代码到构建后代码映射技术(如果构建后代码出错,通过映射可以追踪源代码错误)

devtool: 'inline-source-map|hidden-source-map|eval-source-map|nosources-source-map|cheap-source-map|cheap-module-source-map'

开发环境  eval-source-map 
生产环境  source-map

2.3. oneOf


只会匹配一个 loader

// 以下 loader 只会匹配一个
// 注意:不能有两个配置处理同一种类型文件
oneOf: [
    {
        test: /\.css$/, 
        use: [...commonCssLoader] 
    },
    { 
        test: /\.less$/, 
        use: [...commonCssLoader, 'less-loader'] 
    },

2.4. 缓存


// 开启 babel 缓存 
// 第二次构建时,会读取之前的缓存 
cacheDirectory: true

2.5. tree shaking


去除无用代码

  • 前提:1. 必须使用ES6模块化 2. 开启production环境
  • 作用:减少代码体积

2.6. code split


代码分割

// 单入口 
// entry: './src/js/index.js', 
entry: { 
    // 多入口:有一个入口,最终输出就有一个 bundle 
    index: './src/js/index.js',
    test: './src/js/test.js' },
    output: { 
        // [name]:取文件名 
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build') 
    },
        
/*
	1. 可以将 node_modules 中代码单独打包一个 chunk 最终输出 
	2. 自动分析多入口 chunk 中,有没有公共的文件。如果有会打包成单独一个 chunk 
*/
    optimization: {
        splitChunks: {
            chunks: 'all' 
        } 
    },

2.7. 懒加载和预加载(lazy loading 和 prefetch)


2.8. PWA


渐进式网络开发应用程序(离线可访问)

const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

plugin: [
    new WorkboxWebpackPlugin.GenerateSW({
        /*
        1. 帮助 serviceworker 快速启动
        2. 删除旧的 serviceworker 
        3. 生成一个 serviceworker 配置文件~ 
        */
        clientsClaim: true, skipWaiting: true })
]

2.9. 多进程打包


// npm install --save-dev thread-loader

{
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
    loader: 'thread-loader',
    options: {
        workers: 2 // 进程 2 个 
    }]
}

2.10. externals


externals: {
    // 拒绝 jQuery 被打包进来
    // 忽略库名 -- npm包名
    jquery: 'jQuery' 
}

2.11. dll


对某些库(第三方库:jquery,react,vue)进行单独打包

三. webpack详细配置


3.1. resolve

配置路径别名

resolve: {
    // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示 
    alias: { 
        $css: resolve(__dirname, 'src/css') 
    },
    // 配置省略文件路径的后缀名 
    extensions: ['.js', '.json', '.jsx', '.css'],
    // 告诉 webpack 解析模块是去找哪个目录
    modules: [resolve(__dirname, '../../node_modules'),'node_modules'] 
}
3.2. optimization
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

/* ------------------------------------------- */

optimization: {
    splitChunks: {
        chunks: 'all'
        // 默认值,可以不写~ 
    },
    // 将当前模块的记录其他模块的 hash 单独打包为一个文件 runtime
    // 解决:修改 a 文件导致 b 文件的 contenthash 变化
    runtimeChunk: {
        name: entrypoint => `runtime-${entrypoint.name}` 
    },
    minimizer: [
        // 配置生产环境的压缩方案:js 和 css
        new TerserWebpackPlugin({
            // 开启缓存
            cache: true,
            // 开启多进程打包
            parallel: true,
            // 启动 source-map
            sourceMap: true 
        }) 
    ] 
}

四. WebPack5


此版本重点关注以下内容:

  • 通过持久缓存提高构建性能.
  • 使用更好的算法和默认值来改善长期缓存.
  • 通过更好的树摇和代码生成来改善捆绑包大小.
  • 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
  • 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.

4.1. Chunk 和模块 ID

添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。

chunkIds: "deterministic", moduleIds: "deterministic", mangleExports: "deterministic"

4.2. Chunk ID

在开发模式下,默认启用的新命名代码块 ID 算法为模块(和文件名)提供了人类可读的名称。 模块 ID 由其路径决定,相对于 context。 代码块 ID 由代码块的内容决定。

所以你不再需要使用import(/* webpackChunkName: "name" */ "module")来调试。 但如果你想控制生产环境的文件名,还是有意义的。

可以在生产环境中使用 chunkIds: "named" 在生产环境中使用,但要确保不要不小心暴露模块名的敏感信息。

迁移:如果你不喜欢在开发中改变文件名,你可以通过 chunkIds: "natural" 来使用旧的数字模式。

4.3. Tree Shaking

  1. webpack 现在能够处理对嵌套模块的 tree shaking
// inner.js
export const a = 1;
export const b = 2;

// module.js
import * as inner from './inner';
export { inner };

// user.js
import * as module from './module';
console.log(module.inner.a);

在生产环境中, inner 模块暴露的 b 会被删除

  1. webpack 现在能够多个模块之前的关系
import { something } from './something';

function usingSomething() {
  return something;
}

export function test() {
  return usingSomething();
}

当设置了"sideEffects": false时,一旦发现test方法没有使用,不但删除test,还会删除"./something"

  1. webpack 现在能处理对 Commonjs 的 tree shaking

4.4. Output

webpack 4 默认只能输出 ES5 代码

webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.

如:output.ecmaVersion: 2015

4.5. SplitChunk

// webpack4
minSize: 30000;
// webpack5
minSize: {
  javascript: 30000,
  style: 50000,
}

4.6. Caching

// 配置缓存
cache: {
  // 磁盘存储
  type: "filesystem",
  buildDependencies: {
    // 当配置修改时,缓存失效
    config: [__filename]
  }
}

缓存将存储到 node_modules/.cache/webpack

4.7. 监视输出文件

之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。

此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。

4.8. 默认值

  • entry: "./src/index.js
  • output.path: path.resolve(__dirname, "dist")
  • output.filename: "[name].js"

4.9. 更多内容

github.com/webpack/cha…