ReactJS入門(一)—— 初步認識React

React剛開始紅的時候,因爲對其不甚瞭解,以爲JSX的寫法略非主流,故一直沒打算將其應用在項目上,隨着身邊大神們的科普,才後知後覺是個好東西。html

好在哪裏呢?我的拙見,有倆點:node

1. 虛擬DOM —— 在DOM樹的狀態須要發生變化時,虛擬DOM機制會將同一Event loop先後的DOM樹進行對比(天然經過一系列高效的算法),若是倆個DOM樹存在不同的地方,那麼React僅僅會針對這些不同的區域(DOM diff)來進行響應的DOM修改,從而實現最高效的DOM操做和渲染。react

以下圖,咱們修改了DOM樹上一些節點(或UI組件)對應綁定的state(不知道state是什麼不要緊,後續咱們會說起),React會即刻將其標記爲「髒狀態」,在一個Event loop結束時(即便過程當中你對某個組件的state進行了屢次修改),React會計算得出DOM樹上須要修改的地方(「髒」了的地方,以下圖紅點)及其最終的狀態,並僅僅針對這些地方進行一次性的從新渲染。git

因而好處顯而易見,並不是每修改一次組件的state,就會從新渲染一次,而是在Event loop結束後作一次「秋後算帳」,減小冗餘的DOM操做。另外React只針對須要修改的地方來作新的渲染,而非從新渲染整個DOM樹,天然效率非常不錯。github

 

2. 組件可嵌套,並且,能夠模版化嘛 —— 其實在React裏說起的「組件」,常規是一些可封裝起來、複用的UI模塊,說的接地氣了能夠理解爲「帶有細粒度UI功能的部分DOM區域」。而後咱們能夠把這些組件層層嵌套起來使用(固然這樣組件間會存在依賴關係)。算法

至於模塊化,相似於ejs那樣能夠做爲獨立的模塊被引用到頁面上來複用,不過咧,它能夠直接把UI組件看成腳本模塊那樣來使用,咱徹底能夠配合CommonJS、AMD、CMD等規範來require須要的組件模塊,並處理好它們的依賴關係(是否是碉堡了)。gulp

 

 

基於上述的倆點,天然的也打算投入React懷抱了。不過在這以前得先理清倆點事情:數組

