使用 gulp,webpack,es6,sass,react 等开发复杂单页面项目

自从开始放肆的使用es6之后就变得一发不可收拾,经手的很多项目也逐渐变成了基于react.js的单页面项目。中间走了不少弯路,尤其是在各种工具之间的选型和使用上,折腾大半年之后,稍稍记录一下这个过程中使用到的一些东西以及遇到的一些坑。

0. 初始化项目(npm相关)

开始之前先简单的介绍一下npm的相关知识,由于现代的项目依赖于各种工具插件,因此一个项目需要一个包管理工具,用来解决各种脚本,包的安装问题,npm作为Node.js的模块依赖管理工具,能很好的解决这些问题。

开始一个项目,首先要在项目目录里执行下面的命令

$ npm init

npm会问你一系列的问题,包括项目名称,版本描述等,可以详细填写,也可以直接一路回车。执行完成之后,会在当前目录下生成一个文件package.json,一个简单的例子如下:

{
  "name": "temp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "css-loader": "^0.25.0",
    "style-loader": "^0.13.1"
  },
  "devDependencies": {
    "dufing": "^1.0.13"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "fn": "echo \"123\""
  },
  "author": "",
  "license": "ISC"
}

其中name,version,description字段分别表示项目的名称、版本、描述等,main表示项目的主要入口文件。

dependenciesdevDependencies是我们关注的比较多的,他们分别描述了项目需要依赖的文件。如果我们在项目中安装了相应的依赖同时也希望这个依赖能被记录在package.json中,我们就可以执行如下的命令:

# css-loader 会被记录在 dependencies 中
$ npm install css-loader --save
# dufing 会被记录在 devDependencies 中
$ npm install dufing --save-dev

这样,别人拿到一个新的项目,只要有package.json在,运行

$ npm install

就会自动安装dependenciesdevDependencies中的依赖。

有人会问dependenciesdevDependencies会有什么区别呢?其实可以简单的理解成前者是项目运行必备的依赖,后者是开发需要依赖的插件。如果在运行npm install加上--production字段,系统就会只安装dependencies的依赖文件。

scripts字段中记录了各种脚本,通过npm run [name]就可以执行对应的脚本了,如上package.json执行npm run fn就会在控制台中输出123。scripts看起来简单,实际上如果运用的好的话甚至可以取代gulp等自动化工具,由于这个不是本次的重点,这里不详细说明。

1. gulp

1.1 grunt VS gulp VS npm script

提到前端自动化工具,耳熟能详的就是 grunt ( grunt官网 , grunt中文网 ) 和 gulp ( gulp官网 , gulp中文网 ) 了,它们在前端自动化任务处理方面都是十分优秀的。区别在于,grunt是前端工程化的先驱,偏配置化,可能对于其他语言转入的前端会比较容易上手。gulp属于后来居上型没有那么多的配置,采用了流的方式处理任务,性能更优秀。

至于到底是使用grunt还是gulp,就要根据自己的实际情况去选择了,但于我而言:

我是早期的grunt用户,现在的gulp用户,未来的npm script用户。

之所以提到 npm script 是因为 gulpgrunt 有一定的局限性,虽然大多数情况下你都能找到合适的插件,但是也可能某些情况下找不到你想要的插件(怎么办?除非自己写)以及可能出现的Debug等方面的困难。很多情况下你还得去安装gulp或者grunt,但是npm script就不一样了,大多数的项目都会有一个package.json,基于这个配置文件,我们可以很轻松的运行npm脚本,最重要的是,它的功能比我们想象的要强大的多,由于npm script不是本次讨论的重点,就不多说,放两个链接,感兴趣的童鞋可以查阅一下 [ 《也许你并不需要 Gulp, Grunt ?》 , 《使用npm scripts替代gulp》 ]。

1.2 gulp的基础配置

gulp主要是负责各种前端任务的自动执行,比如sass的自动编译、es6的bundle、react的编译、js的压缩,静态文件的版本控制等。

gulp的安装很简单,首先安装gulp-cli

$ npm install --global gulp-cli

然后,在项目目录里面安装gulp

$ npm install --save-dev gulp

在项目目录里创建gulp的配置文件gulpfile.js,具体的内容可以查看官网(或查看下面的实例中的配置)

最后在项目目录里运行gulp即可

$ gulp

