一.webpack的一些理解
1.什么是webpack
webpack是一款模塊加載器兼打包工具,它把各種資源,例如js(含jsx)、coffee哟绊、樣式(含less/sass)、圖片等作為模塊來使用和處理痰憎,它的目的就是把有依賴關(guān)系的各種文件打包成一系列的靜態(tài)資源
2.webpack的優(yōu)勢
- webpack是以commonJs的形式來書寫腳本的票髓,但對AMD/CMD的支持也很全面
- 支持很多模塊加載器的調(diào)用,可以使模塊加載器靈活定制铣耘,比如babel-loader加載器洽沟,該加載器能使我們使用es6的語法來編寫代碼;less-loader加載器蜗细,可以將less編譯成css文件
- 開發(fā)便捷裆操,能替代部分grunt/gulp的工作,比如打包炉媒、壓縮混淆踪区、圖片轉(zhuǎn)base64等
- 可以通過配置打包成多個文件,有效的利用瀏覽器的緩存功能提升性能
3.webpack與其他類似工具有哪些不同
- 有同步和異步兩種不同的加載方式
- loader吊骤,加載器可以將其他資源整合到j(luò)s文件中缎岗,通過這種方式,將所有源文件形成一個模塊
- 優(yōu)秀的語法分析能力白粉,支持CommonJs AMD規(guī)范
- 有豐富的開源插件庫传泊,可以根據(jù)自己的需求定制webpack的配置
4.webpack的打包流程
4-1.讀取webpack的配置參數(shù)
4-2.啟動webpack鼠渺,創(chuàng)建Compiler對象并開始解析項目
4-3.從入口文件entry開始解析,并且找到其導入的依賴模塊眷细,遞歸遍歷分析拦盹,形成依賴關(guān)系樹
4-4.對不同文件類型的依賴模塊文件使用對應(yīng)的Loader進行編譯,最終轉(zhuǎn)為javascript文件
4-5.整個過程中webpack會通過發(fā)布訂閱模式薪鹦,向外拋出一些hooks掌敬,而webpack的插件即可通過監(jiān)聽這些關(guān)鍵的事件節(jié)點惯豆,執(zhí)行插件任務(wù)進而達到干預輸出結(jié)果的目的
5.webpack文件的解析與構(gòu)建
文件的解析與構(gòu)建是一個比較復雜的過程池磁,在webpack源碼中主要依賴compiler和compilation兩個核心對象實現(xiàn)
compiler是一個全局單例,他負責把控整個webpack打包的構(gòu)建過程楷兽,compilation對象是每一次構(gòu)建的上下文對象地熄,它包含了當次構(gòu)建所需要的所有信息,每次熱更新和重新構(gòu)建芯杀,compiler都會重新生成一個新的compilation對象端考,負責此次更新的構(gòu)建過程
而每個模塊間的依賴關(guān)系,則依賴于AST語法樹揭厚。每個模塊文件在通過Loader解析完成之后却特,會通過acorn庫生成模塊代碼的AST語法樹,通過語法樹就可以分析這個模塊是否還有依賴的模塊筛圆,進而繼續(xù)循環(huán)執(zhí)行下一個模塊的編譯解析裂明。
最終Webpack打包出來的bundle文件是一個IIFE的執(zhí)行函數(shù)。
6.webpack里的sourcemap
sourmap是一項將編譯太援、打包闽晦、壓縮后的代碼映射回源代碼的技術(shù),由于打包壓縮后的代碼沒有閱讀性可言提岔,一旦報錯或者遇到問題仙蛉,我們只能定位到壓縮處理后的代碼位置,無法定位到開發(fā)環(huán)境的代碼碱蒙,不好調(diào)試荠瘪,而sourcemap可以快速幫我們定位到源代碼的位置,提高開發(fā)效率
在項目打包完后赛惩。在打包的文件夾里除了js,css等資源文件外巧还,還有xxx.js.map的文件,這種帶map后綴的文件就是sourcemap文件坊秸,它保存了源代碼和轉(zhuǎn)換之后代碼(通常經(jīng)過壓縮混淆和其他轉(zhuǎn)換)的關(guān)系
6-1.常見的轉(zhuǎn)換過程包括但不限于:
壓縮混淆(UglifyJS)
編譯(TypeScript, CoffeeScript)
轉(zhuǎn)譯(Babel)
合并多個文件麸祷,減少帶寬請求。
6-2.sourcemap
SourceMap 的主要作用是為了方便調(diào)試
映射轉(zhuǎn)換過后的代碼和源代碼之間的關(guān)系
源代碼引入 //# sourceMappingURL=build.js.map
source Map 解決了源代碼和運行代碼不一致所產(chǎn)生的問題
注:sourceMap并不是webpack特有的功能
二.webpack配置
1.使用不同的配置文件:
如果需要使用不同的配置文件褒搔,需要在package.json文件中使用--config標志修改阶牍,例:
2.webpack使用不同編程語言和數(shù)據(jù)描述格式來編寫配置文件
2-1.typeScript
npm install --save-dev typescript ts-node @types/node @types/webpack
# 如果使用版本低于 v4.7.0 的 webpack-dev-server喷面,還需要安裝以下依賴
npm install --save-dev @types/webpack-dev-server
#webpack.config.ts
import * as path from 'path';
import * as webpack from 'webpack';
// in case you run into any typescript error when configuring `devServer`
import 'webpack-dev-server';
const config: webpack.Configuration = {
mode: 'production',
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js',
},
};
export default config;
該示例需要 typescript 版本在 2.7 及以上,并在 tsconfig.json 文件的 compilerOptions 中添加 esModuleInterop 和 allowSyntheticDefaultImports 兩個配置項走孽。
注意:你需要確保tsconfig.json的compilertions文件的compilerOptions中module選項的值為common.js,否則webpack的運行會失敗報錯惧辈,因為ts-node不支持commonjs以外的其他的模塊規(guī)范。
你可以通過三個途徑來完成module的設(shè)置:
- 直接修改tscinfig.json文件
- 修改tsconfig.json并且添加ts-node的設(shè)置
- 使用tsconfig-paths
第一種方法就是打開你的 tsconfig.json 文件磕瓷,找到 compilerOptions 的配置盒齿,然后設(shè)置 target 和 module 的選項分別為 "ES5" 和 "CommonJs" (在 target 設(shè)置為 es5 時你也可以不顯示編寫 module 配置)。
第二種方法 就是添加 ts-node 設(shè)置:
你可以為 tsc 保持 "module": "ESNext"配置困食,如果你是用 webpack 或者其他構(gòu)建工具的話边翁,為 ts-node 設(shè)置一個重載(override)
{
"compilerOptions": {
"module": "ESNext",
},
"ts-node": {
"compilerOptions": {
"module": "CommonJS"
}
}
}
第三種方法需要先安裝 tsconfig-paths 這個 npm 包,如下所示:
npm install --save-dev tsconfig-paths
安裝后你可以為 webpack 配置創(chuàng)建一個單獨的 TypeScript 配置文件硕盹,示例如下:
# tsconfig-for-webpack-config.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"esModuleInterop": true
}
}
注:ts-node 可以根據(jù) tsconfig-paths 提供的環(huán)境變量 process.env.TS_NODE_PROJECT 來找到 tsconfig.json 文件路徑符匾。
# package.json
{
"scripts": {
"build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" webpack"
}
}
注:之所以要添加 cross-env,是因為我們在直接使用 TS_NODE_PROJECT 時遇到過 "TS_NODE_PROJECT" unrecognized command 報錯的反饋瘩例,添加 cross-env 之后該問題也似乎得到了解決啊胶。
2-2.coffeeScript
npm install --save-dev coffeescript
示例如下:
#webpack.config.coffee
HtmlWebpackPlugin = require('html-webpack-plugin')
webpack = require('webpack')
path = require('path')
config =
mode: 'production'
entry: './path/to/my/entry/file.js'
output:
path: path.resolve(__dirname, 'dist')
filename: 'my-first-webpack.bundle.js'
module: rules: [ {
test: /\.(js|jsx)$/
use: 'babel-loader'
} ]
plugins: [
new HtmlWebpackPlugin(template: './src/index.html')
]
module.exports = config
3.導出
3-1.導出函數(shù):
module.exports=function(env,argv){
return {
mode:env.production ? 'production' : 'development',
devtool:env.production ? 'source-map' : 'eval',
plugins:[
new TerserPlugin({
terserOptions:{
compress:argv.mode === 'production' //only if `--mode production` was passed
}
})
]
}
}
3-2.導出promise
module.exports=()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve({
entry:'./app.js'
})
},5000)
})
}
注:支持使用Promise.all導出多個promise
3-3.導出多種配置
module.exports = [
{
output: {
filename: './dist-amd.js',
libraryTarget: 'amd',
},
name: 'amd',
entry: './app.js',
mode: 'production',
},
{
output: {
filename: './dist-commonjs.js',
libraryTarget: 'commonjs',
},
name: 'commonjs',
entry: './app.js',
mode: 'production',
},
];
注:如果你只傳了一個
--config-name
名字標識,webpack 將只會構(gòu)建指定的配置項垛贤。
3-3-1.dependencies
以防你的某個配置依賴于另一個配置的輸出焰坪,你可以使用一個dependencies列表指定一個依賴列表
#webpack.config.js
module.exports = [
{
name: 'client',
target: 'web',
// …
},
{
name: 'server',
target: 'node',
dependencies: ['client'],
},
];
3-3-2.parallelism
如果你導出了多個配置,你可以在配置中使用parallelism選項來指定編譯的最大并發(fā)數(shù)
#webpack.config.js
module.exports = [
{
//config-1
},
{
//config-2
},
];
module.exports.parallelism = 1;
4.入口和上下文
入口對象是用于 webpack 查找開始構(gòu)建 bundle 的地方聘惦。上下文是入口文件所處的目錄的絕對路徑的字符串某饰。
4-1.content(基礎(chǔ)目錄,絕對路徑部凑,用于從配置中解析入口點和加載器)
const path = require('path');
module.exports = {
//...
context: path.resolve(__dirname, 'app'),
};
默認使用 Node.js 進程的當前工作目錄露乏,但是推薦在配置中傳入一個值。這使得你的配置獨立于 CWD(current working directory, 當前工作目錄)涂邀。
4-2.entry開始應(yīng)用程序打包過程的一個或多個起點(配置文件中entry接受三種形式的值:字符串瘟仿,數(shù)組,函數(shù)和對象比勉,如果entry是字符串或者字符串數(shù)組劳较,文件導出后會被命名為main,如果傳入的是個對象浩聋,則每個屬性的鍵是導出的文件的名字观蜗,鍵值則是該文件的入口點)
例1:
module.exports={
entry:'./app.js'
}
例2:
entry: {
'path/of/entry': './deep-app.js',
'app': './app.js'
}
例3:
entry: {
vendor: ['jquery', 'lodash']
}
例4:
entry: () => new Promise((resolve) => resolve(['./demo', './demo2'])),
如果傳入的是個函數(shù),它將在每次make事件中被調(diào)用
默認情況下衣洁,入口的輸出文件名是從output.filename中提取出來的墓捻,但可以指定一個自定義的輸出文件名
例5:
module.exports = {
//...
entry: {
app: './app.js',
home: { import: './contact.js', filename: 'pages/[name][ext]' },
about: { import: './about.js', filename: 'pages/[name][ext]' },
},
};
5.mode
5-1.用法
在配置對象中使用mode選項
例:
module.exports={
mode:'development'
}
從CLI參數(shù)中傳遞
例:
webpack --mode-development
mode支持以下字符串值
development:會將 DefinePlugin 中 process.env.NODE_ENV 的值設(shè)置為 development. 為模塊和 chunk 啟用有效的名。
production:會將 DefinePlugin 中 process.env.NODE_ENV 的值設(shè)置為 production坊夫。為模塊和 chunk 啟用確定性的混淆名稱砖第,F(xiàn)lagDependencyUsagePlugin撤卢,F(xiàn)lagIncludedChunksPlugin,ModuleConcatenationPlugin梧兼,NoEmitOnErrorsPlugin 和 TerserPlugin
none:不使用任何默認優(yōu)化選項
如果沒有設(shè)置放吩,webpack的mode的默認值是production
6.output
6-1.asyncChunks:創(chuàng)建按需加載的異步chunk
例:
module.exports={
output:{
asyncChunks:true
}
}
6-2.filename控制輸出資源的文件名,也可以是個相對路徑羽杰,文件夾不存在會在輸出時創(chuàng)建
例:
module.exports={
entry:'/src/main.js',
output:{
filename:'bundle.js
}
}
6-3.path(指定資源輸出的位置渡紫,在webpack4之后,output.path默認為dist目錄考赛,除非我們想更改它惕澎,否則不必單獨配置,不需要寫)
const path=require('path')
module.exports={
entry:'./src/main.js',
output:{
filename:'bundle.js',
//將資源輸出位置設(shè)置為該項目的dist目錄
path: path.resolve(__dirname, 'dist')
},
}
多入口的情況下欲虚,我們需要對產(chǎn)生的每個bundle指定不同的名字集灌,Webpack支持使用一種類似模板語言的形式動態(tài)地生成文件名
module.exports = {
entry:{
main:'./src/main.js',
vender:'./src/vender.js'
},
output: {
filename: '[name].js',
},
};
filename中的[name]會被替換為chunk name即main和vender悔雹。因此最后會生成vendor.js與main.js
模板變量
可在編譯層面進行替換的內(nèi)容
[fullhash]:指代Webpack此次打包所有資源生成的hash
[hash]:指代Webpack此次打包所有資源生成的hash复哆,已棄用
可在chunk層面進行替換的內(nèi)容
[id]:指代當前chunk的id
[name]:設(shè)置則設(shè)置的值為此chunk的名稱,否則使用chunk的id
[chunkhash]:指代當前chunk內(nèi)容的hash腌零,包含該chunk的所有元素
[contenthash]:此 chunk 的 hash 值梯找,只包括該內(nèi)容類型的元素(受 optimization.realContentHash 影響)
可在模塊層面替換的內(nèi)容
[id]:模塊的 ID
[moduleid]:模塊的 ID,已棄用
[hash]:模塊的 Hash 值
[modulehash]:同上益涧,但已棄用
[contenthash]:模塊內(nèi)容的 Hash 值
可在文件層面替換的內(nèi)容
[file] : filename 和路徑锈锤,不含 query 或 fragment
[query] :帶前綴 ?
的 query
[fragment] :帶前綴 #
的 fragment
[base]:只有 filename(包含擴展名),不含 path
[filebase]:同上闲询,但已棄用
[path]:只有 path久免,不含 filename
[name]:只有 filename,不含擴展名或 path
[ext]: 帶前綴 .
的擴展名(對 output.filename不可用)
可在 URL 層面替換的內(nèi)容
[url]:URL
6-4.publicPath
publicPath是一個非常重要的配置項扭弧,用來指定資源的請求位置
原本圖片請求的地址是./img.jpg阎姥,而在配置上加上publicPath后,實際路徑就變成了了./dist/static/img/img.jpg鸽捻,這樣就能從打包后的資源中獲取圖片了
6-5.HtmlWebpackPlugin
npm install --save-dev html-webpack-plugin
//添加插件 plugins:[ new HtmlWebpackPlugin({ title:'output management' }) ],
打包完成后你會發(fā)現(xiàn)dist中出現(xiàn)了一個新的index.html,上面自動幫我們添加所生成的資源呼巴,打開后會發(fā)現(xiàn)瀏覽器會展示出內(nèi)容
6-6. assetModuleFilename配置圖片打包
圖片打包,webpack通過配置assetModuleFilename可以打包css里面的圖片。js import的圖片默認是不能失敗的御蒲。webpack5通過內(nèi)置配置可以實現(xiàn)衣赶,也可以安裝url-loader或者file-loader模塊。
6-7.auxiliaryComment模塊化導出技術(shù)的注釋屬性(需要output.library和output.libraryTarget一起使用厚满,如果想要注釋更細粒度的控制府瞄,可以傳入一個對象)
例:
module.exports = {
//...
output: {
library: 'someLibName',
libraryTarget: 'umd',
filename: 'someLibName.js',
auxiliaryComment: 'Test Comment',
},
};
將會生成:
(function webpackUniversalModuleDefinition(root, factory) {
// Test Comment
if (typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require('lodash'));
// Test Comment
else if (typeof define === 'function' && define.amd)
define(['lodash'], factory);
// Test Comment
else if (typeof exports === 'object')
exports['someLibName'] = factory(require('lodash'));
// Test Comment
else root['someLibName'] = factory(root['_']);
})(this, function (WEBPACK_EXTERNAL_MODULE_1) {
// ...
});
例2:
module.exports = {
//...
output: {
//...
auxiliaryComment: {
root: 'Root Comment',
commonjs: 'CommonJS Comment',
commonjs2: 'CommonJS2 Comment',
amd: 'AMD Comment',
},
},
};
6-8.charset(告訴webpack為html的script的標簽添加)
6-9.chunkFilename
當你的代碼中使用了動態(tài)import時,webpack會將動態(tài)import的包碘箍,單獨打包遵馆, 這樣子實現(xiàn)按需載入续崖,但是打包后的文件名可能是一個隨機串。
所以為了識別這些bundle塊团搞,使用的時候需要傳入name严望,如下所示
document.getElementById('btn').onclick = function() {
import( /* webpackChunkName: "test123123" */ './test').then((res) => {
console.log(res)
console.log(res.default())
})
}
import調(diào)用的時候在括號里傳入注釋,如上所示逻恐,注釋是/* webpackChunkName: "test123123" */像吻,后面跟個空格,接上路徑地址复隆,意思是打包出來的bundle文件名叫test123123.js拨匆,webpack的output需要配置成 chunkFilename: 'static/js/[name].js',,這個[name]就能取到test123123
6-10.chunkLoadTimeout
chunk 請求到期之前的毫秒數(shù)挽拂,默認為 120000
6-11.chunkLoadingGlobal
webpack用于加載春看到·全局變量
例:
module.exports = {
//...
output: {
//...
chunkLoadingGlobal: 'myCustomFunc',
},
};
6-12.chunkLoading加載chunk方法
'jsonp' (web)惭每、'import' (ESM)、'importScripts' (WebWorker)亏栈、'require' (sync node.js)台腥、'async-node' (async node.js)
6-13.clean
例:
module.exports={
output:{
clean:true//在生成文件之前清空output目錄
}
}
例2:
module.exports={
output:{
clean:{
dry:true//打印而不是刪除應(yīng)該移除的靜態(tài)資源
}
}
}
例3:
module.exports={
output:{
clean:{
keep:/ignored/dir//, //保留ignored/dir下的靜態(tài)資源
}
}
}
6-14.compareBeforeEmit(告訴webpack在寫入到輸出文件系統(tǒng)時檢查輸出的文件是否已經(jīng)存在并且擁有相同內(nèi)容)
6-15.crossOriginLoading(告訴webpack啟用cross-origin屬性加載chunk,僅在target設(shè)置為web時生效绒北,通過使用JSONP來添加腳本標簽黎侈,實現(xiàn)按需加載模塊)
'anonymous'-不帶憑據(jù)(credential)啟用跨域加載
'use-credentials'-攜帶憑據(jù)(credential)啟用跨域加載
6-16.library(輸出一個庫,為你的入口做導出)
module.exports = {
// …
entry: './src/index.js',
output: {
library: 'MyLibrary',
},
};
library.export(指定哪一個導出應(yīng)該被暴露為一個庫)
7.module
7-1.generator(在一個地方配置所有生成器的選項)
module.exports = {
module: {
generator: {
asset: {
// asseet 模塊的 generator 選項
// 自定義 asset 模塊的 publicPath闷游,自 webpack 5.28.0 起可用
publicPath: 'assets/',
// 將靜態(tài)資源輸出到相對于 'output.path' 的指定文件夾中峻汉,webpack 5.67.0 后可用
outputPath: 'cdn-assets/',
},
'asset/inline': {
// asset/內(nèi)聯(lián)模塊的 generator 選項
},
'asset/resource': {
// asset/資源模塊的 generator 選項
// 自定義 asset/resource 模塊的 publicPath,自 webpack 5.28.0 起可用
publicPath: 'assets/',
// 將靜態(tài)資源輸出到相對于 'output.path' 的指定文件夾中脐往,webpack 5.67.0 后可用
outputPath: 'cdn-assets/',
},
javascript: {
// 該模塊類型尚不支持 generator 選項
},
'javascript/auto': {
// 同上
},
'javascript/dynamic': {
// 同上
},
'javascript/esm': {
// 同上
},
// 其他...
},
},
};
7-2.parser(類似于module.generator休吠,你可以用 module.parser
在一個地方配置所有解析器的選項)
7-3.noParse(防止 webpack 解析那些任何與給定正則表達式相匹配的文件。忽略的文件中 不應(yīng)該含有 import, require, define 的調(diào)用业簿,或任何其他導入機制瘤礁。忽略大型的 library 可以提高構(gòu)建性能)
例:
module.exports = {
//...
module: {
noParse: /jquery|lodash/,
},
};
7-4.unsafeCache(緩存模塊請求的解析)
包含如下幾個默認值
如果cache未被啟用,則默認值為false
如果cache被啟用辖源,并且此模塊的來自于node_modules則值為true蔚携,否則為false
module.exports = {
//...
module: {
unsafeCache: false,
},
};
7-5.rules
創(chuàng)建模塊時,匹配請求的規(guī)則數(shù)組克饶,這些規(guī)則能夠修改模塊的創(chuàng)建方式酝蜒。 這些規(guī)則能夠?qū)δK(module)應(yīng)用 loader,或者修改解析器(parser)矾湃。
7-5-1.rule:每個規(guī)則可以分為三部分 - 條件(condition)亡脑,結(jié)果(result)和嵌套規(guī)則(nested rule)
7-5-1-1.rule條件
條件有兩種輸入值
- 1.resource:資源文件的絕對路徑,它根據(jù)resolve規(guī)則解析
- 2.issuer:請求者的文件絕對路徑,是導入時的位置