[关闭]
@wy 2018-10-23T12:44:55.000000Z 字数 6291 阅读 525

react学习

react


React介绍

React.js 是一个帮助你构建页面 UI 的库。主要解决了数据和UI同步的问题。

React.js 将帮助我们将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。

一个组件的显示形态和行为有可能是由某些数据决定的。而数据是可能发生改变的,这时候组件的显示形态就会发生相应的改变。

React.js 不是一个框架,它只是一个库。它只提供 UI (view)层面的解决方案。在实际的项目当中,它并不能解决我们所有的问题,需要结合其它的库,例如 Redux、React-router 等来协助提供完整的解决方法。

环境搭建

使用create-react-app脚手架工具搭建项目,全局安装:

npm i -g create-react-app

之后就可以使用create-react-app命令

创建项目目录

create-react-app

创建之后,cd < project-dir >进入项目目录,启动项目:

npm start

启动后,自动打开浏览器。

JSX介绍

  1. const element = < h1>Hello, world!</h1>;

这种有趣的标签语法既不是字符串也不是HTML。这称作是JSX ,他是 JavaScrip 的一种扩展语法。
JSX语法用来描述 UI 信息,也就是描述组件长什么样的。

嵌入表达式

可以用 花括号 把任意的 JavaScript 表达式 嵌入到 JSX 中。

类似:1+1;函数之类的。

  1. let data = 'hello react';
  2. const element1 = < h1>{data}</h1>;
  3. const element2 = < h1>{1+1}</h1>;
  4. const element3 = < h1>{[1,2,3].map(item => item * 2)}</h1>;

指定属性值表达式

  1. let attr = 'hello react';
  2. const element1 = < h1 hello = {attr}>{attr}</h1>;

你可以将字符串常量作为属性值传递。下面这两个 JSX 表达式是等价的:

  1. <div message="hello world"></div>
  2. <div message={'hello world'}></div>

如果你没有给属性传值,它默认为 true。因此下面两个 JSX 是等价的:

  1. <div autocomplete ></div>
  2. <div autocomplete={true} ></div>

注意:写的jsx也是js,不能出现关键字:

渲染列表和条件判断

直接在可以使用原生的for循环或者map来渲染结构。

  1. let html = [];
  2. let data = [1,2,3];
  3. for(let i = 0; i < data.length; i++ ){
  4. html.push(<span key={i}>{data[i]}</span>)
  5. }

渲染页面中:

  1. <div>{html}</div>

jsx中表达式可以是一个数组,内部会循环插入到指定的位置,如果是html结构会直接解析。

一般这样循环数组数据,把结构存放在数组后渲染在页面中,可以使用声明式的方式调用map函数代替:

  1. let data = [1,2,3];
  2. < div>
  3. {
  4. data.map((item,index) => < span key={index}>{item}< /span>)
  5. }
  6. < /div>

注意:

Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。

根据条件选择性的渲染结构:

  1. function getGreeting(user) {
  2. if (user) {
  3. return <h1>Hello, {user}!</h1>;
  4. }
  5. return <h1>Hello, Stranger.</h1>;
  6. }

元素渲染

首先我们在一个 HTML 页面中添加一个 id="root" 的 < div>

  1. <div id="root"></div>

在此 div 中的所有内容都将由 React DOM 来管理,所以我们将其称之为 “根” DOM 节点。

我们用React 开发应用时一般只会定义一个根节点。但如果你是在一个已有的项目当中引入 React 的话,你可能会需要在不同的部分单独定义 React 根节点。

  1. ReactDOM.render(jsx,root)

组件

在构建界面开发领域,组件应该是一种独立的、可复用的交互元素的封装,专注于构建每一个单独的部件。组件是由模板结构、数据、样式、业务逻辑组成的。例如在HTML标签中的input、select这样的元素,都属于是组件,由浏览器内置提供。当然这些组件远远不能满足我们的开发需求,需要做开发者自己封装组件。

组件化开发是什么?

页面上的每个 独立的 可视/可交互区域视为一个组件;
每个组件相对独立,页面只不过是组件的容器,组件自由组合形成功能完整的界面;
当不需要某个组件,或者想要替换组件时,可以整个目录删除/替换。
每个组件对应一个工程目录,组件所需的各种资源都在这个目录下就近维护;
参考:https://github.com/fouber/blog/issues/10

组件化开发的好处:

如何设计一个组件?

在React中,核心就是组件。组件有输入、自己的状态和输出。
- 输入在React中叫做prop
- 自己的状态在React中叫做state
- 输入在React中是render函数返回值。

函数定义

定义一个组件最简单的方式是使用JavaScript函数:

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }

使用:

  1. <Welcome name="hello,react"></Welcome>

该函数是一个有效的React组件,它接收一个单一的“props”对象并返回了一个React元素。我们之所以称这种类型的组件为函数定义组件,是因为从字面上来看,它就是一个JavaScript函数。

类定义组件

你也可以使用 ES6 class 来定义一个组件:

  1. class Welcome extends React.Component {
  2. render() {
  3. return <h1>Hello, {this.props.name}</h1>;
  4. }
  5. }

使用:

  1. <Welcome name="hello,react"></Welcome>

注意:

组件名字未来使用自定义标签的形式渲染的,要被当做是组件形式,而不是自定义的元素标签,所以组件名称必须以大写字母开头。

渲染组件

Props

组件是相互独立、可复用的单元,一个组件可能在不同地方被用到。
组件封装后,需要定制组件的数据,需要传入Props。实际上组件看起来就像函数或者是一个类,Props实际是就是传入的参数。

