Heim > Artikel > Entwicklungswerkzeuge > vscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt
Dieser Artikel beschreibt eine Methode zum intelligenten Entfernen nicht verwendeter Variablen-Plug-Ins in vscode in Kombination mit Babel. Ich hoffe, dass es für alle hilfreich ist!
vscode ist zu einem unverzichtbaren Entwicklungstool für das Frontend geworden. Der Grund, warum vscode von Entwicklern bevorzugt wird, hat meiner Meinung nach viel mit seinem „allmächtigen“ Plug-in-System zu tun. Bei der Arbeit können wir es verwenden, um rein toolartige
Plug-Ins zu entwickeln, und wir können es auch verwenden, um einige funktionale Plug-Ins zu entwickeln, die mit dem Geschäft
des Unternehmens kombiniert werden >. Hier teile ich ein Plug-in, das durch die Kombination von babel
auf intelligente Weise nicht verwendete Variablen
entfernen kann. Ich hoffe, es wird jeden bei der Entwicklung von vscode-Plug-ins inspirieren und unterstützen. [Empfohlenes Lernen: „vscode-Einführungs-Tutorial工具型
的插件,也可以用它开发一些和公司业务相结合
的功能插件。在这里我分享一个通过结合babel
来实现一个能够智能移除未使用的变量
插件,希望对大家开发 vscode 插件有一定的启发和帮助。【推荐学习:《vscode入门教程》】
今天我们首先来熟悉一下 vscode 插件项目的搭建流程
安装脚手架
# 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 窗口,在新窗口中按下Ctrl+Shift+P
输入Hello World
,在窗口右下角会看到一个提示框,说明我们第一个 vscode 插件运行成功运行了。
vscode 插件很多功能都是基于一个个命令实现的,我们可以自定义一些命令,这个命令将出现在按下Ctrl+Shift+P
后的命令列表里面,同时可以给命令配置快捷键、配置资源管理器菜单、编辑器菜单、标题菜单、下拉菜单、右上角图标等。
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
看看我们的插件是否能够正常运行。没错就是这么简单,我们的插件已经能够支持快捷键的形式运行了。
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!"); } );
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
"]
const say = () => { console.log("hello"); };Führen Sie das Gerüst aus
function say() { console.log("hello"); }Wählen Sie eine Vorlage. Da einige Entwickler mit TypeScript nicht vertraut sind, wählen wir
Neue Erweiterung (JavaScript)
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"); } */Dies ist unsere endgültig generierte Verzeichnisstruktur "/>
Versuchen wir zunächst, dieses Plug-in auszuführen
🎜🎜Klicken Sie oben auf die Schaltfläche „Ausführen“ und ein neues vscode-Fenster wird geöffnet. Drücken Sie im neuen FensterStrg+Umschalt+P
und geben Sie Hello World ein
, sehen Sie in der unteren rechten Ecke des Fensters ein Eingabeaufforderungsfeld, das anzeigt, dass unser erstes vscode-Plug-in erfolgreich ausgeführt wurde. 🎜🎜🎜Strg+Umschalt+P
in der Befehlsliste geklickt haben, können Sie auch Tastenkombinationen für Befehle konfigurieren, das Ressourcenmanagermenü, das Editormenü, das Titelmenü, das Dropdown-Menü, das Symbol in der oberen rechten Ecke usw. konfigurieren. 🎜import * as vscode from "vscode"; // 当前打开的文件 const { activeTextEditor } = vscode.window; // 然后通过document下的getText就能轻松获取到我们的代码了 const code = activeTextEditor.document.getText();🎜Die Verwendung von Tastenkombinationen ist die bequemste Methode in der Entwicklung In der Konfiguration können Sie die Plug-in-Unterstützung per Tastenkombination ausführen lassen. 🎜
activeTextEditor.edit((editBuilder) => { editBuilder.replace( // 因为我们要全文件替换,所以我们需要定义一个从头到位的区间 new vscode.Range( new vscode.Position(0, 0), new vscode.Position(activeTextEditor.document.lineCount + 1, 0) ), // 我们的新代码 generate(ast).code ); });🎜Lass es uns noch einmal ausführen und die Tastenkombination
Strg+6
verwenden, um zu sehen, ob unser Plug-in normal ausgeführt werden kann. Ja, so einfach ist das. Unser Plug-in unterstützt bereits die Ausführung als Tastenkombination. 🎜import vue from "vue"; const a = { test1: 1, test2: 2 }; const { test1, test2 } = a; function b() {} let c = () => {}; var d = () => {};🎜weil wir uns in
extension.js befinden <der name des befehls ist in> registriert, daher muss er gleichzeitig geändert werden🎜<pre class="brush:js;toolbar:false;">var a = 1;
var b = 2;
console.log(a);</pre><h3 data-id="heading-6">🎜5 Installieren Sie <code>babel
-bezogene Bibliotheken🎜🎜 🎜Wir können den Code in drei Schritte unterteilen🎜🎜1. Den Code in einen AST-Syntaxbaum analysieren
2. Durchlaufen und ändern Sie den AST-Syntaxbaum
3. Generieren Sie neuen Code basierend auf dem modifizierten AST-Syntaxbaum🎜🎜Babel verfügt über entsprechende Bibliotheken, um diese drei Schritte zu verarbeiten🎜 @babel/parser
AST-Syntaxbaum generieren, Dokumentadresse (https://www.babeljs.cn/docs/babel-parser)🎜🎜@babel/traverse
AST-Syntaxbaum durchqueren, Dokumentadresse (https://www.babeljs.cn/docs/babel-traverse) 🎜🎜@babel/generator
Generieren Sie Code basierend auf dem AST-Syntaxbaum, Dokumentadresse ( https:/ /www.babeljs.cn/docs/babel-generator) 🎜🎜@babel/types
Werkzeugbibliothek, Dokumentadresse (https://www.babeljs.cn/docs /babel- (Typen) 🎜🎜🎜🎜🎜6. Werfen wir einen Blick auf die grundlegende Verwendung dieser Bibliotheken, z. B. die Implementierung einer Code-Implementierung, die eine es6-Pfeilfunktion in eine normale Funktion konvertiert 🎜🎜🎜Vor der Konvertierung🎜var a = 1; console.log(a);🎜Nach der Konvertierung🎜
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);🎜 Der Codeteil ist nur zu Lernzwecken fest codiert🎜rrree🎜Viele Schüler sind sicher neugierig, dass unser Ausdruck jetzt relativ einfach ist. Wenn er komplex ist, wird die Definitionsverschachtelung sehr tiefgreifend und kompliziert sein zu diesem Zeitpunkt? . Tatsächlich können Sie 🎜astexplorer.net/🎜 verwenden. Dies ist eine Website zum Online-Konvertieren von AST. Wir können zwei Fenster öffnen, den Code vor der Konvertierung in das erste Fenster einfügen und die zu konvertierende Schnittstelle in das zweite Fenster einfügen. Zu diesem Zeitpunkt können wir die Unterschiede vor und nach der Konvertierung vergleichen, um unseren Code zu implementieren. 🎜
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 转换网站,复制这些内容进去看看生成的语法树结构
我们先来实现一个例子吧,比如把下面代码中没有用的变量移除掉
转换前
var a = 1; var b = 2; console.log(a);
转换后
var a = 1; console.log(a);
代码实现
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);
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);
因为我们现在实现是针对 js 文件的,所以打开其他类型的文件我们可以让我们的快捷键失效。
我们可以修改package.json
package.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" } ] } }
此处省略... 相信看了上面这些介绍大家已经完全有能力自己整合了
打包我们可以vsce
工具
全局安装 vsce
# npm npm i vsce -g # yarn yarn global add vsce
打包插件
打包前先修改 README.md 文件否则会报错
vsce package
执行完毕之后会生成一个.vsix 文件
如果要在本地 vscode 使用可以直接导入
如果要发布到市场的话,我们需要先注册账号 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 插件开发的基本流程已经有了了解。
觉得文章对你有所帮助,可以点个赞
当然 vscode 插件还有非常多的配置没有介绍,后面如果有时间可以单独整理成一篇文章来介绍
如果在开发过程中有问题或者其他前端技术问题也可以加我微信rjjs1221
交流,或者直接在评论区回复。
源码地址 https://github.com/taoxhsmile/rm-unuse-var
更多关于VSCode的相关知识,请访问:vscode教程!!
Das obige ist der detaillierte Inhalt vonvscode+babel entwickelt ein Plug-in, das ungenutzte Variablen (eigentlicher Kampf) intelligent entfernt. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!