超詳細 ElementUI 源碼分析 —— Select(模板篇)

前言

接下來是一段很痛苦的時間!!html

今天詳細分析 Select 源碼,在看到源碼足足有九百行時,我整我的都是懵的,這是迄今爲止讀的最多的一篇源碼,大概瀏覽了以後發現它裏面有不少不少的知識點,光導入的模塊就有 16 個,裏面包含着各類組件、混入以及工具函數,因此鑑於本文篇幅有限,我打算分兩部分來寫,分別爲:「模板篇」和「方法篇」,模板篇主要是分析 Select 模板以及一些比較簡單的屬性,方法篇着重分析 methods 裏面的各類方法,如今就讓咱們一塊兒來看一下 Select 組件到底作了哪些事,建議先點贊/收藏再觀看。前端

用法和功能

瞭解一個 UI 組件首先要從它的功能入手,只有瞭解了組件的具體功能以後纔會知道爲何要封裝?怎樣封裝?在 ElementUI 官方文檔上有詳細使用功能及方法,不熟悉的同窗能夠先研究一下。Select 主要有如下用法:vue

  • 基礎用法:就是一個基本的下拉框,下拉框的位置會根據頁面上空間大小而選擇性地出如今上方或下方
  • 禁用選項:能夠禁用某一個選項或者整個下拉框
  • 清空按鈕:這個沒必要多說,input 組件也有這個功能
  • 多選:選擇多個下拉選項,選中的選項能夠以 tag 形式展現或者合併爲一段
  • 自定義下拉選項:下拉選項能夠是自定義的樣式
  • 分組:能夠對下拉選項按照某些類別分組
  • 搜索:與服務器結合,進行搜索聯想
  • 建立條目:能夠經過輸入建立一個 tag 顯示在輸入框裏

能夠看到,一個 Select 的功能有這麼多,基本上把咱們平常需求中須要使用到的功能所有考慮進來了,既然功能這麼多,那麼封裝起來確定就特別麻煩了,畢竟人家九百行代碼不是白寫的!!node

理清了功能這對於咱們看源碼是頗有幫助的,咱們能夠根據功能來找對應的代碼實現,接下來直接上源碼:git

基本結構

el-select

看結構最好不要在源碼裏直接看,這樣很容易懵圈,首先咱們看一下最基礎的用法渲染出來的 HTML 結構:github

從這個比較簡單的結構裏能夠很容易的看出來主體是由 input 和 後綴元素組成,如今咱們一點一點分析它的模板結構:

首先最外層,是一個類名爲 el-selectdiv,若是聲明瞭 size,還會根據 size 添加 el-select-size 類,裏面包裹着的是 el-select__tagsdiv,咱們暫時先不看與 tags 相關的,先看一下 el-inputweb

這個 el-input 渲染出來就是 ElementUI 封裝的 input 組件,若是你沒有看過,能夠先移步超詳細 ElementUI 源碼分析系列仔細閱讀一下 input 源碼。數組

<el-input>
  <template slot="prefix" v-if="$slots.prefix">
    <slot name="prefix"></slot>
  </template>
  <template slot="suffix">
    <i v-show="!showClose" :class="['el-select__caret', 'el-input__icon', 'el-icon-' + iconClass]"></i>
    <i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close" @click="handleClearClick"></i>
  </template>
</el-input>
複製代碼

input 組件裏包含了兩個插槽,前綴和後綴。prefix 插槽用於展現 Select 組件頭部內容(若是有的話),而 suffix 是用來顯示後面的清空按鈕和小箭頭的。能夠看到這裏有一個細節就是小箭頭用了 v-show 而清空按鈕用了 v-if,這裏簡單介紹一下二者的區別:瀏覽器

  • 二者都適用於切換 DOM 元素的顯示和隱藏
  • v-show 操做的是 DOM 元素的 display: none 屬性,不會改變 DOM 樹的結構
  • v-if 操做的是 DOM 樹,直接加入或者刪除控制的 DOM 元素
  • v-if 在「初始條件爲假時不會渲染」,直到第一次爲真時纔開始渲染
  • v-if 有更高的「切換開銷」,v-show 有更高的「初始渲染開銷
  • 也就是說 v-show 適用於須要頻繁操做 DOM,而 v-if 則用於你不會去頻繁操做它的 DOM 結構的時候

它所觸發的事件也留到下一期再和你們分析,記得準時閱讀。服務器

因爲後面的結構用到了不少之前沒有看過的組件,因此接下來先對 Select 引用的組件進行分析。

