聯(lián)系人列表

2023-05-11 10:19 更新

了解 React 的工作方式

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

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

Sample Mobile Application with React and Cordova 頁面有一張關(guān)于 React 組件的圖,說明了這個 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)是做一個通訊錄,功能和而已都與上圖類似,只是界面改用 Amaze,所以得先去 Amanze UI React 組件文檔頁面 了解需要使用的組件,并畫出自己的草圖。

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

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

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

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

之前已經(jīng)確定了要使用 jQuery,React 和 Amaze UI React。按我個人的習(xí)慣,會把它放在 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 會將組件渲染在 <body> 中。

<head> 最后引入的 js/index.jsx 就是頁面對應(yīng)的腳本,使用 JSX 語法以 React 組件的形式實(shí)現(xiàn)。一般這個文件是以 .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òu)建用戶界面的 JavaScript 庫,它為構(gòu)建大型應(yīng)用而生,這些應(yīng)用可能隨時(shí)產(chǎn)生數(shù)據(jù)變動。很多人把 React 作為 MVC 中的 V 來使用。

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

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

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

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

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

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

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

定義 Page 組件

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

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


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


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

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

然而通過 Nginx 跑出來之后,從瀏覽器的控制臺得到了一個錯誤消息

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

大概意思是說,JSX 的元素必須是1個封閉的標(biāo)簽。這里提供了兩個要素:“1個”、“封閉”。所以我根據(jù)這個意思,修改了一下,然后運(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 定義的這個命名空間太長,可以自己縮短一下,比如

var A = AMUIReact;

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

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

參考

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

對 Page 組件的解釋

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

  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)容是用括號 () 包起來的,其實(shí)如果不包起來也不會出錯。但很顯然,在寫 JavaScript 程序的時(shí)候,如果 return 的內(nèi)容是多行,用括號包起來是個好習(xí)慣。

添加列表項(xiàng)

現(xiàn)在繼續(xù)下一步,添加列表項(xiàng)。在沒搞清楚如何使用循環(huán)之前,還是先用重復(fù)的代碼把列表項(xiàng)顯示出來再說——當(dāng)然,在目前沒有定義數(shù)據(jù)結(jié)構(gòu)的情況下,也不會用到循環(huán)??紤]到對組件還不夠熟悉,列表項(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);

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

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

數(shù)據(jù)當(dāng)然不會是固定不變的,直接將列表項(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)然的又寫了個段錯誤代碼

// 錯誤的代碼
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>);
}

這回從錯誤消息中可以發(fā)現(xiàn)是 for 循環(huán)惹的禍。一開始不明白為啥,仔細(xì)思考之后明白了——return 后面的應(yīng)該是一個表達(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>);
}

這回是對了,但是這寫法看起來不夠簡潔,容易把人搞暈。參考網(wǎng)上的資料,發(fā)現(xiàn)可以將 map() 的結(jié)果保存在一個變量中,再在 <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 中如果在代碼中嵌入成對的 <XML標(biāo)簽&,會被翻譯成組件代碼(React.createElement(...) 等),而在 xml 標(biāo)簽中使用一對大括號 {} 可以嵌入 JS 代碼。如果對 ASP.NET MVC 的 Razor 模塊有所了解,就會有似曾相識的感覺。

未完待續(xù)

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

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

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號