使用Gulp和Browserify來搭建React應用程序

對React有必定了解以後,咱們知道,須要把JSX文件轉換成JS文件,組件須要導入導出。本篇就體驗使用Gulp把JSX文件轉換成JS文件,使用Browserify來把組件捆綁到一個文件並理順組件之間的依賴關係。html


Gulp是用來幹嗎的呢?用來把Coffeescript, SASS, JSX等轉換成瀏覽器能理解的JavaScript或CSS,再好比壓縮文件到最小尺寸,再好比把文件捆綁到一個文件以減小請求次數,等等。node


【文件結構】react

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


【需求】gulp


Development階段:把JSX文件轉換成JS文件,並保存到dist/src文件夾中;把src文件夾中的index.html文件複製到dist文件夾中瀏覽器


Product階段:把全部的JS文件concat, minify, 最終綁定到一個文件build.js,把index.html中全部<script>,替換成一個<script>標籤。緩存


【index.html】app


<!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】dom


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




【Parent.js】
ui


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']);
相關文章
相關標籤/搜索