在美团主站的应用
liyang24 @meituan
-
Grunt基础介绍
-
Grunt API
-
Grunt Plugins
-
-
版本号生成Demo => fe-version
-
Grunt 在美团主站项目中的应用
-
主站仓库中基于Grunt实现的工具
-
上线发布脚本对静态文件的处理
-
Grunt 要干啥?
输入 + 配置 =》 输出
Gruntfile.js + packages.json(npm) + Task
Gruntfile.js => 配置
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint']);
};
packages.json => 依赖
{
"name": "fe-version",
"version": "1.0.0",
"description": "version number of static files.",
"main": "lib/version.js",
"scripts": {
"test": "jest"
},
"keywords": [
"version"
],
"author": "solome",
"license": "MIT",
"devDependencies": {
"jest-cli": "^0.4.0"
},
"dependencies": {
"grunt": "^0.4.5",
"grunt-contrib-watch": "^0.6.1"
}
}
Task => 具体的处理逻辑
module.exports = function(grunt) {
var version = require('../lib/version');
grunt.registerMultiTask('fe-version', '给静态文件添加版本号', function() {
var task = this;
var options = task.options();
var files = task.files;
files.forEach(function(file){
file.src.forEach(function(f) {
var v = version(f);
console.log(f, '=>', version(f));
grunt.file.copy(f, f + '.' + v);
});
});
});
}
Grunt API
不建议过度使用Grunt API,容易造成与Grunt的深耦合!
Grunt Plugins
JavaScript Plugins
- grunt-jscs => Check code style
- grunt-contrib-jshint => Check code errors
- grunt-contrib-concat => Concat multiple files
- grunt-contrib-uglify => Minify the JS files
CSS Plugins
- grunt-contrib-compass => Sass t CSS using Compass
- grunt-contrib-csslint => Check code errors
- grunt-scsslint => Check scss code errors
- grunt-contrib-cssmin => Minify the CSS files
Other Plugins
Grunt最大的优势:官方提供了许多高质量的Plugins!
.
|____Gruntfile.js
|____lib
| |____version.js
|____package.json
|____task
| |____fe-version.js
|____test
| |____test.js
核心逻辑:lib/version.js
'use strict';
var crypto = require('crypto');
var fs = require('fs');
/**
* 计算文件的md5加密值
*/
function md5(fp) {
var md5 = crypto.createHash('md5');
md5.update(fs.readFileSync(fp));
return md5.digest('hex').substring(0, 8);
}
/**
* 获取文件的版本号
*/
function version(file) {
if (fs.existsSync(file)) {
return md5(file);
}
return false;
}
module.exports = version;
npm依赖包:packages.json
{
"name": "fe-version",
"version": "1.0.0",
"description": "version number of static files.",
"main": "lib/version.js",
"scripts": {
"test": "jest"
},
"keywords": [
"version"
],
"author": "solome",
"license": "MIT",
"devDependencies": {
"jest-cli": "^0.4.0"
},
"dependencies": {
"grunt": "^0.4.5",
"grunt-contrib-watch": "^0.6.1"
}
}
Task模块:task/fe-version.js
'use strict';
module.exports = function(grunt) {
var version = require('../lib/version');
grunt.registerMultiTask('fe-version', '给静态文件添加版本号', function() {
var task = this;
var options = task.options();
var files = task.files;
files.forEach(function(file){
file.src.forEach(function(f) {
var v = version(f);
console.log(f, '=>', version(f));
grunt.file.copy(f, f + '.' + v);
});
});
});
}
配置:Gruntfile.js
module.exports = function(grunt) {
'use strict';
var appPath = '/Users/ivanlyons/app/raspberrypi/';
grunt.loadTasks('./task');
// 配置
grunt.initConfig({
appPath: appPath,
'fe-version': {
src: ['<%= appPath %>/**/*.js','<%= appPath %>/**/*.css'],
options: {
flag: 'v'
}
},
watch: {
files: ['<%= appPath %>/**/*.js'],
tasks: ['fe-version']
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.file.setBase(appPath);
grunt.registerTask('default', ['fe-version']);
};
$ npm install
$ grunt fe-version
.
|____css
| |____common.css
| |____common.css.3fb999ef
| |____ready2retire.css
| |____ready2retire.css.32fc7c82
|____js
| |____common.js
| |____common.js.7c5b2157
| |____jquery.min.js
| |____jquery.min.js.397754ba
| |____jquery.min.map
| |____ready2retire.js
| |____ready2retire.js.7a405d55
Grunt在主站的应用
-
编译工具
-
审查工具
-
上线前对静态资源的处理
www仓库中的Grunt
-
编译工具
-
审查工具
编译工具:编译LightnCandy模板文件.hbs => .php
grunt.registerMultiTask(
'candyCompile',
'compile lightncandy template',
function() {}
);
$ grunt candyCompile:file:{filePath}
$ grunt candyCompile:file:partials
=> grunt/task/candyCompile.js
// 快速配置一个component
grunt.loadNpmTasks('mtfe-grunt-component');
// JavaScript 模块依赖文件生成
grunt.loadNpmTasks('@mtfe/grunt-buildmeta');
// 将svg矢量图标编译成使用
grunt.loadNpmTasks('mtfe_fe.iconfont');
$ grunt mtfe-grunt-buildmeta
$ grunt iconfont
JavaScript 模块依赖文件生成
将svg矢量图标编译成字體文件
编译工具
$ grunt jshint
审查工具:保障源码质量
$ grunt csshint
$ grunt scsshint
$ grunt checkimgext
$ grunt phphint
检查JavaScript语法
检查CSS/SCSS源码
检查PHP源码语法
检查图片资源后缀名的合法性
这么多的Plugins・・・・!
主站www仓库所有的Grunt Task
上线发布工具
美团主站平均每天上线40次左右!
从前端工程师的角度来看,上线前都做了什么呢!?
每次上线平均耗时两分钟左右!
主要对静态文件的处理
grunt.registerTask('pre',
['clean:build', 'version:img', 'mt_cssimg', 'mtfe-grunt-buildmeta']
);
grunt.registerTask('build',
['version:js', 'mt_uglify', 'checkimgext', 'mt_imagemin',
'version:css', 'mt_cssmin', 'rsync:build', 'after']
);
grunt.registerTask('default', ['build']);
- 'clean:build' 清理目录
- 'version:img' 给图片资源生成版本号
- 'mt_cssimg' 给CSS文件中的图片url增加版本号
- 'mtfe-grunt-buildmeta' 生成JavaScript文件依赖
grunt.registerTask('pre',[
'clean:build',
'version:img',
'mt_cssimg',
'mtfe-grunt-buildmeta'
]);
预处理
Task=> version
基于md5算法计算文件的版本号
- version:css
- version:js
- version:img
Task => css_img
-
给CSS文件中的图片url增加版本号
.slider-sprite{
background-image:url(si/slider.png);
background-repeat:no-repeat
}
.slider-sprite{
background-image:url(si/slider.v32cb9c24.png);
background-repeat:no-repeat
}
-
检查CSS文件中的图片url是否存在
=> 即判断si/slider.png文件是否存在
Task => mtfe-grunt-buildmeta
上线前依旧会执行buildmeta操作,
做JavaScript模块依赖最后的检查工作。
- mt_uglify => 压缩JavaScript文件
- checkimgext => 检查图片资源后缀名的合法性
- mt_imagemin => 压缩图片
- mt_cssmin => 压缩css文件
- rsync => 拷贝构建涉及的静态文件到缓存目录
- after => 生成version.json,供FileHelper使用
grunt.registerTask('build',[
'version:js', 'mt_uglify',
'checkimgext', 'mt_imagemin',
'version:css', 'mt_cssmin',
'rsync:build', 'after'
]);
构建:压缩 & 版本号文件生成
构建流程
本地模拟上线
Grunt vs Gulp
Thanks!
Grunt 在美團主站的應用
By Ivan Lyons
Grunt 在美團主站的應用
主要介绍Grunt在美团主站的应用,如javascript/scc压缩,模块依赖,源码审查等。 https://github.com/solome/fe-version
- 2,526