跳到主要内容

12 篇博文 含有标签「webpack」

查看所有标签

externals

为什么需要externals?

有时候我们不希望将某些包进行打包,而是通过CDN链接进行引入,此时我们就可以用到externals。

使用externals的步骤

  1. 再webpack.config.js中添加配置项指定要忽略打包的内容
  externals: {
jquery: 'jQuery'
}
  1. 在静态文件中通过CDN引入
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>

dll

什么是dll?为什么需要dll?

dll可以帮助我们对某些库(第三方库:jquery、react、vue...)等库进行单独打包。通过插件引入后,可以有效避免对这些包的重复打包。当运行webpack时,默认查找webpack.config.js配置文件,需要运行下面的指令。

webpack --config webpack.dll.js

如何实现dll打包

  1. 在webpack.config.js的同名文件创建一个webpack.dll.js文件,并在其中对jquery进行单独打包。
const { resolve } = require('path');
const webpack = require('webpack');

module.exports = {
entry: {
jquery: ['jquery']
},
output: {
filename: '[name].js',
path: resolve(__dirname,'dll'),
library: '[name]_[hash]'
},
plugins: [
// 打包生成一个manifest.json提供和jquery的映射
new webpack.DllPlugin({
name: '[name]_[hash]',
path: resolve(__dirname,'dll/manifest.json') // 输出文件路径
})
],
mode: 'production'
}
  1. 生成dll文件夹和指定文件
webpack --config webpack.dll.js
  1. 在webpack.config.js中增加配置项

告诉webpack哪些库不参与打包,并使用插件将dll已经打包好的jquery文件进行引入,这样以后jquery都不需要参与打包了,因为已经打包好了,我们只需要引入即可。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const {resolve} = require('path');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')

module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname,'build')
},
module: {
rules: [

]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
// 告诉webpack哪些库不参与打包,同时使用的名称根据manifest.json中的来
new webpack.DllReferencePlugin({
manifest: resolve(__dirname,'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname,'dll/jquery.js')
})
],
mode: 'development'
}

Justinwebpack阅读需 2 分钟

一、entry

入口个数和输出个数的关系

  1. 字符串形式

当只有一个入口的时候,打包形成一个chunk,输出一个bundle。

entry: './src/index.js',
  1. 数组形式

当以数组的形式引入文件的时候,也只是形成一个chunk,输出一个bundle。(这种方式可以在HMR功能中,让html的热更新生效)

entry: ['./src/index.js','./src/add.js'],
  1. 对象形式

多入口,有几个入口文件就行形成几个chuank,输出几个bundle文件。此时chunk的名称就是key。

entry: {
index: './src/index.js',
add: './src/add.js'
}
  1. 特殊用法

下面的这种写法会将index.js和count.js合并打包到index中,会将add单独进行打包。

entry: {
index: ['./src/index.js','./src/count.js'],
add: './src/add.js'
}

二、output

  1. publicPath的作用

这个属性主要是配置所有资源引入公共路径的前缀。

output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build'),
publicPath: '/'
}

加上上面的属性后,引入资源的路径就会变为下面的样子。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DklLkh55-1640396718734)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e0a869d0c514eef8332ba8333d95acc~tplv-k3u1fbpfcp-watermark.image?)]

  1. filename

指定打包后文件的目录和名称。

filename: 'js/[name].js'
  1. path

指定输出文件的路径。

path: resolve(__dirname, 'build')
  1. chunkFilename

非入口chunk的名称,例如通过import导入的chunk。

chunkFilename: 'js/[name]_chunk.js'
  1. library

这个属性主要定义了打包后的库向外暴露的变量名,同时通过livraryTarget还可以指定变量名添加到哪里。

library: '[name]'

加上上面这个配置项,打包的文件变为下面的样子。

image.png

同时可以通过下面的配置,设置变量名添加到哪个环境中。

libraryTarget: 'window',
libraryTarget: 'global',
libraryTarget: 'commonjs'

三、module

  1. use

多个loader使用use,单个loader用loader。

use: ['style-loader','css-loader']
  1. exclude

让loader排除指定的文件。

exclude: /node_modules/
  1. test

