聯(lián)系人列表

2023-05-11 10:19 更新

了解 React 的工作方式

在閱讀了大量 React 的資料(主要是官網(wǎng))之后,發(fā)現(xiàn)有兩個(gè)概念非常重要

  1. React 的基本元素是組件,所有內(nèi)容都由各種組件或包含,或組合,搭建而成。
  2. React 從數(shù)據(jù)渲染頁面,單向進(jìn)行。這個(gè)在實(shí)踐的過程中會(huì)慢慢的體會(huì)到。

Sample Mobile Application with React and Cordova 頁面有一張關(guān)于 React 組件的圖,說明了這個(gè) React Demo 應(yīng)用中各組件的關(guān)系(注意觀察線框和箭頭的顏色)。

了解 Amaze UI React

Amaze UI React 是在 Amaze UI (jQuery版) 的基礎(chǔ)上,拋棄 jQuery,使用 React 開發(fā)的組件庫。Amaze UI React 和 Amaze UI (jQuery版) 共用一套 CSS。

就從 Amaze 官網(wǎng)的菜單組織也能看到二者的區(qū)別。jQuery 版的菜單包含三項(xiàng):CSS、JS插件、Web組件;而 React 版就只有一項(xiàng):組件。因此 Amaze UI 的 React 版不再需要用戶去了解 CSS 類和過多的腳本操作,只需要關(guān)注組件,及其數(shù)據(jù)(屬性和狀態(tài))即可。

開始實(shí)現(xiàn)通訊錄列表

參考文章的通訊錄包含列表頁和詳情頁。學(xué)習(xí)得一步步進(jìn)行,所以先實(shí)現(xiàn)列表頁。

學(xué)習(xí)的目標(biāo)是做一個(gè)通訊錄,功能和而已都與上圖類似,只是界面改用 Amaze,所以得先去 Amanze UI React 組件文檔頁面 了解需要使用的組件,并畫出自己的草圖。

這個(gè)草圖就是最初需要實(shí)現(xiàn)的東西:一個(gè)頁頭、一個(gè)列表、列表項(xiàng)分圖標(biāo)、文本、圖標(biāo)按鈕三個(gè)部分。而圖標(biāo)按鈕點(diǎn)擊可以轉(zhuǎn)到撥號(hào)頁面準(zhǔn)備撥號(hào)。

目標(biāo)已經(jīng)清楚了,下面就要開始搭建程序。第一步,先實(shí)現(xiàn)在 Nginx 中能正常顯示;第二步再實(shí)現(xiàn)構(gòu)建成 Android 程序在手機(jī)上使用(包括顯示和撥號(hào)操作)。

代碼前最后的準(zhǔn)備

之前已經(jīng)做了很充分的準(zhǔn)備,但那主要是對(duì)環(huán)境的準(zhǔn)備?,F(xiàn)在馬上要開始寫代碼,不得不做一些細(xì)致的準(zhǔn)備,比如,把需要用到的庫安放在代碼中適當(dāng)?shù)奈恢谩?/p>

之前已經(jīng)確定了要使用 jQuery,React 和 Amaze UI React。按我個(gè)人的習(xí)慣,會(huì)把它放在 Web 應(yīng)用的 /libs 目錄下。沒有必要去篩選哪些文件用得上哪些用不上,都拷貝過來,結(jié)果就有了這樣的目錄結(jié)構(gòu)

開始編碼

修改 index.html

首先就是修改 index.html 文件,在 index.html 中引入需要的 AmazeUI 的 CSS,以及各依賴庫的腳本文件。直接從默認(rèn)生成的 index.html 改過來就好。

<!doctype html>
<html>


<head>
    <meta name="format-detection" content="telephone=no">
    <meta name="msapplication-tap-highlight" content="no">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    <title>Concats</title>


    <link rel="stylesheet" type="text/css" href="libs/amazeui/css/amazeui.min.css" />
    <link rel="stylesheet" type="text/css" href="css/index.css">


    <script src="libs/jquery/jquery-2.1.4.min.js"></script>
    <script src="libs/react/react.min.js"></script>
    <script src="libs/react/JSXTransformer.js"></script>
    <script src="libs/amazeui/js/amazeui.react.min.js"></script>


    <script type="text/jsx" src="js/index.jsx"></script>
</head>


<body>
</body>


</html>

<body> 標(biāo)簽中留空,因?yàn)橹?React 會(huì)將組件渲染在 <body> 中。

<head> 最后引入的 js/index.jsx 就是頁面對(duì)應(yīng)的腳本,使用 JSX 語法以 React 組件的形式實(shí)現(xiàn)。一般這個(gè)文件是以 .js 作為擴(kuò)展名,但是我認(rèn)為用 .jsx 作為擴(kuò)展名可以清楚表明該文件中使用了 JSX 語法。

React 渲染入口

