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>