Giới thiệu webpack

Khi tôi tìm hiểu về React.js, tôi thấy họ có dùng webpack để “bundle” các module. Trong các bài giới thiệu về Reactjs trước tôi có dùng Browserify để “bundle” các module của Node.js thấy đơn giản và cũng khá hay. Tôi có qua trang tài liệu của webpack-doc để thử tìm hiểu thì thấy hơi khó nhai nên tôi quyết định viết blog để có thể tìm hiểu sâu hơn cũng như có thể chia sẽ với những ai muốn tìm hiểu về nó.

Webpack là gì

Ngay tại trang document:
http://webpack.github.io/docs/what-is-webpack.html

webpack is a module bundler.

webpack takes modules with dependencies and generates static assets representing those modules.

Tôi nghĩ cách hiểu nhanh nhất về nó là ta phải “đụng tay đụng chân” với nó thôi.

Hướng tìm hiểu

  1. Bundle các file JS thành 1 file JS.
  2. Bundle ra các file JS chính từ nhiều file JS
  3. Chia file JS và load khi cần
  4. Nhúng HTML vào trong file JS
  5. Nhúng CSS vào trong file JS
  6. Nhúng file hình Base64 vào trong file JS

1. Bundle các file JS thành 1 file JS

Chuẩn bị:

./
 |_app.js
 |_sub.js
 |_dist
    |_index.html

app.js

var sub = require('./sub.js');
sub("Hello world");

sub.js

module.exports = function(msg){
  alert(msg);
}

index.html

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack Tutorial Demo</title>
  </head>
  <body>
    <script src="bundle.js"></script>
  </body>
</html>

Nếu bạn lười thực hiện các bước trên, các bạn có thể clone từ github repo demo của tôi:

https://github.com/phungnc/webpack-tut/tree/master/demo01

Bundle command:

webpack app.js dist/bundle.js

Sau đó chạy file index.html thì nó sẽ hiển thị message “Hello world”.

Bundle command trên có cú pháp như khi sử dụng Browserify (browserify app.js > dist/bundle.js). Tuy nhiên khác với Browserify, Webpack có thể config việc bundle đó trong file: webpack.config.js. Hãy tạo file webpack.config.js đồng cấp với file app.js

module.exports = {
  entry: {
    app: './app.js'
  },
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  }
}

Sau đó chỉ cần bạn chạy lệnh

webpack

Thì nó cũng tạo ra file bundle.js giống như trước.

2. Bundle ra các file JS chính từ nhiều file JS

Source code demo:
https://github.com/phungnc/webpack-tut/tree/master/demo02

webpack.config.js

module.exports = {
  entry: {
    app: './app.js',
    list: './list.js',
    item: './item.js',
  },
  output: {
    path: __dirname + '/dist',
    filename: '[name].js'
  }
}

Run webpack command, hãy xem output in ra:

Hash: 42caea66a4d1045d2c6c
Version: webpack 1.12.2
Time: 68ms
  Asset     Size  Chunks             Chunk Names
 app.js  1.56 kB       0  [emitted]  app
item.js  1.56 kB       1  [emitted]  item
list.js  1.56 kB       2  [emitted]  list
   [0] ./app.js 43 bytes {0} [built]
   [0] ./item.js 43 bytes {1} [built]
   [0] ./list.js 44 bytes {2} [built]
   [1] ./sub.js 47 bytes {0} {1} {2} [built]

Nó built ra các file trong thư mục dist các file app.js,list.js, item.js tương ứng đã được config trong file webpack.config.js. Khả năng có thể build ra nhiều file js cũng là điểm thú vị của webpack.

Nhưng mà, nếu ta xem trong các file app.js, list.js hay item.js thì có phần source code cả 3 file đều trùng nhau:

/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // Check if module is in cache
/******/        if(installedModules[moduleId])
/******/            return installedModules[moduleId].exports;

/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }


/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/

Để giải quyết vấn đề này ta có thể sử dụng plugin:

webpack.config.js

var webpack = require('webpack');
module.exports = {
  entry: {
    app: './app.js',
    list: './list.js',
    item: './item.js',
  },
  output: {
    path: __dirname + '/dist',
    filename: '[name].js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('app','app.js')
  ]
}

