Tổ Chức Code, Build và Bundle

Nguyễn Duy

Trong bài học này chúng ta sẽ tổ chức lại source code trong ứng dụng. Đối với các ứng dụng nhỏ thì công việc này không tạo ra nhiều sự khác biệt, tuy nhiên với các ứng dụng lớn thì việc làm này là hết sức cần thiết giúp cho developer có thể dễ dàng quản lý các thay đổi có thể phát sinh sau này. Mặc dù các bước trong bài học này là tùy chọn và bạn có thể bỏ qua, tuy nhiên nếu muốn trở thành một developer chuyên nghiệp bạn nên rèn luyện thói quen tổ chức source code theo cách tốt nhất có thể.

Bước 7.1: Bundle với Browserify

Ở bước này chúng ta sẽ thực hiện việc đóng đóng gói (hay bundle) các file JavaScript khác nhau (dùng bởi trình duyệt) về một file duy nhất. Cách làm này sẽ giúp hạn chế số lượng request mà trình duyệt phải gửi lên server qua đó tăng tốc độ tải trang.

Chúng ta sẽ sử dụng một mô-đun trong Node.js tên là browserify để thực hiện thao tác bundle các file JavaScript. Mở chương trình cửa sổ dòng lệnh terminal và chạy câu lệnh dưới đây để cài đặt mô-đun này:

$ sudo npm install -g browserify

Sau khi hoàn tất cài đặt mô-đun trên chúng ta sẽ có thể sử dụng câu lệnh browserify trên terminal để bundle:

$ browserify

Câu lệnh trên sẽ hiển thị cách sử dụng của công cụ này. Tạm thời chúng ta chưa cần bận tâm đến các thông tin này. Bây giờ chúng ta sẽ cài đặt các mô-đun react, react-domjquery của Node.js rồi sau đó quay lại với browserify để tìm hiểu cách sử dụng công cụ này như thế nào.

$ npm install --save react@0.14.5
$ npm install --save react-dom@0.14.5
$ npm install --save jquery@2.1.4

Một tính năng tuyệt vời ở browserify đó là chúng ta có thể sử dụng cú pháp require() của Node.js để nhập vào các mô-đun JavaScript khác. Sau khi kết thúc cài đặt các mô-đun trên bạn mở file App.js và thêm 3 dòng code sau ở đầu file này:

var React = require('react');
var ReactDOM = require('react-dom');
var $ = require('jquery');

Bạn lưu ý App.js là file JavaScript được dùng bởi trình duyệt chứ không phải Node.js trên server. Vậy thì làm sao có thể dùng require() được? Đừng ngạc nhiên nếu bạn cảm thấy hơi bối rối bởi điều này.

Đúng là trình duyệt không hiểu được require() tuy nhiên browserify mới là công cụ được dùng để thực hiện việc gom các thư viện cần dùng chứ không phải trình duyệt.

Bây giờ chúng ta sẽ thực hiện thao tác bundle các thư viện dùng trong file App.js sử dụng browserify:

$ browserify static/App.js > static/bundle.js

Câu lệnh trên sẽ thực hiện việc gom các file được nhập vào App.js sử dụng cú pháp require() cuối cùng tổng hợp ra một file tên là bundle.js. Bạn có thể mở file bundle.js để xem cụ thể.

Kết thúc bước này bằng việc cập nhật code trong file index.html giống như sau:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Bug Tracker - a React tutorial using MERN</title>
    <style>
      th, td {border: 1px solid silver; padding: 2px;}
      table {border-collapse: collapse};
    </style>
  </head>
  <body>
    <div id="main"></div>
    <script src="/bundle.js" type="text/javascript"></script>
  </body>
</html>

Ở trên chúng ta đã xóa các dòng code có thẻ <script> tham chiếu tới react, react-dom, jquery và thay bằng việc tham chiếu tới một tập tin duy nhất là bundle.js.

So sánh thay đổi trong source code ở bước 7.1 ở đây.

Lưu ý: Thay đổi trong source code không bao gồm hướng dẫn cho việc cài đặt mô-đun browserify trên toàn hệ thống.

Bước 7.2: Tự Động Hóa với Gulp

Ở bước này chúng ta sẽ tự động hóa các tác vụ sử dụng một công cụ có tên là Gulp. Gulp được coi là một task runner giúp tự động hóa các tác vụ thủ công.

Đầu tiên chúng ta cần cài đặt mô-đun gulp-cli của Node.js trên toàn hệ thống:

$ sudo npm install -g gulp@3.9.0

Đồng thời cài đặt mô-đun gulp ở phạm vi local của dự án, chúng ta sẽ cần tới mô-đun này khi chạy gulp-cli trên terminal:

$ npm install --save gulp@3.9.0

Ngoài ra chúng ta cũng sẽ cài đặt các mô-đun babelify, browserifyvinyl-source-stream để kết hợp sử dụng cùng Gulp. Tương tự như gulp thì các mô-đun này sẽ được cài đặt nội bộ trong dự án:

$ npm install --save babelify
$ npm install --save browserify
$ npm install --save vinyl-source-stream

Chúng ta sẽ cần tạo ra một file tên là gulpfile.js, file này là file cấu hình và được Gulp dùng để chạy các task được khai báo trong đó. Trong thư mục dự án tạo file gulpfile.js này với nội dung như sau:

var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream');