el-select-menu

首先是 el-select-menu,查看導入的模塊可知使用的是 select-dropdown.vue 文件,進去瞅一眼。這個組件的結構很簡單,只有一個 div,裏面包含一個插槽,這個 divclass"el-select-dropdown el-popper",它渲染到頁面上是下面這種結構:

能夠看到這是一個下拉框的結構,它是被添加到 body 節點上了,經過 position 定位到了輸入框的上方或者下方,而且能夠根據輸入框的位置進行調整。這個組件自己不是很複雜,可是它混入了 vue-popper,而在 vue-popper 中又引入了 popper-manager,同時 vue-popper 又引入了第三方的定位庫 popper.js,因此這裏面的關係很複雜,看下面這張圖:

先來講一下每個模塊的做用:

  • vue-popper:用於管理組件的彈出框,何時建立、在哪一個位置建立、何時又須要銷燬以及怎麼銷燬
  • popup:主要是作彈出框的打開和關閉操做
  • popup-manager:用來管理頁面中全部的 modal 層
  • popper.js:第三方庫,主要是用來定位彈出框的

對於 popper.js 的分析參考了這篇 CSDN 博客

因爲每一個模塊的內容都很是多,這裏只挑和 Select 組件有關的分析一下,若是想看具體的,能夠移步個人 github 上查看。

咱們再回過頭來看 Select 組件,el-select-menu 中包裹着 el-scrollbar 用於下拉框的內容滾動,那麼接下來的內容就是 el-scrollbar 的分析了。

el-scrollbar

先來看一下入口文件 index.js 它導入的 ScrollBarsrc/main,這纔是 el-scrollbar 的組件的文件,官方說了這個文件整個思路是參考了 gemini-scrollbar,我去對比了一下,發現思路果真同樣,連命名都是同樣的,不過別人的作了兼容。

這裏面導入的文件主要是:

  • utils/resize-event.jsresize 事件的綁定與解除
  • utils/scrollbar-width.js:計算滾動條的寬度
  • toObject:將數組裏面的全部對象合併到一個對象上去
  • Bar:自定義的滾動條組件

main.js/render

對於每個文件的源碼我都進行了分析,先看 main.js 裏面的源碼:

