精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

從 12.9K 的前端開(kāi)源項(xiàng)目我學(xué)到了啥?

開(kāi)發(fā)
阿寶哥帶領(lǐng)大家開(kāi)啟 BetterScroll 2.0 源碼的學(xué)習(xí)之旅。

[[343776]]

 接下來(lái)本文的重心將圍繞 插件化 的架構(gòu)設(shè)計(jì)展開(kāi),不過(guò)在分析 BetterScroll 2.0 插件化架構(gòu)之前,我們先來(lái)簡(jiǎn)單了解一下 BetterScroll。

一、BetterScroll 簡(jiǎn)介
BetterScroll 是一款重點(diǎn)解決移動(dòng)端(已支持 PC)各種滾動(dòng)場(chǎng)景需求的插件。它的核心是借鑒的 iscroll 的實(shí)現(xiàn),它的 API 設(shè)計(jì)基本兼容 iscroll,在 iscroll 的基礎(chǔ)上又?jǐn)U展了一些 feature 以及做了一些性能優(yōu)化。

BetterScroll 1.0 共發(fā)布了 30 多個(gè)版本,npm 月下載量 5 萬(wàn),累計(jì) star 數(shù) 12600+。那么為什么升級(jí) 2.0 呢?

  1. 做 v2 版本的初衷源于社區(qū)的一個(gè)需求: 
  2.  
  3. BetterScroll 能不能支持按需加載? 
  4. 來(lái)源于:BetterScroll 2.0 發(fā)布:精益求精,與你同行 

為了支持插件的按需加載,BetterScroll 2.0 采用了 插件化 的架構(gòu)設(shè)計(jì)。CoreScroll 作為最小的滾動(dòng)單元,暴露了豐富的事件以及鉤子,其余的功能都由不同的插件來(lái)擴(kuò)展,這樣會(huì)讓 BetterScroll 使用起來(lái)更加的靈活,也能適應(yīng)不同的場(chǎng)景。

下面是 BetterScroll 2.0 整體的架構(gòu)圖:

該項(xiàng)目采用的是 monorepos 的組織方式,使用 lerna 進(jìn)行多包管理,每個(gè)組件都是一個(gè)獨(dú)立的 npm 包:

與西瓜播放器一樣,BetterScroll 2.0 也是采用 插件化 的設(shè)計(jì)思想,CoreScroll 作為最小的滾動(dòng)單元,其余的功能都是通過(guò)插件來(lái)擴(kuò)展。比如長(zhǎng)列表中常見(jiàn)的上拉加載和下拉刷新功能,在 BetterScroll 2.0 中這些功能分別通過(guò) pull-up 和 pull-down 這兩個(gè)插件來(lái)實(shí)現(xiàn)。

插件化的好處之一就是可以支持按需加載,此外把獨(dú)立功能都拆分成獨(dú)立的插件,會(huì)讓核心系統(tǒng)更加穩(wěn)定,擁有一定的健壯性。

好的,簡(jiǎn)單介紹了一下 BetterScroll,接下來(lái)我們步入正題來(lái)分析一下這個(gè)項(xiàng)目中一些值得我們學(xué)習(xí)的地方。

二、開(kāi)發(fā)體驗(yàn)方面
2.1 更好的智能提示
BetterScroll 2.0 采用 TypeScript 進(jìn)行開(kāi)發(fā),為了讓開(kāi)發(fā)者在使用 BetterScroll 時(shí)能夠擁有較好的智能提示,BetterScroll 團(tuán)隊(duì)充分利用了 TypeScript 接口自動(dòng)合并的功能,讓開(kāi)發(fā)者在使用某個(gè)插件時(shí),能夠有對(duì)應(yīng)的 Options 提示以及 bs(BetterScroll 實(shí)例)能夠有對(duì)應(yīng)的方法提示。

2.1.1 智能插件 Options 提示

2.1.2 智能 BetterScroll 實(shí)例方法提示

接下來(lái),為了后面能更好地理解 BetterScroll 的設(shè)計(jì)思想,我們先來(lái)簡(jiǎn)單介紹一下插件化架構(gòu)。

三、插件化架構(gòu)簡(jiǎn)介
3.1 插件化架構(gòu)的概念
插件化架構(gòu)(Plug-in Architecture),是一種面向功能進(jìn)行拆分的可擴(kuò)展性架構(gòu),通常用于實(shí)現(xiàn)基于產(chǎn)品的應(yīng)用。插件化架構(gòu)模式允許你將其他應(yīng)用程序功能作為插件添加到核心應(yīng)用程序,從而提供可擴(kuò)展性以及功能分離和隔離。

插件化架構(gòu)模式包括兩種類型的架構(gòu)組件:核心系統(tǒng)(Core System)和插件模塊(Plug-in modules)。應(yīng)用邏輯被分割為獨(dú)立的插件模塊和核心系統(tǒng),提供了可擴(kuò)展性、靈活性、功能隔離和自定義處理邏輯的特性。

圖中 Core System 的功能相對(duì)穩(wěn)定,不會(huì)因?yàn)闃I(yè)務(wù)功能擴(kuò)展而不斷修改,而插件模塊是可以根據(jù)實(shí)際業(yè)務(wù)功能的需要不斷地調(diào)整或擴(kuò)展。插件化架構(gòu)的本質(zhì)就是將可能需要不斷變化的部分封裝在插件中,從而達(dá)到快速靈活擴(kuò)展的目的,而又不影響整體系統(tǒng)的穩(wěn)定。

