12/08/2018, 14:20

Sử dụng Webpack trong Angular 2

1. Webpack là gì ? Webpack là một module bundler cho những ứng dụng javascript hiện đại. Bundle là quá trình gom (hay còn gọi là nén) các tài nguyên khác nhau (source code) vào một file duy nhất sau đó sẽ trả về client. Bundle có thể bao gồm javascript, css, html và hầu hết các loại file khác. ...

1. Webpack là gì ?

Webpack là một module bundler cho những ứng dụng javascript hiện đại. Bundle là quá trình gom (hay còn gọi là nén) các tài nguyên khác nhau (source code) vào một file duy nhất sau đó sẽ trả về client. Bundle có thể bao gồm javascript, css, html và hầu hết các loại file khác. Đồng thời, nó cũng có những ứng dụng khá tương đồng với các thư viện khác như: RequireJs, SystemJs,..Kết hợp với một số plugin nó có thể xử lý và nén các loại file như: Typescript, SASS, và LESS. Dưới đây là một số khái niệm quan trọng của Webpack.

1.1 Entry

Webpack tạo ra một đồ thị của toàn bộ những phụ thuộc trong ứng dụng của bạn. Điểm bắt đầu của đồ thị này được gọi là một entry. Entry nói cho Webpack nơi để bắt đầu và theo dõi các phụ thuộc để biết được cái gì cần bundle. Hoặc bạn có thể hiểu entry như file đầu tiên để khởi động app.

webpack.config.js

module.exports = {
  entry: './path/to/my/entry/app.js'
};

1.2 Output

Đây là nơi chỉ cho Webpack cần lưu trữ khi hoàn thành quá trình bundle

webpack.config.js

module.exports = {
  entry: './path/to/my/entry/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'output.bundle.js'
  }
};

1.3 Loaders

Loader in webpack chuyển đổi những file .css, .html, .jpg,.. đến những modules mà chúng được thêm trong đồ thị sự phụ thuộc. Chúng có 2 mục đích chính trong config:

  • Xác định những file nào nên được chuyển đổi bởi loader nào.
  • Chuyển đổi file đó để làm sao nó có thể thêm vào đồ thị sự phụ thuộc.

webpack.config.js

const config = {
  entry: './path/to/my/entry/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'output.bundle.js'
  },
  module: {
    rules: [
      {test: /.(js|jsx)$/, use: 'babel-loader'}
    ]
  }
};

1.4 Plugins

Plusgins được sử dụng phổ biến nhất là để thực hiện bước cập nhật, biên tập các modules đã được bundle:

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins

