Gulp&Webpack搭建属于自己的特性化前端脚手架

1,436 阅读6分钟

前言

之前看了很多教程,比如vue的脚手架,gulp的脚手架和大牛自己写的脚手架。发现没有什么是银弹能够满足公司业务的需要又能够开发的爽。所以结合公司业务的特性,自己动手用gulp和webpack写了一套适合我司的脚手架。

使用范围

  • 专题等静态页面多
  • 后台主导逻辑业务,前端主导交互等
  • 需要整合es6,scss,eslint等

上面只列出了三点。其实适用范围还有挺多。

技术栈

  • webpack^3
  • gulp-webpack^1.5
  • gulp-pug^3.3
  • gulp^3.9
  • eslint^4
  • eslint-config-alloy^1.4
  • babel^6.5
  • gulp-postcss^6.2
  • autoprefixer^6.4
  • browser-sync^2.1
  • gulp-sass^2.3

当然还有一些就不一一列出了,这里只列出几个主要的模块 下面来解释一下上述模块对应的功能。s

webpack

我个人是比较讨厌webpack做除了js以外的事情,包括打包css,copy等。 我不否认webpack的强大,但是我还是更希望术业有专攻,webpack只负责js相关的事情就好。 所以webpack负责的范围为 es6->es5,eslint的检测,js的sourcemap文件的生成。

babel

这个没什么好说的,babel也提供了cli的形式,但这里整合进了webpack当中。

gulp-sourcemaps&gulp-sass&gulp-postcss&autoprefixer

这些都是用来处理sass的相关,比如 sass的编译,css的浏览器兼容性处理,css的sourcemap对应。

browser-sync

webpack也提供了静态服务器并且支持增量刷新,但是这里我选择使用browser-sync提供了静态服务器。 browser-sync可以快速的搭建起一个静态服务器并且支持api级别的接口刷新。所以我可以很方便的去自动或者手动调用刷新页面功能

eslint&eslint-config-alloy

这里使用的是腾讯alloy团队的eslint检测配置,个人比较喜欢此方案。

gulp

最后说说gulp,所有的模块都是由gulp来主导并且粘合的。为什么这么说。 因为所有的模块都不知道啥时候去调用啥时候去做该做的事情,那么这里就用到了gulp.watch和gulp.src。 简单来个例子

/**
 *
 * 监听文件
 *
 */
gulp.task("watch", () => {
    let bs = browserSync.init({
        server: {
            baseDir: "./",
            directory: true,
        },
        open: false,
        ui: {
            port: 3008,
            weinre: {
                port: 3009
            }
        },
        port: devConf.gulp.webPort?devConf.gulp.webPort:3012,
        ghostMode: false
    });
    gulp.watch(["dev/jade-html/**/*.pug", "dev/jade-component/**/*.pug"], ["compile:pug"]);
    gulp.watch(["dev/js/**/*.js"], ["compile:js"]);
    gulp.watch(["dev/sass/**/*.scss"], ["compile:sass"]);
    gulp.watch(["dev/img/**/*.*", "dev/lib/**/*.*"], ["copy"]);
});

比较简单,大意就是监听某些文件变动的时候执行对应的task。

使用问题

  1. gulp-webpack编译每次都是重新编译

gulp-webpack每次调用webpack都需要重新编译,所以时间会很长很长很长。其实还好 个人测试中,webpack增量编译时间为1s的话,gulp-webpack编译时间为2s-3s左右。如果对时间不是特别敏感的可以忽略。 当然这个问题还是有解决方案的,也是之前看的某篇文章提出的 使用proxy机制, webpack的增量编译刷新调动browserSync的刷新。 这样一来可以做到同步刷新 并且不适用gulp-webpack来重新编译转而使用webpack的增量编译和静态服务器功能。当然具体怎么实现还没有研究 但是可以得出的结论就是 不难。

  1. pug,sass文件过多每次都编译出额外的文件

这个什么意思呢?看代码说话

