部署React+Redux Web App
前段時間使用React+Redux做了個後臺管理的專案,在React初體驗中分享了下入門經驗。這篇文章談談我的部署實踐。
目標
怎樣才是好的部署呢?我覺至少有以下2點:
- 優化效能:包括程式碼執行速度、頁面載入時間
- 自動化:重複的事情儘量讓機器完成,最好能執行一條命令就完成部署
程式碼層面
首先從程式碼層面來分析。
使用React+Redux,往往會用到其強大的除錯工具 Redux DevTools 。在 手動配置DevTools 時需要圍繞Store、Component進行一些配置。然而這些都是用來方便除錯的,生產環境下我們不希望加入這些東西,所以建議就是從程式碼上隔離development和production環境:
containers/
Root.js
Root.dev.js
Root.prod.js
...
store/
index.js
store.dev.js
store.prod.js
同時採用單獨的入口檔案(比如上面的 containers/Root.js
)按需載入不同環境的程式碼:
if (process.env.NODE_ENV === 'production') {
module.exports = require('./Root.prod');
} else {
module.exports = require ('./Root.dev');
}
有一個細節需要注意:ES6語法不支援在if中編寫import,所以這裡採用了CommonJS的模組引入方法 require
。
具體可以看看Redux的 Real World 示例專案。
程式碼層面還需要注意的一點就是按需import,否則可能會在打包時生成不必要的程式碼。
OK,我們現在用webpack打個包, webpack --config webpack.config.prod.js --progress
,結果可能會讓你下一跳:8.4M!我的心中有一萬頭草泥馬在奔騰!
使用webpack打包
別急,接下來我們來調教下打包工具。目前React主流打包工具有2種:
同上,建議為不同的環境準備不同的webpack配置檔案,比如: webpack.config.dev.js
、 webpack.config.prod.js
。下面我們來看看幾個比較關鍵的配置選項:
devtools
文件在 這裡 ,我對source
map技術不太瞭解,所以幾個選項真不知道是幹什麼的。不過好在下面的表格中有寫哪些是production supported,隨便選擇一個就好,感覺結果區別不大。這裡我選擇了 source-map
,webpack一下後生成了2個包:
- bundle.js:3.32 MB
- bundle.js.map:3.78 MB
唔,這樣好多了,把用於定位原始碼的source map分離出去了,一下子減少了一半以上的體積。(注:source map只會在瀏覽器devtools啟用時載入,並不會影響正常的頁面載入速度,具體可參考 When is jQuery source map loaded? 、 JavaScript Source Map 詳解 。)
plugins
你可能會問“怎麼不祭出UglifyJS啊?”,現在就祭...webpack文件中有一節 Optimization ,講到了一些優化技巧。Chunks略高階沒用過,看前面兩個吧。提到了3個外掛:UglifyJsPlugin、OccurenceOrderPlugin、DedupePlugin,第一個外掛應該都懂是幹啥,後面兩個描述得挺高深的,不過不懂沒關係,全用上試試,反正沒副作用:
plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurenceOrderPlugin() ]
打包結果:1.04 MB。
不要忽視NODE_ENV
NODE_ENV其實就是一個環境變數,在Node中可以通過 process.env.NODE_ENV
獲取。目前大家往往用這個環境變數來標識當前到底是development還是production環境。
We provide two versions of React: an uncompressed version for development and a minified version for production. The development version includes extra warnings about common mistakes, whereas the production version includes extra performance optimizations and strips all error messages.
同時在React文件中明確建議在生產環境下設定 NODE_ENV
為 production
(見: npm ):
Note: by default, React will be in development mode. To use React in production mode, set the environment variable NODE_ENV to production (using envify or webpack's DefinePlugin). A minifier that performs dead-code elimination such as UglifyJS is recommended to completely remove the extra code present in development mode.
可以通過webpack的DefinePlugin設定環境變數,如下:
plugins: [ ... new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }), ]
打包結果:844 KB。
OK,webpack到此為止,給出完整的 webpack.config.prod.js
:
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'source-map',
entry: [
'./index.js'
],
output: {
path: path.join(__dirname, 'webpack-output'),
filename: 'bundle.js',
publicPath: '/webpack-output/'
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
],
module: {
loaders: [
{
test: /.js$/,
loader: 'babel',
exclude: /node_modules/,
include: __dirname
},
{
test: /\.css$/,
loaders: ["style", "css"]
},
{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
}
]
},
};
打包結果存放在 webpack-output
資料夾下。
使用FIS3新增hash
前端公認的Best Practice就是給資源打上hash標籤,這對快取前端資源很有用。webpack文件中有一節 Long-term Caching 就是專門講整的,然而配置起來好麻煩的樣子,最後我還是選擇了百度的 FIS3 做hash。
使用方法見文件,寫的很詳細。貼一下我的 fis-conf.js
:
// 需要打包的檔案 fis.set('project.files', ['index.html', 'static/**', 'webpack-output/**']); // 壓縮CSS fis.match('*.css', { optimizer: fis.plugin('clean-css') }); // 壓縮PNG圖片 fis.match('*.png', { optimizer: fis.plugin('png-compressor') }); fis.match('*.{js,css,png}', { useHash: true, //啟用has domain: 'http://7xrdyx.com1.z0.glb.clouddn.com', // 新增CDN字首 });
其中,通過 useHash: true
啟用了hash功能,同時壓縮了CSS、PNG圖片,然後通過 domain
添加了CDN字首。
執行 fis3 release -d ./output
後,就把所有的檔案打包到 output
資料夾下了,截個圖:
使用CDN
844 KB雖然比最開始的8.4 M縮小到了1/10,但其實也很嚇人。包大小基本上已經壓縮到極限了,但我們還可以通過CDN來加快頁面載入時間。
我選擇的是 七牛 ,效果不錯,而且免費額度夠用。
上一步中我們已經用FIS3添加了七牛CDN的字首,接下來就是上傳打包檔案了。手動上傳太麻煩,七牛提供了一個用來批上傳的命令列工具 qrsync ,具體用法見文件。
使用Fabric進行遠端部署
部署的時候難免會涉及到登陸server執行部署命令,你可以手動操作,但我推薦還是用一些工具來做。這方面工具不少,選擇順手的就行,我因為之前有過Python開發經驗,所以一直用 Fabric ,很好用。安裝下python,然後安裝包管理工具 pip ,然後 sudo
pip install fabric
就行了。
在專案檔案下建立 fabfile.py
,通過寫Python程式碼描述遠端部署過程:
# coding: utf-8
from fabric.api import run, env, cd
def deploy():
env.host_string = "[email protected]"
with cd('/path/to/your/project'):
run('git pull')
run('npm install')
run('webpack --progress --config webpack.config.prod.js')
run('fis3 release -d ./output')
run('qrsync qrsync.conf.json')
其中, env.host_string
描述server資訊,然後cd到專案資料夾, git
pull
從GitHub拉取原始碼, npm install
安裝第三方庫,接下來就是各種打包,最後批量上傳到CDN。
執行 fab deploy
就部署到生產伺服器了。
Nginx
收尾工作交給Nginx:
- 域名與本地資料夾路徑關聯起來
- gzip支援:這個一定要做,效果很贊,具體啟用方法就是將
/etc/nginx/nginx.conf
與gzip相關的東西uncomment一下就行 - 不存在的path一律導向
/index.html
:否則在非根路徑下重新整理瀏覽器,就會出現404,開發React的童鞋應該都懂這個坑...
我的 nginx.conf
如下所示:
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/project;
location / {
try_files $uri /index.html;
}
}
注:有童鞋可能奇怪為什麼沒有新增cache的配置,因為所有東西都上傳到CDN了...
瀏覽器實際載入效果
在Chrome除錯工具下看。
禁止快取:
可以看到bundle的最終大小為206KB,載入時間是118ms。
啟用快取:
效果還不錯。
開發->部署流程
從開發到部署的流程如下:
- 編寫程式碼:01010000101001010
- 提交到程式碼倉庫:
git add -A && git commit -m 'Some notes.' && git push
- 部署到server:
fab deploy
是不是很簡單?
其他
還有一些東西可以加進來:
具體就不展開了。
就寫到這裡,歡迎建議。
· EOF ·