Rekit 2.0 構(gòu)建基于React+Redux+React-router的可擴(kuò)展Web應(yīng)用

Rekit 2.0 出了好用的新特性!

文章翻譯來自:http://rekit.js.org/

著作權(quán)歸作者所有呵恢。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處孵睬。

前言

前端開發(fā)前景一片美好止毕,大量的框架和工具來幫助你開發(fā)復(fù)雜的項(xiàng)目。但也面臨著許多痛點(diǎn)迂猴,你需要持續(xù)學(xué)習(xí)不斷更新的框架和工具,如何利用各種框架來提高前端的開發(fā)質(zhì)量和效率是大家關(guān)注的重點(diǎn)背伴。

為了使項(xiàng)目遵循一種最佳错忱,最優(yōu)的實(shí)踐方式,本文介紹一個(gè)名為Rekit的工具挂据。

在我的理解中,一個(gè)優(yōu)秀的Web項(xiàng)目應(yīng)該考慮一下幾點(diǎn):

1. 易開發(fā):開發(fā)功能需求時(shí)儿普,無需關(guān)注復(fù)雜的技術(shù)架構(gòu)
2. 易擴(kuò)展:在擴(kuò)展新功能時(shí)崎逃,不需要對(duì)架構(gòu)進(jìn)行改動(dòng),新功能不對(duì)舊功能產(chǎn)生影響
3. 易維護(hù):架構(gòu)和代碼結(jié)構(gòu)清晰眉孩,可讀性強(qiáng)个绍,新開發(fā)人員上手快
4. 易測(cè)試:代碼模塊化強(qiáng)勒葱,易于單元測(cè)試

這幾個(gè)點(diǎn)是相互依賴相互制約的,我們可以根據(jù)項(xiàng)目的實(shí)際需求來權(quán)衡各個(gè)點(diǎn)巴柿,使項(xiàng)目能夠達(dá)到最優(yōu)的狀態(tài)凛虽。

本文將介紹Rekit是如何基于React+Redux+React-router創(chuàng)建可擴(kuò)展Web應(yīng)用的,它為創(chuàng)建React app提供了全功能解決方案广恢。

Rekit創(chuàng)建應(yīng)用程序遵循一般的最佳實(shí)踐凯旋,創(chuàng)建的應(yīng)用程序具有可擴(kuò)展性、可測(cè)試性和可維護(hù)性钉迷,優(yōu)化了應(yīng)用程序邏輯的歸類和解耦至非。您只需專注于業(yè)務(wù)邏輯,而不需花費(fèi)大量的時(shí)間來處理庫(kù)糠聪、模式荒椭、配置。

除此之外舰蟆,Rekit還提供管理項(xiàng)目的強(qiáng)大工具:

1. 命令行工具: 您可以使用這些工具來創(chuàng)建/重命名/移動(dòng)/刪除項(xiàng)目元素趣惠,比如components、 actions等身害。

2. Rekit portal:它是一個(gè)新的開發(fā)工具味悄,附帶了Rekit 2.0。它不僅提供了用于創(chuàng)建/重命名/移動(dòng)/刪除Rekit應(yīng)用的web UI题造,而且還提供了用于分析/構(gòu)建/測(cè)試Rekit應(yīng)用的測(cè)試工具傍菇,你可以將Rekit portal視為React開發(fā)的IDE。參見在線演示: https://rekit-portal.herokuapp.com

下面是兩個(gè)快速視頻演示(需要翻墻):

  1. 計(jì)數(shù)器: 花費(fèi)1分鐘創(chuàng)建一個(gè)簡(jiǎn)單計(jì)數(shù)器界赔!

  1. Reddit API的用法: 在Reddit上使用異步actions來顯示最新的react.js主題.

開始

嘗試使用Rekit最簡(jiǎn)單的方式是創(chuàng)建一個(gè)Rekit APP并且玩轉(zhuǎn)它丢习,僅僅3步:

1. 安裝Rekit

npm install -g rekit

2. 創(chuàng)建app

$ rekit create my-app
$ cd my-app
$ npm install

3. 運(yùn)行

$ npm start

進(jìn)入welcome page

應(yīng)用程序應(yīng)該在幾秒鐘內(nèi)啟動(dòng),你可以輸入下面URL訪問:http://localhost:6075

如果一切正常淮悼,您應(yīng)該能夠看到如下的welcome page:

Rekit Demo

頁面由3部分組成:

1. 一個(gè)簡(jiǎn)單的導(dǎo)航組件咐低。它讀取整個(gè)應(yīng)用程序的路由配置,并生成指向不同頁面的鏈接袜腥。

2. 使用同步actions的計(jì)數(shù)器演示见擦。通過示例,您可以快速地看到component羹令、actions和reducers三者是怎樣協(xié)同工作的鲤屡。