gulp.task("compile:sass", (event) => {
    return gulp.src('dev/sass/output/**/*.scss')
    //.pipe(changed("dist/css")) disabled changed sass需要全部编译..
        .pipe(sourcemaps.init())
        .pipe(sass(sassOptions).on("error", sass.logError))
        .pipe(gulpif(extBase64File,base64({
            maxImageSize: 15*1024, // bytes 15KB
        })))
        .pipe(postcss([autoprefixer()]))
        .pipe(sourcemaps.write("./"))
        .pipe(gulp.dest("dist/css"))
        .pipe(gulp.dest(copyCssPath))
        .on("finish", () => {
            browserReload()
        })
});

这里如果编译的话 每次都会把output下面所有的scss文件编译出来,同理 编译pug的时候也是一样会把某个文件夹下面的所有pug全部编译出来。当然解决办法也很简单

配置文件,没错,再使用额外的配置文件,比如

const devConf=require("./dev-conf");
let compilePugFile= devConf.gulp.compilePugFile?devConf.gulp.compilePugFile:["dev/jade-html/**/*.pug"];
let compileSassFile= devConf.gulp.compileSassFile?devConf.gulp.compileSassFile:["dev/sass/output/**/*.scss"];
let copyJsPath=devConf.gulp.copyJsPath?devConf.gulp.copyJsPath:`/public/dist/js`;
let copyCssPath=devConf.gulp.copyCssPath?devConf.gulp.copyCssPath:`/public/dist/css`;
let copyImgPath=devConf.gulp.copyImgPath?devConf.gulp.copyImgPath:`/public/img`;

使用额外的配置文件来做到按需编译和加载

  1. prod和dev如何区分。

这个其实很简单 可以使用npm run 也可以使用gulp的多个任务方式

gulp.task('default::production',['set-env::production','watch']);

/**
 * 设置环境变量
 */
gulp.task('set-env::production',()=>{
    webpackEnv='production';
    return Promise.resolve(true);
});

需要dev模式那么直接 gulp就好 需要生产模式就 gulp default::production 这里的设置变量可以通过process.env来设置,这样就可以在全局拿到参数了。

    process.env.NODE_ENV = 'dev'||'prod';
  1. 报错退出机制

gulp的每次报错都会导致gulp退出,比如调用gulp-webpack编译js的时候 某个js错误导致整个gulp退出, 当然解决办法也很简单

const plumber = require('gulp-plumber');
const webpackFile=require("./webpack.config.js");
gulp.task("compile:js", () => {
    return gulp.src("dev/js/main.js")
        .pipe(plumber({errorHandler:function () {
                
            }}))
        .pipe(webpack(webpackFile({env:webpackEnv})))
        .pipe(gulp.dest("dist/js/"))
        .pipe(gulp.dest(copyJsPath))
        .on("finish", () => {
            browserReload()
        })
});

使用gulp-plumber便可以解决问题了, 因为webpack自带错误提示功能 所以不需要gulp-plumber再次报错, 这里就把errorHandler给定义了空函数不做处理。

改进

基本上说到这里整个方案就差不多了。 这一套方案的好处就是 html,css,js分离并且互不影响。 html,css,js都使用了代替品来方便开发 比如html使用了pug(说pug不好的同学,你们有嵌套过10层以上的html吗?),css使用了sass,js使用了es6,并且三者可以快速替换和代替 比如 我哪天不想使用pug了 那么可以快速使用ejs等其他模板 只需要改改gulp的task, 哪天我想使用vue了 那么只需要改改webpack的配置文件,并且增加gulp.watch范围就可以了。 是不是听着很强大?难道就没什么弊端么?

答案是....没有...哈哈哈哈 有点狂。 不过其实还是有很多问题的,比如webpack的性能,在文件量大的话 其实会编译的比较久,会影响开发效率,这个问题在后期是不可忽视的。 耦合过大也是个问题,所有的编译都是依靠文件目录来做分配的,如果某个目录变了那么很多东西都需要修改重来。 等等... 不过后面再慢慢改进吧,毕竟从一开始自己搭建脚手架到现在公司中稳定使用也经历了重构—>改版->抽离->放弃(???什么鬼)。

这里给出一个vue2.0的demo