插件化架構(gòu)的核心系統(tǒng)通常提供系統(tǒng)運(yùn)行所需的最小功能集。插件模塊是獨(dú)立的模塊,包含特定的處理、額外的功能和自定義代碼,來(lái)向核心系統(tǒng)增強(qiáng)或擴(kuò)展額外的業(yè)務(wù)能力。通常插件模塊之間也是獨(dú)立的,也有一些插件是依賴于若干其它插件的。重要的是,盡量減少插件之間的通信以避免依賴的問(wèn)題。

3.2 插件化架構(gòu)的優(yōu)點(diǎn)
靈活性高:整體靈活性是對(duì)環(huán)境變化快速響應(yīng)的能力。由于插件之間的低耦合,改變通常是隔離的,可以快速實(shí)現(xiàn)。
可測(cè)試性:插件可以獨(dú)立測(cè)試,也很容易被模擬,不需修改核心系統(tǒng)就可以演示或構(gòu)建新特性的原型。
性能高:雖然插件化架構(gòu)本身不會(huì)使應(yīng)用高性能,但通常使用插件化架構(gòu)構(gòu)建的應(yīng)用性能都還不錯(cuò),因?yàn)榭梢宰远x或者裁剪掉不需要的功能。
介紹完插件化架構(gòu)相關(guān)的基礎(chǔ)知識(shí),接下來(lái)我們來(lái)分析一下 BetterScroll 2.0 是如何設(shè)計(jì)插件化架構(gòu)的。

四、BetterScroll 插件化架構(gòu)實(shí)現(xiàn)
對(duì)于插件化的核心系統(tǒng)設(shè)計(jì)來(lái)說(shuō),它涉及三個(gè)關(guān)鍵點(diǎn):插件管理、插件連接和插件通信。下面我們將圍繞這三個(gè)關(guān)鍵點(diǎn)來(lái)逐步分析 BetterScroll 2.0 是如何實(shí)現(xiàn)插件化架構(gòu)。

4.1 插件管理
為了統(tǒng)一管理內(nèi)置的插件,也方便開(kāi)發(fā)者根據(jù)業(yè)務(wù)需求開(kāi)發(fā)符合規(guī)范的自定義插件。BetterScroll 2.0 約定了統(tǒng)一的插件開(kāi)發(fā)規(guī)范。BetterScroll 2.0 的插件需要是一個(gè)類,并且具有以下特性:

1.靜態(tài)的 pluginName 屬性;

2.實(shí)現(xiàn) PluginAPI 接口(當(dāng)且僅當(dāng)需要把插件方法代理至 bs);

3.constructor 的第一個(gè)參數(shù)就是 BetterScroll 實(shí)例 bs,你可以通過(guò) bs 的 事件 或者 鉤子 來(lái)注入自己的邏輯。

這里為了直觀地理解以上的開(kāi)發(fā)規(guī)范,我們將以內(nèi)置的 PullUp 插件為例,來(lái)看一下它是如何實(shí)現(xiàn)上述規(guī)范的。PullUp 插件為 BetterScroll 擴(kuò)展上拉加載的能力。

顧名思義,靜態(tài)的 pluginName 屬性表示插件的名稱,而 PluginAPI 接口表示插件實(shí)例對(duì)外提供的 API 接口,通過(guò) PluginAPI 接口可知它支持 4 個(gè)方法:

finishPullUp(): void:結(jié)束上拉加載行為;
openPullUp(config?: PullUpLoadOptions): void:動(dòng)態(tài)開(kāi)啟上拉功能;
closePullUp(): void:關(guān)閉上拉加載功能;
autoPullUpLoad(): void:自動(dòng)執(zhí)行上拉加載。
插件通過(guò)構(gòu)造函數(shù)注入 BetterScroll 實(shí)例 bs,之后我們就可以通過(guò) bs 的事件或者鉤子來(lái)注入自己的邏輯。那么為什么要注入 bs 實(shí)例?如何利用 bs 實(shí)例?這里我們先記住這些問(wèn)題,后面我們?cè)賮?lái)分析它們。

4.2 插件連接
核心系統(tǒng)需要知道當(dāng)前有哪些插件可用,如何加載這些插件,什么時(shí)候加載插件。常見(jiàn)的實(shí)現(xiàn)方法是插件注冊(cè)表機(jī)制。核心系統(tǒng)提供插件注冊(cè)表(可以是配置文件,也可以是代碼,還可以是數(shù)據(jù)庫(kù)),插件注冊(cè)表含有每個(gè)插件模塊的信息,包括它的名字、位置、加載時(shí)機(jī)(啟動(dòng)就加載,或是按需加載)等。

這里我們以前面提到的 PullUp 插件為例,來(lái)看一下如何注冊(cè)和使用該插件。首先你需要使用以下命令安裝 PullUp 插件:

  1. $ npm install @better-scroll/pull-up --save 

