https://segmentfault.com/a/1190000003060016

离开qunar有一个多月了,在离开的时候就决定不再用fekit。做出这个决定并不是因为fekit不好,恰恰相反,fekit帮我们做了很多事情,还屏蔽了许多细节,让开发人员能够专注于开发过程。不过随着fekit的升级,也出现了一些问题,同时fekit和公司业务及发布流程有一定耦合,所以觉得采用开源的构建方案。

在使用gulp的过程中,基本也是依据使用fekit的思路来逐步完善构建过程的,所以还是要感谢fekit。现在进入正题,
看看利用gulp如何实现本地服务、mock数据、css预处理、js模块化、打包压缩、版本号替换等功能的。

1. 本地服务

依赖模块:gulp-webserver

使用:

gulp.task('webserver', function() {
gulp.src('./app')
.pipe(webserver({
livereload: true,
directoryListing: {
enable:true,
path: 'app'
},
host: '172.16.100.27',
port: 8000
}));
});

注意:

  1. app是你的项目目录,比如我的目录结果如下

    -app
    |--- src
    |--- images
    |--- mock
    |--- prd
    |--- index.html -gulpfile.js
  2. 开启directoryListing的enbale,访问根目录(172.16.100.27:8000)时才能显示目录或文件列表。

  3. host可以是ip也可以是域名,不过在虚拟机里面调试时,用域名访问似乎有点问题,没有深究;如果要在手机上调试,同样建议用ip。

2. mock 数据

依赖模块:gulp-webserver

使用:请参考之前的博文 gulp构建之mock data(模拟数据、转发请求)

3. css 预处理编译

依赖模块:gulp-sass gulp-minify-css gulp-autoprefixer

使用:我这里使用的是sass

var sass = require('gulp-sass'),
minifyCSS = require('gulp-minify-css'),
autoprefix = require('gulp-autoprefixer'); var cssFiles = [
'app/src/styles/page/index.scss',
'app/src/styles/page/touch.scss'
]; /* 编译压缩sass文件 */
gulp.task('sass', function() {
gulp.src(cssFiles)
.pipe(sass().on('error', sass.logError))
.pipe(autoprefix())
.pipe(minifyCSS())
.pipe(gulp.dest(paths.build.styles));
});

注意:

  1. 使用sass模块时,要加上.on('error', sass.logError)),这样sass编译出错时会打log,而不是终止运行。

  2. autoprefix,这是一个好东西,也是我不用fekit的一个原因(它还没集成autoprefix)

4. js 模块化开发

依赖模块:gulp-webpack gulp-uglify vinyl-named imports-loader

说明:fekit采用的CommonJs的模块化方式,此外还有requireJS这种AMD的模块化方式。而webpack则是理想中的打包工具,它同时支持CommonJs和AMD,并拥有大量的加载器,包括上面说都的sass编译、前端模板编译等加载器,而且还有webpack-dev-server,已经强大到几乎不需要gulp了。我对webpack还不太熟,并且需要用到gulp的一些其他功能,所以暂时考虑把webpack作为gulp的一个模块来使用。

使用:

var named = require('vinyl-named');
var webpack = require('gulp-webpack'); var jsFiles = [
'app/src/scripts/page/index.js',
'app/src/scripts/page/touch.js'
]; // webpack打包压缩js
gulp.task('packjs', function() {
return gulp.src(jsFiles)
.pipe(named())
.pipe(webpack({
output: {
filename: '[name].js'
},
module: {
loaders: [{
test: /\.html$/,
loader: 'mustache'
}, {
test: /\.js$/,
loader: "imports?define=>false"
}]
},
resolve: {
alias: {
jquery: 'app/src/scripts/lib/jquery-1.11.3.min.js'
}
},
devtool: "#eval-source-map"
}))
.pipe(uglify().on('error', function(e) {
console.log('\x07',e.lineNumber, e.message);
return this.end()}
))
.pipe(gulp.dest(paths.build.scripts));
});

