Browserify + Gulp + Babelify

一看就懂的 React 開發環境建置與 Webpack 入門教學

在進入第二種方法前,首先先介紹一下會用到 BrowserifyGulpBabelify 三種前端開發常會用到的工具:

Browserify

  • 如同官網上說明的:Browserify lets you require('modules') in the browser by bundling up all of your dependencies.,Browserify 是一個可以讓你在瀏覽器端也能使用像 Node 用的 CommonJS 規範一樣,用輸出(export)和引用(require)來管理模組。此外,也能使用許多在 NPM 中的模組

Gulp

  • Gulp 是一個前端任務工具自動化管理工具。隨著前端工程的發展(Task Runner),我們在開發前端應用程式時有許多工作是必須重複進行,例如:打包文件、uglify、將 LESS 轉譯成一般的 CSS 的檔案,轉譯 ES6 語法等工作。若是使用一般手動的方式,往往會造成效率的低下,所以透過像是 Grunt、Gulp 這類的 Task Runner 不但可以提昇效率,也可以更方便管理這些任務。由於 Gulp 是透過 pipeline 方式來處理檔案,在使用上比起 Grunt 的方式直觀許多,所以這邊我們主要討論的是 Gulp

Babelify

  • Babelify 是一個使用 Browserify 進行 Babel 轉換的外掛,你可以想成是一個翻譯機,可以將 React 中的 JSXES6 語法轉成瀏覽器相容的 ES5 語法

初步了解了三種工具的概念後,接下來我們就開始我們的環境設置:

  1. 若是電腦中尚未安裝 Node(Node.js 是一個開放原始碼、跨平台的、可用於伺服器端和網路應用的 Google V8 引擎執行執行環境)和 NPM(Node 套件管理器 Node Package Manager。是一個以 JavaScript 編寫的軟體套件管理系統,預設環境為 Node.js,從 Node.js 0.6.3 版本開始,npm 被自動附帶在安裝包中)的話,請先 上官網安裝

  2. npm 安裝 browserify

  3. npm 安裝 gulpgulp-concatgulp-html-replacegulp-streamifygulp-uglifywatchifyvinyl-source-stream 開發環境用的套件(development dependencies)

    1. // 使用 npm install --save-dev 會將安裝的套件名稱和版本存放到 package.json 的 devDependencies 欄位中
    2. $ npm install --save-dev gulp gulp-concat gulp-html-replace gulp-streamify gulp-uglify watchify vinyl-source-stream
  4. 安裝 babelifybabel-preset-es2015babel-preset-react,轉譯 ES6JSX 開發環境用的套件,並於根目錄底下設定 .babelrc,設定轉譯規則(presets:es2015、react)和使用的外掛

    1. // 使用 npm install --save-dev 會將安裝的套件名稱和版本存放到 package.json 的 devDependencies 欄位中
    2. $ npm install --save-dev babelify babel-preset-es2015 babel-preset-react
    1. // filename: .babelrc
    2. {
    3. "presets": [
    4. "es2015",
    5. "react",
    6. ],
    7. "plugins": []
    8. }
  5. 安裝 react 和 react-dom

    1. $ npm install --save react react-dom
  6. 撰寫 Component

  1. // filename: ./app/index.js
  2. import React from 'react';
  3. import ReactDOM from 'react-dom';
  4. class App extends React.Component {
  5. constructor(props) {
  6. super(props);
  7. this.state = {
  8. };
  9. }
  10. render() {
  11. return (
  12. <div>
  13. <h1>Hello, World!</h1>
  14. </div>
  15. );
  16. }
  17. }
  18. ReactDOM.render(<App />, document.getElementById('app'));
  1. <!-- filename: ./index.html -->
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>Hello React!</title>
  7. </head>
  8. <body>
  9. <div id="app"></div>
  10. <!-- build:js -->
  11. <script src="./dist/src/bundle.js"></script>
  12. <!-- endbuild -->
  13. </body>
  14. </html>
  1. 設定 gulpfile.js
  1. // filename: gulpfile.js
  2. // 引入所有需要的檔案
  3. const gulp = require('gulp');
  4. const uglify = require('gulp-uglify');
  5. const htmlreplace = require('gulp-html-replace');
  6. const source = require('vinyl-source-stream');
  7. const browserify = require('browserify');
  8. const watchify = require('watchify');
  9. const babel = require('babelify');
  10. const streamify = require('gulp-streamify');
  11. // 檔案位置參數
  12. const path = {
  13. HTML: 'index.html',
  14. MINIFIED_OUT: 'bundle.min.js',
  15. OUT: 'bundle.js',
  16. DEST: 'dist',
  17. DEST_BUILD: 'dist/build',
  18. DEST_SRC: 'dist/src',
  19. ENTRY_POINT: './app/index.js'
  20. };
  21. // 複製 html 到 dist 資料夾中
  22. gulp.task('copy', function(){
  23. gulp.src(path.HTML)
  24. .pipe(gulp.dest(path.DEST));
  25. });
  26. // 監聽檔案是否有變化,若有變化則重新編譯一次
  27. gulp.task('watch', function() {
  28. gulp.watch(path.HTML, ['copy']);
  29. var watcher = watchify(browserify({
  30. entries: [path.ENTRY_POINT],
  31. transform: [babel],
  32. debug: true,
  33. }));
  34. return watcher.on('update', function () {
  35. watcher.bundle()
  36. .pipe(source(path.OUT))
  37. .pipe(gulp.dest(path.DEST_SRC))
  38. console.log('Updated');
  39. })
  40. .bundle()
  41. .pipe(source(path.OUT))
  42. .pipe(gulp.dest(path.DEST_SRC));
  43. });
  44. // 執行 build production 的流程(包括 uglify、轉譯等)
  45. gulp.task('copy', function(){
  46. browserify({
  47. entries: [path.ENTRY_POINT],
  48. transform: [babel],
  49. })
  50. .bundle()
  51. .pipe(source(path.MINIFIED_OUT))
  52. .pipe(streamify(uglify(path.MINIFIED_OUT)))
  53. .pipe(gulp.dest(path.DEST_BUILD));
  54. });
  55. // 將 script 引用換成 production 的檔案
  56. gulp.task('replaceHTML', function(){
  57. gulp.src(path.HTML)
  58. .pipe(htmlreplace({
  59. 'js': 'build/' + path.MINIFIED_OUT
  60. }))
  61. .pipe(gulp.dest(path.DEST));
  62. });
  63. // 設定 NODE_ENV 為 production
  64. gulp.task('apply-prod-environment', function() {
  65. process.env.NODE_ENV = 'production';
  66. });
  67. // 若直接執行 gulp 會執行 gulp default 的任務:watch、copy。若跑 gulp production,則會執行 build、replaceHTML、apply-prod-environment
  68. gulp.task('production', ['build', 'replaceHTML', 'apply-prod-environment']);
  69. gulp.task('default', ['watch', 'copy']);
  1. 成果展示
    到目前為止我們的資料夾的結構應該會是這樣:

