首页 > 代码库 > Yeoman+Bower+gulp web前端自动化工作流程(初级教程)
Yeoman+Bower+gulp web前端自动化工作流程(初级教程)
Yeoman包括了三个部分yo(脚手架工具)、grunt/gulp(构建工具)、bower(包管理器).听说gulp更容易上手,所以我就没用grunt而选的gulp
什么是开发流程?
在我看来一个完整的开发流程应该包括:
- 本地开发环境的初始化
- 第三方依赖的管理
- 源文件编译
- 自动化测试
- 发布到pipeline和各个环境
而现代的开发流程,就是要使上面的各个部分都可以自动化,一个命令就可以使这些流程都自动走完,并且快速的得到错误或通过的反馈,让我们可以方便快速的修复错误和release。
工具简单介绍
脚手架工具(Yeoman官网http://yeoman.io/)
脚手架是帮你减少「为减少重复性工作而做的重复性工作」的工具,这就像假如你要开始一个新的小项目,原来你可能至少需要手动创建个js、css、images三个文件夹吧?而脚手架工具就是帮你每次新建项目时候执行这个动作流程部分的工具,也就是说以后就需要一行命令而已,不用自己再去手动创建文件夹及结构了。
可是呢,你有可能又要想了,那我每个项目的目录结构不可能都要一吧,那应该怎么办呢?那就需要根据不同的项目安装不同的 generator了(http://yeoman.io/generators/),当然你也可以自定义个generator,然后公开给别人使用,现在我们只是学怎么使用别人的generator,我也不会自定义呢,这个自定义的问题请大家自行百度教程吧!哈哈
构建工具(gulp中文网地址http://www.gulpjs.com.cn/)
gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器,简单的说就是你把一套什么压缩,合并这些规则提前写好,等你写完代码,只需一行命令就可以生成上线的代码,然后直接上传到服务器就好了,当然你也可以有更高级的使用,后续自行研究。
包管理工具(bower官网地址https://bower.io/)
Bower是一个客户端技术的软件包管理器,它可用于搜索、安装和卸载如JavaScript、HTML、CSS之类的网络资源。比如:jQuery、Bootstrap
具体安装流程
准备工作你全局应该已经安装nodejs、npm了吧?没安装的请自行百度安装,下载安装包->双击->一路next->安装完毕
全局安装Yeoman、gulp、bower
npm install yo gulp-cli bower -g
如果执行完这个命令还没有报错,那么恭喜你,基本环境就已经搭建完毕了,剩下就是需要根据你的项目,然后按照generator了,暂时先创建个webapp项目为例,所以我们先安装个generator-webapp
npm install -g generator-webapp
安装完generator-webapp后,在项目目录下直接
yo webapp
即可创建一个webapp项目。至此就搭建好了,然后就可以安静的写代码的。
附:(gulpfile.js配置文件及index.html个人理解)
index.html
<!-- build:css styles/main.css --> <link rel="stylesheet" href="http://www.mamicode.com/styles/main.css"> <!-- 这个link是你开发时候页面引用的css路径,上面的build:css 是build时候存放的路径和名字,路径和名字都可以自定义,下面的js原理一样 --> <!-- endbuild -->
gulpfile.js
/*//导入工具包 require(‘node_modules里对应模块‘)var gulp = require(‘gulp‘), //本地安装gulp所用到的地方 less = require(‘gulp-less‘); //定义一个testLess任务(自定义任务名称)gulp.task(‘testLess‘, function () { gulp.src(‘src/less/index.less‘) //该任务针对的文件 .pipe(less()) //该任务调用的模块 .pipe(gulp.dest(‘src/css‘)); //将会在src/css下生成index.css}); gulp.task(‘default‘,[‘testLess‘, ‘elseTask‘]); //定义默认任务 elseTask为其他任务,该示例没有定义elseTask任务 //gulp.task(name[, deps], fn) 定义任务 name:任务名称 deps:依赖任务名称 fn:回调函数//gulp.src(globs[, options]) 执行任务处理的文件 globs:处理的文件路径(字符串或者字符串数组) //gulp.dest(path[, options]) 处理完后文件生成路径 */// generated on 2016-08-26 using generator-webapp 2.1.0const gulp = require(‘gulp‘);//可以批量require package.json中的devDependencies插件,不必一个一个导入了const gulpLoadPlugins = require(‘gulp-load-plugins‘);// 浏览器自动刷新插件const browserSync = require(‘browser-sync‘);//删除文件和文件夹const del = require(‘del‘);//wiredep解决了bower前端库引入进html的问题const wiredep = require(‘wiredep‘).stream;// 本地开发代理跨域请求用的插件const proxyMiddleware = require(‘http-proxy-middleware‘);const $ = gulpLoadPlugins();const reload = browserSync.reload;// 定义proxy规则,供下面创建服务使用,以/service/开头的代理到target域名下const proxy= proxyMiddleware([‘/service/‘],{target:‘http://xxx.xxxx.com‘,changeOrigin: true});// 编译sassgulp.task(‘styles‘, () => { return gulp.src(‘app/styles/*.scss‘) //plumber 是一个错误处理插件,当出现错误时,不会立即卡主,而是进入 plumber,防止程序运行终止。plumber可以阻止 gulp 插件发生错误导致进程退出并输出错误日志。 .pipe($.plumber()) //sourcemaps 是用来生成映射文件的一个插件,map 文件记录了从 Sass 编译成 CSS 的过程中,每一行的 Sass 代码对应哪一行的 CSS 代码。在scss编译过程中,添加映射关系,可以方便调试; //在文件流中需要两条语句: //***plugins.sourcemaps.init() //如果要输出sourcemaps文件的话,可以在write(path)添加路径; //***plugins.sourcemaps.write() .pipe($.sourcemaps.init()) //sass 是核心的编译 Sass 的插件,指定了输出格式 expanded,precision 指定了当输出十进制数字时,使用多少位的精度,然后指定了路径和错误日志。 //嵌套输出方式 nested // 展开输出方式 expanded // 紧凑输出方式 compact // 压缩输出方式 compressed .pipe($.sass.sync({ outputStyle: ‘expanded‘, precision: 10, includePaths: [‘.‘] }).on(‘error‘, $.sass.logError)) //添加浏览器前缀 .pipe($.autoprefixer({browsers: [‘> 1%‘, ‘last 2 versions‘, ‘Firefox ESR‘]})) .pipe($.sourcemaps.write()) // .tmp 临时目录,后面还会有一个目录是 dist 目录,试想一下,如果我们编译了 BootStrap 而在 HTML 中没有引用,那编译来还有必要吗?所以说,.tmp 作为临时目录,它可以存放被编译后的文件,但是不一定会被引用。被真正引用的文件才是真正有用的文件,我们将它放到 dist 目录。所以接下来的 HTML 处理就是检查一下有哪些 CSS 和 JS 被引用了,可以将它们合并,然后将新的文件放到 dist 并更新它的引用路径。 .pipe(gulp.dest(‘.tmp/styles‘)) .pipe(reload({stream: true}));});// 编译jsgulp.task(‘scripts‘, () => { return gulp.src(‘app/scripts/**/*.js‘) .pipe($.plumber()) .pipe($.sourcemaps.init()) .pipe($.babel()) .pipe($.sourcemaps.write(‘.‘)) .pipe(gulp.dest(‘.tmp/scripts‘)) .pipe(reload({stream: true}));});function lint(files, options) { return gulp.src(files) .pipe(reload({stream: true, once: true})) .pipe($.eslint(options)) .pipe($.eslint.format()) .pipe($.if(!browserSync.active, $.eslint.failAfterError()));}gulp.task(‘lint‘, () => { return lint(‘app/scripts/**/*.js‘, { fix: true }) .pipe(gulp.dest(‘app/scripts‘));});gulp.task(‘lint:test‘, () => { return lint(‘test/spec/**/*.js‘, { fix: true, env: { mocha: true } }) .pipe(gulp.dest(‘test/spec/**/*.js‘));});// 编译htmlgulp.task(‘html‘, [‘styles‘, ‘scripts‘], () => { return gulp.src(‘app/*.html‘) //useref 这个插件,它可以检测 HTML 中引用的 CSS 和 JS,可以执行合并和压缩,然后更新新的路径。 .pipe($.useref({searchPath: [‘.tmp‘, ‘app‘, ‘.‘]})) .pipe($.if(‘*.js‘, $.uglify())) .pipe($.if(‘*.css‘, $.cssnano({safe: true, autoprefixer: false}))) // options = { // removeComments: true, //清除HTML注释 // collapseWhitespace: true, //压缩HTML // collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input checked /> // removeEmptyAttributes: true, //删除所有空格作属性值 <input id="" /> ==> <input /> // removeScriptTypeAttributes: true, //删除<script>的type="text/javascript" // removeStyleLinkTypeAttributes: true, //删除<style>和<link>的type="text/css" // minifyJS: true, //压缩页面JS // minifyCSS: true //压缩页面CSS // }; .pipe($.if(‘*.html‘, $.htmlmin({collapseWhitespace: true}))) .pipe(gulp.dest(‘dist‘));});gulp.task(‘images‘, () => { return gulp.src(‘app/images/**/*‘) //只压缩修改的图片,没压缩的从缓存文件读取 //optimizationLevel: 5, //类型:Number 默认:3 取值范围:0-7(优化等级) // progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片 // interlaced: true, //类型:Boolean 默认:false 隔行扫描gif进行渲染 // multipass: true //类型:Boolean 默认:false 多次优化svg直到完全优化 // svgoPlugins: [{removeViewBox: false}],//不要移除svg的viewbox属性 // use: [pngquant()] //使用pngquant深度压缩png图片的imagemin插件 .pipe($.cache($.imagemin({ progressive: true, interlaced: true, // don‘t remove IDs from SVGs, they are often used // as hooks for embedding and styling svgoPlugins: [{cleanupIDs: false}] }))) .pipe(gulp.dest(‘dist/images‘));});gulp.task(‘fonts‘, () => { return gulp.src(require(‘main-bower-files‘)(‘**/*.{eot,svg,ttf,woff,woff2}‘, function (err) {}) .concat(‘app/fonts/**/*‘)) .pipe(gulp.dest(‘.tmp/fonts‘)) .pipe(gulp.dest(‘dist/fonts‘));});gulp.task(‘extras‘, () => { return gulp.src([ ‘app/*.*‘, ‘!app/*.html‘ ], { dot: true }).pipe(gulp.dest(‘dist‘));});gulp.task(‘clean‘, del.bind(null, [‘.tmp‘, ‘dist‘]));gulp.task(‘serve‘, [‘styles‘, ‘scripts‘, ‘fonts‘], () => { browserSync({ notify: false, port: 9000, server: { baseDir: [‘.tmp‘, ‘app‘], routes: { ‘/bower_components‘: ‘bower_components‘ }, //这是代理跨域,规则上面已经定义了 middleware: [proxy] } }); gulp.watch([ ‘app/*.html‘, ‘app/images/**/*‘, ‘.tmp/fonts/**/*‘ ]).on(‘change‘, reload); gulp.watch(‘app/styles/**/*.scss‘, [‘styles‘]); gulp.watch(‘app/scripts/**/*.js‘, [‘scripts‘]); gulp.watch(‘app/fonts/**/*‘, [‘fonts‘]); gulp.watch(‘bower.json‘, [‘wiredep‘, ‘fonts‘]);});gulp.task(‘serve:dist‘, () => { browserSync({ notify: false, port: 9000, server: { baseDir: [‘dist‘] } });});gulp.task(‘serve:test‘, [‘scripts‘], () => { browserSync({ notify: false, port: 9000, ui: false, server: { baseDir: ‘test‘, routes: { ‘/scripts‘: ‘.tmp/scripts‘, ‘/bower_components‘: ‘bower_components‘ } } }); gulp.watch(‘app/scripts/**/*.js‘, [‘scripts‘]); gulp.watch(‘test/spec/**/*.js‘).on(‘change‘, reload); gulp.watch(‘test/spec/**/*.js‘, [‘lint:test‘]);});// inject bower componentsgulp.task(‘wiredep‘, () => { gulp.src(‘app/styles/*.scss‘) .pipe(wiredep({ ignorePath: /^(\.\.\/)+/ })) .pipe(gulp.dest(‘app/styles‘)); gulp.src(‘app/*.html‘) .pipe(wiredep({ exclude: [‘bootstrap-sass‘], ignorePath: /^(\.\.\/)*\.\./ })) .pipe(gulp.dest(‘app‘));});//执行build之前先执行数组列表里的任务gulp.task(‘build‘, [‘lint‘, ‘html‘, ‘images‘, ‘fonts‘, ‘extras‘], () => { return gulp.src(‘dist/**/*‘).pipe($.size({title: ‘build‘, gzip: true}));});//gulp==gulp default 默认执行的任务gulp.task(‘default‘, [‘clean‘], () => { gulp.start(‘build‘);});
Yeoman+Bower+gulp web前端自动化工作流程(初级教程)