1. 程式人生 > >ReactJS學習-使用webpack構建工程,使用materialUI構建前端,與hprose後端通訊

ReactJS學習-使用webpack構建工程,使用materialUI構建前端,與hprose後端通訊

實現目標:

Material-UI 是一套用React寫成的,符合Google Material Design 的UI元件庫。

前端通過Material-UI構造介面,然後通過hprose-html5呼叫後端hprose服務取資料

hprose服務參考 上一篇hprose實踐,

環境配置:

必須先安裝nodejs與npm

新建工程目錄

mkdir react-workspace
cd react-workspace
npm init

安裝依賴

npm install --save react react-dom react-tap-event-plugin material-ui
npm install --save-dev babel-core babel-loader 
npm install --save-dev babel-preset-es2015 babel-preset-react babel-preset-stage-1
npm install --save-dev webpack

第一行是生產用的 React 與 Material-UI 部分。
第二行是Babel轉換器的核心部分。
第三行是Babel轉換器的三個額外配置:ES2015(ES6),React,Stage1(ES7)。
第四行是Webpack的部分。

第二、三、四行的內容只在工程構建之前有用(用於開發:-dev)。

安裝完畢之後呢,可以先檢查一下 package.json
應該會看見如下內容

"dependencies": {
    "material-ui": "^0.15.0",
    "react": "^15.1.0",
    "react-dom": "^15.1.0",
    "react-tap-event-plugin": "^1.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.9.1",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.9.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-1": "^6.5.0",
    "webpack": "^1.13.1"
  },

配置 Babel

在 package.json 中新增一個域”babel”,與之前的”dependencies” 同級。

"babel": {
    "presets": [
      "es2015",
      "react",
      "stage-1"
    ],
    "plugins": []
  }

配置 Webpack

在專案目錄新建一個webpack.config.js ,並寫入:

var path = require('path');

