Web Framework
  • Introduction
  • Chapter1: Build Front-End Develope environment
    • 1.1.Install Sublime Text3 and packages
    • 1.2.Sublime plugin
      • 1.2.1.Package install control
      • 1.2.2.Emmet
      • 1.2.3.HTML-CSS-JS Prettify
      • 1.2.4 .SublimeLinter
      • 1.2.5.Install Sublime text Build system
      • 1.2.6.Pretty JSON
      • 1.2.7.SublimeHighlight
      • 1.2.8.SublimeAStyleFormatter
  • Chapter2: Angular.js
  • Chapter3: 3rd Party API
    • 3.function
  • Chapter4: React.js
    • 4.1.Introduction
    • 4.2.Getting started
    • 4.3.JSX
    • 4.4.life cycle
    • 4.5.Data flow: prop vs. prop.children, state
    • 4.6. Layout, Event Handing
    • 4.7.Using JQuery in React
    • 4.8.Using D3.js in React
    • 4.9.Dynamic Routing
  • Chapter5: Webpack
  • Chapter6: web driver IO & mocha
  • Chapter7: RWD
    • 7.1.利用Sass & media query在不使用框架下完成RWD
  • Chapter8: React native
Powered by GitBook
On this page
  • This section contain the following items:
  • 1.Webpack Howto
  • 2.Webpack.config.js
  • 2.1 設定不同的環境變數: test, build(production)
  • 2.2 Export config
  • 3.Webpack preloaders, loaders, postloaders
  • 4.Webpack Plugins
  • 5.3 Directive and Webpack
  • 6.Webpack & React Hot Reloader

Was this helpful?

Chapter5: Webpack

Previous4.9.Dynamic RoutingNextChapter6: web driver IO & mocha

Last updated 5 years ago

Was this helpful?

This section contain the following items:

  • 1.Webpack Howto

  • 2.Webpack.config.js

  • 3.Webpack preloaders, loaders, postloaders

  • 4.Webpack Plugins

  • 5.Webpack & Angular

  • 6.Webpack & React Hot Reloader

1.Webpack Howto

以下參考自:

1.What is webpack?

    (1)可做到bowswerify可做到的, 但是可以分散封裝專案使用的程式碼,使載入頁面時只需載入當頁所需的程式碼以加速載入速度,以下兩行指令的效果是相同的:
        browserify main.js > bundle.js
        webpack main.js bundle.js
    Webpack 提供比 browserify 更多的功能,一般來說會創建一個名為 webpack.config.js 的檔案來做集中管理:
        // webpack.config.js
        module.exports = {
          entry: './main.js',
          output: {
            filename: 'bundle.js'       
          }
        };

    (2)用來取代grunt&gulp, 因為webpack可以build跟bundle css, preprocess css, compile-to-JS languages and images, among other things.

    (3)其他還有:
        ->可同時整合 CommonJS 和 AMD 模組
        ->轉換 JSX, Coffee Script, TypeScript 等
        ->整合樣式表 (css, sass, less 等)
        ->建置 production-ready 的程式碼 (壓縮)

2.How to invoke webpack?

      切換到有 webpack.config.js 檔案的目錄下並執行:
      webpack : 會在開發模式下開始一次性的建置
      webpack -p : 會建置 production-ready 的程式碼 (壓縮)
      webpack --watch : 會在開發模式下因應程式碼的變換持續更新建置 (快速!)
      webpack -d : 加入 source maps 檔案

3.Compile-to-JS languages:

       webpack與browserify transforms及RequireJS pluguns相同作用的是loaders, 以下示範如何使用webpackload Coffeescript, 以及Facebook的JSX+ES6 (必須先npm install babel-loader coffee-loader,npm install babel-core babel-preset-es2015 babel-preset-react):
      (1)先建置webpack.config.js, 並調整loaders的內容:
          // webpack.config.js
          module.exports = {
          entry: './main.js',
          output: {
            filename: 'bundle.js'       
          },
          module: {
            loaders: [
              { test: /\.coffee$/, loader: 'coffee-loader' },
              {
                test: /\.js$/,
                loader: 'babel-loader',
                query: {
                  presets: ['es2015', 'react']
                }
              }
            ]
          }
        };

       (2)如果需要require檔案而不指定特定的副檔名, 可以加入resolve.extensions:
          // webpack.config.js
          module.exports = {
            entry: './main.js',
            output: {
              filename: 'bundle.js'       
            },
            module: {
              loaders: [
                { test: /\.coffee$/, loader: 'coffee-loader' },
                {
                  test: /\.js$/,
                  loader: 'babel-loader',
                  query: {
                    presets: ['es2015', 'react']
                  }
                }
              ]
            },
            resolve: {
              // you can now require('file') instead of require('file.coffee')
              extensions: ['', '.js', '.json', '.coffee'] 
            }
        };