成功安裝完 pullup 插件之后,你需要通過(guò) BScroll.use 方法來(lái)注冊(cè)插件:

  1. import BScroll from '@better-scroll/core' 
  2. import Pullup from '@better-scroll/pull-up' 
  3.  
  4. BScroll.use(Pullup) 

然后,實(shí)例化 BetterScroll 時(shí)需要傳入 PullUp 插件的配置項(xiàng)。

  1. new BScroll('.bs-wrapper', { 
  2.   pullUpLoad: true 
  3. }) 

現(xiàn)在我們已經(jīng)知道通過(guò) BScroll.use 方法可以注冊(cè)插件,那么該方法內(nèi)部做了哪些處理?要回答這個(gè)問(wèn)題,我們來(lái)看一下對(duì)應(yīng)的源碼:

  1. // better-scroll/packages/core/src/BScroll.ts 
  2. export const BScroll = (createBScroll as unknown) as BScrollFactory 
  3. createBScroll.use = BScrollConstructor.use 

在 BScroll.ts 文件中, BScroll.use 方法指向的是 BScrollConstructor.use 靜態(tài)方法,該方法的實(shí)現(xiàn)如下:

  1. export class BScrollConstructor<O = {}> extends EventEmitter { 
  2.   static plugins: PluginItem[] = [] 
  3.   static pluginsMap: PluginsMap = {} 
  4.  
  5.   static use(ctor: PluginCtor) { 
  6.     const name = ctor.pluginName 
  7.     const installed = BScrollConstructor.plugins.some
  8.       (plugin) => ctor === plugin.ctor 
  9.     ) 
  10.     // 省略部分代碼 
  11.     if (installed) return BScrollConstructor 
  12.     BScrollConstructor.pluginsMap[name] = true 
  13.     BScrollConstructor.plugins.push({ 
  14.       name
  15.       applyOrder: ctor.applyOrder, 
  16.       ctor, 
  17.     }) 
  18.     return BScrollConstructor 
  19.   } 