3. 通過Reddit來演示獲取reactjs最新主題拐揭。這僅僅是來自官方Redux網(wǎng)站的例子:https://redux.js.org/docs/advanced/ExampleRedditAPI.html.
它體現(xiàn)了Redux應(yīng)用的異步actions冤寿。但Rekit版本使用每個(gè)action一個(gè)獨(dú)立文件的模式艳狐,增加了錯(cuò)誤處理篮幢,這是程序?qū)嵺`開發(fā)的共同要求反镇。

試用Rekit portal

Rekit portal是裝載在Rekit 2.0上的全新開發(fā)工具劳景。當(dāng)一個(gè)Rekit APP啟動(dòng)時(shí)巷嚣,Rekit portal也會(huì)自動(dòng)啟動(dòng)费什,本地默認(rèn)啟動(dòng)地址為:http://localhost:6076

Rekit portal

它不僅提供了用于創(chuàng)建/重命名/移動(dòng)/刪除Rekit APP元素的Web UI,而且還提供了用于分析/構(gòu)建/測(cè)試Rekit應(yīng)用的許多測(cè)試工具翘鸭。

從網(wǎng)頁面板中滴铅,您發(fā)現(xiàn)尚未生成的測(cè)試覆蓋報(bào)告,不要猶豫就乓,點(diǎn)擊運(yùn)行測(cè)試按鈕汉匙,你會(huì)快速的發(fā)現(xiàn)Rekit portal是怎樣用超級(jí)簡(jiǎn)單的方式做這些工作的。

有關(guān)Rekit portal的更多介紹档址,在本文的后面章節(jié)會(huì)做出詳細(xì)闡述盹兢。

何處著手

Rekit會(huì)默認(rèn)創(chuàng)建一個(gè)SPA,您可以根據(jù)需要編輯根容器來定義自己的容器布局守伸,源文件位于src/features/home/App.js绎秒。

兩個(gè)簡(jiǎn)短視頻教程

在welcome page有兩個(gè)實(shí)例,它們也是Redux官方網(wǎng)站的演示∧崮。現(xiàn)在讓我們看看如何用Rekit創(chuàng)建它們见芹。

  1. 一分鐘用Rekit創(chuàng)建一個(gè)計(jì)數(shù)器

  1. 創(chuàng)建一個(gè)Reddit列表(5分鐘)。

就這樣

你已經(jīng)創(chuàng)建了你的第一Rekit APP蠢涝,并且嘗試了強(qiáng)大的Rekit工具⌒海現(xiàn)在你可以在下面章節(jié)閱讀到更多有關(guān)Rekit的細(xì)節(jié)。

Rekit app 架構(gòu)

通過Rekit和二,僅使用一行命令就可以創(chuàng)建一個(gè)React app, 而不需要其它的額外配置徘铝。該應(yīng)用程序的設(shè)計(jì)具有可擴(kuò)展性、可測(cè)試性和可維護(hù)性惯吕,以滿足實(shí)際應(yīng)用程序的要求惕它。

Rekit一個(gè)關(guān)鍵理念是將一個(gè)大的應(yīng)用程序劃分為功能塊,我們把它稱為features废登,每個(gè)feature是應(yīng)用程序的某個(gè)功能特性淹魄,它體積小、解耦性好堡距,易于理解和維護(hù)甲锡。

一個(gè)React App 已經(jīng)自然的由組件樹構(gòu)建UI。實(shí)際上通過結(jié)合Redux reducers羽戒,定義React router配置的子路由缤沦,我們也可以把整個(gè)應(yīng)用程序的store,路由配置成小塊易稠。通過這樣做疚俱,我們可以將可以把復(fù)雜應(yīng)用程序的管理變成對(duì)應(yīng)用塊的管理,請(qǐng)參閱下圖缩多,以了解整個(gè)Rekit應(yīng)用程序架構(gòu)呆奕。

每個(gè)feature是一個(gè)小的app,這很容易理解衬吆,一個(gè)大的應(yīng)用程序由許多這樣的feature組成梁钾。

目錄結(jié)構(gòu)

無論是 Flux 還是 Redux,官方提供的示例都是以技術(shù)邏輯來組織文件目錄逊抡,雖然這種方式在技術(shù)上清晰姆泻,但在實(shí)際項(xiàng)目中存在許多缺點(diǎn):

1.難以擴(kuò)展。當(dāng)應(yīng)用功能增加冒嫡,規(guī)模變大時(shí)拇勃,一個(gè) components 文件夾下可能會(huì)有幾十上百個(gè)文件,組件間的關(guān)系極不直觀孝凌。
2.難以開發(fā)方咆。在開發(fā)某個(gè)功能時(shí),通常需要同時(shí)開發(fā)組件蟀架,action瓣赂,reducer 和樣式。把它們分布在不同文件夾下嚴(yán)重影響開發(fā)效率片拍。尤其是項(xiàng)目復(fù)雜之后煌集,不同文件的切換會(huì)消耗大量時(shí)間。

如上圖所示捌省,Rekit使用一個(gè)特殊的文件夾結(jié)構(gòu)創(chuàng)建一個(gè)應(yīng)用程序苫纤。根據(jù)features將應(yīng)用程序邏輯分組,每個(gè)feature都包含自己的components纲缓、actions卷拘、路由配置等。

|-- project-name
|    |-- src
|    |    +-- common
|    |    |-- features
|    |    |    |-- home
|    |    |    |    +-- redux
|    |    |    |    |-- index.js
|    |    |    |    |-- DefaultPage.js
|    |    |    |    |-- DefaultPage.less
|    |    |    |    |-- route.js
|    |    |    |    |-- styles.less
|    |    |    |    |-- ...
|    |    |    +-- feature-1
|    |    |    +-- feature-2
|    |    +-- styles
|    --- tools
|    |    +-- plugins
|    |    |-- server.js
|    |    |-- build.js
|    |    |-- ...
|-- .eslintrc
|-- .gitignore
|-- webpack-config.js
|-- ...

概念

Rekit不封裝或更改任何React色徘、Redux和React router的API恭金,只根據(jù)最佳實(shí)踐來創(chuàng)建應(yīng)用程序,并提供管理項(xiàng)目的工具褂策。因此横腿,項(xiàng)目中沒有新的概念(也許除了feature之外),如果你能理解React, Redux 和 React router斤寂,你能夠很容易的理解Rekit項(xiàng)目耿焊。

下面介紹下這些概念是如何被Rekit管理和使用的。

Feature

Feature是項(xiàng)目的頂層概念遍搞,它是Rekit的核心理念罗侯,一個(gè)feature實(shí)際上是描述應(yīng)用程序某些功能的一種自然方式。例如溪猿,一個(gè)電子商店應(yīng)用程序通常包含以下功能:

  • customer 管理基本客戶信息钩杰。
  • product: 銷售管理產(chǎn)品纫塌。
  • category: 管理產(chǎn)品類別。
  • order:管理銷售訂單讲弄。
  • user: 系統(tǒng)管理員管理措左。
  • etc...

一個(gè)feature 通常包含多個(gè)actions、組件或路由規(guī)則避除,一個(gè)Rekit應(yīng)用總是由多個(gè)feature組成怎披。通過種方法,一個(gè)大的應(yīng)用程序可以被劃分為多個(gè)小的瓶摆、完全解耦的凉逛、易于理解的features。

當(dāng)創(chuàng)建Rekit應(yīng)用時(shí)群井,會(huì)自動(dòng)生成兩個(gè)默認(rèn)的features状飞。

1. common:它是放置所有跨feature元素(如components,actions,等)的地方。在Rekit1.0版本中蝌借,有一個(gè)單獨(dú)的components目錄來用來存放common components昔瞧。React2.0版本中。我們把它們放在一個(gè)common feature目錄中菩佑,通過這個(gè)優(yōu)化自晰,減少了conepts的數(shù)量,使目錄結(jié)構(gòu)更加簡(jiǎn)單一致稍坯。

2. home:項(xiàng)目的基本feature和應(yīng)用程序的起點(diǎn)酬荞,通常把最基本的功能放在這里,如整體布局容器瞧哟,基本應(yīng)用程序邏輯等混巧。

然而這僅僅是Rekit推薦的方式,如果您愿意勤揩,你可以重命名目錄或刪除默認(rèn)功能咧党。

為了快速領(lǐng)悟feature概念,你可以看看Rekit portal的在線演示:

https://rekit-portal.herokuapp.com

查看更多有關(guān)feature的介紹: feature oriented architecture

Component

根據(jù)Redux理論陨亡,組件(components)可以分為兩類:容器型組件(container)和展示型組件(presentational)傍衡。Rekit可以很容易的創(chuàng)建它們。

rekit add component home/Comp [-c]

-c標(biāo)志表明它是一個(gè)容器型組件负蠕,否則是一個(gè)展示型組件蛙埂。Rekit將使用不同的模板生成相應(yīng)的組件。

為了能夠使用React router遮糖,組件是一些URL模式的代表绣的,Rekit允許指定-u參數(shù):

rekit add component home/Page1 -u page1

這將在feature目錄的router.js文件中定義一個(gè)路由規(guī)則,然后你可以在本地輸入: http://localhost:6075/home/page1 來訪問組件。

Action

說的是Redux action屡江。有兩種類型的actions:同步(sync)和異步(async)芭概。正如Redux的文檔所描述的,異步action實(shí)際上不是一個(gè)新概念盼理,而是提出了異步操作的數(shù)據(jù)和工作流程谈山。

默認(rèn)情況下,Rekit使用Redux thunkasync actions宏怔。當(dāng)創(chuàng)建一個(gè)async-action時(shí),Rekit創(chuàng)建代碼樣板來處理請(qǐng)求的開始畴椰,請(qǐng)求等待臊诊,請(qǐng)求成功,請(qǐng)求失敗action類型斜脂。在reducer中維護(hù)requestpending抓艳,requestError狀態(tài)。使用下面的命令行工具帚戳,它會(huì)自動(dòng)創(chuàng)建一個(gè)async操作的樣板文件玷或,只需要在不同的技術(shù)構(gòu)件中填充應(yīng)用程序邏輯:即可。

rekit add action home/doRequest [-a]

-a標(biāo)志表明它是否是一個(gè)異步的action片任,否則同步action偏友。

或者,你可以安裝插件rekit-plugin-redux-sagaredux-saga創(chuàng)建異步actions对供。

Reducer

這里講的是Redux reducer位他。Rekit會(huì)按照官方方式為reducers重新組織代碼結(jié)構(gòu)〔。可以閱讀下面章節(jié)的one action one file 來獲取更多的介紹鹅髓。

每個(gè)action一個(gè)獨(dú)立文件

這可能是Rekit方法中最具自主性的部分,也就是:one action one file京景,把相應(yīng)的reducer放在同一個(gè)文件中窿冯。

這個(gè)想法來自Redux開發(fā)所帶來的痛點(diǎn):它幾乎總是在創(chuàng)建一個(gè)新的aciton后,寫一個(gè)reducer确徙。

就拿計(jì)數(shù)器組件的例子來說醒串,在創(chuàng)建新的action COUNTER_PLUS_ONE后,我們立即需要在reducer中來處理它米愿,官方的做法是將代碼分開厦凤,分別寫在actions.js和reducers.js兩個(gè)文件中。現(xiàn)在育苟,我們創(chuàng)建一個(gè)名為counterPlusOne.js的新文件较鼓,把下面的代碼放入里面。

import {
  COUNTER_PLUS_ONE,
} from './constants';

export function counterPlusOne() {
  return {
    type: COUNTER_PLUS_ONE,
  };
}

export function reducer(state, action) {
  switch (action.type) {
    case COUNTER_PLUS_ONE:
      return {
        ...state,
        count: state.count + 1,
      };

    default:
      return state;
  }
}

根據(jù)我的經(jīng)驗(yàn),大多數(shù)reducers 都有相應(yīng)的actions博烂,它很少在全局中使用香椎。因此,將其放在一個(gè)文件中是合理的禽篱,且使開發(fā)更容易畜伐。

這里的reducer不是一個(gè)標(biāo)準(zhǔn)的Redux reducer,因?yàn)樗鼪]有一個(gè)初始狀態(tài)躺率。它只用在feature的根reducer玛界,它通常被稱為reducer。這樣悼吱,根reducer就可以從action 模塊自動(dòng)加載它慎框。

對(duì)于異步actions,action文件可以包含多個(gè)action后添,因?yàn)樗枰幚礤e(cuò)誤笨枯。對(duì)于Rekit應(yīng)用,每個(gè)feature都包含一個(gè)命名為redux的文件夾遇西,在這個(gè)文件夾中放置actions, constants 和 reducers馅精。

如何跨功能actions?

雖然不是很常見粱檀,但是有些action是可能被多個(gè)reducer處理的洲敢。例如,對(duì)于站內(nèi)聊天功能梧税,當(dāng)收到一條新消息時(shí):

  • 如果聊天框開著沦疾,那么直接顯示新消息。

  • 否則第队,顯示一條通知提示有新的消息哮塞。

可見,NEW_MESSAGE這個(gè)action需要被不同的reducer處理凳谦。從而能夠在不同的UI組件做不同的展現(xiàn)忆畅。為了處理這類 action,每個(gè)功能文件夾下都有一個(gè) reducer.js 文件尸执,在里面可以處理跨功能的action家凯。

雖然不同 action 的 reducer 分布在不同的文件中,但它們和功能相關(guān)的 root reducer 共同操作同一個(gè)狀態(tài)如失,即同一個(gè) store 分支绊诲。因此 feature/reducer.js 具有如下的代碼結(jié)構(gòu):

import initialState from './initialState';
import { reducer as counterPlusOne } from './counterPlusOne';
import { reducer as counterMinusOne } from './counterMinusOne';
import { reducer as resetCounter } from './resetCounter';

const reducers = [
  counterPlusOne,
  counterMinusOne,
  resetCounter,
];

export default function reducer(state = initialState, action) {
  let newState;
  switch (action.type) {
    // Put global reducers here
    default:
      newState = state;
      break;
  }
  return reducers.reduce((s, r) => r(s, action), newState);
}

它負(fù)責(zé)引入不同 action 的 reducer,當(dāng)有 action 過來時(shí)褪贵,遍歷所有的 reducer 并結(jié)合需要的全局 reducer 來實(shí)現(xiàn)對(duì) store 的更新掂之。所有功能相關(guān)的 root reducer 最終被組合到全局的 Redux root reducer 從而保證全局只有一個(gè) store 的存在抗俄。

需要注意的是,每當(dāng)創(chuàng)建一個(gè)新的 action 時(shí)世舰,都需要在這個(gè)文件中注冊(cè)动雹。因?yàn)槠淠J椒浅9潭ǎ覀兺耆梢允褂霉ぞ邅碜詣?dòng)注冊(cè)相應(yīng)的代碼跟压。Rekit 可以幫助做到這一點(diǎn):當(dāng)創(chuàng)建 action 時(shí)胰蝠,它會(huì)自動(dòng)在 reducer.js 中加入相應(yīng)的代碼,既減少了工作量震蒋,又可以避免出錯(cuò)茸塞。

使用這種方式,可以帶來很多好處查剖,比如:

1.易于開發(fā):當(dāng)創(chuàng)建 action 時(shí)翔横,無需在多個(gè)文件中跳轉(zhuǎn);
2.易于維護(hù):因?yàn)槊總€(gè) action 在單獨(dú)的文件梗搅,因此每個(gè)文件都很短小,通過文件名就可以定位到相應(yīng)的功能邏輯效览;
3.易于測(cè)試:每個(gè) action 都可以使用一個(gè)獨(dú)立的測(cè)試文件進(jìn)行覆蓋无切,測(cè)試文件中也是同時(shí)包含對(duì) action 和 reducer 的測(cè)試;
4.易于工具化:因?yàn)槭褂?Redux 的應(yīng)用具有較為復(fù)雜的技術(shù)結(jié)構(gòu)丐枉,我們可以使用工具來自動(dòng)化一些邏輯《呒現(xiàn)在我們無需進(jìn)行語法分析就可以自動(dòng)生成代碼。
5.易于靜態(tài)分析:全局的 action 和 reducer 通常意味著模塊間的依賴瘦锹。這時(shí)我們只要分析功能文件夾下的 reducer.js籍嘹,即可以找到所有這些依賴。

命名規(guī)范

Rekit通過自動(dòng)轉(zhuǎn)換輸入來強(qiáng)制達(dá)到一致的命名規(guī)則弯院。無論是命令行工具或Rekit portal都要遵循以下命名規(guī)則來創(chuàng)建features辱士、 components 和 actions,如果手動(dòng)創(chuàng)建它們听绳,也應(yīng)該遵循這些規(guī)則颂碘。

  • feature:文件夾名稱: kebab case( 短橫線隔開)。例如:rekit add feature myFeature將創(chuàng)建一個(gè)文件夾椅挣,命名為my-feature头岔。
  • redux store :駝峰拼寫法。當(dāng)添加一個(gè)feature時(shí)鼠证,Rekit將把 feature reduce合并到根reducer,并以駝峰式命名作為分支名稱峡竣。
  • url path: 短橫線隔開。它將把-u MyPage參數(shù)修改映射到一個(gè)頁面量九。對(duì)于這個(gè)命令适掰,它將URL路徑定義為路由配置中的my-page
  • component:文件名和樣式文件名:駝峰式大小寫。例如:rekit add component feature/my-component將創(chuàng)建文件MyComponent.jsMyComponent.less攻谁。
  • action: 函數(shù)名: 駝峰拼寫法. 例如: rekit add action feature/my-action 將會(huì)在actions.js中創(chuàng)建一個(gè)名為 myAction的函數(shù) 稚伍。
  • action type: 常量名稱和值:upper snake case。Action types是在創(chuàng)建action時(shí)創(chuàng)建的戚宦。例如:rekit add action home/my-action 將會(huì)創(chuàng)建一個(gè) action type HOME_MY_ACTION个曙。

如您所見,任何作為參數(shù)的名稱都將被轉(zhuǎn)換受楼。因此垦搬,Rekit應(yīng)用程序的所有變量都是一致的,易于理解艳汽。

Rekit core

Rekit core提供了用于管理Rekit應(yīng)用的核心功能猴贰,常被用在Rekit命令行工具和Rekit portal中。

當(dāng)一個(gè)Rekit應(yīng)用程序被創(chuàng)建時(shí)河狐,它會(huì)自動(dòng)添加rekit-core作為一個(gè)依賴項(xiàng)米绕。當(dāng)您執(zhí)行rekit add feature f1這樣的命令時(shí),它會(huì)找到當(dāng)前安裝的rekit-core馋艺,在項(xiàng)目中添加了一個(gè)feature栅干。因此,rekit-core不是全局性的捐祠,而是獨(dú)立于項(xiàng)目的碱鳞。不同的Rekit應(yīng)用程序可以使用不同版本的rekit-core。保證了其它項(xiàng)目升級(jí)rekit-core時(shí)不會(huì)破壞現(xiàn)有的Rekit應(yīng)用程序踱蛀。

API 參考

Rekit core APIs 有著良好地模塊化和文檔化窿给,是創(chuàng)建定制Rekit插件的基礎(chǔ)。

您可以查看API文檔:http://rekit.js.org/api率拒。

您可以根據(jù)rekit-core創(chuàng)建自己的插件崩泡。

Rekit portal

Rekit portal是一個(gè)新的開發(fā)工具,附帶了Rekit 2.0俏橘,它在管理和分析您的Rekit項(xiàng)目中占有核心地位允华。Rekit portal本身也是由Rekit創(chuàng)建的,因此寥掐,它也是學(xué)習(xí)Rekit所參考的一個(gè)很好的例子靴寂。

為了快速查看Rekit portal是如何工作的,您可以查看在線演示召耘。

主要功能點(diǎn)

  • 提供一種更直觀的方式來創(chuàng)建百炬、重命名、移動(dòng)或刪除features污它、components或actions剖踊,而不是CLI庶弃,就如同使用eclipse這樣的IDE創(chuàng)建Java類一樣。

  • 通過源代碼生成項(xiàng)目體系結(jié)構(gòu)的圖表報(bào)告德澈。因此歇攻,新團(tuán)隊(duì)成員或者你自己,很短時(shí)間內(nèi)就能夠上手項(xiàng)目梆造。

  • 只需右鍵單擊缴守,就可以輕松運(yùn)行測(cè)試單個(gè)組件或action。

  • 不用打開終端就可運(yùn)行構(gòu)建镇辉。

  • 集成測(cè)試覆蓋報(bào)告屡穗。

安裝

不需要手動(dòng)安裝Rekit portal,當(dāng)創(chuàng)建一個(gè)新的Rekit應(yīng)用程序時(shí)忽肛,rekit-portal將自動(dòng)依賴于npm模塊村砂。打開http://localhost:6076即可訪問Rekit portal。

項(xiàng)目資源管理器

項(xiàng)目資源管理器通過將源文件按features屹逛、actions础废、components分組來提供更有意義的項(xiàng)目文件夾結(jié)構(gòu)視圖。您可以很容易地看到功能結(jié)構(gòu)罕模,而不僅僅是文件夾結(jié)構(gòu)色迂,您可以在Rekit portal的左側(cè)看到它:

除了顯示項(xiàng)目結(jié)構(gòu)之外,它還提供了一些上下文菜單來管理諸如components之類的項(xiàng)目元素手销。

顯示面板

顯示面板提供了項(xiàng)目的總體狀態(tài)視圖,如概覽圖图张,測(cè)試覆蓋率等锋拖。

概覽圖

顯示面板中最引人注目的部分是概覽圖。這是一個(gè)關(guān)于Rekit項(xiàng)目架構(gòu)的直觀視圖祸轮。它具有交互性兽埃,您可以把鼠標(biāo)移動(dòng)到features、 components或适袜、actions上柄错,來查看某些特定元素的關(guān)系。您還可以單擊一個(gè)節(jié)點(diǎn)深入其中苦酱,下面的信息被概覽圖所涵蓋:

  1. 模塊之間的關(guān)系售貌。

  2. features的相對(duì)大小。

  3. 一個(gè)feature是如何組成的疫萤。

當(dāng)鼠標(biāo)經(jīng)過一個(gè)元素時(shí)颂跨,圖表將突出顯示當(dāng)前元素和與當(dāng)前元素有關(guān)的關(guān)系。

理想情況下扯饶,不應(yīng)該在features之間存在循環(huán)依賴恒削。所以它們是可插入的并且更容易理解池颈。但是在實(shí)際項(xiàng)目中,您需要平衡架構(gòu)和開發(fā)效率钓丰。因此躯砰,如果在features之間有輕量級(jí)的循環(huán)依賴關(guān)系,而原則是避免太多此類依賴關(guān)系携丁,這是可以接受的琢歇。當(dāng)某些類型的依賴關(guān)系變得過于復(fù)雜時(shí),您可以延遲刪除依賴項(xiàng)進(jìn)行重構(gòu)则北。

這里列出了不同顏色和線條的含義:

元素圖

雖然概覽圖展示了項(xiàng)目的總體架構(gòu)矿微,但元素圖提供了所選元素和其他元素之間的關(guān)系,它有助于快速理解一個(gè)模塊尚揣,并幫助找出過于復(fù)雜的模塊涌矢。

當(dāng)您從項(xiàng)目資源管理器或概覽圖中單擊一個(gè)元素時(shí),它將默認(rèn)顯示元素圖:

element-diagram

測(cè)試覆蓋率

Rekit使用istanbul生成測(cè)試覆蓋率報(bào)告快骗。在對(duì)項(xiàng)目運(yùn)行所有測(cè)試之后娜庇,測(cè)試覆蓋率將有效。運(yùn)行單個(gè)測(cè)試或文件夾的測(cè)試將不會(huì)生成覆蓋率報(bào)告方篮。注意名秀,如果一些測(cè)試失敗,報(bào)告數(shù)據(jù)可能是不完整的藕溅。

您可以看到來自顯示面板的總體測(cè)試覆蓋率報(bào)告匕得,或者來自詳細(xì)頁面的由istanbul-nyc生成的原始測(cè)試覆蓋率報(bào)告。

管理項(xiàng)目的元素

Rekit portal將命令行工具封裝到UI對(duì)話框中巾表,可以直觀地創(chuàng)建汁掠、重命名或刪除components、actions等集币。打開對(duì)話框考阱,右鍵單擊項(xiàng)目中的某個(gè)元素,然后單擊相應(yīng)的菜單項(xiàng)鞠苟。

cmd-dialogs

運(yùn)行構(gòu)建

Rekit portal執(zhí)行構(gòu)建腳本tools/build.js乞榨。當(dāng)單擊菜單項(xiàng)Build時(shí),它將讀取Webpack構(gòu)建進(jìn)度數(shù)據(jù)來更新進(jìn)度條当娱。盡管build.js是由Rekit創(chuàng)建的吃既,看起來有點(diǎn)復(fù)雜,但是在完全理解它的工作原理之后跨细,您可以根據(jù)需求更新它态秧。

build

運(yùn)行測(cè)試

Rekit portal執(zhí)行測(cè)試腳本tools/run_test.js。當(dāng)單擊菜單項(xiàng)Run tests時(shí)扼鞋,腳本接受測(cè)試運(yùn)行的參數(shù)申鱼,參數(shù)可以是單個(gè)文件或文件夾愤诱。當(dāng)沒有參數(shù)提供時(shí),它運(yùn)行tests文件夾下的所有測(cè)試捐友,并生成測(cè)試覆蓋率報(bào)告淫半。在下面章節(jié)的命令行工具頁的介紹中可以查看到更多詳細(xì)信息。

因此匣砖,當(dāng)單擊項(xiàng)目元素組件上的Run test菜單項(xiàng)時(shí)蕴轨,它只執(zhí)行tools/run_test.js卵贱,將當(dāng)前的組件測(cè)試文件作為參數(shù)傳遞給腳本蚯妇。您還可以根據(jù)需要來更新你的run_test.js腳本滞详。

test

代碼查看器

它有助于快速查看項(xiàng)目的源代碼。例如拂共,當(dāng)選擇一個(gè)組件時(shí)牺弄,默認(rèn)情況下它會(huì)顯示圖表視圖,但是您可以切換到代碼視圖宜狐,在那里您可以查看組件的源代碼势告。您還可以輕松地查看樣式代碼或測(cè)試文件。目前抚恒,Rekit并沒有直接支持編輯代碼咱台,因?yàn)樗淮蛩闾鎿Q您喜愛的文本編輯器。

element-page

命令行工具

Rekit提供了一組命令行工具來管理Rekit項(xiàng)目的 components, actions和路由規(guī)則俭驮。它們作為JavaScript模塊在rekit-core中實(shí)現(xiàn)回溺,Rekit將它們封裝為命令行工具,實(shí)際上rekit-core也由Rekit Portal使用混萝。

Create an app

您可以使用以下命令創(chuàng)建一個(gè)應(yīng)用程序:

rekit create <app-name> [--sass]

這將會(huì)在當(dāng)前目錄下創(chuàng)建一個(gè)名為app-name的應(yīng)用程序馅而。--sass標(biāo)記表明允許使用sass而不是less`來當(dāng)作css編譯器。