4.Stylesheets and images:

      (1)首先更新你的程式碼去require你的static assets:
          require('./bootstrap.css');
          require('./myapp.less');

          var img = document.createElement('img');
          img.src = require('./glyph.png');

     (2)當require css(或less等)時, webpack中CSS是可以被require()的,webpack 會自動生成一個 <style> 標籤並加入到 html的<head>中; 當require image, webpack將URL放到bundle中, 被require時再return回來, 但是必須給他指定loaders:
        // webpack.config.js
        module.exports = {
          entry: './main.js',
          output: {
            path: './build', // This is where images AND js will go
            publicPath: 'http://mycdn.com/', // This is used to generate URLs to e.g. images
            filename: 'bundle.js'
        },
        module: {
          loaders: [
            { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
            { test: /\.css$/, loader: 'style-loader!css-loader' },
            { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } // inline base64 URLs for <=8k images, direct URLs for the rest
          ]
        }
       };

5.Feature flags (特定功能標籤)

    (1)當想要在特定環境(dev)下,可先在code中定義全域變數
        if (__DEV__) {
          console.warn('Extra logging');
        }
        // ...
        if (__PRERELEASE__) {
          showSecretFeature();
        }
    (2)然後在webpack.config.js中定義不同環境時的行為
        // webpack.config.js
        // definePlugin 接收字串,因此你也可以直接寫入字串而不使用 JSON.stringify()
        var definePlugin = new webpack.DefinePlugin({
          __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')),
          __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))
        });

        module.exports = {
          entry: './main.js',
          output: {
            filename: 'bundle.js'       
          },
          plugins: [definePlugin]
        };
    (3)設定完成後, 可以使用 BUILD_DEV=1 BUILD_PRERELEASE=1 webpack 來完成建置。調整指令的參數即可模擬各狀況下的建置。特別注意的是, webpack -p在壓縮時會將沒用到的程式碼刪除, 因此不用擔心會有不該出現的程式碼被加入到最終產出的程式碼內。

6.Multiple entrypoints (分批進入)

(1)如果你有profile page 及 feed page, 如果使用者只想要profile而不希望下載feed的code, 可以建置不同的bundle:
  // webpack.config.js
  module.exports = {
    entry: {
      Profile: './profile.js',
      Feed: './feed.js'
    },
    output: {
      path: 'build',
      filename: '[name].js' // Template based on keys in entry above
    }
  };

(2)For profile, insert <script src="build/Profile.js"></script> into your page. Do a similar thing for feed.

7.Optimizing common code

  (1)Feed與Profile間有許多共同的code, webpack分析出共通處並額外整合成一組在頁面間可快取的共用包:
    // webpack.config.js
    var webpack = require('webpack');
    var commonsPlugin =new webpack.optimize.CommonsChunkPlugin('common.js');
    module.exports = {
      entry: {
        Profile: './profile.js',
        Feed: './feed.js'
      },
      output: {
        path: 'build',
        filename: '[name].js'
        // [name] 會依據上面 entry 的屬性名稱變動
      },
      plugins: [commonsPlugin]
    };
  (2)Add <script src="build/common.js"></script> before the script tag you added in the previous step and enjoy the free caching.

8.Async loading

2.Webpack.config.js

  以下以https://github.com/preboot/angular-webpack中的範例來分析:

