使用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']);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM