<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
平時工作的時候,我喜歡用console.log
偵錯大法。但Vue3更新後,控制檯都是列印的Proxy物件和ref物件,想看裡邊的值,就需要很麻煩的一層一層的展開。
為了解決這個問題,我試過在編輯器中寫一個新的快捷鍵,快速寫出console.log(JSON.parse(JSON.stringify()))
。
但我用的是webStorm
,它自帶的.log
快捷鍵太舒服了,比如這樣:abc.log
點選tab鍵,就自動替換為console.log(abc)
。
我試了好久,終究還是沒能拓展類似的程式碼。所以才有了重寫console.log()的想法。
我希望新的console.log可以像現在的console.log一模一樣,只是當列印Proxy
和ref
物件時可以直接輸出它的源物件或ref.value。並且,還保留記錄當前檔案和行數的功能,可以讓我看到到底是哪個檔案哪個步驟執行的列印。
先說結果:
我翻了好久的檔案,終究還是不能達到我想要的效果,控制檯右側展示出的列印檔案及行號終究還是不能直接顯示原始檔,如果有大神能看到這篇文章的話,希望告訴我怎麼怎麼才能實現這個想法。
但退而求其次,我用console.trace
和Error.stack
兩種方式十分簡陋的完成了這個目標。
各位可以去 下載試試,原始碼也就不到200行,有興趣的同學可以看看。
這個不好判斷,Vue3新增了isProxy 方法,但如果不是Vue環境的話,那這個方法就失效了。 而且就這麼一個簡單的小功能,實在沒必要依賴其他的包。 最終是選擇在使用者new Proxy之前,把Proxy物件改造。
// 記錄使用者new Proxy操作的所有物件 // WeakSet,WeakMap,都是弱參照,不干預其他模組的垃圾回收機制 export const proxyMap = new WeakMap() let OriginalProxy = null export function listenProxy() { if (OriginalProxy) { // 防止使用者多次呼叫監聽 return } OriginalProxy = window.Proxy window.Proxy = new Proxy(Proxy, { construct(target, args) { const newProxy = new OriginalProxy(...args) proxyInstances.set(newProxy, target) return newProxy }, get(obj, prop) { // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance if (prop === Symbol.hasInstance) { // 監控 `instanceof` 關鍵字 return instance => proxyMap.has(instance) } // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get return Reflect.get(...arguments) } }) } export function unListenProxy() { window.Proxy = OriginalProxy || window.Proxy }
按說我們上一步已經監控了使用者動作,可以獲取源物件,等使用者log的時候,我們直接輸出源物件就可以了。但這也有個問題,Proxy畢竟不是普通的物件,通過Proxy獲取的結果,很可能跟源物件沒有一毛錢關係。所以只能通過深克隆返回源物件的值,但這也有個問題,就是對於某些不能遍歷的物件或屬性,就列印不了了……
問題貌似鎖死了,但,我們實際運用中,只是為了簡簡單單輸出一個不用展開的源物件而已,甚至運用場景都特別單一:Vue3
! 使用者如果覺得列印的不準確,換一個api不完了嗎,比如我們監控的是console.log
,那使用者就用console.info
一樣能輸出相同的結果。 把選擇權交給使用者就好了。在參照包的時候,再寫多一個設定項,讓使用者自己選平時的使用場景哪個正確結果比較多,就選哪個。想要完全正確,就換一個其他的api。
我簡直是個天才,哈哈哈
export function getOrg(obj) { return proxyMap.get(obj) } // 深克隆 export function clone(obj, _refs = new WeakSet()) { if (obj === null || obj === undefined) return null if (typeof obj !== 'object') return obj if (obj.constructor === Date) return new Date(obj) if (obj.constructor === RegExp) return new RegExp(obj) const newObj = new obj.constructor() //保持繼承的原型 for (const key in obj) { if (obj.hasOwnProperty(key)) { const val = obj[key] if (typeof val === 'object' && !_refs.has(val)) { newObj[key] = clone(val) } else { newObj[key] = val } } } return newObj }
import { listenProxy, unListenProxy, clone, getOrg } from "./until"; let config = { key: 'log', // any String type: 'trace', // 'trace' | 'error' | 'any String' cloneProxy: getOrg } let Vue = {} export default function (obj = {}, vue) { Vue = vue || {} config = { ...config, ...obj } if (obj.copy === 'clone') { config.cloneProxy = clone } listenLog(config) } // ---------------------------------------- const { groupCollapsed, groupEnd, trace, log } = console // const type = 'trace' | 'error' | '' function listenLog() { const isRef = Vue.isRef || (obj => { return typeof obj === 'object' && !!obj.constructor && obj.constructor.name === 'RefImpl' }) const unref = Vue.unref || (obj => obj.value) const { key, type, cloneProxy } = config if (!key) { console.error('Missing required parameter: key') } listenProxy() // 為 new Proxy 物件新增 `instanceof` 支援 console[key] = function (...arr) { const newArr = arr.map(i => { if (isRef(i)) { return unref(i) } else if (i instanceof Proxy) { return cloneProxy(i) } else { return i } }) groupCollapsed(...newArr) // 以 trace if (type === 'trace') { // trace(...newArr) console.log('第二行即為呼叫者所在的檔案位置') trace('The second line is the file location of the caller') groupEnd() return } let stack = new Error().stack || '' // stack = stack.replace('Error', 'Log') if (type === 'error') { log('%c這不是一個錯誤,請點選第二行的"at",跳轉到對應的檔案', 'color: #008000') log('%cThis is not an error. Please click "at" in the second line to jump to the corresponding file', 'color: #008000') log(stack) groupEnd() return; } // 簡單輸入模式,控制檯看起來是簡單了,卻失去了點選連結直接跳轉到對應檔案的功能 const stackArr = stack.match(/at.*s/g) || [] log(stackArr[1]) groupEnd() } }
至此已全部結束。
再加上一點ts的解釋檔案,那這個庫就能執行在所有平臺了
以上就是列印Proxy物件和ref物件的包實現詳解的詳細內容,更多關於列印Proxy ref物件包的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45