第1章 Pure React

Pure React

ReactReactDOM0.14版本 之后分为两个文件,这样是为了让 React 和浏览器,DOM之间不存在必然的联系,让React写组件更加的灵活,比如与 React Native结合。

下面从基本概念谈起,然后再介绍React的基本方法。

一.基本概念

1.Virtual DOM

虚拟DOM是指js对象告诉React如何在浏览器中构建UI, 虚拟DOM提供一种描述,React将与浏览器DOM进行交互。

2.React Element

浏览器DOM由DOM元素组成,同样的React DOM 由 React Elements组成.React Element是对实际DOM的一种描述,指导浏览器如何构建DOM。

二.React.createElement()

创建React Elements的方法为React.createElement(),可以看出这个方法和浏览器DOM创建元素的方法一致,签名如下

React.createElement(ElementType, ElementProps, children)
// 返回类型为 一个React Element

比如:

var h1 = React.createElement(
        "h1",
         {"id": "heading", "data-type": "title"}, 
        "Hello World"
    );
// 表示
<h1 data-reactroot id="heading" data-type="title">Hello World</h1>

data-reactroot: 这个特性将自动出现在React Component的根元素上。

什么是React Element呢

React Elements 的实质是 Javascript literal 告诉浏览器如何构建DOM

这个字面量有很多字段比如: key, ref, _owner, _store, $$typeof, props

{
    "$$typeof": Symbol(React.element),
    "type": "h1",
    "key": null,
    "ref": null,
    "props": {"children": "Hello World"},
    "_owner": null,
    "_store": {}
}

三.ReactDOM类

这个类包含渲染React Elements的工具方法,主要是 render(), renderToString(),
renderToStaticMarkup()

我们通过 ReactDOM.render() 将 React Elements及其children 渲染成 DOM,语法
ReactDOM.render(ReactElementToRender, Target)

例如:

var para = React.createElement("p", null, "this is a paragragh");
ReactDOM.render(para, document.body); // 将段落para添加到body中 
// 即
<body>
    <p>This is paragragh</p>
</body>

1.React渲染子元素使用 props.children

比如可以创建一个ul

React.createElement("ul", {"className": "navigation"},
    React.createElement("li", null, "Home"),
    React.createElement("li", null, "Images"),
    React.createElement("li", null, "Videos"),
    React.createElement("li", null, "Maps")
)

// 创建的对象字面量为:

{
    "type": "ul",
    "props": {
        "children": [
            {"type": "li", "props": {"children": "Home"}},
            {"type": "li", "props": {"children": "Images"}},
            {"type": "li", "props": {"children": "Videos"}},
            {"type": "li", "props": {"children": "Maps"}}
        ]
    }
    ...
}

注意上面的"className"为html中的class特性, 因为"class"是javascript保留关键词,避免冲突

2.构建带数据的React Elements

使用React的一大好处就是分离UI元素和数据,上面的例子可以写为:

const items = [
    "Home",
    "Images",
    "Videos",
    "Maps"
];
React.createElement(
    "ul",
    {"className": "navigation"},
    items.map(item => React.createElement("li", null, item))
);

渲染之后运行上面的代码浏览器会报出一个错误:Each child in an array or iterator should have an unique "key" prop.

key property 用于帮助React 高效的更新DOM

上面可以写为:

React.createElement(
    "ul",
    {"className": "navigation"},
    items.map((item, i) => React.createElement("li", {key: i}, item))
);
// 这样就不会报错了

四.React Components

每一个用户界面(UI)都由各个零件(parts)组成,在React中我们称这些Parts为 Components,创建组件有3种方法:

  1. React.createClass({});
  2. ES6 class继承的方法
  3. stateless functional components

1.React.createClass()

我们能在这个方法中引用 this,指向组件实例,属性能够通过 this.props 来访问