让loader匹配什么类型的文件。

test: /\.js$/
  1. include

指定检查什么类型的文件。

include: resolve(__dirname,'src')
  1. enforce

优先执行。

enforce: 'pre'
  1. options

给某个loader进行配置特殊属性。

options: {}
  1. oneOf

设置只有一个loader会生效。

oneOf: []

四、resolve

  1. alias

这个属性主要是帮助我们配置解析模块路径别名,有时候引入路径过于复杂,可以通过这个进行简写。

  resolve: {
alias: {
$css: resolve(__dirname,'src/css')
}
}

配置完上面的属性后,引入文件可以通过下面的形式进引入。

import '$css/index.css';
  1. extensions

这个属性可以帮助我们在引入文件的时候,省略后缀,但是系统在查找文件的时候,是按照数组中出现的先后顺序来执行的。

resolve: {
alias: {
$css: resolve(__dirname,'src/css')
},
// 配置省略文件路径的后缀名
extensions: ['.js','.json','.css']
}
  1. modules

告诉webpack解析模块去找哪个目录,这样能够减少webpack的查找次数,提高打包的性能。

resolve: {
alias: {
$css: resolve(__dirname,'src/css')
},
// 配置省略文件路径的后缀名
extensions: ['.js','.json','.css'],
modules: [resolve(__dirname,'../node_modules'),'node_modules']
}

五、dev-server

需要注意的是:dev-server只适用于开发环境中。关于dev-server的常见配置属性都在下面的代码中,具体的注释解释了属性的用途。

devServer: {
// 运行代码的目录
contentBase: resolve(__dirname,'build'),
// 监视contentBase目录下的所有文件,一旦文件变化就会reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动gzip压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本启动信息以外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示
overlay: false,
// 服务器代理 --> 解决开发环境下的跨域问题
proxy: {
// 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写: 将/api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
}

六、optimization

optimization的主要配置信息可以参见下面的代码。

optimization: {
splitChunks: {
chunks: 'all'
},
// 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
new TerserWebpackPlugin({
// 开启多进程打包
parallel: true
})
]
}

JustinWebpack阅读需 4 分钟

一、什么是Webpack?

webpack是用于现代JavaScript应用程序的静态模块打包工具。webpack会分析我们的项目结构,找到JavaScript模块以及其他的一些浏览器不能直接运行的拓展语言(例如Sass、TypeScript等),并将其打包为合适的格式供浏览器使用。

二、为什么需要Webpack?

如今在开发前端项目时,为了简化开发的复杂程度,引入了模块化、TypeScript、less、sass等CSS预处理器实践方法,但是使用它们开发的文件需要进行额外的处理才能被浏览器识别,webpack的出现就是为了解决这个需求的。

三、Webpack的使用场景

  1. 根据模板生成HTML,并自动处理css/js引用路径。
  2. 自动处理img里面的图片路径,字体引用等。
  3. 开启本地服务器,实现修改代码即热更新的功能。
  4. 编译jsx、es6、sass、less等并添加sourcemap等辅助。
  5. 异步加载内容,不需要时不加载到DOM。
  6. 配合Vue.js、react.js等框架的开发。

四、Webpack的构建流程

  1. 初始化参数:从配置文件和shell语句中读取与合并参数,得出最终的参数。
  2. 开始编译:用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译。
  3. 确定入口:根据配置中的entry找到所有的入口文件。
  4. 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出改模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
  5. 完成模块编译:在经过第四步使用Loader翻译完所有模块后,得到了每个模块翻译后的最终内容以及它们之间的依赖关系。
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
  7. 输出完成:在确定好输出内容之后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统中。

五、Loader和Plugins的区别

  1. 用法不同

loader是一个转换器,将A文件编译成B文件,比如将A.less转换为A.css,单纯的文件转换过程。webpack将一切文件视为模块,但是webpack原生只能解析JS文件,如果想将其他文件也打包的话,就会用到loader。plugin是一个扩展器,它丰富了webpack本身,会监听webpack打包过程中的某些节点,在合适的时机通过Webpack提供的API改变输出的结果。比如HtmlWebpackPlugin是用来生成一个HTML文件的。

  1. 运行时机不同