当React遇到的元素是用户自定义的组件,它会将JSX属性作为单个对象传递给该组件,这个对象称之为“props”。
把参数放在标签的属性当中,所有的属性都会作为 props 对象的键值:

  1. <Welcome name="hello,react"></Welcome>

注意:
无论是使用函数或是类来声明一个组件,它决不能修改它自己的props
类似于上面的这种函数称为“纯函数”,它没有改变它自己的输入值,当传入的值相同时,总是会返回相同的结果。

纯函数

简单来说,一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数。这么说肯定比较抽象,我们把它掰开来看:
- 函数的返回结果只依赖于它的参数。
- 函数执行过程里面没有副作用。

设置默认的Props

  1. static defaultProps = {
  2. title: '默认的标题'
  3. }

验证Props

使用模块prop-types,npm地址:https://www.npmjs.com/package/prop-types

  1. import PropTypes from 'prop-types';
  2. class Greeting extends React.Component {
  3. render() {
  4. return (
  5. <h1>Hello, {this.props.name}</h1>
  6. );
  7. }
  8. }
  9. Greeting.propTypes = {
  10. name: PropTypes.string
  11. };

具体类型设置参考:https://www.npmjs.com/package/prop-types

事件处理

React 元素的事件处理和 DOM元素的很相似。但是有一点语法上的不同:

没有经过特殊处理的话,这些 on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上。

这些事件属性名都必须要用驼峰命名法。

React中写法:

  1. <button onClick={activateLasers}>
  2. Activate Lasers
  3. </button>

类定义组件中的事件

当你使用 ES6 class 语法来定义一个组件的时候,事件处理器会成为类的一个方法

  1. class Toggle extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. // 把this绑定指向当前的组件的实例
  5. this.handleClick = this.handleClick.bind(this);
  6. }
  7. handleClick() {
  8. console.log(this)
  9. }
  10. render() {
  11. return (
  12. <button onClick={this.handleClick}>
  13. 测试this
  14. </button>
  15. );
  16. }
  17. }
  18. ReactDOM.render(
  19. <Toggle />,
  20. document.getElementById('root')
  21. );

如果使用 bind 让你很烦,这里有两种方式可以解决。如果你正在使用实验性的属性初始化器语法,你可以使用属性初始化器来正确的绑定回调函数:

  1. class LoggingButton extends React.Component {
  2. handleClick = () => {
  3. console.log('this is:', this);
  4. }
  5. render() {
  6. return (
  7. <button onClick={this.handleClick}>
  8. Click me
  9. </button>
  10. );
  11. }
  12. }

以上这种语法暂时在原生的ES6不支持,需要借助babel的插件来做,https://babeljs.io/docs/plugins/transform-class-properties/

如果你没有使用属性初始化器语法,你可以在回调函数中使用 箭头函数:

  1. class LoggingButton extends React.Component {
  2. handleClick() {
  3. console.log('this is:', this);
  4. }
  5. render() {
  6. // This syntax ensures `this` is bound within handleClick
  7. return (
  8. <button onClick={(e) => this.handleClick(e)}>
  9. Click me
  10. </button>
  11. );
  12. }
  13. }

这个语法有个问题就是每次 LoggingButton 渲染的时候都会创建一个不同的回调函数。

向事件监听函数传参

使用bind

  1. <button onClick={this.handleClick.bind(this,参数)}>
  2. Click me
  3. </button>

使用箭头函数

  1. <button onClick={(e) => this.handleClick(e,参数)}>
  2. Click me
  3. </button>

合成事件可以参考这里:https://doc.react-china.org/docs/events.html

状态state

一个组件的显示形态是可以由它数据状态和配置参数决定的。一个组件可以拥有自己的状态。

类定义的组件设置状态

  1. constructor(props){
  2. super(props);
  3. this.state = {
  4. value: 1
  5. }

跟新状态,必须使用setState:

  1. this.setState({
  2. value:2
  3. })

取值

  1. {this.state.value}

当我们调用setState函数时会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上。

异步更新

当你调用 setState 的时候,React.js 并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state 当中,然后再触发组件更新。这一点要好好注意。可以体会一下下面的代码:

  1. this.setState({ count: 0 }) // => this.state.count 还是 undefined
  2. this.setState({ count: this.state.count + 1}) // => undefined + 1 = NaN
  3. this.setState({ count: this.state.count + 2}) // => NaN + 2 = NaN

如果需要同步运算,可给setState传入函数:

  1. this.setState((prevState) => {
  2. return { count: 0 }
  3. })
  4. this.setState((prevState) => {
  5. return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
  6. })
  7. this.setState((prevState) => {
  8. return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
  9. })
  10. // 最后的结果是 this.state.count 为 3

这样就可以达到上述的利用上一次 setState 结果进行运算的效果。

上面我们进行了三次 setState,但是实际上组件只会重新渲染一次,而不是三次;这是因为在 React.js 内部会把 JavaScript 事件循环中的消息队列的同一个消息中的 setState 都进行合并以后再重新渲染组件。
在使用 React.js 的时候,并不需要担心多次进行 setState 会带来性能问题。

数据流

数据流指的是数据的流向,在React中,数据是自定向下单向流动的,即父组件到子组件。这个原则让组件之间的关系变得简单并且可以预测。

在顶层初始化了props,会遍历整颗组件树,渲染相关的子组件。props本身是不可变的。

state只关心组件内部的自己的状态,这些状态只在组件内部改变。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注