Create a plugin

您可以使用以下命令創(chuàng)建一個(gè)插件:

rekit create-plugin <plugin-name>

如果當(dāng)前目錄是在Rekit項(xiàng)目中譬圣,那么將創(chuàng)建一個(gè)本地插件,否則創(chuàng)建一個(gè)公共插件雄坪。

想了解更多信息厘熟,請(qǐng)閱讀以下文件: http://rekit.js.org/docs/plugin.html

安裝一個(gè)插件

如果你想通過npm使用一個(gè)插件,請(qǐng)使用下面的命令:

rekit install <plugin-name>

這將執(zhí)行插件的install.js腳本進(jìn)行初始化维哈,并添加名為rekit.plugins的插件到package.json中绳姨。

Rekit tools

Rekit工具是創(chuàng)建的應(yīng)用程序附帶的純腳本,它們被放入在你應(yīng)用程序的tools文件夾中阔挠,并且支持編輯以滿足項(xiàng)目的額外要求飘庄。

tools/server.js

這個(gè)腳本用于啟動(dòng)開發(fā)服務(wù)器,默認(rèn)情況下购撼,它啟動(dòng)3個(gè)服務(wù)器跪削,包括:webpack dev server谴仙、Rekit portal和 build result server,您只能通過參數(shù)啟動(dòng)某個(gè)服務(wù)器碾盐。