loader运行在打包文件之前,plugins则在整个编译周期都起作用。

六、常用的Loader与plugins

常用Loader及其作用

  1. style-loader

用于将css编译后的样式,挂载到页面的style标签上。

  1. css-loader

用于识别.css文件,处理css必须配合style-loader共同使用,只安装css-loader样式不会生效。

  1. sass-loader

css预处理器,sass的特点是编写css更加便捷。

  1. postcss-loader

用于补充css样式各种浏览器内核前缀,可以实现更好的兼容性。

  1. babel-loader

用于将ES6及以上的语法转换为ES5语法。

  1. ts-loader

用于配置项目TypeScript。

  1. html-loader

如果有时候需要引入一个html页面代码片段赋值给DOM元素内容使用,这时就用到html-loader。

  1. file-loader

用于处理文件类型资源,如jpg,png等图片。

  1. url-loader

url-loader也是处理图片类型资源,这个与file-loader的不同之处是,url-loader可以根据图片大小进行不同的操作,如果图片大小大于指定的大小则将图片进行打包,反之则将其转换为base64字符串合并到js文件中。

  1. html-withimg-loader

我们在编译图片时,都是使用file-loader和url-loader,这两个loader都是查找js文件中的相关图片资源的,但是HTML文件中的图片不会进行打包,因此,要想实现打包就要使用html-withimg-loader。

  1. vue-loader

用于编译.vue文件,如果我们自己搭建vue项目就可以使用vue-loader。

  1. eslint-loader

用于检查代码是否符合规范,是否存在语法错误。

常用Plugins及其作用

  1. html-webpack-plugin

可以根据模板自动生成html代码,并自动引用css和js文件。

  1. extract-text-webpack-plugin

将js文件中引用的样式单独抽离成css文件。

  1. HotModuleReplacementPlugin

热更新插件。

  1. optimize-css-assets-webpack-plugin

不同组件中重复的css可以快速去重,压缩CSS代码的。

  1. compression-webpack-plugin

生产环境下可以采用gzip压缩JS和CSS文件。

  1. clean-webpack-plugin

清理每次打包下没有使用的文件。

  1. mini-css-extract-plugin

是一个专门用于将打包的CSS内容提取到单独文件的插件。

七、Webpack生产环境和开发环境的区别

开发环境和生产环境的需求不同。

开发环境的需求

  • 模块热更新
  • sourceMap
  • 接口代理
  • 代码规范检查
  • dev server

生产环境的需求

  • 提取公共代码
  • 压缩混淆
  • 文件压缩
  • 去除无用的代码
  • 改善加载时间

八、如何提高打包速度,减少打包体积?

第一步:对打包的速度和体积进行分析

  1. 使用speed-measure-webpack-plugin来进行速度分析,分析整个打包的总耗时,查看每个loader和plugin的耗时情况。
  2. 采用webpack-bundle-analyzer进行体积分析,主要分析依赖的第三方库的文件大小和业务代码里的组件大小。

第二步:提高Webpack打包速度

一、 确定影响打包速度的因素

  1. 开始打包时,首先需要搜索所有的依赖项,这需要占用一定的时间即搜索时间,所以我们要优化搜索时间
  2. webpack会根据我们配置的loader解析相应的文件,所以我们需要优化这个解析时间
  3. webpack会将所有依赖的模块打包到一个文件中,其中JS压缩时发布编译的最后阶段,JS压缩时需要先将代码转换为抽象语法树,然后处理这个抽象语法树最后还原成JS,这个过程涉及到大量计算,因此我们需要对这个压缩时间进行优化。
  4. 当更改项目中的一个文件,需要重新进行打包,但是很多没有更改的文件也需要重新打包,因此我们需要对二次打包的时间进行优化。

二、 优化解析时间

运行在Node.js上的webpack是单线程模式的,因此在打包大量文件时,就会比较漫长。

  1. 使用thread-loader并行解析资源,只需要把这个loader放置在其他loader之前,就会开启多进程处理。
  2. 在webpack的构建过程中,大多数时间消耗在loader的解析转换上,可以利用happyPack使用多进程对文件进行打包,对多核CPU利用率更高,HappyPack可以让Webpack同一时间处理多个任务,将任务分解给多个子进程去并发的执行,子进程处理完后,再把结果发送给主进程。

