基於 Express+Gulp+BrowserSync 搭建一套高性能的前端開發環境

原文發佈於個人博客 www.kisnows.comjavascript

爲何要搭這麼一套開發環境

公司 Pc 端之前遺留的項目,都是基於 jekyll+ruby-sass 這一套比較老的技術搭建的。不過 jekyll 的模版繼承加上 sass 強大預處理能力,同時配合 Grunt 作任務管理,一切仍是很駕輕就熟的。css

然而隨着項目規模的急劇增大,這一套東西的速度是在是太慢了,一至於後來一旦這個項目有須要求要改我就頭疼,倒不是說頭疼需求怎麼改,而是你隨便改一個文件,從 jekyll 檢測到改動到編譯完 sass 到瀏覽器自動刷新,基本須要 40+ s,這徹底不能接受。html

因而一直就打算新搭一套開發環境,恰好前段時間有個新項目,我就拿來操刀動手了,目前已經用它作了倆個項目下來,在原有基礎上作了一些修改以後,已經徹底能夠替代原來那一套東西了。java

性能

得益於 node-sass 對 ruby-sass 在編譯速度上的碾壓以及被 jade 完爆的 jekyll。
如今項目中從文件變更=>編譯完成=>瀏覽器自動刷新,整個過程在 1s 左右,能夠說速度提高了幾十倍。
並且整個過程都是全自動的,不管你修改了 js,css 仍是 html 文件,瀏覽器都會在從新編譯完成後自動刷新。node

編譯時間

文件結構

這是項目開發時的文件結構:git

文件結構

其中 public,router,views 都是 express 默認的文件結構,這裏就不作介紹了。
submodule 是咱們不一樣項目中公用的代碼庫,這樣能夠少寫不少公共代碼。github

技術細節

整個項目的技術棧是 jade+node-sass+js,實現全自動開發。
首先在 views 下面,定義好 jade 模版文件,而後開始新加頁面chrome

關係依賴

這個環境,主要基於 Express,Gulp 以及一系列的 Gulp 插件搭建,這個是開發環境的依賴關係:express

{
  "dependencies": {
    "body-parser": "~1.13.2",
    "cookie-parser": "~1.3.5",
    "debug": "~2.2.0",
    "express": "~4.13.1",
    "jade": "~1.11.0",
    "morgan": "~1.6.1",
    "serve-favicon": "~2.3.0"
  },
  "devDependencies": {
    "browser-sync": "^2.9.1",
    "del": "^2.0.2",
    "gulp": "^3.9.0",
    "gulp-autoprefixer": "^3.0.1",
    "gulp-jade": "^1.1.0",
    "gulp-nodemon": "^2.0.4",
    "gulp-sass": "^2.0.4",
    "gulp-sourcemaps": "^1.5.2",
    "jade": "^1.11.0"
  }
}

其中,express 主要用來渲染 jade 模版引擎以及提供路由功能,同時起一個本地的服務器。有人說,渲染 jade 模版,我直接用 gulp 也能夠啊。是的,gulp 是能夠直接渲染 jade 模版,可是有一個問題,就是若是你本地只修改了一個 jade 文件時候,gulp 也須要把全部的 jade 文件所有渲染一遍,這明顯會浪費不少時間。gulp

因此我引入了 express,只會按需渲染 jade,節省了不少時間。同時一個高效的開發環境必須有瀏覽器自動刷新,可是 express 沒有這個功能。因此我引入了 browser-sync,用它來代理 express 啓動的本地服務,而後來監測本地文件的變更,來作到瀏覽器自動刷新。

有同窗可能會問了,若是我改了 express 相應的 js 文件怎麼辦,或者 jade 文件編譯出錯了怎麼辦。由於這些時候 express 服務器都會 crash 掉,須要重啓。因此我引入了 nodemon,來作 express 服務器的自動重啓功能。

在這個環境下面,我在開發中所能碰到的全部 jade,scss,js 文件的改動都會被檢測到並做出相應的動做,並且速度還很是快。讓我能夠花更多時間在業務代碼的開發上,而不是不斷重啓服務和刷新瀏覽器。

最後

這個東西被我放到 Github上了,地址在這。

同時附上 gulpfile 全文,以供參考:

'use strict';

var gulp = require('gulp');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var sass = require('gulp-sass');
var prefix = require('gulp-autoprefixer');
var nodemon = require('gulp-nodemon');
var sourcemaps = require('gulp-sourcemaps');
var jade = require('gulp-jade');
var stylus = require('gulp-stylus');
var rename = require('gulp-rename');
var del = require('del');

//dev task start
//DONE can not compile the sass or less file
gulp.task('sass', function () {
  return gulp.src(['./sass/personal.scss'])
    .pipe(sourcemaps.init())
    .pipe(sass({errLogToConsole: true})
      .on('error', sass.logError))
    .pipe(prefix('last 2 versions', '> 1%', 'ie 8', 'Android 2'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('./public/css'))
    .pipe(reload({stream: true}));
});

gulp.task('browser-sync', ['nodemon'], function () {
  browserSync.init(null, {
    proxy: 'http://localhost:3000',
    files: ['public/**/*.*', 'views/**/*.*', 'submodule/**/*.*'],
    browser: 'google chrome',
    notify: false,
    port: 5000
  });
});

gulp.task('movesub', function () {
  return gulp.src(['./submodule/images/**/*.*'], {base: './submodule'})
    .pipe(gulp.dest('./public'))
});

gulp.task('stylus', function () {
  return gulp.src('submodule/stylus/public.styl')
    .pipe(stylus())
    .pipe(rename({
      extname: '.scss'
    }))
    .pipe(gulp.dest('submodule/stylus/'))
});

gulp.task('nodemon', function (cb) {
  del(['./public/*.html']);

  var called = false;

  return nodemon({
    script: 'bin/www'
  }).on('start', function () {
    if (!called) {
      cb();
      called = true;
    }
  });
});
//dev task end

gulp.task('clean', function (cb) {
  del(['./dist/*'], cb)
});

gulp.task('copy', function () {
  return gulp.src([
    'public/css/**/*',
    'public/images/**/*',
    'public/js/**/*',
    'public/pageScripts/**/*'
  ], {base: './public'})
  .pipe(gulp.dest('./dist'))
});

//build task start
//DONE add build task
gulp.task('jade', function () {
  return gulp.src(['views/**/*.jade', '!views/layout/**/*.jade', '!views/includes/**/*.jade'])
    .pipe(jade({pretty: true}))
    .pipe(gulp.dest('./dist'));
});
//build task end

gulp.task('dist', ['clean', 'copy', 'jade']);

gulp.task('default', ['browser-sync', 'sass', 'movesub'], function () {
  gulp.watch(['sass/**/*.*', '.submodule/stylus/**/*.*'], ['sass']);
});
相關文章
相關標籤/搜索