这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

环境搭建

Vue Web-Extension - A Web-Extension preset for VueJS (vue-web-extension.netlify.app)

npm install -g @vue/cli
npm install -g @vue/cli-init
vue create --preset kocal/vue-web-extension my-extension
cd my-extension
npm run server

会提供几个选项,如Eslint,background.js,tab页,axios,如下图

选择完后,将会自动下载依赖,通过npm run server将会在根目录生成dist文件夹,将该文件拖至Chrome插件管理便可安装,由于使用了webpack,所以更改代码将会热更新,不用反复的编译导入。

项目结构

├─src
|  ├─App.vue
|  ├─background.js
|  ├─main.js
|  ├─manifest.json
|  ├─views
|  |   ├─About.vue
|  |   └Home.vue
|  ├─store
|  |   └index.js
|  ├─standalone
|  |     ├─App.vue
|  |     └main.js
|  ├─router
|  |   └index.js
|  ├─popup
|  |   ├─App.vue
|  |   └main.js
|  ├─override
|  |    ├─App.vue
|  |    └main.js
|  ├─options
|  |    ├─App.vue
|  |    └main.js
|  ├─devtools
|  |    ├─App.vue
|  |    └main.js
|  ├─content-scripts
|  |        └content-script.js
|  ├─components
|  |     └HelloWorld.vue
|  ├─assets
|  |   └logo.png
├─public
├─.browserslistrc
├─.eslintrc.js
├─.gitignore
├─babel.config.js
├─package.json
├─vue.config.js
├─yarn.lock

根据所选的页面,并在src与vue.config.js中配置页面信息编译后dist目录结构如下

├─devtools.html
├─favicon.ico
├─index.html
├─manifest.json
├─options.html
├─override.html
├─popup.html
├─_locales
├─js
├─icons
├─css

安装组件库

安装elementUI

整体的开发和vue2开发基本上没太大的区别,不过既然是用vue来开发的话,那肯定少不了组件库了。

要导入Element-ui也十分简单,Vue.use(ElementUI); Vue2中怎么导入element,便怎么导入。演示如下

不过我没有使用babel-plugin-component来按需引入,按需引入一个按钮打包后大约1.6m,而全量引入则是5.5左右。至于为什么不用,因为我需要在content-scripts.js中引入element组件,如果使用babel-plugin-component将无法按需导入组件以及样式(应该是只支持vue文件按需引入,总之就是折腾了我一个晚上的时间)

安装tailwindcss

不过官方提供了如何使用TailwindCSS,这里就演示一下

在 Vue 3 和 Vite 安装 Tailwind CSS - Tailwind CSS 中文文档

推荐安装低版本,最新版有兼容性问题

npm install tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9

创建postcss.config.js文件