2.1 設定不同的環境變數: test, build(production)

  利用定義NODE_ENV變數, 此部份可參考npm life cycle (https://docs.npmjs.com/misc/scripts#current-lifecycle-event)來區分dev時或是production時應該做什麼事,例如在命令列下輸入:

  1.npm run build: run build scripts
  2.npm run test: run test server
  3.npm run start: run production server


 設定可以在package.json中設定:
 {
    "scripts": {
      "build": "rimraf dist && webpack --bail --progress --profile",
      "server": "webpack-dev-server --history-api-fallback --inline --progress",
      "test": "karma start",
      "test-watch": "karma start --auto-watch --no-single-run",
      "start": "npm run server"
    }
 }

  行為則在Webpack.config.js中定義:
  取得環境變數
    var ENV = process.env.npm_lifecycle_event;
    var isTest = ENV === 'test' || ENV === 'test-watch';
    var isProd = ENV === 'build';

reference:

2.2 Export config

  module.exports = function makeWebpackConfig () {
    var config = {};
    return config;
  }();

  config的內容大致如下:

  1.entry: webpack的進入點:
    example: 設定進入點為./src/app/app.js
      config.entry = isTest ? {} : {
        app: './src/app/app.js'
      };
  2.output: 設定打包後的輸出檔
       example:
        config.output = isTest ? {} : {
         // 輸出檔的輸出路徑
         path: __dirname + '/dist',

         // require()時參考的路徑,例如圖片的路徑
         // 在dev環境下, 使用webpack-dev-server 
         publicPath: isProd ? '/' : 'http://localhost:8080/',

         // 輸出的bundle檔名
         // build mode下加入hash
         filename: isProd ? '[name].[hash].js' : '[name].bundle.js',

         // 未被列在entry裡, 卻又希望被輸出的budle
         // build mode下加入hash
         chunkFilename: isProd ? '[name].[hash].js' : '[name].bundle.js'
       };
  3.devtool
  4.module:
        preloaders, loaders, postloaders
        example:
          preloaders: 圖片壓縮 
          loaders:配置文件, 如coffie-script轉換成js 
          postloaders: 代碼覆蓋率測試等
  5.plugins
  6.devServer: 
        contentBase: 啟動webpack-dev-server的路徑
        example:
          config.devServer = {
            contentBase: './src/public',
            stats: 'minimal'
          };

3.Webpack preloaders, loaders, postloaders

1.preloaders,

2.loaders

    1.babel loader
    2.css loader
    3.file loader
    4.raw-loader
    5.ngtemplate-loader: https://github.com/WearyMonkey/ngtemplate-loader
    6.raw-loader: https://github.com/webpack/raw-loader

4.Webpack Plugins

  • Extract Text Plugin:

    • webpack中CSS是可以被require()的,webpack 會自動生成一個style標籤並加入到 html的head中

    • ExtractTextPlugin可輸出打包成一包的css, 一般來說會用在production的時候

      example:
         new ExtractTextPlugin('[name].[hash].css', {disable: !isProd})
  • HtmlWebpackPlugin: 可以動態組成index.html

      example:
        new HtmlWebpackPlugin({
          template: './src/public/index.html',
          inject: 'body'
        })
        template: 動態產生檔案所存放的位置
    • CopyWebpackPlugin:

      copies individual files or entire directories to the build directory.

    • NoErrorsPlugin

    • DedupePlugin

    • UglifyJsPlugin

      5.Webpack & Angular

      5.1 Environment

      5.2 Webpack.config.js

      (1)Add 2 things into webpack.config.js: 'use strict'; var webpack = require('webpack'); (2)Add 3 thing in module.exports = {}: (2.1) Entry: 告訴webpack打包的進入點 (2.2) Output: filename: 指定webpack打包後所產出的檔案名稱 path: 指定webpack打包後所產出的檔案的路徑 (2.3) Module: loaders: 指定Loader的種類 (3)Add loaders to webpack.config.js (4)Example: var webpack = require('webpack');

      module.exports = {
        entry: [
          './Angular/app.js'
        ],
        output: {
          path: __dirname + '/public',
          filename: 'bundle.js'
        },
        module: {
        loaders: [
            { test: /\.js$/, loader: 'babel', exclude: [/node_modules/] },
            { test: /\.html$/, loader: 'html-loader', exclude: [/node_modules/] },
            { test: /\.css$/, loader: "style!css", exclude: [/node_modules/] },
            { test: /\.scss$/, loader: "style!css!sass", exclude: [/node_modules/] }
          ]
        }
      };

5.3 Directive and Webpack

6.Webpack & React Hot Reloader

  • 1.Introduction

    • 雖然使用webpack有很多優點, 但比較不便利的地方是更新完必須重新build bundle.js, 再重新啟動node.js, 重新reload, 造成開發上的不方便.

    • Hot Reloader的目的在於一旦有更新檔案, Webpack可以自己重build, Browser可接到訊息並reload.

    • 要讓Webpack做到Hot Reload有兩種方式:

      • 一種是使用webpack-dev-server, 可以提供獨立的standalone server.

      • 另一種則是使用webpack提供給express整合的middleware: webpack-dev-middleware及webpack-hot-middleware, 本段所記錄的是使用這種方式.

  • 2.Install packages

    • 需要的套件有webpack, webpack-dev-middleware, webpack-hot-middleware, react-hot-loader, react, babel-loader:

        npm install webpack webpack-dev-middleware, webpack-hot-middleware react-hot-loader react babel-loader
      • 1.react, babel-loader便不多作解釋

      • 2.webpack-dev-middleware, webpack-hot-middleware:讓我們用 express 客製一個有熱替換功能的 webpack 開發伺服器

      • 3.react-hot-loader:可以在不改變 React 元件的 state 下,將更改過程式碼的元件直接更新到畫面上

  • 3.Webpack.config.js

    • 要修改的地方有五處: cache, entry, output, plugins, module

      • 1.cache: enable cache

            cache: true,
      • 2.entry: 加入'webpack-hot-middleware/client', 'webpack/hot/dev-server

             entry: [
             './js/mainentry.js', 'webpack-hot-middleware/client', 'webpack/hot/dev-server'
             ], 
      • 3.output: 加入publicPath

            var publicPath = 'http://localhost:3000/assets';
            output: {
             path: __dirname + '/assets',
             publicPath: publicPath,
             filename: 'mainbundle.js'
         },
      • 4.plugins: 修改如下:

        plugins: [
             new webpack.optimize.OccurenceOrderPlugin(),
             new webpack.HotModuleReplacementPlugin(),
             new webpack.NoErrorsPlugin()
             ],
      • 5.module:

            module: {
            loaders: [{
              test: /\.jsx?$/, // Match both .js and .jsx files
              loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react']
              }]
             }
  • 4.index.dev.js

    • Create一個index.dev.js: 使用webpack-dev-middleware及webpack-hot-middleware,以及導入webpack.config.js

           var webpack = require('webpack'),
            webpackDevMiddleware = require('webpack-dev-middleware'),
            webpackHotMiddleware = require('webpack-hot-middleware'),
            webpackDevConfig = require('./webpack.config.js');
      
            var compiler = webpack(webpackDevConfig);
      
            function useWebpackMiddleware(app) {
                app.use(webpackDevMiddleware(compiler, {
              // public path should be the same with webpack config
                publicPath: webpackDevConfig[0].output.publicPath,
                noInfo: true,
                hot: true,
                inline: true,
                stats: {
                colors: true
                }
                }));
                app.use(webpackHotMiddleware(compiler, {
                    log: console.log
                }));
                return app;
              }
      
            module.exports = {
              useWebpackMiddleware: useWebpackMiddleware
            };
  • 5.app.js

    • 目的是分為develope版本及production版本, 當環境變數不是production時,就會切到index.dev.js

          var webpackDevHelper = require('./index.dev.js');
          if (process.env.NODE_ENV !== 'production') {
              console.log('DEVOLOPMENT ENVIRONMENT: Turning on WebPack Middleware...');
              webpackDevHelper.useWebpackMiddleware(app);
          } else {
              console.log('PRODUCTION ENVIRONMENT');
          }
  • 6.package.json

    • 在package.json的scripts中放入dev, production兩個script

      • production: 將環境變數改為production, 並執行node.js

      • dev: 如果mainbundle.js存在則刪除, 並執行node.js

        "scripts": {
          "production": "export NODE_ENV=production && webpack && node app.js",
          "dev": "rm -f ./assets/mainbundle.js && node app.js"
        },
    • 執行dev: npm run dev

    • 執行production:

      npm run production
  • 7.index.html

    • 記得埋入script source

        <script src="./assets/mainbundle.js"></script>

reference: 1. 2.

1.

2.

Using loaders:

https://github.com/petehunt/webpack-howto
https://github.com/petehunt/webpack-howto
https://rhadow.github.io/2015/03/23/webpackIntro/
https://medium.com/@brianhan/use-this-npm-variable-as-a-flag-for-your-build-scripts-31069f5e2e57#.ae34blmqt
https://docs.npmjs.com/misc/scripts#current-lifecycle-event
http://webpack.github.io/docs/using-loaders.html