三、 合理利用缓存

  1. 在性能开销较大的loader之前添加cache-loader,将会显著提升二次构建的速度。
  2. 使用DLLPlugin将第三方模块单独打包生成一个文件,因为一般情况下,第三方模块的代码是不会轻易改变的。这样只有第一次打包的时候去分析这个文件里的代码,再次打包的时候回使用上次已经打包过的代码。

四、 优化压缩时间

webpack4默认内置使用terser-webpack-plugin来压缩优化代码,同时terser可以启动多进程并行运行来提高压缩的速度。

五、 优化搜索时间

我们在使用Loader的时候可以通过test、include、exclude三个配置项来优化Loader的文件搜索范围。比如说将node_modules给排除掉,因为node_modules中使用的代码都是经过编译过的。

第三步:减少Webpack的打包体积

一、 按需加载

在开发项目时,避免引入不需要的代码,尽可能的进行按需加载。

二、Scope Hoisting

Scope Hoisting会分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中。

三、Tree Shaking

利用Tree Shaking可以实现删除项目中共未被引用的代码。

九、 实现Loader和Plugin

实现Loader

Loader的本质是一个函数,这个函数会接收一个参数,这个参数是待匹配文件的源码,函数会返回处理后的源码。

下面我们写一个Loader,将var关键字替换为let

module.exports = function (source) {
return source.replace(/var/g,'let');
}

调用这个Loader只需在webpack.config.js中使用下面的方法

module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: path.resolve('./src/myLoader.js')
}
]
}
]
}

实现Plugin

一个Webpack Plugin包含以下五个部分。

  1. 一个JavaScript命名函数。
  2. 在函数的prototype上有一个apply方法。
  3. 有一个绑定到webpack自身的事件钩子。
  4. 处理webpack内部实例的特定数据。
  5. 功能完成后调用webpack提供的回调。
function myPlugin(options) {}

myPlugin.prototype.apply = function (compiler) {
compiler.plugin('emit',function(compilation,callback) {
// 功能完成后调用webpack提供的回调
console.log('Hello Webpack');
callback();
})
}

module.exports = myPlugin;

如何调用我们手写的插件呢?

const myPlugin = require('./src/myPlugin.js');

module.exports = {
...,
plugins: [
new Plugin()
]
}

十、 Webpack热更新的原理(HMR)

什么是HMR?

HMR即(Hot Module Replacement模块热替换)是指当代码发生修改并保存后,webpack会对更新的部分代码进行重新打包,并将新的模块发送到浏览器端,浏览器将新的模块替换掉旧的模块,这样能够在不刷新浏览器的前提下对应用进行更新。

Webpack热更新的工作流程

  1. 第一步:webpack启动监听模式之后,webpack第一次编译项目,并将结果存储在内存文件系统中,内存webpack服务器通知浏览器加载资源,浏览器获取的静态资源除了JS代码之外,还有一部分是通过webpack-dev-server注入的HMR runtime代码,作为浏览器和webpack通信的客户端。
  2. 第二步:文件系统中的一个文件或者模块发生了变化,webpack监听到文件变化对文件重新编译打包,每次编译生成唯一的hash值,根据变化的内容生成两个补丁文件,说明变化内容的manifest和chunk.js。
  3. 第三步:HMR-server通过websocket将manifest推送给浏览器。
  4. 第四步:浏览器端的HMR runtime根据manifest的hash和chunkid使用ajax拉取最新的模块chunk。
  5. 第五步:触发render流程实现局部热重载。

十一、webpack、babel和babel-loader三者之间的关系

  • babel是编译工具,把ES6及以上语法编译成ES5等低版本语法。
  • webpack是打包工具,定义入口文件,将所有的模块引入整理后,通过loader和plugin处理后,打包输出。
  • webpack通过babel-loader使用babel。

十二、webpack的Tree shaking的作用及原理

作用

