1. 程式人生 > >antd圖示庫按需載入的外掛實現

antd圖示庫按需載入的外掛實現

![](https://img2020.cnblogs.com/blog/2029875/202007/2029875-20200706164008216-126977204.png) ### 前景概要 antd是阿里出品的一款基於antd的UI元件庫,使用簡單,功能豐富,被廣泛應用在中臺專案開發中,雖然也出現了彩蛋事故,但不能否認antd本身的優秀,而我們公司在實際工作中也大量使用antd進行開發,使用的版本主要集中在3.x這個大版本中,在實際使用過程中發現了一個比較明顯的問題,那就是antd的圖示輸出打包體積過大,即便是使用了一個圖示,也會將所有圖示打包輸出,沒有按需載入(4.x版本已經實現了按需載入,但目前公司還沒做整體的升級),在這種情況下,就亟需一款能按需載入的外掛來減小圖示輸出的體積。 ### 解決思路 定位了問題,接下來就是想辦法解決了,前期在網上搜索到一種解決辦法,那就是在webpack中的配置圖示檔案路徑,具體如下: ``` resolve: { alias: { '@ant-design/icons/lib/dist$': './youIcon.js', } } ``` youIcon.js中匯出使用到的圖示,通過這種方式能極大減小靜態資源輸出的體積,但是這一過程是手動配置,維護和使用不是很方便,藉助這種解決方式,加上動態生成本地youIcon.js檔案就可以了,確定瞭解決思路,接下來就動手設計外掛。 ### 外掛設計 #### 1.執行流程 ![圖片描述](http://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/antd-icon-reduce-1.png) #### 2.功能設計 如上圖所示,我們需要的外掛需要滿足兩個功能,第一,動態提取專案原始碼和使用到的antd元件中的圖示,第二,修改webpack的alias配置,接下來將分別講述設計過程: ##### 1.動態提取圖示 提取圖示,需要對目標檔案進行程式碼分析,提取圖示程式碼的相關特徵,然後整理輸出到本地的目標檔案下,過程如下圖: ![圖片描述](http://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/antd-icon-reduce-2.png) 1.) 特徵匹配 利用babel將原始碼編譯輸出,然後得到icon的生成程式碼,結合astexplorer分析節點屬性,確定出匹配方案,這裡還需要注意一點的是,有些元件在生成圖示的過程中比較特殊,比如Button可以通過loading和icon屬性設定,在匹配的時候需要特殊處理; 2.) 屬性提取 在提取圖示的過程中,得到了圖示的屬性後,需要和本地node_modules裡面的@ant-design/icon/lib/dist的所有官方匯出庫匹配,找到目標圖示,就能確定圖示的有效性; 3.) 字串拼接寫入 將圖示名稱和定址路徑拼接起來,再、在寫入antd-icon-reduce.js檔案之前,判斷是否已經存在相同的圖示名稱,如果存在則放棄寫入,經過上述步驟就可以將專案中用到的所有圖示全部收集到antd-icon-reduce.js檔案中了。 ##### 2.動態修改配置 動態修改配置依賴於antd-icon-reduce-plugin外掛來實現,其中的工作原理如下圖: ![圖片描述](http://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/antd-icon-reduce-3.png) 1.) 初始化 在外掛初始化的時候,外掛主要乾了兩件事,第一,生成antd-icon-reduce.js檔案,然後將這個檔案路徑傳遞給antd-icon-reduce-loader,第二,新增專門匹配node_modules/antd目錄下的所有js檔案,確保專案中使用到的元件都能被loader處理,經過初始化之後,專案就有了存放匯出的圖示檔案,更重要的是適配了所有可能生成圖示的原始檔; 2.) 配置檔案 當loader執行完成之後,我們就得到了一份完整的圖示匯出檔案(antd-icon-reduce.js),這個時候就需要修改webpack的alias了,這裡需要在每次loader執行完成後都重新生成一份檔案(內容拷貝至antd-icon-reduce.js檔案),檔名需要動態更新,確保每次webpack記憶體載入的配置檔案都是最新的; 3.) 檔案刪除 在編譯輸出完成後,需要清除antd-icon-reduce.js和另一份配置檔案,這裡都是在webpack相應的hooks裡面實現,具體可以參考外掛原始碼。 #### 3.外掛實現 上面也介紹到,此次涉及到兩個外掛,下面分別簡單講述一下具體的編碼實現: ##### 1.antd-icon-reduce-loader loader的主要實現如下: ``` module.exports = function(source) { parseOptions.call(this); ...... var ast = parser.parse(source, { sourceType: "module", plugins: ['dynamicImport'] }); // 解析原始碼,獲取ast物件 traverse(ast, { CallExpression: function(path) { // 匹配所有呼叫表示式 ...... if (isCreateIcon(Identifier)) { // Icon元件 var iconProps = getIconProps(ObjectExpression.properties); if (Object.keys(iconProps).length > 0) { var type = iconProps.type; var theme = iconProps.theme || 'outline'; if (isArray(type)) { // 三元符情況下,type值不止一個 type.forEach(function(item) { searchIconByName(item, theme); }); } else { searchIconByName(type, theme); } } } else if (isButton(Identifier)) { // Button元件 var btnProps = getBtnProps(ObjectExpression.properties); Object.keys(btnProps).forEach(function(k) { searchIconByName(k === 'loading' ? k : btnProps[k]); }); } }, }); return core.transformFromAstSync(ast).code; }; ``` ##### 2.antd-icon-reduce-plugin ``` AntdIconReducePlugin.prototype.apply = function(compiler) { ...... const rules = compiler.options.module.rules; rules.forEach(function(ruleItem) { ...... if (ruleItem.use[i] === 'ant-icon-reduce-loader') { ruleItem.use[i] = { loader: loaderName, options: { filePath: tempFilePath, // 給loader新增臨時路徑配置 }, }; ...... } ...... }); // 新增專門匹配antd依賴包的loader配置 rules.push({ test: (filePath) => { if (filePath.indexOf(antdModulePath) >= 0 && path.extname(filePath) === '.js') { return true; } return false; }, use: [{ loader: "antd-icon-reduce-loader", options: { filePath: tempFilePath, }, }] }); ...... }; ``` ### 外掛使用 #### 1.安裝依賴項 ``` npm i antd-icon-reduce-loader antd-icon-reduce-plugin -D ``` #### 2.webpack配置 1.新增antd-icon-reduce-loader ``` ...... module: { rules: [ { test: /\.js(x)?$/, exclude: /node_modules/, use: ["antd-icon-reduce-loader", "babel-loader"], } ], }, ...... ``` 2.新增antd-icon-reduce-plugin外掛 ``` ...... var AntdIconReducePlugin = require('antd-icon-reduce-plugin'); ...... plugins: [ ...... new AntdIconReducePlugin({ icons: ['download', { type: 'up', theme: 'outline' }], // 自定義需要加入的圖示,在外掛不能解析原始碼的情況下使用 development: true, // 開發模式下執行外掛,預設true }), ...... ] ``` ### 外掛效果展示 ![圖片描述](http://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/antd-icon-reduce-4.png) 從上圖中可以看到使用了外掛之後,main.js體積減小了差不多500kb左右,效果還是比較明顯(這只是演示外掛效果,沒有做其他輸出優化)。 ### 注意事項 * 外掛只能處理使用字串字面量來定義Icon型別,使用變數或者其他賦值方式將會被忽略,只有如下兩種方式可以被識別: 1.字串字面量直接定義 ```
``` 2.三元符 ``` const isUp = true; ......