基于 NPM 的大型 React 单页应用探索(多场景多方案)
原文地址:https://github.com/luqin/blog/issues/10
未完待续……
本文目标构建基于 NPM 的大型 React 单页应用(以及多页面),支持多模块协同开发、分布式构建与发布。
React
React Router
Redux…
webpack
ES2015+/JSX with babel
JavaScript 规划
首先初始化顶层目录结构:
app/
config/
package.json
README.md
... and tons of project .dotfiles and tool files ...
按文件类型组织 File-Type First (FTF)
app/
reducers/
root.js
memberships.js
components/
home.jsx
memberships.jsx
... of course more type folders/ ...
按功能组织 Feature First (Pods)
app/
authentication/
api/
components/
helpers/
...
comments/
actions/
api/
components/
reducers/
stores/
...
...
可以像这样按功能分组:
app/
settings/
profile/
notifications/
...
那么通用文件如何放置呢?一个方案是将他们放入框架文件夹:
app/
flux/
apiUtils.js
baseActions.js
baseStore.js
connectToStores.js
多 App 模式 Multiple Apps
app/
kapost.jsx
studio/
studioEntry.jsx
content/
...
gallery/
galleryEntry.jsx
collections/
...
insights/
insightsEntry.jsx
content-scoring/
...
members/
membersEntry.jsx
profile/
...
依然有很多通用代码,可以放入通用文件夹:
app/
...
shared/
users/
ui/
dropdowns/
...
...
到目前为止,按功能组织模式仍然可以 hold 住,我们可以在每个 App 使用按文件类型组织模式,但是依然有缺点,仅仅适合单 App 模式。
面对疯狂增长的 routes 或者 reducers,还有一种优雅的方式是使用代码分包(code-splitting),例如动态加载 React Router 和动态增加 Redux reducers,那么我们如何组织这些文件呢?我们可以定义一个顶级文件夹 boot/
,一个项目文件夹例如 kapost/
。
app/
kapost/
routes.jsx (holds and rolls up all other app routes dynamically)
reducer.js (holds all reducers dynamically)
studio/
studioEntry.jsx
app/
routes.jsx (rolls up all application routes)
reducers.jsx (rolls up all studio reducers across all the feature folders)
...
...
……
Application Organization
API
同构 Universal Rendering
Domains and Authentication
Secure cookies over HTTPS (under one primary domain name)
App Configuration
Assets
Styles
CSS 方案:
SASS
LESS
Inline Style
PostCSS
CSS Modules
构建工具:
本文基于 SASS 实现模块化方案。
Without webpack and inlining
每个项目的样式文件目录:
studio/
app/
config/
stylesheets/
spec/
package.json
...
每个样式文件通过命名空间来实现模块化,根据组件确定前缀:
studio/
app/
comments/
commentEntry.jsx
stylesheets/
comments/
_commentEntry.scss
// _commentEntry.scss
.studio-comment-entry-component {
// my name-spaced styles
}
// commentEntry.jsx#render
render() {
<div className="studio-comment-entry-component">...</div>
}
共享的样式可以放入 shared/
目录,全局样式可以放入类似 stylesheets/app/
的目录(使用命名空间)。
每个 app 都负责引入所有功能模块的样式文件,顶层 app 负责引入所有子 app 的样式文件。如果分离一些 apps 到分离的代码仓库,可以共享相同的构建流程,只需要维护相同的构建配置。
With webpack and inlining
studio/
app/
comments/
styles/
individualComponentStylesheet.scss
...
Why You Shouldn’t Style React Components With JavaScript
Testing
studio/
app/
comments/
components/
commentsContainer.jsx
specs/
components/ (unit tests of sorts)
integration/ (testing entire comment feature)
commentsContainerSpec.jsx (container could even be the main integration test)
...
...
基于 NPM 的协同开发方案
私有 NPM 方案:
NPM 分包:
@kapost/app
@kapost/studio
@kapost/gallery
@kapost/insights
...
module/
...
assets
dist
js
scss
test
tools
routes.jsx
package.json
……
分布式编译
gulp + webpack + babel
gulp + System.js + babel