抹桥的博客
Language
Home
Archive
About
GitHub
Language
主题色
250
821 words
4 minutes
# Building a High-Performance Frontend Development Environment with Express, Gulp, and BrowserSync

Why I Built This Framework#

Our company’s legacy PC projects were all built on an older tech stack: jekyll + ruby-sass. However, jekyll’s template inheritance, coupled with sass’s powerful pre-processing capabilities and Grunt for task management, made everything quite manageable.

However, as project scales rapidly increased, this stack became incredibly slow. It got to the point where I’d dread making any changes to these projects. My headache wasn’t about figuring out how to implement a feature, but rather the fact that a simple file modification—from jekyll detecting the change, to sass compiling, to the browser automatically refreshing—would typically take over 40 seconds. This was completely unacceptable.

So, I had always intended to set up a new development environment. Coincidentally, a new project came up recently, which I took as an opportunity to build it. I’ve now used it for two projects, and after making some modifications based on the initial setup, it can completely replace the old stack.

Performance#

Thanks to node-sass’s overwhelming compilation speed advantage over ruby-sass, and jekyll being completely outclassed by jade, the entire process in the current project—from file change to compilation complete to browser auto-refresh—now takes about 1 second. This is a speed improvement of dozens of times. Furthermore, the entire process is fully automated. Whether you modify JavaScript, CSS, or HTML files, the browser will automatically refresh once recompilation is complete. Compilation Time

File Structure#

Here’s the file structure during project development:

File Structure

public, router, and views are all default Express file structures, so I won’t go into detail about them here. submodule is our shared code library across different projects, which helps us reduce redundant common code.

Technical Details#

The entire project’s tech stack is jade + node-sass + JavaScript, enabling fully automated development. First, define the jade template files under views, then begin adding new pages.

Dependencies#

This environment is primarily built upon Express, Gulp, and a series of Gulp plugins. Here’s the dependency relationship for the development environment:

{
  "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"
  }
}

Among these, Express is primarily used to render the jade template engine and provide routing functionality, while also running a local server. Some might say, “I can render jade templates directly with Gulp.” Yes, Gulp can indeed render jade templates directly, but there’s a problem: if you only modify a single jade file locally, Gulp still needs to render all jade files, which clearly wastes a lot of time.

That’s why I introduced Express, which only renders jade on demand, saving a lot of time. Furthermore, an efficient development environment must have browser auto-refresh, but Express doesn’t offer this feature. So, I integrated browser-sync to proxy the local service started by Express, monitor local file changes, and achieve automatic browser refreshing.

Some might ask, “What if I modify an Express-related JavaScript file, or what if a jade file compilation fails?” In such scenarios, the Express server would crash and require a restart. That’s why I integrated nodemon to provide automatic restart functionality for the Express server.

Within this environment, all changes I make to jade, scss, and JavaScript files during development are detected, and appropriate actions are taken, all at a very high speed. This allows me to spend more time on developing business logic, rather than constantly restarting services and refreshing the browser.

Finally#

This project has been uploaded to GitHub. You can find it here.

Additionally, the full gulpfile is attached for your reference:

"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"]);
});

This article was published on November 3, 2015 and last updated on November 3, 2015, 3625 days ago. The content may be outdated.

# Building a High-Performance Frontend Development Environment with Express, Gulp, and BrowserSync
https://blog.kisnows.com/en-US/2015/11/03/dev-environment-express-gulp-browsersync/
Author
Kisnows
Published at
2015-11-03
License
CC BY-NC-ND 4.0