Lucian Maran

home

Gulp pentru dev environment. Partea a II-a - Watch si Livereload

04 Jun 2015

In contextul dezvoltarii unei aplicatii web bazata pe javascript (Node.js), termenul de Watch se refera la monitorizarea fisierelor (client sau server-side), iar Livereload se refera la reincarcarea automata a paginii in browser in momentul in care watch-ul detecteaza o modificare.

Daca vrei sa sari peste explicatii, poti accesa direct codul sursa de aici.

Asa cum reiese si din figura, monitorizarea client-side se realizeaza cu gulp.watch() iar cea server-side cu nodemon. In ambele cazuri, in momentul in care se detecteaza o modificare in codul sursa, se trimite o notificare server-ului de Livereload care, mai departe, va comanda un refresh in browser.

Livereload server

Livereload.listen() are rol de server (hub) care centralizeaza cererile primite de la diversi clienti (nodemon / gulp.watch) si le transmite, mai departe, unei componente care ruleaza in browser.

Pentru rularea server-ului de Livereload am creat un task cu o singura linie:

gulp.task('livereload', function() {
    livereload.listen();
});

Cele mai importante comenzi la care ascula acest server sunt:

Livereload browser plugin

Reincarcarea efectiva a unei pagini (sau a unei portiuni dintr-o pagina) se realizeaza cu ajutorul unei componente care ruleaza in browser. Pentru acesta componenta ai 2 optiuni:

  1. instalezi un plugin direct in browser
  2. instalezi un middleware (connect-livereload) in codul care ruleaza partea de server (Node.js)

Eu am preferat sa nu depind de un anumit plugin asa ca, in pagina de start (app.js) am adaugat urmatoarea secventa:

var app = express();

if (app.get('env') == 'development') {
    app.use(require('connect-livereload')());
};

Monitorizarea client-side (gulp.watch)

gulp.task('watch', function() {

    function injectJsAndReload(cb){
        gulp.src('./client/index.html')
            .pipe(inject(gulp.src('./client/app/**/*.js', {read: false}), {relative: true})) // js app files   
            .pipe(gulp.dest('./client/'))
            .on('end', function(){ 
                livereload.reload();
            }); 
    };

    function injectCssAndReload(){
        gulp.src('./client/index.html')
            .pipe(inject(gulp.src('./client/app/**/*.css', {read: false}), {relative: true})) // css app files   
            .pipe(gulp.dest('./client/'))
            .on('end', function(){ 
                livereload.reload();
            }); 
    };

    gulp.watch('client/app/**/*.*').on('change', function(file) { // no "./" in front of glob       
        var ext = path.extname(file.path); // ex: .js       
        var fileName = path.basename(file.path); // ex: test.css
        var crtDir = path.parse(file.path).dir; // ex: c:/.../controllers
        var name = path.parse(file.path).name; // ex: test      

        gutil.log(gutil.colors.cyan('watch-all'), 'saw',  gutil.colors.magenta(fileName), 'was ' + file.type); 

        if(ext == '.less'){                                                 
            if(file.type === 'deleted'){
                del(path.join(crtDir, name) + '.css'); 
            } else if(file.type === 'added' || file.type === 'changed' || file.type === 'renamed'){
                gulp.src(file.path)
                    .pipe(less())
                    .pipe(gulp.dest(path.parse(file.path).dir)); 
            };             

        } else if(ext == '.css'){                  
            if(file.type === 'added' || file.type === 'renamed'){
                injectCssAndReload();
            } else if(file.type === 'deleted'){ 
                del(file.path, injectCssAndReload);
            } else if(file.type === 'changed'){
                livereload.changed(fileName);         
            };             

        } else if(ext == '.js'){                
            if(file.type === 'added' || file.type === 'renamed'){
                injectJsAndReload();
            } else if(file.type === 'deleted'){
                del(file.path, injectJsAndReload); 
            } else if(file.type === 'changed'){
                gulp.src(file.path)    
                    .pipe(jshint())
                    .pipe(jshint.reporter(stylish))
                    .pipe(jshint.reporter('fail'))
                    .on('error', function(error){
                        gutil.log(gutil.colors.red('JSHINT failed!'));
                        this.emit('end'); // end the current task so that 'passed' msg is no longer displayed
                    })                                   
                    .pipe(notify(function(){ 
                        gutil.log(gutil.colors.green('JSHINT passed!'));
                        livereload.changed(fileName);
                    }));
            };         
        };
    });
});

Explicatii:

Monitorizarea server-side (nodemon)

Pentru partea de server nu este suficient un refresh in browser. Nodemon este un modul care monitorizeaza orice schimbare aparuta pe partea de server-side (node.js) si restarteaza automat procesul aferent aplicatiei.

gulp.task('watch-server', function() {
    nodemon({
            script: 'server/app.js',
            ext: 'js html',
            env: {'NODE_ENV':'development' },
            ignore: ['node_modules/', 'client', 'gulpfile.js']
        })
       .on('restart', function(){                 
            gulp.src('server/app.js', {read:false})
                .pipe(livereload()); 
        });
});

Concluzie

Codul poate parea putin cam lung (in comparatie cu alte surse disponibile pe Internet) dar am preferat sa ma concentrez pe eficienta cu care sunt prelucrate task-urile si mai putin pe dimensiunea codului. Spre exemplu:

Mai mult, datorila lui livereload, orice fisier modific, pe client sau server-side, modificarile se reflecta automat in browser.

comments powered by Disqus