前言

对于前端同学来说,我们会经常用到各种小工具,比如:图床、颜色拾取、二维码生成器、url 管理、文本比对、json 格式化。当然我们可以 chrome 收藏夹来管理各种在线的小工具,但作为一个有追求的前端,我们不仅仅要自己用的爽,也可以将一些好用的工具给团队用,提高团队的研发效率。

所以基于以上诉求,市面上或者很多公司内部都会做一些满足自己团队需要的客户端工具,大多是基于 electron 来实现。但如果本篇文章只介绍如何通过 electron 来做一个工具集,那就小了,格局小了! 要做就做大的,我们不仅仅为前端赋能,我们更需要为后端、测试、UI、产品甚至老板提效赋能。所以一旦做大了,工具箱将会变得非常臃肿,体积也会越来越大。每次更新所有人都必须升级才能体验新功能。已于以上问题,我们需要设计一款可插拔式的设计方式,用到时才安装,用不到不需要进行安装使用。插件独立于工具箱之外单独发布。

如果你对这样一款工具也感兴趣,可以继续阅读实现过程。不过可以直接上源码先:

Rubick 源码

取名

开篇第一步,按照我之前的套路都是先取好名字先占个坑,之前写了一本《从0开始可视化搭建》的小册,里面基于 dota 取了个 coco 的名字。这次我取名的是 rubick拉比克。知道 rubick 技能的就能领会啥意思了,主要是可以随心所欲想用啥技能就用啥技能,可插拔。和我们的理念也非常一致:

实现

初始化项目

这里我采用的是 electron-vue 来做的项目脚手架,直接按照官网介绍,开始 create 好一个 electron 项目:

# 安装 vue-cli 和 脚手架样板代码
npm install -g vue-cli
vue init simulatedgreg/electron-vue rubick # 安装依赖并运行你的程序
cd rubick
yarn # 或者 npm install
yarn run dev # 或者 npm run dev

这里需要注意的是,由于 electron-vue 这玩意使用的 electron 版本太低了,而官方的 electron 文档已经更新到最新的了,所以如果仅仅按照 electron-vue 的版本来开发,再参考官方文档,会发现有很多文档中有但是无法使用的情况。所以尽量升级到最新的版本。

如果你是windows用户,在安装期间遇到了关于node-gyp、C++库等方面的问题的话,请参考官方文档给出的解决办法

main 进程 和 renderer 进程

开发之前,有必要先了解一下 electronmain 进程 和 renderer 进程之间的关系,什么是 main 进程:electron 项目启动的时候会运行 main.js 的进程就是主进程,且一个项目有且只有一个主进程,创建窗口等所有系统事件都要在主进程中进行。简单的说就是我们的 electron 项目的主进程只有一个, 主进程的执行代码需要写到 main.js 中, 所有跟系统事件相关的代码统统都要写在这里。

什么是 renderer 进程呢?最粗浅的理解就是 Renderer 进程负责的就是我们熟悉的页面UI渲染。这里其实有篇博客总结的很好,想继续了解的可以查看 这里我先引用一下这里面的图。这张图包含了main 进程和 renderer 进程所具备的能力。

有了这些基础知识,假设你对 electron 相关的了解已经达到一个基本熟悉的层次。我们开始来进行开发工作。接下来的开发,我将会参考 utools 的设计交互,来一步步设计出一个类似于 utools 的 electron 工具箱。

窗口初始化

electron 可以通过 BrowserWindow 来新建一个窗口对象,这个时候需要构建一个 800 * 60 的主搜索窗口,打开 main.js 调整窗口尺寸和大小:

mainWindow = new BrowserWindow({
height: 60,
useContentSize: true,
width: 800,
frame: false,
title: '拉比克',
webPreferences: {
webSecurity: false,
enableRemoteModule: true,
webviewTag: true,
nodeIntegration: true
}
})

这里有几个 webPreferences 参数需要说明一下:

  • webSecurity 如果 BrowserWindow 发起了一些跨域请求,webSecurity 可以设置成 false
  • enableRemoteModule 可以让 renderer 进程使用 remote 模块
  • webviewTag 允许使用 webview 标签
  • nodeIntegration 允许在网页中使用 node

到这里,我们就可以看到一个最基础的交互窗口了!

开发者模式

插件开发需要和 rubick 进行联调,所以 rubick 需要支持开发者模式,帮助开发者更好的开发插件。首先我们先建一个 plugin.json 用于描述插件的基础信息:

{
"pluginName": "测试插件",
"author": "muwoo",
"description": "我的第一个 rubick 插件",
"main": "index.html",
"version": "0.0.2",
"logo": "logo.png",
"name": "rubick-plugin-demo",
"gitUrl": "",
"features": [
{
"code": "hello",
"explain": "这是一个测试的插件",
"cmds":["hello222", "你好"]
}
],
"preload": "preload.js"
}
核心字段说明
  • name 插件仓库名称,用于 github dowload 标致
  • pluginName : 插件名称。
  • description : 插件描述,简洁的说明这个插件的作用
  • main : 入口文件,如果没有定义入口文件,此插件将变成一个模版插件
  • version : 插件的版本,需要符合 Semver (语义化版本) 规范。用于版本更新提示
  • features : 插件核心功能列表
  • features.code : 插件某个功能的识别码,在进入插件时会传递给你的代码,可用于区分不同的功能,显示不同的 UI
  • features.cmds : 通过哪些方式可以进入这个功能,中文会自动支持 拼音及拼音首字母,无须重复添加

