本文也是多次学习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是一个对象,里面有配置项

  1. filename 输出文件的名称,是string
  • [id Chunk的唯一标识,从0开始]
  • [name Chunk的名称]
  • [hash] Chunk的唯一标识Hash[hash:8]可以指定长度
  • [chunkhash] Chunk内容的Hash值,比如 ExtractTextWebapckPlugin就是用的那个
  1. chunkFilename 配置无入口的Chunk的输出名字,比如CommonChunkPlugin import('path/')动态加载
  2. path输出文件存在的本地目录:path: path.resolve(__dirname, './dist')
  3. publicPath 配置上传cnd的路径
  4. corssOriginLoading 在异步加载是,利用JONSP的原理动态插入script,用来控制crossorigin的值

    当本地尝试使用 window.onerror 去记录脚本的错误时,跨域脚本的错误只会返回 Script error。

    HTML5 新的规定,是可以允许本地获取到跨域脚本的错误信息,但有两个条件:
  • 一是跨域脚本的服务器必须通过 Access-Controll-Allow-Origin 头信息允许当前域名可以获取错误信息
  • 二是当前域名的 script 标签也必须指明 src 属性指定的地址是支持跨域的地址,也就是 crossorigin 属性

可以取一下值:

  • anonymous(默认) 匿名的,加载不会带上用户的cookies
  • use-credentials,在加载脚本是会带上用户的cokires
  1. libraryTarget 和 library
  2. 构建一个可以被导入使用的库
  • 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、减小文件的搜索范围
  1. 优化loader配置,利用好include和exclude
  2. 优化resolve.modules 使用绝对路径
modules: [path.resolve(__dirname, 'node_modules')]
  1. alias 指向 x.min.js 文件,但是lodash不适合
alias: {
react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
},
  1. noParse 配置不解析
noParse: /jquery|chartjs/ //一般是正则 被忽略的文件不应该有import/require/define等
2、优化构建过程
  1. babel-loader 加 cacheDirectory
  2. 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的全局变量来取。

  1. 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,图片和文件支持不好

  1. Devtool 使用 开发用cheap-module-eval-source-map 那个最快

  2. 压缩 UglifyJsParallelPlugin在webpack2.0以后支持并行,就可以弃用

  3. 开启自动刷新(iframe会比inline快一点)和热更新热替换(hot 开启) 优化就是把node_modules 排除

  4. 区分环境

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 支持
05-04 00:38