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="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.0
const 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});
// 编译sass
gulp.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}));
});

// 编译js
gulp.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'));
});

// 编译html
gulp.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($.replace(/\"images\//g, '"statics/images/'))
    .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 components
gulp.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');
});

  

package.json文件

{
  "private": true,
  "engines": {
    "node": ">=4"
  },
  "dependencies": {
    "bootstrap": "^4.3.1",
    "jquery": "^3.4.1",
    "modernizr": "^3.7.1",
    "popper.js": "^1.15.0"
  },
  "devDependencies": {
    "@babel/core": "^7.4.5",
    "@babel/preset-env": "^7.4.5",
    "autoprefixer": "^9.5.1",
    "browser-sync": "^2.26.5",
    "chai": "^4.2.0",
    "cross-env": "^5.2.0",
    "cssnano": "^4.1.10",
    "del": "^4.1.1",
    "gulp": "^4.0.2",
    "gulp-babel": "^8.0.0",
    "gulp-cli": "^2.2.0",
    "gulp-eslint": "^5.0.0",
    "gulp-filter": "^6.0.0",
    "gulp-htmlmin": "^5.0.1",
    "gulp-if": "^2.0.2",
    "gulp-imagemin": "^6.0.0",
    "gulp-load-plugins": "^1.6.0",
    "gulp-plumber": "^1.2.1",
    "gulp-postcss": "^8.0.0",
    "gulp-sass": "^4.0.2",
    "gulp-size": "^3.0.0",
    "gulp-sourcemaps": "^2.6.5",
    "gulp-uglify": "^3.0.2",
    "gulp-useref": "^3.1.6",
    "mkdirp": "^0.5.1",
    "mocha": "^6.2.2",
    "yargs": "13.2.4"
  },
  "scripts": {
    "serve:test": "cross-env NODE_ENV=test gulp serve", //执行gulp的单元测试任务命令
    "serve:dist": "cross-env NODE_ENV=production gulp serve",  //执行build命令,打包文件,并针对打包后的dist目录启动本地服务器命令,方便预览
    "start": "gulp serve", //执行开发服务器命令
    "build": "cross-env NODE_ENV=production gulp",  //执行的build命令,打包文件
    "test": "npm run serve:test",  //间接执行gulp的单元测试任务命令,同第一条命令效果
    "tasks": "gulp --tasks" //查看gulpfile.js导出的可执行gulp任务命令
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "Firefox ESR"
  ],
  "eslintConfig": {
    "parserOptions": {
      "sourceType": "module"
    },
    "env": {
      "es6": true,
      "node": true,
      "browser": true,
      "jquery": true
    },
    "rules": {
      "quotes": [
        2,
        "single"
      ]
    }
  }
}

 index.html

<!doctype html>
<html class="no-js" lang="">
  <head>
    <meta charset="utf-8">
    <meta name="description" content="">

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>gulp2</title>
    <link rel="apple-touch-icon" href="apple-touch-icon.png">
    <!-- Place favicon.ico in the root directory -->
    <!-- build:css styles/vendor.css -->
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css" type="text/css" />
    <!-- endbuild -->
    <!-- build:css styles/main.css -->
    <link rel="stylesheet" href="styles/main.css">
    <!-- endbuild -->
    <!-- build:js scripts/modernizr.js -->
    <script src="scripts/modernizr.js"></script>
    <!-- endbuild -->
  </head>
  <body>
    <!--[if IE]>
      <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
    <![endif]-->
    <div class="container">
      <div class="header">
        <ul class="nav nav-pills float-right">
          <li class="nav-item">
            <a href="#" class="nav-link active">Home</a>
          </li>
          <li>
            <a href="#" class="nav-link">About</a>
          </li>
          <li>
            <a href="#" class="nav-link">Contact</a>
          </li>
        </ul>
        <h3 class="text-muted">gulp2</h3>
      </div>

      <div class="jumbotron">
        <h1 class="display-3">'Allo, 'Allo!</h1>
        <p class="lead">Always a pleasure scaffolding your apps.</p>
        <p><a class="btn btn-lg btn-success" href="#">Splendid!</a></p>
      </div>

      <div class="row marketing">
        <div class="col-lg-6">
          <h4>HTML5 Boilerplate</h4>
          <p>HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.</p>

          <h4>Sass</h4>
          <p>Sass is the most mature, stable, and powerful professional grade CSS extension language in the world.</p>

          <h4>Bootstrap</h4>
          <p>Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.</p>

          <h4>Modernizr</h4>
          <p>Modernizr is an open-source JavaScript library that helps you build the next generation of HTML5 and CSS3-powered websites.</p>
        </div>
      </div>

      <div class="footer">
        <p>♥ from the Yeoman team</p>
      </div>
    </div>
    <!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
    <script>
      (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
      function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
      e=o.createElement(i);r=o.getElementsByTagName(i)[0];
      e.src='https://www.google-analytics.com/analytics.js';
      r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
      ga('create','UA-XXXXX-X');ga('send','pageview');
    </script>
    <!-- build:js scripts/vendor.js -->  //这个会将下面两个文件合并成一个文件,并输出到这个路径,css和js同理
    <script type="text/javascript" src="/node_modules/jquery/dist/jquery.min.js"></script>//引入npm安装的jq插件
    <script type="text/javascript" src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>//引入npm安装的bootstrap模块
    <!-- endbuild -->
<!-- build:js scripts/main.js -->//gulp-useref插件使用,这个表示文件输出的路径,一定要用这种输出方式,如果不写的话不会构建出文件,还得手动去把开发文件复制到dist目录 <script src="scripts/main.js"></script> //开发时候引入文件的路径 <!-- endbuild --> </body> </html>

  

原文地址:https://www.cnblogs.com/jiaoshou/p/5910129.html