一个小型项目的前端构建方案

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

⼀个⼩型项⽬的前端构建⽅案
⾸先,给⼤家说明⼀下这次项⽬的背景,是针对于⼀个⽤smarty模板引擎写的开放平台的UI改版⼯作,需求⽐较急。

原始架构是基于php yii 框架,前后端代码杂糅在⼀起,逻辑也较为混乱,以下是其基本⽬录结构:
基本的业务代码是在websites⽂件⽬录下,assets⽂件夹⽬录下存储了⼀些静态⽂件资源。

鉴于以上⼏个原因,最后定的⽅案是,不进⾏html代码的重构,只进⾏css代码的样式覆盖并采⽤less语⾔进⾏开发从⽽提⾼效率。

⽅案确定好了,接下来就是确定前端的⼀个基本的构建⽅案,要解决的问题有这么⼏个:
1、less⽂件的代码组织⽅式。

2、less⽂件的编译打包,你⾃然不能把⼀堆less⽂件扔到线上让less.min.js去进⾏编译解析。

3、缓存问题,为了最⼤程度的提升性能,我们需把每次上线的新版本⽂件加戳,从⽽不读取缓存获取最新的代码。

关于缓存这⽅⾯的知识可以参考https:///jathon/blog/404968⼀⽂。

针对于第⼀个问题,最终采⽤的⽅案是根据less本⾝函数注⼊、导⼊、变量等特性⽽采取组件化开发的思想,对于整个项⽬进⾏公⽤部分的拆分,针对性的对每⼀个组件单独抽离出相应的less⽂件样式。

最终的⽬录结构如下:(图⼀所⽰)
图⼀图⼆图三
在d_theme_main.less⽂件中是对各组件⽂件的引⽤,如图⼆所⽰。

本次UI改版的样式⽂件引⽤在lib.tpl⽂件下,如图三所⽰。

针对于第⼆、三个问题我们放在⼀起进⾏解决。

与其他项⽬不同的是,本次项⽬开发没有开发、⽣产环境之分,不存在输出⽬录的概念。

我们要做的就是:
1、实现对于less⽂件的编译打包压缩,对于各组件的less⽂件将最终编译打包到⼀个css⽂件⾥。

2、实现对lib.tpl⽂件中对css⽂件引⽤的加戳,从⽽解决缓存问题。

我们⽤的是gulp前端⾃动化⼯具,将在项⽬根⽬录下创建gulpfile.js⽂件,并安装相应的项⽬依赖。

第⼀个问题相对来说好解决,gulpfile.js代码如下:
//导⼊⼯具包 require('node_modules⾥对应模块')
var gulp = require('gulp'), //本地安装gulp所⽤到的地⽅
less = require('gulp-less'),
rev = require('gulp-rev'),
nodepath = require('path'),
through = require('through2'),
gutil = require('gulp-util'),
inject = require('gulp-inject'),
cleanCSS = require('gulp-clean-css'),
assetRev = require('gulp-asset-rev'),
revCollector = require('gulp-rev-collector'),
revReplace = require("gulp-rev-replace");
var path = {
app: process.cwd(), //当前app的⽬录路径
basePath: './datawork/websites/visual', //基础⽬录
lessPath: './datawork/websites/visual/assets/css/theme/', //需打包编译的less路径和产出css⽂件的⽬录
}
//定义⼀个testLess任务(⾃定义任务名称)
gulp.task('testLess', function() {
gulp.src(path.lessPath + 'd_theme_main.less') //该任务针对的⽂件
.pipe(less()) //该任务调⽤的模块
.pipe(cleanCSS())
.pipe(gulp.dest(path.lessPath)); //将会在src/css下⽣成相应的css⽂件
});
经过编译测试发现结果如预期,对less⽂件进⾏编译打包压缩,并在当前⽬录下⽣成相应的css⽂件。

接下来,编写加戳功能和⾃动编译的代码,代码如下所⽰:
//css⽂件⽣成hash编码并⽣成rev-manifest.json⽂件名对照映射
gulp.task('revCss',['testLess'], function() {
return gulp.src(path.lessPath + 'd_theme_main.css')
.pipe(rev()) //针对相应的css⽂件⽣成md5戳
.pipe(rev.manifest()) //⽣成rev-manifest.json⽂件
.pipe(gulp.dest('./')) //rev-manifest.json⽂件的⽣成位置
})
//参照rev-manifest.json⽂件,替换相应⽬录下的lib.tpl⽂件对于css⽂件的引⽤
gulp.task('revhtml',['revCss'], function() {
return gulp.src(['./rev-manifest.json', path.basePath + '/protected/views/layouts/lib.tpl'])
.pipe(revCollector())
.pipe(gulp.dest(path.basePath + '/protected/views/layouts/'));
})
gulp.task('watch', function() {
gulp.watch(path.lessPath + '*.less', ['revhtml']);
})
gulp.task('default', ['revhtml', 'watch']); //定义默认任务 
执⾏gulp命令,得到的结果如下:
根⽬录下⽣成了manifest.json对应⽂件
{
"d_theme_main.css": "d_theme_main-803a7fe4ae.css"
}
<link rel="stylesheet" href="/assets/css/theme/d_theme_main-803a7fe4ae.css">
这显然不符合我们的预期,我们并不需要在相应的⽬录下⽣成d_theme_main-803a7fe4ae.css⽂件同时改变tpl⽂件中的引⽤,我们只需要在相应的引⽤后⾯加戳即可。

