webpack学习笔记

一、webpack是什么?

webpack是前端的一个模块化的打包工具。前端目前模块化是主流,因此,需要一个工具,把各个JS文件,CSS文件或SASS文件转译打包成JS文件,CSS文件和JPG文件,按类型打包在一起。

webpack还拥有强大的配置,可以在构建时做很多事情,进行很多的配置改造。这些配置都支持自定义。使得webpack灵活性很高。

二、webpack知识前奏

(1)在新创建的项目文件夹内,npm init 之后会出现package.json这个文件,这个是管理项目版本和依赖的文件,

(2)使用npm安装webpack和webpack-cli-npm i webpack webpack-cli -D,(webpack-cli是webpack的命令行工具,webpack4.x以后需要单独安装这个依赖包);

(3)npx webpack 命令来运行项目内webpack,构建项目。

(4)可在项目中创建webpack配置文件-webpack.config.js,这是一个node脚本,执行webpack构建时,webpack会读取里面的配置进行构建。

//webpack.config.js //基础配置,可根据项目需求自由配置 const path = require("path"); module.exports = { entry:"./src/index.js",//指定构建的入口文件 output:{//出口 filename:"bundle.js",//指定构建生成文件所在路径 path:path.resolve(__dirname,"dist"),//指定构建生成的文件名 }, mode:"production",//指定构建的模式 }

(5)可安装webpack-dev-server来在开发过程中,动态进行编译。可在package.json中指定npm命令执行。

"scripts": { "serve": "webpack-dev-server", "build": "webpack", "test": "echo \"Error: no test specified\" && exit 1" },

前提是需先要下载此依赖,且在webpack.config.js配置