const nav = React.createClass({
    displayName: "navigation", // 组件的显示名称
    render() {
        return  React.createElement("ul", {"className": "navigation"},
        React.createElement("li", null, "Home"),
        React.createElement("li", null, "Images"),
        React.createElement("li", null, "Videos"),
        React.createElement("li", null, "Maps")
    }
});
// 通过组件来创建 React元素
const list = React.createElement(nav, null, null);
ReactDOM.render(list, document.getElementById("container"));

数据可以通过属性的方式React组件中:

const items = [
    "Home",
    "Images",
    "Videos",
    "Maps"
];
const nav = React.createClass({
    displayName: "navigation",
    render() {
        return React.createElement(
            "ul", 
            {"className": "navigation"},
            this.props.items.map(
                (item, i) =>
                    React.createElement("li", {key: i}, item) 
            )
        );
    }
});

// 通过组件来创建 React元素, 注意items作为属性传入
const list = React.createElement(nav, {items}, null);
// 渲染
ReactDOM.render(list, document.getElementById("container"));

可以通过Ract developer tools 查看组件:

<navigation items=["Home", "Images"...]>
    <ul className="navigation">
        <li key="0">Home</li>
        <li key="0">Images</li>
        <li key="0">Videos</li>
        <li key="0">Maps</li>
    </ul>
</navigation>

组件的本质是对象,以下表示方法是大多数 MVC 中 视图(VIEWS) 的表示方法, 即将方法分离出来

const nav = React.createClass({
    displayName: "navigation",
    // 单独将方法提出来
    renderList(item, i) {
        return React.createElement("li", {key: i}, item)
    },
    render() {
        return React.createElement(
            "ul", 
            {"className": "navigation"},
            // 引用对象中的其他方法
            this.props.items.map(this.renderList)
        );
    }
});

2.抽象类React.Component, 使用ES6语法

我们可以通过继承的方法来创建自定义组件

class MyComponent extends React.Component {
    renderList(item, i) {
        return React.createElement("li", {key: i}, item)
    },
    render() {
        return React.createElement(
            "ul", 
            {"className": "navigation"},
            this.props.items.map(this.renderList)
        );
    }
}

3.无状态函数组件

Stateless functional components are functions not objects, 因此它没有 this 作用域。有以下几个特定:

  1. 它们是简单的纯函数,应用中更多的使用,除非有其他要求要使用React.createClass,或继承
  2. 函数接受 properties, 返回 一个DOM
  3. 这是函数编程的一种实践;
  4. 要引用 this 时则不能使用无状态函数组件

比如上面的例子可以写为:

const nav = props =>
    React.createElement("ul", {"className": "navigation"},
            props.items.map(
                (item, i) =>
                    React.createElement("li", {key: i}, item) 
            )
    )

其他渲染步骤和上面的都一样。

可以通过参数解构的方法改写上面的函数:

const nav = ({items}) =>   // 主要对象的解构这个地方要添加括号
    React.createElement("ul", {"className": "navigation"},
            items.map(
                (item, i) =>
                    React.createElement("li", {key: i}, item) 
            )
    )

// 上面的解构为
let {items} = props;

4.ReactDOM 渲染的优点

ReactDOM渲染如果DOM解构有变化,只更新变化部分,而不是全部重新加载,重新渲染,这样很大的优化了性能


五.Factories

这是另一种创建React Elements 的方法,可以通过React.createFactory

1.React.DOM.ElementType

这种方法用于创建 HTML 类型的 React 元素:

React.DOM.ul({"className": "navigation"}, 
    React.DOM.li(null, "Home"),
    React.DOM.li(null, "Home"),
    React.DOM.li(null, "Home"),
    React.DOM.li(null, "Home")
);

当然这也可以添加数据的方式:

const items = [
    "Home",
    "Images",
    "Videos",
    "Maps"
];
const list =  React.DOM.ul({"className": "navigation"}, 
    items.map((item, i) => 
        React.DOM.li({key: i}, item)
    )
);
ReactDOM.render(
    list,
    document.getElementById("container")
)

2.调用组件作为函数

React.createFactory() 快速将一个组件渲染成一个React Element

const {render} = ReactDOM; // 解构

const items = [
    "Home",
    "Images",
    "Videos",
    "Maps"
];    
// 无状态函数组件
const list => ({items}) =>
    React.createElement(
        "ul", 
        {"className": "navigation"}
        items.map((item, i) => 
            React.createElement("li", {key: i}, ite,)
        )
    )
// 快速将一个组件渲染成React Element
// myList 变为一个函数,接受 属性 和 子元素 作为参数
const myList = React.createFactory(list);

// 渲染
render(
    myList({items}),
    document.getElementById("container")
)

如果不使用 JSX 的话,会发现工厂(factories)方法比反复的书写React.createElement方便;但是如果JSX, 将会发现永远不会使用工厂方法, 下一章将会通过 JSX TAG 来创建 React Elements


总结

通过上面的介绍,我们应该能掌握以下几点:

  1. 掌握 React Elements 是什么
  2. ReactDOM类工具, 如何将一个React元素渲染成 DOM元素
  3. React Components 和 React Elements之间的联系, 如何将React组件转变成React元素,然后再渲染成DOM元素, 书写组件的几种方法
  4. 分离数据与UI的写法
  5. 常用方法有:
    • React.createElement(ElementType, props, children);
    • ReactDOM.render(reactElement, target);
    • React.createClass(obj);
    • React.DOM.HTMLElementType(prop, children);
    • React.createFactory(reactComponent)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容