用法:

node tools/server.js [-m, --mode] [--readonly]
  • mode: 如果沒有提供晃跺,則啟動(dòng)所有3個(gè)開發(fā)服務(wù)器。否則毫玖,只啟動(dòng)指定的開發(fā)服務(wù)器掀虎。它可以是:

    • dev: webpack dev server

    • portal: Rekit portal

    • build: start a static express server for build folder

  • readonly:在只讀模式下啟動(dòng)Rekit portal。啟動(dòng)Rekit portal服務(wù)器只用于探索項(xiàng)目結(jié)構(gòu)是很有用的付枫。例如烹玉,Rekit portal演示是在只讀模式上運(yùn)行。

npm腳本也是有效的: npm start

tools/run_test.js

這個(gè)腳本有助于運(yùn)行一個(gè)或多個(gè)單元測(cè)試阐滩。它接受參數(shù)來判斷哪個(gè)測(cè)試文件應(yīng)該運(yùn)行二打。

用法:

node tools/run_test.js <file-pattern>

文件模式與mocha所接受的相同。如果沒有指定file-pattern叶眉,則運(yùn)行所有測(cè)試并生成測(cè)試覆蓋率報(bào)告址儒。否則運(yùn)行測(cè)試與file-pattern匹配。

例如:

node tools/run_test.js features/home/redux/counterPlusOne.test.js  // run test of a redux action
node tools/run_test.js features/home // run all tests of home feature
node tools/run_test.js // run all tests and generate test coverage report

