事情的原由,是昨天有一個新的需求被提出。前端
需求是要實現,讓咱們本身定製的彈出層,具有按下 ESC 也能退出的功能。我把任務交給了同組的小夥伴S去實現。(這個項目用到了vue技術棧,以及餓了麼的UI框架。)vue
我開完會回來,發現他還在處理那個功能,但好像遇到了什麼瓶頸。因而,我就問他,卡在了什麼地方。node
小夥伴S說,他百度了很多資料,還查了官方文檔,而且嘗試其中的辦法,但就是沒法觸發按下 ESC 的回調方法,非常鬱悶。我看了他的代碼,他的寫法是這樣的:面試
<div class="custom-modal" @keydown.27="handlePressEscape"> ... </div>
... handlePressEscape () { console.log('press escape!'); }, ...
他的想法不錯,由於是自定義的彈出層,因此就想着把 keydown 事件,綁定在最外層的 div 上,讓整個彈出層都能監聽到。element-ui
他給我看了他查的資料,幾乎都是在 input 上綁定 keydown 事件的例子,而 vue 的官方文檔裏也是相似的例子,實踐後卻陷入了瓶頸。可是他忽略了一個問題,keydown 事件,並不是綁在任意一個標籤上,都會起做用。框架
我沒有直接把答案告訴他,而是給他提供了一個思路:在咱們經常使用的 element-ui 的 el-dialog 組件裏,有個屬性叫作 close-on-press-escape
,它的解釋以下圖:學習
從文檔的解釋,能夠看出 el-dialog 在默認狀況下,已經實現了咱們須要解決的需求。因此,我讓他看看 el-dialog 的源碼,是如何實現的。ui
他一聽要看源碼,就露出了恐懼之情。this
源碼是全部框架和API的根基,由於比較複雜深邃,因此讓人很抗拒。我本身也經歷過這個階段,因此很是理解他的心情,鼓勵他一塊兒作一次嘗試。spa
首先,咱們在 node_modules 裏,找到了 element-ui的文件夾,它大體長這個樣子:
接着,咱們找到了 packages 裏的 dialog 文件夾,再從 index 入口,找到了組件 component.vue。但是,點進去找了半天,也只找到個 closeOnPressEscape 屬性的定義,卻沒有實現的方法。
... closeOnPressEscape: { type: Boolean, default: true }, ...
這麼神奇麼?只定義一個屬性,就能實現一個事件的交互了?
感受不太可能啊?!? 爲了揭開迷霧,繼續找。。。
仔細瀏覽了 component.vue 文件,發如今 script 裏,引入下面 3 個文件:
import Popup from 'element-ui/src/utils/popup'; import Migrating from 'element-ui/src/mixins/migrating'; import emitter from 'element-ui/src/mixins/emitter'; ...
在第一個引入的 Popup 中,居然也發現了 closeOnPressEscape,感受彷佛找對方向了。
但使人沮喪的是,Popup 中一樣只有 closeOnPressEscape 的屬性定義,卻沒有實現。不過,它卻引入了另外一個輔助文件 PopupManager,再點進去找。
哇!終於找到了!它的實現,是這樣的:
// handle `esc` key when the popup is shown window.addEventListener('keydown', function(event) { if (event.keyCode === 27) { const topPopup = getTopPopup(); if (topPopup && topPopup.closeOnPressEscape) { topPopup.handleClose ? topPopup.handleClose() : (topPopup.handleAction ? topPopup.handleAction('cancel') : topPopup.close()); } } });
原來,是在 window 上添加了事件監聽 keydown,當監測到是 ESC 的 keyCode 時,則執行相關操做。
ok,如今已經知曉了原理,那就按照咱們的實際需求,模仿改造一下:
... props: { ... closeOnPressEscape: { type: Boolean, default: true } }, ... mounted () { window.addEventListener('keydown', this.handlePressEscape); }, destroyed () { window.removeEventListener('keydown', this.handlePressEscape); }, methods: { ... handlePressEscape (event) { if (this.closeOnPressEscape && event.keyCode === 27) { this.handleClose(); } } }
在上述實現中,有2個須要注意的點:
到此爲止,整個事件畫上了圓滿的句號。
源碼一詞,乍一聽就是神祕、高大上、吊炸天的代名詞,讓不少的前端同窗聞風喪膽。回想當初,我也曾一度對它有一股深深的恐懼。
源碼真的這麼可怕嗎?
從以上的事例中能夠看出,其實並無。例子中的element-ui源碼並不複雜,我和小夥伴S一塊兒看源碼時,他也大概都能看得明白。最後由於弄懂了背後的原理,進行簡單應用,比較輕鬆地就解決了問題。
對於源碼的恐懼,讓咱們漸漸思惟固化,本身告訴本身不要去碰源碼,時間長了就遺忘了還有這樣一條路可走。
關於對源代碼的考察,我也會常常應用在面試中。在面試中,若是候選人給個人感受不錯,個人慣用伎倆是問下面這個問題:
剛纔你說到,用過一段時間 xxx 框架,xx API屬性也用過,也很清楚它達到的效果。那麼如今,若是須要你實現一個相似的效果,拋開 xxx 框架以及 xx API屬性,
你會如何去實現,有沒有其餘思路?
這個問題,意在考量候選人的思惟方式和解決問題的能力,以及把他思考的過程闡述清楚的表達能力。這三種能力,每每比使用過某些框架的經驗,更讓我看中。
這道題的回答思路,其實就是能夠經過挖掘源碼,去實現功能。另外也能夠經過海量地查找資料,發現原生js的實現方式,但這條路沒有直接挖掘源碼來得快。在遇到實際的業務問題的時候,參考源碼的原理和寫法,每每能更快地解決問題。
這是我本身對這道題目,給出的答案。
昨天的案例,引起了個人一連串思考:
現代框架的確下降了前端入門的門檻,提升了開發效率。
可是,在使用這些框架的過程當中,咱們到底學到了什麼?
脫離了框架和它的API,咱們腦海中還剩下些什麼?
以致於,當下一個更新更棒的框架出現的時候,咱們是否可以用已經學到的知識,幫助本身更迅速地上手?
知其然,並知其因此然,學習全部的知識都應當有這種探索精神。咱們不只僅是代碼的搬運工。領悟這些深層次的原理,比起僅僅熟練地掌握一門框架,要實用得多。
PS:歡迎關注個人公衆號 「超哥前端小棧」,交流更多的想法與技術。