1. 程式人生 > >深入探討前端元件化開發

深入探討前端元件化開發

開發十年,就只剩下這套架構體系了! >>>   

前端元件化開發,已經有多年的歷史了,不管是服務端渲染,還是前端SPA,都有了比較成熟的元件化開發的方案。 隨著元件化開發的普及,前端社群中貢獻了很多不錯的前端元件,都提供開箱即用的方案,使得更好的發揮元件化的優勢。 前端團隊內,如果有人對前端元件化的理解不夠深入,就不能開發出好的元件,會給專案的維護帶來更大的成本。


這幾年,從陷入 “React、Vue 和 Angular 哪個效能好?”的爭論,到現在的各個框架(庫)的生態越來越完善,討論效能差距已經沒有價值了。而國內的前端娛樂圈,最火的就是 React 和 Vue 了,而 Angular 由於歷史原因,在國內的佔有率確實不高。

隨著前端生態 jade、less、scss、typeScript 和 webpack 等工具的完善,前端的元件化開發效率已經有了很大的提升。

特別是像 Ant Design、Element UI、iView 這些優秀的前端元件庫的流行,更是將元件化開發發揮到了極致。開發一個前端頁面已經變得非常的高效,特別是在做管理系統的頁面,腳手架搭建、新增依賴包、配置路由、建立頁面、引入元件,很快的就可以構建一個系統。

如果你需要 SEO,React 和 Vue 的 SSR 框架 Next.js 和 Nuxt.js 更是提供了開箱即用的整合方案,也使開發“同構頁面系統“(Google It)變得更加簡單。

下面切入正題,深入探討下前端元件。

什麼是前端元件化開發

首先,我們要搞明白什麼是前端元件化開發?

你應該遇到過,將一個頁面的幾百行,甚至上千行的程式碼邏輯寫在一個 js 檔案中的情況。通常這種程式碼都很難讀下去,更別說要維護起來,新增新功能,移除一些老功能了,因為你不知道改動一個地方,會不會出現意想不到的 bug。

這個時候,你就需要利用元件化開發,拆分功能,封裝元件,單獨維護。 //在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力 現代化前端框架通常都是實現 MVVM 的方案,資料層(M)和 檢視層(V)相互連線,同時變更,使得頁面互動保持高度的一致性。

如果你熟悉 Java,Python,Go 等後端開發語言,你應該對 package (包)的概念很熟悉,前端的元件化在概念上與後端的 package 很相似,只不過前端的元件涉及到更多的是展示和互動方面的邏輯。當然,前端元件與後端架構的微服務概念類似,可以理解成一個元件就是一個服務元件,只提供某個服務。

前端元件化開發,就是將頁面的某一部分獨立出來,將這一部分的 資料層(M)、檢視層(V)和 控制層(C)用黑盒的形式全部封裝到一個元件內,暴露出一些開箱即用的函式和屬性供外部元件呼叫。

一個前端元件,包含了 HTML、CSS、JavaScript,包含了元件的模板、樣式和互動等內容,基本上涵蓋了元件的所有的內容,外部只要按照元件設定的屬性、函式及事件處理等進行呼叫即可,完全不用考慮元件的內部實現邏輯,對外部來說,元件是一個完全的黑盒。

元件可以多層封裝,通過呼叫多個小元件,最後封裝成一個大元件,供外部呼叫。比如:一個 Input 輸入框 是一個元件,一個 Select下拉選擇框 也是一個元件,可以用 form 在這兩個元件上包裝一層,就是一個 Form 的元件

有一些比較常用的前端元件,像 vue-router,vuex,react-router,redux,mobx 等,都是基於 Vue 和 React 的元件,它們只專注於 路由、狀態儲存 的工作,並且把這些事情做好。

只要利用好元件化開發,開發一個頁面,就像是搭積木一樣,將各個元件拼接到一起,最後融合到一起,就是一個完整的系統。

元件化開發的優點

說到底,前端的元件化開發,可以很大程度上降低系統各個功能的耦合性,並且提高了功能內部的聚合性。這對前端工程化及降低程式碼的維護來說,是有很大的好處的。

耦合性的降低,提高了系統的伸展性,降低了開發的複雜度,提升開發效率,降低開發成本。 //在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力 元件封裝的好,加班也少了,bug 也少了,就有更多時間喝喝咖啡、打打農藥了。:)

怎麼設計一個元件

既然前端元件化開發這麼好,那要怎麼設計一個好的元件呢?

經過多次實踐,總結了一些元件設計時的要點。

專一

要想設計一個好的元件,元件也需要專一

設計元件要遵循一個原則:一個元件只專注做一件事,且把這件事做好

一個功能如果可以拆分成多個功能點,那就可以將每個功能點封裝成一個元件,當然也不是元件的顆粒度越小越好,只要將一個元件內的功能和邏輯控制在一個可控的範圍內即可。

舉個例子。頁面上有一個 Table 列表和一個分頁控制元件,就可以將 Table 封裝為一個元件,分頁控制元件 封裝成一個元件,最後再把 Table元件 和 分頁元件 封裝成一個元件。Table 元件還可以再拆分成多個 table-column 元件,及展示邏輯等。

可配置性

一個元件,要明確它的輸入和輸出分別是什麼。

元件除了要展示預設的內容,還需要做一些動態的適配,比如:一個元件內有一段文字,一個圖片和一個按鈕。那麼字型的顏色、圖片的規則、按鈕的位置、按鈕點選事件的處理邏輯等,都是可以做成可配置的。