开发插件的方式是复制 plugin.json 进入到 rubick 的搜索框,所以需要监听搜索框的change 事件,用于读取当前剪切板复制的内容:

onSearch ({ commit }, paylpad) {
// 获取剪切板复制的文件路径
const fileUrl = clipboard.read('public.file-url').replace('file://', ''); // 如果是复制 plugin.json 文件
if (fileUrl && value === 'plugin.json') {
// 读取 json 文件
const config = JSON.parse(fs.readFileSync(fileUrl, 'utf-8'));
// 生成插件配置
const pluginConfig = {
...config,
// 记录 index.html 存方的路径
sourceFile: path.join(fileUrl, `../${config.main || 'index.html'}`),
id: uuidv4(),
// 标记为开发者
type: 'dev',
// 读取 icon
icon: 'image://' + path.join(fileUrl, `../${config.logo}`),
// 标记是否是模板
subType: (() => {
if (config.main) {
return ''
}
return 'template';
})()
};
}
}

到这里我们已经可以根据复制的 plugin.json 能获取到插件的最基础的信息,接下来就是需要展示搜索框:

 commit('commonUpdate', {
options: [
{
name: '新建rubick开发插件',
value: 'new-plugin',
icon: 'https://static.91jkys.com/activity/img/b37ff555c748489f88f3adac15b76f18.png',
desc: '新建rubick开发插件',
click: (router) => {
commit('commonUpdate', {
showMain: true,
selected: {
key: 'plugin',
name: '新建rubick开发插件'
},
current: ['dev'],
});
ipcRenderer.send('changeWindowSize-rubick', {
height: getWindowHeight(),
});
router.push('/home/dev')
}
},
{
name: '复制路径',
desc: '复制路径',
value: 'copy-path',
icon: 'https://static.91jkys.com/activity/img/ac0d4df0247345b9a84c8cd7ea3dd696.png',
click: () => {
clipboard.writeText(fileUrl);
commit('commonUpdate', {
showMain: false,
selected: null,
options: [],
});
ipcRenderer.send('changeWindowSize-rubick', {
height: getWindowHeight([]),
});
remote.Notification('Rubick 通知', { body: '复制成功' });
}
}
]
});

到这里,当复制 plugin.json 进入搜索框时,变可直接出现 2个选项,一个新建插件,一个复制路径的功能:

当点击新建 rubick 插件 功能时,则需要跳转到 home 页,加载插件的基础类容,唯一需要注意的是 home 页加载的内容高度应该是rubick最大窗口的高度。所以需要调整窗口大小:

 ipcRenderer.send('changeWindowSize-rubick', {
height: getWindowHeight(),
});

关于 renderer 里面的 vue 代码这里就不再详细介绍了,因为大多是 css 画一下就好了,直接来看展示界面:

到这里,就完成了开发者模式,接下来再介绍如何让插件在 rubick 中跑起来。

运行插件

运行插件需要容器,electron 提供了一个 webview 的容器来加载外部网页。所以我们可以借助 webview 的能力实现动态网页渲染,这里所谓的网页就是插件。但是网页无法使用node的能力,而且我们做插件的目的就是为了开放与约束,需要对插件开放一些内置的 API 能力。好在 webview 提供了一个 preload 的能力,可以在页面加载的时候去预置一个脚本来执行。

也就是说我们可以给自己的插件写一个 preload.js 来加载。但这里需要注意我们既要保持插件的个性又得向插件内注入全局 API 供插件使用,所以可以直接加载 rubick 内置 preload.jspreload.js 内再加载个性化的 preload.js:

// webview plugin.vue
<webview id="webview" :src="path" :preload="preload"></webview>
<script>
export default {
name: "index.vue",
data() {
return {
path: `File://${this.$route.query.sourceFile}`,
// 加载当前 static 目录中的 preload.js
preload: `File://${path.join(__static, './preload.js')}`,
webview: null,
query: this.$route.query,
config: {},
}
}
}
</script>

对于 preload.js 我们就可以这么用啦:

if (location.href.indexOf('targetFile') > -1) {
filePath = decodeURIComponent(getQueryVariable('targetFile'));
} else {
filePath = location.pathname.replace('file://', '');
} window.utools = {
// utools 所有的 api 实现
}
// 加载插件 preload.js
require(path.join(filePath, '../preload.js'));

到这里就已经实现了插件的加载,我们来看看效果:

结语

本篇主要介绍如何实现一个类似于 utools 的插件加载过程,当然这远远不是 utools 的全部,下期我们再介绍如何实现 utools 的全局插件,比如屏幕取色、截图、搜索等能力。欢迎大家前往体验 Rubick 有问题可以随时提 issue 我们会及时反馈。

