zoukankan      html  css  js  c++  java
  • 使用Gulp和Browserify来搭建React应用程序

    对React有一定了解之后,我们知道,需要把JSX文件转换成JS文件,组件需要导入导出。本篇就体验使用Gulp把JSX文件转换成JS文件,使用Browserify来把组件捆绑到一个文件并理顺组件之间的依赖关系。


    Gulp是用来干嘛的呢?用来把Coffeescript, SASS, JSX等转换成浏览器能理解的JavaScript或CSS,再比如压缩文件到最小尺寸,再比如把文件捆绑到一个文件以减少请求次数,等等。


    【文件结构】

    node_modules/
    gulpfile.js
    Typler/
    .....src/
    ..........index.html
    ..........js/
    ...............App.js
    ...............Child.js
    ...............Parent.js


    【需求】


    Development阶段:把JSX文件转换成JS文件,并保存到dist/src文件夹中;把src文件夹中的index.html文件复制到dist文件夹中


    Product阶段:把所有的JS文件concat, minify, 最终绑定到一个文件build.js,把index.html中所有<script>,替换成一个<script>标签。


    【index.html】


    <!DOCTYPE html>
    <html>
        <head></head>
        <body>
            <div id="app"></div>
            
            <script src="../../lib/react.js"></script>
            <script src="../../lib/react-dom.js"></script>
            <script src="../src/js/Child.js"></script>
            <script src="../src/js/Parent.js"></script>
            <script src="../src/js//App.js"></script>
        </body>
    </html>
    

    【Child.js】


    var child = React.createClass({
        render: function(){
            return (
                <div>
                    and this is the <b>{this.props.name}</b>.
                </div>
            )
        }
    });
    

    ** 【Parent.js】**
    var Parent = React.createClass({
        render: function(){
            return (
                <div>
                    <div>This is the parent.</div>
                    <child name="child" />
                </div>
            )
        }
    });
    

    【App.js】


    ReactDOM.render(<Parent />, document.getElementById('app'));
    

    【下载NPM Packages】


    npm install --save-dev gulp
    npm install --save-dev gulp-concat
    npm install --save-dev gulp-uglify
    npm install --save-dev gulp-react
    npm install --save-dev gulp-html-replace
    

    【gulpfile.js】


    var gulp = require('gulp');
    var concat = require('gulp-concat');
    var uglify = require('gulp-uglify');
    var react = require('gulp-react');
    var htmlreplace = require('gulp-html-replace');
    
    var path = {
        HTML: 'Tyler/src/index.html',
        ALL:['Tyler/src/js/*.js', 'Tyler/src/js/**/*.js','Tyler/src/index.html'],
        JS: ['Tyler/src/js/*.js', 'Tyler/src/js/**/*.js'],
        MINIFIED_OUT: 'build.min.js',
        DEST_SRC: 'Tyler/dist/src', //把从jsx文件转换而来的文件放这里
        DEST_BUILD: 'Tyler/dist/build',
        DEST: 'Tyler/dist'
    };
    
    //获取js的源文件,把jsx转换成js,放到目标文件夹
    gulp.task('transform', function(){
        gulp.src(path.JS)
            .pipe(react())
            .pipe(gulp.dest(path.DEST_SRC))
    })
    
    //把Tyler/src/index.html这个文件复制放到Tyler/dist中
    gulp.task('copy', function(){
       gulp.src(path.HTML)
        .pipe(gulp.dest(path.DEST));
    });
    
    //观察index.html和js文件的变化,执行以上的2个任务
    gulp.task('watch', function(){
        gulp.watch(path.ALL, ['transform', 'copy']);
    });
    
    //名称为default的task,需要
    gulp.task('default',['watch','transform', 'copy']);
    
    • transform这个task用来把jsx转换成js
    • copy这个task用来把Tyler/src/index.html复制拷贝到Tyler/dist/index.html
    • watch这个task用来观察js和html文件的变化,一旦有变化就执行transform和copy这个task
    • default是默认的task,一定需要,执行所有的task

    现在执行gulp命令后,Tyler下多了dist文件夹,dist文件夹下多了index.html和src文件夹,src文件夹下有Child.js, Parent.js, App.js.


    现在还剩下发布状态下的一些task,要做的事包括:

    • 先获取到所有的js文件
    • 把所有的js文件拼接起来
    • 最小化js文件
    • 把输出文件放到dist/build文件夹中

    我们在gulp.js文件中增加如下:

    //发布到生产环境的task
    gulp.task('build', function () {
        gulp.src(path.JS)
            .pipe(react())
            .pipe(concat(path.MINIFIED_OUT)) //合并到build.min.js文件中
            .pipe(uglify(path.MINIFIED_OUT)) //压缩build.min.js文件中
            .pipe(gulp.dest(path.DEST_BUILD));//把build.min.js文件放到Tyler/dist/build文件夹中
    });
    

    运行"gulp build"命令,这样,在Tyler/dist/build下多了一个合并压缩后的build.min.js文件。


    但这里还有一个问题:Tyler/dist/index.html中依然引用的是src/js中的文件

        <script src="../src/js/Child.js"></script>
        <script src="../src/js/Parent.js"></script>
        <script src="../src/js//App.js"></script>
    

    而我们需要引用的是如下这个文件:

    <script src="build/build.min.js"></script>
    

    gulp-html-replace就是为解决这个问题而存在。需要两步。


    第一步:来到Tyler/src/index.html文件中,添加<!--build:js--><!--endbuild-->指令。

    <!DOCTYPE html>
    <html>
        <head></head>
        <body>
            <div id="app"></div>
            
            <script src="../../lib/react.js"></script>
            <script src="../../lib/react-dom.js"></script>
            <!-- build:js -->
            <script src="../src/js/Child.js"></script>
            <script src="../src/js/Parent.js"></script>
            <script src="../src/js//App.js"></script>
            <!-- endbuild -->
        </body>
    </html>  
    

    第二步:添加task

    //在Tyler/dist/index.html中引用的js文件和Tyler/src/index.html中不一样,需要替换
    gulp.task('replaceHTML', function(){
       gulp.src(path.HTML)
        .pipe(htmlreplace({
           'js': 'build/' + path.MINIFIED_OUT
       }))
       .pipe(gulp.dest(path.DEST));
    });
    
    //把发布到生产环境之前的所有任务再提炼
    gulp.task('production', ['replaceHTML', 'build']);
    

    运行:gulp production

    再次来到Tyler/dist/index.html中,惊喜地发现如下:

    <!DOCTYPE html>
    <html>
        <head></head>
        <body>
            <div id="app"></div>
            
            <script src="../../lib/react.js"></script>
            <script src="../../lib/react-dom.js"></script>
            <script src="build/build.min.js"></script>
    
        </body>
    </html>    
    

    使用gulp-html-replace生效了!

    最后,把完整的gulpfile.js呈现如下:

    //Tyler
    
    var gulp = require('gulp');
    var concat = require('gulp-concat');
    var uglify = require('gulp-uglify');
    var react = require('gulp-react');
    var htmlreplace = require('gulp-html-replace');
    
    var path = {
        HTML: 'Tyler/src/index.html'
        , ALL: ['Tyler/src/js/*.js', 'Tyler/src/js/**/*.js', 'Tyler/src/index.html']
        , JS: ['Tyler/src/js/*.js', 'Tyler/src/js/**/*.js']
        , MINIFIED_OUT: 'build.min.js'
        , DEST_SRC: 'Tyler/dist/src', //把从jsx文件转换而来的文件放这里
        DEST_BUILD: 'Tyler/dist/build'
        , DEST: 'Tyler/dist'
    };
    
    //获取js的源文件,把jsx转换成js,放到目标文件夹
    gulp.task('transform', function () {
        gulp.src(path.JS)
            .pipe(react())
            .pipe(gulp.dest(path.DEST_SRC))
    })
    
    //把Tyler/src/index.html这个文件复制放到Tyler/dist中
    gulp.task('copy', function () {
        gulp.src(path.HTML)
            .pipe(gulp.dest(path.DEST));
    });
    
    //观察index.html和js文件的变化,执行以上的2个任务
    gulp.task('watch', function () {
        gulp.watch(path.ALL, ['transform', 'copy']);
    });
    
    //名称为default的task,需要
    gulp.task('default', ['watch', 'transform', 'copy']);
    
    //发布到生产环境的task
    gulp.task('build', function () {
        gulp.src(path.JS)
            .pipe(react())
            .pipe(concat(path.MINIFIED_OUT)) //合并到build.min.js文件中
            .pipe(uglify(path.MINIFIED_OUT)) //压缩build.min.js文件中
            .pipe(gulp.dest(path.DEST_BUILD));//把build.min.js文件放到Tyler/dist/build文件夹中
    });
    
    //在Tyler/dist/index.html中引用的js文件和Tyler/src/index.html中不一样,需要替换
    gulp.task('replaceHTML', function(){
       gulp.src(path.HTML)
        .pipe(htmlreplace({
           'js': 'build/' + path.MINIFIED_OUT
       }))
       .pipe(gulp.dest(path.DEST));
    });
    
    //把发布到生产环境之前的所有任务再提炼
    gulp.task('production', ['replaceHTML', 'build']);
    

    以上,我们了解了有关gulp的好多方面,但这样的做法还有那些不足呢?


    • 需要手写各个组件的js文件位置
    • Parent.js依赖Child.js,需要手动让Child.js先与Parent.js加载
    • App.js依赖Parent.js,需要手动让Parent.js先与App.js先加载
    • 很难调试,很难知道jsx哪里出了问题

    Browserify就是为了解决以上问题而存在的。


    【Browserify+Gulp+React(Developement Tasks)】


    安装NPM Packages

    npm install --save-dev vinyl-source-stream
    npm install --save-dev browserify
    npm install --save-dev watchify
    npm install --save-dev reactify
    npm install --save-dev gulp-streamify
    

    gulpfule.js

    //Tyler using browserify
    
    var gulp = require('gulp');
    var uglify = require('gulp-uglify');
    var htmlreplace = require('gulp-html-replace');;
    var source = require('vinyl-source-stream');
    var browserify = require('browserify');
    var watchify = require('watchify');
    var reactify = require('reactify');
    var streamify = require('gulp-streamify');
    
    var path = {
        HTML: 'Tyler/src/index.html'
        , MINIFIED_OUT: 'build.min.js'
        , OUT: 'build.js'
        , DEST: 'Tyler/dist1'
        , DEST_BUILD: 'Tyler/dist1/build'
        , DEST_SRC: 'Tyler/dist1/src'
        , ENTRY_POINT: 'Tyler/src/js/App.js'
    };
    
    //Tyler/src/index.html中复制到TylerTyler/dist中
    gulp.task('copy', function () {
        gulp.src(path.HTML)
            .pipe(gulp.dest(path.DEST));
    });
    
    //监测
    gulp.task('watch', function () {
        
        //监测html文件
        gulp.watch(path.HTML, ['copy']);
    
        //watchify配合browserify使用,因为单独使用browserify会每次遍历每个组件,一旦有变化就会重新生成绑定文件。而有了watchify,会缓存文件,只更新哪些发生改变的文件
        var watcher = watchify(browserify({
            entries: [path.ENTRY_POINT],//Tyler/src/js/App.js, browserify会检测Tyler/src/js下的所有js文件,以及Tyler/src/js下所有子文件夹下的js文件
            transform: [reactify],//使用reactify把jsx转换成js文件
            debug: true,//告诉Browersify使用source maps, souce maps帮助我们在出现错误的时候定位到jsx中的错误行
            cache: {},//必须的,browserify告诉我们这样使用
            packageCache: {},//必须的,browserify告诉我们这样使用
            fullPath: true//必须的,browserify告诉我们这样使用
        }));
        
        return watcher.on('update', function(){
            watcher.bundle()//把所有的jsx文件绑定到一个文件
                .pipe(source(path.OUT))
                .pipe(gulp.dest(path.DEST_SRC));
            
            console.log('Updated');
        })
            .bundle()
            .pipe(source(path.OUT))
            .pipe(gulp.dest(path.DEST_SRC));
    });
    
    //默认的task
    gulp.task('default', ['watch']);
    

    运行gulp命令,在Tyler文件夹下多了dist1文件夹。


    【Browserify + Gulp + React(Production Tasks)】


    在发布到生产环境之前,需要添加如下task
    
    //发布到生产环境之前
    gulp.task('build', function () {
                browserify({
                        entries: [path.ENTRY_POINT]
                        , transform: [reactify]
                    })
                    .bundle()
                    .pipe(source(path.MINIFIED_OUT))
                        .pipe(streamify(uglify(path.MINIFIED_OUT)))
                        .pipe(gulp.dest(path.DEST_BUILD));
                    });
    
    gulp.task('replaceHTML', function () {
        gulp.src(path.HTML)
            .pipe(htmlreplace({
                'js': 'build/' + path.MINIFIED_OUT
            }))
            .pipe(gulp.dest(path.DEST));
    });
        
    gulp.task('production', ['replaceHTML', 'build']);
    

    使用Browserify完整版如下:


    //Tyler using browserify
    
    var gulp = require('gulp');
    var uglify = require('gulp-uglify');
    var htmlreplace = require('gulp-html-replace');;
    var source = require('vinyl-source-stream');
    var browserify = require('browserify');
    var watchify = require('watchify');
    var reactify = require('reactify');
    var streamify = require('gulp-streamify');
    
    var path = {
        HTML: 'Tyler/src/index.html'
        , MINIFIED_OUT: 'build.min.js'
        , OUT: 'build.js'
        , DEST: 'Tyler/dist1'
        , DEST_BUILD: 'Tyler/dist1/build'
        , DEST_SRC: 'Tyler/dist1/src'
        , ENTRY_POINT: 'Tyler/src/js/App.js'
    };
    
    //Tyler/src/index.html中复制到TylerTyler/dist中
    gulp.task('copy', function () {
        gulp.src(path.HTML)
            .pipe(gulp.dest(path.DEST));
    });
    
    //监测
    gulp.task('watch', function () {
    
        //监测html文件
        gulp.watch(path.HTML, ['copy']);
    
    
        //watchify配合browserify使用,因为单独使用browserify会每次遍历每个组件,一旦有变化就会重新生成绑定文件。而有了watchify,会缓存文件,只更新哪些发生改变的文件
        var watcher = watchify(browserify({
            entries: [path.ENTRY_POINT], //Tyler/src/js/App.js, browserify会检测Tyler/src/js下的所有js文件,以及Tyler/src/js下所有子文件夹下的js文件
            transform: [reactify], //使用reactify把jsx转换成js文件
            debug: true, //告诉Browersify使用source maps, souce maps帮助我们在出现错误的时候定位到jsx中的错误行
            cache: {}, //必须的,browserify告诉我们这样使用
            packageCache: {}, //必须的,browserify告诉我们这样使用
            fullPath: true //必须的,browserify告诉我们这样使用
        }));
    
        return watcher.on('update', function () {
                watcher.bundle() //把所有的jsx文件绑定到一个文件
                    .pipe(source(path.OUT))
                    .pipe(gulp.dest(path.DEST_SRC));
    
                console.log('Updated');
            })
            .bundle()
            .pipe(source(path.OUT))
            .pipe(gulp.dest(path.DEST_SRC));
    
    
    });
    
    //默认的task
    gulp.task('default', ['watch']);
    
    //发布到生产环境之前
    gulp.task('build', function () {
                browserify({
                        entries: [path.ENTRY_POINT]
                        , transform: [reactify]
                    })
                    .bundle()
                    .pipe(source(path.MINIFIED_OUT))
                        .pipe(streamify(uglify(path.MINIFIED_OUT)))
                        .pipe(gulp.dest(path.DEST_BUILD));
                    });
    
    gulp.task('replaceHTML', function () {
        gulp.src(path.HTML)
            .pipe(htmlreplace({
                'js': 'build/' + path.MINIFIED_OUT
            }))
            .pipe(gulp.dest(path.DEST));
    });
        
    gulp.task('production', ['replaceHTML', 'build']);
  • 相关阅读:
    RPC 接口必须是业务职责
    一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践
    t
    hessian-serialization
    服务拆分 服务设计
    灰度架构设计方案
    有赞发号器多机房方案
    解析MySQL中存储时间日期类型的选择问题
    t
    在阿里,我如何做好技术项目管理?
  • 原文地址:https://www.cnblogs.com/darrenji/p/5505499.html
Copyright © 2011-2022 走看看