// postcss.config.js
module.exports = {plugins: [// ...require('tailwindcss'),require('autoprefixer'), // if you have installed `autoprefixer`// ...]
}

创建tailwind.config.js文件

// tailwind.config.js
module.exports = {purge: {// Specify the paths to all of the template files in your projectcontent: ['src/**/*.vue'],// Whitelist selectors by using regular expressionwhitelistPatterns: [/-(leave|enter|appear)(|-(to|from|active))$/, // transitions/data-v-.*/, // scoped css],}// ...
}

在src/popup/App.vue中导入样式,或在新建style.css在mian.js中import "../style.css";

<style>
/* purgecss start ignore */
@tailwind base;
@tailwind components;
/* purgecss end ignore */@tailwind utilities;
</style>

从官方例子导入一个登陆表单,效果如下

项目搭建

页面搭建

页面搭建就没什么好说的了,因为使用的是element-ui,所以页面很快就搭建完毕了,效果如图

悬浮窗

悬浮窗其实可有可无,不过之前写Chrome插件的时候就写了悬浮窗,所以vue版的也顺带写一份。

要注意的是悬浮窗是内嵌到网页的(且在document加载前载入,也就是"run_at": "document_start"),所以需要通过content-scripts.js才能操作页面Dom元素,首先在配置清单manifest.json与vue.confing.js中匹配要添加的网站,以及注入的js代码,如下

  "content_scripts": [{"matches": ["https://www.bilibili.com/video/*"],"js": ["js/jquery.js", "js/content-script.js"],"css": ["css/index.css"],"run_at": "document_start"},{"matches": ["https://www.bilibili.com/video/*"],"js": ["js/jquery.js", "js/bilibili.js"],"run_at": "document_end"}]

   contentScripts: {entries: {'content-script': ['src/content-scripts/content-script.js'],bilibili: ['src/content-scripts/bilibili.js'],},},

由于是用Vue,但又要在js中生成组件,就使用document.createElement来进行创建元素,Vue组件如下(可拖拽)

:::danger

如果使用babel-plugin-component按需引入,组件的样式将无法载入,同时自定义组件如果编写了style标签,那么也同样无法载入,报错:Cannot read properties of undefined (reading 'appendChild')

大致就是css-loader无法加载对应的css代码,如果执意要写css的话,直接在manifest.json中注入css即可

:::

完整代码

// 注意,这里引入的vue是运行时的模块,因为content是插入到目标页面,对组件的渲染需要运行时的vue, 而不是编译环境的vue (我也不知道我在说啥,反正大概意思就是这样)
import Vue from 'vue/dist/vue.esm.js';
import ElementUI, { Message } from 'element-ui';
Vue.use(ElementUI);// 注意,必须设置了run_at=document_start此段代码才会生效
document.addEventListener('DOMContentLoaded', function() {console.log('vue-chrome扩展已载入');insertFloat();
});// 在target页面中新建一个带有id的dom元素,将vue对象挂载到这个dom上。
function insertFloat() {let element = document.createElement('div');let attr = document.createAttribute('id');attr.value = 'appPlugin';element.setAttributeNode(attr);document.getElementsByTagName('body')[0].appendChild(element);let link = document.createElement('link');let linkAttr = document.createAttribute('rel');linkAttr.value = 'stylesheet';let linkHref = document.createAttribute('href');linkHref.value = 'https://unpkg.com/element-ui/lib/theme-chalk/index.css';link.setAttributeNode(linkAttr);link.setAttributeNode(linkHref);document.getElementsByTagName('head')[0].appendChild(link);let left = 0;let top = 0;let mx = 0;let my = 0;let onDrag = false;var drag = {inserted: function(el) {(el.onmousedown = function(e) {left = el.offsetLeft;top = el.offsetTop;mx = e.clientX;my = e.clientY;if (my - top > 40) return;onDrag = true;}),(window.onmousemove = function(e) {if (onDrag) {let nx = e.clientX - mx + left;let ny = e.clientY - my + top;let width = el.clientWidth;let height = el.clientHeight;let bodyWidth = window.document.body.clientWidth;let bodyHeight = window.document.body.clientHeight;if (nx < 0) nx = 0;if (ny < 0) ny = 0;if (ny > bodyHeight - height && bodyHeight - height > 0) {ny = bodyHeight - height;}if (nx > bodyWidth - width) {nx = bodyWidth - width;}el.style.left = nx + 'px';el.style.top = ny + 'px';}}),(el.onmouseup = function(e) {if (onDrag) {onDrag = false;}});},};window.kz_vm = new Vue({el: '#appPlugin',directives: {drag: drag,},template: `<div class="float-page" ref="float" v-drag><el-card class="box-card" :body-style="{ padding: '15px' }"><div slot="header" class="clearfix" style="cursor: move"><span>悬浮窗</span><el-button style="float: right; padding: 3px 0" type="text" @click="toggle">{{ show ? '收起' : '展开'}}</el-button></div><transition name="ul"><div v-if="show" class="ul-box"><span> {{user}} </span></div></transition></el-card></div>`,data: function() {return {show: true,list: [],user: {username: '',follow: 0,title: '',view: 0,},};},mounted() {},methods: {toggle() {this.show = !this.show;},},});
}

因为只能在js中编写vue组件,所以得用template模板,同时使用了directives,给组件添加了拖拽的功能(尤其是window.onmousemove,如果是元素绑定他自身的鼠标移动事件,那么拖拽鼠标将会十分卡顿),还使用了transition来进行缓慢动画效果其中注入的css代码如下
.float-page {width: 400px;border-radius: 8px;position: fixed;left: 50%;top: 25%;z-index: 1000001;
}.el-card__header {padding: 10px 15px !important
}.ul-box {height: 200px;overflow: hidden;
}.ul-enter-active,
.ul-leave-active {transition: all 0.5s;
}
.ul-enter,
.ul-leave-to {height: 0;
}

相关逻辑可自行观看,这里不在赘述了,并不复杂。

也顺带是复习一下HTML中鼠标事件和vue自定义命令了

功能实现

主要功能

  • 检测视频页面,输出对应up主,关注数以及视频标题播放(参数过多就不一一显示了)

  • 监控关键词根据内容判断是否点赞,例如文本出现了下次一定,那么就点赞。

输出相关信息

这个其实只要接触过一丢丢爬虫的肯定都会知道如何实现,通过右键审查元素,像这样

然后使用dom操作,选择对应的元素,输出便可

> document.querySelector("#v_upinfo > div.up-info_right > div.name > a.username").innerText
< '老番茄'

当然使用JQuery效果也是一样的。后续我都会使用JQuery来进行操作

在src/content-script/bilibili.js中写下如下代码

window.onload = function() {console.log('加载完毕');function getInfo() {let username = $('#v_upinfo > div.up-info_right > div.name > a.username').text();let follow = $(`#v_upinfo > div.up-info_right > div.btn-panel > div.default-btn.follow-btn.btn-transition.b-gz.following > span > span > span`).text();let title = $(`#viewbox_report > h1 > span`).text();let view = $('#viewbox_report > div > span.view').attr('title');console.log(username, follow, title, view);}getInfo();
};

重新加载插件,然后输出查看结果

加载完毕
bilibili.js:19 老番茄 1606.0万 顶级画质 总播放数2368406

这些数据肯定单纯的输出肯定是没什么作用的,要能显示到内嵌悬浮窗口,或者是popup页面上(甚至发送ajax请求到远程服务器上保存)

对上面代码微改一下

window.onload = function() {console.log('加载完毕');function getInfo() {let username = $('#v_upinfo > div.up-info_right > div.name > a.username').text().trim()let follow = $(`#v_upinfo > div.up-info_right > div.btn-panel > div.default-btn.follow-btn.btn-transition.b-gz.following > span > span > span`).text();let title = $(`#viewbox_report > h1 > span`).text();let view = $('#viewbox_report > div > span.view').attr('title');//console.log(username, follow, title, view);window.kz_vm.user = {username,follow,title,view,};}getInfo();
};

其中window.kz_vm是通过window.kz_vm = new Vue() 初始化的,方便我们操作vm对象,就需要通过jquery选择元素在添加属性了。如果你想的话也可以直接在content-script.js上编写代码,这样就无需使用window对象,但这样导致一些业务逻辑都堆在一个文件里,所以我习惯分成bilibili.js 然后注入方式为document_end,然后在操作dom元素吗,实现效果如下
如果像显示到popup页面只需要通过页面通信就行了,不过前提得先popup打开才行,所以一般都是通过background来进行中转,一般来说很少 content –> popup(因为操作popup的前提都是popup要打开),相对更多的是content –> background 或 popup –> content

实现评论

这边简单编写了一下页面,通过popup给content,让content输入评论内容,与点击发送,先看效果

同样的,找到对应元素位置

// 评论文本框
$('#comment > div > div.comment > div > div.comment-send > div.textarea-container > textarea').val("要回复的内容");
// 评论按钮
$('#comment > div > div.comment > div > div.comment-send > div.textarea-container > button').click();

接着就是写页面通信的了,可以看到是popup向content发送请求

window.onload = function() {console.log('content加载完毕');function comment() {chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {let { cmd, message } = request;if (cmd === 'addComment') {$('#comment > div > div.comment > div > div.comment-send > div.textarea-container > textarea').val(message);$('#comment > div > div.comment > div > div.comment-send > div.textarea-container > button').click();}sendResponse('我收到了你的消息!');});}comment();
};

<template><div><el-container><el-header height="24">B站小工具</el-header><el-main><el-row :gutter="5"><el-inputtype="textarea":rows="2"placeholder="请输入内容"v-model="message"class="mb-5"></el-input><div><el-button @click="addComment">评论</el-button></div></el-row></el-main></el-container></div>
</template><script>
export default {name: 'App',data() {return {message: '',list: [],open: false,}},created() {chrome.storage.sync.get('list', (obj) => {this.list = obj['list']})},mounted() {chrome.runtime.onMessage.addListener(function (request,sender,sendResponse) {console.log('收到来自content-script的消息:')console.log(request, sender, sendResponse)sendResponse('我是后台,我已收到你的消息:' + JSON.stringify(request))})},methods: {sendMessageToContentScript(message, callback) {chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {chrome.tabs.sendMessage(tabs[0].id, message, function (response) {if (callback) callback(response)})})},addComment() {this.sendMessageToContentScript({ cmd: 'addComment', message: this.message },function () {console.log('来自content的回复:' + response)})},},
}
</script>

代码就不解读了,调用sendMessageToContentScript方法即可。相关源码可自行下载查看

实现类似点赞功能也是同理的。

整体体验

当时写Chrome插件的效率不能说慢,反正不快就是了,像一些tips,都得自行封装。用过Vue的都知道写网页很方便,写Chrome插件未尝不是编写一个网页,当时的我在接触了Vue后就萌发了使用vue来编写Chrome的想法,当然肯定不止我一个这么想过,所以我在github上就能搜索到相应的源码,于是就有了这篇文章。

如果有涉及到爬取数据相关的,我肯定是首选使用HTTP协议,如果在搞不定我会选择使用puppeteerjs,不过Chrome插件主要还是增强页面功能的,可以实现原本页面不具备的功能。

本文仅仅只是初步体验,简单编写了个小项目,后期有可能会实现一个百度网盘一键填写提取码,Js自吐Hooke相关的。(原本是打算做pdd商家自动回复的,客户说要用客户端而不是网页端(客户端可以多号登陆),无奈,这篇博客就拿B站来演示了)

本文转载于:

https://juejin.cn/post/7009128182007742495

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--使用Vue开发Chrome插件相关推荐

  1. 一文教会你如何用Vue开发Chrome插件

    前言 作为一个常年的B端前端开发者来说,千篇一律的业务开发有着些许的枯燥无味.在联调过程中,会经常发现后端在部署服务,然后又不知什么时候部署好,由于公司的部署系统查看系统部署状态入口较深,所以闲暇之余 ...

  2. 利用Vue 脚手架 开发chrome 插件,太方便了

    前言 一个 Chrome 插件,核心就是 manifest.json 文件,manifest.json 下的属性 content_scripts 指定inject的脚本列表 js,css [ inje ...

  3. 使用Vue3+Element Plus开发Chrome插件

    使用Vue3+Element Plus开发Chrome插件 引言 初始Vue项目的创建 Element-Plus组件的安装及导入 其他文件配置 .eslintrc.js文件 vue.config.js ...

  4. 开发chrome 插件, background.js中 console log 看不到解决方法

    开发chrome 插件, background.js中 console log 看不到解决方法 参考文章: (1)开发chrome 插件, background.js中 console log 看不到 ...

  5. blazor wasm开发chrome插件

    用blazor(Wasm)开发了一个chrome插件感觉效率挺高的,分享给大家 先简单介绍下WebAssembly的原理: "WebAssembly是一种用于基于堆栈的虚拟机的二进制指令格式 ...

  6. 使用 React.js 开发 Chrome 插件

    (点击上方公众号,可快速关注) 来源:UncleChen unclechen.github.io/2017/06/16/使用ReactJS开发Chrome插件/ 一.背景 相信看到这篇文章的人应该都用 ...

  7. React开发chrome插件系列教程之chrome插件基本介绍

    文章目录 chrome插件能干什么 chrome插件的版本 chrome插件的浏览器支持 chrome插件的功能组成 manifest.json popup content script backgr ...

  8. 开发chrome插件(扩展)

    官方文档 https://developer.chrome.com/extensions/getstarted.html [干货]Chrome插件(扩展)开发全攻略 http://blog.haoji ...

  9. 试着开发chrome插件

    我的第一个chrome插件,是app形式的 代码如下 创建一个文件: 1.manifest.json 1 { 2 "version": "1.0", 3 &qu ...

最新文章

  1. CommonJS 的 AMD 规范
  2. Huffman(哈夫曼)编码--又称最佳编码(最有效的二进制编码)
  3. 图形结构:安排课程,图的遍历策略
  4. 前后端分离项目部署上线详细教程
  5. mysql不能存字母,使用不常見的字母/符號時,MySql數據庫不能正確存儲數據
  6. php blog to explore
  7. 作为搭建网站的程序员,你有Apsara Clouder云计算专项技能认证吗?
  8. SpringCloudConfig分布式配置中心-基本使用
  9. 如何通过http协议知道是从那个站点过来的请求_聊聊HTTP的那些事,以及在它背后的“勾当”...
  10. VEGAS如何分割与解组?
  11. 搬水果 - 九度教程第31题(哈夫曼树)
  12. 如何打开电脑端口——以开启mysql数据库3306端口为例
  13. 微信小程序 开发者工具和真机调试都能正常请求访问,线上不能登录请求问题
  14. 攻略 | 教你拿下梦寐以求的Offer(多资源)
  15. CSS - id选择器 和 类选择器
  16. 费雪方程式和交易量公式
  17. 线程池之ThreadPoolExecutor详解
  18. 【程序源代码】外卖侠源码
  19. 马无夜草不肥,人无横财不富
  20. 网络强国:网络安全+大数据

热门文章

  1. 『金字塔 区间dp』
  2. 24届近3年安徽大学自动化考研院校分析
  3. 对飚小型机 英特尔发布至强E7 v4系列产品
  4. 酷睿 i3 1115g4处理器属于什么水平 i31115g4性能怎么样
  5. a and b a or b
  6. Windows 7 Ultimate(旗舰版)SP1 32/64位官方原版下载地址
  7. 《乌合之众--大众心理研究》读书笔记
  8. combobox重写扩展
  9. 充电宝有没有必要买?便携充电宝推荐
  10. 记录一次自己的电脑磁盘占用率100%的遭遇