另外,如果觉得设计实现思路对你有用,也欢迎给个 Star:https://github.com/clouDr-f2e/rubick

手把手教你实现一个支持插件化的 uTools 工具箱(一)的更多相关文章

  1. PWA入门:手把手教你制作一个PWA应用

    摘要: PWA图文教程 原文:PWA入门:手把手教你制作一个PWA应用 作者:MudOnTire Fundebug经授权转载,版权归原作者所有. 简介 Web前端的同学是否想过学习app开发,以弥补自 ...

  2. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

  3. iOS回顾笔记(05) -- 手把手教你封装一个广告轮播图框架

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  4. R数据分析:跟随top期刊手把手教你做一个临床预测模型

    临床预测模型也是大家比较感兴趣的,今天就带着大家看一篇临床预测模型的文章,并且用一个例子给大家过一遍做法. 这篇文章来自护理领域顶级期刊的文章,文章名在下面 Ballesta-Castillejos ...

  5. 教你打造一个Android组件化开发框架

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 CC:Component Caller,一个android组件化开发框架, 已开源,github地址:https://github ...

  6. Vue+ElementUI: 手把手教你做一个audio组件

    目的 本项目的目的是教你如何实现一个简单的音乐播放器(这并不难) 本项目并不是一个可以用于生产环境的element播放器,所以并没有考虑太多的兼容性问题 本项目不是ElementUI的一个音频插件,只 ...

  7. 如何写一个c++插件化系统

    1.为什么需要插件化系统 “编程就是构建一个一个自己的小积木, 然后用自己的小积木搭建大系统”. 但是程序还是会比积木要复杂, 我们的系统必须要保证小积木能搭建出大的系统(必须能被组合),有必须能使各 ...

  8. 用Python手把手教你搭一个Transformer!

    来源商业新知网,原标题:百闻不如一码!手把手教你用Python搭一个Transformer 与基于RNN的方法相比,Transformer 不需要循环,主要是由Attention 机制组成,因而可以充 ...

  9. 手把手教你画一个 逼格满满圆形水波纹loadingview Android

    才没有完结呢o( ̄︶ ̄)n .大家好,这里是番外篇. 拜读了爱哥的博客,又学到不少东西.爱哥曾经说过: 要站在巨人的丁丁上. 那么今天,我们就站在爱哥的丁丁上来学习制作一款自定义view(开个玩笑,爱 ...

随机推荐

  1. RabbitMQ高级特性

    消息的可靠投递 在使用Rabbitmq的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景.Rabbitmq为我们提供了两种方式用来控制消息的投递可靠性模式 confirm确认模式 return ...

  2. Go快速入门(二)

    提示:本系列文章适合有其他语音基础并对Go有持续冲动的读者 一.package介绍 ​ Go语言的代码是通过package来组织的,package的概念和你知道的其它语言 里的libraries或者m ...

  3. (五)Jira Api对接:修改任务状态

    项目迭代结束后我们需要把sprint下面的story.task任务状态修改到结束状态,如果手动修改会花费不少时间,本文就介绍如何通过jira api自动修改任务状态,提高工作效率. 一.查看任务工作流 ...

  4. 建站第二步:简单配置Nginx反代理工具

    简单配置Nginx反代理工具 你要用你的域名能和服务器绑定就要用一些反代理工具 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,极其优异的服务器软件,底层为C 来自某些 ...

  5. VBA绘制Excel图表

    VBA调试运行进入: 几个例子: 删除工作表内所有图表 录制一个宏 简化宏再使用 大量图表可采用for循环 CSDN上用积分下载的一个例子 VBA数组 VBA调试运行进入: 右键Excel的Sheet ...

  6. python基础之centos7源码安装python3

    一.先安装python3所依赖的软件包,非常重要(否则可能会出现python3安装成功,却缺少相应的pip) yum groupinstall "Development tools" ...

  7. Serializable_序列化详情

     概述 Java 提供了一种对象序列化的机制.用一个字节序列可以表示一个对象,该字节序列包含该对象的数据.对象的类型和对象中存储的属性等信息.字节序列写出到文件之后,相当于文件中持久保存了一个对象的信 ...

  8. Qt 圆角按钮,面版自动布局

    一.前言 在部分界面开发中,有时需要动态添加控件或按钮到面板中,在不需要时又需要删除该控件,故模仿视频开发中的设置屏蔽词,通过自己绘制的按钮与排布面板控件实现. 实现效果如下: 说明: 1.输入框可设 ...

  9. Java核心技术卷阅读随笔--第4章【对象与类】

    对 象 与 类 4.1 面向对象程序设计概述 面向对象程序设计(简称 OOP) 是当今主流的程序设计范型, 它已经取代了 20 世纪 70 年代的" 结构化" 过程化程序设计开发技 ...

  10. Navicat Premium 15 linux 安装与激活 ArchLinux 2021

    查看了很多教程花了半小时才弄好可真不容易 参考https://github.com/orginly/navicat-keygen 下载地址 http://www.navicat.com.cn/down ...