寫在前面,尤大前一陣子發了一條微博,內容是
Vite + Vue3 + TS + VScode + Volar 誰用誰知道
。好嘛,我用了,我確實知道了,我又行了,我學的動.jpg ...javascript
Vue3
?官方文檔Element-plus
GithubVite
? 下一代前端開發與構建工具Chrome Extension
?Chrome 拓展Volar
? Github本文不會着墨過多的插件內部邏輯和技術棧的基本使用,旨在介紹如何使用 Vite
與 Vue3
從 0開發一個實際項目,以及一些代碼設計的取捨和打包構建遇到的問題及其解決方案。關於技術棧的選擇,沒什麼技巧,個人項目我說的算,我想用哪一個就哪一個😎。css
前端在實際業務中和後端 Battle 的橋樑就是 Ajax[/ˈeɪdʒæks/] 請求,使用請求能夠順着網線聯繫到後端的各類服務。在一個 webApp 之中,咱們幾乎都會使用一些手段,來作請求發出的攔截,好比混入一些公共參數 tokenuid、加密一些數據、刪除一些數據、甚至取消(abort)一些請求;或者作請求響應的攔截,好比統一的錯誤碼處理,統一的數據格式化等等。社區之中,大名鼎鼎的 Axios[æk'sioʊ] 提供了上述的兩種 Interceptors[ˌɪntərˈseptə(r)],來統一處理數據上報前與響應後。 那麼假如不用 Axios,咱們能夠怎麼來攔截請求呢?有的同窗說了,「你能夠修改 xhr 的原型方法,你能夠替換原始的 fetch,來魔改啊~」 確實能夠,可是我不用。今天就來給你們介紹使用 Chrome Extension 的能力,來攔截請求。可惡,又讓我裝到了!html
先記住幾個關鍵點:前端
manifest.json
,這至關於 Chrome Extension 的入口文件,每 release 一次 version
自動 +1yarn create @vitejs/app
複製代碼
按照提示咱們建立一個 vue-ts
的工程。vue
manifest.json
{
"name": "Bad Request",
"version": "0.0.0",
"description": "Bad Request",
"permissions": [
"activeTab",
"declarativeContent",
"storage", // 獲取存儲權限,來存咱們要攔截的 api 連接
"webRequest", // 獲取請求讀取權限,來搞事情
"webRequestBlocking", // 獲取請求 abort 權限,來直接 abort 請求
"<all_urls>" // 獲取全部 url 的權限
],
"background": {
"page": "background/index.html",
"persistent": true // 保證 background.js 一直在後臺運行,攔截一直生效
},
"options_page": "options/index.html",
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"page_action": {
"default_title": "request",
"default_icon": "images/logo.png",
"default_popup": "popup/index.html"
},
"devtools_page": "devtool/index.html",
"icons": {
"16": "images/logo.png",
"32": "images/logo.png",
"48": "images/logo.png",
"128": "images/logo.png"
},
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js"
]
}
]
}
複製代碼
而後根據Vite 官方文檔把它改形成一個多頁面的工程。多 Page 的 vite.config.ts
以下:java
export default defineConfig{
// other setting...
build: {
rollupOptions: {
input: {
/** * 點擊插件圖標出現的彈窗 * */
popup: resolve(__dirname, 'popup/index.html'),
/** * chrome devtool pane 頁面 * */
devtoolPage: resolve(__dirname, 'devtoolPage/index.html'),
/** * 插件的核心 JS,一直活躍在後臺,來監聽全部請求 * */
background: resolve(
__dirname,
'background/index.html'
),
/** * 加載 chrome devtool pane 的入口 * */
devtool: resolve(__dirname, 'devtool/index.html'),
/** * 插件設置頁面 * */
options: resolve(__dirname, 'options/index.html'),
/** * 與頁面同級,並在某個時機執行,能夠拿到頁面的 document * */
content: resolve(__dirname, 'src/content.ts'),
},
output: {
entryFileNames: '[name].js',
},
},
},
// other setting...
}
複製代碼
根據其官方文檔,配置 Vite
按需加載,配好的 vite.config.ts
以下:node
export default defineConfig{
// other setting...
plugins: [
vue(),
styleImport({
libs: [
{
libraryName: 'element-plus',
esModule: true,
ensureStyleFile: true,
resolveStyle: (name) => {
return `element-plus/lib/theme-chalk/${name}.css`
},
resolveComponent: (name) => {
return `element-plus/es/${name}`
},
},
],
}),
],
// other setting...
}
複製代碼
Element Plus 針不戳!大工搞成,讓咱們愉快的開發吧~ios
在 background.ts
寫上這麼一行代碼:git
chrome.webRequest.onBeforeRequest.addListener(
handlerRequest,
{
urls: ['<all_urls>'],
},
// 定義獲取哪些權限
['blocking', 'requestBody', 'extraHeaders']
)
複製代碼
handlerRequest
裏咱們能夠拿到 details
參數,根據這個函數返回值的不一樣,Chrome
會執行以下操做:github
return { redirectUrl:
newurl}
,轉發請求return { cancel: true }
,abort
請求// 其類型是 chrome.webRequest.WebRequestDetails
function handlerRequest( details: chrome.webRequest.WebRequestDetails ) {
// 注意 proxy 和 block 須要你本身定義
/** * 代理轉發 */
if (proxy) {
return {
redirectUrl: details.url.replace(
proxy.origin,
proxy.target
),
}
}
/** * 請求攔截 * */
if (block) {
return { cancel: true }
}
}
複製代碼
高手過招,點到爲止;攔截一個請求就是如此的簡單。
知道原理以後咱們就能夠完善一下咱們總體的插件的需求內容
www.baidu.com
的請求或比較關鍵的接口評審完這個三個需求咱們來作設計稿,先作插件彈窗的設計稿和 devtoolPane 的設計稿吧~
🕐 🕑 🕒 🕓
設計稿作好了,上圖
用了 Element-plus 來作基本的佈局和表單控件
參考 Vue Devtool 的面板作了設計
backgroud.js
能夠讀取存儲的值,來個判斷是否攔截代碼以下:
<!-- 我還用 setup 語法糖 -->
<script setup lang="ts"> import { ElIcon, ElForm, ElFormItem, ElInput, ElSwitch, } from 'element-plus' import { ref, watch } from 'vue' /** * 存儲狀態 */ function saveCurrentStatus( type: string, value: boolean | string | Array<any> ) { // eslint-disable-next-line no-undef chrome.storage?.sync?.set({ [type]: value }, () => { console.log('設置成功') }) } // 定義開關 const blocking = ref(false) // 利用 Vue 3 的 watch,在每次值變化時,存儲狀態 watch([blocking], () => { saveCurrentStatus('blocking', blocking.value) }) // 每次組件初始化時取得開關狀態,setup 至關於 created 生命週期 const initStatus = () => { const storage = chrome.storage?.sync storage?.get('blocking', (data) => { blocking.value = data.blocking || false }) } initStatus() <script> <template> <el-form> <el-form-item label="攔截" size="mini"> <el-switch v-model="blocking" active-color="#2F86F6" /> </el-form-item> </el-form> </template> 複製代碼
background.js
監聽 storage 變化
/** * 監聽 storage 的變化 */
chrome.storage.onChanged.addListener((changes)=> {
console.log(changes)
})
複製代碼
這裏關鍵點在於 background.js
和 devtoolPane 之間的通訊問題,以爲也很簡單
PostMessage 便可
const backgroundPageConnection = chrome.runtime?.connect({
name: 'devtool-page',
})
backgroundPageConnection?.postMessage({
name: 'init',
tabId: chrome.devtools.inspectedWindow.tabId, // 當前 devtoolPane tabId
})
複製代碼
background.js
接口消息,拿到這個 Panelet devtool = null
const connections: Record<string, any> = {}
chrome.runtime.onConnect.addListener((port) => {
port.onMessage.addListener(message => {
if (message.name === 'init') {
connections[message.tabId] = port
devtool = port
}
})
})
// 而後就能夠 使用 devtool 來往 devtoolPane 派發消息了
// 這裏咱們把埋點的請求體所有發過去進行解析
function devtoolandler(details: any) {
devtool && devtool.postMessage(details)
}
複製代碼
上文提到,每次構建時候須要把 manifest.json
版本號 +1,同時拷貝過去,來看看我是怎麼作的~
rollup-plugin-copy
,每次構建結束以後拷貝文件到 dist
,很快啊~vite.config.ts
配置以下
export default defineConfig{
// other setting...
plugins: [
copy({
verbose: true,
hook: 'writeBundle',
targets: [
{
src: 'manifest.json',
dest: 'dist',
},
],
}),
],
// other setting...
}
複製代碼
版本號自動加1
構建完了以後要 壓縮整個 dist
,必需要壓縮才能發佈到 Chrome Extension Store
綜上,得出咱們的發佈腳本
#!/usr/bin/env zx
const semverInc = require('semver/functions/inc')
let manifest = require('../manifest.json')
console.log(
chalk.yellow.bold(`Current verion: ${manifest.version}`)
)
let types = ['patch', 'major', 'minor']
let type = await question(
chalk.cyan(
'Release type? Press Tab twice for suggestion \n'
),
{
choices: types,
}
)
let version = ''
if (type !== '' || types.includes(type)) {
version = semverInc(manifest.version, type)
console.log(
chalk.green.bold(`Release verion? ${version}`)
)
// 使用 sed 命令修改 version
$`sed -i '' s/${manifest.version}/${version}/g manifest.json`
} else {
await $`exit 1`
}
// 構建
await $`yarn build`
// git
await $`git add .`
await $`git commit -m 'Update version to ${version}'`
await $`git tag v${version}`
await $`git push origin refs/tags/v${version}`
await $`git push origin HEAD:refs/for/master`
// 壓縮
await $`cd dist && zip -r bundle.zip * && mv bundle.zip ../`
複製代碼
而後就能夠去 Chrome Extension Store 發佈了
注意:
至此,筆者使用了 Vue3+ Element Plus + TS + Vite 開發出來一個 Chrome Extension,效率很高,代碼很帥,尤大果真沒有騙我。
yyx! yyds!
最後,有什麼想指教筆者的,直接留言吧,歡迎你跟我溝通呀~
👋