webpack的世界
本文也是多次学习webpack积累下来的知识点,一直在云笔记里。
webpack的原理
webpack构建流程
从启动webpack构建到输出结果经历了一系列过程,它们是:
- 解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
- 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
- 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
- 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
- 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
- 输出所有chunk到文件系统。
需要注意的是,在构建生命周期中有一系列插件在合适的时机做了合适的事情,比如UglifyJsPlugin会在loader转换递归完后对结果再使用UglifyJs压缩覆盖之前的结果
配置webpack就是在配置一个node的模块module.exports用于webpack来读取
1、webpack与Rollup的优缺点和区别?
webpack的优点:
- 专注于处理模块化的项目,能做到开箱及用、一步到位
- 可用plugin扩展,完整好用又不失灵活
- 使用场景不限于web开发
- 社区庞大
webapck的缺点:
- 只能用于采用模块化开发的项目
Rollup
- 可以对es6进行 Tree Shaking
- 它支持导出ES模块的包
- 它支持程序流分析,能更加正确的判断项目本身的代码是否有副作用
2、devserver的使用
webpack-dev-server是一个启动服务的程序,可以自动去,读取webpack.config.js 的文件。执行webpack-dev-server来启动,就可以来自动刷新页面,内部的原理是express的启服务,用webSocket来通知浏览器。
3、webapck配置选项
- Entry:入口,webpack执行构建的第一步从entry开始
- Module:模块,一切都是模块,一个模块对应一个文件。webapck从entry开始递归找出依赖的模块。
- Chunk:代码块,一个chunk由多个模块组合而成,用于代码合并与分割。
- Loader:模块转换器,将模块的原内容按照要求转换成新的内容。
- Plugin: 扩展插件,在特定时机执行
- Output:输出结果,把处理的最终想要的代码输出结果
4、Entry的配置
1、context是webpack寻找相对路径文件会以context为根目录
module.exports = {
context: path.resolve(__dirname, './app'),
entry:'./main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist')
}
}
2、Entry
- string 'app/'
- array ['app/a.js', 'app/b.js'] 多入口,单Chunk
- object [a:'a.js', b:'b.js'] 配置多个入口,每个入口生成一个Chunk
也可以动态的配置,就是返回函数,可以同步也可以异步
同步
entry:()=>{
return {
a: './pages/a',
b:'./pages/a'
}
},
异步
entry:new Promise((resolve)=>{
resolve({
a: '/a.js',
b:'/b.js'
})
}),
5、Output配置
output是一个对象,里面有配置项
- filename 输出文件的名称,是string
- [id Chunk的唯一标识,从0开始]
- [name Chunk的名称]
- [hash] Chunk的唯一标识Hash[hash:8]可以指定长度
- [chunkhash] Chunk内容的Hash值,比如 ExtractTextWebapckPlugin就是用的那个
- chunkFilename 配置无入口的Chunk的输出名字,比如CommonChunkPlugin import('path/')动态加载
- path输出文件存在的本地目录:path: path.resolve(__dirname, './dist')
- publicPath 配置上传cnd的路径
- corssOriginLoading 在异步加载是,利用JONSP的原理动态插入script,用来控制crossorigin的值
当本地尝试使用 window.onerror 去记录脚本的错误时,跨域脚本的错误只会返回 Script error。
HTML5 新的规定,是可以允许本地获取到跨域脚本的错误信息,但有两个条件:
- 一是跨域脚本的服务器必须通过 Access-Controll-Allow-Origin 头信息允许当前域名可以获取错误信息
- 二是当前域名的 script 标签也必须指明 src 属性指定的地址是支持跨域的地址,也就是 crossorigin 属性
可以取一下值:
- anonymous(默认) 匿名的,加载不会带上用户的cookies
- use-credentials,在加载脚本是会带上用户的cokires
- libraryTarget 和 library
- 构建一个可以被导入使用的库
- libraryTarget 何种方式,var(默认)comomjs/commonjs2/this/window/global
- library 导出的名字
6、module
- rules来配置Loader
rules: [{
test: '/\.js$/',//命中文件
use: ['babel-loader?cacheDirectory'],//指定loader 解析是从后向前
include: path.resolve(__dirname, 'src'),//包括
exclude: path.resolve(__dirname, 'node_modules')//排除
},{
test: /.vue$/,
use: ['vue-loader']
}]
vue需要使用vue-loader
- noParse 来忽略没有采用模块化的文件的递归
noParse: /jquery|chartjs/ //一般是正则 被忽略的文件不应该有import/requie/define等
- parse可以细颗粒度配置解析比如AMD,commonJS等
7、resolve 配置webpack如何寻找模块
resolve: {
alias: {
components : './src/components'
},
mainFields: ['jsnext:main', 'browser', 'main'],//jsnext 是支持ES6的模式进入
extension: ['.ts','.js','.json'],//后缀列表 //延长;延期
modules: ['./src/components', 'node_modules'],//指定本地解析 import 'button'
enforceExtension: true,//开启必须带后缀
},
8、plugin 接受数组,plugin的实例
9、devServer 使用devserver启动还生效
devServer:{
hot: true,//启用 webpack 的模块热替换特性
inline: true,//fasle是 iframe 模式
historyApiFallback: true,//spa history的模式,
任何请求都会返回index.html 也可以使用重写rewrites来详细配置
contentBase:[path.join(__dirname, "public"),
path.join(__dirname, "assets")],//提供静态文件 string fales关闭 array
host:'0.0.0.0',//服务器外部可访问
port: '9090',//端口
disableHostCheck: true,//关闭host,devServer默认是接受本地请求配合host使用
https:true,//开启https或者自己导入证书
compress: true,//默认false开启gzip压缩 压缩
open: true,//打开浏览器
proxy:{ // credentials 证书 设置成include,表示允许跨越传递cookie
"/api": "http://localhost:3000"
}
},
10、其他配置项
target:'web',//构建到的环境比如node web webworker等
devtool:'source-map',// 可以设置为false
watch:true,//监听文件,默认是关闭的devserer默认打开
externals:{
jquery: 'jQuery'
},
resolveLoader:{ //加载本地loader
},
11、为单页应用生成html
使用 html-webpack-plugin
12、其他常用Loader
file-loader 将js和css中的图片等替换成正确的地址,输出在文件中
url-loader 可以将文件的内容经过base64编码后注入js或者css中,但是要限制大小
{
exclude: [
/\.(js|jsx)(\?.*)?$/,
/\.(css|scss)$/,
/\.json$/,
/\.bmp$/,
/\.jpe?g$/,
/\.png$/,
],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: [/\.bmp$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
raw-loader 和 svg-inline-loader 可以把svg内嵌到网页,把svg当图片用,可以用上面的。
13、Source Map
以方便在浏览器通过代码调试
devtool有很多的取值,由一下6个关键字随意组合而成
-
eval: 用eval语句包裹需要安装的模块
-
source-map:生成独立的Source Map 文件
-
hidden:不在javascript文件中支出source map 文件的所在,这样浏览器就不会自动加载source map
-
inline:将生成的source map转成base64格式内嵌在javascript文件中
-
cheap:在生成的source map中不会包含列信息,那样计算量更小,输出文件更小,同时loader输出的source map不会被采用
-
module:来时loader的source map 被简单处理成每行一个模块
-
eval: 生成代码 每个模块都被eval执行,并且存在@sourceURL
-
cheap-eval-source-map: 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl
-
cheap-module-eval-source-map: 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能
-
eval-source-map: 原始代码 同样道理,但是最高的质量和最低的性能
-
cheap-source-map: 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用
-
cheap-module-source-map: 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射
-
source-map: 原始代码 最好的sourcemap质量有完整的结果,但是会很慢
eval和.map文件都是sourcemap实现的不同方式,虽然大部分sourcemap的实现是通过产生.map文件, 但并不表示只能通过.map文件实现。下面是eval模式后产生的模块代码
cheap关键字的配置中只有行内,列信息指的是代码的不包含原始代码的列信息
- 在开发环境 用source-map 那个最完整 或者cheap-module-eval-source-map 那个最快
- 在生产环境就flase 或者 hidden-source-map 为错误搜集用
- 如果需要加载模块的source-map 需要使用source-map-loader
- vue-cli 用的devtool: '#eval-source-map',
14、代码检测和npm配合
与npm 配着
npm 可以运行node__modules里安装的程序
可以缩短命令
代码检测
javascript 用eslint 用.eslintrc json 文件配置 eslint-loader enforce: 'pre'
ts 用TSlint tslint.json 配置 tslint-loader
css stylelint 用.stylelintrc 配置 StyleLintPlugin 插件来处理
代码检测会变慢构建速度,可以配置IDE来检测,同时配置git Hook在代码提交时检测
15、node运行webapck和中间件
var config = require('../config')
var compiler = webpack(webpackConfig)
调用compiler的watch可以监听文件变化
require('webpack-dev-middleware')是express的一个插件
var app = express()
var compiler = webpack(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
stats: {
colors: true,
chunks: false
}
})
但是不支持hot模式需要require('webpack-hot-middleware')
app.use(hotMiddleware)
代理使用
var proxyMiddleware = require('http-proxy-middleware')
16、webapck构建优化
1、减小文件的搜索范围
- 优化loader配置,利用好include和exclude
- 优化resolve.modules 使用绝对路径
modules: [path.resolve(__dirname, 'node_modules')]
- alias 指向 x.min.js 文件,但是lodash不适合
alias: {
react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
},
- noParse 配置不解析
noParse: /jquery|chartjs/ //一般是正则 被忽略的文件不应该有import/require/define等
2、优化构建过程
- babel-loader 加 cacheDirectory
- DllPlugin 官方推荐做法是把不常变动的文件打DLL
工程就把 react react-dom prop-types classnames mobx mobx-react lodash moment polyfill 等打进来 可以使用 [npm version]_dll.js 用 npm version 的话只要 version 一改变我们会重新打包,比如升级了 react ,我们就会 version +,就会重新打包。
先创建一个webpack.config.dll.js
执行webpack --config ./webpack.config.dll.js把需要dll的文件输出到dist/dll
const webpack = require('webpack');
const path = require('path');
const {version} = require('./package.json');
module.exports = {
entry: {
'react': [
'react', 'react-dom',
'prop-types',
'classnames',
'lodash', 'moment'
]
},
output: {
path: path.join(__dirname, 'dist/dll'),
filename: `[name].${version}.js`,
library: 'dll_[name]',
publicPath: '/dist/dll/'
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'dist/dll/', '[name].manifest.json'),
name: 'dll_[name]'
})
]
};
在webapck配置文件中使用
new webpack.DllReferencePlugin({
context: __dirname,
// 在这里引入 manifest 文件
manifest: require('./dist/dll/react.manifest.json')
}),
//把js插入到html文件中
new AddAssetHtmlPlugin({
filepath: require.resolve(`./dist/dll/${getDLLFileName()}`),
outputPath: 'dll',
includeSourcemap: false,
hash: true,
publicPath: '/dist/dll/'
})
原理就是提前构建,缓存起来,用window的全局变量来取。
- happypack 对 build 的速度大大大提示,可以多线程打包,cache 也让 rebuild 加快
{
test: /\.(js|jsx)$/,//命中文件
use: 'happypack/loader?id=js',//指定loader
include: path.resolve(__dirname, 'src'),//包括
exclude: path.resolve(__dirname, 'node_modules')//排除
}, {
test: /\.(css|scss)$/,
loader: 'happypack/loader?id=css'
}
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
loaders: [{
path: 'babel-loader',
query: {
cacheDirectory: true
}
}]
}),
new HappyPack({
id: 'css',
threadPool: happyThreadPool,
loaders: ['style-loader','css-loader', 'sass-loader']
}),
原理:happypack 的原理是让loader可以多进程去处理文件,css和js,图片和文件支持不好
-
Devtool 使用 开发用cheap-module-eval-source-map 那个最快
-
压缩 UglifyJsParallelPlugin在webpack2.0以后支持并行,就可以弃用
-
开启自动刷新(iframe会比inline快一点)和热更新热替换(hot 开启) 优化就是把node_modules 排除
-
区分环境
if (isDev) {
config.plugins.push(new webpack.NamedModulesPlugin()); //显示模块更新名字
config.plugins.push(new webpack.HotModuleReplacementPlugin()); //hot更新插件
config.devServer = {
hot: true,
contentBase: './',
historyApiFallback: {
index: "/build/index.html"
},
publicPath: '/build/',
host: '0.0.0.0'
};
config.devtool = 'eval';
} else {
config.plugins.push(new webpack.optimize.UglifyJsPlugin({ // 开启压缩
cache: true,
parallel: true,
compress: {
warning: fasle,// 是否删除一个警告信息fasle删除
drop_console: true,//删除console
collapse_vars: true,是否内嵌虽然已定义但是只用到一次的变量。
reduce_vars: true ,提取多次的变量
},
output: {
comments: false,//是否删除注释,默认是不删除,设置为false,删除所有的注释
beautify: fasle, //保留空格和制表符 建议false关闭,最紧凑的输出
}
}));
config.devtool = '#source-map';
}
8、压缩css 在css-loader开启minimize选项 和 提取css到单独的文件
{
test: /\.(css|less)$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'happypack/loader?id=css'
})
},
new ExtractTextPlugin('css/[name].[contenthash:8].css')
9、在webapck中接入cdn
- 在output.publicPath 中设置javascript的地址
- 在css-loader.publicPath 中设置css文件中导入的资源的地址
- 在HtmlWebpackPlugin或者WebPlugin.stylePublicPath 设置css单独是文件的资源
10、Tree Shaking 去除重复代码 ,webapck没有程序流分析,避免不了babel产生的副作用
11、以模块化来引入
// 原来的引入方式
import {debounce} from 'lodash';
//按模块化的引入方式
import debounce from 'lodash/debounce';
12、使用异步的模块加载
require.ensure来设置哪些模块需要异步加载,webpack会将它打包到一个独立的chunk中
$('.bg-input').click(() => {
console.log('clicked, loading async.js')
require.ensure([], require => {
require('./components/async2').log();
require('./components/async1').log();
console.log('loading async.js done');
});
});
import(*) 是新的按需加载,在react-router4中不能使用require.ensure要使用getAsyncComponent函数
component = {
getAsyncComponent(() => {
import('./pages/login')
})
}
13、Scope Hoisting 作用域提升 webpack3的新功能
使用ModuleConcatenationPlugin插件来加快JS执行速度
这是webpack3的新特性(Scope Hoisting),其实是借鉴了Rollup打包工具来的,它将一些有联系的模块,放到一个闭包函数里面去,通过减少闭包函数数量从而加快JS的执行速度
new webpack.optimize.ModuleConcatenationPlugin({
})
原理:原理其实很简单,分析模块之间的依赖关系,尽可能将被打散的模块合并到一个函数中,前提是不能造成代码冗余,源码必须采用es6语句。
14、提取公共代码
使用CommonsChunkPlugin提取公共的模块,可以减少文件体积,也有助于浏览器层的文件缓存,还是比较推荐的,那个是在最后打包的时候用的。
// 提取公共模块文件
new webpack.optimize.CommonsChunkPlugin({
chunks: ['home', 'detail'],
// 开发环境下需要使用热更新替换,而此时common用chunkhash会出错,可以直接不用hash
filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
name: 'common'
}),
// 切合公共模块的提取规则,有时后你需要明确指定默认放到公共文件的模块
// 文件入口配置
entry: {
home: './src/js/home',
detail: './src/js/detail',
// 提取jquery入公共文件
common: ['jquery', 'react', 'react-dom']
},
entry: {index:'./src/index.js',vendor: ['react','react-dom','react-router']},
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
filename: "vendor.js",
}),
15、prepack
利用抽象语法树(AST)来分析源码,过来问题大大,不建议使用
const PrepackWebpackPlugin = require('prepack-webapck-plugin').default;
module.exports = {
plugins: [
new PrepackWebpackPlugin()
]
}
16、可视化的输出分析 Analyse webpack-bundle-analyzer
17、pwa构建
用 Webpack 构建接入 Service Workers 的离线应用要解决的关键问题在于如何生成上面提到的 sw.js 文件, 并且sw.js文件中的 cacheFileList 变量,代表需要被缓存文件的 URL 列表,需要根据输出文件列表所对应的 URL 来决定,而不是像上面那样写成静态值。
假如构建输出的文件目录结构为:
├── app_4c3e186f.js
├── app_7cc98ad0.css
└── index.html
那么 sw.js 文件中 cacheFileList 的值应该是:
var cacheFileList = [
'/index.html',
'app_4c3e186f.js',
'app_7cc98ad0.css'
];
Webpack 没有原生功能能完成以上要求,幸好庞大的社区中已经有人为我们做好了一个插件 serviceworker-webpack-plugin 可以方便的解决以上问题。 使用该插件后的 Webpack 配置如下:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { WebPlugin } = require('web-webpack-plugin');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
module.exports = {
entry: {
app: './main.js'// Chunk app 的 JS 执行入口文件
},
output: {
filename: '[name].js',
publicPath: '',
},
module: {
rules: [
{
test: /\.css/,// 增加对 CSS 文件的支持
// 提取出 Chunk 中的 CSS 代码到单独的文件中
use: ExtractTextPlugin.extract({
use: ['css-loader'] // 压缩 CSS 代码
}),
},
]
},
plugins: [
// 一个 WebPlugin 对应一个 HTML 文件
new WebPlugin({
template: './template.html', // HTML 模版文件所在的文件路径
filename: 'index.html' // 输出的 HTML 的文件名称
}),
new ExtractTextPlugin({
filename: `[name].css`,// 给输出的 CSS 文件名称加上 Hash 值
}),
new ServiceWorkerWebpackPlugin({
// 自定义的 sw.js 文件所在路径
// ServiceWorkerWebpackPlugin 会把文件列表注入到生成的 sw.js 中
entry: path.join(__dirname, 'sw.js'),
}),
],
devServer: {
// Service Workers 依赖 HTTPS,使用 DevServer 提供的 HTTPS 功能。
https: true,
}
};
18、多页
AutoWebPlugin插件来配置多页
├── pages
│ ├── index
│ │ ├── index.css // 该页面单独需要的 CSS 样式
│ │ └── index.js // 该页面的入口文件
│ └── login
│ ├── index.css
│ └── index.js
├── common.css // 所有页面都需要的公共 CSS 样式
├── google_analytics.js
├── template.html
└── webpack.config.js
- 所有单页应用的代码都需要放到一个目录下,例如都放在 pages 目录下;
- 一个单页应用一个单独的文件夹,例如最后生成的 index.html 相关的代码都在 index 目录下,login.html 同理;
- 每个单页应用的目录下都有一个 index.js 文件作为入口执行文件。
AutoWebPlugin 强制性的规定了项目部分的目录结构,在pages下,每一个文件夹就是一个目录。通过插件自动生成手动需要配置的两个html插件。
const { AutoWebPlugin } = require('web-webpack-plugin');
// 使用本文的主角 AutoWebPlugin,自动寻找 pages 目录下的所有目录,把每一个目录看成一个单页应用
const autoWebPlugin = new AutoWebPlugin('pages', {
template: './template.html', // HTML 模版文件所在的文件路径
postEntrys: ['./common.css'],// 所有页面都依赖这份通用的 CSS 样式文件
// 提取出所有页面公共的代码
commonsChunk: {
name: 'common',// 提取出公共代码 Chunk 的名称
},
});
module.exports = {
// AutoWebPlugin 会为寻找到的所有单页应用,生成对应的入口配置,
// autoWebPlugin.entry 方法可以获取到所有由 autoWebPlugin 生成的入口配置
entry: autoWebPlugin.entry({
// 这里可以加入你额外需要的 Chunk 入口
}),
plugins: [
autoWebPlugin,
],
};
template.html 模版文件如下:
<html>
<head>
<meta charset="UTF-8">
<!--在这注入该页面所依赖但没有手动导入的 CSS-->
<!--STYLE-->
<!--注入 google_analytics 中的 JS 代码-->
<script src="./google_analytics.js?_inline"></script>
<!--异步加载 Disqus 评论-->
<script src="https://dive-into-webpack.disqus.com/embed.js" async></script>
</head>
<body>
<div id="app"></div>
<!--在这注入该页面所依赖但没有手动导入的 JavaScript-->
<!--SCRIPT-->
<!--Disqus 评论容器-->
<div id="disqus_thread"></div>
</body>
</html>
和 在模版中生成
- CSS 类型的文件注入到 所在的位置,如果 不存在就注入到 HTML HEAD 标签的最后;
- JavaScrip 类型的文件注入到 所在的位置,如果 不存在就注入到 HTML BODY 标签的最后。
通过gulp来管理html,webapck来处理js也是解决多页的好方法
利用html-webpack-plugin 插件循环生成
// 引入插件
const HTMLWebpackPlugin = require("html-webpack-plugin");
// 引入多页面文件列表
const { HTMLDirs } = require("./config");
// 通过 html-webpack-plugin 生成的 HTML 集合
let HTMLPlugins = [];
// 入口文件集合
let Entries = {}
// 生成多页面的集合
HTMLDirs.forEach((page) => {
const htmlPlugin = new HTMLWebpackPlugin({
filename: `${page}.html`,
template: path.resolve(__dirname, `../app/html/${page}.html`),
chunks: [page, 'commons'],
});
HTMLPlugins.push(htmlPlugin);
Entries[page] = path.resolve(__dirname, `../app/js/${page}.js`);
})
19、同构
构建服务端渲染
服务端渲染的代码要运行在nodejs环境,和浏览器不同的是,服务端渲染代码需要采用commonjs规范同时不应该包含除js之外的文件比如css。webpack配置如下:
module.exports = {
target: 'node',
entry: {
'server_render': './src/server_render',
},
output: {
filename: './dist/server/[name].js',
libraryTarget: 'commonjs2',
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
},
{
test: /\.(scss|css|pdf)$/,
loader: 'ignore-loader',
},
]
},
};
其中几个关键的地方在于:
target: 'node' 指明构建出的代码是要运行在node环境里
libraryTarget: 'commonjs2' 指明输出的代码要是commonjs规范
{test: /.(scss|css|pdf)$/,loader: 'ignore-loader'} 是为了防止不能在node里执行服务端渲染也用不上的文件被打包进去。
20、laoder与plugins插件编写
如果你的扩展是想对一个个单独的文件进行转换那么就编写loader剩下的都是plugin
其中对文件进行转换可以是像:
- babel-loader把es6转换成es5
- file-loader把文件替换成对应的URL
- raw-loader注入文本文件内容到代码里去
编写loader非常简单,以comment-require-loader为例:
module.exports = function (content) {
return replace(content);
};
loader的入口需要导出一个函数,这个函数要干的事情就是转换一个文件的内容。
函数接收的参数content是一个文件在转换前的字符串形式内容,需要返回一个新的字符串形式内容作为转换后的结果,所有通过模块化倒入的文件都会经过loader。从这里可以看出loader只能处理一个个单独的文件而不能处理代码块
class EndWebpackPlugin {
constructor(doneCallback, failCallback) {
this.doneCallback = doneCallback;
this.failCallback = failCallback;
}
apply(compiler) { 汇编者; 编辑者; 编纂者
// 监听webpack生命周期里的事件,做相应的处理
compiler.plugin('done', (stats) => {
this.doneCallback(stats);
});
compiler.plugin('failed', (err) => {
this.failCallback(err);
});
}
}
module.exports = EndWebpackPlugin;
loader的入口需要导出一个class, 在new EndWebpackPlugin()的时候通过构造函数传入这个插件需要的参数,在webpack启动的时候会先实例化plugin再调用plugin的apply方法,插件需要在apply函数里监听webpack生命周期里的事件,做相应的处理。
webpack plugin 里有2个核心概念:
Compiler: 从webpack启动到推出只存在一个Compiler,Compiler存放着webpack配置
Compilation: 由于webpack的监听文件变化自动编译机制,Compilation代表一次编译。
Compiler 和 Compilation 都会广播一系列事件。
webpack生命周期里有非常多的事件可以在event-hooks和Compilation里查到
21、常用loader与plugins
加载文件
- raw-loader:把文本文件的内容加载到代码中去,在 3-20加载SVG 中有介绍。
- file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件,在 3-19加载图片、3-20加载 SVG、4-9 CDN 加速 中有介绍。
- url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去,在 3-19加载图片、3-20加载 SVG 中有介绍。
- source-map-loader:加载额外的 Source Map 文件,以方便断点调试,在 3-21加载 Source Map 中有介绍。
- svg-inline-loader:把压缩后的 SVG 内容注入到代码中,在 3-20加载 SVG 中有介绍。
- node-loader:加载 Node.js 原生模块 .node 文件。
- image-loader:加载并且压缩图片文件。
- json-loader:加载 JSON 文件。
- yaml-loader:加载 YAML 文件。
编译模版
- pug-loader:把 Pug 模版转换成 JavaScript 函数返回。
- handlebars-loader:把 Handlebars 模版编译成函数返回。
- ejs-loader:把 EJS 模版编译成函数返回。
- haml-loader:把 HAML 代码转换成 HTML。
- markdown-loader:把 Markdown 文件转换成 HTML。
转换脚本语言
- babel-loader:把 ES6 转换成 ES5,在3-1使用 ES6 语言中有介绍。
- ts-loader:把 TypeScript 转换成 JavaScript,在3-2使用 TypeScript 语言中有遇到。
- awesome-typescript-loader:把 TypeScript 转换成 JavaScript,性能要比 ts-loader 好。
- coffee-loader:把 CoffeeScript 转换成 JavaScript。
转换样式文件
- css-loader:加载 CSS,支持模块化、压缩、文件导入等特性。
- style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
- sass-loader:把 SCSS/SASS 代码转换成 CSS,在3-4使用 SCSS 语言中有介绍。
- postcss-loader:扩展 CSS 语法,使用下一代 CSS,在3-5使用 PostCSS中有介绍。
- less-loader:把 Less 代码转换成 CSS 代码。
- stylus-loader:把 Stylus 代码转换成 CSS 代码。
检查代码
- eslint-loader:通过 ESLint 检查 JavaScript 代码,在 3-16检查代码中有介绍。
- tslint-loader:通过 TSLint 检查 TypeScript 代码。
- mocha-loader:加载 Mocha 测试用例代码。
- coverjs-loader:计算测试覆盖率。
其它
- vue-loader:加载 Vue.js 单文件组件,在3-7使用 Vue 框架中有介绍。
- i18n-loader:加载多语言版本,支持国际化。
- ignore-loader:忽略掉部分文件,在3-11构建同构应用中有介绍。
- ui-component-loader:按需加载 UI 组件库,例如在使用 antd UI 组件库时,不会因为只用到了 Button 组件而打包进所有的组件。
用于修改行为
- define-plugin:定义环境变量,在4-7区分环境中有介绍。
- context-replacement-plugin:修改 require 语句在寻找文件时的默认行为。
- ignore-plugin:用于忽略部分文件。
用于优化
- commons-chunk-plugin:提取公共代码,在4-11提取公共代码中有介绍。
- extract-text-webpack-plugin:提取 JavaScript 中的 CSS 代码到单独的文件中,在1-5使用 Plugin 中有介绍。
- prepack-webpack-plugin:通过 Facebook 的 Prepack 优化输出的 JavaScript 代码性能,在 4-13使用 Prepack 中有介绍。
- uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码,在 4-8压缩代码中有介绍。
- webpack-parallel-uglify-plugin:多进程执行 UglifyJS 代码压缩,提升构建速度。
- imagemin-webpack-plugin:压缩图片文件。
- webpack-spritesmith:用插件制作雪碧图。
- ModuleConcatenationPlugin:开启 Webpack Scope Hoisting 功能,在4-14开启 ScopeHoisting中有介绍。
- dll-plugin:借鉴 DDL 的思想大幅度提升构建速度,在4-2使用 DllPlugin中有介绍。
- hot-module-replacement-plugin:开启模块热替换功能。
其它
- serviceworker-webpack-plugin:给网页应用增加离线缓存功能,在3-14 构建离线应用中有介绍。
- stylelint-webpack-plugin:集成 stylelint 到项目中,在3-16检查代码中有介绍。
- i18n-webpack-plugin:给你的网页支持国际化。
- provide-plugin:从环境中提供的全局变量中加载模块,而不用导入对应的文件。
- web-webpack-plugin:方便的为单页应用输出 HTML,比 html-webpack-plugin 好用。
22 webpack4
- webpack 4 更快(速度提升98%)
- 新增Mode选项,区分环境
- 零配置以及默认值
- 再见 CommonsChunkPlugin =》 新 API optimize.splitChunks
- WebAssembly 支持
- .mjs 支持