Tree Shaking是一种通过清除多余代码来优化项目打包体积的技术。

原理

借助ES6模块语法的静态结构,通过编译阶段的静态分析,找到没有引入的模块并打上标记,然后在压缩阶段利用像uglify-js这样的压缩工具删除没有用到的代码。

参考链接


JustinWebpack阅读需 11 分钟

懒加载的实现方法

通过动态import的方法来实现懒加载。(下面的函数,不点击按钮是不会加载test.js这个文件的)。

console.log("index.js文件被加载了~");

document.getElementById('btn').onclick = function() {
// 懒加载
import(/* webpackChunkName: 'test' */'./test').then(({mul}) => {
console.log(mul(4,5));
});
}

预加载的开启方式

只需在动态导入的时候,添加如下的参数即可。

webpackPrefetch: true

完整配置请看下面的代码。

console.log("index.js文件被加载了~");

document.getElementById('btn').onclick = function() {
// 懒加载
import(/* webpackChunkName: 'test',webpackPrefetch: true */'./test').then(({mul}) => {
console.log(mul(4,5));
});
}

懒加载、预加载、正常加载的区别

  • 懒加载:当文件需要用到的时候才会去加载,不用的时候不加载。
  • 正常加载:正常的时候是并发去加载,但是一般受到6个个数的限制。
  • 预加载:等其他资源加载完毕后,浏览器空闲了,再加载资源,但是不会运行这个文件。

PWA的使用方法

什么是PWA?PWA有什么用?

PWA可以让我们在离线的时候依然能够正常放问指定的资源。

如何实现PWA?

  1. 安装插件
npm i workbox-webpack-plugin@5.0.0
  1. 引入插件
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
  1. 在webpack.config.js中配置插件

下面的配置会生成一个serviceworker配置文件。

new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
})
  1. 在package.json中配置eslint允许其访问浏览器的全局变量
  "eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
}
  1. 在入口文件处注册serviceWorker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/service-worker.js')
.then(() => {
console.log('sw注册成功~');
})
.catch(() => {
console.log('注册出错!');
});
});
}
  1. 通过服务器运行测试(sw代码必须运行在服务器上)
serve -s build

上述的代码会将build文件当做静态文件对外允许访问。

多进程打包

为什么要有多进程打包?

之所以要有多进程打包,是为了提高打包的速度。

如何开启多线程打包?

  1. 安装插件
npm i thread-loader@2.1.3
  1. 在需要开启的loader前面添加下面的代码

重要的是下面的babel-loader。

{
// 进行js兼容性的处理
test: /\.js$/,
exclude: /node_modules/,
use: [
// 开启多进程打包
'thread-loader',
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
],
cacheDirectory: true
}
}
]
}

指的注意的是:当资源比较小的时候,通过上面的代码甚至会更加耗时,这是正常现象,因为启动也需要一定的时间。


JustinWebpack阅读需 3 分钟

什么是tree shaking?

我们可以将我们的项目比作一棵树,树上每一个用到的第三方模块或者函数都可以比作一个绿色的树叶,但是我们的项目中可能存在很多没有被用到的模块或者函数,这些就是需要我们摇晃掉的树叶,这个过程我们叫做tree shaking。

如何开启tree shaking?

开启tree shaking只需满足下面两个条件,即可自动开启。

  1. 必须使用ES6模块化。
  2. 开启production环境。

如果想要把所有的代码都进行tree shaking还需要在package.json中进行如下的配置。

  • package.json
"sideEffects": ["*.css"]

Webpack中设置多入口文件

  1. 通过对象的形式个entry进行赋值
  entry: {
main: './src/js/index.js',
test: './src/js/test.js'
}
  1. 给output指定输出的文件名以便于区分。
  output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
}

设置公用的模块只打包生成一次

之所以设置将公用的模块之打包一次,是为了进行复用,举个栗子,A文件和B文件都引用了Jquery,不使用下面的配置进行打包的话,会将jquery这个模块打包两次,这显然不合理。但是,通过下面的配置允许只打包一次。(在多入口的情况下)

optimization: {
splitChunks: {
chunks: 'all'
}
}

