网站架构-从无到有
注意:该文件比较长,写的是从没有项目起到项目上线的一些事,从大的方面约束规范到环境的配置、代码的模式、静态文件管理都有些概括,希望对你有帮助~
环境和域名
项目的开发基本是开发->测试->线上
的流程,那么项目开发前,约定好环境和域名,首先我们已知如下条件:
- 使用
git
版本控制,前、后端分2个仓库(repository),会有master线上
、develop开发
、release发布
3大分支 - 后端使用
php
语言,Yii2
库 - 项目域名
fe.com
- 项目静态域名
cdn-fe.com
,线上启用CDN
服务 - 分本地开发、联调测试、线上3个环境,每天环境都能独立的运行,当然前端fe可以不装数据库,但后端环境得有一套
- 后端代码目录:
/home/wwwroot/fe.com/
- 前端代码目录:
/home/wwwroot/cdn-fe.com/
- 全是本地开发,fe本地有静态服务开发,rd本地有后端服务+数据库服务
代码目录只是个例子
本地环境
域名为:fe.me
、cdn-fe.me
前端fe维护前端仓库
,当前如果前端也要"套模板"的话,本地也得有一套后端服务,当前数据库可以连接测试环境的
本地环境主要是前端fe开发静态页面使用,为的是方便快速开发,在develop
分支开发并每天下班前推送到联调环境,以供后端和团队其他小伙伴及时应用最新版本
联调、测试、发布环境
域名为:fe.dev
、cdn-fe.dev
该环境大多数在内网,测试服务器具有前、后端独立环境,并且具有数据库,联调环境fe.dev
迁出develop
分支,并在每天开发者push
的时候自动更新最新代码,后端开发静态资源连接该环境的代码cdn-fe.dev
在每次上线前,把要上线的版本合并到release
分支,并在测试服务器迁出release
分支为fe.release
和cdn-fe.release
域名,让qa
测试,测试通过后直接更新到线上
也就是说在这个测试服务器上具有4个站点:
- fe.dev - 项目最新代码,用来开发功能
- cdn-fe.dev - 静态项目最新代码,用来开发功能,主要是后端rd本地连接该资源
- fe.release - 每次版本要上线的代码(其实也可以说是沙盒)
- cdn-fe.release - 静态项目上线版本代码
这样的环境不冲突,不至于rd1和qa1在测试上线代码,而导致rd2的开发不正常,理论来说rd跟fe也一样,都是本地开发,只是rd连接的是测试环境(cdn-fe.dev)的静态文件,因为这个静态文件可以保证实时更新
线上
域名fe.com
、cdn-fe.com
,当release
版本测试通过,直接发布到线上
环境配置的问题
看着上面感觉乱乱的,其实都是配置文件,开发个Yii2
模块,让配置为用户配置 > 环境配置 > 默认配置
这样的规则去覆盖,用户配置
可以用电脑主机名为key
去设置,比如:
// 后端环境/config/默认配置.php
{
// 配置
"config": {
// 线上默认配置
"production": {
"数据库": "阿里云",
"域名": "fe.com",
"静态域名": "cdn-fe.com"
},
// 开发环境配置
"develop": {
"数据库": "内网测试机:dev",
"域名": "fe.dev",
"静态域名": "cdn-fe.dev"
},
// 发布/测试配置
"test/release": {
"数据库": "内网测试机:release",
"域名": "fe.release",
"静态域名": "cdn-fe.release"
}
},
// 当前环境
"env": "production"
}
然后我fe本地的是:
// 后端环境/config/fe1.php
{
// 配置
"config": {
"develop": {
"数据库": "测试机:dev",
"域名": "fe.me",
"静态域名": "cdn-fe.me"
}
},
// 当前环境
"env": "develop"
}
可见我本地有环境,但数据库我连接的是测试机的dev,而rd的本地可以是这样:
// 后端环境/config/rd1.php
{
// 配置
"config": {
"develop": {
"数据库": "本地",
"域名": "fe.rd",
"静态域名": "cdn-fe.dev"
}
},
// 当前环境
"env": "develop"
}
整个配置的思路感谢 @阿旭、@消寒 大神的指导,非常好用
可见rd本地域名是自己、数据库是自己,因为这样很方便修改,而由于rd不改静态文件,那么又想保证链接最新的静态文件,那就连接测试机的dev吧,因为不管前端后端,只要往develop
分支push
,测试的dev
版本就会自动更新。
当然配置式覆盖只是个思路,具体如何编写我想肯定难不倒你~
ps: 有次rd发现我个小问题,正好我也看到了,我顺手就改好了,然而rd刷新他的本地页面想稳定复现后找我来报bug,但发现我
push
后他那么好了,哈哈
多域名的好处
- 域名后缀使用语义化,比如
.me
就是本地、.dev
就是测试机开发分支、.release
就是要发布啊、.com
就是线上 - 多个域名可以达到
cookie
不共享,避免线上出问题,本地想查却发现cookie
共享了
规范
目录规范
前端仓库目录:
# 静态文件源码
./src/
# 一些公用模块,包括js、css、和图片
./common/
./jquery.js
./zepto.js
./dialog/
./dialog.js
./alert.js
./close.png
./popup/
./select.js
./select.css
./base.js
./base.css
# 单独的公用样式
./css/
./reset.css
./markdown.css
# 单独的公用图片
./img/
./loading-16.gif
./loading-32.gif
# 其他的以业务/方向名命名,比如:user、home、login
./{module}/
./img/
./login.png
./user.png
./page.js
./page.css
# 编译后的目录,里面目录结构同src一致,方便2个版本之间的切换
dist/
# 模板文件
./tpl/
# 公用的基础样式演示
./commom/
# 弹出层演示
./dialog/index.html
# 其他的以业务/方向名命名,比如:user、home、login
./{module}/
./index.html
./login.html
./xxoo.html
# 单元测试
./test/
为什么模板文件单独存放,而不是跟src/
静态文件在一起呢?是因为模板文件最终会给后端rd并套到php Yii2
框架里运行,前端只是本地开发方便而已
样式、交互规范
同ue、pm约定好基础样式、交互的规范,比如:
- psd确认稿单位、比例、标注
- 定义基础样式:栅格、颜色、间距、字号等
- 定义组件:幻灯、Tab、筛选、手风琴、输入框、下拉框等
- 数据加载:加载中、加载超时、加载出错、数据为空
- 样式规范抽离成公用组件文档
前、后端联调规范
- 接口规范、数据规范 - 因为约定好这些前端就可以使用
mock
数据而不依赖后端开发,达到并行开发- 接口参数类型和值
- 返回值结构和类型(强制类型)
- 统一返回状态码
- 接口路径(版本、语义化)
- 模板代码注释,比如告诉套模板的人如何使用页面上的元素状态(登录前用xx,使用后用xx)、如何循环列表
- 约定整个项目的公用模块,比如公用的
layout
、商家中心的layout
- 文档
静态文件引用规范
本地开发时在/tpl/*.html
里引用静态文件是:
<script src="/src/xxoo.js"></script>
<link rel="stylesheet" type="text/css" href="/src/xxoo.css">
这样是可以快速开发,但由于前、后端不是一个项目目录,又不想每次修改个静态文件还需要上线 静态文件+后端文件,但不编译后端如何处理静态文件版本号呢?
如果在后端项目里使用前端的一些编译语法,这样导致修改个静态文件后端就得编译,不然版本号对不上,当然你会说把后端的View
层也放在前端项目里,做到前、后端模板分离,其实也可以,但模板里就会有些数据相关的问题,我们这里使用这样的方法处理静态文件:
- 后端扩展一个
php
的方法,启名为STATIC_FILE
,后端模板(View层)里所有引用静态文件都使用该方法 - 前端在
release
的时候把代码编译并生成一个md5
映射文件存放在前端项目/static_map.php
中 - 模板里使用
<script src="<?php echo STATIC_FILE('common/login.js')?>"></script>
,文件路径基于src/
开始,因为这样可以很好的在src dist
两个版本切换
再配置里添加个是否使用md5
版本和使用src
还是dist
的参数,如:
{
// 配置
"config": {
// 线上默认配置
"production": {
"static_model": "dist",
"static_md5": true
},
// 开发环境配置
"develop": {
"static_model": "src",
"static_md5": false
},
// 发布/测试配置
"test/release": {
"static_model": "dist",
"static_md5": true
}
}
}
这样就是线上使用压缩版本dist
并且开启md5
功能,而开发环境既使用开发代码src
也不开启md5
功能,由于测试\发布环境就是沙盒所以必须跟线上一致。
前端生成的static_map.php
大概长这样:
<?php
$STATIC_FILE_MD5 = array(
'common/login.js' => '123456',
'user/index.css' => '123456',
);
STATIC_FILE
方法大概长这样:
<?php
# 假如 $CONFIG 就是配置变量,是由 【用户配置 > 项目配置 > 默认配置】 合并过来
/**
* 获取静态文件路径
*
* @param {string} $uri 文件uri,以静态项目里的src为起始目录
* @return {string} 文件的绝对路径
*/
function STATIC_FILE ($uri) {
# 静态域名 + 静态模式(src,dist) + 路径
$url = $CONFIG['静态域名'] . '/' . $CONFIG['static_model'] . '/' . $uri;
# 如果没有开启md5则直接返回路径
if (isset($CONFIG['static_md5']) && $CONFIG['static_md5'] === false) {
return $url;
}
# 处理md5版本号
# 加载前端项目编译后的md5文件
include_once(前端项目 . '/static_map.php');
$version = '';
# 如果md5版本里有该文件
if (!empty($STATIC_FILE_MD5[$uri])) {
$version = '?' . $STATIC_FILE_MD5[$uri];
}
# 把版本号追加上返回,当前如果不存在md5也就忽略
return $url . $version;
}
这里有个问题就是说,前端项目/static_map.php
这个配置文件,他只能跟在前端项目仓库里,如果把她放在后端仓库也那也就成了前端改静态文件也需要上后端项目
的问题。
这样比如前端要改个样式,只需要改好测好后编译下,把前端代码一上线即可,线上影射
文件一更新就会生成新的md5
文件路径,比如:
前端项目里开发静态页面
<script src="/src/xx.js"></script>
后端项目里
<script src="<?php echo STATIC_FILE('xxoo.js')?>"></script>
而STATIC_FILE('xxoo.js')
会根据环境的配置生成不同的,比如:
测试环境关闭了md5并且引用开发代码src
http://cdn-fe.dev/src/xxoo.js
上线开启了md5并引用压缩代码dist
http://cdn-fe.com/dist/xxoo.js?qwerty
发布/测试环境同线上一致
http://cdn-fe.release/dist/xxoo.js?qwerty
你可能会说为啥不是xxoo_qwerty.js
呢?我想说你随便啊,改这个不过就是改下编译脚本和STATIC_FILE
方法而已~
前端项目
开发模式
前端使用requirejs
开发,页面中始终加载一个入口文件,这个入口文件在src
里只是个加载器和项目配置,而在dist
环境就是一些公用某块打包合并之后的,这样可以减少请求,通常这个引用是在公用的layout
里,比如:
<body>
这里是其他的block
<script src="<?php echo STATIC_FILE('common/main.js')?>"></script>
...
<script>
require(['home/index', 'common/xxoo']);
</script>
</body>
</html>
src/common/main.js
文件内容是:
// requirejs代码
// ...
//requirejs配置
requirejs.config({
// 默认主路径是src/
baseUrl: '../',
// 其他的路径映射
paths: {},
//...
});
而dist/common/main.js
文件是这样:
// requirejs代码
// requirejs配置代码
// jquery.js
// util.js
// ...
当然你可以有很多公用文件,比如频道的公用文件、某模块集合的配置文件,我们在开发时全使用异步加载,在发布、测试时前端使用脚本编译处理下,当然编译前\后的模块路径不换,也方便文件定位
项目配置
前端项目配置在/package.json
里,大概如:
{
"name": "前端项目",
"version": "1.0.0",
"description": "xx网前端项目",
"srcipts": {
"server": "http-server -p 8080",
"start": "npm run server",
"test": "mocha --reporter spec --timeout 5000 --recursive test/",
"test-cov": "istanbul cover _mocha -- -t 5000 --recursive -R spec test/",
"fecs": "fecs check src/ test/",
"release": "",
"watch": "",
"debug": ""
},
"devDependencies": {
"http-server": "*",
"mocha": "*",
"istanbul": "*",
"fecs": "*"
}
}
说下几个命名:
npm run server, npm run start, npm start
- 本地开启web server
功能,用来开发静态页npm run test
- 使用mocha
运行单元测试npm run test-cov
- 使用istanbul
运行测试覆盖率npm run fecs
- 使用fecs
检查代码规范npm run release
- 编译发布代码,把src/
文件编译、混淆、压缩、合并到dist/
目录里,把dist/
目录里的所有文件生成md5
并生成static_map.php
npm run watch
- 本地监听静态文件修改并实时刷新浏览器npm run debug
- 本地开启web server
,配置host
修改把线上域名cdn-fe.com
域名到本地环境,并可配置dist->src
或者src->dist
的映射,方便调试线上代码
至于
watch
、debug
、release
这些脚本,我想你可以根据你的项目写出来,但不使用全局的包,目前webpack
、gulp
都支持非全局使用
开发流程
这里的开发流程只是说前端的一些流程工作,不包括诸如跟pm对需求这些
制作静态页面
develop分支:根据psd确认稿开发成本地的.html
文件,可以使用npm run *
的一些命令更好的本地开发,必须在文件内写好相关注释,开发完成(自测通过)后可在测试静态环境(cdn-fe.dev)让ue、pm确认效果,当然这里也只能确认下效果,逻辑还得出整个测试环境才可以测,在这里确认效果方便fe修改,不然套完模板告诉你有问题你就哭吧
套模板
develop分支:把静态项目里的.html
文件代码片段根据注释编写出后端View
层代码,并写相关的逻辑处理,某些功能告一段落后可以让联调
发布版本
在准备上线时把前端代码编译处理,再把开发分支代码合并入release
分支,推送到测试机,让qa测试该版本的release
代码,通过后给该版本打上标签,并合并入master
、develop
上线
线上永远是master
,只要合并入master
分支表示代码没有问题,上线可使用线上机器
拉分支,或者.tar
包上线,当然这些看你如何处理了,线上只要一更新到master
代码,就表示项目上线
修复线上问题
前端项目上如果发现线上问题,可在本地把master
迁出一个fix-*
分支,可以使用npm run debug
命令把线上压缩代码重写到本地开发代码以方便调试,代码没问题后,合并release
,并测试,通过后合并到master
上线并打上标签,并合并到develop
整个架构的特点
优点
- 本地开发,开发代码不依赖于编译才能查看
- 合理的使用
npm run
功能,杜绝使用全局包(开发只需要克隆仓库和npm install
安装依赖即可 - url规则重写方便调试线上问题
- 静态文件自动
md5
,而不用担心上线需要编译后端模板 - 多环境配置不冲突,如果你们是
session
单点服务器,可以相应的调整下配置的方案
缺点
- 线上后端机器也需要迁出一份前端代码,因为需要前端的文件
md5
映射
总结
你会不会说为啥感觉这么多额,其实当你整个走下来也没什么,至于说不用requirejs
而用commonjs
或者es6
啥的,亦或者说用fis3
,使用react
不使用smarty
之类我认为大的流程上都是大同小异,开发模式很多,好的不一定适合自己项目~
当然可能考虑的还不够多,欢迎吐槽~