前言
前后端分离以来,因为应用类型和技术栈的原因,我们需要采用多页应用的形式,并且没有引入js包管理工具。也就是,前端项目就是一个由静态文件构成的网站,只是因为可以跨域调用接口而使其可以独立成为一个项目。但是,这里面的管理机制确是非常匮乏的。本次改造,主要意在对前端项目整体建构进行调整,减少重复内容,并初步尝试对代码进行切面功能注入。本次规划的改造内容主要有一下三点:
- 所有引入文件加hash
- 提取公共文件引入
- 重构前端配置
引入gulp
本质上,gulp是一个js包,需要通过npm进行安装,基本的引入命令如下(进入项目目录):
npm init (需要先安装npm,安装nodejs会连带安装npm)
npm install --global gulp-cli
npm install --save-dev gulp
# 这个时候可以查看gulp的版本,告诉我们安装成功了
gulp --version
这个时候,让我们新建一个gulp用的配置文件gulpfile.js,放在根目录。这时,配置文件里什么特殊的事情也不做,先把源码输出到指定的目录。但是,我之前的代码目录就是在根目录的,在根目录还有几个html文件。而现在,由于引入了npm和gulp,也就多了一些他们的配置文件,指代起来就很麻烦。所以,我把所有的代码和前端用到的资源都放到了source目录下。gulpfile.js文件内容如下:
var gulp = require('gulp');
function defaultProcedure() {
var result=gulp.src("source/**").pipe(gulp.dest("output/"));
return result;
}
exports.default = defaultProcedure;
这时执行gulp,就可以看到会自动创建target文件夹,将source文件夹里的内容全部考进去了。
这里有几个注意事项:
- 最上面,使用gulp包定义了gulp对象,以让后面我们可以使用。
- defaultProcedure是我们默认执行的过程,及命令行里,我们只输入了gulp,没带任何参数。
- 最后,需要将export.default设置为defaultProcedure才能实现默认过程的调用。
所有引入文件加hash
安装相应的插件
npm install --save-dev gulp-rev gulp-rev-collector
这东西对于一个新手而言可是折腾了我不少时间。不过好在最后搞出来了,也填了不少坑。先说基本思路吧。gulp里面没有能够对这个需求一步到位的插件,所以,它是分两步来的。
- gulp-rev:这个插件负责将文件重命名,另外可以生成一个叫做mainfast的json文件,用来存储每个文件的原名和现在的名称。
- gulp-rev-collector:这个插件是使用上面生成的mainfast文件替换html中的相关文件用的后面我们介绍相关的具体任务。
文件重命名
//CSS生成文件hash编码并生成 rev-manifest.json文件名对照映射
gulp.task('revCss', function(){
return gulp.src('./source/static/css/**/*.css')
.pipe(rev())
.pipe(gulp.dest('./target/static/css/'))
.pipe(rev.manifest())
.pipe(gulp.dest('./target/static/css/'));
});
//js生成文件hash编码并生成 rev-manifest.json文件名对照映射
gulp.task('revJs', function(){
return gulp.src('./source/static/js/**/*.js')
.pipe(rev()) //给文件添加hash编码
.pipe(gulp.dest('./target/static/js'))
.pipe(rev.manifest()) //生成rev-mainfest.json文件作为记录
.pipe(gulp.dest('./target/static/js'));
});
这是我写的两个重命名的任务,分别对js和css进行重命名,并复制拷贝到目标目录中。没什么可多说的。需要说明的是,这个方法的写法和引入gulp里面的代码的写法是等效的,个人比较喜欢这样,所以就写成这样了。
重构html
//Html替换css、js文件版本
gulp.task('revHtmlCss', function () {
return gulp.src(['./target/static/css/*.json', './target/**/*.html'])
.pipe(revCollector()) //替换html中对应的记录
.pipe(gulp.dest('./target')); //输出到该文件夹中
});
gulp.task('revHtmlJs', function () {
return gulp.src(['./target/static/js/*.json', './target/**/*.html'])
.pipe(revCollector())
.pipe(gulp.dest('./target'));
});
gulp.task('copyHtml',function(){
return gulp.src('./source/**/*.html')
.pipe(gulp.dest('target/'))
})
这里可以看到的是,我们使用了生成的json文件,以及目标区域的html,而目标区域的html是我用copyHtml提前复制过去的。开始的时候我都是用source里面的,结果,变成了只包含了后面替换的任务的结果,所以我就先考过去,然后改了就复制到原地。
整体执行
gulp.task('build'
,gulp.series('copyHtml'
,'revCss'
,'revHtmlCss'
,'revJs'
,'revHtmlJs'
,'copyAdminlte'
,'copyImg'
,'copyPlugin'));
注意这里代码我自己加了换行,其实是一行里的代码。特别注意一下这里的执行顺序,他们都是顺序执行的。里面最后三个copy任务和copyHtml类似,我就不写了,是把剩余不需要改变的文件复制过去。
网上顺序执行有的是使用run-sequence组件进行的,我试了,报错,没具体查原因,可能是因为gulp版本升级了。还有的是直接用中括号来表示顺序的,这个可以明确是版本升级后不支持那种语法了,现在的写法就是这样的。
提取公共文件引入
可以提取公共文件是建立在对代码整体结构的约定之上的。为此,首先需要调整代码结构,我对代码结构做了如下调整:
cdn引入本地化
这个东西,可能有些人会反对。但是,前段时间,我们出现过由于引入的文件的cdn不稳定导致页面无法使用的问题。所以,我把项目里所有用到cdn的地方都都改成了本地引用,以避免这种情况。
调整目录结构
之前,关于html的页面的目录存在于两个位置,一个是和page目录平级的目录,一个是page下的二级目录。这样就导致,在引用资源的时候存在两种不同的目录。就难以用统一的引入,所以这里我把外面的部分也放到了page下的二级目录中去了。这样,页面和资源的相对位置,就是比较固定的了。
使用插件进行替换
这次要用的是gulp-file-include插件。用法也很简单,官网这次写的还算详细,下面是我使用它写的任务:
var gulp = require('gulp');
var fileInclude = require('gulp-file-include');
//替换公有部分
gulp.task('htmlInclude',function(){
return gulp.src(['target/page/**/*.html']) //指明被替换的文件的目录
.pipe(fileInclude({
prefix : '@@', //指明使用的前缀,以方便识别占位位置
basepath : 'source/include' //公有文件的检索基础地址
}))
.pipe(gulp.dest('target/page/'))
})
页面上的写法如下:
@@include('commonStatic.html')
关于这个的写法有必要解释一下:
- 前面的两个@是我们在prefix中约定的前缀
- include以及后面的小括号,是它的语法,小括号里面是要放在这里的内容的文件名
- commonStatic.html,这是公有视图的文件名,其实也是路径。它在我的项目中的完整路径是source/include/commonStatic.html,而我在basepath中配置了source/include,所以这里直接写文件名就可以了。
技术实践通了,剩下的问题就变成了,哪些是属于公共的,然后逐一在对应的页面添加占位代码。
重构前端配置
在项目里面,总是有一些东西是需要配置的。这个,并不是说那些常量就是需要配置的。因为它们在任何地方都是那样,所以我反而不关心它们,它们就是一般的代码。我说的,是因为有环境的不同或者其它因素导致的,在软件的生命周期中可能因为在这些地方需要改变。这些代码,目前 我这里只有接口的连接地址需要进行配置。
这里我们用到的插件叫做gulp-template,它可以直接替换对应的占位符为指定的字符串。先上代码吧,任务是这么写的:
gulp.task('config',function(){
return gulp.src('source/static/js/common.js')
.pipe(template({config_BaseUrl: 'http://192.168.1.25:1111'}))
.pipe(gulp.dest('source/static/js'))
})
这个任务里,我是把这个common.js改了之后又考到了原地,就形成了替换。而原来里面是这样写占位符的:
var BaseUrl ='<%= config_BaseUrl %>'
就是这种尖括号的语法,然后用config_BaseUrl做映射,就可以了。
配置文件分割
目前,我们仅有的配置是写在gulpfile.js里面的。这是不利于进行多环境自动化部署的。因为,如果我要用多个环境部署,要么在这个文件里写满所有环境的配置,要么将这个部署脚本复制多分以用来做不同的环境脚本。前者不够优美,这个文件会变得非常臃肿,后者则会带来复制粘贴的地狱,在未来脚本演进的过程中难以保持版本一致。好在,gulp其实就是node.js脚本。这个问题的解决,对于大多数用惯node.js的人来说,或许不是事,但对于我来说,就是个事了。解决方案很简单。
我们创建一个config.js文件,代码如下:
var configParams={config_BaseUrl: 'http://192.168.1.25:1111'};
exports.config= configParams;
然后在gulpfile.js中通过以下代码引入它:
var config=require('./config');
使用的代码变成如下形式即可:
……
.pipe(template(config.config))
……
这样,我们就把配置从gulpfile.js中移除了,这个文件仅仅是需要执行的脚本。
运行构建
上面说了这么一大堆,都是关于配置的。怎么运行配置好的gulp呢,命令如下:
gulp <task名称>
对就是这么简单,如果你配置了defaultTask,那么连task名称也不用输入。
调试
其实,这样工程化带来了一个很大的弊端。就是你编辑的文件并不会改变你输出的文件。也就是,每次改动什么东西后,需要进行build才能看到效果。为此,有个插件gulp-watch,能够提供监听功能。每次被监听的文件产生了变动,它可以触发自动的行为,具体task的代码如下:
var watch = require('gulp-watch');
gulp.task('watch',function(){
return gulp.watch('source/**',gulp.series('buildHtml'));
});
这里面buildHtml的内容我就不介绍了,就是你希望它执行的task。需要注意的是,这个watch并没有被我弄到build里面。事实上,他是一个启动了就不会终止的任务,它会一直监听和执行task。这样我们改了什么内容过后就不需要手动构建了。我这个项目是用es5写的。所以,浏览器里面调试的代码实际上还是和我写的代码是一致的,只是我进行了构建而已。es6似乎还要加上sourceMapping才能形成映射,等回头我项目引入es6再说吧。
网友评论