xxholly32 / Blog

个人空间
https://www.xxholly32.com
4 stars 0 forks source link

grunt,gulp和webpack介绍与项目实践 #6

Open xxholly32 opened 8 years ago

xxholly32 commented 8 years ago

在项目打包上我们这块一直没有一个完整的培训,之前是引入grunt的内容较多点,我们一开始也分享一些grunt的打包内容,希望能做一些对比,特别是在技术选型中能够区分。当然我们的目的也只有一个,就是js、css、less、html能够一套完整的规范体系,压缩,打包,合并,校验,测试等。

grunt

grunt用的比较多,直接放在项目中的一些code吧

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        /* 文件合并 */
        concat: {
            // 将所有插件js合并成一个plugin.js
            dist: {
                src: ['public/javascript/common/plugin/*.js'],
                dest: 'public/javascript/common/plugin.js',
            },
        },

        // js压缩
        uglify: {
            options: {
                banner: '/*! 页面公共类 <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            dist3: {
                src: ['public/javascript/common/global.js',
                    'public/javascript/common/plugin.js',
                    'public/javascript/common/util.js'
                ],
                dest: 'public/javascript/common/common.min.js'
            }
        },

        // less插件配置
        less: {
            main: {
                expand: true,
                src: ['public/style/less/app.less'],
                dest: '',
                ext: '.css'
            },
            dev: {
                options: {
                    compress: true,
                    yuicompress: false
                }
            }
        },

        // css压缩插件
        cssmin: {
            target: {
                files: [{
                    expand: true,
                    src: ['public/style/less/app.css'],
                    dest: '',
                    ext: '.min.css'
                }]
            }
        },

        // watch插件的配置信息(监控less文件,如改变自动压缩,语法检查)
        watch: {
            // 用于监听js文件修改
            js: {
                files: [
                    'public/javascript/common/global.js',
                    'public/javascript/common/util.js',
                    'public/javascript/common/plugin/*.js'
                ],
                tasks: ['concat', 'uglify'],
                options: {
                    livereload: true
                }
            },
            // 用于监听less文件,当改变时自动编译成css文件
            css: {
                files: ['public/style/less/*.less',
                    'public/style/less/page/*.less',
                    'public/style/less/common/*.less',
                    'public/style/less/common/minxins/*.less',
                    'public/style/less/common/plugin/*.less'
                ],
                tasks: ['less', 'cssmin'],
                options: {
                    livereload: true
                }
            }
        }
    });

    // 加载任务插件
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');

    // 默认被执行的任务列表
    grunt.registerTask('default', ['concat' ,'uglify', 'less', 'cssmin', 'watch']);
};

grunt-contrib-concat 代码合并

grunt-contrib-uglify js压缩

grunt-contrib-less less合并

grunt-contrib-cssmin css压缩

grunt-contrib-jshint js校验

grunt-contrib-watch 自动监控

grunt 比较立体和直观,但相对比较笨重,可能会有3,4百行的配置文件要写

gulp

Gulp 提供了一个不一样的解决方案,而不是依赖于各种插件的配置。Gulp 使用了一个文件流的概念。如果你熟悉 Unix,那么 Gulp 对你来说会差不多,Gulp 会提供你一些简单化的操作。在这个解决方案中,是去匹配一些文件然后操作(就是说和 JavaScript 相反)然后输出结果(比如输出在你设置的编译路径等)。

var gulp = require('gulp');
var coffee = require('gulp-coffee');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var del = require('del');

var paths = {
  scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee'],
};

// 不是所有的任务需要使用 streams
// 一个 gulpfile 只是另一个node的程序,所以你可以使用所有 npm 的包
gulp.task('clean', function(cb) {
  // 你可以用 `gulp.src` 来使用多重通配符模式
  del(['build'], cb);
});

gulp.task('scripts', ['clean'], function() {
  // 压缩和复制所有 JavaScript (除了第三方库)
  // 加上 sourcemaps
  return gulp.src(paths.scripts)
    .pipe(sourcemaps.init())
      .pipe(coffee())
      .pipe(uglify())
      .pipe(concat('all.min.js'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('build/js'));
});

// 监听文件修改
gulp.task('watch', function() {
  gulp.watch(paths.scripts, ['scripts']);
});

// 默认任务(就是你在命令行输入 `gulp` 时运行)
gulp.task('default', ['watch', 'scripts']);

这些配置都是代码,所以当你遇到问题也可以修改,你也可以使用已经存在的 Gulp 插件,但是你还是需要写一堆模板任务。

个人感觉比grunt灵活一点,任务是针对文件而不是配的任务。

个人写的简单的gulp的例子 link to gulp!

webpack

Webpack 扩展了 CommonJs 的 require 的想法,比如你想在 CoffeeScript、Sass、Markdown 或者其他什么代码中 require 你想要的任何代码的话?那么 Webpack 正是做这方面的工作。它会通过配置来取出代码中的依赖,然后把他们通过加载器把代码兼容地输出到静态资源中。

简单来说就是我的js能够引入,css,sass,less,markdown,coffeescript,等等,看一个官方的例子

module.exports = {
    entry: "./entry.js",
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            { test: /\.css$/, loader: "style!css" }
        ]
    }
};