module.exports = {
    entry: './entry.js',
    output: {
        path: path.join(__dirname, '/dist'),
        filename: 'bundle.js'
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    module: {
        loaders: [
            { test: /\.jsx?$/, loaders: ['babel'] }
        ]
    }
}

這裡對Webpack的打包行為做了配置,主要分為幾個部分:

entry:指定打包的入口檔案,每有一個鍵值對,就是一個入口檔案
output:配置打包結果,path定義了輸出的資料夾,filename則定義了打包結果檔案的名稱
resolve:定義瞭解析模組路徑時的配置,常用的就是extensions,可以用來指定模組的字尾,這樣在引入模組時就不需要寫字尾了,會自動補全
module:定義了對模組的處理邏輯,這裡可以用loaders定義了一系列的載入器,以及一些正則。當需要載入的檔案匹配test的正則時,就會呼叫後面的loader對檔案進行處理,這正是webpack強大的原因。比如這裡定義了凡是.js結尾的檔案都是用babel-loader做處理,而.jsx結尾的檔案會先經過jsx-loader處理,然後經過babel-loader處理。當然這些loader也需要通過npm install安裝
plugins: 這裡定義了需要使用的外掛,比如commonsPlugin在打包多個入口檔案時會提取出公用的部分,生成common.js

當然Webpack還有很多其他的配置,具體可以參照它的配置文件

配置 npm 指令碼
現在我們還缺少一個構建指令碼,編輯package.json 中的 “scripts” 域:

"scripts": {
    "build": "webpack",
    "build-dev": "webpack -w -d"
  }

接下來我們就可以在專案目錄下使用簡單的構建指令碼了:

$ npm run build

如果是開發中,可以使用監聽式的Webpack,差量打包,提升效率。

$ npm run build-dev

按照配置,打包生成的檔案為 dist/bundle.js 。

至此,基本的環境已經配置完畢,我們來嘗試一下呼叫Material-UI庫。

程式碼編寫

建立Web入口
在專案目錄下建立一個index.html,寫入:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <script src="dist/bundle.js"></script>
    </body>
</html>

編寫Webpack入口
編輯專案目錄下的 entry.js,寫入:

import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';

const App = () => (
  <MuiThemeProvider muiTheme={getMuiTheme()}>
    <AppBar title="Hello, Material-UI!" />
  </MuiThemeProvider>
);

let app = document.createElement('div');
ReactDOM.render(<App />, app);
document.body.appendChild(app);

執行構建指令碼:

npm run build

輸出
6.png

現在可以看到目錄下有了一個dist/bundle.js

開啟index.html

7.png
成功

瀏覽器自動重新整理

如果需要一直輸入 npm run build 確實是一件非常無聊的事情,幸運的是,我們可以把讓他安靜的執行,讓我們設定 webpack-dev-server。

npm install --save webpack-dev-server

修改package.json檔案的scripts:

"scripts": {
        "build": "webpack",
        "dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build"
      }

當你在命令列裡執行 npm run dev 的時候他會執行 dev 屬性裡的值。這是這些指令的意思:

webpack-dev-server - 在 localhost:8080 建立一個 Web 伺服器
–devtool eval - 為你的程式碼建立源地址。當有任何報錯的時候可以讓你更加精確地定位到檔案和行號
–progress - 顯示合併程式碼進度
–colors - Yay,命令列中顯示顏色!
–content-base build - 指向設定的輸出目錄
總的來說,當你執行 npm run dev 的時候,會啟動一個 Web 伺服器,然後監聽檔案修改,然後自動重新合併你的程式碼。真的非常簡潔!

訪問發現 “Cannot GET /” 錯誤

因為我們的index.html不是在build目錄下

調整專案結構為

  • /app
    • main.js
    • component.js
  • /build
    • bundle.js (自動建立)
    • index.html
  • package.json
  • webpack.config.js

修改index.html

<!doctype html>
    <html>
        <head>
            <meta charset="utf-8" />
        </head>
        <body>
            <script src="bundle.js"></script>
        </body>
    </html>

將entry.js移動到app/main.js

修改webpack.config.js,將入口檔案設定為app/main.js,output設為build目錄下的bundle.js,
並新增入口點,使得瀏覽器在檔案修改之後會自動重新整理。

var path = require('path');

module.exports = {
    entry: [
      'webpack/hot/dev-server',
      'webpack-dev-server/client?http://localhost:8080',
      path.resolve(__dirname, 'app/main.js')
    ],
    output: {
        path: path.join(__dirname, '/build'),
        filename: 'bundle.js'
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    module: {
        loaders: [
            { test: /\.jsx?$/, loaders: ['babel'] }
        ]
    }
}

執行命令 npm run dev, 然後訪問http://localhost:8080/
看到之前同樣頁面,然後修改一下main.js

<AppBar title="Hello, Material-UI!" />

修改為

<AppBar title="Hello, World!" />

儲存一下,再回到瀏覽器,會發現自動重新整理了,內容也變成了Hello, World!

通過hprose-html5取資料

下面通過後端服務取資料,來替代Hello,World!

現已有hprose for php 構建的hprose服務,在遠端伺服器http://xx.xx.xx.xx:8080/
方法為getUserByID

修改index.html,引入hprose-html5.js,使用的是bootcss提供的cdn

<script type="text/javascript" src="http://cdn.bootcss.com/hprose-html5/2.0.8/hprose-html5.js"></script>

修改main.js

import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';

const muiTheme = getMuiTheme();

class App extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      data:"none",
    };
    this.componentDidMount = this.componentDidMount.bind(this);
  }

  componentDidMount() {
      this.setState({data: "block"});
      let self = this;
      let client = hprose.Client.create("http://xx.xx.xx.xx:8080/", ["getUserByID"]);
      client.getUserByID(2, function(result) {
            console.log(result.player_name);
            self.setState({data: result.player_name});
        }, function(name, err) {
            console.log(err);
        });
  }
  render() {
    return (
    <MuiThemeProvider muiTheme={muiTheme}>
    <AppBar title={this.state.data} />
    </MuiThemeProvider>
    );
  }
}

let app = document.createElement('div');
ReactDOM.render(<App />, app);
document.body.appendChild(app);

檢視結果

5.png

從後端服務取到了資料“keyunqqq”

PS:React在ES6的實現中去掉了getInitialState這個hook函式,規定state在constructor中實現,如下:

Class App extends React.Component {

constructor(props) {
super(props);
this.state = {};
}

}

Babel的Blog上還有一種實現方法,即直接使用賦值語句:

Class App extends React.Component {

constructor(props) {
super(props);
}

state = {}

}

ES6中this需要手動繫結:

this.componentDidMount = this.componentDidMount.bind(this);