也可以運(yùn)行npm腳本: npm test.

tools/build.js

這個(gè)腳本用于構(gòu)建項(xiàng)目衅疙。

用法:

node tools/build.js

也可以使用npm腳本:npm run build莲趣。它將項(xiàng)目構(gòu)建到build文件夾中。

管理 features, components and actions

這是每日Rekit發(fā)展的關(guān)鍵饱溢。您將使用以下命令來管理Rekit元素喧伞。

注意:盡管所有命令都放在rekit命令下,也就是rekit add component home/comp1绩郎。實(shí)際上Rekit會(huì)在你的應(yīng)用程序中找到本地的rekit-core包來完成運(yùn)行潘鲫。因此,如果這些應(yīng)用程序依賴于不同版本的rekit-core肋杖,那么在不同的rekit應(yīng)用程序下執(zhí)行rekit命令可能會(huì)有不同的行為溉仑。

所有這些命令都有類似的模式:

  • rekit add <type>: 添加一個(gè)類型的元素。
  • rekit mv <type>: 移動(dòng)/重命名一個(gè)類型的元素状植。
  • rekit rm <type>: 刪除一個(gè)類型的元素浊竟。

所有命令都支持[-h]參數(shù)來查看使用幫助。即rekit add -h津畸。

下面是所有Rekit命令來管理項(xiàng)目的元素列表

Commands Description
rekit add feature <feature-name> Add a new feature.
rekit mv feature <old-name> <new-name> Rename a feature.
rekit rm feature <feature-name> Delete a feature.
rekit add component <component-name> [-u] [-c] Add a new component.-u: specify the url pattern of the component. -c: it's a container component connected to Redux store.
rekit mv component <old-name> <new-name> Rename a component.
rekit rm component <component-name> Delete a component.
rekit add action <action-name> [-a] Add a new action. -u: add an async action.
rekit mv action <old-name> <new-name> Rename an action.
rekit rm action <action-name> Delete an action.

插件

Rekit 2.0引入了一個(gè)新的插件機(jī)制來擴(kuò)展Rekit的功能振定。如果您嘗試過Rekit命令行工具,您可能熟悉它的模式:

rekit <add|rm|mv> <element-type> <feature>/</element-name>

內(nèi)部Rekit支持3種元素類型:: feature, componentaction 并且定義了怎么 add/rm/mv 它們肉拓。

現(xiàn)在后频,您可以創(chuàng)建一個(gè)Rekit插件來改變默認(rèn)行為,例如Rekit創(chuàng)建異步action或讓Rekit支持基于reselect的新元素類型selector暖途。

實(shí)際上卑惜,有這樣的兩個(gè)插件:

  1. rekit-plugin-redux-saga: 允許Rekit在創(chuàng)建異步操作時(shí)使用redux-saga而不是redux-thunk膏执。
  2. rekit-plugin-reselect:添加一個(gè)基于reselect的新元素類型選擇器。因此残揉,您可以通過Rekit為您的項(xiàng)目管理選擇器胧后。