這已經(jīng)是第2次提到渲染了,為什么說 React 渲染,而不說“執(zhí)行”、“搭建”、“構(gòu)造”諸如此類的詞呢?先來看看 React 官方是怎么描述 React 的:

React is a JavaScript library for creating user interfaces by Facebook and Instagram. Many people choose to think of React as the V in MVC.

We built React to solve one problem: building large applications with data that changes over time.

簡單的說,React 是一個(gè)構(gòu)建用戶界面的 JavaScript 庫,它為構(gòu)建大型應(yīng)用而生,這些應(yīng)用可能隨時(shí)產(chǎn)生數(shù)據(jù)變動(dòng)。很多人把 React 作為 MVC 中的 V 來使用。

而在其它文章資料中也提到,React 的處理過程是從數(shù)據(jù)到視圖的一個(gè)單向過程。綜合起來就可以這樣理解:React 對(duì)數(shù)據(jù)進(jìn)行渲染,以 UI 的形式呈現(xiàn)。如果數(shù)據(jù)發(fā)生變動(dòng),React 會(huì)重新進(jìn)行渲染(實(shí)際上 React 會(huì)判斷數(shù)據(jù)變動(dòng)造成的形式,智能選擇最小渲染范圍以提高效率)。

上面提到將 <body> 標(biāo)簽內(nèi)容留空,以便 React 在其中進(jìn)行渲染。也就是下面這句,React 的渲染入口:

React.render(<Page />, document.body);

上述表達(dá)式用了 JSX 語法,而 JSX 中的 X 部分,我理解為是以 XML 方式描寫的表達(dá)式。為什么是表達(dá)式,這會(huì)在后面的循環(huán)中提及。

正如 React 文檔所述,JSX 語法只是一個(gè)語法糖,它完全可以用純粹的腳本來寫,而且 React 本身也是需要將 JSX 翻譯成 JS 來執(zhí)行的。上面那句話的純 JS 寫法會(huì)是這樣

React.readner(React.createElement(Page), document.body);

那么 Page 是什么,這里看起來它應(yīng)該是一個(gè)合法的 JS 變量——是的,這就是馬上需要定義的 React 組件。

定義 Page 組件

定義組件會(huì)使用 React.createClass() 方法。根據(jù)對(duì) React 的初步了解,我想當(dāng)然的寫下了一個(gè)錯(cuò)誤的定義

var Header = AMUIReact.Header;
var List = AMUIReact.List;


// 錯(cuò)誤的定義
var Page = React.createClass({
    render: function() {
        return (
            <Header title="通訊錄" />
            <List />
        );
    }
});


React.render(React.createElement(Page), document.body);

我的原意是希望能定義 <Page /> 組件,并將其渲染在 <body> 中,只要能顯示頁頭就行,列表部分暫時(shí)留空。

然而通過 Nginx 跑出來之后,從瀏覽器的控制臺(tái)得到了一個(gè)錯(cuò)誤消息

Uncaught Error: Parse Error: Line 13: Adjacent JSX elements must be wrapped in an enclosing tag

大概意思是說,JSX 的元素必須是1個(gè)封閉的標(biāo)簽。這里提供了兩個(gè)要素:“1個(gè)”、“封閉”。所以我根據(jù)這個(gè)意思,修改了一下,然后運(yùn)行出了預(yù)期的效果。

render: function(){
    return (
        <div>
            <Header title="通訊錄" />
            <List />
        </div>
    );
}

上面的代碼中,var Header = AMUIReact.Headervar List = AMUIReact.List 是 Amaze UI React 教程示例中演示的用法——為帶命名空間的組件創(chuàng)建簡短的名稱。其實(shí)我更傾向于使用帶命名空間的名稱,就像 <AMUIReact.Header title="通訊錄" />。這可以避免自定義組件和 Amaze UI React 組件的名稱沖突。不過 Amaze UI React 定義的這個(gè)命名空間太長,可以自己縮短一下,比如

var A = AMUIReact;

<A.Header title="通訊錄" />

后面的示例中就采用這種縮短命名空間的寫法。

參考

- Header 組件 | Amaze UI React
- List 組件 | Amaze UI React

對(duì) Page 組件的解釋

  1. ReactClass createClass(object specification) 根據(jù)參數(shù)提供的規(guī)格說明,創(chuàng)建組件類。規(guī)則說明是一個(gè) JavaScript 對(duì)象,其提供的 ReactElement render() 函數(shù)會(huì)返回一個(gè) ReactElement (對(duì)象)用于渲染。

  1. render() 中返回的 ReactElement 可以是 JSX 描述,也可以是純 JS 腳本。上面的例子是用的 JSX 描述,如果改成 JS 腳本,應(yīng)該像這樣
    render: function() {
        return React.createElement("div", {}, [
                React.createElement(Header, {
                    title: "通訊錄"
                }),
                React.createElement(List)
            ]);
    }

    很明顯,JSX 描述更清晰易讀也更容易寫出來。

  1. return 后面的的內(nèi)容是用括號(hào) () 包起來的,其實(shí)如果不包起來也不會(huì)出錯(cuò)。但很顯然,在寫 JavaScript 程序的時(shí)候,如果 return 的內(nèi)容是多行,用括號(hào)包起來是個(gè)好習(xí)慣。

