掘金从上线到如今,网站的前端重构了 3 次,后端也陆陆续续修改了整个网站的结构 2 次,可是随着业务不断推演复杂,团队人手增长,有须要一波进一步的优化!javascript
这周,咱们会根据当下掘金的状况和接下里的主要业务,整理代码。css
在架构开发以前,咱们首先要梳理现有的网站状态和需求,而后再作优化html
Node.js
因为网站的早期开发是我来负责的,虽然我曾经写过 PHP
、Ruby on Rails
和 Python
的后台,但由于本身同时写不少前端代码所以对 JavaScript
最熟。与此同时,咱们选择了 LeanCloud 做为咱们的云存储、推送、托管平台,于是就继续使用了 Node.js
为开发框架。前端
v4.4.5
Express.js
由于选择了 LeanCloud 的缘故,其后端你托管要用 Express
加上自己对这个框架的使用还算熟练,便沿用了下来。vue
4.13.3
上面已经讲了咱们选择了 LeanCloud,具体来说咱们使用了以下功能:java
LeanStorage
:数据存储LeanMessage
:移动应用推送Leanengine
:云引擎
LeanAnalytics
:数据统计工具1.0.0-beta
因为掘金网站的主页面是一个纯前端应用,其部分业务会与后端数据接口直接关联。下图主要展现了咱们后端的总体状态:node
其中黑色的箭头表示业务需求,而黄色箭头表明数据更新需求。webpack
网站服务器是整个应用最基础的部分,它处理网页端的页面、API 请求,外联用户信息和 LeanStorage 数据库。git
config.js // 后端配置文件
server.js // 服务器启动脚本
app.js // 后端业务,被 server.js 引用
cloud.js // 云函数定义,被 app.js 引用
webpack.config.js // webpack 打包配置文件复制代码
根据不一样的环境(生产环境、前端开发、后端开发),config.js
定义了各个开发环境须要的配置文件信息。例如在 package.json
的 npm scripts
里会定义:github
"dev": "cross-env NODE_ENV=devFrontend supervisor -i vue,node_modules server"复制代码
这样,npm run dev
就会设置当下的 NODE_ENV
是 devFrontend
,也就是前端开发环境。
接下来 server.js
就会根据当下环境读取的配置文件开启服务端业务,如 app.js
定义的后端业务代码或 cloud.js
里的云函数。固然,在不一样环境下 webpack
也会作不一样的操做。
app.js
app.js
援引了配置文件后,执行 Express.js
框架,开启业务代码。除了基本的 middleware(页面 template engine jade
, cookie, session 等等),其最主要的业务包含:
routes
auth
因为网站大量使用前端 router,于是在 routes
定义中也要格外当心,掘金的开发方式是这样的。例如 Styleguide 页面,多是这样的定义的:
// app.js 文件
// routes/page.js 里定义了网页相关的路由及后端渲染
var page = require('./routes/page');
// app 绑定了 styleguide 页面的路由,及几个前端路由统一
app.get('/styleguide', page.styleguide);
app.get('/styleguide/base', page.styleguide);
app.get('/styleguide/components', page.styleguide);复制代码
cloud.js
云函数LeanCloud 很好地支持了云函数,能够帮助你完成后端的数据触发性 Hook 脚本及定时脚本。
例如每一条评论存储在 Comment
Table 里,那么对于这条评论,咱们能够捕捉到
beforeSave // 存储以前
afterSave // 存储以后
beforeUpdate // 更新以前
afterUpdate // 更新以后
beforeDelete // 删除以前
afterDelete // 删除以后复制代码
加入,咱们想要实现一个功能,就是增长一个 comment
后,更新相应的文章的评论数加一,而删除后则减一。
// cloud.js 文件
// cloud/comment.js 定义了关于 Comment 数据的 Hook 函数
var comment = require('./cloud/comment');
AV.Cloud.afterSave('Comment', comment.afterSave);
AV.Cloud.afterDelete('Comment', comment.afterDelete);复制代码
// cloud/comment.js
exports.afterSave = function(request, response) {
... // update 相关文章的数据
if (ok) {
response.success();
} else {
response.error('...');
}
})复制代码
webpack.config.js
webpack 是一个模块打包工具,随着它的插件、业务愈来愈强大,它也像是以前的 grunt
和 gulp
同样分摊了一部分脚本自动化的功能。
webpack.config.base.js
:基本的打包配置文件,主要用于开发环境热更新webpack.config.prod.js
:生产环境的配置文件,引用了 base
,定义打包需求,生成 build 好的文件这里我就不展开关于 webpack 自己的配置优化的部分。
除了上面说的最基本的服务器开启文件,整个项目的代码结构以下:
config.js
server.js
app.js
/routes // 各个路由的后端业务逻辑
/views // 网页渲染的 jade 文件
/vue // 各个页面的 vue 业务逻辑
/redis // 缓存定义
/public // 外部访问的静态文件
/assets // 后端静态文件
/data // 后端静态数据
/scss // SCSS 样式文件
cloud.js
/cloud // 云函数相关定义文件
webpack.config.js // webpack 打包配置文件
webpack.config.base.js
webpack.config.prod.js复制代码
咱们再以 Styleguide 为例,若是咱们要添加这样的一个网页代码:
app.js
的路由的哪一个模块下,/styleguide
是一个独立页面,于是它应该被定义在 /routes/page.js
里,并定义到:// page.js 文件
exports.styleguide = function(req, res) {
res.render('styleguide', {
title: '掘金前端 Style Guide'
})
}复制代码
// app.js
app.get('/styleguide', page.styleguide);复制代码
/views
里面定义 /views/styleguide.jade
/vue
里定义,并要在 webpack.config.base.js
里定义打包逻辑page.js
里后端渲染页面是传入数据/assets/scss/styleguide.scss
(更多 CSS 结构咱们会在另一篇文章中详细描述)这样,整个 Styleguide 页面会影响到的后端代码是:
app.js // 路由绑定到 /styleguide
/routes
page.js // 定义了 styleguide 后端业务
/views
styleguide.jade
/vue
/styleguide // styleguide 相关 vue 前端业务
main.js
app.vue
/assets
/scss
/pages/styleguide // [optional] styleguide 内的复杂组件样式
__style.scss
layout.scss
...
styleguide.scss // styleguide 相关独立样式
webpack.config.base.js // 定义新的 styleguide 相对应的 entry复制代码
举例,咱们要开发一个数据的 Hook 函数到 LeanCloud,好比说每当一个新的 Comment 生成的时候,咱们要更新对应文章的评论数及最新的评论:
cloud.js // AV.Cloud.afterSave('Comment', comment.afterSave)
/cloud
comment.js // exports.afterSave = function(request, response) {}复制代码
/cloud/____.js
文件里cloud.js
文件注册脚本,如:AV.Cloud.define('cloudFunctionName', functionName)
config.js
里,不要用 if
和 else
语句区分npm scripts
绑定运行函数,如:npm run dev // 开发,测试数据
npm run dev-backend // 后端开发,测试数据
npm run dev-build // 测试数据,打包
npm run prod // 开发,生产数据
npm run prod-backend // 后端开发,生产数据
npm run prod-build // 生产数据,部署前的打包
npm run test // 测试
npm start // 开启服务器复制代码
getPopEntries
能够明确到 getPopularEntries
npm install/uninstall PACKAGE --save/--save-dev
,随时更新库I_LOVE_YOU
用下划线加大写字母iLoveYou
驼峰,不管是普通变量仍是函数名ILoveYou
首字母大写的驼峰,包括 LeanCloud 本身的数据 Table 名_iLoveYou
加前置下划线AV.User
,不要使用 '_User'
Promise
,将复杂的业务改写为清晰的异步处理流:start()
.then(step1)
.then(step2)
.then(step3)
.then(step4)
.catch(errorHandler)
.finally(callback)复制代码
always
替换了原有的 finally
AV.Promise.error
替换了 Promise.reject
AV.Promise.when([promise1, promise2, promise3])
能够在三个 promise 都完成的状况下作异步操做exists
, startsWith
, matches
等select
拿去部分数据query.first()
和 query.get(id)
,可是注意:
first()
后 then(function(obj) {})
中的 obj
多是 null
get()
后若是得不到数据会直接引起 error
AV.Object.createWithoutData(TABLE_NAME, ID)
来实现指针查询,无需取一遍数据query.include('reply.user')
,一个文章查询能够用这类语句直接查出来一个评论的回复的用户数据,也就是说拿出来的一个 comment
能够访问到 comment.get('reply')
和 comment.get('reply').get('user')
。matchesQuery
origin/
master // 线上版本
|- hotfix-login // 热修复,如登陆异常
release // 最新的要部署的版本
develop // 开发分支
|- feature-homepage-v2 // 正在开发的业务,如第二版的首页
|- feature-timeline-api // 正在开发的业务,如 Timeline 的 API
developer-ming
master
release
develop
|- feature-timeline-api // 我正在开发这个 feature,不断和 origin 同步复制代码
develop
fork 出来一个新的 branch feature-name
feature-name -> develop
,记得打 label 到 feature
feature-name
里修复develop
上不断 merge 新的 review 过的业务功能develop -> release
npm run build
打包业务代码,准备部署publish
release -> master
,标注版本号hotfix
分支develop
和 release
的同步,用 git rebase
develop
及 feature
分支不作 build
操做根据产品接下来的发展路径,有几个重要的功能须要优化。