本文基于bootstrap2.3.2。
一,文件结构:
▾ docs/
▸ assets/
▸ build/
▸ examples/
▸ templates/
base-css.html
components.html
customize.html
extend.html
getting-started.html
index.html
javascript.html
scaffolding.html
▸ img/
▸ js/
▸ less/
▸ node_modules/
bower.json
CHANGELOG.md
composer.json
CONTRIBUTING.md
LICENSE
Makefile
package.json
README.md
文件结构如上所示,其中:
docs:文档,里面其实有两种文档,docs根目录下的是有templates里面的模板编译生成的,examples里面是直接写的html没有用模板。assets目录里面好包括了从根目录copy过去的bootstrap代码(js,css和img)。其中templates下的模板是用mustache写的,mustache是一个通用的无逻辑模板语言,
http://mustache.github.io/
, 其中js的实现是hogan。
img: 两种sprite icon
js:js源码,这里没有用任何工具解决依赖,是通过在make的时候指定文件顺序来解决模块依赖关系的。
less:less源码,这里有两个入口文件分别是bootstrap和responsive。
bootstrap可以按顺序分成如下几个部分:
1,变量和函数:variables.less,mixins.less
2,重置默认样式:reset.less
3,脚手架:scaffolding.less,grid.less,layout.less
4,基础样式:type.less,form.less,tables.less
5,css组件:btn nav等
6,js组件:popover, dialog
Makefile:make脚本,下面会详细讲
在执行make之后会生成bootstrap目录,里面就是编译后的js和css文件。
2.3和3.0在文件结构上也有些区别:
1,用icon font代替了img
2,用grunt代替了make
二,代码构建工具makefile:
bootstrap使用make脚本构建代码,但是make脚本实际上是调用nodejs,所以仍然需要执行npm install之后才能执行make。
make实际上定义了四种任务:
1,编译源码,包括js和css,输出到bootstrap目录下
2,测试,包括了hint进行语法检查和qunit进行单元测试
3,构建文件,这里包括两个步骤,一是调用npm插件编译mustache模板,二是把必要的文件从根目录拷贝到docs/assets目录下
4,watch
在3.0版本中,make已经被grunt取代了,如果使用2.3但是任然想使用grunt而不是make,可以参考3.0版本的grunt脚本进行修改。等价的Gruntfile.js如下:
/* jshint node: true */
exec = require("child_process").exec;
module.exports = function(grunt) {
'use strict';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
banner: '/*dpl started*/',
distRoot: 'dist',
docsRoot: 'docs',
clean: {
dist: ['<%= distRoot %>']
},
jshint: {
options: {
jshintrc: 'js/.jshintrc'
},
gruntfile: {
src: 'Gruntfile.js'
},
src: {
src: ['js/*.js']
},
test: {
src: ['js/tests/unit/*.js']
}
},
concat: {
options: {
banner: '<%= banner %>',
stripBanners: false
},
bootstrap: {
src: [
'js/bootstrap-transition.js',
'js/bootstrap-alert.js',
'js/bootstrap-button.js',
'js/bootstrap-carousel.js',
'js/bootstrap-collapse.js',
'js/bootstrap-dropdown.js',
'js/bootstrap-modal.js',
'js/bootstrap-tooltip.js',
'js/bootstrap-popover.js',
'js/bootstrap-scrollspy.js',
'js/bootstrap-tab.js',
'js/bootstrap-affix.js'
],
dest: '<%= distRoot %>/js/<%= pkg.name %>.js'
},
},
uglify: {
options: {
banner: '<%= banner %>'
},
bootstrap: {
src: ['<%= concat.bootstrap.dest %>'],
dest: '<%= distRoot %>/js/<%= pkg.name %>.min.js'
}
},
recess: {
options: {
compile: true
},
bootstrap: {
src: ['less/bootstrap.less'],
dest: '<%= distRoot %>/css/<%= pkg.name %>.css'
},
reponsive: {
src: ['less/responsive.less'],
dest: '<%= distRoot %>/css/<%= pkg.name %>-responsive.css'
},
min: {
options: {
compress: true
},
src: ['less/bootstrap.less'],
dest: '<%= distRoot %>/css/<%= pkg.name %>.min.css'
},
minresponsive: {
options: {
compress: true
},
src: ['less/responsive.less'],
dest: '<%= distRoot %>/css/<%= pkg.name %>-responsive.min.css'
}
},
copy: {
docs: { //doc 必须依赖于bootstap.min.js
files: [
{ expand: true, src: ['img/*'], dest: '<%= docsRoot %>/assets/' },
{ expand: true, src: ['js/*.js'], dest: '<%= docsRoot %>/assets/' },
{ expand: true, src: ['fonts/*'], dest: '<%= docsRoot %>/assets/' },
{ expand: true, cwd: 'js/test/vendor/', src:['jquery.js'], dest: '<%= docsRoot %>/assets/js/' },
{ expand: true, cwd: '<%= distRoot %>/js/', src: ['<%= pkg.name %>.js'], dest: '<%= docsRoot %>/assets/js/' },
{ expand: true, cwd: '<%= distRoot %>/css/', src: ['<%= pkg.name %>.css'], dest: '<%= docsRoot %>/assets/css/' }
]
}
},
qunit: {
options: {
inject: 'js/tests/unit/bootstrap-phantom.js'
},
files: ['js/tests/*.html']
},
connect: {
server: {
options: {
port: 3000,
base: '.'
}
}
},
watch: {
css: {
files: 'less/*.less',
tasks: ['recess']
},
js: {
files: 'js/*.js',
tasks: ['dist-js']
}
}
});
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-html-validation');
grunt.loadNpmTasks('grunt-jekyll');
grunt.loadNpmTasks('grunt-recess');
grunt.loadNpmTasks('browserstack-runner');
// Test task.
var testSubtasks = ['dist-css', 'jshint'];
grunt.registerTask('test', testSubtasks);
grunt.registerTask('hogan', 'compile mustache template', function() {
var done = this.async();
var child = exec('node docs/build', function(e) { done(); })
});
// JS distribution task.
grunt.registerTask('dist-js', ['concat', 'uglify']);
// CSS distribution task.
grunt.registerTask('dist-css', ['recess']);
// Full distribution task.
grunt.registerTask('dist', ['clean', 'dist-css', 'dist-js']);
grunt.registerTask('docs', ['dist', 'hogan', 'copy']);
// Default task.
grunt.registerTask('default', ['test', 'dist']);
}