背景:承受二手项目会开发周期较长得项目,会忘记自己引证了哪些iconfont的icon内容。了解引入的icon需求到iconfont官网登陆查看,不同开发者可能还不知道对方引证了哪些icon。新增iconfont时,又会影响到原有的icon内容。
问题: 1.iconfont.js是压缩后的代码,不够直观化,运用icon的名字进程太过繁琐,乃至还无从查找。
对以上问题思考后,决定处理以上脚本比较好的方式就是开发一个解析iconfont.js的vscode插件,便能够直接在项目中运用。
效果:
初始化一个项目
初始化一个名为IconView
的插件
// 安装需求的包
npm install yo generator-code -D
// 运转,然后按流程走一遍,能够生成初始模板的插件
npx yo code
开发进程中,按F5
进行调试。
可视化进程
新建一个webview
vscode插件机制提供了一个可翻开一个自界说页面的功用,即webview,这儿选用在webview中展现解析出来的icon。
在extension.ts(vscode 插件的进口文件)
文件中
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "IconView" is now active!');
// 追寻当时 webview 面板
let currentPanel: vscode.WebviewPanel | undefined | any = undefined;
// 创立翻开IconView的指令
const openIconViewCommand = vscode.commands.registerCommand(
"IconView.openIconView",
async (uri: vscode.Uri) => {
// 获取当时活动的编辑器
const columnToShowIn = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;
if (currentPanel) {
// 如果我们已经有了一个面板,那就把它显现到方针列布局中
if (columnToShowIn)
currentPanel.reveal(columnToShowIn === 2 ? 1 : 2);
} else {
// 不然,创立一个新面板
vscode.window.showInformationMessage("IconView.openIconView");
currentPanel = await openIconView(context, uri, columnToShowIn === 2 ? 1 : 2);
// 当时面板被关闭后重置
currentPanel.onDidDispose(
() => {
currentPanel = undefined;
},
null,
context.subscriptions
);
}
}
);
context.subscriptions.push(openIconViewCommand);
}
这儿注册了一个IconView.openIconView
的指令,指令首要是获取webview显现的面板方位。将指令丢进监听池。具体新建和翻开webview的操作在openIconView中。
为了方便运用,需求将openIconView
注册到翻开文件的右键菜单,需求在package.json中装备相关参数,注册指令和装备菜单项,即可在翻开的iconfont.js文件右键翻开webview
// 插件激活事情
"activationEvents": [
"onCommand:IconView.openIconView",
],
// 功用装备点
"contributes": {
"commands": [
{
"command": "IconView.openIconView",
"title": "IconView Open IconView"
}
],
"menus": {
"editor/context": [
{
"command": "IconView.openIconView",
"group": "navigation",
"when": "editorFocus"
}
]
},
"configuration": {
"title": "IconView"
}
},
openIconView的代码
export async function openIconView(context: vscode.ExtensionContext, uri: vscode.Uri, viewColumn: vscode.ViewColumn) {
// 当时文件绝对途径
const currentFile = uri.fsPath;
if (projectPath) {
// 创立webview
const panel: vscode.WebviewPanel = vscode.window.createWebviewPanel(
'IconView', // viewType
"IconView", // 视图标题
viewColumn, // 显现在编辑器的哪个部位
{
enableScripts: true, // 启用JS,默许禁用
retainContextWhenHidden: true, // webview被隐藏时保持状况,避免被重置
}
);
panel.webview.html = await getWebViewContent(
context,
uri,
'./iconView/index.html',
{
beforeBodyScripts: [currentFile]
}
);
// 监听音讯
let global = { currentFile, panel, uri };
panel.webview.onDidReceiveMessage(message => {
const [_, cmdName] = message.cmd.split(':')
if (messageHandler[cmdName]) {
// cmd表明要履行的办法称号
messageHandler[cmdName](global, message);
} else {
vscode.window.showErrorMessage(`未找到名为 ${cmdName} 的办法!`);
}
}, undefined, context.subscriptions);
return panel;
}
}
/**
* 寄存一切音讯回调函数,根据 message.cmd 来决定调用哪个办法,
*/
var messageHandler: any = {
// 弹出提示
alert(global: any, message: any) {
vscode.window.showInformationMessage(message.info);
},
// 显现过错提示
error(global: any, message: any) {
vscode.window.showErrorMessage(message.info);
},
/** 将内容写入剪贴板 */
copy(global: any, message: any) {
vscode.env.clipboard.writeText(message.content);
invokeCallback(global.panel, message, { success: true, msg: '内容已仿制,可直接运用!' });
},
// 获取urls信息
async getUrlsInfo(global: any, message: any) {
const data = {
targetPath: global.uri.fsPath,
}
invokeCallback(global.panel, message, data);
},
}
function invokeCallback(panel: any, message: any, resp: any) {
// 过错码在400-600之间的,默许弹出过错提示
if (typeof resp == 'object' && resp.code && resp.code >= 400 && resp.code < 600) {
vscode.window.showErrorMessage(resp.message || '发生不知道过错!');
}
panel.webview.postMessage({ cmd: 'vscodeCallback', cbid: message.cbid, data: resp });
}
openIconView中运用vscode.window.createWebviewPanel新建了一个webviewPanel,模板途径是./iconView/index.html
,panel需求加载html字符串内容,然后panel开启音讯监听并履行相应事情。
messageHandler是信息通道的处理器,首要处理来自webView传递的事情,这儿界说了几个根本事情处理函数。
invokeCallback是处理音讯监听的回调函数,在具体的操作事情中调用。
webview首要经过音讯通道的机制来履行vscode的插件能力,包括文件的读取修正,仿制等操作。
getWebViewContent
代码
export async function getWebViewContent(context: any, uri: vscode.Uri, templatePath: any, config: any = {}) {
const projectPath = await getProjectPath(uri);
const resourcePath = path.join(context.extensionPath, templatePath);
const dirPath = path.dirname(resourcePath);
let html = fs.readFileSync(resourcePath, 'utf-8');
// 增加前置script 用户从项目本地刺进脚本
let beforeBodyScriptStr = '';
if (config?.beforeBodyScripts && Array.isArray(config?.beforeBodyScripts)) {
console.log('beforeBodyScripts',config?.beforeBodyScripts)
beforeBodyScriptStr = config?.beforeBodyScripts.map((src: string) => {
return `<script src="https://juejin.im/post/7202526307329409081/${src}"></script>`
}).join('\n');
}
html = html.replace('{@beforeBodyScript}',beforeBodyScriptStr)
// vscode不支持直接加载本地资源,需求替换成其专有途径格式,这儿只是简略的将款式和JS的途径替换
html = html.replace(/(<link.+?href=["']|<script.+?src=["']|<img.+?src=["'])([^@].+?)["']/g, (m, $1, $2) => {
return $1 + vscode.Uri.file(path.resolve(dirPath, $2)).with({ scheme: 'vscode-resource' }).toString() + $1[$1.length - 1];
});
html = html.replace(/(<link.+?href=["']@|<script.+?src=["']@|<img.+?src=["']@)(.+?)["']/g, (m, $1, $2) => {
return $1.substring(0, $1.length - 1) + vscode.Uri.file(path.resolve(projectPath, $2)).with({ scheme: 'vscode-resource' }).toString() + $1[$1.length - 2];
});
return html;
}
getWebViewContent除了读取界说的html模板之外,还将html中的引证途径改为vscode插件的资源途径,不然不能拜访。
增加前置script 用户从项目本地刺进脚本部分用于注入iconfont的js文件途径,这儿经过html引入script的方式,能够统一来自iconfont,iconPark的iconjs文件。然后webview中可经过use来展现。
这儿选用html途径的方式来开发,而不是直接选用字符串模板,是html有格式化,结构清晰。留意的是iconView一定是要榜首级,因为打包后的代码中是没有src的,所以放到src中无法辨认。
展现icon列表
在html引入了vue@2.x,便于html开发。下载vue.js的离线版本,这儿放在了在index.html中引入vue.js,以及相关初始化脚本index.js
var app = new Vue({
el: '#root',
data: {
searchIcon: '',
icons: [],
showIcons: [],
},
mounted: function () {
// 调用vscode,经过音讯机制
callVscode({ cmd: 'vscode:getUrlsInfo' }, (data) => {
if (data) {
this.targetPath = data.targetPath;
}
});
let icons = [];
if (document) {
// 等待页面渲染完成,延迟获取symbol标签
setTimeout(() => {
const iconElements = document.querySelectorAll('symbol');
Array.from(iconElements).forEach((ele) => {
icons.push({
id: `#${ele.id}`,
name: ele.id
})
})
this.icons = icons;
this.showIcons = icons.slice();
}, 1000)
}
},
methods: {
/** icon查找 */
onSearch: function (e) {
const value = e.target.value;
if (value) {
this.showIcons = this.icons.filter((v) => {
return v.name.indexOf(value) > -1;
})
} else {
this.showIcons = this.icons.slice();
}
},
onClickIcon: function ({ item }) {
const temp = `<IconFont type="https://juejin.im/post/7202526307329409081/${item.id.substring(1)}"/>`;
callVscode({ cmd: 'vscode:copy', content: temp }, (data) => {
if (data.success) {
createMsg(data.msg, 'success')
}
});
},
},
})
新建一个vue实例,挂载在html中,然后在mounted钩子中延迟获取到注入html中的symbol标签,一般来说一个symbol代表一个icon,这儿拿到之后,提取symbol的id,以便后边的运用,这儿区别两个icon列表,用于查找展现的全量副本和展现副本。
一起新增onSearch,onClickIcon办法,用于查找和点击仿制模板。
icon是个列表,为了方便操作,新建一个vue的component
Vue.component('icon-item', {
props: {
id: {
type: String,
default: '',
},
name: {
type: String,
default: '',
}
},
data() {
return { preview: false };
},
methods: {},
template: `
<div class="--ch-icon-item">
<div class="--ch-icon-item-icon">
<svg>
<use v-bind:xlink:href="id"></use>
</svg>
</div>
<span class="--ch-icon-name" :title="name">{{name}}</span>
</div>
`
})
icon用于展现icon列表
接下来看看html内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<link rel="stylesheet" type="text/css" href="index.css" />
</head>
<script src="./other/vue.js"></script>
{@beforeBodyScript}
<body>
<div id="root">
<div class="--ch-target-path">
<span>文件途径:</span>
<span>{{targetPath}}</span>
</div>
<input v-model="searchIcon" class="--ch-search" v-on:input="onSearch($event)" placeholder="请输入icon名字"></input>
<div class="--ch-icon-list">
<div
v-for="item in showIcons"
:key="item.id"
class="--ch-icon-item-wrapper"
v-on:click="onClickIcon({item})"
>
<icon-item
v-bind:id="item.id"
v-bind:name="item.name"
></icon-item>
</div>
</div>
</div>
<script src="./communication.js"></script>
<script src="./utils.js"></script>
<!-- 组件部分 -->
<script src="./components/iconItem.js"></script>
<script src="./index.js"></script>
</body>
</html>
html中简略界说了查找,文件途径,icon列表展现。
展现出来之后,接下来要完成的是点击icon可仿制模板代码到剪贴板中。iconItem的容器组件绑定了点击事情,点击事情中可获取到item内容,这儿界说了<IconFont type="${item.id.substring(1)}"/>
(结合antd的IconFont组件运用),然后调用了vscode的仿制功用。
callVsCode的代码
var callbacks = {}; // 寄存一切的回调函数
var vscode = window.acquireVsCodeApi ? window.acquireVsCodeApi() : {
postMessage(data) {
console.log(data)
}
};
function callVscode(data, cb) {
if (typeof data === 'string') {
data = { cmd: data };
}
if (cb) {
// 时刻戳加上5位随机数
const cbid = Date.now() + '' + Math.round(Math.random() * 100000);
// 将回调函数分配一个随机cbid然后存起来,后续需求履行的时候再捞起来
callbacks[cbid] = cb;
data.cbid = cbid;
}
vscode.postMessage(data);
}
window.addEventListener('message', event => {
const message = event.data;
switch (message.cmd) {
// 来自vscode的回调
case 'vscodeCallback':
console.log(message.data);
(callbacks[message.cbid] || function () { })(message.data);
delete callbacks[message.cbid]; // 履行完回调删去
break;
default: break;
}
});
打包运用
因为没有发布到插件市场,这次运用打包成vsix插件,经过本地安装。
// 安装打包东西vsce
npm i vsce -g
// 打包成vsix文件
vsce package
下一篇:IconView——在项目中保护confont.js
注: 以上部分代码来自于网络。
参考资料:
blog.haoji.me/vscode-plug…