站長資訊網
        最全最豐富的資訊網站

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        本篇文章分享一個在vscode中結合babel開發一個智能移除未使用變量插件的方法,希望對大家有所幫助!

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        vscode 已經成為前端不可缺失的開發工具之一,之所以 vscode 能夠獲得開發者的青睞,我想和它“無所不能”的插件體系有很大一部分關系。在工作中我們能用它來開發純工具型的插件,也可以用它開發一些和公司業務相結合的功能插件。在這里我分享一個通過結合babel來實現一個能夠智能移除未使用的變量插件,希望對大家開發 vscode 插件有一定的啟發和幫助。【推薦學習:《vscode入門教程》】

        正文

        今天我們首先來熟悉一下 vscode 插件項目的搭建流程

        1、使用官方提供的腳手架初始化一個項目

        安裝腳手架

        # npm 形式 npm install -g yo generator-code # yarn 形式 yarn global add yo generator-code

        運行腳手架

        # 運行腳手架 yo code

        選擇模板,考慮到有些開發者對 TypeScript 并不熟悉,所以我們這里選擇 New Extension (JavaScript)

        ? What type of extension do you want to create? New Extension (JavaScript) ? What's the name of your extension? rm-unuse-var ? What's the identifier of your extension? rm-unuse-var ? What's the description of your extension? 移除未使用的變量 ? Enable JavaScript type checking in 'jsconfig.json'? Yes ? Initialize a git repository? Yes ? Which package manager to use? yarn

        這是我們最終生成的目錄結構

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        我們先來運行一下這個插件試試

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        點擊上面運行按鈕,會打開一個新的 vscode 窗口,在新窗口中按下Ctrl+Shift+P輸入Hello World,在窗口右下角會看到一個提示框,說明我們第一個 vscode 插件運行成功運行了。

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        2、自定義命令、快捷鍵、菜單

        vscode 插件很多功能都是基于一個個命令實現的,我們可以自定義一些命令,這個命令將出現在按下Ctrl+Shift+P后的命令列表里面,同時可以給命令配置快捷鍵、配置資源管理器菜單、編輯器菜單、標題菜單、下拉菜單、右上角圖標等。

        3、如何添加命令列表

        package.json 部分配置

        {   // 擴展的激活事件   "activationEvents": ["onCommand:rm-unuse-var.helloWorld"],   // 入口文件   "main": "./extension.js",   // 添加指令   "contributes": {     "commands": [       {         // 這里的值必須和activationEvents里面配置的一樣         "command": "rm-unuse-var.helloWorld",         // 這個就是我們指令的名稱,可以修改這里的值重新運行插件試試看         "title": "Hello World"       }     ]   } }

        在開發中快捷鍵的使用方式是最便捷的,接下來我們修改一下配置,讓插件支持快捷鍵的方式運行。

        {   "contributes": {     "commands": [       {         // 這里的值必須和activationEvents里面配置的一樣         "command": "rm-unuse-var.helloWorld",         // 這個就是我們指令的名稱,可以修改這里的值重新運行插件試試看         "title": "Hello World"       }     ],     // 快捷鍵綁定     "keybindings": [       {         "command": "rm-unuse-var.helloWorld",         "key": "ctrl+6",         "mac": "cmd+6"       }     ]   } }

        我們再重新運行一下,通過快捷鍵Ctrl+6看看我們的插件是否能夠正常運行。沒錯就是這么簡單,我們的插件已經能夠支持快捷鍵的形式運行了。

        4、叫 helloWorld 太土了,下一步我們來修改一下指令的名稱

        package.json

        {   "activationEvents": ["onCommand:rm-unuse-var.rm-js-var"],   "main": "./extension.js",   "contributes": {     "commands": [       {         "command": "rm-unuse-var.rm-js-var",         "title": "Hello World"       }     ],     "keybindings": [       {         "command": "rm-unuse-var.rm-js-var",         "key": "ctrl+6",         "mac": "cmd+6"       }     ]   } }

        因為我們在extension.js中注冊了指令的名稱,所以也要同步修改

        let disposable = vscode.commands.registerCommand(   "rm-unuse-var.rm-js-var",   function () {     vscode.window.showInformationMessage("Hello World from rm-unuse-var!");   } );

        5、安裝babel相關庫

        我們修改代碼可以分為 3 個步驟

        1、將代碼解析成 AST 語法樹 2、遍歷修改 AST 語法樹 3、根據修改過的 AST 語法樹生成新的代碼

        這 3 個步驟 babel 都有對應的庫來處理

        • @babel/parser生成 AST 語法樹,文檔地址(https://www.babeljs.cn/docs/babel-parser)

        • @babel/traverse遍歷 AST 語法樹,文檔地址(https://www.babeljs.cn/docs/babel-traverse)

        • @babel/generator根據 AST 語法樹生成代碼,文檔地址(https://www.babeljs.cn/docs/babel-generator)

        • @babel/types工具庫,文檔地址(https://www.babeljs.cn/docs/babel-types)

        6、我們來看下這些庫的基本用法,比如實現一個將 es6 的箭頭函數轉換成普通函數

        轉換前

        const say = () => {   console.log("hello"); };

        轉換后

        function say() {   console.log("hello"); }

        代碼實現,代碼部分寫死僅供學習參考

        const t = require("@babel/types"); const parser = require("@babel/parser"); const traverse = require("@babel/traverse").default; const generate = require("@babel/generator").default; // 1、將代碼解析成 AST 語法樹 const ast = parser.parse(`const say = () => {   console.log("hello"); };`); // 2、遍歷修改 AST 語法樹 traverse(ast, {   VariableDeclaration(path) {     const { node } = path;     // 寫死找到第一個申明     const declaration = node.declarations[0];     // 定義的內容     const init = declaration.init;     // 判斷是否是箭頭函數     if (t.isArrowFunctionExpression(init)) {       // 將原來的表達式替換成新生成的函數       path.replaceWith(         t.functionDeclaration(           declaration.id,           init.params,           init.body,           init.generator,           init.async         )       );     }   }, }); // 3、根據修改過的 AST 語法樹生成新的代碼 console.log(generate(ast).code); /* function say() {   console.log("hello"); } */

        很多同學肯定好奇現在我們的表達式比較簡單還好,如果復雜的話定義嵌套會非常深和復雜,這個時候應該怎么知道去替換哪個節點?。其實這里可以借助astexplorer.net/ 這是一個在線轉換 AST 的網站。我們可以打開兩個窗口,把轉換前的代碼放到第一個窗口,把需要轉換的接口放到第二個窗口。這個時候我們就可以對比一下轉換前后的差異,來實現我們的代碼了。

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        6、思考插件如何實現?

        1、獲取編輯器當前打開的 js 文件的代碼 2、將代碼解析成 AST 語法樹 3、遍歷 AST 語法樹,刪除未使用的定義 4、根據修改過的 AST 語法樹生成新的代碼 5、替換當前 js 文件的代碼

        其中 2、4 我們已經會了,接下來只需要看下 1、3、5 如何實現就行

        1 和 5 我們可以通過 vscode 提供的方法

        1、獲取編輯器當前打開的 js 文件的代碼

        import * as vscode from "vscode"; // 當前打開的文件 const { activeTextEditor } = vscode.window; // 然后通過document下的getText就能輕松獲取到我們的代碼了 const code = activeTextEditor.document.getText();

        5、替換當前 js 文件的代碼

        activeTextEditor.edit((editBuilder) => {   editBuilder.replace(     // 因為我們要全文件替換,所以我們需要定義一個從頭到位的區間     new vscode.Range(       new vscode.Position(0, 0),       new vscode.Position(activeTextEditor.document.lineCount + 1, 0)     ),     // 我們的新代碼     generate(ast).code   ); });

        好了接下來我們就剩核心的第 3 步了。

        3、遍歷 AST 語法樹,刪除未使用的定義

        我們先來分析一下,未使用的定義包含了哪些?

        import vue from "vue"; const a = { test1: 1, test2: 2 }; const { test1, test2 } = a; function b() {} let c = () => {}; var d = () => {};

        然后在線 ast 轉換網站,復制這些內容進去看看生成的語法樹結構

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        我們先來實現一個例子吧,比如把下面代碼中沒有用的變量移除掉

        轉換前

        var a = 1; var b = 2; console.log(a);

        轉換后

        var a = 1; console.log(a);
        • scope.getBinding(name) 獲取當前所有綁定
        • scope.getBinding(name).referenced 綁定是否被引用
        • scope.getBinding(name).constantViolations 獲取當前所有綁定修改
        • scope.getBinding(name).referencePaths 獲取當前所有綁定路徑

        代碼實現

        const t = require("@babel/types"); const parser = require("@babel/parser"); const traverse = require("@babel/traverse").default; const generate = require("@babel/generator").default;  const ast = parser.parse(`var a = 1; var b = 2; console.log(a);`);  traverse(ast, {   VariableDeclaration(path) {     const { node } = path;     const { declarations } = node;     // 此處便利可以處理 const a = 1,b = 2; 這種場景     node.declarations = declarations.filter((declaration) => {       const { id } = declaration;       // const { b, c } = a;       if (t.isObjectPattern(id)) {         // path.scope.getBinding(name).referenced 判斷變量是否被引用         // 通過filter移除掉沒有使用的變量         id.properties = id.properties.filter((property) => {           const binding = path.scope.getBinding(property.key.name);           return !!binding?.referenced;         });         // 如果對象中所有變量都沒有被應用,則該對象整個移除         return id.properties.length > 0;       } else {         // const a = 1;         const binding = path.scope.getBinding(id.name);         return !!binding?.referenced;       }     });     // 如果整個定義語句都沒有被引用則整個移除     if (node.declarations.length === 0) {       path.remove();     }   }, }); console.log(generate(ast).code);

        7、了解基本處理流程之后,我們就來看下最終的代碼實現吧

        const t = require("@babel/types"); const parser = require("@babel/parser"); const traverse = require("@babel/traverse").default; const generate = require("@babel/generator").default;  const ast = parser.parse(   `import vue from 'vue';   var a = 1; var b = 2; var { test1, test2 } = { test1: 1, test2: 2 }; function c(){} function d(){} d(); console.log(a, test1);`,   {     sourceType: "module",   } );  traverse(ast, {   // 處理 const var let   VariableDeclaration(path) {     const { node } = path;     const { declarations } = node;      node.declarations = declarations.filter((declaration) => {       const { id } = declaration;       if (t.isObjectPattern(id)) {         id.properties = id.properties.filter((property) => {           const binding = path.scope.getBinding(property.key.name);           return !!binding?.referenced;         });         return id.properties.length > 0;       } else {         const binding = path.scope.getBinding(id.name);         return !!binding?.referenced;       }     });      if (node.declarations.length === 0) {       path.remove();     }   },   // 處理 import   ImportDeclaration(path) {     const { node } = path;     const { specifiers } = node;     if (!specifiers.length) {       return;     }     node.specifiers = specifiers.filter((specifier) => {       const { local } = specifier;       const binding = path.scope.getBinding(local.name);       return !!binding?.referenced;     });     if (node.specifiers.length === 0) {       path.remove();     }   },   // 處理 function   FunctionDeclaration(path) {     const { node } = path;     const { id } = node;     const binding = path.scope.getBinding(id.name);     if (!binding?.referenced) {       path.remove();     }   }, }); console.log(generate(ast).code);

        8、vscode 設置我們的插件只支持 js 文件的限制

        因為我們現在實現是針對 js 文件的,所以打開其他類型的文件我們可以讓我們的快捷鍵失效。 我們可以修改package.jsonpackage.json

        {   "contributes": {     "commands": [       {         "command": "rm-unuse-var.remove",         "title": "Hello World"       }     ],     "keybindings": [       {         "command": "rm-unuse-var.remove",         "key": "ctrl+6",         "mac": "cmd+6",         "when": "resourceLangId == javascript"       }     ]   } }

        9、整合到我們前面創建的項目中去

        此處省略… 相信看了上面這些介紹大家已經完全有能力自己整合了

        10、打包發布插件

        打包我們可以vsce工具

        全局安裝 vsce

        # npm npm i vsce -g # yarn yarn global add vsce

        打包插件

        打包前先修改 README.md 文件否則會報錯

        vsce package

        執行完畢之后會生成一個.vsix 文件

        如果要在本地 vscode 使用可以直接導入

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        如果要發布到市場的話,我們需要先注冊賬號 https://code.visualstudio.com/api/working-with-extensions/publishing-extension#publishing-extensions

        # 登錄賬號 vsce login your-publisher-name # 發布 vsce publish

        發布成功之后就能在我們的市場上看到了 marketplace.visualstudio.com/items?itemN… 也可以在 vscode 中搜索打我們的插件

        vscode+babel開發一個智能移除未使用變量的插件(實戰)

        總結

        到此為止,相信大家對 vscode 插件開發的基本流程已經有了了解。

        覺得文章對你有所幫助,可以點個贊

        當然 vscode 插件還有非常多的配置沒有介紹,后面如果有時間可以單獨整理成一篇文章來介紹

        如果在開發過程中有問題或者其他前端技術問題也可以加我微信rjjs1221交流,或者直接在評論區回復。

        源碼地址 https://github.com/taoxhsmile/rm-unuse-var

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 精品一区二区久久| 国产成人久久精品一区二区三区 | 99久久99久久精品国产片| 最新国产成人精品2024| 精品人妻少妇一区二区| 午夜精品在线观看| 国产亚洲精品xxx| 99久久精品日本一区二区免费| 久久亚洲美女精品国产精品| 中文字幕无码精品三级在线电影| 久久精品中文字幕有码| 国产精品视频九九九| 一区二区三区精品| 2020最新久久久视精品爱| 国产精品特级毛片一区二区三区| 精品少妇无码AV无码专区| 亚洲人成精品久久久久| 日本一区二区三区精品国产| 日韩精品一区二区三区中文字幕| 免费看一级毛片在线观看精品视频 | 久久夜色精品国产噜噜噜亚洲AV| 久久精品国产一区二区| 国内精品久久久久久久影视麻豆 | 99re国产精品视频首页| 精品国产三级a∨在线欧美| 精品无人区一区二区三区| 国产女人精品视频国产灰线| 精品欧洲av无码一区二区| 久久99精品国产自在现线小黄鸭 | 亚洲精品在线观看视频| 中文字幕成人精品久久不卡| 911亚洲精品不卡| 99久久免费只有精品国产| 国产精品va久久久久久久| 国产欧美精品区一区二区三区| 国产精品无打码在线播放| 国产精品久久久久一区二区三区| 国产亚洲精品拍拍拍拍拍| 日韩精品成人亚洲专区| 亚洲精品人成无码中文毛片| 亚洲国产精品无码久久久不卡|