在開發 React.js 專案時,使用 JSX 語法撰寫 React component 較為方便,因此專案初始階段就需要將 JSX 相容性納入考量。除了跟隨官網的 getting started 範例,尋找合適的模版架構(boilerplate)也是很好的作法(註1),能夠以他人架好的基礎上快速開始,也可以學習 boilerplate project 內的專案優點。
React.js boilerplate 選擇眾多,一般會依照自己想要的功能需求(Server-side rendering、Redux、Flux、React-Router)和可讀性或自己的喜好來選擇。以作者個人喜好而言,會以下列功能需求做考量:
回到本次的專案架構,比起直接選擇現有的 boilerplate,我們以上述的考量點來進行專案初始化,並以最低限度的需求來做組合,之後開發過程碰到需要的功能再加進去。以下是初始化後的專案結構:
gulp
src
|- client
|- server
webpack
.babelrc
.eslintrc
gulpfile.babel.js
package.json
為何選擇 Gulp 作為 task runner?以作者本身曾經使用 Grunt 和 Gulp 的經驗而言,Grunt 的運作邏輯偏向以「功能完整的 plugin」搭配「gruntfile 提供的 config」運作,使用上比 Gulp 容易上手一些;然而這樣同時意味著專案的套件相依性容易受不同的 plugin 影響。
例如,若使用 grunt-webpack 進行 Webpack 整合,專案所使用的
Webpack 版本就會受限於 grunt-webpack plugin 所使用的版本(定義於 plugin 自己的 package.json
);因此,當專案本身需要升級 Webpack 的話,便容易產生 dependency 衝突的狀況。
Gulp 相較之下更為輕量,雖然需要自己額外做較多設定,但也帶來比較高的彈性。
在 gulp
資料夾內宣告了幾個功能不同的 Gulp task:
build
: clean
+ webpack
,先清空上次的 build 結果後再行 buildclean
: 清空上次的 build 結果env
: 強制讓 NODE_ENV
環境變數指定為 local
(開發階段)或 production
lint
: 執行 Linting 程序server-hot
: 運行 Webpack dev server 於開發階段使用 hot module replacement (HMR) 功能server-node
: 運行 server 程式server-nodedev
: 用 node-dev 運行 server 程式server
: 同時充當 default
task,依 NODE_ENV
差異而有兩種版本:
lint
+ watch
+ server-hot
和 server-node
,也就是會有兩個 server 同時運行build
+ server-node
watch
: 當 src/client
的 JS 程式碼變動時重新進行 Linting。(重新 compile 這個工作由 HMR 機制負責)webpack
: Build 前端的 JS/CSS 程式碼,並 bundle 成兩隻檔案關於 Webpack 和 ESLint 等設定的細節將於後續文章說明。
執行方式就是直接呼叫 gulp
、gulp build
或 gulp lint
等即可;或是再更進一步,於 package.json
的 npm scripts 宣告如何執行這些 Gulp task:
"scripts": {
"local": "gulp",
"production": "NODE_ENV=production gulp"
},
然後使用 npm run local
或 npm run production
執行。
附帶一提,包含 gulpfile.babel.js
在內,此專案所撰寫的 Gulp tasks 使用了 ES6 語法,若是沒有在 node_modules
安裝 babel-register
等 Babel 套件的話會造成執行失敗。這是因為 gulpfile 的名稱帶有 .babel
後綴,Gulp 會在 runtime 會找尋專案內的 .babelrc
檔案並試著以 babel-register
進行 transpiling 再執行的緣故。
註1:Find your Perfect React starter project 和 Awesome React 都值得一看