转载:阮一峰的网络日志 15年出的 React 入门实例教程

react_learn

https://gitee.com/shafish/react_learn

Description

facebook 开源前端框架学习记录

例子

react-demos

学前个人看法

因为之前一直都是侧重学java方面后端技术,虽早听闻世上存有三大前端开发框架,但一直没打算学(懒+拖)。后来看到说可以做后端??嗯?什么鬼,是不是是时候学了?感觉,是滴:
以上。

react历史

不多说了,自己百度吧

html模板

大致结构:

<!DOCTYPE html>
<html>
  <head>
    <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">
      // ** Our code goes here! **
    </script>
  </body>
</html>

模板需要注意两个事情:

  1. 如果需要使用JSX语法,必须由type值为text/babbel的script的标签包围;
  2. 代码用到的react js库,比如react、readct-dom、browser等等都必须首先加载(放在开头)。

ReactDOM.render()

函数用于将模板转为html代码,并插入指定的dom节点内容。相当于render的两个参数。
详细使用:

    <script type="text/babel">
        ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
        );
    </script>

上面的代码就是将h1标签,整个插入到id为example节点内。运行结果:

JSX语法

是不是对上面例子里,h1内容可以直接写在js代码里感到奇怪??
可以不加引号写在js代码中,这就是JSX语法,它允许Html与JavaScript代码混写。
jsx语法有几个个特点:

  1. html标签用<> -- html规则解析
  2. 代码块用{} -- js解析
  3. js变量可以直接插入模板中,用{变量}获取变量值

例子:

var names = ['alice','emily','kate'];
ReactDOM.render(
    <div>
    {
        names.map(function(name){
            return <div>Hello! {name}! </div>
        })
    }
    </div>,
    document.getElementById('example')
)
var arr = [
  <h1>Hello world!</h1>,
  <h2>React is awesome</h2>,
];
ReactDOM.render(
  <div>{arr}</div>,
  document.getElementById('example')
);

组件

react允许将代码封装成一个组件,也就是我们html中用到的标签,然后跟普通html标签一样使用。创建组件需要继承React.Component。没错,是继承

class HelloMessage extends React.Component {
    render(){
        return <h1>Hello,{this.props.name}</h1>;
    }
}
ReactDOM.render(
    <HelloMessage name="shafish"/>,
    document.getElementById("example")
);

我们可以发现组件的几大特点:

  1. 组件需要用class定义,(组件名/类名??)组件类名必须要大写开头
  2. 组件类下必须有reander()函数
  3. 使用时,标签参数xxxx,需要用this.props.xxxx获取。比如上面的name参数。
  4. reander函数内,只能包含一个顶层标签,比如上面的h1标签,顶层标签不能并列。
    以上。

遵守上面几点就可以灵活使用组件类来自定义我们的标签咧。组件可以添加多个属性;组件的属性也可以接受任意值,字符串、对象、函数等等都可以。

注意:
class属性强制为className
for属性强制为htmlFor
因为class和for是js的保留字。

ps:保留字是语言中定义具有特殊含义的标识符,保留字不能作为标识符使用

this.props.children

上面说了this.props.xxx是拿来去属性值的,但是有一个例外,就是this.props.children
它表示了组件的所有子节点:

class NoteList extends React.Component{
    render() {
        return (
        <ol>
            {  /* 之前用过map,大概用法就是:集合.map(单个元素,处理当个元素函数),函数里用到的一个无序列表li*/
            React.Children.map(this.props.children, function (child) {
                return <li>{child}</li>;
            })
            }
        </ol>
        );
    }
}
ReactDOM.render(
    <NoteList> 
        <span>hello</span>
        <span>world</span>
        <span>shafish</span>
    </NoteList>,
    document.getElementById("example")
);

建议:
vscode的语言模式选javascript react,或者百度查查vscode关于react的语法查错,老夫打错好几次了。

上面的代码NoteList组件标签在用的时候,包含了三个子节点,都可以通过this.props.children获取:

注意:
有没有想过如果NoteList标签里不设子节点会是什么情况??this.props.children 的值有三种可能:需要注意了

  1. 如果当前组件没有子节点,它就是 undefined ;
  2. 如果有一个子节点,数据类型是 object ;
  3. 如果有多个子节点,数据类型就是 array;

你们注意到React.Children了吗,React提供了这个工具方法,不管什么类型,放进map中,迭代就行了。更多Children用法

PropTypes

上面在组件那小节提到-组件的属性可以接受任意值,字符串、对象、函数等等。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。
组件类PropTypes属性,就是用来验证组件实例属性是否符合要求的!!
记得先导包

<script src="react-demos/build/prop-types.js"></script>
var data = 123; 
class MyTitle extends React.Component {
    static propTypes = {
        title: PropTypes.string.isRequired, /*指定title必须为string*/
    }
    render() {
        return <h1> {this.props.title} </h1>;
    }
}
ReactDOM.render(
    <MyTitle title={data} />, /*强制转类型,不过js的类型好像用的时候区分不大吧,先mark*/
    document.getElementById('example')
);

此时显示是可以显示的,但是控制台就存在验证不通过报错了。