注意:

  1. 对于有多个入口文件(entry point)的情况,需要使用vinyl-named这个模块,这样就能实现以下打包需求

    src/scripts/index.js -> prd/scripts/index.js
    src/scripts/touch.js -> prd/scripts/touch.js
    在output里面可以设置打包后的文件名,如 "[name].min.js"
  2. 我在项目中使用的是commonjs的模块化方式,但大多数插件(如jquery或zepto插件)都是支持两种模块化方式,并且是先判断 define 再判断 module.export,所以我们需要“屏蔽”amd的模块加载方式。

    首先安装`imports-loader`模块,然后做如下配置:
    module: {
    loaders: [{
    test: /\.js$/,
    loader: "imports?define=>false"
    }]
    }

    这样我们就能放心地使用各种插件了。
  3. 我的项目中使用了HoganJs作为前端模块,在webpack中只要找到相应的加载器就行。

    首先安装[mustache-loader](https://github.com/deepsweet/mustache-loader)模块,然后做如下配置:
    module: {
    loaders: [{
    test: /\.html$/,
    loader: 'mustache'
    }]
    }

    通过 2. 和 3. 两个例子,大家可以看出test就是要处理的文件类型,loader就是处理文件的加载器。
  4. 调试仍然是个比较大的问题,虽然webpack提供了各种调试模式(在devtool中配置,实现sourcemap的调试),但在实际使用时,经常会遇到跳过断点的问题,不知道是不是gulp-webserver和webpack不适配的原因。所以在实际开发中,我会先把uglify给注释掉,这样至少能在一个未混淆压缩的文件里调试。如果大家有好的方法,麻烦告知,谢谢~

5. 生成文件对应的 md5(版本号),并在 html 中引用文件时添加

依赖模块:gulp-rev gulp-rev-collector

说明:在实际生产环境中,我们页面引用的css和js文件的文件名都是带版本号的,这样方便回滚和防止缓存。通常我们使用文件的md5编码作为版本号。

使用:

var rev = require('gulp-rev'),
revCollector = require('gulp-rev-collector'); var cssDistFiles = [
'app/prd/styles/index.css'
]; var jsDistFiles = [
'app/prd/scripts/index.js'
]; // prd文件加md5后缀,并生成替换map
gulp.task('ver', function() {
gulp.src(cssDistFiles)
.pipe(rev())
.pipe(gulp.dest('app/prd/styles')) // 生成 name-md5.css
.pipe(rev.manifest())
.pipe(gulp.dest('app/ver/styles')); // 生成 rev-manifest.json(映射) gulp.src(jsDistFiles)
.pipe(rev())
.pipe(gulp.dest('app/prd/scripts'))
.pipe(rev.manifest())
.pipe(gulp.dest('app/ver/scripts'));
}); // html文件添加md5引用
gulp.task('html', function() {
gulp.src(['app/ver/**/*.json', 'app/*.html'])
.pipe(revCollector())
.pipe(gulp.dest('app/'));
});

结果如下:

index.html

<script src="prd/scripts/index.js"></script>
=>
<script src="prd/scripts/index-0884a4f91b.js"></script>

注意:

  1. 我把生产md5和替换html中的版本号拆分为了两步,之前是放在一起,结果会出现用上一次版本号替换html中文件名的问题。

6. 将外部引用的资源文件内联到 html 中

依赖模块:gulp-inline-source

说明:一些touch上的活动页,样式和脚本都不多,与其增加额外的请求数,不如把样式和脚本都以内联的方式嵌到html文件中。

使用:

gulpfile.js
// 把css、js以inline的形式插入html
gulp.task('inlinesource', function () {
return gulp.src('app/index.html')
.pipe(inlinesource())
.pipe(gulp.dest('dist'));
}); index.html
<link rel="stylesheet" href="../prd/styles/index.css " inline>
<script src="../prd/scripts/index.js" inline></script>

这一个月来,我用到的基本就这么多了,其实回头看看,自己东拼西凑也算是造了一个自己的构建小工具。同时,我也更深入地理解了fekit的设计思路和一些原理。

不过,类似FIS的smarty模板、fekit的velocity mock等功能,我还没发现怎么在gulp中来实现。简单的说,就是能在前端环境开发jsp、velocity或者php(我目前的需求是php),数据采用mock方式,不依赖于后端,从而把view的控制权完全拿到前端,实现前后端的分离(非ajax方式)。如果大家有任何建议,麻烦指教!


更新于08.11

7、模版层面的前后端分离

依赖模块: gulp-swig

说明:swig 是一个类django、twig模板的前端模版,说是类似,基本语法其实一样,这样前端开发用swig,后端用对应的模板引擎(比如python的django、php用twig等),这样一套模版文件在前后端都能解析,从而实现前后端分离。

由于swig和twig在一些语法上存在差异,我们需要扩展swig:

swig.setDefaults({
cache: false,
loader: swig.loaders.fs(path.join(appbase)),
locals: {
environment: "local", // 全局变量,表示本地环境,用于区分swig和twig不一样的地方
range: function (start, end) {
return (new Array(end-start+1)).join().split(',').map(function (n, idx) { return idx + start; });
}
}
});
swig.setFilter('length', function (input) {
return input.length;
});
swig.setFilter('slice', function(input, begin, len){
return input.slice(begin, len);
});
swig.setFilter('json_encode', function(input){
return JSON.stringify(input);
});
swig.setFilter('replace', function(input, obj) {
var output = input;
for (var key in obj) {
output = output.replace(key, obj[key]);
}
return output;
});

另外我在locals里面设置了environment这个字段,这样在某些地方可以通过environment判断是否是本地环境,从而解决swig和twig不兼容的问题:

{% if environment == 'local' %}
{% set tpl_path = './components/ctn_publish/' + type + '/index.tpl' %}
{% else %}
{% set tpl_path = './components/ctn_publish/' ~ type ~ '/index.tpl' %}
{% endif %}

用gulp替代fekit构建前端项目的更多相关文章

  1. vue 构建前端项目并关联github

    这几天尝试用node开发一个网站,后端的接口已经初步开发完成,现在开始构建前端的项目,记录下过程,在学习下吧. 用vue-cli 构建项目,myproject.(构架过程略过) 每次在本地构建项目后和 ...

  2. jenkins自动构建前端项目(window,vue)

    我们把一个多人协作的vue前端项目发布服务器,一般要经过以下步骤: git更新最新的代码 构建项目 把构建后的代码上传到服务器 如果用jenkins来构建的话,只需要点击一次构建按钮,就可以自动完成以 ...

  3. 如何使用Docker构建前端项目

    原文链接 Docker单独部署前端项目和Node项目是非常便捷的,在这里分享一下Docker的使用,主要聊聊它的部署实践.(我是window10专业版安装Docker) Docker Docker是一 ...

  4. Webpack构建前端项目

    前言 公司据说要搞前后端分离,趁这两天项目完成的差不多,抓紧时间学习一下前端知识 现在流行前端项目工程化,那么第一个问题就是如何创建工程(项目),第一次玩webpack 通过 NPM 创建项目 # 创 ...

  5. 【gulp】gulp + browsersync 构建前端项目自动化工作流

    什么是 gulp? gulp.js 是一个自动化构建工具,开发者可以使用它在项目开发过程中自动执行常见任务.gulp.js 是基于 node.js 构建的,利用 node.js 流的威力,你可以快速构 ...

  6. 利用fis3构建前端项目工程

    FIS3是国内百度公司产出的一款前端工程构建工具,FIS3可以解决前端工程中性能优化.资源加载(异步.同步.按需.预加载.依赖管理.合并.内嵌).模块化开发.自动化工具.开发规范.代码部署等问题,首先 ...

  7. 使用npm构建前端项目基本流程

    现在各种前端框架, 库文件基本都托管到npm上, 我们平常下载到别人的项目文件, 也基本是用npm 构建的, 不了解点node和npm那是寸步难行. 下面介绍的代码示例不敢说是最佳实践, 但都是我亲自 ...

  8. 使用grunt构建前端项目

    1. grunt构建工具是基于nodejs上的,所以在使用之前一定要先安装好nodejs 2. 安装好nodejs后,node -v查看node版本 npm-v 查看npm版本信息 3. 在需要用到的 ...

  9. react构建前端项目方法汇总

    react简介: 一.使用react 创建一个PC端的项目 (a):使用 yemon 创建一个 webpack 的 react 的项目 控制台安装并且产看 yemon 的版本 yo -v (b): 全 ...

随机推荐

  1. JQ 特效下拉列表 写出与css一样的效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. logstash json和rubydebug 第次重启logstash都会把所有的日志读完 而不是只读入新输入的内容

    查看一下agent端的shipper的配置: # cat logstash_test2.shipper.conf input { file { path => ["/apps/logs ...

  3. vim: vs sp 调整窗口高度和宽度

    转自:http://www.cnblogs.com/xuechao/archive/2011/03/29/1999292.html vim多窗口有时候需要调整默认的窗口宽度和高度,可以用如下命令配合使 ...

  4. MySql中delimiter的作用是什么?

    这个命令与存储过程没什么关系吧.其实就是告诉mysql解释器,该段命令是否已经结束了,mysql是否可以执行了.默认情况下,delimiter是分号;.在命令行客户端中,如果有一行命令以分号结束,那么 ...

  5. OCJP(1Z0-851) 模拟题分析(七)-->214

    Exam : 1Z0-851 Java Standard Edition 6 Programmer Certified Professional Exam 以下分析全都是我自己分析或者参考网上的,定有 ...

  6. poj 2155:Matrix(二维线段树,矩阵取反,好题)

    Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 17880   Accepted: 6709 Descripti ...

  7. NPOI读写Excel

    1.整个Excel表格叫做工作表:WorkBook(工作薄),包含的叫页(工作表):Sheet:行:Row:单元格Cell. 2.NPOI是POI的C#版本,NPOI的行和列的index都是从0开始 ...

  8. WPF ListView展示层叠信息

    通常我们在ListView中展示一列同类数据,例如城市名称.不过可以对ListView的DataTemplate稍作修改,让其显示层叠信息.例如:需要在ListView中显示省份和省份对应的城市名称. ...

  9. windows 2003 企业版 下载地址+序列号

    迅雷地址: thunder://QUFodHRwOi8vcy5zYWZlNS5jb20vV2luZG93c1NlcnZlcjIwMDNTUDJFbnRlcnByaXNlRWRpdGlvbi5pc29a ...

  10. Vijos P1459 车展 treap求任意区间中位数

    描述 遥控车是在是太漂亮了,韵韵的好朋友都想来参观,所以游乐园决定举办m次车展.车库里共有n辆车,从左到右依次编号为1,2,…,n,每辆车都有一个展台.刚开始每个展台都有一个唯一的高度h[i].主管已 ...