gulp.task('bundle', function() {
  return browserify('src/App.js')
    .transform('babelify', {presets: 'react'})
    .bundle()
    .pipe(source('bundle.js'))
    .pipe(gulp.dest('static/'));
});

Ở đoạn code trên trên chúng ta truyền vào đối số thứ hai cho gulp.task() là một hàm callback. Bên trong hàm này task của gulp sẽ được chạy. Bạn nên dành ra một chút thời gian để nhìn lại đoạn code bên trong hàm này để hiểu ý nghĩa các task.

Sau đó trở lại cửa sổ terminal và chạy câu lệnh sau:

$ gulp bundle

Ở cuối câu lệnh bạn sẽ thấy terminal hiển thị kết quả như sau:

[18:06:52] Using gulpfile /var/www/html/test/mern/gulpfile.js
[18:06:52] Starting 'bundle'...
[18:06:55] Finished 'bundle' after 3.1 s

So sánh thay đổi trong source code ở bước 7.2 ở đây.

Bước 7.3: Theo Dõi Thay Đổi Của Source Code với watchify

Ở bước này chúng ta muốn Gulp tự động chạy lại các task được định nghĩa trong gulpfile.js nếu như có bất cứ sự thay đổi nào trong source code.

Chúng ta sẽ làm điều này thông qua mô-đum watchify của Node.js. Tương tự như ở bước trước chúng ta sẽ cần cài mô-đun này ở phạm vi nội bộ của dự án:

$ npm install --save watchify@3.6.1

Cập nhật file gulpfile.js để sử dụng mô-đun này khi Gulp chạy task:

...
var watchify = require('watchify');
...

Đồng thời chúng ta cũng thêm đoạn code sau vào cuối file:

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

  var b = browserify({
    entries: ['src/App.js'],
    cache: {}, packageCache: {},
    plugin: ['watchify']
  });

  b.on('update', makeBundle);

  function makeBundle() {
    b.transform('babelify', {presets: 'react'})
      .bundle()
      .pipe(source('bundle.js'))
      .pipe(gulp.dest('static/'));
  }

  // khởi động bundle (một lần đầu tiên).
  makeBundle();

  return b;
});

Trở lại terminal và chạy lại gulp bundle. Bây giờ Gulp sẽ tự động chạy các task được khai báo trong hàm makeBundle() ở trên khi có sự thay đổi tạo ra cho source code dự án.

So sánh thay đổi trong source code ở bước 7.3 ở đây.

Bước 7.4: Xử Lý Error

Trong bước này chúng ta sẽ thêm chức năng để Gulp hiển thị thông báo cho các lỗi mà công cụ này gặp phải khi chạy các task. Cập nhật hàm makeBundle() trong file gulpfile.js thành như sau:

...
  function makeBundle() {
    b.transform('babelify', {presets: 'react'})
      .bundle()
      .on('error', function(err) {
        console.error(err.message);
        console.error(err.codeFrame);
      })
      .pipe(source('bundle.js'))
      .pipe(gulp.dest('static/'));
    console.log("Bundle updated, success");
  }
...

Đoạn code trên sẽ listen sự kiện khi Gulp gặp phải lỗi và hiển thị thống báo lỗi tương ứng.

Và cuối cùng chúng ta thiết lập task mặc định cho Gulp là watch bằng việc thêm dòng code sau ở cuối gulpfile.js:

gulp.task('default', ['watch']);

So sánh thay đổi trong source code ở bước 7.4 ở đây.

Bước 7.5: Mô-đun Hóa (Modulize) Source Code

Trước khi kết thúc bài học này chúng ta sẽ tổ chức lại source code bằng việc mô-đun hóa các component của React.js trong ứng dụng. Chúng ta thực hiện việc này bằng cách tách các component ra các file riêng thay vì để toàn bộ trong một file App.js như hiện tại.

Đầu tiên bạn tạo một file BugFilter.js trong thư mục src với nội dung sau:

var React = require('react');
var ReactDOM = require('react-dom');

var BugFilter = React.createClass({
  render: function() {
    console.log("Rendering BugFilter");
    return (
      <div>Hiển thị filter ở đây.</div>
    )
  }
});

module.exports = BugFilter;

Ở trên chúng ta đã copy phần lớn code liên quan tới việc tạo BugFilter component có trong file App.js và chuyển qua file mới BugFilter.js để thực hiện thao tác mô-đun hóa qua dòng code cuối:

module.exports = BugFilter;

Tương tự chúng ta sẽ mô-đun hóa các component BugAddBugList. Bạn có thể tự làm công việc này sau đó đối chiếu lại bằng cách xem thay đổi trong source code ở bước này trên Github ở đây.

Lưu ý: Trong khi mô-đun hóa BugList component chúng ta sẽ cần nhập (import) vào các mô-đun BugAddBugFilter. Ngoài ra chúng ta sẽ không mô-đun hóa BugRow.

Cuối cùng chúng ta cần cập nhật App.js để sử dụng các component được mô-đun hóa. Code bên trong App.js lúc này sẽ trông đơn giản hơn rất nhiều như sau:

var React = require('react');
var ReactDOM = require('react-dom');

var BugList = require('./BugList');

ReactDOM.render(
  <BugList />,
  document.getElementById('main')
);

So sánh thay đổi trong source code ở bước 7.5 ở đây.

Thêm Phản Hồi

Câu Hỏi Liên Quan

Hướng Dẫn Liên Quan