更多PropTypes用法

getDefaultProps

跟上面那个proptypes用法差不多,但是这个是用来设置组件属性默认值的。

class MyTitle extends React.Component {
  getDefaultProps : function () {
    return {
      title : 'Hello World'
    };
    render() {
        return <h1> {this.props.title} </h1>;
    }
}
ReactDOM.render(
    <MyTitle />, /*用默认值就好*/
    document.getElementById('example')
);

获取真实的DOM节点

组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM。只有当它插入文档以后,才会变成真实的 DOM 。
根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。

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

class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.myTextInput = React.createRef();
        this.handleClick = this.handleClick.bind(this)
    }
    handleClick() {
        this.myTextInput.current.focus();
    }
    render() {
        return (
        <div>
            <input type="text" ref={this.myTextInput} />
            <input type="button" value="Focus the text input" onClick={this.handleClick} />
        </div>
        );
    }
}
ReactDOM.render(
    <MyComponent />,
    document.getElementById('example')
);

这个就有点复杂了,一步步来看。

  • 先看布局,render()里包含一个文本输入和按钮。输入框需要获取用户输入,前面说了组件类是虚拟的dom,为了做到用户输入,文本输入框必须有一个 ref 属性,然后 this.refs.[refName] 就会返回这个真实的 DOM 节点。
  • this.refs.[refName] 属性获取的是真实 DOM。所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs.[refName] 属性。
  • React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等

更多事件用法

this.state

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

class LikeButton extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        liked: false
        }
        this.handleClick = this.handleClick.bind(this)
    }
    handleClick(event) {
        this.setState({ liked: !this.state.liked });
    }
    render() {
        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('example')
);

一步步来:

  • 组件LikeButtongetInitialState方法用于定义初始状态,也就是一个对象,这个对象可以通过this.state属性读取。当用户点击组件,导致状态变化,this.setState 方法就修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。
  • 由于this.propsthis.state 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。

表单

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

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        value: 'Hello!'
        }
        this.handleChange = this.handleChange.bind(this)
    }
    handleChange(event) {
        this.setState({ value: event.target.value });
    }
    render() {
        var value = this.state.value;
        return (
        <div>
            <input type="text" value={value} onChange={this.handleChange} />
            <p>{value}</p>
        </div>
        );
    }
}

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

上面代码中,文本输入框的值,不能用 this.props.value 读取,而要定义一个 onChange 事件的回调函数,通过 event.target.value 读取用户输入的值。textarea 元素、select元素、radio元素都属于这种情况
更多表达用法

组件的生命周期

组件的生命周期:

    1. Mounting:已插入真实 DOM
    1. Updating:正在被重新渲染
    1. Unmounting:已移出真实 DOM

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

componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()

React 还提供两种特殊状态的处理函数:

componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用

例子:

class Hello extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        opacity: 1.0
        };
    }
    componentDidMount() {
        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() {
        return (
        <div style={{opacity: this.state.opacity}}>
            Hello {this.props.name}
        </div>
        );
    }
}
ReactDOM.render(
    <Hello name="world"/>,
    document.getElementById('example')
);

写的时候是懵的,这都是什么??
我们只看render函数,除了一个style参数其他都是之前熟悉的。继续

  • setState函数,我是知道的,作用跟在flutter中用法是一样的,拿来刷新状态。
  • setInterval设定了一个100毫秒的定时器,来处理opacity值,emmm? opacity是什么,opacity属性是css3中设置元素不透明级别的一个参数。而且React组件在构造函数里设置的一个初始值,this.state.opacity
  • componentDidMount,组件插入dom之后。每隔0.1s,opacity以0.5递减,低于0.1又是一个循环。

所以最后显示的结果就是hello world在不停地渐变。

注意:
取opacity值的时候,不是style="{opacity: this.state.opacity}",而是style={{opacity: this.state.opacity}}
这是因为 React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象

ajax

last one
组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount 方法设置 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI :
上面代码使用 jQuery 完成 Ajax 请求,这是为了便于说明。React 本身没有任何依赖,完全可以不用jQuery,而使用其他库。
我们甚至可以把一个Promise对象传入组件:

class UserGist extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            username: '',
            lastGistUrl: ''
        }
    }
    componentDidMount() {
        $.get(this.props.source, function(result) {
            var lastGist = result[0];
            this.setState({
            username: lastGist.owner.login,
            lastGistUrl: lastGist.html_url
            });
        }.bind(this));
    }
    render() {
        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('example')
);

从Github的API抓取数据,然后将Promise对象作为属性,传给RepoList组件:

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

如果Promise对象正在抓取数据(pending状态),组件显示"正在加载";如果Promise对象报错(rejected状态),组件显示报错信息;如果Promise对象抓取数据成功(fulfilled状态),组件显示获取的数据:

class RepoList extends React.Component{
  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) {
        return (
          <li>
            <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>
      );
    }
  }
};

现在还没有需求用react,看什么时候找个react的项目来做做,网上看到不少大佬用react做的网站都很漂亮。
感觉前端也不容易呀,js还能玩出这么多东西,mark。