CommonJS
核心思想是通过 require
方法来 同步加载依赖 的其他模块,通过 module.exports
导出需要暴露的接口// 导入
const moduleA = require ( './moduleA');
// 导出
module.exports = moduleA.someFunc;
AMD
与 CommonJS
最大的不同在于,它采用了 异步的方式去加载依赖 的模块。AMD
规范主要用于解决针对浏览器环境的模块化问题,最具代表性的实现是 requirejs.org
// 定义一个模块
define ('module', [ 'dep'] , function (dep) {
return exports;
});
//导入和使用
require (['module'] ,function (module){
});
Entry
: 入口,Webpack
执行构建的第一步将从 Entry
开始,可抽象成输入。
Module
: 模块,在 Webpack
里一切皆模块,一个模块对应一个文件。Webpack
会从配置的 Entry
开始递归找出所有依赖的模块。
Chunk
: 代码块,一个Chunk
由多个模块组合而成,用于代码合并与分割。
Loader
: 模块转换器,用于将模块的原内容按照需求转换成新内容。
Plugin
: 扩展插件,在 Webpack
构建流程中的特定时机注入扩展逻辑,来改变构 建结果或做我们想要的事情。
Output
: 输出结果,在Webpack
经过一系列处理并得出最终想要的代码后输出结果。
Webpack
在启动后会从Entry
里配置的Module
开始,递归解析Entry
依赖的所有Module
。每找到一个Module
,就会根据配置的Loader
去找出对应的转换规则,对Module
进行转换后, 再解析出当前Module
依赖的Module
。 这些模块会以Entry
为单位进行分组, 一个Entry
及其所有依赖的Module
被分到一个组也就是一个Chunk
。最后,Webpack
会将所有Chunk
转换成文件输出。在整个流程中,Webpack
会在恰当的时机执行Plugin
里定义的逻辑。
context
Webpack
在寻找相对路径的文件时会以 context
为根目录, context
默认为执行启动 Webpack
时所在的当前工作目录。context
的默认配置,则可以在配置文件里设置它 :module.exports = {
context: path.resolve (__dirname ,'app')
}
Entry
配置模块的入口,必填。
类型 | 列子 | 含义 |
---|---|---|
string |
'. / app/entry ' |
入口模块的文件路径,可以是相对路径 |
array |
[' ./app/entryl’,'./app/entry2'] |
入口模块的文件路径,可以是相对路径 |
object |
{a: '.app/entry-a', b: ['./app/entry-bl', './app/entry-b2']} |
配置多个入口,每个入口生成一个 Chunk |
如果 entry
是一个 string
或 array
,就只会生成一个 Chunk
,这时 Chunk
的名 称是main
。
如果 entry
是一个 object
,就可能会出现多个 Chunk
,这时 Chunk
的名称是 object
键值对中键的名称。
Output
filename
变量名 | 含义 |
---|---|
id |
Chunk 的唯一标识,从 0 开始 |
name |
Chunk 的名称 |
hash |
Chunk 的唯一标识的 Hash 值 |
chunkhash |
Chunk 内容的 Hash 值 |
hash
和 chunkhash
的长度是可指定的,[ hash:8]
代表取 8
位 Hash
值,默认是 20
位hash
和 chunkhash
和 contenthash
区别hash
: 只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值
chunkhash
:根据不同的入口文件进行依赖文件解析、构建对应的 chunk
,生成对应的哈希值。
contenthash
: 使用extra-text-webpack-plugin
里的contenthash
值,保证即使css
文件所处的模块里就算其他文件内容改变,只要css
文件内容不变,那么不会重复构建
chunkFilename
Chunk
在输出时的文件名称。会在运行时生成 Chunk
的常见场景包括:使用 CommonChunkPlugin
、使用 import ('path/to/module')
动态加载等path
string
类型的绝对路径publicPath
URL
前缀,为 string
类型。默认值是空字符串 ""
,即使用相对路径。Module
rules
配置模块的读取和解析规则,通常用来配置 Loader
。其类型是一个数组,数组里
的每一项都描述了如何处理部分文件。配置一项 rules 时大致可通过以下方式来完成。
条件匹配: 通过 test
、 include
、 exclude
三个配置项来选中 Loader
要应用规则的文件。
应用规则: 对选中的文件通过 use
配置项来应用 Loader
,可以只应用一个 Loader
或者按照从后往前的顺序应用一组 Loader
,同时可以分别向 Loader
传入参数。
重置顺序: 一组 Loader
的执行顺序默认是从右到左执行的,通过 enforce
选项可以将其中 一个 Loader
的执行顺序放到最前(pre
)或者最后(post
) 。
noParse
noParse
配置项可以让 Webpack
忽略对部分没采用模块化的文件的递归解析和处理,这 样做的好处是能提高构建性能。Resolve
Resolve
配置 Webpack
如何寻找模块所对应的文件。Webpack
内置 JavaScript
模块化语法解析功能,默认会采用模块化标准里约定的规 则去寻找,但也可以根据自己的需要修改默认的规则。alias
// Webpack alias 配置
resolve:{
alias : {
components : './src/components/'
}
}
当通过
import Button from 'components/button'
导入时,实际上被alias
等 价替换成了import Button from './src/components/button '
。
mainFields
Webpack
会根据 mainFields
的配置去决定 优先采用哪份代码, mainFields
默认如下:
mainFields : ['browser', 'main']
,
Webpack
会按照数组里的顺序在 package.json
文件里寻找,只会使用找到的第 1
个
假如我们想优先采用 ES6
的那份代码,则可以这样配置: mainFields: ['jsnext:main','browser','main']
extensions
在导入语句没带文件后缀时, Webpack
会自动带上后缀后去尝试访问文件是否存在。
resolve.extensions
用于配置在尝试过程 中用到的后缀列表,默认是:
extensions: ['.js','.json']
也就是说,当遇到 require ('./data ')
这样的导入语句时, Webpack
会先寻找./data.js
文件,如果该文件不存在 , 就去寻找 ./data.json
文件,如果还 是找不到,就报错 。 假如我们想让 Webpack
优先使用目录下的 Typescript
文件,则可以这样配置:
extensions:['.ts','.j5','.json']
modules
配置 Webpack
去哪些目录下寻找第三方模块,默认是只会去 node_modules
目录下寻找。
有时项目里会有一些模块会大量被其它模块依赖和导入,由于其它模块的位置分布不定,针对不同的文件都要去计算被导入模块文件的相对路径,这个路径有时候会很长,就像这样 import '../../../components/button'
这时你可以利用 modules
配置项优化,假如那些被大量导入的模块都在 ./src/components
目录下,把 modules
配置成 modules:['./src/components','node_modules']
后,你可以简单通过 import 'button'
导入。
descriptionFiles
package.json
文件。默认如下:descriptionFiles: ['package.json']
enforceExtension
true
所有导入语句都必须要带文件后缀,import './foo'
能正常工作,开启后就必须写成import './foo.js'
。enforceModuleExtension
enforceModuleExtension
和 enforceExtension
作用类似,但 enforceModuleExtension
只对 node_modules
下的模块生效。 enforceModuleExtension
通常搭配 enforceExtension
使用,在 enforceExtension:true
时,因为安装的第三方模块中大多数导入语句没带文件后缀, 所以这时通过配置 enforceModuleExtension:false
来兼容第三方模块。Plugin
plugin
的实例。const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
...
new webpack.optimize.UglifyJsPlugin()
...
]
...
};
DevServer
DevServer
去启动webpack
时配置文件里devServer
才会生效。hot
devServer.hot
配置是否启用模块热替换功能。DevServer
默认是在发现源代码被更新后会通过自动刷新整个页面来做到实现预览,开启模块热替换功能后在不刷新整个页面的情况下通过用心模块替换老模块来实现实时预览。inline
DevServer
的实时预览功能依赖注入到页面里的代理客户端去接受来自DevServer
的命令和负责刷新网页的工作。devServer.inline
用于配置是否自动注入这个代理客户端到将运行在页面的Chunk
里面,默认是会自动注入。DevServer
会根据你是否开启inline
来调整它的自动刷新策略:
- 如果开启
inline
,DevServer
会在构建完变化后的代码时通过代理客户端控制网页刷新。- 如果关闭
inline
,DevServer
将无法直接控制要开发的网页。这时它会通过iframe
的方式去运行要开发的网页,当构建完变化后的代码通过刷新iframe
来实现实时预览。
historyApiFallback
devServer.historyApiFallback
用于方便的开发使用HTML5
HistroyAPI
的单页应用。historyApiFallback: {
//使用正则匹配命中路由
rewrites: [
{
from: /^\/user/, to: '/user.html'},
{
from: /^\/game/, to: '/game.html'},
{
from: /./, to: '/index.html'}
]
}
contentBase
devServer.contentBase
配置DevServer
HTTP
服务器的文件根目录。默认情况下为当前执行目录,所以一般不必设置它,除非有额外的文件需要被DevServer
服务。例如你想把项目根目录下的public
目录设置成DevServer
服务器的文件根目录:devServer: {
contentBase: path.join(__dirname, 'public')
}
- 暴露本地文件
- 暴露
webpack
构建出的结果,由于构建出的结果交给DevServer
,所以你在使用DevServer
时在本地找不到构建出的文件。
contentBase
只能用来配置暴露本地文件的规则,你可以通过contentBase: false
来关闭暴露本地文件。
headers
devServer.headers
配置项可以在 HTTP
响应中注入一些HTTP
响应头,使用如下:devServer: {
headers: {
'X-foo': 'bar'
}
}
host
devServer.host
配置项用于配置DevServer
服务器监听的地址。port
devServer.port
配置项用于配置DevServer
服务监听的端口,默认使用8080
端口。allowedHosts
devServer.allowedHosts
配置一个白名单列表,只有HTTP
请求的HOST
在列表里才正常返回,使用如下:allowedHosts: [
//匹配单个域名
'host.com',
'sub.host.com',
// host.com 和所有的子域名 *.host.com 都将匹配
'.host.com'
]
disableHostCheck
devServer.disableHostCheck
配置项用于配置是否关闭用于DNS
重绑定的HTTP
请求的host
检查。DevServer
默认只接受来自本地的请求,关闭后可以接受来自任何 HOST
的请求。https
DevServer
默认使用 HTTP
协议服务,它也能通过 HTTPS
协议服务。devServer:{
https: true
}
DevServer 会自动的为你生成一份 HTTPS 证书
devServer:{
https: {
key: fs.readFileSync('path/to/server.key'),
cert: fs.readFileSync('path/to/server.crt'),
ca: fs.readFileSync('path/to/ca.pem')
}
}
clientLogLevel
devServer.clientLogLevel
配置在客户端的日志等级,可取如下之一的值 none
、error
、 warning
、 info
。 默认为 info
级别,即输出所有类型的日志,设置成 none
可以不输出任何日志。compress
devServer.compress
配置是否启用 gzip
压缩。boolean 为类型,默认为 false
。open
devServer.open
用于在DevServer
启动且第一次构建完时自动用你系统上默认的浏览器去打开要开发的网页。 同时还提供 devServer.openPage
配置项用于打开指定 URL
的网页。Target
target
配置项可以让webpack
构建出针对不同运行环境的代码。target可以是以下之一:
web
: 针对浏览器(默认),所有代码都集中在一个文件里node
:针对node
,使用require
语句加载chunk
代码async-node
: 针对node
, 异步加载Chunk
代码webworker
:针对webworker
electron-main
:针对Electron
主线程electron-renderer
:针对electron
渲染线程
Devtool
devtool
配置webpack
如何生成Source Map
, 默认值是false
,即不生成Source Map
, 想为构建出的代码生成Source Map
以方便调试,可以这样配置:module.exports = {
devtool: 'scource-map'
}
watch
和watchOptions
webpack
的监听模式,支持监听文件更新,在文件发生变化时重新编译。webpack
时监听模式默认是关闭的,想打开需要如下配置:module.exports = {
watch: true
}
DevServer
时,监听模式默认是开启的。webpack
还提供了watchOptions
配置项去更灵活的控制监听模式:module.export = {
//只有开启监听模式的时候,watchOptions才有意义
watch: true,
//监听模式运行时参数
watchOptions: {
//不监听的文件或文件夹,支持正则匹配
//默认为空
ignored: /node_modules/,
//监听到变化后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
//默认为300ms
aggregateTimeout: 300,
//判断文件是否发生变化是通过不停的询问系统指定文件有没有变化实现的
//默认每秒问1000次
poll: 1000
}
}
Externals
Externals
用来告诉webpack
要构建的代码中使用了哪些不用被打包的模块,也就是说这些模板是外部环境提供的,webpack
在打包的时候可以忽略它们。例如:,引入
jquery
后,全局变量jquery
就会被注入到网页的JavaScript
运行环境。
如果想在使用模块化的源代码里导入和使用jquery
,可能需要这样:import $ from 'jquery';
构建后会发现输出的Chunk
中包含jquery
库的内容,这导致了jquery
库出现了两次,浪费了加载流量,我们做到的最好是不要在chunk
里包含jquery
。
module.export = {
externals: {
//把导入语句里的jquery替换成运行环境里的jQuery
jquery: 'jQuery'
}
}
ResolveLoader
ResolveLoader
告诉webpack
如何去寻找loader
。因为在使用loader
时是通过其包名称去引用的,webpack
需要根据配置的loader
包名去找到loader
的实际代码,以调用loader
去处理源文件。module.exports = {
resolveLoader:{
// 去哪个目录下寻找 Loader
modules: ['node_modules'],
// 入口文件的后缀
extensions: ['.js', '.json'],
// 指明入口文件位置的字段
mainFields: ['loader', 'main']
}
}
loader
。webpack
的整体配置结构const path = require('path')
module.exports = {
//entry表示入口, webpack执行构建的第一步将从entry开始,可抽象成输入。
//类型可以是string | object | array
entry: './app/entry', //只有1个入口,入口只有一个文件
entry: ['./app/entry1', './app/entry2'], //只有一个入口,入口有两个文件
entry: {
//有2个入口
a: './app/entry-a',
b: ['./app/entry-b1', './app/entry-b2']
},
//如何输出结果: 在webpack经过一系列处理后,如何输出最终想要的代码。
output: {
//输出文件存放的目录, 必须是string类型的绝对路径
path: path.join(__dirname, 'dist')
//输出文件的名称
filename: 'bundle.js', //完整的名称
filename: '[name].js', //当配置多个entry时,通过名称模板为不同的entry生成不同的文件名称
filename: '[chunkhash].js', //根据文件内容hash值生成文件名称,用于浏览器长时间缓存
//发布到线上的所有资源的URL前缀,string类型
publicPath: '/assets/', //放到指定目录下
publicPath: '', //放到根目录下
publicPath: 'https://cdn.example.com/'放到cdn上去
//导出库的名称, string类型
//不填它时, 默认输出格式是匿名的立即执行函数
library: 'MyLibrary',
//导出库的类型,枚举类型,默认是var
//可以是umd | umd2 | commonjs2 | commonjs | amd | this | var。。。
libraryTarget: 'umd'
//是否包含有用的文件路径信息到生成的代码里去,boolean类型
pathinfo: true,
//附加Chunk的文件名称
chunkFilename: '[id].js',
chunkFilename: '[chunkhash].js'
//JSONP异步加载资源时的回调函数名称, 需要和服务端搭配使用
jsonpFunction: 'myWebpackJsonp'
//生成的Source Map文件名称
sourceMapFilename: '[file].map',
//浏览器开发者工具里显示的源码模块名称
devtoolModuleFilenameTemplate: 'webpack:///[resource-path]',
//异步加载跨域资源时使用的方式
crossOriginLoading: 'use-credentials',
crossOriginLoading: 'anonymous',
crossOriginLoading: false,
},
//配置模块相关
module: {
//配置loader
rules: [
{
test: /\.jsx?$/, //正则匹配命中要使用loader的文件
include: [ //只会命中这里面的文件
path.resolve(__dirname, 'app')
],
exclude: [ //忽略这里的文件
path.resolve(__dirname, 'app/demo-files')
],
use: [ //使用哪些loader
'style-loader', //直接使用loader名称
{
loader: 'css-loader',
options: {
//loader传递参数
}
}
]
}
],
noParese: [ //不用解析和处理的模块
/special-library\.js$/ // 用正则匹配
]
},
//配置插件
plugins: [],
//配置寻找模块的规则
resolve: {
modules: [ //寻找模块的根目录,array类型,默认以node_modules为根目录
'node_modules',
path.resolve(__diranme, 'app')
],
extensions: ['.js','.json','.jsx','.css'], //模块的后缀名
alias: {
//模块别名配置, 用于映射模块
//把module映射new-module, 同样的'module/path/file'也会被映射到new-module/path/file
'module': 'new-module',
//使用结尾符号$后,把'only-module'映射成'new-module'
//但是不想上面的,'module/path/file',不会被映射成new-module/path/file
'only-module$': 'new-module'
},
alias: [ //alias还支持使用数组来详细配置
{
name: 'modules' //老的模块
alias: 'new-module', //新的模块
//是否是只映射模块,如果是true只有'module'会被映射,如果是 false 'module/inner/path' 也会被映射
onlyModule: true
}
],
symlinks: true, //是否跟随文件软链接去搜寻模块的路径
descriptionFile: ['package.json'], //模块的描述文件
mainFields: ['main'], 模块的描述文件里的描述入口的文件的字段名称
enforceExtension: false, //是否强制导入语句必须要写明文件后缀
},
//输出文件性能检查配置
performance: {
hints: warning, //有性能问题时输出警告
hints: 'error', //有性能问题时输出错误
hints: false, //关闭性能检查
maxAssetSize: 200000, //最大文件大小,单位bytes
maxEntrypointSize: 400000, //最大入口文件大小(单位bytes)
assetFilter: function(assetFilename) {
//过滤要检查的文件
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js')
}
},
devtool: 'source-map', //配置source-map类型
context: __dirname, //webpack使用的根目录,string类型必须是绝对路径
//配置输出代码的运行环境
target: 'web', // 浏览器,默认
target: 'webworker', // WebWorker
target: 'node', // Node.js,使用 `require` 语句加载 Chunk 代码
target: 'async-node', // Node.js,异步加载 Chunk 代码
target: 'node-webkit', // nw.js
target: 'electron-main', // electron, 主线程
target: 'electron-renderer', // electron, 渲染线程
externals: {
//使用来自JavaScript运行环境提供的全部变量
jquery: 'jQuery'
},
stats: {
// 控制台输出日志控制
assets: true,
colors: true,
errors: true,
errorDetails: true,
hash: true,
},
devServer: {
// DevServer 相关的配置
proxy: {
// 代理到后端服务接口
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, 'public'), // 配置 DevServer HTTP 服务器的文件根目录
compress: true, // 是否开启 gzip 压缩
historyApiFallback: true, // 是否开发 HTML5 History API 网页
hot: true, // 是否开启模块热替换功能
https: false, // 是否开启 HTTPS 模式
},
profile: true, // 是否捕捉 Webpack 构建的性能信息,用于分析什么原因导致构建性能不佳
cache: false, // 是否启用缓存提升构建速度
watch: true, // 是否开始
watchOptions: {
// 监听模式选项
// 不监听的文件或文件夹,支持正则匹配。默认为空
ignored: /node_modules/,
// 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
// 默认为300ms
aggregateTimeout: 300,
// 判断文件是否发生变化是不停的去询问系统指定文件有没有变化,默认每秒问 1000 次
poll: 1000
},
}
file-loader
...
rules:[
...
{
test: /\.png$/,
use: ["file-loader"]
},
...
]
...
base64
url-loader
...
rules:[
...
{
test: /\.png$/,
use: [{
loader: "file-loader",
options:{
limit: 1024 * 30,
failback: "file-loader"
}
}]
},
...
]
...
imagemin-webpack-plugin
const ImageminPlugin = require('imagemin-webpack-plugin').default
module.exports = {
plugins: [
new ImageminPlugin({
disable: process.env.NODE_ENV !== 'production',
pngquant: {
quality: '95-100'
}
})
]
}
webpack-spritesmith
const path = require('path');
const SpritesmithPlugin = require('webpack-spritesmith');
module.exports = {
// ...
module: {
rules: [
{
test: /\.png$/, use: [
'file-loader?name=i/[hash].[ext]'
]}
]
},
resolve: {
modules: ["node_modules", "spritesmith-generated"]
},
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/ico'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl')
},
apiOptions: {
cssImageRef: "~sprite.png"
}
})
]
// ...
};
SVG
压缩 svg-inline-loader
...
rules:[
...
{
test: /\.svg/,
use: ["svg-inline-loader"]
},
...
]
...
使用 Loader
时,可以通过 test
、 include
、 exclude
三个配置项来命中 Loader
要应用规则的文件
resolve.modules
用于配置 Webpack
去哪些目录下寻找第三方模块。默认值是 ["node modules "]
。使用绝对路径指明第三方模块存放的位置,以减少搜索步骤。
resolve.mainFields
用于配置第三方模块使用哪 个入口文件(在其package. json
中)。
resolve.mainFields
的默认值和当前的target
配置有关系,对应的关系如 下。
- 当
target
为web
或者webworker
时,值是['browser','module','main']
。- 当
target
为其他情况时,值是[ 'module','main']
。
resolve.alias
配置项通过别名来将原导入路径映射成一个新的导入路径。
resolve.extensions
用于配置在尝试过程中用到的后缀列表,默认是:
extensions : [ '.js' , '.json']
module.noParse
配置项可以让 Webpack
忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能
动态链接库 DllPlugin 插件
将任务分解给多个子进程去并发执行,子进程处理完后再将结果发送给主进程。HappyPack插件
ParallelUglifyPlugin插件
会开启多个子进程,将对多个文件的压缩工作分配给多个 子进程去完成.
UglifyJsPlugin
压缩 JS
uglifyjs-webpack-plugin
压缩 ES6
开启 css-loader
的 minimize
选项压缩 CSS
借助 publicpath
开启 CDN
Tree Shaking
去除死代码
CommonsChunkPlugin
提取公共代码
按需加载 import (***).then()
Prepack
优化代码运行效率
Scope Hoisting
可以让 Webpack
打包出来的代码文件更小、运行更快
// 侧重优化开发体验
const path = require('path');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const {
AutoWebPlugin} = require('web-webpack-plugin');
const HappyPack = require('happypack');
// 自动寻找 pages 目录下的所有目录,把每一个目录看成一个单页应用
const autoWebPlugin = new AutoWebPlugin('./src/pages', {
// HTML 模版文件所在的文件路径
template: './template.html',
// 提取出所有页面公共的代码
commonsChunk: {
// 提取出公共代码 Chunk 的名称
name: 'common',
},
});
module.exports = {
// AutoWebPlugin 会找为寻找到的所有单页应用,生成对应的入口配置,
// autoWebPlugin.entry 方法可以获取到生成入口配置
entry: autoWebPlugin.entry({
// 这里可以加入你额外需要的 Chunk 入口
base: './src/base.js',
}),
output: {
filename: '[name].js',
},
resolve: {
// 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
// 其中 __dirname 表示当前工作目录,也就是项目根目录
modules: [path.resolve(__dirname, 'node_modules')],
// 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件,使用 Tree Shaking 优化
// 只采用 main 字段作为入口文件描述字段,以减少搜索步骤
mainFields: ['jsnext:main', 'main'],
},
module: {
rules: [
{
// 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
test: /\.js$/,
// 使用 HappyPack 加速构建
use: ['happypack/loader?id=babel'],
// 只对项目根目录下的 src 目录中的文件采用 babel-loader
include: path.resolve(__dirname, 'src'),
},
{
test: /\.js$/,
use: ['happypack/loader?id=ui-component'],
include: path.resolve(__dirname, 'src'),
},
{
// 增加对 CSS 文件的支持
test: /\.css$/,
use: ['happypack/loader?id=css'],
},
]
},
plugins: [
autoWebPlugin,
// 使用 HappyPack 加速构建
new HappyPack({
id: 'babel',
// babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
loaders: ['babel-loader?cacheDirectory'],
}),
new HappyPack({
// UI 组件加载拆分
id: 'ui-component',
loaders: [{
loader: 'ui-component-loader',
options: {
lib: 'antd',
style: 'style/index.css',
camel2: '-'
}
}],
}),
new HappyPack({
id: 'css',
// 如何处理 .css 文件,用法和 Loader 配置中一样
loaders: ['style-loader', 'css-loader'],
}),
// 4-11提取公共代码
new CommonsChunkPlugin({
// 从 common 和 base 两个现成的 Chunk 中提取公共的部分
chunks: ['common', 'base'],
// 把公共的部分放到 base 中
name: 'base'
}),
],
watchOptions: {
// 4-5使用自动刷新:不监听的 node_modules 目录下的文件
ignored: /node_modules/,
}
};
// 侧重优化输出质量
const path = require('path');
const DefinePlugin = require('webpack/lib/DefinePlugin');
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const {
AutoWebPlugin} = require('web-webpack-plugin');
const HappyPack = require('happypack');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
// 自动寻找 pages 目录下的所有目录,把每一个目录看成一个单页应用
const autoWebPlugin = new AutoWebPlugin('./src/pages', {
// HTML 模版文件所在的文件路径
template: './template.html',
// 提取出所有页面公共的代码
commonsChunk: {
// 提取出公共代码 Chunk 的名称
name: 'common',
},
// 指定存放 CSS 文件的 CDN 目录 URL
stylePublicPath: '//css.cdn.com/id/',
});
module.exports = {
// AutoWebPlugin 会找为寻找到的所有单页应用,生成对应的入口配置,
// autoWebPlugin.entry 方法可以获取到生成入口配置
entry: autoWebPlugin.entry({
// 这里可以加入你额外需要的 Chunk 入口
base: './src/base.js',
}),
output: {
// 给输出的文件名称加上 Hash 值
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, './dist'),
// 指定存放 JavaScript 文件的 CDN 目录 URL
publicPath: '//js.cdn.com/id/',
},
resolve: {
// 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
// 其中 __dirname 表示当前工作目录,也就是项目根目录
modules: [path.resolve(__dirname, 'node_modules')],
// 只采用 main 字段作为入口文件描述字段,以减少搜索步骤
mainFields: ['jsnext:main', 'main'],
},
module: {
rules: [
{
// 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
test: /\.js$/,
// 使用 HappyPack 加速构建
use: ['happypack/loader?id=babel'],
// 只对项目根目录下的 src 目录中的文件采用 babel-loader
include: path.resolve(__dirname, 'src'),
},
{
test: /\.js$/,
use: ['happypack/loader?id=ui-component'],
include: path.resolve(__dirname, 'src'),
},
{
// 增加对 CSS 文件的支持
test: /\.css$/,
// 提取出 Chunk 中的 CSS 代码到单独的文件中
use: ExtractTextPlugin.extract({
use: ['happypack/loader?id=css'],
// 指定存放 CSS 中导入的资源(例如图片)的 CDN 目录 URL
publicPath: '//img.cdn.com/id/'
}),
},
]
},
plugins: [
autoWebPlugin,
// 4-14开启ScopeHoisting
new ModuleConcatenationPlugin(),
// 4-3使用HappyPack
new HappyPack({
// 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
id: 'babel',
// babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
loaders: ['babel-loader?cacheDirectory'],
}),
new HappyPack({
// UI 组件加载拆分
id: 'ui-component',
loaders: [{
loader: 'ui-component-loader',
options: {
lib: 'antd',
style: 'style/index.css',
camel2: '-'
}
}],
}),
new HappyPack({
id: 'css',
// 如何处理 .css 文件,用法和 Loader 配置中一样
// 通过 minimize 选项压缩 CSS 代码
loaders: ['css-loader?minimize'],
}),
new ExtractTextPlugin({
// 给输出的 CSS 文件名称加上 Hash 值
filename: `[name]_[contenthash:8].css`,
}),
// 4-11提取公共代码
new CommonsChunkPlugin({
// 从 common 和 base 两个现成的 Chunk 中提取公共的部分
chunks: ['common', 'base'],
// 把公共的部分放到 base 中
name: 'base'
}),
new DefinePlugin({
// 定义 NODE_ENV 环境变量为 production 去除 react 代码中的开发时才需要的部分
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
new ParallelUglifyPlugin({
// 传递给 UglifyJS 的参数
uglifyJS: {
output: {
// 最紧凑的输出
beautify: false,
// 删除所有的注释
comments: false,
},
compress: {
// 在UglifyJs删除没有用到的代码时不输出警告
warnings: false,
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
}
},
}),
]
};
Webpack
运行流程Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程 。
初始化参数 : 从配置文件和 Shell
语句中读取与合并参数,得出最终的参数 。
开始编译:用上一步得到的参数初始化 Compiler
对象,加载所有配置的插件,通过执行对象的 run
方法开始执行编译 。
确定入口 : 根据配置中的 entry
找出所有入口文件
编译模块:从入口文件出发,调用所有配置的 Loader
对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 。
完成模块编译 : 在经过第 4
步使用 Loader
翻译完所有模块后, 得到了每个模块被翻译后的最终内容及它们之间的依赖关系。
输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk
, 再将每个 Chunk
转换成一个单独的文件加入输出列表中,这是可以修改输出内容 的最后机会 。
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,将文件的内容写入文件系统中