教你如何編寫Babel外掛
前置知識:瞭解babel的使用,瞭解JavaScript語法樹
安裝babel-cli, babel-core
我們的打包檔案
import antd, { Table } from 'antd'; let arrow = () => {}; const component = <Table />; 複製程式碼
.babelrc
配置
{ "presets": ["react"], "plugins": [ [ "lessimport", //這是你開發的plugin名稱,包名命名為babel-plugin-xxxx { // 這些屬性可以隨意,最後可以在opts裡面訪問得到 "libraryName": "antd", "modulePath": "/lib/{moduleName}", "styleSuffix": "css", "stylePath": "/lib/{moduleName}/style/index.{styleSuffix}" } ] ] } 複製程式碼
外掛程式碼編寫
這是進行轉換操作的程式碼,在你新建的專案node_modules下面新建babel-plugin-lessimport資料夾 新建index.js,寫入以下程式碼
const babel = require('babel-core'); const type = require('babel-types'); const visitor = { ImportDeclaration(path, ref = { opts: {} }) { const specifiers = path.node.specifiers; const source = path.node.source; const libraryName = source.value; /** * 第二個判斷條件是判斷import語句裡面有沒有使用 import {xxx} 的語法,如果有,就替換 * 不加這個條件的後果就是,死迴圈 */ if (libraryName === ref.opts.libraryName && specifiers.find(specifier => type.isImportSpecifier(specifier))) { const declarationNodes = []; specifiers.forEach(specifier => { /** 不是預設匯入的 *為什麼要這麼判斷,因為可能會有這種寫法,import React, { Component } from 'react'; */ if (!type.isImportDefaultSpecifier(specifier)) { declarationNodes.push( /** * importDeclaration 第一個引數是import xxx from module 裡面的xxx * xxx可以是 {yyy} => [importSpecifier], yyy => [importDefaultSpecifier], 空 => [] * 第二個引數是module字串 */ type.importDeclaration( // 新增一個預設匯入的 specifier,可以多個,這樣就是import xxx, yyy from "test" [type.importDefaultSpecifier(specifier.local)], // type.stringLiteral 返回一個字面量字串 type.stringLiteral( // {moduleName} 是在配置裡面配置的,代表需要引入模組的名字 `${libraryName}${ref.opts.modulePath.replace('{moduleName}', specifier.local.name.toLowerCase())}` ) ) ); // 引入css 或者 less,可配置 if (ref.opts.styleSuffix) { declarationNodes.push( type.importDeclaration( [], // 空的specifier, 這樣就是 import from "xxxx" type.stringLiteral( `${libraryName}${ref.opts.stylePath .replace('{moduleName}', specifier.local.name.toLowerCase()) .replace('{styleSuffix}', ref.opts.styleSuffix)}` ) ) ); } return; } declarationNodes.push( type.importDeclaration([type.importDefaultSpecifier(specifier.local)], type.stringLiteral(libraryName)) ); }); // 一個節點替換成多個 path.replaceWithMultiple(declarationNodes); } }, /** * 轉換箭頭函式很簡單,直接把id, 引數,函式體,是否generator,是否async函式都賦給新函式 * 然後替換節點即可 */ ArrowFunctionExpression(path) { const node = path.node; if (node.type === 'ArrowFunctionExpression') { path.replaceWith(type.functionExpression(node.id, node.params, node.body, node.generator, node.async)); } }, /** * 把let, const轉換成var */ VariableDeclaration(path) { const node = path.node; if (node.kind === 'let' || node.kind === 'const') { // 變數宣告的kind, 可以是var let const // 然後第二個引數是宣告的變數,let a, b, c這裡的a, b, c就是node.declarations const varNode = type.variableDeclaration('var', node.declarations); path.replaceWith(varNode); } }, }; module.exports = function() { // 名稱必須是visitor return { visitor }; }; 複製程式碼
第一個 Node 是 source 的值,第二個是 specifiers 的值 你也可以去astexplorer看對應的語法樹(網址在文末給出)
執行,檢視外掛轉換效果
對程式碼執行
babel index.js --out-file out.js 複製程式碼
轉換後的程式碼為,說明轉換成功了
import antd from 'antd'; import Table from 'antd/lib/table'; import 'antd/lib/table/style/index.css'; var arrow = function (x = 5) { console.log(x); }; var component = React.createElement(Table, null); 複製程式碼
看到這就要恭喜你啦,再也不用寫這種囉裡囉嗦,打包出來檔案體積還大的程式碼了
import {Table} from "antd"; import from "antd/lib/table/style/index.css"; 複製程式碼
使用外掛後只需要第一句即可完成按需引入和自動引入css 還可以配置多個不同的庫
以後面試當面試官問你你寫過babel外掛時再也不用慌了(๑•̀ㅂ•́)و✧