下面以一个真实的项目为例做简要的说明(由于主要是说明整个开发流程,这里略去js压缩,静态文件的版块控制等步骤,着重描述一些比较重要的地方)。

项目目录如下:

├── dist
│   ├── css
│   ├── fonts
│   ├── images
│   ├── index.html
│   └── js
├── gulpfile.js
├── package.json
└── src
    ├── fonts   // 静态资源
    ├── images    // 静态资源
    ├── index.jsx   // react项目的主要入口
    ├── jsx    // react项目的其他功能模块
    ├── js  // 静态资源
    └── sass  // sass目录
  • /src为开发目录,里面有一些静态文件的目录(fonts,images,js等)
  • /src/jsx目录为项目所需的react.js项目文件
  • /src/sass目录为sass文件对应的目录
  • /src/index.jsx是这个项目的主要入口
  • /dist为项目的产出目录,所有文件经过处理之后都会放到这个目录里面,由于所有的文件资源都会被打包到这个目录,因此如果涉及到上线等操作的话,直接将这个文件上线即可。

实际上这个项目要做这么几件事:

  • 处理es6和react的编译
  • 处理sass的编译
  • 处理静态文件的复制(如果有任何变动从src中复制变动到dist中)

所以,这个项目主要有3个任务:

  • webpack任务使用gulp-webpack处理es6以及react的编译;
  • sass任务使用gulp-sass处理sass资源的编译;
  • static任务处理静态资源的拷贝等。

来看一下gulpfile.js的内容:

// 引入必要的资源
const gulp = require('gulp');
const webpack = require('gulp-webpack');  // gulp-webpack 用来处理 es6 以及 jsx
const sass = require('gulp-sass');   // gulp-sass 用来处理 sass -> css

// webpack任务,后面会详细介绍,这里略过
gulp.task('webpack', function () {
    return gulp.src('src/**/*.jsx')
        .pipe(webpack({
            watch: true,
            entry: {
                index: './src/index.jsx'
            },
            output: {
                filename: './js/[name].js',
                chunkFilename: './js/[name].chunk.js',
            },
            module: {
                loaders: [{
                    test: /.jsx?$/,
                    loader: 'babel-loader',
                    query: {
                        compact: false,
                        presets: ['es2015', 'react']
                    }
                }, {
                    test: /\.scss$/,
                    loaders: ["style", "css", "sass"]
                }]
            }
        }))
        .pipe(gulp.dest('dist/'));
});

/**
  * sass任务
  * 编译/src/sass目录下所有后缀为.scss的文件,并将编译好的文件放置到/dist/css目录下
  **/
