欢迎一起探讨各自在前端开发中遇到的工程问题
fouber opened this issue · 204 comments
恩,这个repos虽然命名是blog,一开始也定义为我的个人博客,但感觉光我一个人码字也挺空虚寂寞冷的,所以希望有在前端工程方面遇到问题,或者有所感悟,或者有所实践的同学,可以尽情在issues里开启话题来与大家分享交流,有个比较集中的地方讨论有关前端方面的工程问题也挺好的不是~~
工作5年了,一直处于公司需要什么研究什么的的状态,一直没仔细认真的去研究过某项东西,之前做的项目都是基于局域网的管理系统,现在准备做web项目,但是从来没设计过这方面所以有些不知所措,不知道该怎么做,后来通过网络了解了前端模块化的概念,也使用了seajs等,最近有开始接触fis打算想把这两个混合用,但是不知道怎么下手,不知道楼主有没有什么好意见
@lucifercha
我来凑个热闹。
不建议将 seajs 与 fis 混合在一起用,建议用 webpack + fis3
seajs 后续衍生了 spm , spm 后续因为webpack, spm 停止维护了。
如何与 fis 的 map.json 结合构建一个基于 git 和 node 的前端静态资源服务器?
不了解 fis 的 map.json 的请点击此处
以前前端静态文件是与后端 server 一起发布的,现在有一个服务器可以用于做专门的静态资源服务器。
目前想法:
- 使用 github 做版本控制和 code review
- 静态资源服务器基于 node 和 nginx
- 使用 fis 作为前端工程核心,md5化所有静态资源文件(map.json 在后端 server 上)
我现在遇到 2个关键问题
-
用哪种方式发布代码到静态资源服务器?
-
基于map.json 后遇到问题如何做版本回滚?
没配置过独立的前端静态资源服务器,希望各位能分享出自己团队的做法和给我一些建议。
可以新开issue。。。
关于代码发布
发布静态资源看你们自己的部署方案吧。一般是公司内撘一个ci系统,比如jenkins,然后在上面安装运维提供的部署脚本,然后配置gitlab(通常不用github,防止泄密)的webhook,一有提交就发请求到jenkins上,jenkins拉取代码,调用fis构建,然后把产出的代码通过运维脚本推送到测试或者生产环境。大致的流程是:
- 内网搭建gitlab/svn
- 内网搭建ci系统(jenkins)
- 在ci系统所在机器上安装fis、运维推送脚本
- 配置gitlab的webhook,一有提交就发请求给jenkins
- jenkins中创建job,填写gitlab中的url,填写hook脚本
运行效果:
- 开发人员提交代码,gitlab触发webhook,推送信息到jenkins
- jenkins根据推送的信息执行对应的job
- job中的脚本clone对应的分支,调用构建工具对代码进行构建
- 使用运维脚本将构建完成的结果推送到测试/生产服务器。
上图中的SCRAT是我们基于FIS定制的自己的工具
总之效果就是【提交代码】→【自动部署】,【自动部署】包括了【构建】+【代码推送】
关于map.json回滚
其实每次发布,都可以把构建好的代码生成一份tar包存到代码库里,生产/测试/开发环境可以自由切换任意版本的包。服务端的包自然携带了map.json,切换哪个就代表了回滚哪个。静态资源不用回滚,丢在静态资源服务器就好了
@fouber
在 fouber/blog 新开 issues 么?
感谢回复,目前我们没有运维人员。
考虑使用 githook master分支同步部署
本地 fis 构建 -> 提交构建后的代码 -> githook部署
版本回滚通过回滚后端中的 map.json 实现。
区别于 server 构建缺点应该是在 git 中会存在很多构建后的 md5后缀文件。
对于这些 jenkins
运维脚本
都完全不了解,公司也没有相关的运维人员。小型团队的前端开发人员想要构建一套代码发布系统应该通过哪些渠道和学习哪些知识可以做到这件事?
这里的运维脚本包括中 fis 的构建代码么?
或者使用另外一种手动发布的方案
前端生成环境使用git/svn,需要发布时使用 fis 的deploy-部署配置 和 fis-deploy-git 手动发布构建后的代码到前端静态资源服务器。
没有运维人员的情况下就做好版本控制和能快速控制版本回滚。
有一些低成本的做法,借助git:
-
创建两个仓库(注意,是仓库,不是分支):
release
:包发布仓库,用于管理包和回滚source
:源代码仓库,用于开发
-
在
source
仓库中准备一个release.sh
脚本,大致的内容是:fis release -Duolmpd ./output # fis构建到output目录 cd output # 进入output目录 pack_time=`date -d now +'%Y%m%d%H%M'` # 构建时间 pack_name=build-$pack_time # 压缩包名称 tar zcf ../$pack_name.tgz * # 打一个压缩包,带时间戳的 cd .. # 回到上一级目录 rm -rf output # 删除output git clone ``你的release仓库`` # 克隆release仓库 cd release # 进入目录 mv ../$pack_name.tgz . # 把之前打的tgz包贴过来 git add -A && git commit -am "xxx" git push origin master # 提交
-
在你的
release
仓库中准备一个deploy.sh
脚本,你可以给它传参数,指定某个具体的tgz文件,这个sh脚本的功能就是解压缩对应的代码包,然后把内容推送到部署服务器上。
你的 source
仓库看起来内容是:
source
├─ components
├─ views
├─ server
├─ release.sh
└─ fis-conf.js
你的 release
仓库的内容大概是:
release
├─ build-20150328143321.tgz
├─ build-20150410162404.tgz
├─ build-20150628203349.tgz
├─ ...
└─ deploy.sh
要上线(或回滚)什么包,就在release仓库下执行:
./deploy.sh build-20150410162404.tgz
webpack+gulp,基本可以解决模块打包和上线的问题了 😄
@fouber
关于 release
source
git 低成本方案。
回滚可以通过操作后端服务器中 map.json 控制,那么 ./deploy.sh build-20150410162404.tgz
的操作是否可以省去?
而 release
仓库直接就是正式环境 git
webpack和gulp都只是构建工具,上线部署和包管理还需要额外的工作
release是保存各种历史发布包的仓库,你需要这个仓库以方便快速回滚啊,map.json只是资源表,回滚不仅仅回滚前端资源表,还有你的后端模板也可能需要一并回滚
@nimojs 静态资源服务器上的内容可以只增不减,但你的服务端模板以及模板所使用的map.json还是需要整体部署或回滚,只改map.json可能会导致模板与静态资源内容不匹配而报错
@fouber
那么构建后的前端静态资源文件是必须做版本控制以方便回滚?
构建后的前端静态资源文件可以通过 *.tgz
的方式备份?
看来还是 有一些低成本的做法,借助git 方式最合适。
按照这个方法,每次版本更新需要开发人员在 release 仓库运行 ./deploy.sh build-20150410162404.tgz
?
如果你的静态资源服务器和模板服务器是不同的webserver,比如静态资源要推送到七牛上,那么你可以:
- 修改source仓库的release.sh执行过程:
- fis release
- 将静态资源推送到静态资源服务器
- 只把模板(或页面)和map.json打包到tgz文件中
- 每次发布或回滚,只需要开人员去release仓库执行deploy.sh即可,切换的都是模板和配套的map.json,静态资源始终扔在静态资源服务器
@fouber
感谢
我们 php server和 static server 是分开的。
那么最终可以采用:发布到测试环境时提交后端模板、map.json 、执行 deploy.sh 发布静态资源到static server。
测试环境发布到正式环境由后端解决,而前端资源在发布测试机时就已经部署好了。
@fouber 感谢,我这里现在还没有包管理的需求(主要是太小了....),暂时webpack和gulp直接可以解决上线部署的东西,不过看了这些开阔了视野了!!!
楼上的各位讲的都太高大上 看的云里雾里的
@jialezhang
上线部署是指项目构建之后,需要把构建好的代码上传到服务器上,上传的过程有些团队用的是ftp,有些用的是rsync,这两种方式有对应的gulp插件,个人开发可以直接上传。不过对于团队要长久维护一个大一些的项目,上线/回滚是很常见的运维操作,每次部署的历史版本都应该保留,在新版本有问题的时候方便立刻恢复之前的版本,在服务器挂掉之后方便立刻部署到其他机器上。
多谢 @fouber 大神讲解!这些东西估计要等到有好几个人一起开发才需要了
@fouber
重新整理了下思路,请帮忙看下如下方案是否靠谱:
(以下源码指的是未处理的 less sass 等文件)
- 使用 git 管理源码。分支 PR 到 master (master 中只有源码)
- 当 master 有修改后 webhook 推送到 node server
- node server 接受文件,在 server 中对源码执行 fis 构建
- 将构建后的代码发布到前端静态资源服务器
这样源码在 git中做版本控制。需要回滚就回滚 map.json。(其实就是 fouber 给出的第一个方案中去掉 jenkins 的版本)。开发人员只关心2个步骤 【本地编码看效果】 -> 【GIT RP 到 master】
但是 在 server 运行 fis 构建后得到的 map.json 如何发布到后端服务器我不知道怎么做合适。
目前想出2种做法:
- 当构建完成后像后端 server 提供一个接口发送 map.json 文件?
- 当构建完成后在 server 中将 map.json commnit 到一个单独的 git 中,这个单独git 的 webhook 触发后端脚本将 map.json 部署在后端服务器中?
@nimojs
你没有使用后端模板吗?如果使用了,每次回滚map.json也要回滚模板的。
关于map.json推送服务器的问题,可以考虑用rsync,建立两台机器的信任关系,然后rsync推送
@jialezhang
能交流一下你们webpack+gulp的工作方式吗?
@Jokcy
webpack:管理JS之间的依赖,(写React的时候很爽 hot-loader); 会用ES6,webpack上babel。
gulp: SCSS预处理(之前会处理coffee),后期静态资源链接替换(gulp-rev)。
当然webpack的功能远不止如此.....我只是用了冰山一角
有没有介绍FIS的灰度发布相关的内容
我理解下来,灰度发布可以通过类似于<%require_js("core:core.js")%>这样的require_js方法来控制读取不同版本的文件。同时,获取mapjson的时候也需要返回不同版本才行。想法不太成熟。
有没有介绍FIS的灰度发布相关的内容
问下楼主一般网站的主页静态页面是自动生成的还是通过后端管理系统手动生成的,还有现在一般用什么方式生成静态页
民工兄,最近一直也在思考前端构建的问题,你的文章里面涉及到静态资源更新似乎都是可以直接更新整个html文件的,但是如果不是html,而是php之类的文件,而且这些文件是由后台的程序员来更新和维护(也就是说前端工程师没办法直接更新php文件),那么涉及到一些静态资源的更新就没办法那么简单的直接替换静态文件的链接了,每次md5之后还需要告诉后台来更新链接,请问有什么好点的解决方法吗。
@a-lian123
前端工程师应该争取模板及模板引擎的控制权!!!
写php的模板对于前端工程师来说没什么难的吧,不过确实很多公司对这个是抓住不放的,会有“静态页面模板化”这样的研发流程,这种流程其实非常反工程,不然也不会有这么多有关“前后端分离”的讨论。
不吐槽了,我先假设你们团队由于某种不可抗力确实无法把“静态页面模板化”的流程从整个研发环境中剔除,然后给出一些我的意见。
首先,假设你们的前端项目只有三个文件:index.html、index.css和logo.png。其中index.html代码中写了对index.css和logo.png资源的引用,形如:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
...
<img src="logo.png" />
</body>
</html>
基于我其他文章说到的静态资源更新思路,通过实现给文件添加指纹来解决资源缓存问题,这样,你的html构建之后应该变成:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/public/css/index.a8f79ad.css">
</head>
<body>
...
<img src="/public/img/logo.053a8e6.png" />
</body>
</html>
如果把以上代码发给后端去做“模板化”,很明显,当项目仅更新了css文件之后,由于资源的md5戳改变,html中的资源引用会发生更新,就不得不让后端再搞一次“模板化”,他们知道你只是修改了css,html又没变过,肯定不理解为啥还要再模板化一次,觉得前端都是**。
你的目标其实是:
如果项目迭代只是修改了静态资源,而没有修改主文档模板,应该不需要再次进行模板化的研发流程
按我经常絮叨的那套东西来说,解决这个问题的方案就是——基于表的静态资源管理系统。
回到最开始的前端HTML页面代码:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
...
<img src="logo.png" />
</body>
</html>
调整你的构建工具,使得最终产出结果并不是在页面上直接引用带md5戳的资源,而是变成这个样子:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="<?= uri('logo.png')?>">
</head>
<body>
...
<img src="<?= uri('logo.png')?>" />
</body>
</html>
即把你交付后端做模板化的页面变成一个PHP代码,替换源码中的资源定位,将其变成一个php函数调用。
相应的,你的源码目录结构可能是这样的:
src
- index.html
- index.css
- logo.png
而构建之后的结果是:
src
- index.php
- public
- css
- index.a8f79ad.css
- img
- logo.053a8e6.png
- map.json
其中public目录部署到静态资源服务器,index.php交给后端做最终的模板化,而map.json就是我说的“资源表”了,内容大致为:
{
"index.css": "/public/css/index.a8f79ad.css",
"logo.png": "/public/img/logo.053a8e6.png"
}
好了,现在你想办法说服你们后端工程师,在php中添加那个 uri
函数,其代码大概是:
function uri($id){
$map = json_decode(file_get_contents('map.json')); //此处可优化
return $map[$id];
}
也就是说,页面上的资源地址,变成通过读表动态输出。这样,你们每次项目迭代,如果没有修改模板,只要更新静态资源和资源表,php模板就能正确加载更新的资源了。
列一下这个方案的改造成本:
- 修改你们的构建工具,给静态资源加md5指纹戳;
- 修改你们的构建工具,将js/css中的资源定位直接替换为部署路径(带md5);
- 修改你们的构建工具,将html中资源定位变成php函数调用,再交付后端模板化;
- 修改你们的构建工具,构建之后输出静态资源表,表中记录资源的部署路径(带md5);
- 跟你们的后端协商,晓之以情,动之以理,请求帮忙实现这个uri函数:读表返回资源部署路径。
上面的清单前4项都是可以控制在前端内的工作,只有最后一项需要后端配合一下,但其逻辑其实非常简单,就是查表返回资源的部署路径,不会带来多少性能负担,却可以实现缓存控制,提升页面性能,相信有足够的理由推动其实现。
最终完成以上工作之后,前端再发布就不用担心资源引用问题了,因为你们可以通过更新资源表实现页面资源的更新,模板文件不用二次处理。
以上方案其实算是比较折中的办法,在后端的威压下委曲求全的结果。我觉得最好的实现还是想办法争取模板及模板引擎的控制权(重说三),将“模板化”的流程彻底从整个研发过程中剔除掉,比较容易针对前端做更多的性能优化策略,定制更加合理的组件化开发方案。
毕竟大家都是为了项目好,谁也别惯着谁。
赞同,前后端谁的话语权大,谁的项目就更好组织和架构。努力成长为全栈吧
支持 基于表的静态资源映射系统
FIS的亮点在于静态资源表,而绝大部分场景都需要与后端模板结合才能发挥最大的价值。但发现很多团队还是前端写HTML交付给后端的工作流程。
基本上高效的团队都是前端控制后端模板,而一些小团队不能实现前端控制View我觉得主要原因在两点:
- 前后端都没有让前端可以写模板的意识
- 没有合适的mock 工具支持前端写后端模板
我目前是用自己写的 mock server 实现前端在Node 中写页面渲染配置,然后本地的PHP 127.0.0.1:1234 端口提供一个 PHP Smarty 渲染接口。
配置代码
$.page({
title: '退订',
url: '/unsubscribe/',
template: 'www/unsubscribe.html',
data: {
email: 'mail@qq.com'
}
})
模板 www/unsubscribe.html
email: <input name="email" value="{{$email}}" />
<input type="submit"/>
当用户访问 127.0.0.1:3000/unsubscribe/ (node server)时 node 会将 url template data 都 post到 127.0.0.1:1234 然后 127.0.0.1:1234 直接执行PHP Smarty 渲染返回HTML。
最近跟一些朋友交流的时候发现其实他们是有后端模板的控制权的。都是先写HTML交付后端,然后后端转换为后端模板后就必须在PHP 环境中写模板了,但是因为没有 mock server 的支持后期维护非常麻烦。
我们是所有数据都在mock server 中模拟好,最终只交付给后端文档和模板。fis-plus 好像是 Java 调PHP实现的,但也都是可以在本地模拟后端模板渲染的。
—
其实去年也就请教过了 fouber 关于前端写模板的事,那时候是在 node 中解析 handlebars ,然后让后端用 php 版的 handlebars 。也是个坑,fouber 当时也提醒过了,后来参考fis-plus 才想到可以直接让PHP解析模板。node 的 mock server 当做一个中间层调 php 接口。
因为我是实现后端模板渲染接口的方式来模拟页面渲染,理论上是支持任何后端模板渲染的。我想将前端控制模板层的解决方案和mock server 向社区推出去。发现 fis 在后端模板和前端工程的结合太赞了,不知道我这种node 做中间层调 php 直接渲染模板的方式会不会在某些业务场景下不能跟前端工程结合好。(目前我只是用 smarty 读 map.json 让资源 md5 化)
因为之前两个语言版本的 handlebars 共用就是个坑,还踩过用js版本的smarty 的坑。虽然目前这种方式没有遇到任何不爽的地方,但还是希望 fouber 能帮看下有哪些地方会坑。
@fouber
指的是这个?
php -S 127.0.0.1:1234
我现在也是用 php -S 启动内置 server 的,不用node 直接在 php -S 对应PHP文件写 php 代码渲染页面么?
//$view( url, 数据 ,模板)
$view('/url/','{"email":"mail@qq.com"}', 'www/index.html')
和根据配置用 Node 生成简单的文档
$.get({
title: '任务统计报告点击详情',
url: '/ajax/get_report_click_detail/',
request: {
id: '23',
_id: '任务 id',
url: 'http://cunog.com/wprr',
_url: '点击地址'
}
}).ok({
view: [
{
datetime: '2014-12-23 14:23:32',
email: 'mail@qq.com',
id: '122.132.113.11',
count: 3
},
{
datetime: '2014-12-23 14:23:32',
email: 'mail@qq.com',
id: '122.132.113.11',
count: 3
},
{
datetime: '2014-12-23 14:23:32',
email: 'mail@qq.com',
id: '122.132.113.11',
count: 3
},
{
datetime: '2014-12-23 14:23:32',
email: 'mail@qq.com',
id: '122.132.113.11',
count: 3
}
],
pagecount: 10
}).error({
status: "error",
msg: "Error detail"
})
$.page({
title: '企业信息完善',
url: '/system/company_info/',
template: 'system/company_info.html',
data: {
address: '公司地址',
company_name: '公司名称',
website: 'http://www.baidu.com',
tel: '24242',
name:' 联系人姓名',
qq:'23242',
industry: [{key:"1", value:"汽车制造与销售"}, {key:"2", value:"生物医药与保健品"}, {key:"3", value:"广告与传媒", checked:true}, {key:"4", value:"房地产开发与管理"}, {key:"5", value:"消费电子与计算机"}, {key:"6", value:"软件与系统集成"}, {key:"7", value:"半导体与电子元器件"}, {key:"8", value:"通信制造与运营"}, {key:"9", value:"材料与包装"}, {key:"1", value:"机械设备"}, {key:"1", value:"能源与采矿"}, {key:"1", value:"纺织服装"}, {key:"1", value:"物流与后勤"}, {key:"1", value:"金融服务"}, {key:"1", value:"零售业"}, {key:"1", value:"烟草酒业"}, {key:"1", value:"家用电器"}, {key:"1", value:"食品饮料"}, {key:"1", value:"农林牧副渔"}, {key:"2", value:"教育培训"}, {key:"2", value:"公共事业服务"}, {key:"2", value:"化学工业"}, {key:"2", value:"日用化工"}]
}
})
我现在是将mock server 直接放在前端的一台服务器上,然后给后端看前端模拟出来的所有页面交互。
php -S localhost:8000 router.php
这种模式下所有请求会转发给router.php处理,然后你可以在router.php中实现一个路由规则,把你们线上的URL规则在这个router.php中都体现出来
👌 理解了,原本 node 所处理的路由都可以由 router.php 处理。
如果直接用 php 等于也是可以用 php -S 启动一套类似下面语法的自己实现的 mock server 对吧
$view('/url/','{"email":"mail@qq.com"}', 'www/index.html')
目前大多数前端team都在采用模块化[amd/cmd]方式开发,现在前端脚手架这么多,打包部署发布流程也都并行不悖。目前webpack也有很大的气势,与grunt/gulp相比大家觉得优势在哪?未来脚手架还会有怎样的革新或优化?
目前我觉得 combo url
+ scrat.js
这样的方案才是最好的,根本就不需要打包合并文件嘛。有了一份给浏览器的依赖表,基本就足够了。不知道的 @fouber 在这个实践过程中有遇到什么坑?
- combo url过长,需要截断。这是目前最大的问题,combo url需要压缩,但随着项目的增大,combo url的长度总是会超过浏览器限制(2K),所以有时候会被拆成两个请求
- 缓存失效率高,缓存复用率低。combo的资源越多,版本迭代之后资源被更新的概率越高,而且网站内的AB两个页面可能因为使用的组件不同而有不同的combo url,二者无法复用缓存,所以scrat增加了localstorage的资源存储功能,解决页面间缓存复用问题
主要问题还是combo url过长吧,到说不上危害,只是可能会导致某些页面变成两个请求而一定程度上可能
影响性能
@fouber
当一个 loader 基于 资源表实现了 combo url 和 localstorage 缓存后是否可以不使用构建工具的本地打包功能了?(除了会出现变成2个请求)
不应该是@ 我嘛😭。
的确,对于浏览器根据url
进行命中缓存来说,是不太友好的。
不过良好地请求分割,应该是可以提高命中缓存的概率的吧。例如:combo(框架库.js + 插件.js); combo(页面单独的功能.js+其依赖.js)。
嗯,url 真的超过了限制,这个应该要在loader里面也进行下处理吧。~
@nimojs 本地打包应该是可以剔除了的。loader
也是可以处理成两个请求的,就看你要的配置要怎么给到loader
了
@jincdream
�我心中理想的代码打包是这样的
base.js (loader.js 高度复用的基础库)
page.index.js (页面所需JS,只涉及组件调用代码)
combo资源 (page.index.js 所需的所有模块)
我也是不喜欢用本地打包,这样开发人员只用写 base.js 和 page.name.js
你现在的loader用的是什么?
可以看下scrat的webapp模式,跟你需要的打包方式差不多。
发自我的 iPhone
在 2015年9月8日,19:19,Nimo Chu notifications@github.com 写道:
@jincdream �我心中理想的代码打包是这样的
base.js (loader.js 高度复用的基础库)
page.index.js (页面所需JS,只涉及组件调用代码)
combo资源 (page.index.js 所需的所有模块)我也是不喜欢用本地打包,这样开发人员只用写 base.js 和 page.name.js
你现在的loader用的是什么?
—
Reply to this email directly or view it on GitHub.
遇到的问题:
我用的grunt的usemin插件,由于后端映射的路径跟项目结构的物理路径不同,所以html中js、css和图片的引用路径都是按映射的路径写的,但是采用usemin处理html这些资源的链接的时候是按物理路径去更改的,请问大家有什么好的方法吗?
大致结构是这样:
project/
├── css
│ └── index.css
├── html
│ └── index.html
├── img
│ └── index.png
└── js
└── index.js
映射结果:
http://localhost:8080/index.html
http://localhost:8080/css/index.css
这样,所以在index.html中引入js路径为 src="js/index.js",而在物理路径下应该是src="../js/index.js",所以在用usemin的时候会找不到资源文件导致无法修改静态资源路径。
注:我的解决方案是先把html文件夹下的所有内容先copy到project目录下,usemin执行完成后再clean掉,感觉比较笨。
@zmpandzmp grunt的话如果既要保持工程路径的书写方式,又要跟线上部署映射起来,就只能暴力的在构建中处理了。。。
如果是fis的话,可以直接配置任意文件的发布属性,书写的时候始终是工程中的相对路径,构建时会根据用户配置替换对应的地址。
@nimojs 我现在用的 loader
是自己基于 scrat.js
那套**构建的。太挫了就不放出来了。可以参考scrat.js
。 scrat 构建工具
@fouber 上述的路径映射问题用grunt的copy和clean的方法解决了一部分,因为今天弄了一下,发现有些公用的common.css中,因为所有的html文件都要用到且html文件层级不确定,所以在commons.css中引用的图片url都采用了决定路径url(/img/index.jpg)的形式,上述copy和clean的解决方案又解决不了啦。%>_<%
感觉grunt好坑。
难道要自定义方法替换url吗?
😭
@zmpandzmp
把所有相对路径替换成构建后的绝对路径,这是保证资源定位准确最简单最可靠的做法了。这个过程确实需要支持自定义url,因为工程目录可能跟部署目录不同,构建工具要有url替换的依据,这些应该是必然要实现的。。。
@fouber 云龙大大,一直看你文章收益匪浅,这边我一直也在研究前端工程化方面。现在公司部署了一套自动构建与发布方案,但上面图中UAE那块(也就是代码推送这块),并没有思路,能否具体介绍下。系统监控方面也很想了解目前你们团队的大致方案。
开发流程方面,至少涉及四个平台:
- 代码托管
- 持续集成
- 代码库
- 运维操作
『代码托管平台』主要是git/svn的托管平台,现在应该很多人在用 gitlab,用这类平台的主要目的是利用其中的『hook』功能,就是可以在这类平台上设置代码提交之后要做哪些操作,这样我们可以在开发者提交代码之后把提交信息推送到持续集成平台。
『持续集成平台』常用来做自动构建和测试,主流的是 Jenkins CI 和 Gitlab CI ,代码托管平台发现有代码提交之后,会把提交信息推送到持续集成平台,持续集成平台会根据提交信息拉取指定的git/svn分支,然后根据开发者在平台上的配置对项目进行自动化构建和测试,然后把构建后的代码打个tar.gz包推送到代码库存储起来。
『代码库平台』其实就是一个存储持续集成平台每次构建结果的地方,根据构建时间来存放,这样运维操作平台在部署的时候就可以从代码库拉取代码进行上线部署了。
『运维操作平台』主要的工作就是从代码库拉取指定的代码部署到指定的环境中。这些一般需要开发者操作,这里一般会有『上线单』的概念,每次发布前配置一份上线单,包括上线版本,上线说明,指定拉取的代码库中哪个代码包,部署到哪些机器上,部署顺序等。每个上线单可以视为一个部署版本,遇到问题需要回滚的时候直接重新执行之前稳定时期的上线单就行了。
开发者提交代码后,整个持续集成的自动化的流程为:
开发者:git push
→ 代码托管平台:hook
→ 持续集成平台:build && test && tar && push
→ 代码库:save
部署过程就是:
运维操作平台:
* 创建上线单
* 说明部署原因
* 指定代码库的包(从代码库拉取)
* 指定部署机器
* 启动部署
* 记录上线单
* 回滚
一个完整的自动化流程我感觉至少需要上述4个节点,其中前三个可以由前端自行组建,找个限制的台式机,配置好一点,装一个linux系统,撘gitlab、jenkins,jenkins自带归档功能(程序包列表),可以省掉代码库平台。与运维平台的打通(主要是代码库的接入部分)就要和你们的运维团队协商完成了,这个通常都不是什么难事。
不推荐由持续集成平台直接推送构建结果到运维然后自动部署,这种方式非常危险,所以一定要有代码库,能记录历史版本,找到备份,方便回滚,而且运维操作是主动拉取代码,不是被动推送,增加了安全性
@fouber 谢谢云龙大大的回复,说的非常详细,我自己把前三部分搭建起来,不过部署还是通过持续集成平台推送的,我也一直觉得是个安全隐患,但目前由于运维人手不够比较忙,等运维抽时间规划下。还有一个系统监控的问题,目前我是自己查看日志去定位一些静态资源访问和接口调用的问题,想请教下云龙大大关于日志的分析,以及 js 错误收集方案,有好的工程化建议嘛?
mark
统计和监控一般分为两大类:『业务统计』和『性能监控』
前者是业务指标,后者是技术指标
业务统计我们一般都是统计用户行为,点击、路径追踪、落地页、滚屏高度等,这部分应该没啥可说的,最多是可以实现一个小框架,然后约定页面上有某种特殊标签的元素,捕获其上事件,自动上报,这样就不用针对每个元素去自定义了,比如我们上约定元素如果有 data-stat
标记的元素被点击就要上报,上报的信息也都写在元素的属性中,这样开发过程就相对简化下来,在需要统计的地方给元素加上这些属性就好了。
技术指标就有很多内容,包括『T0时间』、『T1时间』、『首屏时间』、『可交互时间』,如果你是单页面应用,可能还会关注『页面切换时间』,我们团队在海外的项目,由于当地网络环境实在太差了,差不多是我们5、6年前的移动网络情况,所以我们还监控了页面的『到达率/折损率』,就是在页面的html代码的前中后三个地方分别打点,判断用户在哪个位置离开。最后就是『JS报错监控』,其实js报错的误报率挺高的(浏览器插件报错都可能被捕获到),千奇百怪,所以有点报错不用担心,主要是监控那些高频错误,js报错的收集就是window.onerror了,这些大家应该都做的差不多。另外还有一类日志,属于前端安全,主要是监控非本网站js脚本注入,可以看 这里
日志上报用 new Image().src=xxx
,不赘述。
此外,保留服务器访问日志(这个一般运维都会做),必要的时候可以通过写脚本分析访问日志来追查问题,这也是非常重要的问题分析和定位手段。
总的来说:
- 基础设施需要一个『日志存储和离线计算的平台』,可以自己搞,用应该ga也行,因为量比较大,前端要折腾可能还需要其他角色的配合。
- 前端写一个『日志框架』,识别有特定属性的元素,对其进行用户行为日志上报,并提供接口,方便在js中上报日志
- 统计『T0/T1时间』、『首屏展现时间』、『用户可交互时间』、『JS报错监控』、『脚本注入监控』(可选)、如果是单页面应用还有『页面切换时间』、如果产品运行环境的网络非常差,还有『页面到达率/折损率』
- 保留服务器访问日志,必要时可以下载之后本地写脚本慢慢分析
- 最后还需要一个『统计数据展示平台』,所谓的报表平台,有标准的数据展示平台,好像ga的那个分析后台,还有一些特色的,比如我们运营同学希望能在页面上可视化标记出用户点击数据,这类很早以前在百度的时候也有人实践过,类似的还有点击热力图等
整套东西差不多就这些吧,可深入的细节还很多,总之大数据时代不搞搞小数据真的没法出去吹牛逼。
个人觉得统计与监控也是前端工程的重要组成部分,必须好好搞
@fouber UAE 代表什么东西
@fouber 每次构建的代码库包是补丁增量的形式,还是整个项目工程文件包呢?
@majianxiong
- uae是我们公司内部使用的应用管理平台,可以类比bae、sae、gae,都是*ae。
- 每次构建的代码库包是整个项目工程文件包。不过这只是我们偷懒的结果。
@fouber @nimojs 我想知道如何配置可以实现打包结果是 common:js/components/menus 而不是我下面的那样?两位谁知道在fis3里要配置什么?
define('js/components/menus', function(require, exports, module) {
var $ = require('common:widget/jquery/jquery.js');
exports.init = function() {
$('.menu-ui ul li a').click(function(event) {
var self = this;
$('.menu-ui ul li a.active').removeClass('active');
$(self).addClass('active');
event.preventDefault();
});
};
});
目录结构
├── common-ui
│ ├── images
│ ├── js
│ │ ├── components
│ │ └── utils
│ ├── less
│ │ └── mixins
│ ├── lib
│ └── widget
├── lib
│ └── mod
│ ├── amd
│ ├── lib
│ └── test
│ ├── async_in_async
│ ├── asyncmore
│ ├── asynctest1
│ ├── circle
│ ├── complex
│ ├── cross
│ ├── manyasync
│ ├── multi
│ ├── pkg
│ ├── repeat
│ ├── ring
│ ├── ringcross
│ ├── self
│ └── single
├── mod-admin
└── mod-hr
@fouber 关于map.json的使用方式我有些想法不知可不可行,想请教下的。将前端的部署独立开来,将map.json存在数据库当中,后端去读取map.json中的列表然后自己渲染。这样前端能独立部署升级,后端也不需要做多余的工作。版本出现问题的话,前后端可以各自回滚。
这个方式当然可以,不过我们一般前后端分离的界限是,前端接管视图渲染层,通过API接口跟后端请求数据,这样map.json跟着前端就好了
@atian25 其实这样做是有一部分折中。因为index需要后端的特殊处理,所以index依旧需要后端处理。这样map.json是跟随着后端一起发布的,不是很灵活,所以产生了这样的想法。
先做个自我介绍,首先做前端 一极大个人爱好 和 实现自身价值。 前不久就再做 类似淘宝店铺装修项目,涉及点击穿透,用kissy框架去做的底层,用遮罩实现的 完全隔离操作层和展示层。 基本逻辑这些,有没有类似做过的 出来说一下 经验
特殊处理指?node直接发个http跟后端交互不就OK?
大家是如何处理大促之类的临时代码的?
举个例子,双十一了,运营/产品需要加一个特殊的逻辑。为了处理这个需求,在代码层面,我们肯定是需要加一个时间判断的,另外再加些这样的逻辑。但是,随着时间的推移,问题来了,这些代码已经没什么卵用了;而且随着这样的需求越多,这样『垃圾代码』也就会越来越多。这必然会影响整体的项目,项目会看起来越来越臃肿不堪。
我相信,不仅是前端会碰到,相信后端也会碰到,不知道有没有合理的工程化思路来解决这个问题?
@myhirra 写好注释,写好TODO,很多编辑器如webstorm是可以check所有TODO的
在尝试模块化,但是模块化后有一些问题。
- 例如百度统计、谷歌分析这类的代码算是一个模块吗,如何组织这类代码?
- css 模块化以后怎么解决容易命名冲突的问题?
- 例如 less 中有一些 mixin 或 css basic 样式怎么模块化?
- 第三方js就是直接通过浏览器的loader加载进来就好了吧。
- css是比较难搞,模块化后,应该会有一类基础库类的css提供给个组件进行 @import ,然后通过BEM命名进行隔离。
@zhoukekestar 组件化肯定是很好的,但也要考虑页面资源的问题,你把所有的组件都加载到一个页面,但是你的js,css文件合并和请求的策略是什么呢?如果都是分别加载的,那样对效率的影响还是挺大的
@zhoukekestar
赞同。组件是应该要独立,不论外部环境如何变化都应该表现一致。
但是对有些问题的处理还是比较困惑。
比如组件样式用到 rem 的时候需要依赖 html 的 font-size, 需要把这个控制权交给页面吗?
如果需要做到表现一致,那会有比较多基础的样式要在组件内部实现,这样不是会造成代码的冗余?
@feizhen js、css和html都并在一个html里面,所以,一个页面需要什么哪些组件,只要合并请求所需要的组件就行了,组件内部就包含了css和js,所以我说html是比js更好的粘合剂
a.html
<div data-register='a'>
<style>...</style>
<div>...</div>
<script>...</script>
</div>
b.html
<div data-register='a'>
<style>...</style>
<div>...</div>
<script>...</script>
</div>
demo.html
<link rel="import-webcom" href="./a.html,b.html">
<div>这里a和b组件的css,js和html都有了</div>
<div data-is='a'>a组件</div>
<div data-is='b'>b组件</div>
@sssession 你的问题非常好,确实是这样的,我在项目中的实践时也遇到了,多个项目公用一个组件,但有些用了bootstrap,有些没用,一种方式是用px,另外一种在组件内部定义一个字体大小,然后采用em,如果还解决不了,那就采用js,动态去设置咯,毕竟js还是蛮强大的。最后,也是比较推荐的一种方式,还是在所有项目中约定好html的font-size,这个由前端leader定好,然后执行就行了,也就一行代码的事。
@zhoukekestar 我明白你的意思,这里的问题是,假设你的一个页面由4个组件组成,如果用你这种方法来组合的话,在http1.x的协议下,那么在加载这个页面的时候会向服务器发送多次的http请求,而且很多时候组件的css和js文件相对较小,如果不采取合适的合并和请求策略,那么在加载这个页面的时候会产生很多不必要的延迟
百度统计、谷歌分析,loading 页面完全可以不走模块化~
- CSS 的模块化离不开构建工具辅助,全局通用的 minxins 可以配置到构建工具中,推荐 est
- 业务组件模块化,比较难搞的是
版本控制
、组件依赖
、生命周期
,资源加载协议的引入,另外一个就是组件的测试,组件可以独立运行 - 页面所需的组件,应该可以分析出所需的 CSS、JS、Tpl, 并且合并一次性加载~~
好多好复杂·~~
天猫中将单图组件、上图下文这么细的都拆分成组件,模块与不模块化,组件与不组件化,还是要看怎么去拆分,怎么去看待拆分后的利与弊。
关于你说的问题,都是可以解决的。
tmallfe/tmallfe.github.io#35
@mycoin FIS3可以解决的。
我自己认为,组件只是业务逻辑的最终表现表现而已,其组件的相应js也只是结合自己的html调用了各js模块。
页面所需的组件,应该可以分析出所需的 CSS、JS、Tpl, 并且合并一次性加载~~
这个通过FIS3的配置和插件就能实现,也不难。
大多数组件应该是可以独立编译再运行的。
关于传统web与工程化
我想问下各位大神我从事前端也有几年了 不过都是1个人在摸索 ,工作中也是一个人独立完成项目。我现在想了解下前端工程化方面的东西。要从哪里下手呢。真的有点迷茫了。各种框架,工具,知识层出不穷。要怎么整理学习的思路呢 先学什么 后学什么 都要掌握哪些。
@web-basess
我觉得吧,首先要先清楚你所在的公司在前端开发的过程中有什么问题,而这些问题是可以通过工具去解决的。
另外我安利下自己写的这个 fis3封装指南 ,应该会对你的思考有所帮助。~
jincdream/jincdream.github.io#1
前端工程化的一个弊病是容易导致前端过分复杂化,不知道有没有同感?
不同意,你所谓的复杂化是有原因的,这些出来的工具和类库都是为了解决问题而出现的,你没有用过就不能下这样的断言
@robotJiang 个人认为是前端规模变大导致的前端工程化的产生,想象一下汽车工厂放弃工程化,全部改为手工生产,同时还要保持原来质量的产出,带来的麻烦会更多。复杂化是什么呢,操作员和工程师看到的东西可能还不一样。
复杂是相对的,工程的构建之初当然需要相对复杂的架构设计,这样是为了让流水线生产的员工操作更简单。
在 2016年4月13日,上午11:33,Robot notifications@github.com 写道:
前端工程化的一个弊病是容易导致前端过分复杂化,不知道有没有同感?
—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub #8 (comment)
@Thyiad 我从 grunt 再到 gulp ,还用了 webpack。框架的话最初用 backbone, 后来用 react, flux. 看我这个路线图,如果你一路下来,前端构建和开发都变的复杂了。当然也是前端更重了,业务结构大部分也转到前端了。然后用 react构建组件化开发,所以我说复杂化了,这个是个不争的事实。组件化后有利于分工合作,这个我也同意。但对于1,2个人的小团队来说,就过重了。
@robotJiang
认同『重』的事实
规范越来越多,就会越来越重。 1 ~ 2 人的小团队可以根据实际情况决定哪些规范和流程要哪些不需要。木有银弹
要根据项目情况选择合适的解决方案。但是有些事应该在开始就做的。
- 模块化开发
- 构建工具(即使不压缩代码也可以,但是要为后续项目增加构建环节做准备)
- 代码规范(提高可维护性最重要,最基本的 CSS 防止冲突约定肯定要)
工具用 gulp fis react vue ng jquery 都无所谓,你不知道一个小型项目会不会慢慢发展成大型项目。
一个未经过前端工程化的小型项目如果演变成中型项目后代码冲突、文件过大等问题就及难解决。
@nimojs 严重同意。前期花费大量时间,还是值得的。不过后续员工的培训成本也会提高,除非大家已经有这方面的架构经验了。
@robotJiang 是的啊,不是每个公司都辣么重视前端,都搞前后端分离的
是因为前端复杂了,才需要引入工程化。因果颠倒了。你写个中型网站,全站异步加载。尝试不用构件工具,不用工程化的**去做。你就发现问题了。
在 2016年4月13日,上午11:33,Robot notifications@github.com 写道:
前端工程化的一个弊病是容易导致前端过分复杂化,不知道有没有同感?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub #8 (comment)
哟,还可以这样,呵可
大家讨论如何 优化,页面的加载速度吧
@Thyiad 不需要彻底的前后端分离,比如在 MVC 中后端愿意将 View 层交给前端维护前端就可以在前端工程上做很多事。(让前端写后端模板,Controler 层还是交给后端写)这样 fouber 博客中讲到的的静态资源表就可以实现了。更深入一点还可以在html直接显示 css js ,一个页面一次HTTP请求显示所有内容。
当然后端不愿意让前端写模板也还是可以做很多事的。
前后端分离还是挺重要的,但是又涉及到SEO问题,这个就比较坑了。