通過(guò)觀察以上代碼,可知 use 方法接收一個(gè)參數(shù),該參數(shù)的類型是 PluginCtor,用于描述插件構(gòu)造函數(shù)的特點(diǎn)。PluginCtor 類型的具體聲明如下所示:

  1. interface PluginCtor { 
  2.   pluginName: string 
  3.   applyOrder?: ApplyOrder 
  4.   new (scroll: BScroll): any 

當(dāng)我們調(diào)用 BScroll.use(Pullup) 方法時(shí),會(huì)先獲取當(dāng)前插件的名稱,然后判斷當(dāng)前插件是否已經(jīng)安裝過(guò)了。如果已經(jīng)安裝則直接返回 BScrollConstructor 對(duì)象,否則會(huì)對(duì)插件進(jìn)行注冊(cè)。即把當(dāng)前插件的信息分別保存到 pluginsMap({}) 和 plugins([]) 對(duì)象中:

另外調(diào)用 use 靜態(tài)方法后,會(huì)返回 BScrollConstructor 對(duì)象,這是為了支持鏈?zhǔn)秸{(diào)用:

  1. BScroll.use(MouseWheel) 
  2.   .use(ObserveDom) 
  3.   .use(PullDownRefresh) 
  4.   .use(PullUpLoad) 

現(xiàn)在我們已經(jīng)知道 BScroll.use 方法內(nèi)部是如何注冊(cè)插件的,注冊(cè)插件只是第一步,要使用已注冊(cè)的插件,我們還需要在實(shí)例化 BetterScroll 時(shí)傳入插件的配置項(xiàng),從而進(jìn)行插件的初始化。對(duì)于 PullUp 插件,我們通過(guò)以下方式進(jìn)行插件的初始化。

  1. new BScroll('.bs-wrapper', { 
  2.   pullUpLoad: true 
  3. }) 

所以想了解插件是如何連接到核心系統(tǒng)并進(jìn)行插件初始化,我們就需要來(lái)分析一下 BScroll 構(gòu)造函數(shù):

  1. // packages/core/src/BScroll.ts 
  2. export const BScroll = (createBScroll as unknown) as BScrollFactory 
  3.  
  4. export function createBScroll<O = {}>( 
  5.   el: ElementParam, 
  6.   options?: Options & O 
  7. ): BScrollConstructor & UnionToIntersection<ExtractAPI<O>> { 
  8.   const bs = new BScrollConstructor(el, options) 
  9.   return (bs as unknown) as BScrollConstructor & 
  10.     UnionToIntersection<ExtractAPI<O>> 

在 createBScroll 工廠方法內(nèi)部會(huì)通過(guò) new 關(guān)鍵字調(diào)用 BScrollConstructor 構(gòu)造函數(shù)來(lái)創(chuàng)建 BetterScroll 實(shí)例。因此接下來(lái)的重點(diǎn)就是分析 BScrollConstructor 構(gòu)造函數(shù):

  1. // packages/core/src/BScroll.ts 
  2. export class BScrollConstructor<O = {}> extends EventEmitter { 
  3.   constructor(el: ElementParam, options?: Options & O) { 
  4.     const wrapper = getElement(el) 
  5.     // 省略部分代碼 
  6.     this.plugins = {} 
  7.     this.hooks = new EventEmitter([...]) 
  8.     this.init(wrapper) 
  9.   } 
  10.    
  11.   private init(wrapper: MountedBScrollHTMLElement) { 
  12.     this.wrapper = wrapper 
  13.     // 省略部分代碼 
  14.     this.applyPlugins() 
  15.   } 

通過(guò)閱讀 BScrollConstructor 的源碼,我們發(fā)現(xiàn)在 BScrollConstructor 構(gòu)造函數(shù)內(nèi)部會(huì)調(diào)用 init 方法進(jìn)行初始化,而在 init 方法內(nèi)部會(huì)進(jìn)一步調(diào)用 applyPlugins 方法來(lái)應(yīng)用已注冊(cè)的插件:

  1. // packages/core/src/BScroll.ts 
  2. export class BScrollConstructor<O = {}> extends EventEmitter {   
  3.   private applyPlugins() { 
  4.     const options = this.options 
  5.     BScrollConstructor.plugins 
  6.       .sort((a, b) => { 
  7.         const applyOrderMap = { 
  8.           [ApplyOrder.Pre]: -1, 
  9.           [ApplyOrder.Post]: 1, 
  10.         } 
  11.         const aOrder = a.applyOrder ? applyOrderMap[a.applyOrder] : 0 
  12.         const bOrder = b.applyOrder ? applyOrderMap[b.applyOrder] : 0 
  13.         return aOrder - bOrder 
  14.       }) 
  15.       .forEach((item: PluginItem) => { 
  16.         const ctor = item.ctor 
  17.     // 當(dāng)啟用指定插件的時(shí)候且插件構(gòu)造函數(shù)的類型是函數(shù)的話,再創(chuàng)建對(duì)應(yīng)的插件 
  18.         if (options[item.name] && typeof ctor === 'function') { 
  19.           this.plugins[item.name] = new ctor(this) 
  20.         } 
  21.       }) 
  22.   } 

在 applyPlugins 方法內(nèi)部會(huì)根據(jù)插件設(shè)置的順序進(jìn)行排序,然后會(huì)使用 bs 實(shí)例作為參數(shù)調(diào)用插件的構(gòu)造函數(shù)來(lái)創(chuàng)建插件,并把插件的實(shí)例保存到 bs 實(shí)例內(nèi)部的 plugins({}) 屬性中。

到這里我們已經(jīng)介紹了插件管理和插件連接,下面我們來(lái)介紹最后一個(gè)關(guān)鍵點(diǎn) —— 插件通信。

4.3 插件通信
插件通信是指插件間的通信。雖然設(shè)計(jì)的時(shí)候插件間是完全解耦的,但實(shí)際業(yè)務(wù)運(yùn)行過(guò)程中,必然會(huì)出現(xiàn)某個(gè)業(yè)務(wù)流程需要多個(gè)插件協(xié)作,這就要求兩個(gè)插件間進(jìn)行通信;由于插件之間沒(méi)有直接聯(lián)系,通信必須通過(guò)核心系統(tǒng),因此核心系統(tǒng)需要提供插件通信機(jī)制。

這種情況和計(jì)算機(jī)類似,計(jì)算機(jī)的 CPU、硬盤、內(nèi)存、網(wǎng)卡是獨(dú)立設(shè)計(jì)的配置,但計(jì)算機(jī)運(yùn)行過(guò)程中,CPU 和內(nèi)存、內(nèi)存和硬盤肯定是有通信的,計(jì)算機(jī)通過(guò)主板上的總線提供了這些組件之間的通信功能。

同樣,對(duì)于插件化架構(gòu)的系統(tǒng)來(lái)說(shuō),通常核心系統(tǒng)會(huì)以事件總線的形式提供插件通信機(jī)制。提到事件總線,可能有一些小伙伴會(huì)有一些陌生。但如果說(shuō)是使用了 發(fā)布訂閱模式 的話,應(yīng)該就很容易理解了。這里阿寶哥不打算在展開(kāi)介紹發(fā)布訂閱模式,只用一張圖來(lái)回顧一下該模式。

對(duì)于 BetterScroll 來(lái)說(shuō),它的核心是 BScrollConstructor 類,該類繼承了 EventEmitter 事件派發(fā)器:

  1. // packages/core/src/BScroll.ts 
  2. export class BScrollConstructor<O = {}> extends EventEmitter {   
  3.   constructor(el: ElementParam, options?: Options & O) { 
  4.     this.hooks = new EventEmitter([ 
  5.       'refresh'
  6.       'enable'
  7.       'disable'
  8.       'destroy'
  9.       'beforeInitialScrollTo'
  10.       'contentChanged'
  11.     ]) 
  12.     this.init(wrapper) 
  13.   } 

EventEmitter 類是由 BetterScroll 內(nèi)部提供的,它的實(shí)例將會(huì)對(duì)外提供事件總線的功能,而該類對(duì)應(yīng)的 UML 類圖如下所示:

講到這里我們就可以來(lái)回答前面留下的第一個(gè)問(wèn)題:“那么為什么要注入 bs 實(shí)例?”。因?yàn)?bs(BScrollConstructor)實(shí)例的本質(zhì)也是一個(gè)事件派發(fā)器,在創(chuàng)建插件時(shí),注入 bs 實(shí)例是為了讓插件間能通過(guò)統(tǒng)一的事件派發(fā)器進(jìn)行通信。

第一個(gè)問(wèn)題我們已經(jīng)知道答案了,接下來(lái)我們來(lái)看第二個(gè)問(wèn)題:”如何利用 bs 實(shí)例?“。要回答這個(gè)問(wèn)題,我們將繼續(xù)以 PullUp 插件為例,來(lái)看一下該插件內(nèi)部是如何利用 bs 實(shí)例進(jìn)行消息通信的。

  1. export default class PullUp implements PluginAPI { 
  2.   static pluginName = 'pullUpLoad' 
  3.   constructor(public scroll: BScroll) { 
  4.     this.init() 
  5.   } 

在 PullUp 構(gòu)造函數(shù)中,bs 實(shí)例會(huì)被保存到 PullUp 實(shí)例內(nèi)部的 scroll 屬性中,之后在 PullUp 插件內(nèi)部就可以通過(guò)注入的 bs 實(shí)例來(lái)進(jìn)行事件通信。比如派發(fā)插件的內(nèi)部事件,在 PullUp 插件中,當(dāng)距離滾動(dòng)到底部小于 threshold 值時(shí),觸發(fā)一次 pullingUp 事件:

  1. private checkPullUp(pos: { x: number; y: number }) { 
  2.   const { threshold } = this.options 
  3.   if (...) { 
  4.       this.pulling = true 
  5.       // 省略部分代碼 
  6.       this.scroll.trigger(PULL_UP_HOOKS_NAME) // 'pullingUp' 
  7.   } 

知道如何利用 bs 實(shí)例派發(fā)事件之后,我們?cè)賮?lái)看一下在插件內(nèi)部如何利用它來(lái)監(jiān)聽(tīng)插件所感興趣的事件

  1. // packages/pull-up/src/index.ts 
  2. export default class PullUp implements PluginAPI { 
  3.   static pluginName = 'pullUpLoad' 
  4.   constructor(public scroll: BScroll) { 
  5.     this.init() 
  6.   } 
  7.  
  8.   private init() { 
  9.     this.handleBScroll() 
  10.     this.handleOptions(this.scroll.options.pullUpLoad) 
  11.     this.handleHooks() 
  12.     this.watch() 
  13.   } 

在 PullUp 構(gòu)造函數(shù)中會(huì)調(diào)用 init 方法進(jìn)行插件初始化,而在 init 方法內(nèi)部會(huì)分別調(diào)用不同的方法執(zhí)行不同的初始化操作,這里跟事件相關(guān)的是 handleHooks 方法,該方法的實(shí)現(xiàn)如下:

  1. private handleHooks() { 
  2.   this.hooksFn = [] 
  3.   // 省略部分代碼 
  4.   this.registerHooks( 
  5.     this.scroll.hooks, 
  6.     this.scroll.hooks.eventTypes.contentChanged, 
  7.     () => { 
  8.       this.finishPullUp() 
  9.     } 
  10.   ) 

很明顯在 handleHooks 方法內(nèi)部,會(huì)進(jìn)一步調(diào)用 registerHooks 方法來(lái)注冊(cè)鉤子:

  1. private registerHooks(hooks: EventEmitter, name: string, handler: Function) { 
  2.   hooks.on(name, handler, this) 
  3.   this.hooksFn.push([hooks, name, handler]) 

通過(guò)觀察 registerHooks 方法的簽名可知,它支持 3 個(gè)參數(shù),第 1 個(gè)參數(shù)是 EventEmitter 對(duì)象,而另外 2 個(gè)參數(shù)分別表示事件名和事件處理器。在 registerHooks 方法內(nèi)部,它就是簡(jiǎn)單地通過(guò) hooks 對(duì)象來(lái)監(jiān)聽(tīng)指定的事件。

那么 this.scroll.hooks 對(duì)象是什么時(shí)候創(chuàng)建的呢?在 BScrollConstructor 構(gòu)造函數(shù)中我們找到了答案。

  1. // packages/core/src/BScroll.ts 
  2. export class BScrollConstructor<O = {}> extends EventEmitter { 
  3.   constructor(el: ElementParam, options?: Options & O) { 
  4.     // 省略部分代碼 
  5.     this.hooks = new EventEmitter([ 
  6.       'refresh'
  7.       'enable'
  8.       'disable'
  9.       'destroy'
  10.       'beforeInitialScrollTo'
  11.       'contentChanged'
  12.     ])  
  13.   } 

很明顯 this.hooks 也是一個(gè) EventEmitter 對(duì)象,所以可以通過(guò)它來(lái)進(jìn)行事件處理。好的,插件通信的內(nèi)容就先介紹到這里,下面我們用一張圖來(lái)總結(jié)一下該部分的內(nèi)容:

介紹完 BetterScroll 插件化架構(gòu)的實(shí)現(xiàn),最后我們來(lái)簡(jiǎn)單聊一下 BetterScroll 項(xiàng)目工程化方面的內(nèi)容。

五、工程化方面
在工程化方面,BetterScroll 使用了業(yè)內(nèi)一些常見(jiàn)的解決方案:

lerna:Lerna 是一個(gè)管理工具,用于管理包含多個(gè)軟件包(package)的 JavaScript 項(xiàng)目。
prettier:Prettier 中文的意思是漂亮的、美麗的,是一個(gè)流行的代碼格式化的工具。
tslint:TSLint 是可擴(kuò)展的靜態(tài)分析工具,用于檢查 TypeScript 代碼的可讀性,可維護(hù)性和功能性錯(cuò)誤。
commitizen & cz-conventional-changelog:用于幫助我們生成符合規(guī)范的 commit message。
husky:husky 能夠防止不規(guī)范代碼被 commit、push、merge 等等。
jest:Jest 是由 Facebook 維護(hù)的 JavaScript 測(cè)試框架。
coveralls:用于獲取 Coveralls.io 的覆蓋率報(bào)告,并在 README 文件中添加一個(gè)不錯(cuò)的覆蓋率按鈕。
vuepress:Vue 驅(qū)動(dòng)的靜態(tài)網(wǎng)站生成器,它用于生成 BetterScroll 2.0 的文檔。
因?yàn)楸疚牡闹攸c(diǎn)不在工程化,所以上面阿寶哥只是簡(jiǎn)單羅列了 BetterScroll 在工程化方面使用的開(kāi)源庫(kù)。如果你對(duì) BetterScroll 項(xiàng)目也感興趣的話,可以看看項(xiàng)目中的 package.json 文件,并重點(diǎn)看一下項(xiàng)目中 npm scripts 的配置。

 

 

責(zé)任編輯:姜華 來(lái)源: 全棧修仙之路
相關(guān)推薦

2021-10-25 05:43:40

前端技術(shù)編程

2021-03-09 09:55:02

Vuejs前端代碼

2020-02-22 15:01:51

后端前端開(kāi)發(fā)

2020-07-07 08:52:16

機(jī)器學(xué)習(xí)機(jī)器學(xué)習(xí)工具人工智能

2022-03-27 09:06:04

React類型定義前端

2020-12-31 10:47:03

開(kāi)發(fā)Vuejs技術(shù)

2016-01-18 10:06:05

編程

2021-04-15 08:15:27

Vue.js源碼方法

2024-04-12 08:54:13

從庫(kù)數(shù)據(jù)庫(kù)應(yīng)用

2020-02-22 14:49:30

畢業(yè)入職半年感受

2020-11-04 07:13:57

數(shù)據(jù)工程代碼編程

2021-07-28 07:01:09

薅羊毛架構(gòu)Vue+SSR

2021-01-02 09:48:13

函數(shù)運(yùn)算js

2020-10-30 12:40:04

Reac性能優(yōu)化

2019-08-27 10:49:30

跳槽那些事兒技術(shù)Linux

2013-06-27 10:31:39

2019-08-16 17:14:28

跳槽那些事兒技術(shù)Linux

2015-06-29 13:47:19

創(chuàng)業(yè)創(chuàng)業(yè)智慧

2011-10-18 11:43:25

UNIXC語(yǔ)言丹尼斯·里奇

2023-06-06 08:14:18

核心Docker應(yīng)用程序
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

午夜欧美视频在线观看| 精品久久久久久综合日本欧美| 国产欧美黑人| 乱色588欧美| 日本免费在线视频不卡一不卡二| 天天干天天色综合| 欧美精品中文字幕一区| 在线一级成人| 欧美色图17p| 日韩一区二区三区电影在线观看| 成人黄色免费观看| 风韵丰满熟妇啪啪区老熟熟女| 偷拍亚洲欧洲综合| 一区二区三区短视频| 亚洲精品手机在线观看| 亚洲综合一区二区三区| 在线观看a级片| 男女视频一区二区三区| 综合色中文字幕| 九色porny丨首页在线| 97人人爽人人| 欧美激情在线观看视频| 人人妻人人澡人人爽欧美一区| 色哟哟中文字幕| 麻豆精品在线视频| 98精品国产自产在线观看 | 麻豆成全视频免费观看在线看| 欧美激情一区不卡| 国产欧美日韩在线播放| 亚洲一区二区色| 午夜宅男久久久| 欧美成人免费一级人片100| 欧美黄色激情视频| 欧洲vs亚洲vs国产| 日韩午夜电影av| 色啦啦av综合| 欧美日韩尤物久久| 欧美性生活大片免费观看网址| 久久福利一区二区| 久久久久久国产精品免费无遮挡 | 国产精品久久精品视| 亚洲天堂狠狠干| 久久香蕉精品| 欧美一区三区三区高中清蜜桃| 免费中文字幕在线观看| 欧美激情偷拍自拍| 在线观看国产精品91| 中文字幕免费在线播放| 2020最新国产精品| 欧美一级生活片| 日韩成人av免费| 91精品国产自产观看在线| 欧美综合天天夜夜久久| 成人亚洲视频在线观看| av在线不卡免费| 亚洲午夜久久久久久久久电影院| 成年人视频大全| 国产最新在线| 亚洲欧美日韩国产另类专区| 在线视频不卡国产| 在线日本中文字幕| 国产精品无圣光一区二区| 欧洲亚洲一区二区三区四区五区| 日韩电影免费| 久久精品亚洲乱码伦伦中文| 欧美一区二区三区成人久久片| 久青草国产在线| 国产日韩欧美在线一区| 亚洲精品9999| 麻豆91在线| 亚洲精品国产高清久久伦理二区| 国产一二三四区在线观看| 最新黄网在线观看| 亚洲一区二区三区四区在线 | mm131美女视频| 久久99国产精一区二区三区| 中文字幕不卡在线视频极品| 日本免费网站视频| 午夜精品影院| 欧美一级片在线播放| 亚洲 日本 欧美 中文幕| 秋霞成人午夜伦在线观看| 成人a在线视频| 亚洲av综合色区无码一区爱av | a毛片在线播放| 亚洲高清久久久| 18岁网站在线观看| jizz亚洲女人高潮大叫| 欧美一级在线视频| 疯狂揉花蒂控制高潮h| 欧美日中文字幕| 久久99视频精品| 免费黄色网址在线| 久久国产三级精品| 国产精品一区而去| 二区三区在线播放| 亚洲综合免费观看高清完整版在线 | 欧美超碰在线| 高清在线视频日韩欧美| 这里只有精品国产| 国产超碰在线一区| 日韩精品一线二线三线| 亚洲图区一区| 在线欧美日韩国产| 成人欧美精品一区二区| 精品亚洲成人| 国模精品视频一区二区三区| 国产一级片一区二区| 丁香五精品蜜臀久久久久99网站| 日本欧洲国产一区二区| 丝袜在线视频| 欧美日韩专区在线| 老司机免费视频| 色综合天天综合网中文字幕| 91国产美女视频| 999久久久久| 国产亚洲精品7777| 91精品国产91久久久久麻豆 主演| a屁视频一区二区三区四区| 亚洲а∨天堂久久精品喷水 | 国产99精品一区| 欧美极品少妇xxxxⅹ裸体艺术 | 少妇视频在线观看| 欧美一区二区三区四区久久 | 亚洲黄色在线观看视频| 国产亚洲精品精华液| 国产a级片网站| 亚洲成人精品综合在线| 亚洲视频日韩精品| 久久亚洲精品国产| 波多野结衣中文字幕一区| 日本高清xxxx| 日韩专区视频| 在线看福利67194| 亚洲视频 欧美视频| 粉嫩蜜臀av国产精品网站| 国产又黄又爽免费视频| 日本成人在线网站| 一区三区二区视频| 日韩三级一区二区| 久久综合色之久久综合| 米仓穗香在线观看| 精品国产鲁一鲁****| 日韩在线视频观看| 中文字幕一区二区三区免费看 | sm在线播放| 精品久久久久99| 欧美成人精品欧美一级| 九九国产精品视频| 中文字幕一区二区三区5566| 懂色aⅴ精品一区二区三区| 亚洲欧洲一区二区三区在线观看| 中日韩黄色大片| av电影一区二区| 亚洲熟妇av日韩熟妇在线| 久久香蕉网站| 欧美在线亚洲一区| 久久天堂电影| 精品1区2区3区| 免费精品在线视频| 国内精品久久久久影院一蜜桃| 亚洲一区二区三区精品动漫| 欧美美女福利视频| 久久久国产视频91| www黄色网址| 亚洲国产精品自拍| 亚洲精品女人久久久| 午夜一区不卡| 色99中文字幕| 成人网av.com/| 久久99精品久久久久久琪琪| 五月婷婷免费视频| 色婷婷久久久亚洲一区二区三区| 蜜桃无码一区二区三区| 麻豆国产精品777777在线| 美女黄色片网站| julia中文字幕一区二区99在线| 久久久久久999| 日本黄在线观看| 欧美日韩久久久一区| 欧美成人精品一区二区免费看片| 成人手机电影网| 日本一本二本在线观看| 日韩一区欧美| www.av一区视频| 国偷自产一区二区免费视频 | 免费黄在线观看| 久久99精品久久久久久| 拔插拔插海外华人免费| 欧美日本成人| 91在线观看免费高清| 精品丝袜在线| 久久精品国产欧美激情| 日日夜夜精品免费| 欧美性受极品xxxx喷水| 久久99久久98精品免观看软件| 96av麻豆蜜桃一区二区| 欧美国产日韩另类| 国产日韩欧美| 少妇熟女一区二区| 美女久久久久| 97人人干人人| 亚洲精品555| 午夜精品一区二区三区av| 中文日本在线观看| 亚洲国产成人精品久久| 91精品在线视频观看| 欧美日韩性生活视频| 国产美女福利视频| 久久久午夜精品| 精品国产乱码久久久久夜深人妻| 免费在线亚洲欧美| 99热久久这里只有精品| 色综合狠狠操| 日本不卡免费新一二三区| 中文一区二区三区四区| 国产精品久久久久久久久久ktv| 欧洲黄色一区| 久久久精品视频在线观看| 熟妇高潮一区二区三区| 欧美一区二区三区在线视频| 成年人av网站| 精品国产鲁一鲁一区二区张丽| 一级性生活免费视频| 久久婷婷成人综合色| 国产艳妇疯狂做爰视频 | 亚洲成人日韩| 日韩和欧美的一区二区| 成人影院中文字幕| 成人欧美在线观看| 欧美va在线观看| 欧美中文字幕在线| www在线观看黄色| 欧美理论电影在线观看| 黄色在线免费网站| 深夜福利国产精品| 国产福利电影在线| 亚洲精品视频在线观看视频| 国模私拍视频在线| 精品欧美一区二区久久| 精品久久久免费视频| 欧美日韩高清一区二区三区| 天天综合久久综合| 日本韩国欧美在线| 国产一级免费视频| 精品久久中文字幕久久av| 久久中文字幕无码| 亚洲高清不卡在线观看| 久久久久久av无码免费网站| 亚洲精品国久久99热| 国产黄a三级三级| 中文字幕一区二区三区不卡在线| a级在线免费观看| 久久久久久久久99精品| 法国空姐电影在线观看| 国产丝袜欧美中文另类| 谁有免费的黄色网址| 国产欧美一区二区三区在线老狼| 欧美老熟妇乱大交xxxxx| 久久综合九色综合久久久精品综合| 欧美深性狂猛ⅹxxx深喉| 99久久夜色精品国产网站| 激情综合丁香五月| 国产亚洲欧美色| 能直接看的av| 17c精品麻豆一区二区免费| 91精品一区二区三区蜜桃| 一区二区三区成人在线视频| 国产一级片播放| 天天操天天色综合| 亚洲毛片一区二区三区| 欧美性猛交xxxxxxxx| 国产免费不卡av| 日韩欧美一级精品久久| 黄色成人一级片| 亚洲欧美日韩国产中文| 福利成人在线观看| 美女精品视频一区| 18video性欧美19sex高清| 日本国产一区二区三区| 欧美成人xxxx| av日韩免费电影| 免费看av成人| 一区二区三区三区在线| 欧美日韩三级| 日韩精品一区二区三区色欲av| 免费av网站大全久久| 制服下的诱惑暮生| 91免费视频网| 你懂得在线观看| 亚洲福利一区二区| 亚洲 欧美 中文字幕| 欧美一区二区三区播放老司机| 国精品人妻无码一区二区三区喝尿 | 97视频在线播放| 草莓视频成人appios| aa日韩免费精品视频一| 激情综合网站| 妺妺窝人体色www看人体| 老司机午夜精品视频在线观看| 182午夜视频| 91麻豆精品一区二区三区| 国产又色又爽又高潮免费| 亚洲成人自拍偷拍| 中文字幕永久免费视频| 亚洲成人aaa| 精品国产丝袜高跟鞋| 91国内在线视频| 在线高清欧美| 欧美日韩三区四区| 亚洲五月婷婷| 少妇一级淫免费播放| a级高清视频欧美日韩| 日本不卡一二区| 日韩欧美国产成人| 精品人妻一区二区三区四区不卡| 亚洲欧美成人网| heyzo高清国产精品| 国产在线拍揄自揄视频不卡99| 日本欧美三级| 国产在线xxxx| 国产中文字幕一区| 免费一级特黄3大片视频| 狠狠久久五月精品中文字幕| 亚洲大尺度网站| 久久香蕉国产线看观看网| 亚洲成人va| 欧美日韩中文国产一区发布| 亚洲激精日韩激精欧美精品| 永久免费黄色片| 国产精品免费网站在线观看| 国产三级精品三级在线观看| 亚洲高清不卡av| 欧美人与性动交α欧美精品图片| 成人激情在线播放| 第一会所sis001亚洲| 欧美牲交a欧美牲交aⅴ免费真 | 欧美日韩中文国产| 青青青手机在线视频观看| 91精品国产成人www| 91成人入口| 成年丰满熟妇午夜免费视频 | 韩国三级视频在线观看| 亚洲欧美日韩国产手机在线 | 高清成人在线观看| www.99re7| 欧美一二三四在线| 国产cdts系列另类在线观看| 成人av在线亚洲| 四季av在线一区二区三区 | av成人在线看| 亚洲一卡二卡三卡| 麻豆国产欧美一区二区三区| 中文字幕第69页| 69av一区二区三区| caopon在线免费视频| av蓝导航精品导航| 亚洲精品日韩久久| 在线观看av中文字幕| 欧美性少妇18aaaa视频| 大胆av不用播放器在线播放| 国产精品日韩电影| 91青青国产在线观看精品| 激情文学亚洲色图| 夜夜嗨av一区二区三区中文字幕| 亚洲a视频在线| 91超碰caoporn97人人| 制服丝袜日韩| 五月天婷婷亚洲| 亚洲男人的天堂网| 丰满熟妇人妻中文字幕| 97精品免费视频| 中文有码一区| 成 人 黄 色 小说网站 s色| 亚洲免费av观看| 欧美自拍偷拍一区二区| 国产999精品久久久影片官网| 精品一区二区三区中文字幕老牛| 午夜在线观看av| 艳妇臀荡乳欲伦亚洲一区| 亚洲 小说区 图片区 都市| 国产91精品最新在线播放| 婷婷久久一区| 青青草视频网站| 欧美在线播放高清精品| av片在线观看永久免费| 狠狠色伊人亚洲综合网站色| 老牛国产精品一区的观看方式| 中文字幕亚洲欧美日韩| 日韩国产在线看| 国产精品美女午夜爽爽| 欧美乱大交xxxxx潮喷l头像| 欧美国产激情一区二区三区蜜月| 国产乱淫片视频| 欧美一级高清免费| 91精品久久久久久久久久不卡| 国产美女视频免费观看下载软件| 欧美日韩国产a| 亚洲校园激情春色| 国内外成人激情免费视频|