添加列表項(xiàng)

現(xiàn)在繼續(xù)下一步,添加列表項(xiàng)。在沒搞清楚如何使用循環(huán)之前,還是先用重復(fù)的代碼把列表項(xiàng)顯示出來再說——當(dāng)然,在目前沒有定義數(shù)據(jù)結(jié)構(gòu)的情況下,也不會(huì)用到循環(huán)??紤]到對(duì)組件還不夠熟悉,列表項(xiàng)的內(nèi)容,暫時(shí)僅展示姓名。

// js/index.jsx
var A = AMUIReact;
var Page = React.createClass({
    render: function() {
        return (<div>
            <A.Header title="通訊錄" />
            <A.List>
                <A.ListItem>
                    張三
                </A.ListItem>
                <A.ListItem>
                    李四
                </A.ListItem>
                <A.ListItem>
                    王麻子
                </A.ListItem>
            </A.List>
        </div>);
    }
});
React.render(React.createElement(Page), document.body);

這個(gè)結(jié)果并不好看,但至少已經(jīng)實(shí)現(xiàn)了列表的顯示。待功能完善之后還不能達(dá)到滿意的效果,可以自定義 CSS 來調(diào)整,所以不急。

引入 JSON 數(shù)據(jù)并循環(huán)處理列表項(xiàng)

數(shù)據(jù)當(dāng)然不會(huì)是固定不變的,直接將列表項(xiàng)寫死非常不切合實(shí)際。在學(xué)習(xí)初期,我暫時(shí)還不想去和數(shù)據(jù)庫打交道,所以暫時(shí)用 JSON 來保存數(shù)據(jù),而且為了保持獲取數(shù)據(jù)不節(jié)外生枝,先把數(shù)據(jù)定義在 index.jsx 的最前面。

// js/index.jsx
var data = [
    {
        "name": "張三",
        "tel": "13801234567"
    },
    {
        "name": "李四",
        "tel": "18018001800"
    },
    {
        "name": "王麻子",
        "tel": "17098765432"
    }
];


var A = AMUIReact;


// ... 后面的代碼略

然后改 render(),想當(dāng)然的又寫了個(gè)段錯(cuò)誤代碼

// 錯(cuò)誤的代碼
render: function() {
    return (<div>
        <A.Header title="通訊錄" />
        <A.List>
            for (var i = 0; i < data.length; i++) {
                <A.ListItem>{data[i].name}</A.ListItem>
            }
        </A.List>
    </div>);
}

這回從錯(cuò)誤消息中可以發(fā)現(xiàn)是 for 循環(huán)惹的禍。一開始不明白為啥,仔細(xì)思考之后明白了——return 后面的應(yīng)該是一個(gè)表達(dá)式,而 for 循環(huán)不是表示式。于是嘗試把 for 語句改成 Array.prototype.map() 方法

render: function() {
    return (<div>
        <A.Header title="通訊錄" />
        <A.List>
            {data.map(function(t) {
                return (
                    <A.ListItem>{t.name}</A.ListItem>
                );
            })}
        </A.List>
    </div>);
}

這回是對(duì)了,但是這寫法看起來不夠簡潔,容易把人搞暈。參考網(wǎng)上的資料,發(fā)現(xiàn)可以將 map() 的結(jié)果保存在一個(gè)變量中,再在 <A.List> 中引用

render: function() {
    var items = data.map(function(t) {
        return <A.ListItem>{t.name}</A.ListItem>;
    });


    return (<div>
        <A.Header title="通訊錄" />
        <A.List>
            {items}
        </A.List>
    </div>);
}

小結(jié) JSX 中如果在代碼中嵌入成對(duì)的 <XML標(biāo)簽&,會(huì)被翻譯成組件代碼(React.createElement(...) 等),而在 xml 標(biāo)簽中使用一對(duì)大括號(hào) {} 可以嵌入 JS 代碼。如果對(duì) ASP.NET MVC 的 Razor 模塊有所了解,就會(huì)有似曾相識(shí)的感覺。

未完待續(xù)

接下來還要為列表項(xiàng)添加圖標(biāo),以 React 的組件化思維來思考,比較好的作法是定義一個(gè)組件,不妨叫 Person 來封裝圖標(biāo)、姓名和電話按鈕。那么,如何將每個(gè)人的數(shù)據(jù)傳入 Person 對(duì)象?

另外,把數(shù)據(jù)放在 index.jsx 中也不是個(gè)辦法,遲早還是得用異步(比如 Ajax 或從數(shù)據(jù)庫獲?。┘虞d的,又該如何將數(shù)據(jù)更新到頁面中?

欲知后事如何,且看下回分解!

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)