前端組件化開發,已經有多年的歷史了,不論是服務端渲染,仍是前端SPA,都有了比較成熟的組件化開發的方案。 隨着組件化開發的普及,前端社區中貢獻了不少不錯的前端組件,都提供開箱即用的方案,使得更好的發揮組件化的優點。 前端團隊內,若是有人對前端組件化的理解不夠深刻,就不能開發出好的組件,會給項目的維護帶來更大的成本。css
這幾年,從陷入 「React、Vue 和 Angular 哪一個性能好?」的爭論,到如今的各個框架(庫)的生態愈來愈完善,討論性能差距已經沒有價值了。而國內的前端娛樂圈,最火的就是 React 和 Vue 了,而 Angular 因爲歷史緣由,在國內的佔有率確實不高。前端
隨着前端生態 jade、less、scss、typeScript 和 webpack 等工具的完善,前端的組件化開發效率已經有了很大的提高。vue
特別是像 Ant Design、Element UI、iView 這些優秀的前端組件庫的流行,更是將組件化開發發揮到了極致。開發一個前端頁面已經變得很是的高效,特別是在作管理系統的頁面,腳手架搭建、添加依賴包、配置路由、建立頁面、引入組件,很快的就能夠構建一個系統。react
若是你須要 SEO,React 和 Vue 的 SSR 框架 Next.js 和 Nuxt.js 更是提供了開箱即用的集成方案,也使開發「同構頁面系統「(Google It)變得更加簡單。webpack
下面切入正題,深刻探討下前端組件。web
首先,咱們要搞明白什麼是前端組件化開發?vue-router
你應該遇到過,將一個頁面的幾百行,甚至上千行的代碼邏輯寫在一個 js 文件中的狀況。一般這種代碼都很難讀下去,更別說要維護起來,添加新功能,移除一些老功能了,由於你不知道改動一個地方,會不會出現意想不到的 bug。vuex
這個時候,你就須要利用組件化開發,拆分功能,封裝組件,單獨維護。 //在此我向你們推薦一個前端全棧開發交流圈:619586920 突破技術瓶頸,提高思惟能力 現代化前端框架一般都是實現 MVVM 的方案,數據層(M)和 視圖層(V)相互鏈接,同時變動,使得頁面交互保持高度的一致性。redux
若是你熟悉 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) } /> ) } }
前端組件化開發的實踐,是一個很長的過程,堅持並持續優化,帶動系統總體的優化。
結語
感謝您的觀看,若有不足之處,歡迎批評指正。