通过JS代码,让某个文件被单独打包成一个chunk

这种使用方式适用于单入口文件的情况。只需在使用的地方,通过下面的使用方式即可。

import(/* webpackChunkName: 'test' */'./test')
.then(({ mul, count }) => {
// eslint-disable-next-line
console.log(mul(2, 5));
})
.catch(() => {
// eslint-disable-next-line
console.log('文件加载出错');
})

下面给出完整代码,以方便大家理解。

function sum(...args) {
return args.reduce((p, c) => p + c, 0);
}

import(/* webpackChunkName: 'test' */'./test')
.then(({ mul, count }) => {
// eslint-disable-next-line
console.log(mul(2, 5));
})
.catch(() => {
// eslint-disable-next-line
console.log('文件加载出错');
})


// eslint-disable-next-line
console.log(mul(2, 3));
// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

JustinWebpack阅读需 2 分钟

source-map是什么?

source-map是一种提供构建后代码到源代码的映射技术,可以根据这个映射来追踪源代码的错误。

设置source-map的方法

我们只需在webpack.config.js中添加一个配置项即可。

devtool: "source-map"

选择类型

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

oneOf是什么?

有时候,我们在webpack配置中写了很多的loader,这也就意味着,一个文件要被多个loader过滤一遍,这在有些情况下是很冗余的,因此,我们希望只匹配一个loader,这就是我们为什么需要oneOf的原因。

oneOf如何使用?

module: {
rules: [
{
// 进行Eslint检查
test: /\.js$/,
// 排除其他文件
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 设置自动修复
fix: true
}
},
{
oneOf: [
{
// 处理CSS
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},

{
// 进行js兼容性的处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
// 打包图片资源
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
// 处理HTML中的图片资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
}

需要注意的是:被oneOf包裹的所有loader中,不能存在两个配置处理同一种类型的文件,如果存在这样的两个loader,可以将其中一个提取出来,就像上面的那种处理方式一样。

缓存

之所以需要缓存的原因在于,以babel为例,对一个项目进行编译,如果每次都是对整个项目进行重新编译比较浪费资源,因为我们可能只是对项目中进行了细微的改动,这就是为什么需要缓存的原因。

babel-loader开启缓存

只需给babel-loader的options中添加一个配置项即可。

{
// 进行js兼容性的处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
],
cacheDirectory: true
}
}

文件资源缓存

hash配置

每次进行webpack打包的时候,给文件生成不同的文件名,这样静态资源的引入路径名也会发生变化,所以一旦进行重新打包,即使是强缓存,也能够感知到最新的变化,因为文件名变啦。

  • 修改js文件的哈希后缀
output: {
filename: 'js/built.[hash:10].js',
path: resolve(__dirname, 'build')
}
  • 修改CSS文件的哈希后缀
new MiniCssExtractPlugin({
filename: 'css/built.[hash:10].css'
})

注意:上面的这种方式存在一个缺点,就是一旦重新打包,会导致所有的缓存失效,即使只改动了一个文件。

contenthash(推荐使用)

contenthash根据文件的内容生成hash值,不同文件hash值一定不一样。

  • 处理JS文件
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
}
  • 处理CSS文件
new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css'
})

JustinWebpack阅读需 3 分钟

在文章开始之前,首先想让大家知道一个知识点,那就是loader的加载顺序问题,是从下到上开始进行加载的。

定义nodejs的环境变量,决定使用browserslist的哪个环境

process.env.NODE_ENV = 'production';

复用loader

首先定义好需要复用的loader。

const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
// 还需要再package.json中定义browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
]

在需要进行使用的地方,通过拓展运算符来进行使用。

module: {
rules: [
{
// 处理CSS
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader,'less-loader']
}
]
}

生产环境下基本文件的配置方式

下面的配置包含了HTML、CSS、JS、图片、压缩、兼容性等的处理方式。

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 下面的这个插件是压缩CSS的插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// 定义nodejs的环境变量,决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
// 还需要再package.json中定义browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
]

module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
// 处理CSS
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
{
// 进行Eslint检查
test: /\.js$/,
// 排除其他文件
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 设置自动修复
fix: true
}
},
{
// 进行js兼容性的处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
// 打包图片资源
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
// 处理HTML中的图片资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: "production"
}