更改gulp-rev和gulp-rev-collector
打开node_modules\gulp-rev\index.js
第144⾏ manifest[originalFile] = revisionedFile;
更新为: manifest[originalFile] = originalFile + '?v=' + file.revHash;
打开nodemodules\gulp-rev\nodemodules\rev-path\index.js
10⾏ return filename + '-' + hash + ext;
更新为: return filename + ext;
打开node_modules\gulp-rev-collector\index.js
31⾏ if ( !_.isString(json[key]) || path.basename(json[key]).replace(new RegExp( opts.revSuffix ), '' ) !== path.basename(key) ) {
更新为: if ( !_.isString(json[key]) || path.basename(json[key]).split('?')[0] !== path.basename(key) ) {
打开node_modules\gulp-assets-rev\index.js
78⾏ var verStr = (options.verConnecter || "-") + md5;
更新为:var verStr = (options.verConnecter || "") + md5;
80⾏ src = src.replace(verStr, '').replace(/(\.[^\.]+)$/, verStr + "$1");
更新为:src=src+"?v="+verStr;
再执⾏gulp命令,得到的结果如下(效果正确):
<link rel="stylesheet" href="/assets/css/theme/d_theme_main.css?v=afd12860ab">
但是假如我们更改了css和js后,再执⾏gulp命令,得到的结果会如下:
<link rel="stylesheet" href="/assets/css/theme/d_theme_main.css?v=afd12860ab?v=803a7fe4ae">
有没有发现,会在版本号后⾯再添加⼀个版本号,因为gulp只替换了原来⽂件名,这样⼜不符合预期效果了,所以我们想到,还需要修改插件的替换正则表达式。

4、继续更改gulp-rev-collector
打开node_modules\gulp-rev-collector\index.js
第107⾏ regexp: new RegExp( '([\/\\\\\'"])' + pattern, 'g' ),
更新为: regexp: new RegExp( '([\/\\\\\'"])' + pattern+'(\\?v=\\w{10})?', 'g' ),
现在你不管执⾏多少遍gulp命令,得到的html效果都是
<link rel="stylesheet" href="/assets/css/theme/d_theme_main.css?v=86f020d88a">
⾄此,⼀个较为完善的⼀个⽅案就诞⽣了,但是还有优化的地⽅吗,答案⾃然⾃然是有的,假如这时⼜有其他⼈介⼊此项⽬的开发,你总不能让他们根据你的⽂档去修改相应插件的源码吧,于是进⾏以下修改:
//导⼊⼯具包 require('node_modules⾥对应模块')
var gulp = require('gulp'), //本地安装gulp所⽤到的地⽅
less = require('gulp-less'),
rev = require('gulp-rev'),
nodepath = require('path'),
through = require('through2'),
gutil = require('gulp-util'),
inject = require('gulp-inject'),
cleanCSS = require('gulp-clean-css'),
revReplace = require("gulp-rev-replace");
var path = {
app: process.cwd(), //当前app的⽬录路径
basePath: './datawork/websites/visual', //基础⽬录
lessPath: './datawork/websites/visual/assets/css/theme/', //需打包编译的less路径和产出css⽂件的⽬录
}
//定义⼀个testLess任务(⾃定义任务名称)
gulp.task('testLess', function() {
gulp.src(path.lessPath + 'd_theme_main.less') //该任务针对的⽂件
.pipe(less()) //该任务调⽤的模块
.pipe(cleanCSS())
.pipe(gulp.dest(path.lessPath)); //将会在src/css下⽣成index.css
});
gulp.task('rev', ['testLess'], function() {
return gulp.src([path.lessPath + 'd_theme_main.css'])
.pipe(rev())
.pipe(function() {
var hashes = {},
oPath = '';
var collect = function(file, enc, cb) {
if (file.revHash) {
var filename = nodepath.basename(file.revOrigPath);
hashes[filename] = filename + '?v=' + file.revHash;
oPath = file.base;
}
return cb();
}
var emit = function(cb) {
if (oPath) {
var file = new gutil.File({
base: oPath,
cwd: process.cwd(),
path: nodepath.resolve(oPath, 'rev-manifest.json')
});
file.contents = new Buffer(JSON.stringify(hashes, null, 4));
this.push(file);
}
return cb();
}
return through.obj(collect, emit);
}())
.pipe(gulp.dest(path.app))
.pipe(rev.manifest())
})
gulp.task('revCollector', ['rev'], function() {
var manifest = gulp.src([nodepath.resolve(path.app, 'rev-manifest.json')]);
var cssPath = nodepath.resolve(path.lessPath, 'd_theme_main.css');
return gulp.src([path.basePath + '/protected/views/layouts/lib.tpl'])
.pipe(inject(gulp.src(cssPath, {read: false}), {
transform: function (filepath, file, i, length) {
var fpath = ['/assets', filepath.split('assets')[1]].join('');
return '<link rel="stylesheet" href="'+fpath+'">';
}
}))
.pipe(revReplace({
manifest: manifest,
replaceInExtensions: ['.tpl']
}))
.pipe(gulp.dest(path.basePath + '/protected/views/layouts/'));
})
gulp.task('watch', function() {
gulp.watch(path.lessPath + '*.less', ['revCollector']);
})
gulp.task('default', ['revCollector', 'watch']); //定义默认任务
//gulp.task(name[, deps], fn) 定义任务 name:任务名称 deps:依赖任务名称 fn:回调函数
//gulp.src(globs[, options]) 执⾏任务处理的⽂件 globs:处理的⽂件路径(字符串或者字符串数组) //gulp.dest(path[, options]) 处理完后⽂件⽣成路径
⾄此,⼀个更为完善的前端构建⽅案就诞⽣了。

相关文档
最新文档