前言
碰巧掘金新上簽到活動(dòng)史侣,碰巧喝了咖啡睡不著惊橱,碰巧雙休不用上班回季,所以寫了個(gè)插件,方便工作日簽到(順道練練手)
先來看看成品
總的來看,插件需要實(shí)現(xiàn)以下幾個(gè)目標(biāo):
- 檢測(cè)當(dāng)前用戶登錄態(tài)
- 判斷用戶今天是否已簽到
- 發(fā)送簽到請(qǐng)求
- 展示信息(用戶信息涵但,獎(jiǎng)勵(lì)信息)
請(qǐng)求分析
用戶登錄憑證
通過登錄請(qǐng)求[POST] /passport/web/user/login
可以看到棘催,請(qǐng)求響應(yīng)設(shè)置的Cookie
中有好幾個(gè)鍵值對(duì),選擇一個(gè)普通的請(qǐng)求在postman
上分析呼猪,可以看到掘金
通過Cookie
中的sessionid
作為用戶登錄憑證
簽到相關(guān)的接口
在簽到功能的請(qǐng)求中砸琅,跟這次要實(shí)現(xiàn)功能相關(guān)的接口有4個(gè)谚赎,分別是:
- 獲取簽到天數(shù)的匯總信息
[GET]/growth_api/v1/get_counts
- 獲取當(dāng)前的礦石數(shù)量
[GET]/growth_api/v1/get_cur_point
- 判斷用戶今天是否已簽到
[GET]/growth_api/v1/get_today_status
- 用戶簽到
[POST]/growth_api/v1/check_in
這里大致分析一下功能對(duì)應(yīng)的請(qǐng)求即可壶唤,具體傳參以及返回值的含義可以通過瀏覽器控制臺(tái)查看(
F12
)
流程圖
下面通過幾個(gè)場(chǎng)景的時(shí)序圖來闡述清楚插件的工作流程
未登錄場(chǎng)景
未簽到場(chǎng)景
已簽到場(chǎng)景
搭建chrome插件開發(fā)工程
通過vue-web-extension實(shí)現(xiàn)快速搭建chrome插件開發(fā)工程(Vue)
首先確保這兩個(gè)已經(jīng)安裝了
npm install -g @vue/cli
npm install -g @vue/cli-init
然后通過vue-web-extension
創(chuàng)建工程琳省,我選擇vue-web-extension
的版本是v1
vue init kocal/vue-web-extension#v1 juejin-auto-sign
按需選擇自己需要的功能(axios
必選)
安裝element ui
(按需加載)
cd juejin-auto-sign && vue add element
由于element ui
的配置會(huì)寫在package.json
文件中babel
部分酵幕,跟工程原有的.babelrc
配置文件重疊了韭邓,需要將package.json
中關(guān)于babel
部分的配置合并到.babelrc
文件中
合并前
# .babelrc配置文件
{
"plugins": [
"@babel/plugin-proposal-optional-chaining"
],
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": 3,
"targets": {
// https://jamie.build/last-2-versions
"browsers": ["> 0.25%", "not ie 11", "not op_mini all"]
}
}]
]
}
# package.json配置文件
{
.......
"babel": {
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
}
合并后,將package.json
中babel
部分刪除荧缘,.babelrc
配置文件如下
在工程根目錄下執(zhí)行yarn build
,能夠正常打包
yarn build
![image-20210717175823529](/Users/luoxiongjian/Library/Application Support/typora-user-images/image-20210717175823529.png)
至此绸罗,工程已經(jīng)基本搭建完成了珊蟀!可以正式投入開發(fā)
工程常用命令:
- yarn build 構(gòu)建插件育灸,輸出到dist目錄下
- yarn build-zip 按照插件名+版本號(hào)的形式磅崭,構(gòu)建插件壓縮包
-
yarn watch
構(gòu)建插件,輸出到dist目錄下杭煎,如果發(fā)生改動(dòng)羡铲,會(huì)即時(shí)刷新
關(guān)鍵代碼
manifest.json配置文件
manifest.json文件中記載著插件的原信息扑媚,其中包括插件的基礎(chǔ)信息(插件名稱费坊,版本號(hào)附井,ICON等)永毅,以及插件涉及頁面(popup,options意蛀,background等)县钥,還有插件需要向chrome申請(qǐng)的權(quán)限
{
// 插件名稱
"name": "juejin-auto-sign",
// 插件描述
"description": "掘金簽到助手",
// 插件版本號(hào)
"version": "1.0.0",
"manifest_version": 2,
"icons": {
"48": "icons/icon.png",
"128": "icons/icon.png"
},
......
// [1] 申請(qǐng)掘金的cookie、網(wǎng)絡(luò)請(qǐng)求的權(quán)限
"permissions": [
"cookies",
"*://*.juejin.cn/",
"webRequest",
"webRequestBlocking"
]
}
[1]處可以看到,插件需要申請(qǐng)網(wǎng)絡(luò)權(quán)限 webRequest
和 webRequestBlocking
细移,這兩個(gè)權(quán)限是跟用戶簽到請(qǐng)求有關(guān)系的(POST請(qǐng)求),后面會(huì)詳細(xì)介紹為什么需要這兩個(gè)權(quán)限
popup頁面
目前插件的功能實(shí)現(xiàn)都是在popup頁面碗殷,所謂popup頁面就是在瀏覽器插件欄處點(diǎn)擊展示的頁面
拿Google翻譯
插件來看精绎,紅色箭頭指向的頁面就是popup頁面
chrome插件開發(fā)有分好幾種頁面以及腳本
頁面有:popup,optional锌妻,background代乃,插件上不同頁面的展示位置是不同,用途也不同仿粹,目前只需要了解到popup頁面即可
腳本有:background.js搁吓,content script等原茅,不同的腳本聲明周期也是不同的
下面展示簽到助手插件popup頁面的主要代碼
<template>
<div class="sign-body">
<div class="sign-image">
<el-avatar size="large" :src="imageUrl"></el-avatar>
</div>
<div class="sign-text">{{ nickName }}</div>
<div class="sign-label">
當(dāng)前礦石數(shù)量:<el-tag size="mini" type="success">{{ currentPoint }}</el-tag>
</div>
<div class="sign-label">
連續(xù)簽到天數(shù):<el-tag size="mini" type="success">{{ continueSignDays }}</el-tag>
</div>
<div class="sign-btn" v-if="!loading">
<el-button v-if="!login" type="primary" @click="toLogin">去登錄</el-button>
<el-button v-else type="primary" :loading="signing" :disabled="todaySign" @click="toSign">{{ todaySign ? '已簽到' : '去簽到' }}</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
// 頭像
imageUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
// 昵稱
nickName: 'null',
// 當(dāng)前礦石數(shù)量
currentPoint: 0,
// 連續(xù)簽到天數(shù)
continueSignDays: 0,
// 是否登錄
login: false,
// 今日是否已簽到
todaySign: true,
loading: true,
signing: false,
};
},
.......
async mounted() {
this.loading = true;
// 獲取用戶信息
let resp = await getUserInfo();
// 判斷cookie有效性
this.login = !resp.data.err_no && resp.data.data;
if (!this.login) {
this.loading = false;
return;
}
// 頭像,昵稱
this.imageUrl = resp.data.data.avatar_large;
this.nickName = resp.data.data.user_name;
// 礦石數(shù)量
resp = await getCurrentPoint();
this.currentPoint = resp.data.data;
// 連續(xù)簽到天數(shù)
resp = await getSignData();
this.continueSignDays = resp.data.data.cont_count;
// 當(dāng)前簽到狀況
resp = await getTodaySign();
this.todaySign = resp.data.data;
this.loading = false;
},
};
</script>
主要的邏輯都包含在頁面mounted
階段堕仔,該階段需要執(zhí)行一系列操作仿吞,包括獲取用戶信息你虹,判斷cookie有效性董饰,獲取用戶當(dāng)前簽到狀態(tài)以及獎(jiǎng)勵(lì)信息等等
忽略<template>中的一堆笨拙的<div>標(biāo)簽也祠,前端我只會(huì)寫<div>
修改請(qǐng)求頭
簽到請(qǐng)求/growth_api/v1/check_in
是一個(gè)POST
請(qǐng)求昔字,瀏覽器會(huì)自動(dòng)帶上origin
請(qǐng)求頭所坯,其值為chrome-extension://xxxxx
无蜂,此時(shí)掘金會(huì)校驗(yàn)請(qǐng)求頭中的origin
酣倾,非掘金的origin會(huì)直接報(bào)403
(應(yīng)該是網(wǎng)關(guān)層做了請(qǐng)求來源的校驗(yàn))
此時(shí)插件就需要修改請(qǐng)求頭中的origin
字段,然而origin
字段并不能隨意修改
比方說axios強(qiáng)制指定請(qǐng)求頭中origin的值是不生效的抬伺,并且插件的控制臺(tái)會(huì)有對(duì)應(yīng)的錯(cuò)誤提示,該操作不符合規(guī)范
此時(shí)就需要使用到manifest.json
中注冊(cè)的網(wǎng)絡(luò)請(qǐng)求的權(quán)限
一個(gè)正常響應(yīng)的請(qǐng)求在chrome中會(huì)經(jīng)歷如下圖所示的聲明周期
如果我們需要修改請(qǐng)求頭字段的話钥屈,則可以在onBeforeSendHeaders
發(fā)送請(qǐng)求頭之前通過事件監(jiān)聽的方式修改origin
字段,同時(shí)锯玛,該事件監(jiān)聽?wèi)?yīng)該是貫穿整個(gè)插件的生命周期的肯腕,所以代碼應(yīng)該寫在background.js
中
// background.js文件
chrome.webRequest.onBeforeSendHeaders.addListener(
function(details) {
details.requestHeaders.push({ name: 'origin', value: 'https://juejin.cn' });
return { requestHeaders: details.requestHeaders };
},
{ urls: ['*://*.juejin.cn/*'] },
['blocking', 'requestHeaders', 'extraHeaders']
);
上述代碼中可以看到,chrome.webRequest.onBeforeSendHeaders.addListener
接受三個(gè)參數(shù):
- 監(jiān)聽器回調(diào)方法妖泄,修改請(qǐng)求頭的操作應(yīng)該放在這個(gè)方法中
- 過濾器荷并,用于控制監(jiān)聽的URL范圍幻工,這里選擇監(jiān)聽掘金相關(guān)的請(qǐng)求
- 元信息(opt_extraInfoSpec)嗅骄,簡(jiǎn)單來說就是,這個(gè)參數(shù)填寫的值代表監(jiān)聽器的回調(diào)方法的執(zhí)行方式以及回調(diào)時(shí)傳入的數(shù)據(jù)
本次監(jiān)聽器的元數(shù)據(jù)中包含['blocking', 'requestHeaders', 'extraHeaders']
炊林,這三個(gè)值分別表示:-
blocking
表示回調(diào)方法是同步調(diào)用的饵逐,就是說一個(gè)請(qǐng)求的回調(diào)方法執(zhí)行完之后才會(huì)輪到下一個(gè)請(qǐng)求的回調(diào)方法 -
requestHeaders
表示回調(diào)方法的參數(shù)details
中包含請(qǐng)求頭的數(shù)據(jù) -
extraHeaders
這個(gè)字段比較神奇题画,由于origin
請(qǐng)求頭這玩意不是說改就改的课梳,chrome
也不推薦步势,如果真的要修改的話灾搏,就必須要填寫這個(gè)字段,這樣對(duì)origin
的修改才會(huì)生效
-
在manifest配置文件中可以看到立润,插件申請(qǐng)的權(quán)限除了
webRequest
之外,還有webRequestBlocking
媳板,添加這個(gè)權(quán)限是因?yàn)楸O(jiān)聽方法中使用blocking
同步的方式
總結(jié)
至此桑腮,這個(gè)插件的實(shí)現(xiàn)思路基本介紹完了,總的來看蛉幸,插件的實(shí)現(xiàn)難度不高破讨,有興趣的可以自己試著嘗試實(shí)現(xiàn)一下
當(dāng)然,以插件的形式來實(shí)現(xiàn)簽到的功能其實(shí)并沒有極大幅度地提高簽到的效率奕纫,最好的方式肯定還是在服務(wù)器中定時(shí)簽到提陶,這樣即使不上掘金網(wǎng)站,也能收獲滿滿的礦石用以抽獎(jiǎng)匹层,但是這樣不可避免地將用戶登錄憑證暴露出去隙笆,可能存在一些安全風(fēng)險(xiǎn)锌蓄,同時(shí)也喪失了掘金舉辦這個(gè)活動(dòng)的意義
萬一被別人拿到自己的cookie跑去刪除自己的文章,那真的是欲哭無淚
這次掘金的簽到活動(dòng)個(gè)人覺得還是辦的可以的
任務(wù)難度很低撑柔,規(guī)則簡(jiǎn)單瘸爽,活動(dòng)入口明顯
簽到獎(jiǎng)勵(lì)比較多,一個(gè)月簽到下來铅忿,應(yīng)該都有好幾千礦石剪决,應(yīng)該能抽獎(jiǎng)好幾十次
唯一不好的地方就是我抽了好幾天都沒抽到switch,哈哈哈哈