tpai
6/21/2016 - 7:29 AM

the webpack settings you should know when developing isomorphic app

the webpack settings you should know when developing isomorphic app

# Isomorphic App 開發要注意的 Webpack 設定

![](http://i.giphy.com/l46Cm2w1aLH8rWHG8.gif)

## 前情提要

[鍵盤發電機](http://github.com/tpai/keyboard-generator)是當初與 [@steveice](http://github.com/steveice) 和 [@vansteki](http://github.com/vansteki) 參加[百度黑客松 2013](http://tpai.github.io/2013/07/29/baidu-hackathon-at-clbc/) 時的作品,後端技術有 Node.js & Express & Socket.io,前端技術則是 jQuery,現在要對這個專案進行重構。

## 前端後端設定大不同

一開始,我很天真的拿之前寫好的 Webpack 設定檔直接套到 Backend 上開發,想當然爾就炸裂了 XD

跟前端設定不一樣的地方:

* 不打包內建模組,如:`fs` 或 `path`。
* 不打包 `node_modules/` 下的模組。
* 須告知 Webpack 引入 node 變數,如:`__dirname` 或 `__filename`。

[webpack.config.server.js:13-19](https://github.com/tpai/keyboard-generator/blob/master/webpack.config.server.js#L13,L19)

```
var nodeModules = {};
fs.readdirSync('node_modules')
    .filter(function(x) {
        return ['.bin'].indexOf(x) === -1;
    })
    .forEach(function(mod) {
        nodeModules[mod] = 'commonjs ' + mod;
    });

module.exports = {
    target: "node",
    externals: nodeModules,
    node: {
      __dirname: true
    },
    entry: ["server"],
    ...
};
```

另外值得一提的是 Entry Point 為何使用 Array 引入,因為原本的寫法是:

```
entry: {
    "server": "server"
    // input: ./src/server.js
    // output: ./server.js
},
resolve: {
    root: [ path.resolve("./src") ],
    extensions: ["", ".js"]
},
```

但是這樣會造成 `Error: a dependency to an entry point is not allowed` 的錯誤發生,而我到 Webpack Github Issue 也只找到 technical reasons 的回覆,建議用 Array 處理掉這樣。(文末參考連結有該 Issue 討論串連結)

## 開發與部署環境切換

前端需要注意的主要是 Socket Server URL 在 Development 和 Production 的環境下要切換,所以我用了 [DefinePlugin](https://webpack.github.io/docs/list-of-plugins.html#defineplugin) 去定義全域變數來藉此判別現在的環境去打包 Front-End Code。

[webpack.config.server.js:37](https://github.com/tpai/keyboard-generator/blob/master/webpack.config.client.js#L37)

```
plugins: [
    new webpack.DefinePlugin({
        __DEVELOPMENT__: JSON.stringify(JSON.parse(process.env.NODE_ENV === 'development'))
    })
]
```

`NODE_ENV` 的設定部份推薦使用 [cross-env](https://www.npmjs.com/package/cross-env) 去實作。

[package.json:7-12](https://github.com/tpai/keyboard-generator/blob/master/package.json#L7,L12)

```
"scripts": {
    "watch": "cross-env NODE_ENV=development npm run ...",
    "build": "cross-env NODE_ENV=production npm run ..."
}
```

## 結論

比起之前一坨混在一起的 [Client](https://github.com/tpai/keyboard-generator/blob/5a313bc60e68e339ca133442fe0d9427f141fc20/www/js/socket.handler.js) 和 [Server](https://github.com/tpai/keyboard-generator/blob/5a313bc60e68e339ca133442fe0d9427f141fc20/app.js) 真的舒服太多了,這就是所謂重構的成就感吧 XD

雖然大多時間都在弄環境以及處理莫名噴出來的 Issue,其實架構 Webpack 並不難,難的是要得心應手地使用這把武器,如果對 Webpack 還不是很熟,可以參考 [Webpack - The Confusing Parts](https://medium.com/@rajaraodv/webpack-the-confusing-parts-58712f8fcad9#.tzqd6lo76) 這篇文章。

> Refs:f
>
> * [Backend Apps With Webpack](http://jlongster.com/Backend-Apps-with-Webpack--Part-I)
> * [The need for multiple output paths?](https://github.com/webpack/webpack/issues/1189#issuecomment-156576084)
> * [Error: a dependency to an entry point is not allowed](https://github.com/webpack/webpack/issues/300#issuecomment-45313650)
> * [__dirname returns '/' when js file is built with webpack](https://github.com/webpack/webpack/issues/1599#issuecomment-186841345)