webpack 全局安装

# 用 npm 安装 Webpack全局依赖
$ npm install webpack -g

此时 Webpack 已经安装到了全局环境下,可以通过命令行 webpack -h 帮助。

# 安装 webpack 依赖
$ npm install webpack --save-dev

webpack 简单使用

创建一个简单的html页面

<!-- index.html -->
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <script src="bundle.js"></script>
</body>
</html>

一个js入口文件:

// entry.js
document.write('It works.')

然后编译 entry.js 并打包到 bundle.js:

$ webpack entry.js bundle.js

打包过程会显示日志:

Hash: e964f90ec65eb2c29bb9
Version: webpack 1.12.2
Time: 54ms
    Asset     Size  Chunks             Chunk Names
bundle.js  1.42 kB       0  [emitted]  main
   [0] ./entry.js 27 bytes {0} [built]

用浏览器打开 index.html 将会看到 It works. 。

webpack commonJS的文件引入

接下来添加一个模块 module.js 并修改入口 entry.js:

// module.js
module.exports = 'It works from module.js.'
// entry.js
document.write('It works.')
document.write(require('./module.js')) // 添加模块

重新打包 webpack entry.js bundle.js 后刷新页面看到变化 It works.It works from module.js.

webpack loader

关于loader的文件有很多种,我这边的例子就写3种jade,css,json,还有就是babel-loader

首先来看下官方的loader种类

LIST OF LOADERS!

loader的特性

先看demo3中的css-loader

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

entry.js

require("!style!css!./style.css") // 载入 style.css
document.write('It works.')
document.write(require('./module.js'))

安装loader

npm install css-loader style-loader

webpack 配置文件编写

demo-loader 的例子

module.exports = {
    entry: './entry.js',
    output: {
        filename: 'bundle.js',
    },
    module: {
        loaders: [
            { test: /\.json$/, loader: "json" },
            { test: /\.jade$/, loader: "jade" },
            { test: /\.css$/, loader: "style!css" },
            { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}
        ]
    }
}

这里我们的entry.js已经引入了 es6的写法: entry.js

import 'babel-polyfill';
import $ from 'jquery';

require("!style!css!./style.css");
var xxjade = require("!jade!./dir/xxjade.jade");
var json = require("!json!./dir/test.json");
console.log(json);

$(xxjade()).appendTo('body');

这里我说明下babel-loader引入必须的一些配置 package.json

"babel-core": "^6.13.2",
"babel-loader": "^6.2.5",
"babel-polyfill": "^6.13.0",
"babel-preset-es2015": "^6.13.2",

还需要引入一个.babelrc

 { "presets": [ "es2015" ] }

webpack项目实践

引入jQuery和bootstrap

webpack.config.js

var path=require('path');
var webpack = require('webpack');
module.exports = {
    entry: {
        entry : './entry.js',
        index : './index.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: './js/[name].debug.js',
    },
    module: {
        loaders: [
            { test: /\.json$/, loader: "json" },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },{
                test: /\.css$/,
                loaders: ['style', 'css']
            }, {
                test: /\.woff$/,
                loader: "url-loader?limit=10000&mimetype=application/font-woff&name=[path][name].[ext]"
            }, {
                test: /\.woff2$/,
                loader: "url-loader?limit=10000&mimetype=application/font-woff2&name=[path][name].[ext]"
            }, {
                test: /\.(eot|ttf|svg|gif|png)$/,
                loader: "file-loader"
            }
        ]
    },
    plugins:[
        new webpack.ProvidePlugin({ //加载jq
            $: 'jquery',
            jQuery: 'jquery'
        }),
        new webpack.optimize.CommonsChunkPlugin("./js/commons.js", ["index", "entry"])
    ]
}

实际上对bootstrap的引入并没有想象中这么友好,特别是中间的一些插件,引入起来极其困难。

webpack 和 react 结合

index.js

import React from 'react';
import { render } from 'react-dom';
import Hello from './component.jsx';

main();

function main() {
  React.render(<Hello />, document.getElementById('app'));
}

react 自定义组件 component.js

import React from 'react';

export default class Hello extends React.Component {
    render() {
        return <h1>Hello world</h1>;
    }
}

webpack.config.js

var path = require('path');
module.exports = {
  entry:  path.resolve(__dirname, 'app/index.js'),
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      { test: /\.css$/, loader: 'style-loader!css-loader' },
      { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' },
    ]
  }
};

react 和 webpack还是能够完美结合的,它的理念是把html完全嵌入到了js当中,和我们之前用到的项目jquery和bootstrap不一样。具体的react项目,再之后的react培训会细说。

其他的一些功能

其他还有一些工具bower,karma,和grunt类似。相对于grunt和gulp来说,webpack是完全不一样的框架,特别是在与js的处理,css和js的loader,es6的支持方面。也有项目gulp和webpack一起用的,gulp处理文件合并,webpack生产其他js文件等方式。但在和react的一些项目上,webpack还是首选的打包方式。

参考文献

https://segmentfault.com/a/1190000002985564 https://webpack.github.io/docs/what-is-webpack.html http://zhaoda.net/webpack-handbook/preface.html