使用HMR

什么是HMR?

HMR可以理解为模块热替换,所谓的模块热替换指的是一个模块重新发生变化,只会重新打包这一个模块,而不是打包所有的模块,这可以极大的提升构建速度。

开启HMR

只需在devServer中开启hot为true即可。

devServer: {
contentBase: resolve(__dirname,'build'),
compress: true,
port: 3000,
open: true,
// 开启HMR功能
hot: true
}

哪些文件支持HMR,哪些文件不支持HMR?

支持HMR的文件

样式文件(CSS文件)可以使用HMR功能,因为style-loader内部实现了。

不支持HMR的文件

  • HTML文件:HTML文件默认不能使用HMR功能,并且设置了HMR会使得HTML的热更新失效,如果要解决这个热更新的问题,需要修改entry入口。
entry: ['./src/index.js','./src/index.html']
  • JS文件:默认不能使用HMR功能的,如果想要使用HMR功能,需要添加支持HMR功能的代码。

下面我们在入口文件中来检测print.js这个文件的变化,只有这个文件发生了变化的时候,才会热加载这个模块,并且只加载这个模块。

if (module.hot) {
module.hot.accept('./print.js',function() {
// 一旦print.js这个文件发生了变化,会执行下面的函数
print();
})
}

注意:上面的这段代码是添加到入口文件中的。


JustinWebpack阅读需 3 分钟

一、引入Eslint

  1. 安装相关工具包
npm install eslint eslint-loader eslint-config-airbnb-base eslint-plugin-import
  1. loader配置的核心写法
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复
fix: true
}
}
]
}
  1. 在package.json中引入下面的配置
  "eslintConfig": {
"extends": "airbnb-base"
}
  1. 如果想要忽略某些语句,可以在语句的上方通过下面的注释来实现。
// eslint-disable-next-line

二、JS兼容性处理

方式1:基本兼容处理(缺点:并不能完全兼容新特性)

  1. 安装相关工具包
npm install babel-loader @babel/preset-env @babel/core
  1. 配置核心module
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
}

上面提到的方法只能解决基础的兼容性问题,却不能解决类似于Promise等ES6语法的兼容性问题,如果想要实现全部的兼容可以通过下面的方法。

方式2:全部兼容处理(缺点:占用内存大)

  1. 安装babel/polyfill
npm i @babel/polyfill@7.8.3
  1. 无需进行配置,只需在需要进行兼容处理的js文件中进行引入即可。
import '@babel/polyfill';

方式3:按需处理(推荐方式1+3)

  1. 安装相关模块
npm i core-js@3.6.4
  1. 核心webpack配置文件(重点看module部分)
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
corejs: {
version: 3
},
// 指定兼容性做到哪个版本的浏览器
targets: {
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17"
}
}]
]
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
}

三、压缩JS

只要我们开启了生产环境JS会自动帮我们压缩代码。

mode: 'production'

四、压缩HTML

如果想要压缩HTML的话,只需在HtmlWebpackPlugin插件中添加如下配置即可。

  plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
// 移出空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
]

JustinWebpack阅读需 2 分钟

配置dev-server

安装相关包

npm i webpack-dev-server@3.10.3

增加配置项

  devServer: {
contentBase: resolve(__dirname,'build'),
compress: true,
port: 3000,
open: true
}

下面我们对配置项进行一一解读。

  • contentBase:指定服务器的启动路径。
  • compress:是否进行压缩。
  • port:服务器的启动端口号。
  • open: 是否默认打开浏览器。

进行完上面的配置后,我们便可以实现在源文件中编写代码,自动编译打包了。

搭建开发环境

下面的开发环境具有以下几个特点:

  • 能够处理Less资源
  • 能够处理CSS资源
  • 能够处理图片资源
  • 能够处理HTML中的img资源
  • 能够处理字体等其他资源
  • 能够将打包的文件加入到HTML中。
  • 包含devServer
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname,'build')
},
module: {
rules: [
{
// 处理less资源
test: /\.less$/,
use: ['style-loader','css-loader','less-loader']
},
{
// 处理CSS资源
test: /\.css$/,
use: ['style-loader','css-loader']
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭es6模块
esModule: false
}
},
{
// 处理html中的img资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname,'build'),
compress: true,
port: 3000,
open: true
}
}

