React基础知识

1.React介绍

中文官网

英文官网

  1.  react、vue、angular俗称前端框架三驾马车
  2. React起源于Facebook的内部项目,因为该公司对市场上所有的JavaScript MVC框架都不满意,就决定自己写一套,用来架设Instagram的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了
  3. React是一个用于构建用户界面的JavaScript库
  4. React主要用户构建UI,很多人认为React是MVC中的V(视图)

1.1.react特点

1.2.react和vue的区别

1.首先,这两个框架,都是以组件化的思想进行开发的!

2.从开发团队上进行比较:

  1. React的开发团队,是Facebook官方大牛团队,团队技术实力雄厚;
  2. Vue:第一版本,主要是作者尤雨溪进行维护。

3.从社区方面进行对比:

  1. React社区早,解决方案多
  2. Vue社区晚一点,解决方案相对少一点

4.从移动App开发方面:

  1. 使用React这门技术,可以分分钟转到ReactNative的开发中
  2. Vue这门技术,也提供了无缝的转移到移动端开发中,通过weex可以使用Vue的语法,进行移动端APP开发

 

1.3.react核心知识

1.3.1React核心概念

  • 虚拟DOM(Virtual DOM)
  • Diff算法(Diff Algorithm)
  • 单向数据流渲染(Data Flow)
  • 组件生命周期
  • JSX
  • 一切皆为组件

2.我的第一个React项目

推荐使用yarn来安装node包,时间更快

2.1.新建文件夹,初始化项目

mkdir react-demo

cd react-demo

npm init -y

2.2.安装依赖

yarn add react react-dom webpack webpack-cli webpack-dev-server babel babel-cli babel-core babel-loader@7 babel-preset-react babel-preset-env babel-preset-es2015

2.3.编写文件

2.3.1.新建webpack.config.js

目录下新建webpack配置文件webpack.config.js

module.exports = {
  entry: "./main.js",
  output: {
    path: "/",
    filename: "index.js"
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['env', 'react', 'es2015']
          }
        }
      }
    ]
  }
}

2.3.2.新建页面文件index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="root"></div>
  <script src="./index.js"></script>
</body>
</html>

2.3.3.新建入口文件main.js

目录下新建main.js

import React, { Component } from 'react';
import ReactDom from 'react-dom';

class App extends Component {
    render() {
        return <h1> Hello, world! </h1>
    }
}

ReactDom.render(
    <App />,
    document.getElementById('root')
)

2.3.4.修改package.json

  "scripts": {
    "start": "webpack-dev-server --inline --hot --open --port 8090 --mode development"
  },

2.3.5.运行

yarn start

3.脚手架-我的第一个React项目

create-react-app是react脚手架的名称

脚手架官网

3.1.快速开始

npm5.2或者更高带来的一个新特性,可以不同全局安装脚手架,就可以直接使用脚手架

npx create-react-app my-app

cd my-app

yarn start

 3.2.运行成功

3.3.项目结构

│  .gitignore                git忽略清单
│  package.json                项目配置文件
│  README.md                说明文件
│  
├─public            公共资源
│      favicon.ico           图标 
│      index.html            首页html
│      manifest.json        缓存清单
│      
└─src                源代码
        App.css                组件的样式文件
        App.js                组件文件
        App.test.js            组件的测试文件
        index.css            全局样式文件
        index.js            入口文件
        logo.svg            logo图标
        serviceWorker.js    serviceWorker文件

4.JSX

 jsx是React框架中的一种语法,它是JavaScript的语法拓展。既可以在js文件中写js逻辑代码,也可以在js文件中写HTML语言。

JSX是JavaScript XML的简写

import React from 'react';
import ReactDOM from 'react-dom';

// const App = () => <h1>我就是jsx</h1>;

const App = function () {
  return (
    <h1>我就是jsx</h1>
  );
}

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

4.1.JSX嵌套元素

组件中,必须返回一个根元素

如下列语法是错误的!

const App = function () {
  return (
    <h1>我就是jsx</h1>
    <h1>我就是jsx</h1>
  );
}

假设想要在根目录下直接渲染2个标签,可以使用Fragment标签,类似vue中的template

引入Fragment组件

import React, { Fragment } from 'react';

包裹标签

import React, { Fragment } from 'react';
import ReactDOM from 'react-dom';

