React 简明教程

2018-01-13 Saturday     misc

React 起源于 Facebook 的内部项目,据说是因为该对市场上 JavaScript MVC 框架都不满意,就决定自己写一套,用来架设 Instagram 的网站,做出来以后,发现这套东西很好用,就在 2013 年 5 月开源了。

其设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。

这里简单介绍其使用方法。

react logo

1. 安装使用

React 的安装包可以从 官网 下载,简单的示例可以参考 Try React,其中 Single File Example 是一个单页的示例。

关于发布版本可以查看 Github React Release

2. HTML 模版

最简单的可以参考如上的 Single File Example ,将其中的 JS 从 Github React Release 下载后,可以简化如下。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>Hello World</title>
		<script src="../build/react.js"></script>
		<script src="../build/react-dom.js"></script>
		<script src="../build/browser.min.js"></script>
	</head>
	<body>
		<div id="example"></div>
		<script type="text/babel">
			ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById('root'));
		</script>
	</body>
</html>

需要注意的是,在最后一个 <script> 标签的 type 属性为 text/babel,这是因为 React 独有的 JSX 语法,跟 JavaScript 不兼容。

这里供用到了三个库:A) react.js 是 React 的核心库;B) react-dom.js 是提供与 DOM 相关的功能;C) browser.js 的作用是将 JSX 语法转为 JavaScript 语法,这一步很消耗时间,实际上线的时候,应该将它放到服务器完成。

其中 ReactDOM.render() 是最基本用法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。

3. JMX 语法

如上,HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的语法,它允许 HTML 与 JavaScript 的混写。

var names = ['Alice', 'Emily', 'Kate'];

ReactDOM.render(
    <div>
    {
        names.map(function (name, index) {
            return <div key={index}>Hello, {name}!</div>
        })
    }
    </div>,
    document.getElementById('example')
);

如上是 JSX 的基本语法规则,遇到 HTML 标签 (以 < 开头),就用 HTML 规则解析;遇到代码块 (以 { 开头),就用 JavaScript 规则解析。

实际上,JSX 允许直接在模板插入 JavaScript 变量,如果这个变量是一个数组,则会展开这个数组的所有成员。

var arr = [
    <h1 key="1">Hello world!</h1>,
    <h2 key="2">React is awesome</h2>,
];
ReactDOM.render(
    <div>{arr}</div>,
    document.getElementById('example')
);

上面代码的arr变量是一个数组,结果 JSX 会把它的所有成员,添加到模板。

4. 组件

允许将代码封装成组件,并像插入普通 HTML 标签一样,在网页中插入组件,React.createClass 方法就用于生成一个组件类。

var HelloMessage = React.createClass({
    render: function() {
        return <h1>Hello {this.props.name}</h1>;
    }
});

ReactDOM.render(
    <HelloMessage name="John" />,
    document.getElementById('example')
);

上面代码中,变量 HelloMessage 就是一个组件类,当模板插入 <HelloMessage /> 时,会自动生成 HelloMessage 的一个实例,所有组件类都必须有自己的 render 方法,用于输出组件。

注意 组件类的第一个字母必须大写,而且只能包含一个顶层标签,否则会报错,例如如下代码包含了两个顶层标签 h1p

var HelloMessage = React.createClass({
    render: function() {
        return <h1>
            Hello {this.props.name}
        </h1><p>
            some text
        </p>;
    }
});

组件的用法与原生的 HTML 标签完全一致,可以任意加入属性,比如 <HelloMessage name="John"> ,就是 HelloMessage 组件加入一个 name 属性,值为 John。组件的属性可以在组件类的 this.props 对象上获取,比如 name 属性就可以通过 this.props.name 读取。

添加组件属性,需要注意 class 属性需要写成 classNamefor 属性需要写成 htmlFor ,这是因为 classforJavaScript 的保留字。

5. this.props.children

this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性,它表示组件的所有子节点。

var NotesList = React.createClass({
    render: function() {
        return (
            <ol>
                {
                    React.Children.map(this.props.children, function (child) {
                        return <li>{child}</li>;
                    })
                }
            </ol>
        );
    }
});

ReactDOM.render(
    <NotesList>
        <span>hello</span>
        <span>world</span>
    </NotesList>,
    document.getElementById('root')
);

上面代码的 NoteList 组件有两个 span 子节点,它们都可以通过 this.props.children 读取。

注意 this.props.children 的值有三种可能:A) 如果当前组件没有子节点,它就是 undefined;B) 如果有一个子节点,数据类型是 object;C) 如果有多个子节点,数据类型就是 array 。

