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

环境搭建

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 project
content: ['src/**/*.vue'], // Whitelist selectors by using regular expression
whitelistPatterns: [
/-(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-input
type="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. 使用 Vuejs 开发 chrome 插件的注意事项

    使用 Vuejs 开发 chrome 插件 chrome 插件的开发其实并不难,web开发者可以使用 html, css, javascript 轻松的开发实用的 chrome 插件. 一个好的 ch ...

  2. 使用Vuejs 开发chrome 插件的注意事项

    chrome 插件的开发其实并不难,web开发者可以使用 html, css, javascript 轻松的开发实用的 chrome 插件. 一个好的 chrome 插件可以提高我们的开发效率,甚至方 ...

  3. vue开发chrome扩展,数据通过storage对象获取

    开发chrome插件时遇到一个问题,那就是单文件组件的data数据需要从chrome提供的storage对象中获取,但是 chrome.storage.sync.get 方法是异步获取数据的,需要通过 ...

  4. 自己开发chrome插件生成二维码

    摘要: 最近在开发微信项目时,需要在微信调试,所以经常会在微信中输入本地服务地址,输入起来特别麻烦,所以自己就想了想微信中的扫一扫,然后开发了这款chrome插件,将当前url生成二维码,用微信扫一扫 ...

  5. 开发chrome插件(扩展)

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

  6. 替代Infinity绝佳的自主开发chrome插件

    最近闲来无事在好朋(da)友(shen)的帮助下开发一个chrome插件,目的是为了替换infinity主页插件, 当然在此也推荐一波infinity确实不错,界面和易用性都是非常好用的水准了. 主页 ...

  7. blazor wasm开发chrome插件

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

  8. 开发Chrome插件,实现网站自动登录

    近期被一个事情困扰着,我们采购了一款软件,里面有一个数据大屏页,当登录过期后,数据就会保持原状,不再更新.和供应商反馈了很多次,都无法彻底解决数据显示的问题,没办法,自己周末在家研究,网站自动登录的事 ...

  9. 试着开发chrome插件

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

  10. 使用Python开发chrome插件

    本文由 伯乐在线 - xianhu 翻译,Daetalus 校稿.未经许可,禁止转载!英文出处:pythonspot.com.欢迎加入翻译小组. 谷歌Chrome插件是使用HTML.JavaScrip ...

随机推荐

  1. JS模块化系统

    随着 JavaScript 开发变得越来越广泛,命名空间和依赖关系变得越来越难以处理.人们已经开发出不同的解决方案以模块系统的形式来解决这个问题. CommonJS(CJS) CommonJS 是一种 ...

  2. NC20951 网络优化

    题目链接 题目 题目描述 <梦三国2>是一款3D MOBA类网游.游戏继承<梦三国>的三国文化背景和基础玩法,并加入许多全新地图和全新竞技玩法.由于人气高,游戏在线人数与日俱增 ...

  3. NC17871 CSL分苹果

    题目链接 题目 题目描述 CSL手上有n个苹果,第i个苹果的质量是wi,现在他想把这些苹果分给他的好朋友wavator和tokitsukaze.但是CSL为了不让他们打架,根据质量决定尽量地均分成两堆 ...

  4. Springboot+JdbcTemplate模拟SQL注入攻击案例及解决方法

    说明 SQL注入是软件开发项目测试过程中必测项,重要等级极高.本文以springboot项目为例,模拟含有SQL注入攻击,并提供解决方法.部分内容整理自网络. 搭建项目 1.创建表tbuser DRO ...

  5. 易语言连接Mysql

    最近在写游戏的辅助工具研究了下易语言,下面就说下如何连接Mysql. .版本 2 .支持库 mysql .支持库 spec Mysql句柄 = 连接MySql ("127.0.0.1&quo ...

  6. 双哈希_Birthday_Cake

    Birthday Cake 思路:找到每个串的公共前后缀,统计公共前后缀之间的字符串的hash值,并判断所给n个串中是否存在符合条件的串 eg:abbddab 对于该串,我们不难发现,公共前后缀是ab ...

  7. Git 分支管理参考模型

    一个值得参考的Git分支管理模型如下: master 生产主分支,发布到生产环境使用这个分支,由hotfix或者release分支合并过来,不直接提交代码. release 预发布分支, 基于feat ...

  8. Qt+QtWebApp开发笔记(二):http服务器日志系统介绍、添加日志系统至Demo测试

    前言   上一篇使用QtWebApp的基于Qt的轻量级http服务器实现了一个静态网页返回的Demo,网页服务器很重要的就是日志,因为在服务器类上并没有直接返回,所以,本篇先把日志加上.   Demo ...

  9. 06-Redis系列之-哨兵(Redis-Sentinel)和集群详解和搭建

    主从架构高可用 主从架构存在的问题 主从复制,主节点发生故障,需要做故障转移.(可以手动转移:让其中一个slave变成master) 主从复制,只有主写数据,所以写能力和存储能力有限 总结:redis ...

  10. 【Azure 云服务】云服务(经典) 迁移至云服务(外延支持) 的相关疑问

    问题描述 根据微软官方文档说明,云服务(经典)已弃用.所以关于它有以下的一些疑问: 一:迁移时候的停机时间问题? 二:云服务(经典) 与 云服务(外延支持) 的区别是什么? 三:注意事项有那些呢?如 ...