const App =  ()=> {
  return (
    <Fragment>
      <h1>我就是jsx</h1>
      <h1>我就是jsx</h1>
    </Fragment>
  )
}

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

4.2.JSX表达式

1.普通渲染

<h1>我就是jsx</h1>

2.数学表达式

<h1>{1 + 1}</h1>

3.字符串

<h1>{'hello world'}</h1>

4.bool类型-无法渲染

<h1>{isBoy}</h1>

5.使用变量

<h1>{msg}</h1>

6.三目运算符

<h1>{isBoy ? "男生" : "女生"}</h1>

7.调用方法

 const format = (msg) => {
  return '---' + msg + '---';
}    

<h1>{format(msg)}</h1>

8.使用对象

const person = {
  name: "沙和尚"
};      

<h1>{person.name}</h1>

完整代码

import React, { Fragment } from 'react';
import ReactDOM from 'react-dom';

const msg = "你们好呀";
const isBoy = false;
const format = (msg) => {
  return '---' + msg + '---';
}
const person = {
  name: "沙和尚"
};

const App = () => {
  return (
    <Fragment>
      <h1>我就是jsx</h1>
      {/* 数字 */}
      <h1>{1 + 1}</h1>
      {/* 字符串 */}
      <h1>{'hello world'}</h1>
      {/* bool类型 */}
      <h1>{isBoy}</h1>
      {/* 使用变量 */}
      <h1>{msg}</h1>
      {/* 3目运算符 */}
      <h1>{isBoy ? "男生" : "女生"}</h1>
      {/* 调用方法 */}
      <h1>{format(msg)}</h1>
      {/* 使用对象 */}
      <h1>{person.name}</h1>
    </Fragment>
  )
}

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

4.3.JSX嵌套JSX JSX循环

按照这个思路去理解即可

  1. index.js文件可以直接写标签入<h1></h1>
  2. 在标签中可以通过{}直接写js如<h1>{‘你们好’}</h1>
  3. 既然1中的js可以直接写标签,那么{}中的js也可以直接写标签

如下所示:

import React from 'react';
import ReactDOM from 'react-dom';

const msg = "今天天气不错";

const App = () => {
  return (
    <div >
      {
        <div>
          <h1>我也是大头 {msg} </h1>
          <h1>我也是大头 {msg} </h1>
        </div>
      }
    </div>
  )
}

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

加强版本,必须要掌握

import React from 'react';
import ReactDOM from 'react-dom';

const list = ['苹果', '香蕉', '雪梨', '西瓜'];


const App = () => {
  return (
    <div >
      {
        <div>
          {
            list.map(function (v) {
              return (
                <h1 key={v}>{v}</h1>
              )
            })
          }
        </div>
      }
    </div>
  )
}

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

终极版 最常用

import React from 'react';
import ReactDOM from 'react-dom';

const list = ['苹果', '香蕉', '雪梨', '西瓜'];

const App = () => {
  return (
    <div >
      {
        <div>
          {
            list.map(v => <h1 key={v}>{v}</h1>)
          }
        </div>
      }
    </div>
  )
}

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

4.4.JSX注释

    {
      // 这里是单行注释
    }
    {
      /*
      这里是多行注释
      这里是多行注释
      这里是多行注释
      这里是多行注释
      */
    }

4.5.JSX标签属性

jsx标签上可以设置绝大部分和以前html标签一样的属性,如checked、图片的src

除此之外

1.html的class属性改为className

 <div className="redCls">大头大头</div>

2.html中label标签的for属性改为htmlFor

<label htmlFor="inp">
        点我点我
          <input id="inp" type="text" />
</label>

3.标签中的自定义属性使用data

 <div data-index={'hello'} >自定义属性</div>

4.渲染html字符串使用dangerouslySetInnerHTML属性

<li dangerouslySetInnerHTML={{__html:"<i>打我呀</i>"}}></li>

5.bool类型的值可以这样用

<input type="checkbox" checked={true} />

6.当属性太多了,可以使用...拓展运算符

const props={
  className:"redCls",
  "data-index":5
}

<div {...props}>展开属性</div>

4.6.JSX的行内样式

JSX可以像传统的HTML标签一样添加行内样式,不同的是,要通过对象的方式来实现。并且属性名是以驼峰命名。

