PJCHENder 未整理筆記

[Unit5] Webpack deployment (middleware)

2017-09-26

[Unit5] Webpack deployment (middleware)

@([Udemy] Webpack 2: The Complete Developer’s Guide)

static assets

aws s3

在根目錄建立一個 .env 檔案:

1
2
AWS_ACCESS_KEY_ID= <access_key_id>
AWS_SECRETE_ACCESS_KEY= <secrete_access_key>

定義環境變數

我們可以使用另一個 plugin 來定義環境變數,它的值會來自 package.json 的 Scripts

1
2
3
4
5
6
7
8
// webpack.config.js

// 定義 global variable,它的值是來自 Package.json 中的 Scripts
plugins:[
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
]

package.json 中:

  • build 指令,指令 NODE_ENV 為 production。
  • webpack -p 則是告訴 webpack 我們這是 production mode,因此它會幫我們把所有的 js 檔壓縮合併。
  • deploy 則是可以只將 dist 的內容上傳到 github pages
1
2
3
4
5
6
"scripts": {
"clean": "rimraf dist",
"build": "NODE_ENV=production npm run clean && webpack -p",
"serve": "webpack-dev-server",
"deploy": "npm run build && git subtree push --prefix <folder> origin <branch>"
}

deployment for server

在發佈到 server 時,測試環境和正式環境的差異是,測試環境只要每次連 request 的時候都會透過 webpack-dev-middleware 重新執行 webpack;但在正式環境則是只會會先把要用的檔案打包壓縮好,不會每次都重新執行 webpack。

首先我們要安裝 webpack-dev-middleware

webpack middleware for development

img

我們需要在我們的 server.js 中載入 webpackMiddleware, webpackwebpackConfig

接著在 middleware 去使用它:

1
app.use(webpackMiddleware(webpack(webpackConfig)))
1
2
3
4
5
6
7
8
9
10
11
12
// server.js
const express = require('express')
const webpackMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const webpackConfig = require('./webpack.config')
const app = express()

app.use(webpackMiddleware(webpack(webpackConfig)))

app.listen(3050, () => {
console.log('Listening')
})

webpack in production environment

img

在正式環境中,在使用者發出 request 時,我們不要每次都重啟 webpack ,而是讓它們直接連到已經打包好的檔案。因此我們可以用 process.env.NODE_ENV 這個變數來做判斷,當 NODE_ENV 是 production(這個值可能隨著發佈的環境不同而有不同)。

另外,在 port 的部分,多數的部屬空間不會讓我們自訂 Port ,因此我們可以使用環境變數提供的 port,process.env.PORT || 3050 ,如果 process.env.PORT 不存在的話,再使用我們預設的 port。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// server.js

const express = require('express')
const path = require('path')

const app = express()

// Server Routes ...
// 很重要的是其他的路由都要寫在 webpack-dev-middleware 前面

// process.env.NODE_ENV 這個變數的值可能會隨著發佈的環境不同而有不同
if(process.env.NODE_ENV !== 'production') {
// 不是正式環境的話每次都要重新執行 webpack
const webpackMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const webpackConfig = require('./webpack.config')
app.use(webpackMiddleware(webpack(webpackConfig)))
} else {
app.use(express.static('dist')) // 告訴 express 這個 dist 資料夾是任何人都可以使用的
app.get('*', (req, res) =>{
res.sendFile(path.join(__dirname, 'dist/index.html')) // 到任何路由時都會載入 index.html 這支
})
}

// 由於多數的部屬空間不會讓你自訂 port,因此我們讓 port 由環境空間決定
app.listen(process.env.PORT || 3050, () => {
console.log('Listening')
})

deploy to heroku

在發佈到 heroku 前,我們要在根目錄新增一支 .Profile,在這支檔案中要告訴 heroku 要執行哪個 npm 指令:

1
2
// .Profile
web: node server.js