为此提供一个工具方法 React.Children 来处理 this.props.children,可用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型。

6. PropTypes

组件的属性可以接受任意值,字符串、对象、函数等等都可以;有时需要一种机制,验证别人使用组件时,提供的参数是否符合要求。

组件类的 PropTypes 属性,就是用来验证组件实例的属性是否符合要求。

var data = 123;

var MyTitle = React.createClass({
    propTypes: {
        title: React.PropTypes.string.isRequired,
    },

    render: function() {
        return <h1> {this.props.title} </h1>;
    }
});

ReactDOM.render(
    <MyTitle title={data} />,
    document.getElementById('example')
);

上面的 Mytitle 组件包含一个 title 属性,且通过 PropTypes 告诉 React,这个 title 属性是必须的,而且它的值必须是字符串,如上设置为了数值那么就会报错。

此时,控制台会显示一行错误信息。

Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.

更多的 PropTypes 设置,可以查看官方文档

此外,可以通过 getDefaultProps() 方法可以用来设置组件属性的默认值。

var MyTitle = React.createClass({
    getDefaultProps : function () {
        return {
            title : 'Hello World'
        };
    },

    render: function() {
        return <h1> {this.props.title} </h1>;
    }
});

ReactDOM.render(
    <MyTitle />,
    document.body
);

上面代码会输出 Hello World

7. 获取真实的DOM节点

组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM),只有当它插入文档以后,才会变成真实的 DOM 。

所有 DOM 变动都先发生在虚拟 DOM,然后再将实际发生变动的部分,反映在真实 DOM 上,该算法叫做 DOM diff ,可以提高网页性能。

但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性。

var MyComponent = React.createClass({
    handleClick: function() {
        this.refs.myTextInput.focus();
    },
    render: function() {
        return (
            <div>
                <input type="text" ref="myTextInput" />
                <input type="button" value="Focus the text input" onClick={this.handleClick} />
            </div>
        );
    }
});

ReactDOM.render(
    <MyComponent />,
    document.getElementById('root')
);

上面代码中,组件 MyComponent 的子节点有一个文本输入框,用于获取用户的输入,这时就必须获取真实的 DOM 节点,虚拟 DOM 是拿不到用户输入的。

为了做到这一点,文本输入框必须有一个 ref 属性,然后 this.refs.[refName] 就会返回这个真实的 DOM 节点。

React 组件支持很多事件,除了 Click 事件以外,还有 KeyDownCopyScroll 等,完整的事件清单请查看 官方文档

8. this.state

组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI 。

var LikeButton = React.createClass({
    getInitialState: function() {
        return {liked: false};
    },
    handleClick: function(event) {
        this.setState({liked: !this.state.liked});
    },
    render: function() {
        var text = this.state.liked ? 'like' : 'haven\'t liked';
        return (
            <p onClick={this.handleClick}>
                You {text} this. Click to toggle.
            </p>
        );
    }
});

ReactDOM.render(
    <LikeButton />,
    document.getElementById('root')
);

上面代码是一个 LikeButton 组件,其中 getInitialState() 用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。

用户点击组件状态发生变化,this.setState() 修改状态值,每次修改后会自动调用 this.render() 再次渲染组件。

其中 this.propsthis.state 都用于描述组件特性,易混淆,简单区分方法是:A) this.props 表示那些一旦定义,就不再改变的特性;B) this.state 会随用户互动而产生变化的特性。

9. 表单

用户在表单填入的内容,属于用户跟组件的互动,所以不能用 this.props 读取。

