国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

React為 Vue 引入容器組件和展示組件的教程詳解

2019-11-19 13:55:34
字體:
來源:轉載
供稿:網友

如果你使用過 Redux 開發 React,你一定聽過 容器組件(Smart/Container Components) 或 展示組件(Dumb/Presentational Components),這樣劃分有什么樣的好處,我們能否能借鑒這種劃分方式來編寫 Vue 代碼呢?這篇文章會演示為什么我們應該采取這種模式,以及如何在 Vue 中編寫這兩種組件。

為什么要使用容器組件?

假如我們要寫一個組件來展示評論,在沒聽過容器組件之前,我們的代碼一般都是這樣寫的:

components/CommentList.vue

<template> <ul> <li v-for="comment in comments"  :key="comment.id" >  {{comment.body}}―{{comment.author}} </li> </ul></template><script>export default { name: 'CommentList', computed: { comments () {  return this.$store.state.comments } }, mounted () { this.$store.dispatch('fetchComments') }}</script>

store/index.js

import Vue from 'vue';import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({ state: { comments: [], }, mutations: { setComments(state, comments) {  state.comments = comments; }, }, actions: { fetchComments({commit}) {  setTimeout(() => {  commit('setComments', [   {   body: '霸氣側漏',   author: '雷叔',   id: 1123,   },   {   body: '機智如我',   author: '蕾妹',   id: 1124,   },  ]);  }); }, },});

export default store;

這樣寫看起來理所當然,有沒有什么問題,或者可以優化的地方呢?

有一個很顯而易見的問題,由于 CommentList.vue 與 項目的 Vuex store 產生了耦合,導致脫離當前的項目很難復用。

有沒有更好的組件的組織方式,可以解決這個問題呢?是時候了解下 React 社區的容器組件的概念了。

什么是容器組件

在 React.js Conf 2015 ,有一個 Making your app fast with high-performance components 的主題介紹了容器組件。

 

容器組件專門負責和 store 通信,把數據通過 props 傳遞給普通的展示組件,展示組件如果想發起數據的更新,也是通過容器組件通過 props 傳遞的回調函數來告訴 store。

由于展示組件不再直接和 store 耦合,而是通過 props 接口來定義自己所需的數據和方法,使得展示組件的可復用性會更高。

容器組件 和 展示組件 的區別


展示組件
容器組件
作用 描述如何展現(骨架、樣式) 描述如何運行(數據獲取、狀態更新)
直接使用 store
數據來源 props 監聽 store state
數據修改 從 props 調用回調函數 向 store 派發 actions

來自 Redux 文檔 https://user-gold-cdn.xitu.io/2018/5/2/1631f590aa5512b7

用 容器組件/展示組件 模式改造上面的例子

針對最初的例子,如何快速按照這種模式來劃分組件呢?我們主要針對 CommentList.vue 進行拆分,首先是基本的概要設計:

概要設計

展示組件

components/CommentListNew.vue 這是一個新的評論展示組件,用于展示評論
comments: Array prop 接收以 { id, author, body } 形式顯示的 comment 項數組。
fetch() 接收更新評論數據的方法
展示組件只定義外觀并不關心數據來源和如何改變。傳入什么就渲染什么。

comments、fetch 等這些 props 并不關心背后是否是由 Vuex 提供的,你可以使用 Vuex,或者其他狀態管理庫,甚至是一個 EventBus,都可以復用這些展示組件。

同時,可以利用 props 的類型和驗證來約束傳入的內容,比如驗證傳入的 comments 是否是一個含有指定字段的對象,這在之前混合組件的情況是下是沒有的,提高了代碼的健壯性。

容器組件

containers/CommentListContainer.vue 將 CommentListNew 組件連接到 store
容器組件可以將 store 對應的 state 或者 action 等封裝傳入展示組件。

編碼實現

Talk is cheap, show me the code!
components/CommentListNew.vue

這個文件不再依賴 store,改為從 props 傳遞。

值得注意到是 comments 和 fetch 分別定義了 type 、default 和 validator,用以定義和驗證 props。