// main.js
render(h) {
  // 獲取系統自帶的滾動條的寬度
  // scrollbarWidth() 看後文
  let gutter = scrollbarWidth();
  let style = this.wrapStyle;
  // 若是滾動條存在
  if (gutter) {
    // 我以爲這地方應該是 `gutterWidth` 不過不重要了
    const gutterWith = `-${gutter}px`;
    const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`;

    if (Array.isArray(this.wrapStyle)) {
      // toObject 看後文
      style = toObject(this.wrapStyle);
      style.marginRight = style.marginBottom = gutterWith;
    } else if (typeof this.wrapStyle === 'string') {
      style += gutterStyle;
    } else {
      style = gutterStyle;
    }
  }
  // 這是最外層的 ul
  const view = h(
    this.tag,
    {
      class: ['el-scrollbar__view', this.viewClass],
      style: this.viewStyle,
      ref: 'resize'
    },
    // 子虛擬節點數組
    this.$slots.default
  );
  // ul 外層包裹的 div
  const wrap = (
    <div ref='wrap' style={style} onScroll={this.handleScroll} class={[ this.wrapClass, 'el-scrollbar__wrap', gutter ? '' : 'el-scrollbar__wrap--hidden-default' ]} > {[view]} </div>
  );
  let nodes;
  // 是否使用元素滾動條,默認是 false
  // 使用自定義的 Bar 組件
  if (!this.native) {
    nodes = [
      wrap,
      <Bar move={this.moveX} size={this.sizeWidth}></Bar>,
      <Bar vertical move={this.moveY} size={this.sizeHeight}></Bar>
    ];
  } else {
    nodes = [
      <div ref='wrap' class={[this.wrapClass, 'el-scrollbar__wrap']} style={style} > {[view]} </div>
    ];
  }
  return h('div', { class: 'el-scrollbar' }, nodes);
},
複製代碼

能夠看到,這個下拉框的滾動部分主要是使用 render 渲染函數來構建一個 DOM 結構的,整個渲染出來的結構如圖所示:

關於 li 標籤的渲染是由 el-option 完成的,稍後再分析。在渲染函數裏給外層的 wrap 綁定了一個 onscroll 事件,監聽方法在 methods 裏面定義了:

// onscroll 事件處理函數
handleScroll() {
  const wrap = this.wrap;

  // 計算出滾動條須要滾動的距離(百分比)
  this.moveY = (wrap.scrollTop * 100) / wrap.clientHeight;
  this.moveX = (wrap.scrollLeft * 100) / wrap.clientWidth;
},
複製代碼

當內部的列表滾動時,計算出滾動條須要滾動的距離,這裏是使用的百分比,而後在 Bar 組件裏使用。這個 Bar 是官方自定義的一個滾動條組件,也放在下文分析。咱們注意到組件接收了一個 native 屬性,這個屬性表示是否使用瀏覽器自帶的滾動條,默認是 false 也就是不使用,而是去使用 Bar 組件,而後把整個結構放進 h 裏交給 Vue 解析。

main.js/methods

methods 裏定義了兩個方法:

  • handleScrollonscroll 事件處理函數
  • update:當觸發 resize 事件時,改變滾動條的大小
update() {
  // 寬高百分比
  let heightPercentage, widthPercentage;
  const wrap = this.wrap;
  if (!wrap) return;

  // 求出可視區域佔內容總大小的百分比,這就是滾動條相對於內容的百分比
  heightPercentage = (wrap.clientHeight * 100) / wrap.scrollHeight;
  widthPercentage = (wrap.clientWidth * 100) / wrap.scrollWidth;

  // 滾動條的大小
  // 若是可視區域比內容總大小要小,證實須要滾動,把百分比賦值給 sizeXXX
  // 若是不須要滾動 clientHeight = scrollHeight
  this.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : '';
  this.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : '';
}
複製代碼

當下拉框組件被掛載時,調用 update 方法,值得說明的是在組件的 prop 屬性裏有一個屬性爲 noresize,這個屬性是禁止框架調整大小的,官方給的註釋是「若是 container尺寸不會發生變化,最好設置它能夠優化性能」,優化性能在後面能夠看出來,組件掛載和被銷燬前都調用了 update 方法,而頻繁調用 update 會消耗必定的性能,因此咱們不想要調整框架大小時,儘可能聲明 noresize 屬性。

mounted() {
  if (this.native) return;
  // update 須要用到更新後的 DOM,因此放在 $nextTick 裏
  this.$nextTick(this.update);
  // 若是能夠調整框架的大小,就給元素添加一個 resize 監聽事件
  !this.noresize && addResizeListener(this.$refs.resize, this.update);
},

beforeDestroy() {
  if (this.native) return;
  // 移除元素的 resize 監聽事件
  !this.noresize && removeResizeListener(this.$refs.resize, this.update);
}
複製代碼

關於 addResizeListener 方法,官方是借用了第三方包 resize-observer-polyfill 來處理 resize 事件的,ResizeObserver 是新出的 API,有很是好的性能,具體去 MDN 瞭解一下吧。

Bar

接下來咱們看 Bar 組件的調用:

<Bar move={this.moveX} size={this.sizeWidth}></Bar>,
<Bar vertical move={this.moveY} size={this.sizeHeight}></Bar>
複製代碼

傳遞了兩個或者三個參數:

  • move:水平或者垂直移動的距離
  • size:滾動條的大小
  • vertical:是不是垂直滾動條,不是就爲水平的滾動條

dom.js/on

bar.js 文件就是 Bar 組件,裏面導入了兩個工具類的對象,關於工具類的分析,我打算後期再專門寫一個專欄,這裏先簡單的看一下相關的方法。

/** * 封裝 on 方法給指定元素綁定事件 * @param {HTMLElement} element 要綁定事件的元素 * @param {String} event 要綁定的事件 * @param {Function} handler 事件觸發時執行的函數 */
export const on = (function() {
  if (!isServer && document.addEventListener) {
    return function(element, event, handler) {
      if (element && event && handler) {
        // false 表示在冒泡階段執行
        // true 表示在捕獲階段執行
        element.addEventListener(event, handler, false);
      }
    };
  } else {
    // IE 中使用 attachEvent 添加事件監聽
    return function(element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();
複製代碼

這個 on 方法是用於給指定元素綁定事件用的,仔細看源碼發現,它使用的是一個當即執行函數(IIEF),而後將執行的結果導出,它執行的結果仍是一個函數。這裏有出現兩個疑問:

  • 爲何使用當即執行函數?
  • 爲何返回一個函數呢?

先說第二個,返回一個函數明顯是使用了「閉包」,閉包的好處是可以「訪問到外層做用域」,好比說這裏的 isServer 就是 dom.js 裏面定義的變量。可是使用閉包會形成「內存泄漏」,若是不銷燬的話,咱們的內存將不堪重負,因此這裏纔會使用「當即執行函數」來消除閉包帶來的反作用,

bar.js/render

再回過來看 Bar 組件,裏面仍然是使用了 render 函數來渲染組件的:

render(h) {
  const { size, move, bar } = this;
  return (
    <div class={ ['el-scrollbar__bar', 'is-' + bar.key] } onMousedown={ this.clickTrackHandler } > <div ref="thumb" class="el-scrollbar__thumb" onMousedown={ this.clickThumbHandler } style={ renderThumbStyle({ size, move, bar }) }> </div> </div>
  );
},
複製代碼

渲染出來的結構在上一張圖中能夠看出來,就是兩個嵌套的 div,這兩個 div 上都綁定了 onmousedown 事件,用來處理鼠標按下的事件,在 style 中還有一個 renderThumbStyle 函數,咱們先看一個這個函數的做用:

export function renderThumbStyle({ move, size, bar }) {
  const style = {};
  // 平移多少距離
  const translate = `translate${bar.axis}(${ move }%)`;

  // 設置滾動條的寬/高
  style[bar.size] = size;
  style.transform = translate;
  style.msTransform = translate;
  style.webkitTransform = translate;

  return style;
};
複製代碼

每當滑動列表時,滾動條也會跟着變化,它的移動就是這個函數控制的,看一下滾動先後,它的 style 的變化:

translateY 發生了變化,也就是說它是靠平移來模擬滾動的,而具體的數值是有父組件(這裏是el-scrollbar)傳過來的。

bar.js/methods

接下來看它裏面的幾個方法,都是跟事件綁定有關的:

// 鼠標按鈕在 滾動條上 被按下時的事件處理方法
clickThumbHandler(e) {
  // prevent click event of right button
  // ctrlKey 事件屬性可返回一個布爾值,指示當事件發生時,Ctrl 鍵是否被按下並保持住
  // e.button = 2 表示鼠標右鍵
  if (e.ctrlKey || e.button === 2) {
    return;
  }
  this.startDrag(e);
  this[this.bar.axis]
    = (e.currentTarget[this.bar.offset] 
    - (e[this.bar.client]
    - e.currentTarget.getBoundingClientRect()[this.bar.direction]));
},
複製代碼

若是點擊的時候按下了 Ctrl 或者按下的是鼠標右鍵直接中止事件的執行,按下時執行拖動方法 startDrag

// 點擊並拖拽滾動條
startDrag(e) {
  // 拖動的時候當前元素剩下的監聽函數將不會執行
  e.stopImmediatePropagation();
  this.cursorDown = true;

  // 給 document 綁定鼠標移動事件 和 鼠標按鈕擡起事件
  on(document, 'mousemove', this.mouseMoveDocumentHandler);
  on(document, 'mouseup', this.mouseUpDocumentHandler);
  // 禁止文字被選中
  // 參考 https://www.jianshu.com/p/701cc19d2c5a
  document.onselectstart = () => false;
}
複製代碼

解釋一下 e.stopImmediatePropagation() 方法,平時咱們用到的比較少。當一個元素上綁定了不少同類型的事件時,它會按照綁定時的順序依次執行回調函數,可是當咱們在事件處理函數中聲明瞭這個方法時,那麼當前元素剩下的監聽函數將不會執行。

// 鼠標按鈕在 滾動條所在的區域 被按下時的事件處理方法
// 當鼠標點擊滾動條 `上方空白處` 時,滾動條向上滾動
// 當鼠標點擊滾動條 `下方空白處` 時,滾動條向下滾動
clickTrackHandler(e) {
  // 獲取點擊的位置距離元素上邊距的距離
  // 即 IE 下的 offsetX/offsetY 屬性
  const offset
  	= Math.abs(e.target.getBoundingClientRect()[this.bar.direction] 
    - e[this.bar.client]);
  // 滾動條寬/高的一半
  const thumbHalf = (this.$refs.thumb[this.bar.offset] / 2);
  const thumbPositionPercentage
 	  = ((offset - thumbHalf) * 100 / this.$el[this.bar.offset]);

  // 舉個例子
  // wrap.scrollTop = -10(假數據) * wrap.scrollHeight / 100
  this.wrap[this.bar.scroll]
    = (thumbPositionPercentage * this.wrap[this.bar.scrollSize] / 100);
}
複製代碼

這個方法用來處理滾動條外層被點擊的事件,實現了點擊空白處就能滾動的效果。 startDrag 裏對於 mousemovemouseup 事件的監聽計算方法和上述相似,這裏節省篇幅再也不介紹了。

看了鼠標事件的綁定,咱們要注意事件有綁定就必定要有取消監聽,特別是鼠標移動時的事件,在源碼裏使用的是 off 方法,具體和 on 相似。

繼續來解決咱們在 main.js/render 裏面挖下的坑 scrollbarWidthtoObject

scrollbar-width.js

這個文件很簡單,就是爲了計算出系統自帶的滾動條的寬度,我看了一下,網上基本上都是這種方法。

export default function() {
  if (Vue.prototype.$isServer) return 0;
  // 若是存在 scrollBarWidth 就直接返回
  if (scrollBarWidth !== undefined) return scrollBarWidth;

  const outer = document.createElement('div');
  outer.className = 'el-scrollbar__wrap';
  outer.style.visibility = 'hidden';
  outer.style.width = '100px';
  outer.style.position = 'absolute';
  outer.style.top = '-9999px';
  document.body.appendChild(outer);

  // 沒有滾動條時的寬度 = 元素的 offsetWidth
  const widthNoScroll = outer.offsetWidth;
  // 使外層可滾動而且出現滾動條
  outer.style.overflow = 'scroll';

  const inner = document.createElement('div');
  // 設置 width 爲 100% 時,強制子元素的內容寬度等於父元素內容寬度
  // 當子元素內容寬度大於父元素的內容寬度時,就會出現滾動條
  inner.style.width = '100%';
  outer.appendChild(inner);

  const widthWithScroll = inner.offsetWidth;
  outer.parentNode.removeChild(outer);
  scrollBarWidth = widthNoScroll - widthWithScroll;

  return scrollBarWidth;
};
複製代碼

不過這裏我以爲它計算的時候用錯了屬性,inner 不該該使用 offsetWidth 而應該使用 clientWidth,由於 offsetWidth 是包含了滾動條在內的,這樣根本計算不出來,不知道是他們寫錯了仍是我理解錯了,反正這個方法最後獲得的結果都是 0,由於我在 Mac 的谷歌瀏覽器上跑了一遍,都不能拿到滾動條寬度,設置了 overflow 也沒辦法一直讓滾動條存在,不知道在 Windows 系統上會怎麼樣。麻煩各位跑一遍這個方法,而後在評論區告訴我,謝謝。

util.js/toObject

function extend(to, _from) {
  // _from 若是是基本數據類型就不會循環
  for (let key in _from) {
    to[key] = _from[key];
  }
  return to;
}

// 把數組裏面的全部對象轉成一個對象
export function toObject(arr) {
  var res = {};
  for (let i = 0; i < arr.length; i++) {
    if (arr[i]) {
      extend(res, arr[i]);
    }
  }
  return res;
}
複製代碼

這裏面的方法很簡單,就是經過遍歷將數組上的全部對象的屬性都轉到一個新對象上來,若是數組中有基本數據類型會直接跳過。

到此,咱們的 scrollbar 已經所有分析完畢,可是還沒到撒花完結的時候,接下來還有 el-option 組件。

el-option

el-option 部分包含組件自己和一個 el-option-group 組件,option 是真正渲染下拉框列表的組件,渲染到頁面中就是 <li> 標籤,這 option 的模板結構裏有一個默認的插槽用於顯示列表項的文本內容。因爲 option 裏面不少是和 select 組件的方法有關,因此我打算放在下一篇來分析,先看一些簡單的:

// 判斷兩個參數是否相等
isEqual(a, b) {
  if (!this.isObject) {
    return a === b;
  } else {
    // 拿到 select 組件實例的 valueKey 
    // valueKey 是做爲 value 惟一標識的鍵名,綁定值爲對象類型時必填
    const valueKey = this.select.valueKey;
    return getValueByPath(a, valueKey) === getValueByPath(b, valueKey);
  }
}
複製代碼

getValueByPathutil 裏面導入的,這個方法主要是用來訪問對象指定的屬性的:

/** * 深層次訪問對象的屬性 * @param {Object} object 目標對象 * @param {string} prop 屬性名 xxx.xxx.xxx 形式 */
export const getValueByPath = function(object, prop) {
  prop = prop || '';
  // paths => [xxx, xxx, xxx]
  // object: {
  // xxx: {
  // xxx: {
  // xxx: 'xxx'
  // }
  // }
  // }
  const paths = prop.split('.');
  // 把對象保存起來,以避免改變了原有對象
  let current = object;
  let result = null;
  for (let i = 0, j = paths.length; i < j; i++) {
    const path = paths[i];
    if (!current) break;

    // 當到達指定的屬性名時,返回它的屬性值
    if (i === j - 1) {
      result = current[path];
      break;
    }
    // 不然繼續往下遍歷
    current = current[path];
  }
  return result;
};
複製代碼

可是在我看來,官方的實現還能夠簡單一點,由於既然把屬性名保存到了數組裏,用數組的方法豈不是更好,而後再用一個 while 循環幾行代碼就能實現:

function getValByPath(obj, path) {
  const paths = path.split('.')
  let res = obj
  let prop
  while ((prop = paths.shift())) {
    res = res[prop]
  }
  return res
}
複製代碼

再看一個方法:

// 鼠標移動時觸發的事件監聽方法
hoverItem() {
  // 若是當前項沒有被禁用,就設置 select 組件的 `hoverIndex`
  // 它的值爲當前列表項在 options 數組裏的索引
  if (!this.disabled && !this.groupDisabled) {
    this.select.hoverIndex = this.select.options.indexOf(this);
  }
}
複製代碼

主要是當鼠標移動到列表項上時顯示出 hover 的狀態,具體實現是放在了 select 裏面。這裏面其餘的就須要後續再分析了,真的肝不動了...至於 option-group 裏面的內容很簡單,和 option 的相差不大,本身稍微掃一眼就行,這裏就不寫了。

總結與反思

至此,咱們總算是把 select 組件的模板部分分析完了,請注意,這纔是模板部分,真正的大頭來沒有來,在 select 裏面方法佔了大多數,大概 400 行的樣子,其餘的都是一些屬性和生命週期鉤子,關於方法的等我下一篇文章,先來總結一下 select 模板:

  • 首先,整個 select 組件是由一個輸入框和一個下拉框組成
  • 輸入框使用的是 el-input 組件,下拉框使用的是 el-select-menu 組件
  • el-select-menu 是添加到 body 節點上的,經過 v-show 切換顯示與隱藏
  • 下拉框裏面包含了一個內置的滾動組件 el-scrollbar
  • 在滾動組件裏使用了自定義的滾動條 Bar 組件
  • 列表項是經過 el-optionel-option-group 渲染出來的 ul 標籤和 li 標籤

針對本文還有一些未解決的問題:

  • el-tag 組件未詳細分析
  • Vue 動畫 transition 組件未分析

最後總結一下我看 select 組件的感覺吧,看源碼加寫這篇文章足足花了我一個周的時間,還只是看了一小部分,不得不說裏面涉及到的知識點太多太多了,對於我這樣一個前端小白來講實在是難度太大了,這一個星期常常會有看不下去的時候,有一些知識點我歷來就沒有見過,經過不斷地看文檔,不斷地查博客,漸漸地進入了一種享受的狀態,你把部分代碼拿到瀏覽器中跑一下,打個斷點一會兒就能明白原理(我真的不懂如何把整個項目跑起來,我試了不少方法都沒有成功)。這期間我也看了不少 Vue 的教程和 API,也在慢慢更新我對 Vue 的認知,我相信在之後開發中使用 Vue 必定會更加熟練,由於有了實際的項目去理解概念會變得很容易,慢慢地當你的積累足夠時你就能造成一個完整的知識閉環。在看源碼的時候「多去問幾個爲何」,真的很可以幫助你理解它。另一個就是組件的設計思想,它是一個很抽象的東西,光靠你看一兩個組件是沒有辦法理解的,你須要大量的閱讀組件源碼,而且知道它解決了什麼問題,有什麼功能,爲何要這樣設計,當你看一個組件的時候能很快搞明白這三個問題那麼組件的思想你也就具備了。這是必定要大量閱讀和實踐的狀況下才能有的,光靠你看幾篇博客,看兩個組件是沒有辦法實現的。

好了,期待下一篇的文章吧,若是你喜歡這篇文章,不妨點個贊讓更多人看見,若是文章中有分析的不對的地方,歡迎指出,也能夠加個人微信【Liu472362746】一塊兒討論,同時詳細代碼我也會推送至 Github 倉庫。

傳送門

【2020.3.15】超詳細 ElementUI 源碼分析 —— Input

【2020.3.16】超詳細 ElementUI 源碼分析 —— Layout

【2020.3.18】超詳細 ElementUI 源碼分析 —— Radio

相關文章
相關標籤/搜索