1. React是一個純View層,不擅長於和動態數據打交道(哎喲咱也不談Flux了,Flux的概念其實也不完善,所以它不一樣於,也替代不了常規的MV*框架;瀏覽器

2. React很擅長於處理組件化的頁面,在頁面上搭組件的形式有點像搭樂高同樣,所以用上React的項目需求常規爲界面組件化。另外React只支持到IE8+,就天朝的狀況,是否使用React仍是得稍微斟酌一番。框架

嘮嗑了這麼多,下面開始進入React的入門課程。本章說起的代碼均可以在個人Github上下載到。

JSX

JSX是React編寫組件的一種語法規範,能夠看爲是js的擴展,它支持將HTML和JS混寫在一塊兒,最終會經過React提供的 JSXTransformer.js 來將其編譯爲常規的js,方便瀏覽器解析。咱們來個最簡單的例子:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>最簡單的jsx</title>
    <script src="react.js"></script>
    <script src="JSXTransformer.js"></script>
</head>
<body>
<div id="a">123</div>
<script type="text/jsx">
    var name = 'VaJoy';
    React.render(<h1>{name}</h1>, document.getElementById('a'));
</script>
</body>
</html>

頁面上咱們引入了 react.js 和 JSXTransformer.js 倆個重要的腳本,前者天然是react的核心,後者則是將JSX語法轉爲js語法。

實際上使用 JSXTransformer.js 在客戶端來解析JSX語法是一件冗餘、耗性能的事情,咱們能夠在上線項目以前,事先使用諸如 gulp-react 的工具先把全部的JSX均轉換爲js文件再引入到頁面中。

固然若是你掌握了react的JS寫法(固然相比JSX的學習成本要高很多),你能夠直接在頁面上書寫相應的 原生js 便可,來個對比:

//使用JSX
React.render(
    <div>
        <div>
            <div>content</div>
        </div>
    </div>,
    document.getElementById('example')
);

//不使用JSX
React.render(
    React.createElement('div', null,
        React.createElement('div', null,
            React.createElement('div', null, 'content')
        )
    ),
    document.getElementById('example'

回到開頭第一段代碼段,咱們將頁面的JSX直接寫在 <script type="text/jsx"> 中,注意 type 類型要標明爲 "text/jsx" 。

而後咱們定義了個變量name,並使用了React最基礎和最經常使用的一個方法 React.render() 來渲染DOM:

 var name = 'VaJoy';
    React.render(<h1>{name}</h1>, document.getElementById('a'));

React.render() 支持兩個參數(其實還有第三個可選的參數,做爲渲染後的回調),第一個參數爲模板的渲染內容(HTML形式),第二個參數表示要插入這段模板的DOM節點(DOM node)

這裏要說起一個知識點 —— 在JSX中,遇到 HTML 標籤(以 < 開頭),將用 HTML 規則解析;遇到代碼塊(以 { 開頭),則用 JavaScript 規則解析。因此咱們在<h1>中嵌入變量 name 時是以花括號的形式 {name} 來實現的。

至於執行結果,相信你們很容易猜出來:

即往div裏插入了(其實應該說是完全替換爲)JSX中的模板內容(<h1>元素)

鑑於JSX支持HTML跟JS的混寫,故其靈活性很高,咱們試着把變量換爲數組:

var arr = ['HELLO', "here is", 123, "VaJoy`s blog"];
    React.render(
        <ul>{
            arr.filter(function(v){
                return typeof v === 'string'
            }).map(function(v){
                return <li> {v} </li>
            })
        }</ul>,
        document.getElementById('a')
    );

結果以下:

組件

開頭已經說起React是用於組件化開發的,組件可看爲是其最小組成單位,那麼什麼是組件(component)呢?咱們看看下方這張圖:

這是一個移動端上的UI界面,用於查詢員工列表和某員工的具體信息。那麼咱們按頁面上各功能來細分,以左側的搜索主頁界面而言,能夠把它細分爲SearchBar、EmployeeList、EmployeeListItem等UI組件,其中EmployeeList組件嵌套了多個EmployeeList組件,而這衆多組件的搭配組成了整個Hompage界面。

常規咱們用React開發的頁面能夠看爲一個大組件,它由多個小組件搭建而成。

在JSX中,咱們能夠經過 React.createClass() 方法來建立一個組件(注意組件的名稱必須爲大寫字母開頭),並以命名標籤的形式(<MyClassName />)來引用:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>組件</title>
    <style>
        .nameStyle{
            color: dodgerblue;
        }
    </style>
    <script src="react.js"></script>
    <script src="JSXTransformer.js"></script>
</head>
<body>
<div id="a"></div>
<script type="text/jsx">
    var name = 'VaJoy';
    var Demo = React.createClass({  //隨便建了個叫Demo的組件
        render: function() {  //每一個組件都須要有本身的render方法
            var a = "Hello,";
            return (
                <p className="nameStyle" >{a,name}</p> //固然你能夠寫爲{a}{name}
            );
        }
    });
    React.render(
        <Demo />,  //引入組件Demo
        document.getElementById('a')
    );
</script>
</body>
</html>

注意每一個組件都須要有一個render方法,用於輸出組件內容。另外組件DOM元素上的 class 屬性須要寫成 className ,for 屬性須要寫成 htmlFor ,這是由於 class 和 for 是 JavaScript 的保留字。

執行效果以下:

能夠看到,若是在一個容器中引用了多個變量,React會對應每一個文本節點來自動生成對應span標籤(React防止XSS的機制,所以要注意規避span的樣式污染!)

再來看個組件嵌套的示例,其實都很簡單:

  View Code

注意在JSX裏,給DOM設置 style 必須寫成 {{ marginRight : '10px', fontSize : '18px' }} 的映射形式(預防XSS),不然會報錯

執行結果(注意連空格都生成了一個span)

組件的「數據」

咱們在前頭說起了,React只是一個純view層,並不涉及model。不過但對於React組件而言,它有兩種特殊的data —— Props 和 States。其中的 States 有點相似於各MV*框架中的model部分,能夠稱爲React的狀態機制,用於與用戶進行交互。

1. Props

props表示組件自身的屬性,也能夠用於在嵌套的內外層組件中傳遞信息(常規是由父層組件傳遞給子層組件)。要注意它是不變的、也不該該嘗試去改變的。咱們來個簡單的示例:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>組件Props</title>
    <script src="react.js"></script>
    <script src="JSXTransformer.js"></script>
</head>
<body>
<div id="a"></div>
<script type="text/jsx">
    var Component1 = React.createClass({
        render: function() {
            return <p> {this.props.abc, this.props.name} </p>;
        }
    });
    var Component2 = React.createClass({
        render: function() {
            return (
                    <div className="commentList" onClick={this.handleClick}>
                        <Component1 abc="你好!" name="張三" />
                        <Component1 abc="Hi!" name="李四" />
                    </div>
            );
        },
        handleClick: function (e) {
            console.log(this.props.name, e.target);
        }
    });
    React.render(
        <Component2 name="我是Component2的name哦!" />,
        document.getElementById('a')
    );
</script>
</body>
</html>
複製代碼

這裏咱們註冊了倆個組件類 Component1 和 Component2 ,其中 Component2 內嵌了 Component1 。

咱們先看 Component1 的定義:

 var Component1 = React.createClass({
        render: function() {
            return <p> {this.props.abc, this.props.name} </p>;
        }
    });

它返回了一個p標籤,其中的內容是 this.props.abc 和 this.props.name,這裏的 this 指的是組件 Component1 自己,所以好比 this.props.name 獲取到的即是組件 Component1 自身的屬性 name 的值(abc 和 name 的值咱們都在 Component2 中進行了定義)。

咱們接着看 Component2:

  var Component2 = React.createClass({
        render: function() {
            return (
                    <div className="commentList" onClick={this.handleClick}>
                        <Component1 abc="你好!" name="張三" />
                        <Component1 abc="Hi!" name="李四" />
                    </div>
            );
        },
        handleClick: function (e) {
            console.log(this.props.name, e.target);
        }
    });

在這裏咱們除了嵌入組件 Component1 並定義了它們的屬性值 abc 和 name ,也使用了React的事件機制——咱們用 onClick 方法來觸發點擊事件,其對應的 this.handleClick 是咱們自定義的一個方法,用於輸出 Component2 的 name 屬性和當前被點擊的目標元素。

最後咱們用 React.render 方法渲染組件 Component2 ,並定義了它的 name 屬性:

 React.render(
        <Component2 name="我是Component2的name哦!" />,
        document.getElementById('a')
    );

執行結果以下:

這裏有點要留意的,不管是 props 組件屬性,或者是 onClick 事件,都是 React 內部的東西,咱們在HTML上是看不到它們的:

順便提個Props的語法糖 —— 咱們能夠經過 「...this.props」 來將父層組件綁定的所有屬性都直接寫到子組件中:

  View Code

執行結果:

Props也並不是都是直接寫在組件標籤上的屬性,有一個例外 —— props.children,它表示當前組件的全部子節點(它們常規是在外部的組件賦予的)

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>props.children</title>
    <style>
        p:active{ color: deeppink; }
    </style>
    <script src="react.js"></script>
    <script src="JSXTransformer.js"></script>
</head>
<body>
<div id="a"></div>
<script type="text/jsx">
    var Component1 = React.createClass({
        render: function() {
            return <li>{this.props.children}</li>
        }
    });
    var Component2 = React.createClass({
        list: ['張三', '李四', '王五'],
        render: function() {
            return (
                <ul>
                    {
                        this.list.map(function(item){
                            return <Component1>{item}</Component1>
                        })
                    }
                </ul>
            );
        }
    });
    React.render(
        <Component2 />,
        document.getElementById('a')
    );
</script>
</body>
</html>

留意第27行的代碼 return <Component1>{item}</Component1> ,其中的{item}會做爲組件Component1的子節點(即 props.children)來處理。

執行以下:

2. State

State 是組件的狀態,它是可變的,所以經常使用於跟用戶的交互。

不一樣於Props,咱們須要在組件中使用 getInitialState() 方法來初始化組件的State,並使用 this.setState() 來修改State的值:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>state</title>
    <style>
        p:active{ color: deeppink; }
    </style>
    <script src="react.js"></script>
    <script src="JSXTransformer.js"></script>
</head>
<body>
<div id="a"></div>
<script type="text/jsx">
    var Component = React.createClass({
        getInitialState: function() {
            return {
                isClick: !1
            }
        },
        render: function() {
            var text = this.state.isClick ? 'clicked!' : 'none click';
            return <div>
                <input type="checkbox" onChange={this.clickCb} />{ text }
            </div>;
        },
        clickCb: function(){
            this.setState({
                isClick : !this.state.isClick  //點擊checkbox時改變state.isClick
            })
        }
    });

    React.render(
        <Component />,
        document.getElementById('a')
    );
</script>
</body>
</html>

咱們先經過 getInitialState() 方法初始化了組件Component的State對象,裏面有個 isClick 的屬性,默認值爲 false。

接着在組件的 render 方法裏定義了一個 text 變量,其值會根據 state.isClick 的變化而變化:

var text = this.state.isClick ? 'clicked!' : 'none click';

另外給checkbox控件綁定了React的 onChange 事件,checkbox的勾選狀態改變時觸發組件的 clickCb 自定義回調事件,它是這樣的:

  clickCb: function(){
            this.setState({
                isClick : !this.state.isClick  //點擊checkbox時改變state.isClick
            })
        }

如註釋,它會經過 this.setState 方法來改變 state.isClick 的值,進而依賴於 state.isClick 的變量 text 的值也會間接變化:

相關文章
相關標籤/搜索