上一篇文章談到了重構(gòu)的原因和基本思路绅项,在這片文章里我們將具體地闡述我們是如何做的紊册。
技術(shù)選型
基本采用了原有的技術(shù)實現(xiàn),以js
快耿、less
和html
為基礎(chǔ)囊陡,這沒什么好說的。在服務(wù)端模板的選擇中掀亥,我們選擇了smarty
模板关斜,在糯米的wap端,我們采用了crox
這個模板铺浇,兩者我們都解決了同一份模板在前后端通用的小難題痢畜,大大地提高了開發(fā)效率。
目錄結(jié)構(gòu)
我們的目錄結(jié)構(gòu)參考了我們的團隊規(guī)范鳍侣,拋棄了原有的FIS
的結(jié)構(gòu)形式丁稀,變?yōu)橐韵滦问剑?/p>
.
├── bower.json
├── build
├── build.sh
├── dep // 第三方依賴
├── fis-conf.js
├── gulpfile.js
├── mock
├── node_modules
├── package.json
├── preview
├── src
├── static // 靜態(tài)資源
├── template // 后端模板
├── test
├── tool
└── webpack.config.js
模塊管理
我們的模塊構(gòu)建方式采用commonjs
,因此我們可以利用npm
上大量的優(yōu)質(zhì)模塊倚聚,通過webpack
可以跟我們前端開發(fā)常用的AMD
和CMD
無縫的結(jié)合线衫。
第三方依賴
開發(fā)過程中,必不可少將會用到社區(qū)里現(xiàn)有的優(yōu)質(zhì)模塊惑折,通過bower
進行所有模塊的管理授账,我們將統(tǒng)一放在dep
文件夾下進行管理,并通過webpack
的配置惨驶,可以在本地模塊的應用白热。 如在webpack
的reslove
配置項中配置相應的alias
,就可以在實際開發(fā)中調(diào)用它粗卜,如requirejs
的path
配置屋确。
// webpack.config.js
alias = {
'moye': depPath + '/moye/src/ui',
'avalon': depPath + '/avalon/dist/avalon',
'etpl': depPath + '/etpl/src/main'
};
// main.js
var moye = require('moye');
var avalon = require('avalon');
管理其他資源
開發(fā)過程中,一個組件通常包含多種資源(圖片、less攻臀、swf焕数、tpl等),如何才能方便的調(diào)用和管理呢刨啸?
對于以上問題堡赔,通過webpack
的loader機制,我們將所有的資源都統(tǒng)一在JS中進行管理设联,而且我們可以通過編寫自己的loader善已,來實現(xiàn)自己的所需的資源加載機制。舉個栗子仑荐,
require('./main.less'); // 組件所需的less
var etpl = require('etpl');// 前端模板引擎
var search = require('./search/main'); // js
var suggestion = require('raw!./suggestion.html'); // html文本雕拼,前端模板引擎用
var smartyTpl = require('smarty4js!./smarty.tpl'); // smarty模板纵东,轉(zhuǎn)成js模板函數(shù)
var $ = require('jquery');
var exports = {
init: function (options) {
// do something
},
render: function () {
// data是數(shù)據(jù)對象
etpl.compile(suggestion).render(data);
smartyTpl.render(data);
}
};
module.exports = exports;
通過上面的代碼實例粘招,我們發(fā)現(xiàn)所有的資源都在一個main.js
獲得清晰的展現(xiàn),而不是分散在整個項目中偎球,因此便于更好地維護一個組件洒扎,組件在一定程度上變得內(nèi)聚。通過webpack
的loader衰絮,我們將一些需要通過手動或者命令行方式的工作袍冷,直接通過代碼就能實現(xiàn)。
前端測試
測試采用了karma
+ jasmine
結(jié)合完成猫牡,因為我們的運行代碼是通過webpack
打包器生成的胡诗,所以測試過程中需要karma
與webpack
相結(jié)合。
上線部署
在上線部署中淌友,我們保留原有的FIS的部署功能煌恢,利用FIS強大的工程化的功能,我們的將資源路徑替換震庭、發(fā)布到測試機器瑰抵、壓縮打包、strong text器联、資源統(tǒng)計追蹤等繁瑣的工作全部以工程化的形式完成二汛,大大的節(jié)約了人力成本,有興趣可以參考FIS官網(wǎng)的示例拨拓。
構(gòu)建工具
將一系列工作我們通過gulp
全部串聯(lián)起來肴颊,整個過程所需要的命令為數(shù)不多。我們所用的gulp文件如下:
var gulp = require('gulp');
var config = require('./webpack.config');
var webpack = require('gulp-webpack-build');
var shelljs = require('shelljs');
var path = require('path');
var fs = require('fs');
var cwd = process.cwd();
var etpl = require('etpl');
var karma = require('karma').server;
// clean all files
gulp.task('clean', function () {
shelljs.rm('-rf', path.join(cwd, 'static','common'));
shelljs.rm('-rf', path.join(cwd, 'static','car'));
});
function printResult(stats) {
stats = stats.toJson();
(stats.errors || []).forEach(function (err) {
console.error('error', err);
});
stats.assets.forEach(function (item) {
var size = (item.size / 1024.0).toFixed(2) + 'kB';
console.log('generated', item.name, size);
});
}
var src = './src',
webpackOptions = {
debug: false,
// devtool: '#source-map',
watchDelay: 200
},
webpackConfig = {
useMemoryFs: true,
progress: true
};
var CONFIG_FILENAME = './webpack.config.js';
var dest = './static';
gulp.task('webpack', [], function() {
return gulp.src('./webpack.config.js', { base: path.resolve(src) })
.pipe(webpack.init(webpackConfig))
.pipe(webpack.props(webpackOptions))
.pipe(webpack.run())
.pipe(webpack.format({
version: false,
timings: true
}))
.pipe(webpack.failAfter({
errors: true,
warnings: true
}))
.pipe(gulp.dest(dest));
});
gulp.task('build', function () {
gulp.src('./src/**/*.tpl')
.pipe(gulp.dest('template/newpc'));
});
gulp.task('watch', function () {
gulp.watch(['./src/**/*.js', './src/**/*.less']).on('change', function(event) {
if (event.type === 'changed') {
gulp.src(event.path, { base: path.resolve(src) })
.pipe(webpack.closest(CONFIG_FILENAME))
.pipe(webpack.init(config))
.pipe(webpack.props(webpackOptions))
.pipe(webpack.watch(function(err, stats) {
gulp.src(this.path, { base: this.base })
.pipe(webpack.proxy(err, stats))
.pipe(webpack.format({
verbose: true,
version: false
}))
.pipe(gulp.dest(dest));
}));
}
});
});
gulp.task('test', function (done) {
karma.start({
configFile: __dirname + '/test/config.js'
}, done);
});
gulp.task('debug', ['webpack'], function(){
var process = require('child_process');
process.exec('fis release -d local');
});
后續(xù)規(guī)劃
現(xiàn)如今糯米PC的技術(shù)框架還是采用以jquery
為基礎(chǔ)的模塊化前端框架渣磷,這次通過commonjs
的組織方式苫昌,也是為了后面代碼能夠在Node
等后端進行處理,更好地達到前后端分層開發(fā),完完全全去除后端模板的依賴祟身。
在思考的過程中奥务,實際是借鑒React
,希望后續(xù)在瀏覽器兼容放開的基礎(chǔ)和React
更加成熟的基礎(chǔ)上袜硫,糯米部分復雜的業(yè)務(wù)能采用React Component
的方式進行開發(fā)氯葬,來實現(xiàn)同構(gòu)開發(fā)。