編寫"谷歌擴充套件"便捷"自測"埋點上報資訊是否正確
推薦我之前寫的谷歌擴充套件入門&svelte入門文章:
記一次前端"chrome擴充套件"簡單易懂的實戰分享會(上)
記一次前端"chrome外掛"基礎實戰分享會(建議收藏)(下篇)
聊聊Svelte.js技術它做了什麼以及如何實現的(上)
聊聊Svelte.js技術它做了什麼以及如何實現的(下)
背景
每次開發完專案的埋點功能後都需要進行埋點自測, 此時我只能通過在控制檯的Network
上進行篩選, 由於埋點的請求引數是個"物件型別", 需要將傳參逐一點開並且找到params
這個key, 但是params
是一個被json序列化的字串, 需要進行JSON.parse(xxxxx.params);
, 然後再開啟它裡面的陣列結構
因為可能是多個埋點資訊一起上報所以JSON.parse
後是個資料, 最後還要將裡面的十個固有引數過濾掉只看我們新增的埋點引數。
上述過程很有規律所以可以抽象成一個工具查詢的邏輯, 那我們可以把問題拆解成3步:
- (展示)有合適的介面方便顯示你想要的請求資料。
- (獲取)攔截到你想要的請求。
- (處理)將請求的引數按固定規則解析, 剔除不需要檢視的引數與父級。
- (配置)可配置哪些請求需要被監測。
當然不只是埋點
這個功能, 只要你有分析api資料的需求都可以做類似的外掛。
最終效果
一、模擬資料的定義
埋點上報資料我這裡展示一個大概的資料結構, 大家根據業務自行調節, 假設下面的資料結構就是埋點上報api的上報引數
[
{
events: [
{
eventName: "事件名: 點選事件",
params:
'{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn1", "new2":"nnnnnn2"}',
xxxxxxxx: 1636179764881,
xxxxxxx: 0,
},
{
eventName: "事件名: 取消事件",
params:
'{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn2", "new3":"nnnnnn3"}',
xxxxxxxx: 1636179764881,
xxxxxxx: 0,
},
],
user: {
userId: 123,
userName: "lulu",
},
header: {
app_id: 999,
os_name: "mac",
os_version: "10_15_7",
device_model: "Macintosh",
language: "zh-CN",
},
},
];
- 傳參本身是個"物件型別"
- 第一層主要是"埋點事件", "使用者資訊", "裝置資訊&更多"
- "events"是事件陣列
- "eventName"事件名稱, "params"裡面是被"JSON序列化"的事件引數
- "params"裡有一些我們不用看的預設值, 因為每個埋點都會帶一些像"路徑資訊"&"停留事件"&"使用者操作路徑"等等的預設資訊, 這些資訊不用我們手動加入所以也就不用每次都看他的正確性。
- "new1, new2, new3"是我們新加入的引數, 也就是我們這次重點驗證的引數
二、模擬埋點資料的發出
編寫兩個按鈕負責上報埋點請求, 方便我們後續的驗證, 新建一個html檔案:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button onclick="send(`https://zhangzhaosong1.com/`)">點選上報1</button>
<button onclick="send(`https://zhangzhaosong2.com/`)">點選上報2</button>
<script>
function send(url) {
fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json;charset=utf-8;",
},
body: JSON.stringify([
{
events: [
{
eventName: "事件名: 點選事件",
params:
'{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn1", "new2":"nnnnnn2"}',
xxxxxxxx: 1636179764881,
xxxxxxx: 0,
},
{
eventName: "事件名: 取消事件",
params:
'{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn2", "new3":"nnnnnn3"}',
xxxxxxxx: 1636179764881,
xxxxxxx: 0,
},
],
user: {
userId: 123,
userName: "lulu",
},
header: {
app_id: 999,
os_name: "mac",
os_version: "10_15_7",
device_model: "Macintosh",
language: "zh-CN",
},
},
]),
}).then(() => {});
}
</script>
</body>
</html>
上面html裡面主要做的工作是新增兩個"按鈕", 每次點選發出埋點的模擬上報的post請求, 每個按鈕上報的不一樣所以有兩個按鈕。
三、建立一個谷歌外掛專案 & 定義圖示
沒看過基礎課的同學一定要先看看基礎文章, 否則沒法轉換思維。
主檔案
manifest.json
{
"manifest_version": 2,
"name": "埋:更方便的觀察埋點資料",
"description": "更方便的觀察埋點資料",
"version": "0.1",
"browser_action": {
"default_icon": "images/logo.png"
}
}
上述的圖示大家自由發揮哈
引入外掛
四、定義控制檯tab
在manifest.json檔案裡面增加
{
"manifest_version": 2,
"name": "埋:更方便的觀察埋點資料",
"description": "更方便的觀察埋點資料",
"version": "0.1",
"browser_action": {
"default_icon": "images/logo.png"
},
"devtools_page": "devtools/index.html" // 這句是新增的
}
devtools/index.html
主要功能只是引入js檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
devtools/index.js
主要功能是建立tab
chrome.devtools.panels.create(
"埋點驗證tab, 還原埋點上報資訊",
null,
"../panel/index.html",
function () {
console.log("自定義皮膚建立成功!");
}
);
在panel/index.html
裡面才是tab的內容區樣式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>建立成功</div>
</body>
</html>
五、初始化svelte專案
tab裡面的內容我們懶得原生操作dom, 使用"react"框架有點重, 所以這次我選擇了"svelte框架", 具體它的好處可以看看我之前寫的文章(文章放在文章頭部了)。
初始專案
npx degit sveltejs/template devpanel
cd ./devpanel
yarn
我們編輯一下 rollup.config.js
檔案, 使得打包後的檔案直接放入panel
資料夾裡。
output: {
sourcemap: false,
format: "iife",
name: "app",
file: production ? "../panel/index.js" : "public/build/bundle.js",
},
並且將panel
裡面的index.html
檔案的檔案引用指向我們的index.js檔案
, 以及引入樣式 <link rel="stylesheet" href="./bundle.css">
在這裡執行命令yarn build
後的效果如下, 請點選重新整理外掛並且"F12"重新開啟控制檯:
開發時候在localhost:5000/
, 開發完打包就可以和外掛一起上線了。
六、攔截請求
核心功能就是攔截請求, 讓我們使用onRequestFinished
api
chrome.devtools.network.onRequestFinished.addListener((res) => {})
上面的返回值res的資料格式如下圖:
關鍵資訊都有, 請求方法與url可以用來比對是否為需要攔截的請求, 而我們剩下的工作就是對"text"的解析了。
要注意的是, svelte
裡面無法讀取到chrome
的值, 所以需要用if (chrome) {}
包裹起來, 所以在svelte裡面開發時我使用的是mock資料除錯。
七、處理資料
<script>
const path = "https://zhangzhaosong.com/";
import getNowTime from "./getNowTime";
if (chrome) {
let checkEvent = "";
let dataList = [];
const baseKeyArr = [
"default1",
"default2",
"default3",
"default4",
"default5",
"default6",
"startTime",
"endTime",
];
chrome.devtools.network.onRequestFinished.addListener((res) => {
const req = res.request;
if (req.method === "POST" && req.url === path) {
const eventsArr = JSON.parse(req.postData.text);
eventsArr.forEach((item) => {
if (item.events) {
item.events.forEach((event) => {
const params = JSON.parse(event.params);
const res = {};
for (let i in params) {
if (!baseKeyArr.includes(i)) {
res[i] = params[i];
}
}
dataList.unshift({
res,
time: getNowTime(),
eventName: event.eventName,
});
dataList = dataList;
});
}
});
console.log("---", dataList);
}
});
}
</script>
<main>
<div>list</div>
</main>
<style>
</style>
baseKeyArr
這個陣列裡面是一些埋點預設的key
, 用來過濾掉我們不關心的值path
是要攔截的請求地址, 當然也可以換成正則之類的getNowTime
獲取當前的時間
八、簡單設計個樣式
將上面得到的陣列迴圈顯示出來, 形成一個被攔截的埋點事件的引數list:
<main>
{#each Object.keys(dataList) as item}
<div>
<header>
<span>{dataList[item].time} : </span>
<span class="title">{dataList[item].eventName}</span>
</header>
<ul>
{#each Object.keys(dataList[item].res) as key}
<li>
<span>{key} :</span>
<span>{dataList[item].res[key]}</span>
</li>
{/each}
</ul>
</div>
{/each}
</main>
<style>
.title {
background-color: bisque;
}
</style>
九、過濾功能與清除功能
為了好用我們可以新增一個過濾功能與清除功能, 這樣檢視起來更舒服:
end
網站快卡炸了, 這次就是這樣, 希望與你一起進步。