創(chuàng)建一個(gè)插件

要?jiǎng)?chuàng)建一個(gè)插件,請(qǐng)使用以下命令:

rekit create-plugin <plugin-name>

如果當(dāng)前目錄在Rekit項(xiàng)目中抱环,它將創(chuàng)建一個(gè)本地插件壳快,否則創(chuàng)建一個(gè)公共插件。

插件結(jié)構(gòu)

創(chuàng)建一個(gè)插件后镇草,您可以查看文件夾結(jié)構(gòu)眶痰。在插件文件夾下可能有一些特殊的文件:

config.js

插件唯一的mandotory文件,它定義了處理的元素類型梯啤,必要時(shí)定義命令參數(shù)竖伯。例如:

module.exports = {
  accept: ['action'],
  defineArgs(addCmd, mvCmd, rmCmd) { // eslint-disable-line
    addCmd.addArgument(['--thunk'], {
      help: 'Use redux-thunk for async actions.',
      action: 'storeTrue',
    });
  },
};

有兩部分:

1. accept

它是一個(gè)數(shù)組,用來接收插件要處理的類型元素因宇。當(dāng)一個(gè)類型的元素被定義在這里時(shí)七婴,應(yīng)該有一個(gè)名為${ elementType }.js的模塊。在這里定義了添加察滑、刪除打厘、移動(dòng)方法來管理這些元素。例如贺辰,元素類型action被定義户盯,在插件文件夾里將會(huì)有一個(gè)action.js模塊:

module.exports = {
  add(feature, name, args) {},
  remove(feature, name, args) {},
  move(source, target, args) {},
};

您可以只導(dǎo)出一些addremovemove饲化。例如莽鸭,如果只定義add命令,那么當(dāng)執(zhí)行rekit mv action ...時(shí),它將默認(rèn)回歸到Rekitmvactions吃靠。

您還可以分開創(chuàng)建3個(gè)插件add硫眨、removemove,它們接受相同的元素類型巢块。

2. defineArgs(addCmd, mvCmd, rmCmd)

Rekit使用argparse來解析命令參數(shù)礁阁。這種方法允許為命令行工具自定義參數(shù)。這里的addCmd夕冲、mvCmdrmCmd都是全局Rekit命令的子命令。根據(jù)argparse的文檔裂逐,您可以為子命令添加更多的選項(xiàng)以滿足您的需求歹鱼。例如:redux-saga插件定義了一個(gè)新選項(xiàng)——thunk,默認(rèn)情況下redux-saga允許在異步操作中使用redux-thunk,而redux-saga在默認(rèn)情況下使用卜高。然后您就可以使用:

rekit add action home/my-action -a --thunk

hooks.js

當(dāng)您想要掛起某些操作時(shí)弥姻,此文件僅是必需的南片。即在創(chuàng)建或刪除feature之后,做一些事情庭敦。每個(gè)元素類型有兩種鉤子點(diǎn):beforeafter疼进,它們與操作類型相結(jié)合,形成多個(gè)鉤子點(diǎn)秧廉。例如伞广,feature元素類型有一下鉤子點(diǎn):

  • beforeAddFeature()
  • afterAddFeature()
  • beforeMoveFeature()
  • afterMoveFeature()
  • beforeRemoveFeature()
  • afterRemoveFeature()

參數(shù)只從鉤子目標(biāo)繼承。也就是說疼电,傳遞給addFeature的任何參數(shù)也都傳遞到beforeAddFeature()嚼锄。

注意,不只是內(nèi)部元素類型有鉤子點(diǎn)蔽豺,所有被插件支持的元素類型都有這樣的鉤子點(diǎn)区丑。

例如,redux-saga插件使用鉤子在添加或刪除features時(shí)進(jìn)行初始化和初始化修陡。

const fs = require('fs');
const _ = require('lodash');
const rekitCore = require('rekit-core');

const utils = rekitCore.utils;
const refactor = rekitCore.refactor;
const vio = rekitCore.vio;

function afterAddFeature(featureName) {
  // Summary:
  //  Called after a feature is added. Add sagas.js and add entry in rootSaga.js
  const rootSaga = utils.mapSrcFile('common/rootSaga.js');
  refactor.updateFile(rootSaga, ast => [].concat(
    refactor.addImportFrom(ast, `../features/${_.kebabCase(featureName)}/redux/sagas`, null, null, `${_.camelCase(featureName)}Sagas`),
    refactor.addToArray(ast, 'featureSagas', `${_.camelCase(featureName)}Sagas`)
  ));

  const featureSagas = utils.mapFeatureFile(featureName, 'redux/sagas.js');
  // create sagas.js entry file for the feature
  if (!fs.existsSync(featureSagas)) vio.save(featureSagas, '');
}

function afterRemoveFeature(featureName) {
  // Summary:
  //  Called after a feature is removed. Remove entry from rootSaga.js
  const rootSaga = utils.mapSrcFile('common/rootSaga.js');
  refactor.updateFile(rootSaga, ast => [].concat(
    refactor.removeImportBySource(ast, `../features/${_.kebabCase(featureName)}/redux/sagas`),
    refactor.removeFromArray(ast, 'featureSagas', `${_.camelCase(featureName)}Sagas`)
  ));
}

function afterMoveFeature(oldName, newName) {
  // Summary:
  //  Called after a feature is renamed. Rename entry in rootSaga.js
  const rootSaga = utils.mapSrcFile('common/rootSaga.js');
  refactor.updateFile(rootSaga, ast => [].concat(
    refactor.renameModuleSource(ast, `../features/${_.kebabCase(oldName)}/redux/sagas`, `../features/${_.kebabCase(newName)}/redux/sagas`),
    refactor.renameImportSpecifier(ast, `${_.camelCase(oldName)}Sagas`, `${_.camelCase(newName)}Sagas`)
  ));
}

module.exports = {
  afterAddFeature,
  afterRemoveFeature,
  afterMoveFeature,
};

使用插件

對(duì)于本地插件沧侥,除了創(chuàng)建它之外,您不需要做任何其他事情魄鸦。如果存在沖突宴杀,處理元素類型的優(yōu)先級(jí)最高。

對(duì)于公共插件号杏,從npm安裝婴氮。您需要在package.json的rekit部分注冊(cè)它。

{
...,
"rekit": {
  "plugins": ["redux-saga", "apollo"], 
},
...,
}

這里你只需要在插件屬性中定義公共插件盾致,以便Rekit能夠加載主经。本地插件將總是由Rekit加載。注意插件名稱的順序在它們接受相同的元素類型時(shí)很重要庭惜,而eariers則有更高的優(yōu)先級(jí)罩驻。
雖然存在多個(gè)插件接受相同的元素類型的沖突,但從高到低的優(yōu)先級(jí)是:本地插件<公共插件< Rekit默認(rèn)行為护赊。

注意:支持更多元素類型的插件現(xiàn)在只能通過命令行工具使用惠遏。

插件開發(fā)

對(duì)于大多數(shù)情況,一個(gè)插件只會(huì)基于一些模板創(chuàng)建一個(gè)bolierplate代碼;在移動(dòng)時(shí)重構(gòu)代碼骏啰,刪除源文件节吮。Rekit-core為促進(jìn)插件開發(fā)提供了許多有用的APIs 。您可能只需要編寫這些APIs 來滿足您的需求判耕。

API參考

查看鏈接: http://rekit.js.org/api/index.html

路由

