屬性可用于父組件向子組件傳遞參數,但僅限于相鄰父子組件。如果一個組件要給它的子組件的子組件(即孫組件)傳遞參數,不得不拆成兩個步驟:先傳給它的兒子,再由兒子傳給孫子。
祖孫三代之間的屬性透傳
盡管可以實現目的,但是中間層組件需要定義自身用不到的屬性。如果嵌套層級更深,需要傳遞的屬性更多,可以想象,這條鏈路的每個中間層組件都會變得不幸。這種為了透傳而聲明屬性造成的不幸,Vue 叫它屬性下鉆(Prop Drilling)。
為了解決屬性下鉆的問題,Vue 提供了 Provide/Inject 解決方案。這個方案定義了兩個角色:Provider 和 Injector,Provider 是數據的提供方,Injector 是數據的消費方。
Provider 通過 provide(key, data) 把數據存放在特定區域,Injector 通過 inject(key) 從特定區域獲取數據。其中的 key 用于區分不同用途的數據。
數據的提供和獲取
這套機制跟智能取餐柜有些類似。送餐員是食物(數據)的 Provider,你是食物的 Injector,而 key 是取餐柜號。送餐員通過 provide(key, food) 把食物放到特定柜子,你通過 inject(key) 從正確的柜子取走食物并消費它。
和取餐柜不同的是,一個 Provider 可以對應多個 Injector。即父組件提供一條數據后,可以被子子孫孫組件反復消費。
provide() 函數的兩個參數,第一個參數叫注入鍵(injection key),類型可以是字符串或 Symbol。第二個參數是注入的數據,可以是任意類型,包括響應式數據。
使用 provide 和 inject 傳遞數據
渲染效果:
使用 provide 和 inject 的渲染結果
在一個層級較深的組件樹中,如果出現多個 Provider 組件,且提供的 key 是相同的,最終 Injector 取到的數據是怎樣的?
Injector 永遠獲取離它最近的祖先組件提供的數據,再往上的數據會被同名覆蓋。用代碼驗證一下:
同名覆蓋
渲染效果:
同名覆蓋的渲染結果
如果你在開發一個很大的應用,為了防止同名覆蓋,可以在 provide() 中使用 Symbol 類型代替字符串。
在 Vue 3 中,最頂級的 Provider 是 app。通過 app.provide(key, value) 提供數據。數據的消費方法沒有什么不同。
頂級 Provider
頂級 Provider 的渲染結果
如果子組件通過 inject(key) 消費了一個 key,但是所有的上級組件都沒提供數據,會讓 Vue 惱火,并發出警告信息。
上游沒有提供數據,但下游用了
沒有注入數據的渲染結果
為了平息 Vue 的警告信息,可以在子組件使用 inject() 時,傳入第二個參數當作默認值。
使用默認值
這樣,當所有上級都不提供數據時,子組件就會啟用默認值。
正常顯示默認值
有的時候,默認值需要調用工廠函數計算才能獲得。此時,函數本身不是默認值,函數的返回值才是默認值,需要將 inject() 第三個參數設為 true,表示第二個參數的特殊作用。
標記默認的工廠函數
如果提供的數據是響應式數據,建議把響應式數據和變更數據的方法放在一個地方,打包一并提供給下游。誰聲明誰治理,這樣職責比較清晰。
響應式數據的聲明和變更在一個地方
渲染結果:
響應式數據的渲染結果
對于某些重要數據,如果上游組件不希望下游組件改動,可以先用 readonly() 方法保護好,然后再打包發送給下游。
設定為只讀數據
下游修改只讀數據時,Vue 不僅會阻止操作,還會發出警告:
修改只讀數據時的警告信息
完
rovide 和 inject 主要在開發高階插件/組件庫時使用。并不推薦用于普通應用程序代碼中。
這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在起上下游關系成立的時間里始終生效。
provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的 property。在該對象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的環境下可工作。
向子組件注入數據
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>provide / inject</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="js_app">
<children></children>
</div>
<script>
var Children = {
inject: ['list'],
template: '<ul><li v-for="(v,i) in list" :key="i">姓名:{{ v.name }} 分數:{{ v.score }}</li></ul>'
};
var app = new Vue({
el: '#js_app',
provide: {
list: [
{ name: 'tom', score: 88 },
{ name: 'jack', score: 75 },
{ name: 'rose', score: 96 }
]
},
components: {
children: Children
}
});
</script>
</body>
</html>
var symbol = Symbol();
var Children = {
inject: { // 這里是一個對象,自定義key指定值
list: symbol
},
template: '<ul><li v-for="(v,i) in list" :key="i">姓名:{{ v.name }} 分數:{{ v.score }}</li></ul>'
};
var app = new Vue({
el: '#js_app',
provide: function() { // 這里是一個函數,返回一個對象
return {
[symbol]: [
{ name: 'tom', score: 88 },
{ name: 'jack', score: 75 },
{ name: 'rose', score: 96 }
]
};
},
components: {
children: Children
}
});
var Children = {
inject: {
list: {
from: 'list',
default: function() {
return [
{ name: 'tom', score: 88 },
{ name: 'jack', score: 75 },
{ name: 'rose', score: 96 }
];
}
}
},
template: '<ul><li v-for="(v,i) in list" :key="i">姓名:{{ v.name }} 分數:{{ v.score }}</li></ul>'
};
var app = new Vue({
el: '#js_app',
components: {
children: Children
}
});
瀏覽器開發者工具在爬蟲中常用來進行簡單的抓包分析、JS逆向調試,打開方式:
常見禁用開發者工具手段:https://blog.csdn.net/cplvfx/article/details/108518077
官方文檔:https://developer.chrome.com/docs/devtools/
元素選擇
可以直接點擊頁面的元素,會自動跳轉到對應的源代碼。
終端模擬
模擬各種終端設備,支持自定義終端。
設置
開發者工具設置,包括一些外觀、快捷置、終端設備、地理位置設置等。
自定義
自定義和控制開發者工具,包括調整工具的位置、全局搜索、運行命令、其他工具等。
終端模擬
可以模擬各種終端設備,適合查看手機頁面的數據,點擊【More tools】—> 【Sensors】可以模擬終端的地理位置、終端朝向等;工具欄可以選擇要模擬的終端型號,其中 Responsive 是自適應。
是否開啟抓包
清除請求
是否隱藏 Filter(過濾器)窗格
搜索
Network conditions,網絡條件,允許在各種網絡環境中測試網站,包括 3G,離線等,還可以自定義限制最大下載和上傳流量。
Import/Export HAR file,導入導出抓包數據。
適用于分析關鍵函數代碼邏輯
各個選項功能:
執行到下一個斷點
執行下一步,不會進入所調用的函數內部
進入所調用的函數內部
跳出函數內部
一步步執行代碼,遇到有函數調用,則進入函數
停用斷點
不要在出現異常時暫停。
匹配 url 中關鍵詞,匹配到則跳轉到參數生成處,適用 于url 中的加密參數全局搜索搜不到,可采用這種方式攔截。
Event Listener Breakpoints,事件偵聽器斷點,當鼠標點擊、移動、鍵盤按鍵等行為或者其他事件發生時可以觸發斷點,比如 Mouse —> click,可快速定位點擊按鈕后,所執行的 JS。
在 sources —> snippets 下可以新建 JS 腳本。
在 console 中輸入如下代碼,如只打印 `_$` 開頭的變量值:
for (var p in window) {
if (p.substr(0, 2) !== "_$")
continue;
console.log(p + " >>> " + eval(p))
}
某些頁面打開調試工具會出現無限 debugger 的現象:
查看調用棧,點擊第二行跳轉到原函數:
可以看到 _0x2ba9bc[_0x20b2('0x79')] 和 _0x2ba9bc[_0x20b2('0x7a')] 分別對應 debu 和 gger,連起來就是 debugger,在本地重寫這個 JS,直接將這兩個值置空:
使用插件 ReRes,編寫規則,遇到此 JS,就替換成我們本地經過修改過的 JS,替換后無限 debugger 就不存在了:
直接在 Console 中將無限 debugger 的函數重寫置空也可以破解無限 debugger,缺點是刷新后失效。
適用于定時器類觸發的 debug:
for (var i = 1; i < 99999; i++)window.clearInterval(i);
鉤子英文 Hook,在 windows 系統中,所有的都是消息,按了一下鍵盤,就是一個消息,Hook 的意思就是勾住,在消息過去之前先把消息勾住,不讓其執行,然后自己優先處理。也就是這個技術提供了一個入口,能夠針對不同的消息或者 api 在執行前,先執行我的操作?!拔业牟僮鳌本褪倾^子函數。在開發者工具中以 chrome 插件的方式,在匹配到關鍵詞處插入斷點。
創建一個文件夾,文件夾中創建一個鉤子函數文件 inject.js 以及插件的配置文件 manifest.json :
打開 chrome 的擴展程序, 打開開發者模式,加載已解壓的擴展程序,選擇創建的文件夾即可:
以一個 header 鉤子為例,其配置文件如下:
{
"name": "Injection",
"version": "1.0",
"description": "RequestHeader鉤子",
"manifest_version": 1,
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"inject.js"
],
"all_frames": true,
"permissions": [
"tabs"
],
"run_at": "document_start"
}
]
}
header 鉤子用于定位 header 中關鍵參數生成位置,以下代碼演示了當 header 中包含 `Authorization` 時,則插入斷點
var code = function(){
var org = window.XMLHttpRequest.prototype.setRequestHeader;
window.XMLHttpRequest.prototype.setRequestHeader = function(key,value){
if(key=='Authorization'){
debugger;
}
return org.apply(this,arguments);
}
}
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
cookie 鉤子用于定位 cookie 中關鍵參數生成位置,以下代碼演示了當 cookie 中匹配到了 `abcdefghijk`, 則插入斷點:
var code = function(){
var org = document.cookie.__lookupSetter__('cookie');
document.__defineSetter__("cookie",function(cookie){
if(cookie.indexOf('abcdefghijk')>-1){
debugger;
}
org = cookie;
});
document.__defineGetter__("cookie",function(){return org;});
}
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
請求鉤子用于定位請求中關鍵參數生成位置,以下代碼演示了當請求的 url 里包含 `AbCdE` 時,則插入斷點:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。