你要是沒有抽象語法樹基礎的話,建議先看一下上一篇文章抽象語法樹 Abstract syntax tree這樣更有助於對下文的理解!
背景:當我們同時引入一個包中的兩個方法,有兩種形式
第一種形式
import {flatten,join} from 'lodash';
複製程式碼
第二種形式
import flatten from 'lodash/flatten';
import join from 'lodash/join';
複製程式碼
對比兩種形式,我們可以看出:
第一種方式的引入會把整個lodash包引進來 第二種方式是指引入整個包中的兩個方法
顯然我們要用第二種
但是一般的專案中 大部分都是以import解構的形式,所以在這裡我們就寫一個外掛,當我們寫成第一種形式引入的時候,利用外掛轉化成第二種形式
這是我們要寫的外掛的功能
第一步:初始化一個webpack的專案
npm i webpack webpack-cli babel-core babel-loader babel-preset-env babel-preset-stage-0 -D
複製程式碼
在Webpack中,提供了mode變數,用於配置執行環境,mode的值可以為development,表示的是開發模式,或者是production,表示的是生產模式。
在package.json中寫入編譯的命令
"scripts":{
"build":"webpack --mode production/development"
}
複製程式碼
第二步:建立一個專案結構
webpack-plugin // 專案名稱
dist // 打包輸出目錄
bundle.js // 打包輸出的檔案
src // 主要邏輯
index.js // 專案的入口檔案
./babelrc // 語法解析配置
package.json
webpack.config.js
複製程式碼
第三步:寫webpack的配置檔案
const path = require('path');
module.exports = {
entry: './src/index.js',// 入口檔案
output: {
path: path.join(__dirname, 'dist'), // 輸出路徑
filename: 'bundle.js' // 輸出的檔名稱
},
// 配置
module: {
// 配置載入器
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
}
}
複製程式碼
第四步:配置.babelrc
{
"presets": [
"env",
"stage-0"
],
"plugins": [
[
// "demand-loading", // 注:這個是我們自己寫的外掛名 顯示先不放,等我們寫好外掛後再加上
{
"library": "lodash", // 我們在引用哪個庫的時候使用我們寫的這個外掛,這裡的意思是當我們引用lodash庫的時候使用我們寫的這個外掛
},
"syntax-decorators"
]
]
}
複製程式碼
第五步:index.js中寫入對比指令碼
// import {flatten,join} from 'lodash'
import flatten from 'lodash/flatten'
import join from 'lodash/join'
複製程式碼
先引入後面的這兩句,然後打包一下
Hash: fcb0bd5d9734b5f56676
Version: webpack 4.2.0
Time: 346ms
Built at: 2018-3-27 21:24:33
Asset Size Chunks Chunk Names
bundle.js 21.3 KiB main [emitted] main
Entrypoint main = bundle.js
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 823 bytes {main} [built]
[./src/index.js] 286 bytes {main} [built]
+ 15 hidden modules
複製程式碼
看到這樣打包後的程式碼,我們發現這種方式引入 打包後的大小是。21.3k
然後註釋掉後兩行,只引入第一行
Hash: aa8b689e1072463fc1cd
Version: webpack 4.2.0
Time: 3277ms
Built at: 2018-3-27 21:30:22
Asset Size Chunks Chunk Names
bundle.js 483 KiB main [emitted] main
Entrypoint main = bundle.js
[./node_modules/webpack/buildin/amd-options.js] (webpack)/buildin/amd-options.js 82 bytes {main} [built]
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 823 bytes {main} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 521 bytes {main} [built]
[./src/index.js] 47 bytes {main} [built]
+ 1 hidden module
複製程式碼
這次打包後的大小是 483k
通過對比 證實了我們所說的兩種引入方式的區別
第六步:那我們就來寫寫這個外掛吧!
先對比一下兩種引入方式的抽象語法樹的差別
通過對比我們發現,只是ImportDeclaration不相同
const babel = require('babel-core');
const types = require('babel-types');
let visitor = {
// 這裡的ref是ImportDeclaration的第二個引數,這裡的值是.babelrc中的 {
// "library": "lodash"
//}, 這裡是指定 我們在引用哪個庫的時候使用這個外掛
ImportDeclaration(path, ref={options:{}}) {
let node = path.node;
let specifiers = node.secifiers
if (options.library == node.soure.value && !types.isImportDeclaration(specifiers[0])) {
let newImport = specifiers.map((specifier) => (
types.importDeclaration([types.ImportDefaultSpecifier(specifier.local)], types.stringLiteral(`${node.soure.value}/${specifier.local.name}`))
));
path.replaceWithMultiple(newImport)
}
}
}
const code = "import {flatten, join} from 'lodash';";
let r = babel.transform(code, {
plugins: [
{visitor}
]
})
複製程式碼
在建立替換邏輯的時候,types上的方法 用github上的這個網址,哪個不會搜哪個,媽媽再也不用擔心我的學習。嘻嘻
第七步:將上面的程式碼整理一下放到node_modules檔案中
新建一個資料夾 babel-plugin-demand-loading 放到node_modules中,再新建一個index.js檔案,將下面的程式碼放進去,再然後進入這個資料夾 npm init -y初始化一個package.json檔案,裡面的入口檔案寫成index.js
需要注意的事項:
第一: babel外掛的資料夾命名,必須以 babel-plugin-xxx(你寫的外掛名)命名,否則引入不成功 第二: babel外掛返回的是一個物件,裡面有一個訪問者模式的visitor物件,裡面是我們的轉化程式碼
const babel = require('babel-core');
const types = require('babel-types');
module.exports = {
visitor: {
// 這裡的ref是ImportDeclaration的第二個引數,這裡的值是.babelrc中的 {
// "library": "lodash"
//}, 這裡是指定 我們在引用哪個庫的時候使用這個外掛
ImportDeclaration(path, ref={}) {
let { opts } = ref
let node = path.node;
let specifiers = node.specifiers
if (opts.library == node.source.value && !types.isImportDeclaration(specifiers[0])) {
let newImport = specifiers.map((specifier) => (
types.importDeclaration([types.ImportDefaultSpecifier(specifier.local)], types.stringLiteral(`${node.source.value}/${specifier.local.name}`))
));
path.replaceWithMultiple(newImport)
}
}
}
}
複製程式碼
最後 npm run build 編譯後,發現打包後的大小是。20多k說明我們的外掛起作用了。
到這裡你是不是也會了呢?嘻嘻!
不要厭煩熟悉的事物,每天都進步一點;不要畏懼陌生的事物,每天都學習一點;