要做可配置性,最基本的方式是通過屬性向元件傳遞配置的值,而在元件初始化的宣告週期內,通過讀取屬性的值做出對應的顯示修改。還有一些方法,通過呼叫元件暴露出來的函式,向函式傳遞有效的值;修改全域性 CSS樣式;向元件傳遞特定事件,並在元件內監聽該事件來執行函式等。

在做可配置性時,為了讓元件更加健壯,保證元件接收到的是有效的屬性、函式接收到的是有效的引數,需要做一些校驗。

一. 屬性的值的校驗

對屬性的值進行校驗,一般要考慮以下幾個方面。

1.屬性值的型別是否是有效的。如果某個屬性要求傳遞一個數組,那麼傳遞過來的值不是陣列時,就要丟擲異常,並給出對應的提示。

2.屬性是否是必填的。有的屬性的值,是元件內不可缺少的時,就要是必填的,在元件初始化時要做是否傳遞的檢查,如果沒有傳遞,則需要丟擲異常,並給出相應的提示。如果屬性不是必填的,可以設定一個預設值,當屬性沒有被設定時,就使用預設值。

得益於 React、Vue 內部實現的屬性檢查,且這些屬性檢查會在元件初始化階段預設執行,你可以很容易的給元件設定屬性的檢查。React 中可以使用 React.PropTypes 進行型別檢查設定,Vue 中只需要給元件設定 props 即可。 //在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力 在 React 中進行型別檢查:

// title.jsx (Title元件)
  import React, { Component, PropTypes } from 'react';
 
  export default class Title extends Component {
    constructor(props) {
      super(props);
    }
 
    static propTypes = {
      title: PropTypes.string.isRequired
    }
 
    render() {
      const { title } = this.props;
 
      return (
        <p>{ title }</p>
      )
    }
  } 

在 Vue 中進行型別檢查:

// title.vue (Title元件)
  <template>
    <p>{{ title }}</p>
  </template>
 
  <script>
    export default {
      props: {
        title: {
          type: String,
          required: true
        }
      }
    }
  </script> 

二. 函式的引數的校驗

函式的引數校驗,只要按照傳統的方法進行校驗即可。在函式內部頂部判斷引數的值和型別,如果不滿足要求,則丟擲異常,並給出相應的提示。

判斷一個函式的第一個必填,且為 String 格式

// ES6 語法
  changeTitle(title) {
    if (typeof title !== 'string') {
      throw new Error('必須傳入一個 title,才能修改 title。')
    }
    // 滿足條件,可以進行修改
    this.title = title   // vue 語法
    this.setState({      // react 語法,修改state的值
      title
    })
  } //在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力

##生命週期

一個元件,需要明確知道在生命週期的不同階段做該做的事。

初始化階段,讀取屬性的值,如果需要做資料和邏輯處理的話,在這個階段進行。

屬性值變化時,如果屬性發生變化,且需要對變化後的資料進行處理的話,在這個階段進行處理。

元件銷燬階段,如果元件已經建立了一些可能會對系統產生一些副作用的東西,可以在這個階段進行清除。比如 timeInterval、timeout 等。

如果元件在渲染的時候報錯,需要展示錯誤資訊。React v16 中提供了 componentDidCatch 生命週期函式,Vue v2.5 中提供了 errorCaptured 的鉤子函式。

React 中提供了一些生命週期函式:componentWillMount,componentDidMount,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,componentDidUpdate,render,componentWillUnmount,componentDidCatch(React v16)。

Vue 中提供了一些生命週期函式:beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed,errorCapture,errorCaptured(Vue v2.5)。

每個生命週期的具體用法,請參考官方詳細文件。

##事件傳遞

Vue 中傳遞事件很簡單,只需要在子元件內使用 this.$emit(‘event1’) 即可向外傳遞一個事件 event1,在父元件呼叫該子元件時,只需要監聽 event1 事件,並給出對應事件處理邏輯即可。

Vue中事件傳遞

Vue子元件定義 child-component.vue

Vue.component('child-component', {
  methods: {
    emitEvent() {
      this.$emit('event1', 'This is a event.')
    }
  },
  mounted() {
    this.emitEvent()
  }
}) //在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力

Vue 父元件呼叫子元件

<template>
  <div>
    <child-component @event1="eventHandler" />
  </div>
</template>
 
<script>
  import childComponent from './child-component'
  export default {
    components: {
      childComponent
    },
    methods: {
      eventHandler(event) {
        console.log(event)
      }
    }
  }
</script> 

而在 React 中,官方沒有給出元件間的事件傳遞解決方案,這也是 React 中比較坑的一點。不過,還是可以使用其他方式來實現。

React 中,父元件可以使用 props 向子元件傳值,而子元件向父元件傳值,需要在父元件內定義函式並通過屬性傳遞給子元件,在子元件內通過呼叫該屬性對應的函式,傳入引數,傳遞給父元件內的函式,並在父元件的該函式中做邏輯的處理。

React 子元件定義 child-component.js

class ChildComponent extends React.Components {
  render() {
    return (
      <button onClick={ 
        () => {
          this.props.clickHandler('This is a click')
        }
      }></button>
    )
  }
} 

React 父元件呼叫子元件

import ChildComponent from './child-component'
//在此我向大家推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提升思維能力 
class ParentComponent extends React.Components {
  clickHandler(message) {
    console.log(message)
  }
 
  render() {
    return (
      <child-component 
        clickHandler={ this.clickHandler.bind(this) } 
        />
    )
  }
} 

前端元件化開發的實踐,是一個很長的過程,堅持並持續優化,帶動系統整體的優化。

結語

感謝您的觀看,如有不足之處,