檔案內容

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
"name": "upstar_music",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"clean": "rimraf dist",
"build": "NODE_ENV=production npm run clean && webpack -p",
"serve": "webpack-dev-server",
"deploy": "npm run build && git subtree push --prefix dist origin gh-pages"
},
"repository": "https://github.com/StephenGrider/WebpackProject",
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.15.2",
"faker": "^3.1.0",
"lodash": "^4.17.2",
"react": "15.4.1",
"react-dom": "15.4.1",
"react-input-range": "^0.9.2",
"react-redux": "^4.4.6",
"react-router": "^3.0.0",
"redux": "^3.6.0",
"redux-form": "^6.3.2",
"redux-thunk": "^2.1.0"
},
"devDependencies": {
"babel-core": "^6.17.0",
"babel-loader": "^6.2.0",
"babel-preset-env": "^1.1.4",
"babel-preset-react": "^6.16.0",
"css-loader": "^0.26.1",
"html-webpack-plugin": "^2.28.0",
"rimraf": "^2.6.1",
"style-loader": "^0.13.1",
"webpack": "2.2.0-rc.0",
"webpack-dev-middleware": "^1.10.2",
"webpack-dev-server": "^2.2.0-rc.0"
}
}

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*eslint no-unused-vars: "webpack"*/

var webpack = require('webpack')
var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin')

// 放入 package.json 中所有 dependency 的 module
const VENDOR_LIBS = ['faker', 'lodash', 'react', 'react-dom', 'react-input-range',
'react-redux', 'react-router', 'redux', 'redux-form', 'redux-thunk']

module.exports = {
// entry: './src/index.js',
entry: {
bundle: './src/index.js', // 我們要根據 index.js 產一支 bundle.js
vendor: VENDOR_LIBS // 產生 vendor.js
},
output: {
path: path.join(__dirname, 'dist'),
// 使用 [chunkhash] 的方式,來讓瀏覽器知道是否需要重新載入檔案
filename: '[name].[chunkhash].js' // [name] 會被 entry 中的 key 替換調
},
module: {
rules: [
{
use: 'babel-loader',
test: /\.js$/,
exclude: /node_module/
},
{
use: ['style-loader', 'css-loader'],
test: /\.css$/
}
]
},
plugins: [
// 避免 vendor 內的程式碼同時出現在 vendor.js 和 bundle.js 中
new webpack.optimize.CommonsChunkPlugin({
// webpack 不知道 vendor 是否有更新,所以我們多加一個 'manifest'
name: ['vendor', 'manifest']
}),

// 幫我們把 dist 中的 js 檔注入 html 當中
new HtmlWebpackPlugin({
template: './src/index.html' // 以 index.html 這支檔案當作模版注入 html
}),

// 定義 global variable,它的值是來自 Package.json 中的 Scripts
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
]
}

.Profile

1
web: node server.js

server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const express = require('express')
const path = require('path')

const app = express()

// Server Routes ...
// 很重要的是其他的路由都要寫在 webpack-dev-middleware 前面

// process.env.NODE_ENV 這個變數的值可能會隨著發佈的環境不同而有不同
if(process.env.NODE_ENV !== 'production') {
// 不是正式環境的話每次都要重新執行 webpack
const webpackMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const webpackConfig = require('./webpack.config')
app.use(webpackMiddleware(webpack(webpackConfig)))
} else {
app.use(express.static('dist')) // 告訴 express 這個 dist 資料夾是任何人都可以使用的
app.get('*', (req, res) =>{
res.sendFile(path.join(__dirname, 'dist/index.html')) // 到任何路由時都會載入 index.html 這支
})
}

// 由於多數的部屬空間不會讓你自訂 port,因此我們讓 port 由環境空間決定
app.listen(process.env.PORT || 3050, () => {
console.log('Listening')
})

.env

1
2
AWS_ACCESS_KEY_ID= <access_key_id>
AWS_SECRETE_ACCESS_KEY= <secrete_access_key>

掃描二維條碼,分享此文章