這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

環境搭建
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 = 'https://www.cnblogs.com/smileZAZ/archive/2022/10/22/appPlugin';
element.setAttributeNode(attr);
document.getElementsByTagName('body')[0].appendChild(element);
let link = document.createElement('link');
let linkAttr = document.createAttribute('rel');
linkAttr.value = 'https://www.cnblogs.com/smileZAZ/archive/2022/10/22/stylesheet';
let linkHref = https://www.cnblogs.com/smileZAZ/archive/2022/10/22/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 ref="float" v-drag>
<el-card :body-style="{ padding: '15px' }">
<div slot="header" 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" >
<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"
>
</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
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/518995.html
標籤:其他
上一篇:手動實作一個Promise