devServer: { contentBase: path.resolve(__dirname, 'dist') // 开发服务器启动路径 }

(6)前端基础构建配置(常用的loader及plugin)

const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin")//自动生成html的插件 const MiniCssExtractPlugin = require("mini-css-extract-plugin")//打包时生成独立css文件插件 module.exports = { entry:"./src/index.js",//指定构建的入口文件 output:{//出口 filename:"bundle.js",//指定构建生成文件所在路径 path:path.resolve(__dirname,"dist"),//指定构建生成的文件名 }, mode:"production",//指定构建的模式 devServer: { contentBase: path.resolve(__dirname, 'dist') // 开发服务器启动路径 }, module:{//loader进来的模块使用 rules:[ { test:/\.css/, include:[ path.resolve(__dirname,'src'), ], use:[ MiniCssExtractPlugin.loader,//因为这个插件要干涉模块转换的内容,所以需要使用它对应的laoder // 'style-loader',//将css-loader的解析结果转化为js代码,动态插入style让样式生效(style-loader必须在css-loader之前先引用,否则会报错) 'css-loader',//解析css代码,主要是css中的import、url等引用外部文件的声明 ] }, { test:/\.(png|jpg|gif)$/, use:[ { loader:'file-loader',//处理图片等文件的loader,可以把图片等打包出来,这样webpack就有了打包图片的能力 options:{} } ] },{ test:/\.jsx?/,//支持js和jsx文件,有时候react需要 include:[ path.resolve(__dirname,'src'),//指定哪些文件路径下的文件需要经过loader处理 ], use:{ loader:'babel-loader',//指定使用的loader options:{ presets:['@babel/preset-env'], } } } ] }, plugins:[ new HtmlWebpackPlugin({ template:'src/index.html',//配置文件模版(自定义的html文件) }), new MiniCssExtractPlugin({//将css文件单独抽离出来的plugin filename:'index.css'//定义抽离出来的css文件名称,也可使用[hash] }) ], }

三、entry

entry为模块的构建入口。

静态配置:

module.exports = { //简单配置 entry:'./src/index.js', //此配置等同于上面的简单配置 entry:{ main:'./src/index.js', }, //如果有多个页面,需要多个js作为构建的入口,可以进行多个js的配置 //多个入口,生成不同的文件 entry:{ //按需取名,通常是业务名称 foo:'./src/foo.js', bar:'.src/bar.js' }, //也可以把多个js文件,构建为一个统一的入口 entry:{ //按需取名,通常是业务名称 main:[ './src/foo.js', '.src/bar.js' ] }, }

动态配置(可在大型项目时或多页面应用时,自动配置入口文件):

因webpack底层就是node在执行,配置文件也是node在读取,所以,配置文件就是node脚本。

const path = require('path'); const fs = require('fs'); //src/pages 目录作为页面入口的根目录,例如:./src/pages/foo/index.js const pagesRoot = path.resolve(__dirname,'./scr/pages'); const entries = fs.readFileSync(pagesRoot).reduce((entries,page)=>{ entries[page] = path.resolve(pagesRoot,page);//webpack会自动寻找页面文件夹下的'index.js' return entries }) module.exports = { entry:entries, }

四、module与resolve

webpack本质上就是module的整合。所以,webpack的module寻找,特别是js文件之间的依赖寻找,其实就是nodejs模块机制寻找的增强版。

在webpack配置中和模块路径解析相关的配置都在resolve字段下。

(1)resolve.alias

此可以对特定的模块名进行匹配替换

//模糊匹配,如utils这个模块,所有路径带有‘utils’都会被替换为指定的路径 alias:{ utils:path.resolve(__dirname,'src/utils')//这里使用path.resolve和__dirname来获取绝对路径 } //import 'utils/query.js'-----会被替换为import '[绝对路径]/src/utils/query.js' //精准匹配 alisa:{ utils$:path.resolve(__dirname,'src/utils') } //这里只会匹配import 'utils'

(2)resolve.extensions

有时候在import引入时,往往会省略文件最后的后缀,这时候webpack会自动匹配。也可以手动指定,resolve.extensions即可以指定。

extensions:['js','jsx','css']

(3)resolve.modules

此配置为手动配置依赖包的目录路径

//默认 resolve:{ modules:['node_modules'] } //若有依赖包在其他的文件夹下,可指定。提高寻找的速度,可以提高编译的速度 resolve:{ modules:[ path.resolve(__dirname,'node_modules')//优先寻找此文件夹 'node_modules2' ] }

五、loader

loader主要是用于配置第三方类库,这些类库通常都是用于做一些模块打包以外的功能的,但是是对打包的模块整个产生作用。

module.exports = { //所有的loader设置的规则都是在module.rules下,每一个对象就是一个规则,可以use多个loader rules:[ {//一条Object即一条规则 test:/\.jsx?/, include:[ path.resolve(__dirname,'src'),//指定哪些路径下的文件需要经过loader处理 ], use:{ loader:'babel-loader',//指定使用的loader } },{ } ] } //test,include是resource(依赖的源文件)中的内容,是简写,实际上为 resource:{ test:'', include:[] } //规则条件还可以进行多种方式匹配 resoure:{ test;'',//匹配特定条件 include:[],//匹配特定路径 exclude:[],//排除特定路径 and:[],//必须匹配数组中所有条件 or:[],//匹配数组中任意一个条件 not:[],//排除匹配数组中所有的条件 } 上面的值可以是:字符串、正则表达式、函数、数组、对象

loader的配置应用

rules:[{ test:'', use:[//loader从后往前执行 'style-loader',//直接使用loader { loader:'css-loader',//用对象表示loader,可以传递loader配置等 options:{ importLoader:1 } } ] }] //如果只有一个loader,可直接配置loader use:{ loader:'', options:{} }

如果多个rules应用于同一个模块的话,无法保证执行的先后顺序,则webpack提供了enforce字段来进行指定,值为per/post(前/后),如果没有指定,则为普通的类型(顺序)。

六、plugin

plugin是负责webpack除模块打包以外的其他多样性的构建任务处理。

mode:

不同的mode值会影响着不同的plugin配置,譬如就会影响着DefinePlugin来设置process.env.NODE_ENV的值,方便代码中判断构建环境。

DefinePlugin用于创建一些在编译时可以配置值,在运行时可以使用的常量。

plugins:[ new webpack.DefinePlugin = ({ TYPE:JSON.stringify(true),//const TYPE = true VERSION:JSON.stringify('1.0.0'),//const VERSION = '1.0.0' BROWSER_SUPPORTS_HTML5:true,//const BROWSER_SUPPORTS_HTML5 = 'true' TWO:'1+1',//const TWo= = '1+1' }) ], /* *如果配置的是字符串,则会被当作代码片段执行,如1+1,会得出2 *如果配置的不是字符串,也不是对象字面量,则会转化为字符串 *如果是对象字面量,则是普通的对象字面量 */ //可在应用代码中,直接访问定义的常量 console.log('version is :'+VERSION)//version is 1.0.0

IgnorePlugin也是webpack内置插件,可以直接使用,主要用于忽略某些特定的模块,让webpack不把这些指定的模块打包进行。

webpack-bundle-analyzer为分析webpack打包模块的内容,主要查看各个模块之间的依赖关系和各个模块的代码内容大小,以便开发者做性能的优化。

webpack-bundle-analyzer和IgnorePlugin常有助于构建性能和代码的优化。

七、优化图片&HTML&CSS

优化前端加载的速度,一是减少请求的资源数,webpack做的正是把多个模块化文件打包成几个必须的文件;二是减少加载的资源大小。

(1)图片作为占用前端资源体积很大的一部分,进行有效的压缩等,能起到很大的优化作用。

可以使用file-loader处理图片的同时,再加一个“image-webpack-loader”来进行压缩图片等操作;

还可以结合url-loader等配置指定的图片大小,超过指定的标准则仍为图片引入,若小于指定的标准则把小图片自动转化为base64编码,引入js文件中,减少请求图片的资源请求。

(2)“html-webpack-plugin ”插件也可压缩HTML的大小:

plugins:[ new HtmlWebpackPlugin({ template:'src/index.html',//配置文件模版(自定义的html文件) minify:{//压缩HTML的配置 minifyCSS:true,//压缩HTML中出现的CSS代码 minifyJS:true,//压缩HTML中出现的JS代码 collapseInlineTagWhitespace:true, collapseWhitespace:true,//和上一个配合,移除无用的空格和换行 } }), ],

(3)压缩css代码可以使用"postcss"与"cssnano","postcss"是个强大的css处理器,里面有许多优秀的插件可供使用,譬如"autoprefixer"。

{ loader:'postcss-loader', options:{ plugins:() => {//返回postcss的插件列表 require('cssnano')()//使用cssnano } } }

八、优化JS代码

(1)优化JS代码,将可以将未使用到的代码不打包进最终的代码中,这样可大大减少代码的体积。

当在production这一mode下时,使用TerserPlugin后,Terser会移除那些没有使用到的代码,webpack会默认不打包未使用到的代码;

当使用dev等mode也希望不打包未使用的代码时,也可以在配置文件中进行配置:

module.exports = { mode: 'development', //... optimization: { usedExports: true, // 模块内未使用的部分不进行导出, sideEffects:[//指定特定的文件或者类型,不能进行移除(无论是否进行了使用) "*.css"//所有的css都具有副作用,不进行移除 ], concatenateModules:true//清除引入模块后,一些闭包的多余代码,或标明作用的代码注释 }, } //上述的配置,都是webpack进行默认配置好的,也可以进行修改。

九、分离代码

当多个页面具有公共的JS代码或CSS代码时,可以把这部分单独分离出来,这样,可以使后面的页面利用静态资源缓存,加快整个页面的访问速度。虽然初始页面增加了一些请求,但是,把文件单独分离开,也可以加快每个请求。且,后面的网页响应速度也更快。

(1)、分离公共JS代码,会在html中,在构建的js文件前进行引入:

module.exports = { // ... webpack 配置 optimization: { splitChunks: { chunks: "all", // 所有的 chunks 代码公共的部分分离出来成为一个单独的文件 name: 'common', // 给分离出来的 chunk 起个名字 }, }, } //html文件-html-webpack-plugin会自动进行引用 <script src="commons.bundle.js" charset="utf-8"></script> <script src="index.bundle.js" charset="utf-8"></script> //若需根据不同的html页面,引入不同的公共文件的话,可以在html-webpack-plugin的配置中进行指定 module.exports = { // ... plugins: [ new HtmlWebpackPlugin({ filename: 'page.html', chunks: ['common', 'page'], // 这里要包括 commonJS 部分和页面业务部分 }), ] }

(2)也可以设置引入的第三方库,也进行公共库打包。

(3)按需加载

有时候在单页应用或者多页网站内,需要根据用户某些权限或者操作,才执行某些代码,那么即可把这部分代码进行按需加载,用户需要使用到时才进行加载,以此提升应用初始化时加载的速度。

// import 作为一个方法使用,传入模块名即可,返回一个 promise 来获取模块暴露的对象 // 注释 webpackChunkName: "jquery" 可以用于指定 chunk 的名称,在输出文件时有用,若不指定则自动以数字为文件标识,不利于辨识 import(/* webpackChunkName: "jquery" */ 'jquery').then(($) => { // console.log($); });

十、webpack-dev-server

webpack-dev-server是前端工程开发时,使用此可以提供动态资源的服务器插件。并且上面可以进行各种配置,使得开发时更加高效。本质上,webpack-dev-server就是基于node的框架express打造的。

需要注意,webpack-dev-server@4.x版本后,config内的配置不再是contentBase关键字。

webpack-dev-server默认构建的服务是 http://localhost:8080/ ,且如果与html-webpack-plugin 已经构建了index.html页面,打开8080端口,可以默认打开页面。且可以通过http://localhost:8080/index.css等访问文件夹内的文件。

(1)devServer字段可以来配置webpack-dev-server,可以用来配置端口号,路径,代理等

module.exports = { devServer:{ contentBase: path.resolve(__dirname, 'dist') // 开发服务器启动路径 public:'http://xxx.com',//可用于已经经过nginx反向代理设置的域名 port:'8081',//默认8080,可通过port修改端口号 proxy:{//可以将特定的url请求代理到另外一台服务器上 '/api':{ target:'http://localhost:3000',//将url中带有/api的请求代理到http://localhost:3000服务上 pathRewrite:{'^/api':''},//把url中path部分的'api'移除掉 } }, before(app){//在webpack-dev-server静态资源中间件处理之前,用于拦截部分请求,返回特定的mock数据等 app.get('/some/path',function(req,res){ res.json({test:'this is before test'}) }) } after(app){//在webpack-dev-server静态资源中间件处理之后,常用于一些日志的打印或者额外的处理 } } }

十一、模块热替换

即HMR(Hot Module Replacement),部分模块更新后,不用刷新整个页面,而是更新部分相应刷新,即为模块热替换。

HMR在webpack-dev-server内开箱即用,只需要开启相应的配置即可。

module.exports = { devServer:{ hot:true } }

十二、开发流程

在实际开发过程中,往往具有多个环境,以及不同环境采用不同的插件或配置,因此开发流程往往会动态指定环境及配置文件。

(1)动态指定mode模式

且webpack底层运行机制是node,因此可以通过nodeJS提供的机制给webpack程序提供环境变量。

module.exports = (env, argv) => ({//可以通过env获取当前模式 mode: env.production ? 'production' : 'development', // 从 env 参数获取 mode devtool: env.production ? false : 'eval-cheap-source-map', // 开发环境需要 source map }) //在script命令行中指定env { "scripts": { "build:pro": "webpack --env.production" } } // env={ production:true }

(2)某些情况下需要根据不同的环境,进行不同的配置,但是多种配置在一个文件内,不容易阅读。这时候可以根据不同的环境拆分为多个文件,根据不同的环境加载相应的配置文件。

基本划分如下:

  • webpack.base.js:基础部分,即多个文件中共享的配置
  • webpack.development.js:开发环境使用的配置
  • webpack.production.js:生产环境使用的配置
  • webpack.test.js:测试环境使用的配置

因为webpack配置文件就是一个js文件,因此可以动态添加配置或者引入文件

const config = { // ... webpack 配置 } // 我们可以修改这个 config 来调整配置,例如添加一个新的插件 config.plugins.push(new YourPlugin()); module.exports = config;

可以使用webpack-merge把多个配置文件智能整合为一个向外暴露文件:

webpack.base.js module.exports = { entry: '...', output: { // ... }, resolve: { // ... }, module: { // 这里是一个简单的例子 rules: [ { test: /\.js$/, use: ['babel'], }, ], // ... }, plugins: [ // ... ], }

webpack.development.js const { smart } = require('webpack-merge') const webpack = require('webpack') const base = require('./webpack.base.js') module.exports = smart(base, { module: { rules: [ // 用 smart API,当这里的匹配规则相同且 use 值都是数组时,smart 会识别后处理 // 和上述 base 配置合并后,这里会是 { test: /\.js$/, use: ['babel', 'coffee'] } // 如果这里 use 的值用的是字符串或者对象的话,那么会替换掉原本的规则 use 的值 { test: /\.js$/, use: ['coffee'], }, // ... ], }, plugins: [ // plugins 这里的数组会和 base 中的 plugins 数组进行合并 new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), }), ], })

十三、提升构建速度

如果项目过大,或者需要构建的配置比较多,那么构建速度就会很慢。若需要提升webpack的构建速度,那么让webpack少干点活,尽量避免让webpack去做一些不必要的事情,那么构建速度必能显著增加。

1、减少resolve的解析

尽量把modules里面的目录名写详细;

代码中的引用路径尽量写详细,清晰的路径,这样webpack寻找的时间就能大大减少;

删除不必要的的后缀自动补全,代码中尽量书写引用文件后缀,这样也是减少寻找路径文件的时间;

2、把loader应用的文件范围缩小

只在 最少数 的代码模块去使用 必要的 loader

rules: [ { test: /\.jsx?/, include: [ path.resolve(__dirname, 'src'), // 限定只在 src 目录下的 js/jsx 文件需要经 babel-loader 处理 // 通常我们需要 loader 处理的文件都是存放在 src 目录 ], use: 'babel-loader', }, // ... ],

3、减少 plugin 的消耗

4、使用工具提速

(1)thread-loader

thread-loader是官方提供的一个可以利用多进程加速loader的loader,在loader使用特别多,且loader(例如babel、TypeScript)需要处理的内容特别多的时候,可以多线程进行处理。

(2)DLLPlugin

DLLPlugin跟optimization.splitChunks作用类似,都是分离代码,但DLLPlugin使用相对复杂,基本原理是先单独打包出一个JS文件(公共库),然后,再进行构建,webpack此时的构建,将不会构建

DLLPlugin构建出来的代码。再最后构建完后,需要在html中以次引入DLLPlugin构建的公共代码库和构建好的main.js。这样做的好处是,一是减少了构建的代码量,提高了构建速度;二是公共部分如果没有更 改的话,只需构建业务代码即可。公共库一直引用之前的构建。

5、流程优化

(1)拆分构建步骤,例如压缩图片,图片如果处理变化不大的话,可以使用其他方法先压缩了图片,而不需要webpack去处理。

(2)拆分项目代码。一般需要考虑构建性能优化,都是代码非常多的情况下。这时候,如果能根据业务模块拆分不同的代码库,这样不但提升项目的构建速度,更有助于业务清晰,局部更新的快捷。

十四、开发loader

(1)loader本质上是一个functuo,通过书写的js文件将文件按规则编译。本地开发时,可在webpack配置文件将本地JSloader引入或者按一个packge包引入;

/ ... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: path.resolve('./loader/index.js'), // 使用本地的 ./loader/index.js 作为 loader }, ], }, // 在 resolveLoader 中添加本地开发的 loaders 存放路径 // 如果你同时需要开发多个 loader,那么这个方式可能会更加适合你,但是 loader 需要包装成 package,即需要 package.json 文件 module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ 'your-loader' // 匹配 package.json 中的包名称 ] } ] }, resolveLoader: { modules: [ 'node_modules', path.resolver(__dirname, 'loaders') ], },

loader JS文件

const marked = require("marked");//marked 是一个用于解析 Markdown 的类库,可以把 Markdown 转为 HTML const loaderUtils = require("loader-utils"); module.exports = function (markdown){ // 使用 loaderUtils 来获取 loader 的配置项 // this 是构建运行时的一些上下文信息 const options = loaderUtils.getOptions(this); this.cacheable(); // 把配置项直接传递给 marked marked.setOptions(options); // 使用 marked 处理 markdown 字符串,然后以 JS Module 的方式导出,返回最终的 JS 代码 return `export default \`${marked(markdown)}\`;`; } //复杂的loader函数 module.exports = function (content,map,meta,){ //content为传入需要处理的内容,可以是字符串,buffer或者是图片等文件 //map是则是sourcemap对象 //meta是其他的一些元数据 //this.callback(err,content,map,meta)//也可以调用此方法返回这些信息 return ''//可以单独返回一个值 } //如果是user了多个文件,执行顺序是按照从后到前进行loader执行,也是从右到左的执行顺序,后一个loader执行的是前一个loader函数返回的执行的结果 //当有些loader需要依赖外部的i/o结果时,那就是必须是异步的方式来处理,这个时候,需要在loader中进行const callback = this.async(); //且用callback(null,data,map,meta)来返回loader的处理结果; const less = require('less'); module.exports = function (content, map, meta) { const callback = this.async(); // Less 的编译调用是异步的 less.render(content, { sourceMap: {} }, (error, output) => { if (error) callback(error); // 抛出异常 // 正常返回 callback(null, output.css, output.map, meta); }); };

(2)pitch

loader中还有一个pitch的方法,可以让loader链上设置数据,或者调整加载执行loader的步骤;

//yourloader.js function yourLoaderFunction() { // ... } yourLoaderFunction.pitch = function(remainingRequest, precedingRequest, data) { // ... data.value = 42; // data 中挂载的数据在后边 loader 执行时可以从 loader 的 this.data 中访问到 return `module.exports = require(' + JSON.stringify('-!' + remainingRequest) + ');`; // 返回解析结果用于跳过后续 loader 的执行 } //若有a,b,c三个loader,若b.loader.js内设置了pitch-return //原本的执行过程为: -> a-loader pitch -> b-loader pitch -> c-loader pitch -> c-loader 执行 -> b-loader 执行 -> a-loader 执行 //设置后则为: -> a-loader pitch -> b-loader pitch 返回模块结果 -> a-loader 执行

十五、开发plugin

plugin的是webpack中处理构建内容以外的多样性构建配置,实现可以是一个类,使用时传入相关配置来创建一个实例。然后放到配置文件的plugins字段中。

//FileListPlugin.js class FileListPlugin { constructor(option) { // 读取 plugin 实例化时传入的配置 } apply(compiler) {//apply会在webpack compiler安装插件时会被调用一次,用来接收compiler对象实例引用,在compiler注册各种钩子函数,在webpack构建的流程,相应的步骤执行构建任务 // 在 compiler 的 emit hook 中注册一个方法,当 webpack 执行到该阶段时会调用这个方法 compiler.hooks.emit.tap('FileListPlugin', (compilation) => { // 给生成的 markdown 文件创建一个简单标题 var filelist = 'In this build:\n\n' // 遍历所有编译后的资源,每一个文件添加一行说明 for (var filename in compilation.assets) { filelist += ('- ' + filename + '\n') } // 将列表作为一个新的文件资源插入到 webpack 构建结果中 compilation.assets['filelist.md'] = { source: function () { return filelist }, size: function () { return filelist.length }, } }) } } module.exports = FileListPlugin //webpack.config.js const FileListPlugin = require('./plugins/FileListPlugin.js') plugins:[ new FileListPlugin(), // 实例化这个插件,有的时候需要传入对应的配置 ],

开发plugin类,最重要的是webpack提供的hooks,可以查阅hooks列表compiler hooks 和 compilation hooks来查阅webpack提供的事件钩子。

this.hooks = { shouldEmit: new SyncBailHook(["compilation"]), // 这里的声明的事件钩子函数接收的参数是 compilation, done: new AsyncSeriesHook(["stats"]), // 这里接收的参数是 stats,以此类推 additionalPass: new AsyncSeriesHook([]), beforeRun: new AsyncSeriesHook(["compilation"]), run: new AsyncSeriesHook(["compilation"]), emit: new AsyncSeriesHook(["compilation"]), afterEmit: new AsyncSeriesHook(["compilation"]), thisCompilation: new SyncHook(["compilation", "params"]), // ... };

hooks具有同步和异步两类,根据名称可以区分,同步需要使用tap来绑定注册事件,如果是异步的钩子函数tapPromise或者tapAsync来注册事件

compiler.hooks.emit.tap('FileListPlugin', (compilation) => { } compiler.hooks.done.tapPromise('PluginName', (stats) => {//tapPromise必须返回promise // 返回 promise return new Promise((resolve, reject) => { // 这个例子是写一个记录 stats 的文件 fs.writeFile('path/to/file', stats.toJson(), (err) => err ? reject(err) : resolve()) }) }) // 或者 compiler.hooks.done.tapAsync('PluginName', (stats, callback) => {//tapAsync必须使用 callback 来返回结果 // 使用 callback 来返回结果 fs.writeFile('path/to/file', stats.toJson(), (err) => callback(err)) })

通过compiler与compilation的生命周期hooks也可以很好了解webpack整个的构建过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值