「讀懂源碼系列1」還在恐懼讀源碼?看完這篇就不怕了

一個小需求

事情的原由,是昨天有一個新的需求被提出。前端

需求是要實現,讓咱們本身定製的彈出層,具有按下 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個須要注意的點:

  • 代碼方面,在 mounted 中,給 window 添加事件監聽以後,要記得在 destroyed 時,去除監聽。
  • 業務方面,這是一個咱們定製的通用的彈出層組件,因此在 props 中定義了一個 closeOnPressEscape 屬性,以方便在某些業務場景下,不須要按 ESC 就退出這個功能的時候,直接設置它爲 false 便可。

到此爲止,整個事件畫上了圓滿的句號。

源碼真有那麼可怕嗎?

源碼一詞,乍一聽就是神祕、高大上、吊炸天的代名詞,讓不少的前端同窗聞風喪膽。回想當初,我也曾一度對它有一股深深的恐懼。

源碼真的這麼可怕嗎?

從以上的事例中能夠看出,其實並無。例子中的element-ui源碼並不複雜,我和小夥伴S一塊兒看源碼時,他也大概都能看得明白。最後由於弄懂了背後的原理,進行簡單應用,比較輕鬆地就解決了問題。

對於源碼的恐懼,讓咱們漸漸思惟固化,本身告訴本身不要去碰源碼,時間長了就遺忘了還有這樣一條路可走。

面試中的應用

關於對源代碼的考察,我也會常常應用在面試中。在面試中,若是候選人給個人感受不錯,個人慣用伎倆是問下面這個問題:

剛纔你說到,用過一段時間 xxx 框架,xx API屬性也用過,也很清楚它達到的效果。

那麼如今,若是須要你實現一個相似的效果,拋開 xxx 框架以及 xx API屬性,

你會如何去實現,有沒有其餘思路?

這個問題,意在考量候選人的思惟方式和解決問題的能力,以及把他思考的過程闡述清楚的表達能力。這三種能力,每每比使用過某些框架的經驗,更讓我看中。

這道題的回答思路,其實就是能夠經過挖掘源碼,去實現功能。另外也能夠經過海量地查找資料,發現原生js的實現方式,但這條路沒有直接挖掘源碼來得快。在遇到實際的業務問題的時候,參考源碼的原理和寫法,每每能更快地解決問題。

這是我本身對這道題目,給出的答案。

一點點思考

昨天的案例,引起了個人一連串思考:

現代框架的確下降了前端入門的門檻,提升了開發效率。

可是,在使用這些框架的過程當中,咱們到底學到了什麼?

脫離了框架和它的API,咱們腦海中還剩下些什麼?

以致於,當下一個更新更棒的框架出現的時候,咱們是否可以用已經學到的知識,幫助本身更迅速地上手?

知其然,並知其因此然,學習全部的知識都應當有這種探索精神。咱們不只僅是代碼的搬運工。領悟這些深層次的原理,比起僅僅熟練地掌握一門框架,要實用得多。

PS:歡迎關注個人公衆號 「超哥前端小棧」,交流更多的想法與技術。
1877824921-5c2b6771cf4b9_articlex

相關文章
相關標籤/搜索