gulp.task('sass', function () {
    return gulp.src('./src/sass/**/*.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(gulp.dest('./dist/css'));
});

/**
  * static任务
  * 主要负责将静态文件放置到/dist/*目录下
  **/
gulp.task('static', function () {
    gulp.src('./src/fonts/**/*.*')
        .pipe(gulp.dest('./dist/fonts'));
    gulp.src('./src/images/**/*.*')
        .pipe(gulp.dest('./dist/images'));
    gulp.src('./src/page/**/*.*')
        .pipe(gulp.dest('./dist'));
});

/**
  * 把 webpac sass static 任务打包,
  * 这样在执行gulp的时候,就会自动执行webpack,sass,static任务
  */
gulp.task('default', ['webpack', 'sass', 'static'], function () {});

// 监控相应的目录,这样在文件被修改时,gulp会自动执行相应的任务
gulp.watch('src/sass/**/*.scss', ['sass']);
gulp.watch('src/fonts/**/*.*', ['static']);
gulp.watch('src/images/**/*.*', ['static']);
gulp.watch('index.html', ['static']);

2.ES6与Webpack

随着2015年6月ECMAScript 2015(曾用名:ECMAScript 6、ES6,下文使用ES6)的正式发布,各浏览器对ES6的支持度已经越来越高了,部分开发者已经从小规模使用转到了大规模使用,甚至将此最为新项目的标准。

在我大规模使用ES6一段时间之后,偶然的一次机会,发现身边有这样的一群人存在,他们认为现在有些浏览器并不能兼容ES6,所以现在了解ES6还为时过早。其实,这些兼容性问题早已经有了成熟的解决方案,最为出名的就是babel,关于babel的用法,这里不做过多的赘述,大家可以参考官网的一些说明。

这里我们使用Webpack来处理前端资源的模块化和打包,那么Webpack是用来干什么的呢?引用《Webpac中文指南》里面的一句话:

Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。

这里我们用webpack处理ES6、ES6模块加载、Reactjs的编译,具体如下:

2.1 webpack的基础用法

首先安装webpack

$ npm install webpack -g

然后在项目目录安装

$ npm install webpack --save-dev

此时就可以使用webpack命令来打包文件了,例如我们有两个文件如下:

// index.js
console.log('hello');
var m = require('./moudle.js');
m();
// moudle.js
module.exports = function(){
    console.log('world')
}

其中index.js是入口文件,module.js是其依赖的其他模块文件,在index中我们引入了module.js的内容。

接下来开始编译,执行如下命令:

// 以index.js作为入口,打包到bundle.js文件中
$ webpack index.js bundle.js

命令台会打印出如下信息,并产出一个bundle.js的文件,这个就是我们最终打包成功的文件。

Hash: 9f99cbe53612bf1056ab
Version: webpack 2.1.0-beta.25
Time: 62ms
    Asset     Size  Chunks             Chunk Names
bundle.js  2.62 kB       0  [emitted]  main
   [0] ./moudle.js 53 bytes {0} [built]
   [1] ./index.js 59 bytes {0} [built]

此时把bundle.js引入到一个html文件里面,用浏览器打开,在控制台就可以看到输出了hello和world。

2.2 Webpack的Loader

Loader可以说是webpack中最为神奇的地方了,他赋予了webpack处理其他类型文件的能力,你可以通过require来处理任何支持类型的模块或者文件,比如jsx, less, sass, 图片等。

先来个简单的栗子。

在上一步的目录里我们新建一个css文件

/* style.css */
body { background: red; }

然后修改index.js

// index.js
require('!style!css!./style.css') //用style和css的loader处理css文件

console.log('hello');
var m = require('./moudle.js');
m();

安装loader:

$ npm install css-loader style-loader

重新编译

$ webpack index.js bundle.js

之后把编译好的bundle.js引入到html文件中,并打开,就会发现页面的背景变成了红色。

细心的童鞋会问 require('!style!css!./style.css') 这样写的话,每次都要写 !style!css! 会不会很麻烦,其实我们还可以将loader写在编译的脚本中:

$ webpack entry.js bundle.js --module-bind "css=style!css"

或者写在webpack的配置中,这样我们在引入对应的文件只要简单的写成下面这样就好了。

require("./style.css")

2.3 在gulp中使用webpack

正常情况下,我们可以在项目的根目录建立一个webpack.config.js的文件,然后把各种配置写在里面,简单的例子如下:

// webpack.config.js

// 引入webpack
var webpack = require('webpack')

module.exports = {
  entry: './index.js',  // 项目的入口文件
  output: {
    filename: 'bundle.js'  // 输出文件
  },
  module: {
    loaders: [
      {test: /\.css$/, loader: 'style!css'}  // 使用的loader
    ]
  }
}

有了配置之后,我们就可以直接在项目目录里面执行下面的脚本,webpack就会按照配置的约定去执行了。

$ webpack

同理在gulp,我们可以继续使用webpack的config文件,也可以在gulp中去写webpack的配置,这里介绍如何在gulp中使用webpack处理es6以及react。

直接上代码,具体的内容写在注释里面,在执行之前,你可能需要安装下面的包:

# gulp 依赖的bable 和 sass
$ npm install gulp-babel gulp-sass node-sass --save-dev

# bable 的依赖文件
$ npm install babel-core babel-loader babel-preset-es2015 babel-preset-react --save-dev 

# sass 和 css 依赖的文件
$ npm install css-loader style-loader sass-loader --save-dev

具体配置:

// 上下文代码参见1.2中的gulpfile.js,这里只介绍webpack任务
gulp.task('webpack', function () {
     // 处理所有src下面的.jsx文件
    return gulp.src('src/**/*.jsx') 
        .pipe(webpack({
            watch: true,    
            entry: {
                index: './src/index.jsx'  
                // 入口文件为 src/index.js
            },
            output: {
                filename: './js/[name].js',  
                // 输出文件到 js/ 目录下,文件名为jsx文件的前缀+js
                chunkFilename: './js/[name].chunk.js', 
                // 如果用的require,require中的文件会单独打包成一个文件。
            },
            module: {
                loaders: [{
                    test: /.jsx?$/,
                    loader: 'babel-loader',  
                    // 使用babel-loader处理jsx文件
                    query: {
                        compact: false,
                        presets: ['es2015', 'react'] 
                        // 处理es2015和react文件
                    }
                }, {
                    test: /\.scss$/,
                    loaders: ["style", "css", "sass"] 
                    // 处理sass
                }]
            }
        }))
        .pipe(gulp.dest('dist/')); 
        // 编译好的文件放到dist目录下
});

3. Sass

Sass 是对 CSS 的扩展,让 CSS 语言更强大、优雅。 它允许你使用变量、嵌套规则、 mixins、导入等众多功能, 并且完全兼容 CSS 语法。 Sass 有助于保持大型样式表结构良好, 同时也让你能够快速开始小型项目, 特别是在搭配 Compass 样式库一同使用时。

和Sass同级别的CSS扩展还有lesscss,至于那个好用,应该是仁者见仁智者见智的事情,这里不进行讨论。

3.1 Sass的基础用法

Sass是基于Ruby的,所以在开始之前必须安装好Ruby,Mac系统大部分是自带Ruby的不用去考虑这个问题,但是对于window用户就得自己去安装了(安装Ruby)。

有了Ruby之后我们就可以安装Sass了,只要一条命令就可以搞定了(由于某种众所周知的原因,可能无法访问Ruby的镜像网站,这里可以使用taobao的Ruby镜像)

$ gem install sass

安装好了之后,就可以执行 sass -v 查看 sass 的版本,如果需要编译文件就可以执行下面的命令

$ sass test.scss:test.css test1.scss:test1.css --watch

上面的命令表示把test.scss编译成test.css,把test1.scss编译成test1.css, --watch 命令用于监控,如果文件有变化了,sass会自动编译css。

至于sass的语法什么的这里不准备细聊,可以参考下面的sass相关资源:

3.2 Sass与gulp

上面说了如何简单的使用sass命令去编译sass文件,但是大多数情况下,我们不可能一一列出所有的sass文件,这时候gulp之类的工具就帮上大忙了。

下面说实例:

开始之前,你可能需要安装相应的包:

$ npm install gulp-sass --save-dev

注:如果只使用gulp-sass,不用命令行执行sass的话,可以不用安装3.1中的sass

// gulpfile.js 
// 具体完全配置看上文这里只介绍sass相关任务的配置

// 引入sass
const sass = require('gulp-sass');

// sass 任务
gulp.task('sass', function () {
    // 把 src/sass目录下的所有scss文件加入到目录中
   return gulp.src('./src/sass/**/*.scss')
           // 用sass去编译scss文件
        .pipe(sass().on('error', sass.logError))
        // 将编译好的文件放置到 dist/css 目录
        .pipe(gulp.dest('./dist/css'));
});

react

react作为新一代的用于构建用户界面的 JAVASCRIPT 库,已经红遍了大江南北,原本计划细说一下react的用法之类的,后来发现,其实只要一本中文文档即可,后续有空,单独和大家分享一下react处理数据流以及使用过程中的一些心得。

相关资源:

以上为Mofei在实际工作中使用react的一些经验,纯个人摸索,有些地方可能并不是最优的解决方案,如果有任何建议或者纠错,请不吝赐教,共同进步。

Write a response...
Mofei Zhu
publish
liuyu19910624
2017-05-06 17:51
0
 Replay
@liuyu19910624  
Replay
the king
2017-05-02 10:40
受教。非常感谢
0
 Replay
@the king  
Replay
the king
2017-05-02 10:39
受教。非常感谢
0
 Replay
@the king  
Replay
Lucy Wu
2017-03-27 09:40
学习啦,辛苦啦。
0
 Replay
@Lucy Wu  
Replay
Mofei
2017-01-14 10:59
@Tean  已修改,多谢提示~~
0
 Replay
@Mofei  
Replay
Mofei
2017-01-14 10:58
@Gjt  欢迎常来,有其他的问题, 也可以多多交流~
0
 Replay
@Mofei  
Replay
Gjt
2016-12-12 20:03
初学者进来学习
0
 Replay
@Gjt  
Replay
falost
2016-12-06 17:30
@Tean  我经常手速敲成 insatll  然后就看见一堆报错 哈哈
0
 Replay
@falost  
Replay
Tean
2016-11-28 22:45
手误了,npm install中install你写成isntall了!
3
 Replay
@Tean  
Replay