var Input = React.createClass({
    getInitialState: function() {
        return {value: 'Hello!'};
    },
    handleChange: function(event) {
        this.setState({value: event.target.value});
    },
    render: function () {
        var value = this.state.value;
        return (
            <div>
                <input type="text" value={value} onChange={this.handleChange} />
                <p>{value}</p>
            </div>
        );
    }
});

ReactDOM.render(<Input/>, document.getElementById('root'));

上面代码中,文本输入框的值,不能用 this.props.value 读取,而要定义一个 onChange() 事件的回调函数,通过 event.target.value 读取用户输入的值。

10. 组件的生命周期

组件的生命周期分成三个状态。

React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。

下面是一个例子。

var Hello = React.createClass({
    getInitialState: function () {
        return { opacity: 1.0 };
    },

    componentDidMount: function () {
        this.timer = setInterval(function () {
            var opacity = this.state.opacity;
            opacity -= .05;
            if (opacity < 0.1) {
                    opacity = 1.0;
            }
            this.setState({
                opacity: opacity
            });
        }.bind(this), 100);
    },

    render: function () {
        return (
            <div style= { { opacity: this.state.opacity } }>
                Hello {this.props.name}
            </div>
        );
    }
});

ReactDOM.render(
    <Hello name="world"/>,
    document.getElementById('root')
);

上面代码在 Hello 组件加载以后,通过 componentDidMount() 设置一个定时器,每隔 100 毫秒,就重新设置组件的透明度,从而引发重新渲染。

另外,组件的 style 属性的设置方式也值得注意,不能写成 style="opacity:{this.state.opacity};" 而要写成 style= { { opacity: this.state.opacity } } 这是因为 React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象。

11. AJAX

组件的数据来源,通常是通过 AJAX 请求从服务器获取,可以使用 componentDidMount() 设置 AJAX 请求,等到请求成功,再用 this.setState() 方法重新渲染 UI 。

var UserGist = React.createClass({
    getInitialState: function() {
        return {
            username: '',
            lastGistUrl: ''
        };
    },

    componentDidMount: function() {
        $.get(this.props.source, function(result) {
            var lastGist = result[0];
            this.setState({
                username: lastGist.owner.login,
                lastGistUrl: lastGist.html_url
            });
        }.bind(this));
    },

    render: function() {
        return (
            <div>
                {this.state.username}'s last gist is <a href={this.state.lastGistUrl}>here</a>.
            </div>
        );
    }
});

ReactDOM.render(
    <UserGist source="https://api.github.com/users/octocat/gists" />,
    document.getElementById('root')
);

另外一个示例如下。

var RepoList = React.createClass({
    getInitialState: function() {
        return {
            loading: true,
            error: null,
            data: null
        };
    },

    componentDidMount() {
        this.props.promise.then(
            value => this.setState({loading: false, data: value}),
            error => this.setState({loading: false, error: error})
        );
    },

    render: function() {
        if (this.state.loading) {
            return <span>Loading...</span>;
        } else if (this.state.error !== null) {
            return <span>Error: {this.state.error.message}</span>;
        } else {
            var repos = this.state.data.items;
            var repoList = repos.map(function (repo, index) {
                return (
                    <li key={index}><a href={repo.html_url}>{repo.name}</a>
                    ({repo.stargazers_count} stars) <br/> {repo.description}</li>
                );
            });
            return (
                <main>
                    <h1>Most Popular JavaScript Projects in Github</h1>
                    <ol>{repoList}</ol>
                </main>
            );
        }
    }
});

ReactDOM.render(
    <RepoList promise={$.getJSON('https://api.github.com/search/repositories?q=javascript&sort=stars')} />,
    document.getElementById('example')
);

组件生命周期

react lifecycle

参考

这里大部分是参考阮一峰的 React 入门实例教程



如果喜欢这里的文章,而且又不差钱的话,欢迎打赏个早餐 ^_^


About This Blog

Recent Posts

Categories

Related Links

  • RTEMS
    RTEMS
  • GNU
  • Linux Kernel
  • Arduino

Search


This Site was built by Jin Yang, generated with Jekyll, and hosted on GitHub Pages
©2013-2019 – Jin Yang