将文件打包输出到指定的路径

built.js

通过路径拼接的形式。

output: {
filename: 'js/built.js',
path: resolve(__dirname,'build')
}

图片资源

在options中添加outPath配置项

{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭es6模块
esModule: false,
outputPath: 'imgs'
}
}

提取多个CSS文件为单独文件

之所以要提取CSS文件为单独文件,是因为webpack在打包的过程中默认将CSS代码写入到JS文件中了,所以我们希望的是CSS打包后变成单独的文件。

  • 核心配置文件
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
})
],
mode: 'development'
}

CSS兼容性处理

  1. 安装指定插件
npm i postcss-loader@3.0.0 postcss-preset-env@6.7.0 -D
  1. 在package.json中添加下面的配置
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
  1. webpack配置文件
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 设置nodejs环境变量
process.env.NODE_ENV = 'development';

module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
require('postcss-preset-env')()
]
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
})
],
mode: 'development'
}

压缩CSS

  1. 安装指定版本的插件
npm i optimize-css-assets-webpack-plugin@5.0.3
  1. 引入插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
  1. 调用插件
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
new OptimizeCssAssetsWebpackPlugin()
]

JustinWebpack阅读需 3 分钟

安装指定版本的webpack

npm i webpack@4.41.6 webpack-cli@3.3.11 -D

安装指定版本的loader

npm i css-loader@3.4.2 style-loader@1.1.3 -D

配置Webpack使得JS能够处理CSS样式

下面是配置文件webpack.config.js

// 引入用于拼接绝对路径的方法

const { resolve } = require('path');

module.exports = {
// 入口起点
entry: './src/index.js',
// 输出
output: {
// 输出文件名
filename: 'built.js',
// 输出路径
path: resolve(__dirname,'build')
},
module: {
rules: [
{
// 匹配哪些文件
test: /\.css$/,
use: [
// use数组中loader执行顺序是从下到上,依次执行
// 创建style标签,将JS中的样式资源插入其中,添加到header标签中
'style-loader',
'css-loader'
]
}
]
},
// plugins的配置
plugins: [

],
mode: 'development'
}

执行上述的配置命令只需在根目录输入webpack回车即可将打包后的文件输出到指定的路径。

配置Webpack能够处理less样式

只需在webpack的配置文件中的rules下面添加下面的规则即可,需要安装下面的工具包。

npm i less-loader@5.0.0 less@3.11.1 -D
{
// 匹配哪些文件
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}

配置Webpack能够处理HTML资源

主要是引入插件HtmlWebpackPlugin,这个插件能够将打包后的所有资源自动引入到HTML文件中。

主要配置如下:

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

module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname,'build')
},
module: {
rules: [

]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
}

打包图片资源

下面我们首先看看主要的webpack配置文件的代码,然后我将带着大家逐一分析每一个loader的作用。

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


module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname,'build')
},
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader']
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
// 小于限制大小的图片资源将采用base64编码
limit: 40 * 1024,
esModule: false,
name: '[hash:10].[ext]'
}
},
{
test: /\.html$/,
// 负责处理HTML中的图片处理
loader: 'html-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
}
  • style-loader、css-loader、less-loader

将处理好的css样式、less样式插入到style标签中。

  • url-loader

将指定格式的图片进行打包,但是这个loader只能打包css或less文件中的图片,无法打包HTML中引入的图片,因此我们引入了下面的html-loader。

  • html-loader

打包其他资源

这里我们以字体图标为例,来演示如何进行打包,主要是打包后缀为ttf的文件,主要是使用file-loader来进行打包。

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname,'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
{
// 打包其他资源,使用排除的方式
exclude: /\.(css|js|html)$/,
loader: 'file-loader',
options: {
// 定义哈希值的长度
name: '[hash:10].[ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
}

JustinWebpack阅读需 3 分钟