<template> <ul> <li v-for="comment in comments"  :key="comment.id" >  {{comment.body}}―{{comment.author}} </li> </ul></template><script>export default { name: 'CommentListNew', props: { comments: {  type: Array,  default () {  return []  },  validator (comments) {  return comments.every(comment =>   'body' in comment &&   'author' in comment &&   'id' in comment  )  } }, fetch: {  type: Function,  default: () => {} } }, mounted () { this.fetch() }}</script>

containers/CommentListContainer.vue

容器組件的職責

通過 computed 來獲取到狀態更新,傳遞給展示組件

通過 methods 定義回調函數,回調函數內部調用 store 的 dispatch 方法,傳遞給展示組件

<template> <CommentList :comments="comments" :fetch="fetchComments" ></CommentList></template><script>import CommentList from '@/components/CommentListNew'export default { name: 'CommentListContainer', components: { CommentList }, computed: { comments () {  return this.$store.state.comments } }, methods: { fetchComments () {  return this.$store.dispatch('fetchComments') } }}</script>

使用 @xunlei/vuex-connector 實現容器組件

上面演示的容器組件的代碼非常簡單,實際上如果直接投入生產環境,會產生一些問題。

手動實現容器組件存在的不足

代碼比較繁瑣

在上面的例子中,每次傳遞一個 state 都要定義一個 computed,每傳遞一個 mutation 或者 action 都需要定一個方法,而且還要注意這個方法的參數要透傳過去,同時還要處理返回值,比如異步的 action 需要返回 promise 的時候,定義的這個 method 也得把 action 的返回值返回出去。

無法透傳其他 props 給展示組件

比如展示組件新增了一個 prop 叫做 type,可以傳遞一個評論的類型,用來區分是熱門還是最新,如果用上面的容器實現方式,首先需要在容器組件這層新增一個 prop 叫做 type 接受外部傳來的參數,然后在展示組件內部同樣定義一個 叫做 type 的 prop,然后才能傳遞下去。

需要透傳的 prop 必須定義兩遍,增加了維護的成本。

<CommentListContainer type="熱門"></CommentListContainer><CommentList :fetch="fetchComments" :comments="comments" :type="type" ></CommentList>

容器組件無法統一進行優化

每一個手動實現的容器組件實質上代碼邏輯非常近似,但是沒有經過同一層封裝,如果目前實現的容器組件存在一些性能優化的地方,需要每個容器組件都進行統一的修改。

無法控制展示組件不去獲取 store

因為容器組件是通過 this.$store 獲取 store 的,展示組件內部實質上也可以直接跟 store 通信,如果沒有約束,很難統一要求展示組件不得直接和 store 通信。

使用 @xunlei/vuex-connector

@xunlei/vuex-connector 借鑒了 react redux 的 connect 方法,在 vuex 基礎上進行的開發。

有以下幾個特點:

代碼非常簡潔

下面是上面例子中手動實現的容器組件的改造版本:

comonents/ConnectCommentListContainer.vue

<script>import CommentListNew from '@/components/CommentListNew'import { connector } from '@/store'export default connector.connect({ mapStateToProps: {  comments: (state) => state.comments }, mapActionToProps: {  fetch: 'fetchComments' }})(CommentListNew)</script>


通過 connector 的 connnect 方法,傳入要映射的配置,支持 mapStateToProps, mapGettersToProps, mapDispatchToProps, mapCommitToProps 這四種,每一種都是只要配置一個簡單的 map 函數,或者字符串即可。

然后在返回的函數中傳入要連接的展示組件,是不是非常的簡潔,同時借鑒了 redux 優雅的函數式風格。

問題來了,connector 是什么?

connector 實際上是一個能獲取到 store 實例的連接器,可以在初始化 vuex store 的時候進行初始化。

