mern-starter 实战指南:Mongo + Express + React + Node 同构应用的脚手架搭建、服务端渲染与代码生成 后端前端【免费下载链接】mern-starter⛔️ DEPRECATED - Boilerplate for getting started with MERN stack项目地址https://gitcode.com/gh_mirrors/me/mern-starter点击查看免费下载mern-starter项目全称 MERN是一套基于 Mongo、Express、React 与 NodeJS 的同构Isomorphic应用脚手架模板旨在把从零搭建一个前后端同构应用所需的繁琐配置——Webpack 打包、React Router 服务端渲染、Redux 状态管理、MongoDB 数据层、Docker 部署、代码生成器——一次性封装进一个可复用的工程里。本文以仓库根目录的 README.md 为主线结合仓库内的真实源码与测试完整讲解它的快速启动方式、可用命令、同构渲染原理、模块组织规范、代码生成器配置与已知注意事项帮助读者既能快速上手也能深入理解其内部实现。提醒该仓库已在 README 首行标注⛔️ DEPRECATED项目不再积极维护本文内容以当前仓库实际代码版本 2.4.0为准供学习与借鉴架构时参考。项目定位一次配置、两端同构README 对 MERN 的定义非常明确它是一个scaffolding tool脚手架工具让开发者可以基于 Mongo Express React NodeJS 快速构建同构应用减少初始化时间直接站在已被验证的技术栈上开始业务开发。从 package.json 可以看出当前仓库的完整技术选型服务端Express 4、Mongoose 4、body-parser、compression前端React 16、React Router 3、Redux 3 redux-thunk、react-intl国际化、react-helmet文档头管理构建Webpack 3、Babel 6es2015reactstage-0预设、postcss-cssnext测试ava nyc enzyme sinon supertest mockgoose项目版本为2.4.0engines.node 6Docker 基础镜像为node:8。README 中还列出了通往 V3 的里程碑计划V2.5.0 升级 React V16 与 propTypes、V2.6.0 升级 React Router V4、V2.7.0 升级 Webpack 4、V2.8.0 更新 Mongoose、V2.9.0 升级 Express、V3.0.0 优化 CLI 与 Docker 工具链其中 V2.4.0Webpack 3 与多项修复已经完成对应变更记录见 Changes.md。快速开始五分钟跑起一个同构应用README 给出的 Quickstart 只有四条命令但它涵盖了全局安装 CLI → 生成新工程 → 安装依赖 → 启动开发服务器的完整链路npm install -g mern-cli mern init your_new_app cd your_new_app npm install npm start使用前有两个前提条件README 明确提示请确保 MongoDB 正在运行。连接配置在 server/config.js默认值如下均可用环境变量覆盖const config { mongoURL: process.env.MONGO_URL || mongodb://localhost:27017/mern-starter, port: process.env.PORT || 8000, };也就是说默认数据库地址是mongodb://localhost:27017/mern-starter默认端口是8000可通过MONGO_URL与PORT环境变量改写。需要 npm 6。README 特别注明 npm6 is required to install dependencies properly因为 V2.4 起引入了package-lock.json来锁定依赖版本。首次启动时还有一个贴心细节MongoDB 连接成功后server/server.js 会调用dummyData()通过 server/dummyData.js 检查Post集合是否为空若为空则自动插入 Hello MERN 与 Lorem Ipsum 两条示例文章让你打开页面即有数据可看。而npm start实际执行的是nodemon index.js见 package.jsonnodemon.json 中配置了监听server与Intl目录、自动注入NODE_ENVdevelopment实现开发期的自动重启。可用命令六条核心命令与底层脚本README 的 Available Commands 列出了六个命令这里逐条说明其用途并对应到 package.json 中真实的 script 定义命令用途底层脚本npm run start启动开发服务器开启热更新hot reloadingcross-env BABEL_DISABLE_CACHE1 NODE_ENVdevelopment nodemon index.jsnpm run bsbundle start打包代码后启动生产服务器npm run clean npm run build npm run build:server npm run start:prodnpm run test启动测试运行器avacross-env NODE_ENVtest ... nyc node ava/cli.jsnpm run watch:test测试运行器的 watch 模式npm run test -- --watchnpm run cover生成测试覆盖率报告nyc npm run testnpm run lint运行 ESLint 检查client与server的代码规范eslint client server除了上述六条仓库还额外提供了几个常用于生产链路的脚本start:prod直接以NODE_ENVproduction启动不重新打包build执行webpack --config webpack.config.prod.js生成客户端生产包build:server执行webpack --config webpack.config.server.js打包服务端clean用rimraf清空dist目录slate彻底重装依赖rimraf node_modules npm installnpm run bs的完整链路值得展开它先clean清空旧的dist再build生成带 hash 的客户端资源接着build:server把服务端打成dist/server.bundle.js最后start:prod启动生产服务器。生产模式下 index.js 会读取dist/client/manifest.json与dist/client/chunk-manifest.json并直接加载打包后的服务端 bundle开发模式则走 Babel register 动态转译。另外仓库通过pre-commit钩子在每次提交前自动执行lint与test从源头保证代码质量。目录结构与四套 Webpack 配置README 将工程分为 Server 与 Client 两大块这里先看顶层结构再深入到 Webpack 的四个配置。Webpack ConfigsMERN 使用 Webpack 完成模块打包一共提供了四种配置README 逐一说明了各自用途webpack.config.dev.js — 开发配置启用cheap-module-eval-source-map入口加入webpack-hot-middleware/client、react-hot-loader/patch等热更新依赖CSS 使用style-loader注入不抽取文件图片小于 10KB 走url-loader内联。webpack.config.prod.js — 生产配置输出到dist/client/文件名带[chunkhash]用ExtractTextPlugin抽取 CSS、UglifyJsPlugin压缩、ManifestPlugin与ChunkManifestPlugin生成资源清单供服务端渲染时引用带 hash 的文件。webpack.config.server.js — 生产环境服务端打包以server/server.js为入口target: node用webpack2-externals-plugin把node_modules排除在 bundle 之外生产容器里直接 require并通过babel-plugin-webpack-loaders复用 webpack.config.babel.js 让服务端也能正确处理通过 webpack 引入的 CSS 与图片资源。webpack.config.babel.js — 供babel-plugin-webpack-loaders在 Node 环境下转译资源使用定义了 CSS Modules 的localIdentName规则开发期为[name]__[local]__[hash:base64:5]生产期为[hash:base64]与服务端/客户端保持一致避免 CSS 类名错位。README 也强调The Webpack configuration is minimal and beginner-friendly即这套配置刻意保持最小化、对新手友好你可以按需在生产构建上继续叠加功能如代码分割、tree shaking 等。Server 层服务端使用 Express 框架核心在 server/server.js。它首先通过process.env.NODE_ENV判断运行模式L12-L13development 模式注入webpack-dev-middleware带poll: 1000的文件监听与webpack-hot-middleware实现开发期的实时打包与模块热替换。随后统一挂载compression压缩中间件、body-parserJSON 与 urlencoded限制 20mb、静态资源目录dist/client以及挂载在/api前缀下的 Post 路由。Client 层client目录存放所有共享组件、路由与模块components全项目通用的公共组件目录。index.jsclient/index.js 是客户端渲染入口直接用服务端注入的window.__INITIAL_STATE__初始化 Redux store并用react-hot-loader的AppContainer包裹根组件完成挂载与热更新。modules按领域划分的业务模块详见下文模块化组织一节。服务端渲染React Router match 驱动全链路服务端渲染SSR是本仓库最核心的卖点。README 用一段完整的代码展示了其骨架这里给出当前仓库 server/server.js 中的真实实现与 README 示例略有演进如直接向fetchComponentData传入 store 实例而非 dispatch 函数并补充了 302 重定向分支app.use((req, res, next) { match({ routes, location: req.url }, (err, redirectLocation, renderProps) { if (err) { return res.status(500).end(renderError(err)); } if (redirectLocation) { return res.redirect(302, redirectLocation.pathname redirectLocation.search); } if (!renderProps) { return next(); } const store configureStore(); return fetchComponentData(store, renderProps.components, renderProps.params) .then(() { const initialView renderToString( Provider store{store} IntlWrapper RouterContext {...renderProps} / /IntlWrapper /Provider ); const finalState store.getState(); res .set(Content-Type, text/html) .status(200) .end(renderFullPage(initialView, finalState)); }) .catch((error) next(error)); }); });这段逻辑的要点README 已逐条解释这里结合源码再深化match的两个参数第一个是包含routes、location: req.url的对象路由定义在 client/routes.js/与/posts/:slug-:cuid两条路由均使用getComponentrequire.ensure实现异步代码分割第二个是匹配完成后的回调。三分支处理匹配出错返回500发生重定向则302跳转README 示例未含此分支是当前实现的增强没有任何路由匹配则交给next()README 示例中是返回 404当前实现把 404 交给后续中间件/静态资源处理。每个请求都新建 Redux StoreREADME 特别强调 A new Redux Store has populated afresh on every request。这是因为同一份 store 若被多请求共享会造成状态串扰client/store.js 的configureStore(initialState {})每次调用都通过createStore(rootReducer, initialState, compose(...enhancers))生成全新实例并只在客户端 开发模式下挂载 Redux DevTools。fetchComponentData是数据预取的关键server/util/fetchData.js 的实现如下export function fetchComponentData(store, components, params) { const needs components.reduce((prev, current) { return (current.need || []) .concat((current.WrappedComponent (current.WrappedComponent.need ! current.need) ? current.WrappedComponent.need : []) || []) .concat(prev); }, []); return sequence(needs, need store.dispatch(need(params, store.getState()))); }它收集当前路由所有组件含connect包裹后的WrappedComponent上声明的静态属性need一个渲染前必须 dispatch 的 action 数组再通过 server/util/promiseUtils.js 的sequence函数串行执行每个 need前一个 Promise resolve 后才执行下一个全部完成后才渲染页面。例如列表页在 PostListPage.js 声明PostListPage.need [() { return fetchPosts(); }]详情页在 PostDetailPage.js 声明PostDetailPage.need [params fetchPost(params.cuid)]——这样服务端渲染时数据就已经就绪首屏无空白等待。渲染完整 HTML 并把状态交给客户端renderToString把ProviderIntlWrapperRouterContext渲染成字符串Helmet.rewind()负责收集文档head标签renderFullPageserver/server.js在页内注入script window.__INITIAL_STATE__ ${JSON.stringify(initialState)}; /script生产模式下还会额外注入window.webpackManifestchunk 清单并引用 manifest 中带 hash 的vendor.js与app.js。客户端 client/index.js 拿到window.__INITIAL_STATE__后用它初始化 store实现服务端与客户端状态的无缝衔接。React Helmet 管理文档头页面组件如 PostDetailPage.js通过Helmet title{props.post.title} /声明标题服务端渲染阶段收集后写入 HTML实现 SEO 友好的标题与 meta。路由与客户端接管服务端完成首次渲染后浏览器端由 client/App.js 接管它用browserHistory构建Router包一层IntlWrapperreact-intl 的IntlProvider之后的所有路由切换都在客户端完成不再向服务器发起整页请求。数据获取与 REST API 层同构应用的关键在于同一套 API 两端复用。MERN 的 API 层由四部分构成路由server/routes/post.routes.js 定义了四类 REST 端点GET /api/posts 获取全部文章 GET /api/posts/:cuid 按 cuid 获取单篇文章 POST /api/posts 新增文章 DELETE /api/posts/:cuid 删除文章控制器server/controllers/post.controller.js 中addPost展示了完整的数据清洗流程先校验name/title/content三个必填字段再用sanitize-html对输入做 XSS 清洗用limax生成 slug、用cuid生成唯一 ID最后落库。模型server/models/post.js 定义了Post的 Mongoose Schemaname/title/content/slug/cuid为必填 StringdateAdded默认为当前时间。客户端 API 封装client/util/apiCaller.js 基于isomorphic-fetch其API_URL的取值逻辑非常关键export const API_URL (typeof window undefined || process.env.NODE_ENV test) ? process.env.BASE_URL || (http://localhost:${process.env.PORT || Config.port}/api) : /api;即浏览器端直接请求同源/api由 Express 反向代理转发服务端渲染与测试环境则请求http://localhost:8000/api可用BASE_URL环境变量覆盖。这样一套callApi(endpoint, method, body)就同时服务于客户端 action 与服务端数据预取。服务端渲染时列表页 dispatch 的fetchPosts()PostActions.js正是通过callApi(posts)拉取数据再将结果通过ADD_POSTSaction 写入 storePostReducer.js最终由服务端注入window.__INITIAL_STATE__。模块化组织领域模块的标准结构README 用一棵目录树展示了 MERN 的模块组织哲学——每个领域模块自成一体包含组件、页面、reducer、action 与测试。其规范结构如下. └── Post ├── __tests__ // 该模块的全部测试 │ ├── components // 子组件测试 │ ├── pages // 页面测试 │ ├── PostReducer.spec.js │ └── PostActions.spec.js ├── components // 子组件 ├── pages // 该模块对应的 React Router 页面 ├── PostReducer.js └── PostActions.js当前仓库的Post模块就是这套规范的落地实例client/modules/Post/ 下有components/PostCreateWidget、components/PostListItem、components/PostList三个组件pages/PostListPage与pages/PostDetailPage两个路由页面以及 PostActions.js 与 PostReducer.js对应的测试都集中在__tests__目录。根 reducer client/reducers.js 用combineReducers把app、posts、intl三个模块 reducer 合并起来。模块内的数据流遵循标准 Redux 模式页面组件connect到 store通过 selector如PostReducer中的getPosts/getPost读取数据dispatch action creator同步 action 直接返回对象异步 action 返回 thunk 函数如addPostRequest。国际化Intl 模块与语言切换MERN 内置了基于 react-intl 的国际化方案入口是 Intl/setup.js它做了三件事声明可用语言en、fr并在localizationData中按语言注册翻译文案引入intl浏览器 polyfillSafari 原生不支持Intl用intl-locales-supported判断是否需要打补丁用flattenMessages把嵌套的 message 对象拍平支持user {name} {value, plural, ...}这类带复数/格式化参数的 ICU 消息再addLocaleData注册语言数据。语言切换由 IntlActions.js 的switchLanguage(newLang)触发reducerIntlReducer.js会用localizationData[newLang]覆盖当前 locale 与 messages服务端渲染时IntlWrapper在两端共用同一套 store 状态保证语言一致性。Docker开发与生产的双环境编排README 提供了开发与生产两套 Docker 方案。V2.4 重构了镜像构建方式见 Changes.md改用单个 Dockerfile 多阶段构建 target来大幅加速构建。开发环境使用 docker-compose.yml含web与db两个服务docker-compose build # 修改依赖后需重新执行 docker-compose up # 或自定义端口 WEB_PORTyour_custom_port docker-compose up它把client、server、Intl、index.js、各 webpack 配置等文件以 volume 方式挂载进容器实现开发期的实时同步默认以NODE_ENVdevelopment运行。生产环境使用 docker-compose-production.ymldocker-compose -f docker-compose-production.yml up --build生产配置将宿主80端口映射到容器8000同样可用WEB_PORT覆盖并设置NODE_ENVproduction。重置数据库的命令是docker-compose down --volumes它会连同dbdata命名卷一起删除清空 MongoDB 数据。Dockerfile 的多阶段结构值得展开basenode:8→development安装全部依赖、复制源码、npm start→build执行npm run build npm run build:server产出dist→production只npm install --production安装生产依赖再COPY --frombuild复制打包产物以start:prod启动。这样生产镜像不包含任何 devDependencies体积与内存占用都被压到最低。代码生成器让脚手架自己长出代码这是 README 中篇幅最重、也最有特色的部分从 V2 起mern-cli内置了代码生成器可以自动生成组件、模块、服务端文件。生成器的蓝图blueprint放在config/blueprints配置文件是根目录的 mern.json。mern.json生成器配置mern.json的核心是一个blueprints数组每个对象对应一个生成器包含name、description、usage和files四个字段。README 给出的示例配置与仓库实际内容一致{ name: dumb-s, description: Generates a dumb react component in shared components, usage: dumb-s [component-name], files: [ { blueprint-path: config/blueprints/dumb-component.ejs, target-path: client/components/% helpers.capitalize(name) %.js } ] }一个files数组元素file object支持三个字段blueprint-path蓝图文件EJS 模板的位置target-path生成文件的落盘位置支持 EJS 语法parent-path可选参数当需要把文件生成到项目中某个已存在的目录内部时使用。target-path渲染时会注入三个变量name用户输入的component-nameparent特殊场景下从用户输入中获取的父目录名。README 给出的dumb-m示例即是典型用法{ name: dumb-m, description: Generates a dumb react component in a module directory, usage: dumb-m module-name/component-name, files: [ { blueprint-path: config/blueprints/dumb-component.ejs, parent-path: client/modules/% helpers.capitalize(parent) %, target-path: components/% helpers.capitalize(name) %/% helpers.capitalize(name) %.js } ] }注意这里的usage是module-name/component-name形式斜杠前的module-name会被作为parent传入斜杠后的component-name作为name传入——即一次输入同时确定父模块目录和组件名。helpers一个工具对象目前包含capitalize首字母大写。如果需要更多工具函数可以扩展 mern-cli 后提交。当前仓库的 mern.json 共定义了五个生成器生成器用途输入dumb-s在共享 components 下生成类组件dumb-s [component-name]dumb-m在模块目录下生成类组件dumb-m module-name/component-namefunctional-s在共享 components 下生成函数组件functional-s [component-name]functional-m在模块目录下生成函数组件functional-m module-name/component-namemodule生成一整套模块组件、action、reducer、样式、Express 路由、控制器、模型module [module-name]其中module生成器一次产出七个文件覆盖client/modules/Name/下的Name.js、NameActions.js、NameReducer.js、Name.css以及server/controllers/name.controller.js、server/routes/name.routes.js、server/models/name.js实现前后端一条龙的骨架搭建。蓝图文件EJS 模板蓝本质上是EJS 模板渲染时同样注入name、可选parent与helpers三个变量。以 config/blueprints/dumb-component.ejs 为例它会根据用户输入生成一个带propTypes的 React 类组件骨架import React, { Component } from react; import PropTypes from prop-types; class % helpers.capitalize(name) % extends Component { render() { return ( ); } } % helpers.capitalize(name) %.propTypes { }; export default % helpers.capitalize(name) %;模块级模板则更完整例如 config/blueprints/module/module.ejs 生成的是connectbindActionCreators的容器组件module-reducer.ejs 生成带初始状态的 reducer 骨架express-route.ejs 生成 Express Router 骨架model.ejs 生成 Mongoose Schema 骨架。拿到这些模板后新业务模块的开发起点不再是空白文件。Make your MERN打造你自己的变体README 还介绍了如何基于本仓库制作 MERN 的衍生版本比如集成 MaterialUI 或 JWT 认证的变体供mern-cli通过variants.json收录。核心步骤为克隆本仓库git clone https://gitcode.com/gh_mirrors/me/mern-starter自由改造添加依赖包、增加认证、调整目录结构、甚至把 Redux 换成 MobX同步修改 mern.json 与config/blueprints下的生成器配置让代码生成器与你的新结构保持一致克隆mern-cli项目在它的variants.json中登记你的项目信息并提交 Pull Request即可让你的变体被mern init直接使用。已知注意事项CaveatsREADME 末尾坦诚地记录了两个开发期已知问题理解它们有助于排查同构应用常见坑FOUC无样式内容闪烁为了让 CSS 的热更新在开发期生效开发模式下不会抽取 CSS 文件所有样式在脚本加载完成后以 BLOB 形式注入。因此开发环境下页面会出现约一秒钟的无样式闪烁这是设计取舍而非缺陷生产模式下 CSS 会被ExtractTextPlugin抽取为独立文件并在 HTML 模板中提前引用故没有此问题。客户端与服务端标记不一致警告开发模式下偶尔会出现 React 的 client and server markup mismatch 警告README 说明它完全无害根因是react-router的 hash 差异。官方建议使用match函数规避但一旦使用matchreact-hot-reloader就会失效——这是两个依赖之间的历史性权衡MERN 选择了保留热更新。测试体系命令背后的质量保障README 虽未单列测试框架但npm run test背后是一套完整体系见 package.json 的ava与nyc配置ava 负责用例执行nyc 负责覆盖率统计默认 reporter 为 lcov/text/html并有check-coverage脚本可要求四个维度 100% 覆盖setup-test-env.jsserver/util/setup-test-env.js负责注入 jsdom 环境、mock CSS Modules 与图片资源、配置 enzyme 适配器。测试分为两个层面单元/集成测试如 client/util/tests/apiCaller.spec.js 用 nock 拦截 HTTP 请求验证callApi的默认 GET 方法、请求体传递与错误透传接口测试如 server/models/tests/post.spec.js 用 supertest 直接请求真实 app 实例配合 mockgooseserver/util/test-helpers.js 中的connectDB/dropDB在内存中模拟 MongoDB串行验证文章的增删查四条接口。这套内存数据库 真实 HTTP 请求的测试方式让npm run test无需真实 MongoDB 也能稳定运行NODE_ENVtest时 server/server.js 会跳过数据库连接。结语从 README 的 Quickstart 到源码里的match服务端渲染、need数据预取约定、五套代码生成器与双环境 Docker 编排mern-starter 展示了一个成熟同构应用脚手架该有的完整形态开发期极致的开发体验热更新 热替换、生产期可靠的打包与部署链路、以及通过代码生成器把脚手架升级为半自动生产力工具。尽管项目已停止维护其同一套 API 两端复用、每次请求新建 store、静态 need 预取数据、EJS 模板生成代码等设计思路对今天构建同构应用或设计团队脚手架仍有直接的参考价值。本文中所有命令、配置与代码均可对照仓库根目录下的 README.md、package.json 与 mern.json 一一验证。赞分享后端前端【免费下载链接】mern-starter⛔️ DEPRECATED - Boilerplate for getting started with MERN stack项目地址https://gitcode.com/gh_mirrors/me/mern-starter点击查看免费下载相关推荐MicaForEveryone 实战指南用规则系统为 Win32 应用开启 Mica 背景材质MicaForEveryone 实战指南用规则系统为 Win32 应用开启 Mica 背景材质 Mica For Everyone 是一款面向 Windows桌面应用Farm 服务端渲染SSR实战指南从零构建 React / Vue / Solid 同构应用Farm 服务端渲染SSR实战指南从零构建 React / Vue / Solid 同构应用 导读 服务端渲染Server Side Rendering前端构建构建工具开发工具Cheerio与Express集成构建强大的服务器端渲染应用Cheerio与Express集成构建强大的服务器端渲染应用 在Node.js生态系统中 Cheerio快速HTML解析库 与 Express框架 的完美结网页爬虫后端上一篇gpx.studio浏览器中的专业GPX编辑器让GPS轨迹编辑变得简单下一篇终极Windows窗口管理神器WindowResizer完全使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考