1. 程式人生 > >部署React+Redux Web App

部署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、 Browserify 。Browserify沒用過,這裡主要談談webpack的配置經驗。

同上,建議為不同的環境準備不同的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 ·