一看就懂的 React 開發環境建置與 Webpack 入門教學

接下來我們透過在終端機(terminal)下 gulp 指令來處理我們設定好的任務:

  1. // 當只有輸入 gulp 沒有輸入任務名稱時,gulp 會自動執行 default 的任務,我們這邊會執行 `watch` 和 `copy` 的任務,前者會監聽 `./app/index.js` 是否有改變,有的話則更新。後者則是會把 `index.html` 複製到 `./dist/index.html`
  2. $ gulp

當執行完 gulp 後,我們可以發現多了一個 dist 資料夾

一看就懂的 React 開發環境建置與 Webpack 入門教學

如果我們是要進行 production 的應用程式開發的話,我們可以執行:

// 當輸入 gulp production 時,gulp 會執行 production 的任務,我們這邊會執行 replaceHTMLbuildapply-prod-environment 的任務,build 任務會進行轉譯和 uglifyreplaceHTML 會取代 index.html 註解中的 <script> 引入檔案,變成引入壓縮和 uglify 後的 ./dist/build/bundle.min.jsapply-prod-environment 則是會更改 NODE_ENV 變數,讓環境設定改為 production,有興趣的讀者可以參考React 官網說明

  1. $ gulp production

此時我們可以在瀏覽器上打開我們的 ./dist/hello.html,就可以看到 Hello, world! 了!

(image via srinisoundarsitepointkeyholesoftwaresurvivejs)

| 勘誤、提問或許願 |