Rekit使用React router作為路由解決方案透绩。它幾乎是React web應(yīng)用程序的標(biāo)準(zhǔn)方法。通過使用React-router-redux ,可以輕松使用 Redux store來同步路由狀態(tài)帚豪。

即使對(duì)于簡(jiǎn)單的應(yīng)用程序碳竟,路由也非常重要,就像傳統(tǒng)的web應(yīng)用程序需要不同頁面的不同url一樣狸臣,SPA也需要將不同的邏輯分組到不同的UI莹桅,這是Rekit的頁面理念。

使用Rekit烛亦,頁面通常是與URL路徑關(guān)聯(lián)的元素诈泼。每當(dāng)通過Rekit創(chuàng)建頁面時(shí),它自動(dòng)定義路由配置中的映射規(guī)則此洲。

路由配置

由于Rekit使用面向功能的文件夾結(jié)構(gòu)厂汗,因此路由配置也如此,就是是在功能文件夾中定義的所有功能相關(guān)的路由配置呜师。每個(gè)feature都有一個(gè)定義路由規(guī)則的route.js文件娶桦,下面是一個(gè)配置示例:

import {
  EditPage,
  ListPage,
  ViewPage,
} from './index';

export default {
  path: '',
  name: '',
  childRoutes: [
    { path: '', component: ListPage, name: 'Topic List', isIndex: true },
    { path: 'topic/add', component: EditPage, name: 'New Topic' },
    { path: 'topic/:topicId', component: ViewPage },
  ],
};

使用JavaScript定義路由規(guī)則

從上面的示例中,我們可以看到route config使用了JavaScript API 汁汗。實(shí)際上衷畦,路由器支持Json for configuration。Rekit使用了它知牌,因此很容易將不同的路由配置放到不同的features中祈争。下面是一個(gè)Json路由配置示例。

const routes = {
  path: '/',
  component: App,
  indexRoute: { component: Dashboard },
  childRoutes: [
    { path: 'about', component: About },
    {
      component: Inbox,
      childRoutes: [{
        path: 'messages/:id', component: Message
      }]
    }
  ]
}

所有和功能相關(guān)的路由定義都被全局的根路由配置自動(dòng)加載角寸,因此菩混,路由加載器具有如下的代碼模式:

import topicRoute from '../features/topic/route';
import commentRoute from '../features/comment/route';

const routes = [{
  path: '/rekit-example',
  component: App,
  childRoutes: [
    topicRoute,
    commentRoute,
    { path: '*', name: 'Page not found', component: PageNotFound },
  ],
}];

實(shí)際上,這是一個(gè)全局路由加載器扁藕,類似root reducer, 加載了全部的feature的路由規(guī)則沮峡。

注意,您不需要維護(hù)src/common/routeConfig.js亿柑。在添加/刪除一個(gè)feature時(shí)邢疙,Rekit將自動(dòng)添加和刪除路由規(guī)則。

使用isIndex屬性代替indexRoute

不像用JSX的方式望薄,用來<IndexRoute ...>標(biāo)簽來配置一個(gè)路由疟游,JavaScript API的官方indexRoute配置是Rekit的一個(gè)難點(diǎn),Rekit添加了isIndex屬性支持痕支。
一個(gè)路由規(guī)則如果設(shè)置isIndex: true颁虐,它將成為父索引路由
下面的代碼是自動(dòng)創(chuàng)建的home feature的路由配置:

import {
  DefaultPage,
  TestPage1,
  TestPage2,
} from './index';

export default {
  path: '',
  name: 'home',
  childRoutes: [
    { path: 'default-page', component: DefaultPage, isIndex: true },
    { path: 'test-page-1', component: TestPage1 },
    { path: 'test-page-2', component: TestPage2 },
  ],
};

然后,DefaultPage將會(huì)成為根路徑的索引路由卧须。

name屬性

您可能已經(jīng)注意到另绩,route配置規(guī)則有一個(gè)name屬性瞬痘。實(shí)際上它只被SimpleNav組件使用。它只是在dev time中顯示路由配置的一個(gè)方便的組件板熊。您已經(jīng)在歡迎頁面中看到了。對(duì)于一個(gè)實(shí)際的應(yīng)用程序來說察绷,它可能是無用的干签。路由配置API的所有其他用法都與官方的方式相同,您可以參考 React-router官方文檔拆撼。

樣式

Rekit使用Less作為的css預(yù)處理器容劳,因?yàn)槲乙呀?jīng)習(xí)慣了它很長(zhǎng)時(shí)間。Scss支持將成為候選闸度。實(shí)際上竭贩,在我的選擇中,兩者之間并沒有太大的差別莺禁,而且很容易切換留量。

在許多實(shí)踐中,React component中會(huì)導(dǎo)入less文件哟冬,不同的是楼熄,Rekit建議只使用 Less自己來管理依賴項(xiàng)。這種方法有幾個(gè)優(yōu)點(diǎn):

  1. Css可以構(gòu)建并生成separtely浩峡。
  2. React component導(dǎo)入less不是標(biāo)準(zhǔn)的方法可岂,僅是webpack加載器的特性。
  3. 如果組件使用不止一次翰灾,webpack的Css-loader會(huì)為構(gòu)建生成重復(fù)的Css代碼缕粹。

Rekit的使用非常直觀的,如下圖所示:

Styling

一般來說纸淮,一個(gè)Rekit應(yīng)用程序的樣式遵循以下幾個(gè)規(guī)則:

  1. 全局樣式定義在src/styles/global.less平斩,例如css for body,h1,h2…

  2. 每個(gè)組件都有一個(gè)具有相同名稱的樣式文件,例如萎馅,組件SimpleNav双戳。js有一個(gè)名為SimpleNave.less的樣式文件。

  3. 每個(gè)feature都有一個(gè)名為style.less文件糜芳。為頁面和組件導(dǎo)入所必需的樣式文件飒货。文件中還定義了所有 feature 的共同樣式。

  4. src/styles/index.less是導(dǎo)入所有feature的style.lessglobal.less樣式的入口文件峭竣。

對(duì)于其他場(chǎng)景塘辅,您可以隨意使用您喜歡的方式。

測(cè)試

測(cè)試一個(gè)React + Redux應(yīng)用是困難的皆撩,你需要知道很多關(guān)于 React測(cè)試的 libs/tools扣墩,了解它們的用法哲银。 但Rekit會(huì)為你設(shè)置了一切,您只需要在自動(dòng)生成的測(cè)試文件中編寫測(cè)試代碼呻惕,在 Rekit portal中運(yùn)行測(cè)試荆责,就可以瀏覽器中讀取測(cè)試覆蓋率報(bào)告。

下面是React測(cè)試工作的過程亚脆,您可以了解Rekit是如何設(shè)置測(cè)試過程的做院。

  1. Istanbul測(cè)試覆蓋報(bào)告的源代碼。Istanbul本身不支持JSX濒持,但有一個(gè)babel插件babel-plugin-istanbul能夠支持運(yùn)行键耕。

  2. Webpack用mocha-webpack構(gòu)建所有測(cè)試文件,自動(dòng)找到測(cè)試文件進(jìn)行運(yùn)行測(cè)試柑营。

  3. Mocha運(yùn)行測(cè)試文件的構(gòu)建版本屈雄。

  4. nyc生成測(cè)試覆蓋率報(bào)告。

所有應(yīng)用程序測(cè)試文件都保存在test/app目錄中官套。文件夾結(jié)構(gòu)與src文件夾中的源代碼文件夾結(jié)構(gòu)相同酒奶。

Rekit如何生成測(cè)試文件