const config = {
  entry: './path/to/my/entry/app.js',
  output: {
    filename: 'output.bundle.js',
    path: './dist'
  },
  module: {
    rules: [
      {test: /.(js|jsx)$/, use: 'babel-loader'}
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

module.exports = config;

2. Sử dụng Webpack trong Angular 2

- Configure Webpack

Tạo thư mục project mới

mkdir angular-webpack
cd    angular-webpack

Thêm một số file sau vào project:

package.json

{
  "name": "angular2-webpack",
  "version": "1.0.0",
  "description": "A webpack starter for Angular",
  "scripts": {
    "start": "webpack-dev-server --inline --progress --port 8080",
    "test": "karma start",
    "build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
  },
  "license": "MIT",
  "dependencies": {
    "@angular/common": "~2.2.0",
    "@angular/compiler": "~2.2.0",
    "@angular/core": "~2.2.0",
    "@angular/forms": "~2.2.0",
    "@angular/http": "~2.2.0",
    "@angular/platform-browser": "~2.2.0",
    "@angular/platform-browser-dynamic": "~2.2.0",
    "@angular/router": "~3.2.0",
    "core-js": "^2.4.1",
    "rxjs": "5.0.0-beta.12",
    "zone.js": "^0.6.25"
  },
  "devDependencies": {
    "@types/node": "^6.0.45",
    "@types/jasmine": "^2.5.35",
    "angular2-template-loader": "^0.4.0",
    "awesome-typescript-loader": "^2.2.4",
    "css-loader": "^0.23.1",
    "extract-text-webpack-plugin": "^1.0.1",
    ...
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  }
}

webpack.config.js

module.exports = require('./config/webpack.dev.js');

config/helpers.js

var path = require('path');
var _root = path.resolve(__dirname, '..');
function root(args) {
  args = Array.prototype.slice.call(arguments, 0);
  return path.join.apply(path, [_root].concat(args));
}
exports.root = root;

config/webpack.common.js

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');

module.exports = {
  entry: {
    'polyfills': './src/polyfills.ts',
    'vendor': './src/vendor.ts',
    'app': './src/main.ts'
  },

  resolve: {
    extensions: [', '.ts', '.js']
  },

  module: {
    loaders: [
      {
        test: /.ts$/,
        loaders: ['awesome-typescript-loader', 'angular2-template-loader']
      },
      {
        test: /.html$/,
        loader: 'html'
      },
      {
        test: /.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
        loader: 'file?name=assets/[name].[hash].[ext]'
      },
      {
        test: /.css$/,
        exclude: helpers.root('src', 'app'),
        loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
      },
      {
        test: /.css$/,
        include: helpers.root('src', 'app'),
        loader: 'raw'
      }
    ]
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ['app', 'vendor', 'polyfills']
    }),

    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
};

- Các common configurations

entry: {
  'polyfills': './src/polyfills.ts',
  'vendor': './src/vendor.ts',
  'app': './src/main.ts'
},

Chúng ta đang chia ứng dụng tới 3 bundlers:

  • polyfills - các polyfills chuẩn chúng ta yêu cầu để chạy ứng dụng Angular trong hầu hết các trình duyệt hiện đại.
  • vendor - các files vendor chúng ta cần: Angular, lodash, bootstrap.css...
  • app - code ứng dụng.
resolve: {
  extensions: [', '.ts', '.js']
},

Điều này nói cho webpack biết để resovle các files có extension match với biểu thức trên. Như vậy bạn có thể sử dụng

import { AppComponent } from './app.component.ts';

Hoặc

import { AppComponent } from './app.component';

Chỉ định loader:

module: {
  loaders: [
    {
      test: /.ts$/,
      loaders: ['awesome-typescript-loader', 'angular2-template-loader']
    },
    {
      test: /.html$/,
      loader: 'html'
    },
    {
      test: /.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
      loader: 'file?name=assets/[name].[hash].[ext]'
    },
    {
      test: /.css$/,
      exclude: helpers.root('src', 'app'),
      loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
    },
    {
      test: /.css$/,
      include: helpers.root('src', 'app'),
      loader: 'raw'
    }
  ]
},

  • awesome-typescript-loader - một loader để transpile code Typescript tới ES5.
  • angular2-template-loader - tải template của component và styles.
  • html - Cho template của component
  • images/fonts - Images và fonts cũng được bundle.
  • css - Cho css file

Thêm plugin

plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    name: ['app', 'vendor', 'polyfills']
  }),

  new HtmlWebpackPlugin({
    template: 'src/index.html'
  })
]

Ngoài ra còn một số config tôi không trình bày ở đây như: Environment, Test configuration.

- Tạo view, component

src/index.html

<!DOCTYPE html>
<html>
  <head>
    <base href="/">
    <title>Angular With Webpack</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="awidth=device-awidth, initial-scale=1">
  </head>
  <body>
    <my-app></my-app>
  </body>
</html>

src/main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app/app.module';
if (process.env.ENV === 'production') {
  enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

src/app/app.component.ts

import { Component } from '@angular/core';
import '../../public/css/styles.css';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent { }

src/app/app.component.html

<main>
  <h1>Hello from Angular App with Webpack</h1>
  <img src="../../public/images/angular.png">
</main>

Các bạn có thể xem code đầy đủ tại link sau:

Angular2-webpack-demo

Điểm nổi bật:

  • Không có thẻ <script> hoặc thẻ <link> trong indexex.html. "HtmlWebpackPlugin" chèn chúng tự động tại runtime.

  • AppComponent trong app.component.ts import css như những module javascript

  • AppComponent có file html và css của riếng nó. Webpack tải chúng với việc gọi reqired(). Chúng ta không hề thấy những công việc này trong source code, chúng được thêm bên trong plugin "angular2-template-loader"

  • Vendor.ts bao gồm những những câu lệnh import các dependency để đưa tới file vendor.js. Ứng dụng cũng import những module này. Như vậy, sẽ có trùng lặp trong file app.js sau khi bundle. "CommonsChunkPlugin" thực hiện việc phát hiện và gỡ bỏ những trùng lặp này.

3. Kết luận

Hy vọng những nội dung trên đây sẽ đóp góp những hiểu biết cơ bản nhất dành cho các bạn về Webpack và sử dụng nó trong Angular 2. Chúc các bạn thành công.

Link tham khảo:

  • Webpack introduction
  • Webpack concept
0