我认为流水线的想法是有效的,并不过分,因为它加快了开发周期。我有一个项目可以作为起点。
https://github.com/mhanney/twitter-bootstrap-customization
它按照您的建议使用 Grunt、Bower 和 LESS,并且还优化了 css 和 js。我称它为管道,因为它使用grunt-contrib-watch 和grunt-contrib-connect 在文件更改时重建和重新加载文件。与使用 point and click web UI 相比,这使得迭代主题所需的时间要少得多。
我以这种方式进行自定义构建的动机不仅是为了自定义主题,而且是为移动 Web 应用程序制作更小的 Bootstrap.css 和 Bootstrap.js。很多类和js函数可以省略。我使用uncss 和processhtml grunt 插件的组合来做到这一点。
我上面链接的项目是我的典型移动网络应用程序构建过程的简化版本。 gruntfile.js 中的 cmets 应该有助于阐明管道中每个步骤的作用。我在这里的答案中包含了 package.json、bower.json、gruntfile.js 和 README 以供快速参考,但您也需要其他部分。
@staypuftman 和@Hitmands 很重要。为什么对@Hitmands 投反对票?这是一个很好的答案。总是有不止一种方法可以做到这一点。
package.json
{
"name": "Twitter-Bootstrap-Customization-Pipeline-Example",
"description": "Twitter Bootstrap Customization Pipeline using Grunt",
"version": "0.0.1",
"build": "1",
"license": "MIT",
"repository": "git@github.com:mhanney/twitter-bootstrap-customization.git",
"private": false,
"author": {
"name": "mhanney",
"url": "https://github.com/mhanney/twitter-bootstrap-customization"
},
"devDependencies": {
"bower": "^1.4.1",
"connect": "~3.4.0",
"grunt": "^0.4.5",
"grunt-autoprefixer": "^3.0.3",
"grunt-bower-task": "^0.4.0",
"grunt-cli": "0.1.13",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-concat": "^0.5.1",
"grunt-contrib-connect": "^0.10.1",
"grunt-contrib-cssmin": "~0.12.3",
"grunt-contrib-less": "^1.0.1",
"grunt-contrib-uglify": "^0.10.0",
"grunt-contrib-watch": "0.6.1",
"grunt-processhtml": "~0.3.8",
"grunt-uncss": "~0.4.3",
"less-plugin-autoprefix": "^1.4.2"
}
}
bower.json
{
"name": "Twitter-Bootstrap-Customization-Pipeline-Example",
"version": "0.0.1",
"dependencies": {
"bootstrap": "~3.3.5"
},
"private": false
}
gruntfile.js
module.exports = function(grunt) {
'use strict';
grunt.loadNpmTasks('grunt-bower-task');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-uncss');
grunt.loadNpmTasks('grunt-processhtml');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.initConfig({
// variable to the location of the bootstrap javascript source
dirs: {
'bootstrapjs': 'bower_components/bootstrap/js',
},
// defer to bower.json to decide which bower packages to install
bower: {
install: {}
},
// Process the bootstrap less files.
// The paths setting here is critical.
// We include all of the bootstrap less files from bower,
// then incude our modifications and overrides to
// bootstrap by bringing in the file src/theme/build.less.
// Our build.less is actually a list of more includes.
// This is a shortcut. We could have listed all our less includes
// here instead.
//
// We then use the less-plugin-autoprefix plug-in to
// only add brower specific prefixes for our known target audience -
// in my case this is mobile browsers - iOS and Android.
// The result of the less build is placed into our 'intermediate' directory
// for the optimization steps.
less: {
default: {
options: {
paths: ['bower_components/bootstrap/less'],
compress: false,
strictMath: true,
plugins: [
new(require('less-plugin-autoprefix'))({
browsers: ['iOS >= 7', 'Android >= 4', 'last 2 versions']
})
],
},
files: {
'intermediate/bootstrap-custom.css': 'src/theme/build.less'
},
}
},
// css minification
cssmin: {
minify: {
expand: false,
src: 'intermediate/bootstrap-custom.css',
dest: 'public/bootstrap-custom.min.css'
}
},
// uncss removes css styles not found in src/index.html
uncss: {
public: {
files: {
'public/bootstrap-custom.css': ['src/index.html']
}
}
},
// The file 'src/index.html' gets transformed by processhtml to use our
// optimized 'uncss' version of bootstrap.
// The processed index.html is copied into the `public`
// directory where it is served by connect
processhtml: {
public: {
files: {
'public/index.html': ['src/index.html'],
}
}
},
// Declare which components of bootstrap.js to include.
// Concatenate the files we choose into one file
// called bootstrap-custom.js in the `intermediate`
// directory where it will get processed by the next
// step in the pipeline - `uglify` for js minification.
// Be very careful here not to cut too munch of Bootstrap's
// functionality. I do this optimization only when I am absolutely sure
// that whole features - such as modals - are not used in a mobile
// we app.
concat: {
bootstrap: {
src: [
'<%=dirs.bootstrapjs%>/transition.js',
//'<%=dirs.bootstrapjs%>/alert.js',
//'<%=dirs.bootstrapjs%>/button.js',
//'<%=dirs.bootstrapjs%>/carousel.js',
'<%=dirs.bootstrapjs%>/collapse.js',
//'<%=dirs.bootstrapjs%>/dropdown.js',
//'<%=dirs.bootstrapjs%>/modal.js',
//'<%=dirs.bootstrapjs%>/tooltip.js',
//'<%=dirs.bootstrapjs%>/popover.js',
//'<%=dirs.bootstrapjs%>/scrollspy.js',
//'<%=dirs.bootstrapjs%>/tab.js',
//'<%=dirs.bootstrapjs%>/affix.js'
],
dest: 'intermediate/bootstrap-custom.js'
}
},
// js minification
// A standard uglification task.
// Takes the bootstrap js from the `intermediate`
// dir and puts a minified copy in `public` - the dir
// being served by node/connect for previewing.
uglify: {
options: {
sourceMap: true
},
bootstrap: {
files: {
'public/bootstrap-custom.min.js': ['intermediate/bootstrap-custom.js']
}
}
},
// Start up a simple nodejs web server for fast reload of the test index
// page that references our custom bootstrap css and js.
// With livereload = true changes will update in the browser without having
// to manually F5 reload in the browser.
// We set keepalive to false, because the `watch` task has a
// keepalive=true and that is enough to keep the
// grunt task runner from stopping (which would stop connect)
connect: {
base: {
options: {
base: 'public',
port: 8082,
livereload: true,
keepalive: false,
open: true
}
}
},
// Run the css pipeline again if any of the following files change.
// With livereload = true changes will update in the browser without having
// to manually F5 reload in the browser.
watch: {
files: ['src/theme/bootstrap.less',
'src/theme/variables.less',
'src/theme/structural.less',
'src/index.html'
],
tasks: 'css',
options: {
livereload: true,
nospawn: true
}
},
// Utility task to clean up
// the temporary `intermediate`
// and `public` directories.
// Be very, very careful running grunt clean:nuke
// it only exists for making a really, really clean
// source tree for pushing to source control.
clean: {
build: {
src: ['intermediate/*.*',
'public/*.*']
},
nuke: {
src: ['bower_components',
'lib',
'node_modules',
'intermediate',
'public']
}
}
});
// We declare task `targets`, or aliases so that
// we can call them individually on the command line
// if we want. This is useful for testing the order in which
// the tasks need to run. To run just one of these tasks,
// do `grunt css` - for example.
// declare a task target called 'css' that will do the
// less, uncss, cssmin and processhtml tasks
grunt.registerTask('css', ['clean:build', 'less', 'cssmin', 'processhtml:public']);
// declare a task target called 'js' that will do the concat and uglify steps
grunt.registerTask('js', ['concat', 'uglify']);
// declare the default task that will run
// the `css` target, followed by the `js` target.
// Order is important here. We must build the css and js before
// starting the `connect` local web server, and file watcher tasks
grunt.registerTask('default', ['css', 'js', 'connect', 'watch']);
};
自述文件
Twitter Bootstrap 3 自定义管道示例
这是一个使用 grunt 自定义 Twitter Bootstrap 的构建过程。
动机
问题
- Twitter Bootstrap 的流行使得所有使用它的网站看起来都差不多。
我们经常需要定制主题以与公司的品牌保持一致。
此外,Twitter Bootstrap CSS 对于移动浏览器来说实际上有点大(~130K)
并且通常包含许多未使用的样式。许多 JavaScript 组件
对于为移动 Web 应用程序量身定制的超精益构建,也可以删除。最后,网络点击工具很好,但在迭代设计时会耗费大量人力。我们需要一个持续的“观察和重建”管道。
解决方案
- 使用 grunt 任务运行器,我们将构建一个高效的管道以
构建 Bootstrap JavaScript 和 CSS,优化输出,进行测试
index.html 页面,并让 grunt 监视源文件的更改,以便
管道自动重新开始,更改在浏览器中可见
无需按 F5 即可立即使用。
TL;DR
git clone git@github.com:mhanney/twitter-bootstrap-customization.git
cd twitter-bootstrap-customization
npm install --no-optional
grunt bower
grunt
什么做什么?
NPM
NPM 是 node 的包管理器。我们使用它来放置 grunt 任务(在 node_modules 中)。
我们只使用 NPM 来获取我们的构建管道工具。我们不使用 NPM 来获取我们的前端组件源。
尽管可以从 NPM 获取 Bootstrap 和 jQuery,但使用 Bower 更有意义,
因为 Bower 将前端资源放在 ~/lib 中——这是我们在
index.html 测试页面比 ~/node_modules 深处的资源。
凉亭
Bower 是前端 Web 组件的包管理器。
我们将使用它将引导源(及其依赖项 - jQuery)引入我们的项目。
就这些。我们仅使用 bower 将源文件(由其他方维护)复制到位。
bower grunt 任务下载 bootstrap 和 jQuery,并将它们的源代码放入 bower_components 并将它们的未压缩版本放入 lib。
如果 bower 任务失败并显示 - git ls-remote exit code of #128 - 那么您可能遇到阻止 git 协议的防火墙问题。解决方法是将协议更改为https:
git config --global url."https://".insteadOf git://
咕噜声
Grunt 是 JavaScript 的任务运行器。
我们将使用它来自动运行构建 Twitter Bootstrap 所涉及的命令。
Grunt 任务只是带有 JSON 中声明的参数的命令运行器。
grunt 任务是这样排序的:
- Bootstrap LESS 文件转换为 CSS
- CSS 为自定义浏览器列表添加了供应商特定的前缀
- 通过检查使用 CSS 文件的页面删除未使用的 CSS 类
- 处理 HTML 以包含优化的 CSS 而不是原来的
- 优化后的 CSS 进一步缩小
- 监视可编辑的 .less 文件并再次运行整个管道。
- 浏览器会在检测到构建更改时自动刷新。
- Twitter Bootstrap JavaScript 组件可以切换进出。
- Twitter Bootstrap JavaScript 使用 uglify 缩小。
少
Less 是一个 CSS 预处理器,这意味着它扩展了 CSS 语言,添加了允许变量、mixin、函数和许多其他技术的功能,使您可以使 CSS 更易于维护、主题化和可扩展。
lesscss.org.
自定义
主题由三个 LESS 文件组成。
* src/theme/bootstrap.less - 要包含的其他文件列表
如果你知道有你绝对不使用的引导组件,
您可以在src/theme/bootstrap.less 中将它们注释掉
如果您破坏了某些内容,请不要担心,重新评论它是微不足道的。
* src/theme/variables.less,默认包含在 Bootstrap 中,
允许您自定义these settings。
* src/theme/structural.less 引入了更广泛的结构变化。
这些是唯一少被观看的文件。
优化
连接并自动重新加载
连接网络服务器进程将提供 public/index.html 文件
localhost:8082 并观察我们的文件是否有变化。
在检测到更改时,索引页面将自动在浏览器中重新加载。
版权和许可
版权所有 (c) 2014 "mhanney" Michael Hanney
特此免费授予任何人许可
获取此软件和相关文档的副本
文件(“软件”),在没有处理软件的情况下
限制,包括但不限于使用权,
复制、修改、合并、发布、分发、再许可和/或出售
软件的副本,并允许获得该软件的人
提供软件以执行此操作,但须遵守以下规定
条件:
以上版权声明及本许可声明为
包含在软件的所有副本或大部分内容中。
软件按“原样”提供,不提供任何形式的保证,
明示或暗示,包括但不限于保证
适销性、适合特定用途和
非侵权。在任何情况下,作者或版权均不得
持有人应对任何索赔、损害或其他责任负责,
无论是在合同、侵权或其他方面产生的
来自、脱离或与本软件或使用或
软件中的其他交易。