Chạy lại command

webpack

Bây giờ bạn thử check lại trong các file dist/list.jsdist/item.js xem. Phần source code giống nhau bây giờ chỉ còn lại ở file dist/app.js.

3. Chia file JS, chỉ load khi cần thiết.

Nếu bạn nào đã từng dùng qua requirejs rồi thì sẽ dễ dàng hiểu phần này hơn.
Ta có thể gọi chức năng này là module loader, tức là ta có thể load một module khi nào cần thiết. Khác với việc tạo một file bundle bao gồm tất tần tật các module JS vào, với webpack ta có thể cấu hình để chỉ load module cần thiết khi cần thiết thôi. Việc này có lợi cực kỳ khi hệ thống ngày càng lớn, khi đó nếu chỉ dùng 1 file bundle thì việc load ban đầu sẽ trở nên chậm chạp làm giảm performance của app, ngược lại việc load module một cách bất đồng bộ sẽ giúp tăng performance của app.

Ta sẽ demo bằng cách là trong file app.js sẽ load module sub.js sau 3 giây.

Source code:
https://github.com/phungnc/webpack-tut/tree/master/demo04

app.js

window.setTimeout(function() {
    require.ensure([],function(sub) {
        var sub = require('./sub');
        sub('App');
    });
},3000);

Run WebPack command

webpack

Hãy thử mở file index.html, sau khi mở lên, 3 giây sau thì nó sẽ alert “App”. Bạn có thể dùng Chrome Develop Tool để xem tiến trình load này.

4. Nhúng file HTML vào trong file JS

Bằng việc sử dụng module html-loader, ta có thể load file HTML vào trong file JS.
Trước hết ta phải install html-loader

Source code:
https://github.com/phungnc/webpack-tut/tree/master/demo04

npm install html-loader --save-dev

Sau đó trong webpack.config.js file:

module.exports = {
  entry: {
    app: './app.js'
  },
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
      loaders: [
          { test: /\.html$/, loader: 'html-loader' },
      ]
  }
}

Thêm phần load module html-loader.

Về phần source code, chuẩn bị file HTML có nội dung nào đó như là:

container.html

<div id="container">
    <section>
        <h2>Web pack is Cool</h2>
          <p>webpack is a module bundler.</p>
          <p>webpack takes modules with dependencies and generates static assets representing those modules.</p>
    </section>
</div>

app.js

var container = require('./container.html');
document.body.innerHTML = container;

Và run WebPack command:

webpack

Với việc nhúng HTML từ bên ngoài vào trong JS, thì chúng ta sẽ dễ nghĩ đến hướng chia cấu trúc trang web thành các template, component. Phần này có lẽ tôi sẽ giới thiệu trong các bài viết sau.

5. Nhúng file CSS vào trong file JS

Tương tự như việc load file HTML vào trong file JS, ta cũng có thể sử dụng các module style-loadercss-loader để nhúng CSS vào trong file JS.

Source code:
https://github.com/phungnc/webpack-tut/tree/master/demo05

npm install style-loader css-loader --save-dev

css-loader: tạo chuỗi css và nhúng vào trong file JS.

style-loader: ghi style vào trong tag

webpack.config.js

module.exports = {
  entry: {
    app: './app.js'
  },
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
      loaders: [
          { test: /\.html$/, loader: 'html-loader' },
          { test: /\.css$/, loaders: ['style-loader', 'css-loader']},
      ]
  }
}

Sau đó bạn có thể chuẩn bị tùy ý style mà bạn thích trong file style.css như là:

style.css

#container {
  width: 800px;
  margin: 0 auto;
}
section {
  text-align: center;
}

Gọi css vào trong file app.js

require('./style.css');
var container = require('./container.html');
document.body.innerHTML = container;

Run webpack

webpack

6. Nhúng file image dạng base64 vào trong file JS

Sau cùng, chúng ta thử nhúng file hình vào trong file JS. Ta sử dụng url-loader

npm install url-loader --save-dev

Cũng tương tự như những phần trên, cho nên tôi sẽ không giới thiệu lại, các bạn có thể tham khảo source code bên dưới:

Source code
https://github.com/phungnc/webpack-tut/tree/master/demo06