import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css";

const App = () => {
  return (
    <div style={{ color: 'yellow', fontSize: "50px", "backgroundColor": 'blue' }} > 颜色好好看呀 </div>
  )
}

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

4.7.JSX创建组件的本质(了解即可)

JSX俗称 语法糖,提供了一种更好使用的语法让我们使用,其本质是调用React.createElement实现的

React.createElement,接收3个参数

  1. 标签名 如 “div”
  2. 标签上的属性,如{className: "redCls"}
  3. 文本内容或者另一个React.createElement对象或者React.createElement数组
import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css";

const parentProps = {
  className: "redCls",
  "data-index": 100
}

const App = () => {
  return React.createElement(
    "div",
    parentProps,
    [
      React.createElement(
        "span",
        null,
        "你们好呀"
      ),
      React.createElement(
        "span",
        null,
        "你们好呀"
      )
    ]
  )
}

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

5.组件的创建

在React中,组件分为两种,类组件和函数式组件

  1. 简单功能使用函数式组件
  2. 复杂功能使用类组件
  3. 组件名都必须大写

5.1.类组件

  • 使用es6创建class的方式来实现一个组建类
  • 首字母要大写
  • 要继承React中的Component类
  • 必须实现render函数,函数内返回标签
  • 组件有自己的state和生命周期
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import "./index.css";


 class App extends Component {
  render() {
    return (
      <div>
        hello
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

5.2.函数式组件

也叫做无状态组件

简单功能组件可以使用函数式组件,性能更高。

函数式组件其实就是一个函数,只不过函数内部要返回对应的标签

  • 首字母要大写
  • 函数内返回标签
import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css";


// 这个就是函数式组件
const App = () => {
  return (
    <div>这个就是一个简单的函数式组件</div>
  )
}

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

5.3.小结

  • 函数式组件性能更高,因为没有生命周期
  • 函数式组件更方便进行测试
  • 能不用类组件就不要用类组件
  • 当要使用state时,就要使用类组件

6.状态和属性

在React中,状态和属性都可以实现数据动态化,我们现在开始这两个知识

6.1.状态state

在react中,内部组件的数据是通过state来实现和管理

  1. 可以理解为state就是vue中的data
  2. 函数式组件没有自己的state

我们之前使用变量,都是脱离了组件本身,正确来说,组件自身是应该具有私有和独立的数据(状态)的,这个私有的数据和状态就叫做state,以后,只要说到数据的状态那么就是指state

6.1.1.state的声明和使用

在类组件中,state的声明分为两种方式

1.类属性的方式声明

 class Person extends Component {
  // 1 声明 state
  state = {
    date: "2008",
    msg: "大头大头下雨不愁"
  }
  render() {
    return (
      <div>
        {/* 2 使用state */}
        <h1>{this.state.date}</h1>
        <h2>{this.state.msg}</h2>
      </div>
    )
  }
}

2.构造函数中声明

 class Person extends Component {
  // 1 构造函数中 声明 state
  constructor() {
    // 1.1 必须在this之前调用super()方法
    super();
    this.state = {
      date: "2008",
      msg: "大头大头下雨不愁"
    }
  }
  render() {
    return (
      <div>
        {/* 2 使用state */}
        <h1>{this.state.date}</h1>
        <h2>{this.state.msg}</h2>
      </div>
    )
  }
}

6.1.2.state的赋值

state的赋值方式通过this.setState方式来实现

需要注意的是,不能使用this.state.date=100 直接修改

class Person extends Component {
  state = {
    date: 2008
  }

  // 2 事件的声明 要使用箭头函数
  handleClick = () => {
    // 3 获取state中的日期
    let { date } = this.state;
    
    // 4 修改state中的日期
    this.setState({
      date: date + 1
    });

  }
  render() {
    return (
      // 1  绑定事件 事件名必须驼峰命名
      <div onClick={this.handleClick}>
        <h1>{this.state.date}</h1>
      </div>
    )
  }
}

6.1.3.state的赋值是异步的

react为了优化性能,将state的赋值代码改成异步的方式,为了避免反复的设置state而引发的性能损耗问题。

看看下面打印的值

class Person extends Component {
  state = {
    date: 2008
  }
  handleClick = () => {
    let { date } = this.state;

    // 1 修改state中的日期 增加 1000
    this.setState({
      date: date + 1000
    });

    // 2 看看这个date是多少
    console.log(this.state.date);
  }
  render() {
    return (
      <div onClick={this.handleClick}>
        <h1>{this.state.date}</h1>
      </div>
    )
  }
}

有时候,我们希望在一设置值的时候,就希望马上得到最新的state的值,那么可以将代码改为下列的写法,给setState添加一个回调函数,回调中可以获取到修改后的state的值

 class Person extends Component {
  state = {
    date: 2008
  }

  handleClick = () => {
    let { date } = this.state;

    // 添加一个回调函数
    this.setState({
      date: date + 1000
    }, () => {
      // date的值为 3008
      console.log(this.state.date);
    });
  }
  render() {
    return (
      <div onClick={this.handleClick}>
        <h1>{this.state.date}</h1>
      </div>
    )
  }
}

有时候,setState还可以接收一个函数,函数内可以实时获取state中的值,不存在延迟。

    this.setState(preState => {
      console.log("上一次的state", preState.date);
      return {
        date: preState.date + 1000
      }
    })

    this.setState(preState => {
      console.log("上一次的state", preState.date);
      return {
        date: preState.date + 1
      }
    })

6.2.属性props

props意思是属性,一般存在父子组件中,用于父向子传递数据。

子组件不能修改接受到的props值

6.2.1.初体验

1.声明一个类组件HomeTop父组件的数据通过this.props来获取

class HomeTop extends Component {
  render() {
    return (
      <h1>屋顶的颜色是 {this.props.acolor} 尺寸 {this.props.asize}</h1>
    )
  }
}

2.声明一个函数式组件HomeFotter,父组件传递的数据 需要在函数的形参props上接收

const HomeFooter = (props) => {
  return <h1>屋底的颜色是 {props.bcolor}  尺寸 {props.bsize}</h1>
}

3.声明父组件,并在标签上通过属性的方式进行传递数据

class Home extends Component {
  state = {
    color: "blue",
    size: 100
  }
  render() {
    return (
      <div>
        <HomeTop acolor={this.state.color} asize={this.state.size} ></HomeTop>
        <HomeFooter bcolor={this.state.color} bsize={this.state.size}  ></HomeFooter>
      </div>
    )
  }
}

4.需要注意的是父组件中的变量名是color,size而HomeTop和HomeFotter中修改了一下变量名,分别是acolor,asize和bcolor,bsize。

完整代码

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class HomeTop extends Component {
  render() {
    return (

      <h1>屋顶的颜色是 {this.props.acolor} 尺寸 {this.props.asize}</h1>

  )
  }
}

const HomeFooter = (props) => {
  return <h1>屋底的颜色是 {props.bcolor}  尺寸 {props.bsize}</h1>
}

class Home extends Component {
  state = {
    color: "blue",
    size: 100
  }
  render() {
    return (

      <div>
        <HomeTop acolor={this.state.color} asize={this.state.size} ></HomeTop>
        <HomeFooter bcolor={this.state.color} bsize={this.state.size}  ></HomeFooter>
      </div>

    )
  }
}

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

6.2.2.类组件构造函数中的props

当想要在类组件的构造函数中,获取到props时,需要如下使用:

  constructor(props) {
    super(props);
    console.log(props);
  }

6.2.3.props默认值

当父元素没有传递props属性时,子组件可以指定一个默认props属性值来使用。

通过 组件名.defaultProps 来指定

import React from 'react';
import ReactDOM from 'react-dom';

let HomeNav = (props) => {
  return <h1> 导航为 {props.color}  </h1>
}
// 指定一个默认属性
HomeNav.defaultProps = {
  color: "yellow"
}

class Home extends Component {
  state = {
    color: "blue"
  }
  render() {
    return (
      <div>
        <HomeNav></HomeNav>
      </div>
    )
  }
}

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

6.2.4.props类型检验

在一些要求代码更为严格的项目中,父组件传递数据的格式,必须和子组件要求的格式保持一致,否则就认为代码出错。

如子组件要求的是数字类型(100),而父组件传递的是字符串类型(“100”)。

这个需求可以通过react中的props类型检验来完成。

自React v15.5起,react.PropTypes已移入另一个包中。请使用prop-types库代替

 6.2.4.1.安装prop-types

yarn add prop-types --dev

引入

import PropTypes from 'prop-types'; 

6.2.5.使用

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// 1 引入 prop-types
import PropTypes from 'prop-types'; 


let HomeNav = (props) => {
  return <h1> 导航为 {props.color} 数量为 {props.nums}  </h1>
}
// 2  指定要求接收的数据格式
HomeNav.propTypes ={
  color:PropTypes.string,
  nums:PropTypes.number
}



class Home extends Component {
  state = {
    color: "blue",
    nums:100
  }
  render() {
    return (
      <div>
        <HomeNav {...this.state}></HomeNav>
      </div>
    )
  }
}

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

6.2.6.props插槽props.children

props.children可以实现类似Vue中的插槽功能(slot)

6.2.6.1.子组件

let HomeNav = (props) => {
  return (
    <div>
      <div>标题</div>
      <div>{props.children}</div>
    </div>
  )
}

6.2.6.2.父组件

class Home extends Component {
  render() {
    return (
      <div>
        <HomeNav {...this.state}>
          {/* 这里放动态插入的内容 */}
          <div>小标题你</div>
        </HomeNav>
      </div>
    )
  }
}

6.2.6.3.完整代码

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

let HomeNav = (props) => {
  return (
    <div>
      <div>标题</div>
      <div>{props.children}</div>
    </div>
  )
}

class Home extends Component {
  render() {
    return (
      <div>
        <HomeNav {...this.state}>
          {/* 这里放动态插入的内容 */}
          <div>小标题</div>
        </HomeNav>
      </div>
    )
  }
}

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

6.3.state和props对比

相同

  1. 二者都作为React内更新视图的依据,只有他们变化时,React才会进行相应的更新
  2. 二者都不可以通过直接赋值的方式更新

不同

  1. 更新方式不同:state通过setState方法更新(只能在组件内部更新),props则通过传入的值实现(组件内不可变)。
  2. state只维护组件内部的状态,props让外部维护组件的状态。

总结:尽量少用state,尽量多用props,这样既能提高组件的可复用性,又能降低维护成本。

7.事件

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

  1. React事件的命名采用小驼峰式(camelCase),而不是纯小写。
  2. 使用JSX语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

7.1.传统HTML绑定事件

<button onclick="activateLasers()">
  Activate Lasers
</button>

7.2.React中

<button onClick={this.handleClick}>
  Activate Lasers
</button>

7.3.事件中的this

观察以下的this值 它是undefined

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Home extends Component {
  state = {
    msg: "大家好呀"
  }
  handleClick() {
    // undefined
    console.log(this);
  }
  render() {
    return (
      <div>
        <div onClick={this.handleClick} >小标题</div>
      </div>
    )
  }
}

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

因为在react中,绑定事件不是原生HTML的dom元素,onClick只是react帮我们做的一个中间的映射,那么他在处理事件的时候,是不会处理this的指向的,我们需要手动处理这个this

7.3.1.处理方式

1.绑定事件的时候使用bind来锁定this 代码丑陋,不推荐

<div onClick={this.handleClick.bind(this)} >小标题</div>

2.在构造函数中使用bind修改this执行(推荐)

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Home extends Component {
  state = {
    msg: "大家好呀"
  }
  constructor() {
    super();
    // 重新绑定this
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log(this);
  }
  render() {
    return (
      <div>
        <div onClick={this.handleClick} >小标题</div>
      </div>
    )
  }
}

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

3.将事件的执行,改成箭头函数的方式(推荐)

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Home extends Component {
  state = {
    msg: "大家好呀"
  }
    // 修改为箭头函数的方式
  handleClick = () => {
    console.log(this);
  }
  render() {
    return (
      <div>
        <div onClick={this.handleClick} >小标题</div>
      </div>
    )
  }
}

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

7.4.事件传参

执行类似以前HTML的DOM事件传参一样的功能

<button onclick="show('大家好')"></button>

React中的写法为:

不推荐,容易导致意外情况(BUG)

<button onClick={() => { this.handleClick('red') }}>红色</button>

推荐写法:

 <button onClick={ this.handleClick.bind(this,'red') }>红色</button>
原文地址:https://www.cnblogs.com/sauronblog/p/12670830.html