在創(chuàng)建新頁面、組件或action時(shí)奶赔,Rekit 自動(dòng)為它們創(chuàng)建測(cè)試用例讥蟆。即使您不向生成的測(cè)試用例添加任何代碼,它們也可以幫助您避免簡(jiǎn)單的錯(cuò)誤纺阔,如:

  1. 確保了一個(gè)組件是否能渲染成功是通過檢查DOM根節(jié)點(diǎn)的存在和正確的css類來完成的瘸彤。

  2. 通過檢查前后狀態(tài)是否一致來確保reducer不變。

例如笛钝,當(dāng)創(chuàng)建一個(gè)新頁面時(shí)质况,它生成以下測(cè)試案例:

it('renders node with correct class name', () => {
  const pageProps = {
    home: {},
    actions: {},
  };
  const renderedComponent = shallow(
    <TestPage1 {...pageProps} />
  );

  expect(
    renderedComponent.find('.home-test-page-1').node
  ).to.exist;
});

當(dāng)創(chuàng)建一個(gè)action時(shí),它生成下面的reducer測(cè)試用例:

it('reducer should handle MY_ACTION', () => {
  const prevState = {};
  const state = reducer(
    prevState,
    { type: MY_ACTION }
  );
  expect(state).to.not.equal(prevState); // should be immutable
  expect(state).to.deep.equal(prevState); // TODO: replace this line with real case.
});

命令行工具測(cè)試

所有命令行工具也有保存在test/cli文件夾中的單元測(cè)試用例玻靡。不僅在Rekit項(xiàng)目中结榄,而且它們也被復(fù)制到創(chuàng)建的應(yīng)用程序中,這樣您就可以根據(jù)測(cè)試用例自定義工具囤捻。

因?yàn)檫@些工具腳本是用純NodeJs編寫的臼朗,所以測(cè)試更簡(jiǎn)單:

  1. Istanbul 的源代碼。
  2. Mocha運(yùn)行測(cè)試用例蝎土。
  3. nyc生成測(cè)試覆蓋率報(bào)告视哑。

run_test.js

您可能已經(jīng)注意到,測(cè)試用例也需要使用webpack構(gòu)建誊涯,然后用Mocha可以運(yùn)行測(cè)試用例挡毅。因此,運(yùn)行單個(gè)測(cè)試用例文件也需要構(gòu)建暴构。所以 run_test.js的創(chuàng)建是為了簡(jiǎn)化這個(gè)過程跪呈。您可以只通過run_test.js工具運(yùn)行任何測(cè)試用例文件段磨。它也被導(dǎo)出為npm test。所有的參數(shù)都傳遞給run_test.js腳本耗绿。用法如下:

npm test  // run all tests under the `test/app` folder
npm test app/features/home // run tests under the `home` feature
npm test app/**/list.test.js // run all tests named list.test.js under app folder
npm test cli // run tests for all command line tools
npm test all // run tests for both app and cli

查看測(cè)試覆蓋率報(bào)告

如果測(cè)試是由app, cli or all運(yùn)行的苹支,那么所有測(cè)試覆蓋率報(bào)告都將涉及。

可以看到如下的不同覆蓋率報(bào)告:

  1. all: coverage/lcov-report/index.html
  2. app: coverage/app/lcov-report/index.html
  3. cli: coverage/cli/lcov-report/index.html

打包

Webpack總被認(rèn)為是構(gòu)建React應(yīng)用程序的最困難的地方误阻。幸運(yùn)的是沐序,Rekit為你配置了一切,下面有兩個(gè)webpack配置文件對(duì)應(yīng)不同的用法:

  • webpack.config.js:是webpack用于開發(fā)的配置工廠堕绩,dlldist打包。
  • webpack.test.config.js:測(cè)試構(gòu)建的配置邑时。

使用webpack-dll-plugin提高構(gòu)建性能

當(dāng)一個(gè)React應(yīng)用程序變大時(shí)奴紧,開發(fā)構(gòu)建應(yīng)用程序會(huì)很耗時(shí)。webpack-dll-plugin可以解決這個(gè)痛點(diǎn)晶丘。有一個(gè)很好的文章對(duì)其進(jìn)行了介紹:http://engineering.invisionapp.com/post/optimizing-webpack/

基本思想是將諸如 React, Redux, React-router之類的公共libs構(gòu)建為一個(gè)單獨(dú)的dll包黍氮。這樣,在每次更改代碼時(shí)都不需要構(gòu)建它們浅浮。通過此方法沫浆,可以顯著減少構(gòu)建時(shí)間。

Rekit在tools/server.js集成了打包過程滚秩,用npm start即可運(yùn)行专执。腳本檢查dll構(gòu)建所需的所有的版本包。因此郁油,當(dāng)任何依賴版本發(fā)生更改時(shí)本股,它將自動(dòng)構(gòu)建一個(gè)新的dll

代碼質(zhì)量檢查

Rekit使用ESlint對(duì)代碼質(zhì)量進(jìn)行檢查桐腌。并將airbnb javascript guide作為所有代碼的基本規(guī)則拄显。.eslintrc定義了一些細(xì)微的變化用于Rekit應(yīng)用程序的不同部分。

似乎規(guī)則表明airbnb經(jīng)常改變案站,對(duì)于一個(gè)新創(chuàng)建的Rekit應(yīng)用程序躬审,可能有一些eslint錯(cuò)誤,您需要更新代碼或添加自己的規(guī)則以清除這些錯(cuò)誤蟆盐。

總結(jié)

本文介紹了Rekit工具承边,通過Rekit工具構(gòu)建基于React+Redux +React-router的可擴(kuò)展Web應(yīng)用,遵循前端開發(fā)的最佳實(shí)踐石挂。

其核心思想是

  • 以功能(feature)為單位組件文件夾結(jié)構(gòu)炒刁,

  • 每個(gè) action 單獨(dú)文件的模式。

  • 使用 React-router 來讓單頁應(yīng)用(SPA)也擁有傳統(tǒng) Web 應(yīng)用的 URL 導(dǎo)航功能

優(yōu)點(diǎn)

  • 代碼更加模塊化

  • 降低了功能模塊間的耦合行

  • 應(yīng)用結(jié)構(gòu)更加清晰直觀誊稚。

更多的工具介紹可以訪問其官網(wǎng):http://rekit.js.org翔始。

著作權(quán)歸作者所有罗心。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處城瞎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渤闷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子脖镀,更是在濱河造成了極大的恐慌飒箭,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜒灰,死亡現(xiàn)場(chǎng)離奇詭異弦蹂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)强窖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門凸椿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人翅溺,你說我怎么就攤上這事脑漫。” “怎么了咙崎?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵优幸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我褪猛,道長(zhǎng)网杆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任伊滋,我火速辦了婚禮跛璧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘新啼。我一直安慰自己追城,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布燥撞。 她就那樣靜靜地躺著座柱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪物舒。 梳的紋絲不亂的頭發(fā)上色洞,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音冠胯,去河邊找鬼火诸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛荠察,可吹牛的內(nèi)容都是我干的置蜀。 我是一名探鬼主播奈搜,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盯荤!你這毒婦竟也來了馋吗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤秋秤,失蹤者是張志新(化名)和其女友劉穎宏粤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灼卢,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绍哎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鞋真。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崇堰。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖灿巧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揽涮,我是刑警寧澤抠藕,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蒋困,受9級(jí)特大地震影響盾似,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜雪标,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一零院、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧村刨,春花似錦告抄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逆粹,卻和暖如春募疮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背僻弹。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工阿浓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹋绽。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓芭毙,卻偏偏與公主長(zhǎng)得像筋蓖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稿蹲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容