import Vue from 'vue';import Vuex from 'vuex';import VuexConnector from '@xunlei/vuex-connector';Vue.use(Vuex);const store = new Vuex.Store({ // your store});export const connector = new VuexConnector(store);export default store;

一個 Vue 程序實際上只需要初始化一次即可。

支持透傳其他 props 給展示組件

VuexConnector 實現的時候采用了函數式組件( functional: true )

函數式組件是無狀態 (沒有響應式數據),無實例 (沒有 this 上下文)。

在作為包裝組件時函數式組件非常有用,比如,當你需要做這些時:

程序化地在多個組件中選擇一個

在將 children, props, data 傳遞給子組件之前操作它們。

另外,函數式組件只是一個函數,所以渲染開銷也低很多。然而,對持久化實例的缺乏也意味著函數式組件不會出現在 Vue devtools 的組件樹里。

因此需要透傳的 props 可以直接透傳,需要通過 map 方式從 store 里進行獲取的 props 直接會根據配置生成。

統一封裝方便后續統一優化

VuexConnector.connect 方法將本來需要重復做的事情進行了抽象,也帶來了后期進行統一優化和升級的便利。

可以控制展示組件無法直接與 store 通信

VuexConnector 不依賴 this.$store,而是依賴初始化傳入的 store 實例,容器組件可以用 connect 將展示組件與 store 進行連接。

由于不依賴 this.$store,我們在程序入口 new Vue 的時候,就不需要傳入 store 實例了。

比如,之前我們是通過下面的方式進行初始化:

import Vue from 'vue';import App from './App';import store from './store';new Vue({ el: '#app', components: {App}, template: '<App/>', store,});

使用了 VuexConnector 之后,在最初 new Vue 的時候就不需要也最好不要傳遞 store 了,這樣就避免了 this.$store 泛濫導致代碼耦合的問題。

引入容器組件/展示組件模式帶來的好處

可復用性

容器組件/展示組件的劃分,采用了單一職責原則的設計模式,容器組件專門負責和 store 通信,展示組件只負責展示,解除了組件的耦合,可以帶來更好的可復用性。

健壯性

由于展示組件和容器組件是通過 prop 這種接口來連接,可以利用 props 的校驗來增強代碼的可靠性,混合的組件就沒有這種好處。

另外對 props 的校驗可以采取一下幾種方式:

Vue 組件 props 驗證

可以驗證 props 的類型,默認可以校驗是否是以下類型:

  • String
  • Number
  • Boolean
  • Function
  • Object
  • Array
  • Symbol

如果你的 props 是類的一個實例,type 也可以是一個自定義構造器函數,使用 instanceof 檢測。

如果還是不滿足需求,可以自定義驗證函數:

// 自定義驗證函數propF: { validator: function (value) {  return value > 10 }}

TypeScript 類型系統

Vue 組件 props 驗證對于對象或者其他復雜的類型校驗還是不太友好,所以很多人也推薦大家的 props 盡量采取簡單類型,不過如果你有在用 TypeScript 開發 Vue 應用,可以利用 TypeScript 靜態類型檢查來聲明你的 props 。

@Componentexport default class Hello extends Vue { @Prop info: IHelloInfo; // 這里可以用你自定義的 interface}

可測試性

由于組件做的事情更少了,使得測試也會變得容易。

容器組件不用關心 UI 的展示,只關心數據和更新。

展示組件只是呈現傳入的 props ,寫單元測試的時候也非常容易 mock 數據層。

引入容器組件/展示組件模式帶來的限制

學習和開發成本

因為容器組件/展示組件的拆分,初期會增加一些學習成本,不過當你看完這篇文章,基本上也就入門了。

在開發的時候,由于需要封裝一個容器,包裝一些數據和接口給展示組件,會增加一些工作量, @xunlei/vuex-connector 通過配置的方式可以減輕不少你的工作量。

另外,在展示組件內對 props 的聲明也會帶來少量的工作。

總體來說,引入容器組件/展示組件模式投入產出比還是比較值得的。

總結

以上所述是小編給大家介紹的React為 Vue 引入容器組件和展示組件的教程詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 泸水县| 都兰县| 丘北县| 营山县| 龙山县| 宝清县| 通山县| 祁东县| 洛阳市| 新龙县| 麦盖提县| 金坛市| 毕节市| 五大连池市| 西畴县| 西青区| 苍梧县| 通辽市| 安化县| 青铜峡市| 雷波县| 遂溪县| 梓潼县| 延长县| 清水河县| 民和| 枣庄市| 尚义县| 乐平市| 白城市| 郎溪县| 涞源县| 满城县| 宁蒗| 清丰县| 天津市| 章丘市| 清流县| 霞浦县| 寻乌县| 鱼台县|