以gulp为中心的前端工程化改造实录

2020-03-20  本文已影响0人  本然酋长

前言

前后端分离以来,因为应用类型和技术栈的原因,我们需要采用多页应用的形式,并且没有引入js包管理工具。也就是,前端项目就是一个由静态文件构成的网站,只是因为可以跨域调用接口而使其可以独立成为一个项目。但是,这里面的管理机制确是非常匮乏的。本次改造,主要意在对前端项目整体建构进行调整,减少重复内容,并初步尝试对代码进行切面功能注入。本次规划的改造内容主要有一下三点:

引入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文件夹里的内容全部考进去了。
这里有几个注意事项:

所有引入文件加hash

安装相应的插件

npm install --save-dev gulp-rev gulp-rev-collector

这东西对于一个新手而言可是折腾了我不少时间。不过好在最后搞出来了,也填了不少坑。先说基本思路吧。gulp里面没有能够对这个需求一步到位的插件,所以,它是分两步来的。

文件重命名

//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')

关于这个的写法有必要解释一下:

重构前端配置

在项目里面,总是有一些东西是需要配置的。这个,并不是说那些常量就是需要配置的。因为它们在任何地方都是那样,所以我反而不关心它们,它们就是一般的代码。我说的,是因为有环境的不同或者其它因素导致的,在软件的生命周期中可能因为在这些地方需要改变。这些代码,目前 我这里只有接口的连接地址需要进行配置。
这里我们用到的插件叫做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再说吧。

上一篇 下一篇

猜你喜欢

热点阅读