本文结合最近的工作经验,总结出一个较简洁的前端自动化构建方案,主张css和js的模块化,并通过grunt的自动化构建,有效地解决css合并,js合并和图片优化等问题,对于提高前端性能和项目代码质量有一定参考价值,欢迎阅读和点评:)
github地址:https://github.com/liuyunzhuge/generator-web
demo地址:https://liuyunzhuge.github.io/generator-web/
有兴趣的同学,在阅读文章,学习或使用demo的过程中,有任何的疑惑或者发现的问题尽管在评论中与我讨论。
本项目是一个脚手架,应用于前后端结合型项目,可以为这种项目提供如下服务:
前后端结合型项目是指javaweb,asp.net这种非前后端完全分离的项目,页面通常是jsp,aspx,php等模板,部署时的更新包同时包含前后端的文件,属于传统的项目类型。
本脚手架应用场景受限于前后端结合型项目,它仅能解决这类项目的前端构建的工作,对于后端构建以及项目打包发布的工作需要由后端来完成,如果你的是javaweb类型,后端发布可以用maven,它可同时完成后端构建和打包的任务。之所以有本脚手架的产生,主要还是因为目前前后端结合型项目的开发方式还是非常常见,毕竟不是每个公司都有资源去做到完全的前后端分离这类的架构,尤其是小公司或者是项目型公司,没有那么的人和时间等资源能让你玩高级的东西,快速开发才是王道。但是对于任何一个web项目来说,前端部分的基础服务都是一样的,比如图片优化,css和js合并压缩等,而且这都是在团队资源可行的情况下最好做的,对于项目质量,只有好处没有坏处。本脚手架的产生,正是从工作中总结出来的一套开发架子,基于这个架子,你可以很方便得到由它给你带来的以下好处:
WEB-INF/ html/ img/ js/ less/ Gruntfile.js bower.json optimize.json package.json
因为本脚手架是结合demo一起发布到github上的,demo是基于javaweb的,所以你会看到里面有个WEB-INF/文件夹,这个是javaweb必须的,html/里面的页面都是jsp模板,这个也是javaweb必须的;所以如果你想把这个脚手架应用于asp.net和php需要你删掉WEB-INF这个文件夹,然后将html/里的jsp改成aspx或php模板。
另外,不管你想把这个脚手架用于什么类型的项目,最好的使用方式是先把demo跑起来,然后在demo的基础上开发,这样能够减少出错~
html/ 用来存放页面的模板(jsp,aspx,php)或html文件 img/ 存放图片 js/ 存放js less/ 存放less Gruntfile.js 这是grunt任务的配置文件 bower.json 这是bower的配置文件 optimize.json 这是requirejs的优化工具的配置文件 package.json 这是grunt依赖的配置文件
注:html/,img/,js/,less/这三个文件夹里面还有子文件夹,各自都有相关的约定,后续逐个介绍。在demo运行成功之前,那几个配置文件请先不要改动。
查看DEMO:https://liuyunzhuge.github.io/generator-web/ 支持IE9+,chrome,firefox。
该DEMO是gh-pages搭建,你可以通过查看本项目的gh-pages分支看到demo的文件内容,由于github提供的这种服务仅支持静态网页,所以demo的首页其实是由html/index.jsp转化过来的,另外位置也变了一下,index.jsp原来是放置在html/下面的,gh-pages分支里的index.html放在了跟html/同级的位置,不这么干的话,gh-pages就看不到效果了。
由于本项目是结合javaweb一起发布的,所以以下的使用步骤均是在java的开发环境下说明的,IDE为Intellij IDEA。如果你的项目也是javaweb项目,那么推荐你用这个IDE,这是本人用过的最好的java web开发工具,集成了众多前端工具,比如less,emmet和grunt还有bower等。如果你用它来开发项目,你会发现编写less和使用grunt是如此顺畅!纯粹的后台开发可能不挑剔开发工具,但是前端开发如果要结合后台一起弄一下的话, 最好还是使用高级一点的IDE。
安装nodejs,git,bower,grunt。windows下安装即可,不需要linux。其中:
注:这一步与IDE和后端语言没有任何关系。不管什么语言的项目,这个都是使用本脚手架的基础。
新建web项目,比如我用IDEA新建一个项目名为generator-web-demo,它的项目结构如下:
.idea/ src/ web/ generator-web-demo.iml
其中.iead/和generator-web.iml都是IDEA建完项目以后创建的,可以不用管。src是java源文件的目录,web文件夹是项目的web根目录。
在github上,download zip或者用git clone本项目,复制本项目的以下文件夹或文件粘贴到你项目的web根目录(前一步提到的web文件夹):
html/ img/ js/ less/ WEB-INF/ bower.json Gruntfile.js optimize.json package.json
WEB-INF直接覆盖原来的WEB-INF即可。如果原来的web/下有一个index.jsp,可以把它删掉,我的习惯是把页面都放一块,html/已经提供一个index.jsp了,所以原来的index.jsp多余了。
最后你的项目结构应该如下:
.idea/ src/ web/ html/ img/ js/ less/ WEB-INF/ bower.json Gruntfile.js optimize.json package.json generator-web-demo.iml
使用bower安装bower.json中配置的库(jquery,iCheck,requirejs,bootstrap)
bower install --save
注:以上库除requirejs是脚手架必须的外,其它均可根据实际项目需要进行添加和删除,不过为了把demo先跑起来,还是别去改它,看懂了构建的原理再来根据项目需要修改也不迟。
安装grunt和grunt插件:
npm install --save
如果npm安装速度慢,可以按下面网址提供的方式安装,速度会快一些: http://npm.taobao.org/
执行grunt的default任务:
grunt default
如果执行grunt这个任务报错,一般都是grunt-contrib-imagemin插件报的错,你可以用下面的命令重装grunt-contrib-imagemin
npm uninstall grunt-contrib-imagemin --save npm install grunt-contrib-imagemin --save
注:以上是两个命令,分开执行。如果还报同样错误,可将以上命令多试几次。
最后启动你的web服务器,比如tomcat。打开浏览器访问应该就能看到跟demo一致的首页效果:)。
本部分介绍如何在demo的基础上进行开发。
每新增一个页面都放在html/下面,目前demo内包含:
html/ base/ body_end.jsp head_end.jsp head_start.jsp index.jsp ltIE9.html
其中:base/下的是一些公共的jsp页面,你看下index.jsp里面的那些inclue你就明白了。 ltIE9.html是一个提示页面,如果用户以IE8及以下的IE浏览器访问就会跳到这个页面提示更新浏览器或者下其它的好用的浏览器。
如果你的项目是asp.net的项目,请把这些jsp都替换成aspx。
这个html/的思路是:base/放公共的,其它页面按模块分,如果一个模块只有一个页面,那么就把它直接放在html/下面,如果一个模块有多个页面,可以在html/以模块名建一个文件夹,相关页面都放那里面。
你在开发页面的时候,建议:html/base/下的公共jsp保留,新页面以index.jsp为参考进行开发,可以往base/内添加更多公共页面,也可按模块对新页面进行分类。
这个问题是这样的,打比方说:
base元素设置的代码如下<head_start.jsp>:
<% String base = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); base += base.endsWith("/") ? "" : "/"; request.setAttribute("base", base); request.setAttribute("rnd", "?v=0.0.1"); %> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <base href="${base}"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!--[if lt IE 9]> <meta http-equiv="Refresh" content="0; url=${base}html/ltIE9.html"/> <![endif]-->
引用css,图片和js时,直接使用相对web根目录的路径进行引用:
<link href="css/index.css" rel="stylesheet"> <img src="img/dist/logo.png" alt="LOGO"> <script data-main="js/dist/mod/index" src="js/dist/lib/require.js"></script>
还记得web根目录的这个结构吧:
web/ img/ css/ js/
约定项目相关的图片放在img文件夹下,img/的结构为:
img/ dist/ src/ temp/
这个目录结构最好是不改变。其中:
每增加一张图片或雪碧图,就把它丢到img/src里面,页面或css中引用图片时,直接使用img/dist/这个路径去引用即可。
约定css都用less来编写,less文件夹的结构为:
less/ app/ icon/ mixin/ widget/ mod/ sprite/
其中:
grunt的less任务,会把app/icon/下的字体拷贝到web/css/下,把app/mod/下的less编译成的css也放到web/css/下,所以页面引入css的时候,要相对css/这个文件夹引用:
<link href="css/index.css" rel="stylesheet">
在这一块需要注意的问题是字体文件和图片文件的路径问题,记住less编译后的css是放在css/下面的,而图片优化后是放在img/dist/下面的;字体文件跟css默认都是直接位于css/目录下的,具体请多参考demo中app/mod/index.less。
约定js源码都在js/src/下定义,js/src/的结构为
js/ js/src/ app/ mod/ widget/ lib/ mod/ common.js
其中:
在没有优化之前,grunt任务会把js/src/的文件全部拷贝到js/dist/下,在优化之后,grunt任务会把js/dist/mod/下的每个js依赖的所有js,都跟它合并成为一个js。不管有没有优化,页面引用js文件时,都要相对js/dist这个目录引用!
一个页面相关的js整个加载流程,以demo中的index.js为例:
<script data-main="js/dist/mod/index" src="js/dist/lib/require.js"></script>
加载js/dist/mod/index.js requirejs(['../common'], function (common) { requirejs(['app/mod/index']); });
所以开发一个js的步骤为,假设要开发一个userCenter.js:
requirejs(['../common'], function (common) { requirejs(['app/mod/userCenter']); });
另外还要在optimize.json中增加一个对userCenter.js的配置项,以便生产环境构建时能把它依赖的js都跟它合并成一个js 。
简单点来说,照着这个模板就可以了:
[ { "name": "../mod/index", "include": [ "app/mod/index" ] } ]
比如如果userCenter.js,那么就该配置成:
[ { "name": "../mod/index", "include": [ "app/mod/index" ] }, { "name": "../mod/userCenter", "include": [ "app/mod/userCenter" ] } ]
具体的原理牵扯的细节就比较多了,可参考以下两个网址去研究一下:
grunt default
使用以上任务就会启动构建,包括:
这个构建不会压缩css,不会合并压缩混淆js。
grunt release
使用以上任务,完成生产环境构建:
grunt optimize
这个任务可用于测试requirejs的合并是否正确,实际使用方式如下:
grunt default
grunt optimize
之所以没跟default任务合并,是因为这个优化任务比较费时,不用每次开发的时候都执行优化
打包前,先执行下grunt release
,并将修改提交至代码服务器。另外bower_components和node_modules这两个文件夹千万别传到代码服务器上去,前端用的东西,打包的后台同事怎么会要你这个呢;还要记得提醒他们把以下文件夹从工程中排除出去:
img/src/ img/temp/ js/src/ less/
这些文件是没有必要发布出去的。至于具体怎么打包,就是后台同事的责任了。
目前已知的未解决的问题:
下一步计划就是要解决以上问题。