React学习

react的特点

在这里插入图片描述

react 和 vue 的区别

  1. 首先,这两个框架,都是以组件化的思想进行开发的!
  2. 从开发团队上进行对比:
    • React的开发团队,是Facebook官方大牛团队,团队技术实力雄厚;
    • Vue:第一个版本,主要是作者尤雨溪进行维护。
  3. 从社区方面进行对比:
    • React社区早,解决方案多
    • Vue社区晚一点,解决方案相对少一点
  4. 从移动App开发方面:
    • 使用React这门技术,可以分分钟转到ReactNative的开发中
    • VUE 这门技术,也提供了无缝的转移到移动端开发的体验,通过weex可以使用VUE的语法,进行移动端APP开发

脚手架

快速开始

npx create-react-app my-app
cd my-app
npm run start

JSX

JSX嵌套元素

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

如 下列语法是错误的 !

import React, { Component } from 'react'

export default class App extends Component {
  render() {
    return (
      <div>
        Hello World!
      </div>
      <div>
        Hello World!
      </div>
    )
  }
}

假如要想在根目录下直接渲染两个标签,可以使用 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'))

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 lamian = {
  name: "拉面"
};      

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

代码

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

const msg = "你们好呀";
const isBoy = false;
const format = (msg) => {
  return '---' + msg + '---';
}
const lamian = {
  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>{lamian.name}</h1>
    </Fragment>
  )
}

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

JSX嵌套语法与循环

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

const msg = "nice 天气";

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'))

JSX注释

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

JSX标签属性

jsx标签上可以设置绝大部分和以前html标签一样的属性,如 checked、图片的src需要注意几个点

  1. htmlclass属性改为className

     <div className="redCls">👍💯☁️</div>
    
  2. htmllabel标签的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>
    

JSX的行内样式

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

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

const App = () => {
  return (
    <div style={{ color: 'yellow', fontSize: "150px", "backgroundColor": 'red' }} > 颜色真不错</div>
  )
}

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

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": 1000
}

const App = () => {
  return React.createElement(
    "div",
    parentProps,
    [
      React.createElement(
        "span",
        null,
        "不错啊呀"
      ),
      React.createElement(
        "span",
        null,
        "真好呀"
      )
    ]
  )
}

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

1.组件的创建

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

1.简单功能 使用 函数式组件

2.复杂功能 使用 类组件

3.组件名都必须大写

函数式组件

import React from 'react'

export default function App() {
  return (
    <div>
      
    </div>
  )
}

类组件

使用es6创建class的方式来实现一个组件类

1.首字母要大写

2.要继承 React中的Component类

3.必须实现render函数,函数内返回标签

4.组件有自己的state和生命周期

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import "./index.css";


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

小结

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

2.状态和属性

React中,状态和属性都可以实现数据动态化

状态 state

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

可以理解为state就是Vue中的data

函数式组件没有自己的state

1.state的声明和使用

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

1-1.类属性的方式声明
class Person extends Component {
  // 1 声明 state
  state = {
    date: "2009",
    msg: "天啊天啊"
  }
  render() {
    return (
      <div>
        {/* 2 使用state */}
        <h1>{this.state.date}</h1>
        <h2>{this.state.msg}</h2>
      </div>
    )
  }
}
1-2.构造函数中声明
class Person extends Component {
  // 1 构造函数中 声明 state
  constructor() {
    // 1.1 必须在this之前调用super()方法
    super();
    this.state = {
      date: "2009",
      msg: "天啊天啊"
    }
  }
  render() {
    return (
      <div>
        {/* 2 使用state */}
        <h1>{this.state.date}</h1>
        <h2>{this.state.msg}</h2>
      </div>
    )
  }
}
2.state的赋值

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

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

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>
    )
  }
}
3.state的赋值是异步的

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

看看下面打印的值

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

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

    // 2 此时这个date还是2010而不是4010
    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 + 3000
    }, () => {
      // date的值为 5008
      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
    }
})

属性 props

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

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

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

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

2.声明一个函数式组件HomeFooter,父组件传递的数据 需要在函数的形参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>
    )
  }
}

完整代码

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'))
类组件构造函数中的props

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

constructor(props) {
    super(props);
    console.log(props);
}
props默认值

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

通过 组件名.defaultProps 来指定

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

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

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

ReactDOM.render(<Home />, document.getElementById('root'))
props类型校验

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

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

安装 prop-types
npm install prop-types --save
引入
import PropTypes from 'prop-types'; 
使用
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'))
props插槽 props.children

props.children可以实现类似vue中的插槽功能

子组件
let HomeNav = (props) => {
  return (
    <div>
      <div>标题</div>
      <div>{props.children}</div>
    </div>
  )
}
父组件
class Home extends Component {
  render() {
    return (
      <div>
        <HomeNav {...this.state}>
          {/* 这里放动态插入的内容 */}
          <div>在这个父组件中的子组件内插入一个div包含的内容,需要在子组件中{props.children}才能显示</div>
        </HomeNav>
      </div>
    )
  }
}
完整代码
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包含的内容,需要在子组件中{props.children}才能显示</div>
        </HomeNav>
      </div>
    )
  }
}

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

state 和 props 对比

相同

1.二者都作为 React 内更新视图的依据,只有它们变化时,React 才会进行相应的更新。

2.二者都不可以通过直接赋值的方式更新。

不同

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

2.state 只维护组件内部的状态,props 让外部维护组件的状态。

总结

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

事件

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

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

传统HTML绑定事件

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

React中

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

事件中的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

处理方式

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'))

事件传参

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

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

React中的写法

1.不推荐,容易导致意外情况

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

2.推荐写法

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

受控和非受控表单

受控是指受React控制。可以理解为绑定了value或者checked属性的表单就是受控表单。也可以理解为受控表单就是实现了双向绑定。

受控表单

input标签和checkbox标签进行改造

input 受控

必须同时给与 onChange事件和绑定value属性

<input type="text" value={this.state.inpValue} onChange={this.handleChangeValue.bind(this)} />

逻辑中 执行给 inpValue赋值

handleChangeValue(e) {
    this.setState({
      inpValue: e.currentTarget.value
    })
  }

checkbox 受控

必须同时给与 onChange事件和绑定value属性

<input type="checkbox" checked={this.state.isChecked}  onChange={this.handleChkChecked.bind(this)}  />

逻辑中 执行给 inpValue赋值

handleChkChecked(e){
    this.setState({
        isChecked:e.currentTarget.checked
    })
}

更多受控标签的说明

元素绑定事件h获取值
<input type="text" />value="string"onChangeevent.target.value
<input type="checkbox" />checked={boolean}onChangeevent.target.checked
<input type="radio" />checked={boolean}onChangeevent.target.checked
<textarea />value="string"onChangeevent.target.value
<select />value="option value"onChangeevent.target.value

ref转发

在函数式组件中的第二个参数接收父组件传递过来的ref

export default React.forwardRef(
  function Two(props, ref) {
    const p = React.createRef();
    return (
      <div>
        <h1>Two组件</h1>

        <p>test</p>

        <Box ref={ref}/>

      </div>
    )
  }
)

非受控表单 ref

很多时候,我们不得不去操作dom元素。如 :

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

React 16.3 版本后使用 React.createRef() API 来创建ref

使用步骤

1.在构造函数中获取实例

// myRef 为自定义的名字
this.myRef = React.createRef();

2.在render函数中将myRef绑定到对应的html标签或者React组件上

<input type="text" ref={this.myRef} /> 

3.后期可以通过 以下代码来获取 该实例的引用

this.myRef.current
默认值

有时候我们希望给非受控表单一个初始化值,同时又不希望将它变成受控表单。此时可以使用 defaultValuedefaultChecked

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


class App extends Component {
  state = {
    msg: "2008",
    isChecked: true
  }
  render() {
    return (
      <div >
        {/* 添加了 defaultValue */}
        <input type="text" defaultValue={this.state.msg} />
        {/* 添加了 defaultChecked  */}
        <input type="checkbox" defaultChecked={this.state.isChecked} />
      </div>
    )
  }
}

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

设置输入框获得焦点

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



class App extends Component {
  constructor() {
    super();
    this.inp = React.createRef();
  }
  handleClick() {
    this.inp.current.focus();

  }
  render() {
    return (
      <div >
        <input type="text" ref={this.inp} /> <button onClick={this.handleClick.bind(this)}>设置获取焦点</button>
      </div>
    )
  }
}

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

代码分割

App同级文件夹util中alert.js文件

uitl/alert.js

console.log('文件的代码执行了.....');

export default function(msg){
  
  alert(msg);
}

App.js

import React, { Component } from 'react'

// import alertFunc from './util/alert'

export default class App extends Component {
  
  render() {
    return (
      <div>
        <button onClick={this.btnAction1}>按钮1</button>
        <button onClick={this.btnAction2}>按钮2</button>
      </div>
    )
  }

  btnAction1 = async () => {
    // import('./util/alert')
    // .then( ( {default : alertFunc}  )=>{
    //   alertFunc('hello');
    // })
    const {default : alertFunc} = await import(/* webpackChunkName: 'alert' */'./util/alert');
    alertFunc('hello');
  }

  btnAction2 = async () => {

    const {default : conFunc} = await import(/* webpackChunkName: 'console' */'./util/console');
    conFunc('test');

  }
}

Fragment的使用

import React, { Component, Fragment } from "react";
import One from "./One";
export default class App extends Component {
  render() {
    return (
      <Fragment>
        <One />
      </Fragment>
    );
  }
}

children

Wrap.js

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

export default class Wrap extends Component {
  constructor(props) {
    super(props);
    console.log(props);
  }
  
  render() {
    return (
      <div className="wrap">
        <h1>wrap组件</h1>

        {this.props.children}

      </div>
    )
  }

  // children 可以为任意类型
  static propTypes = {
    children: PropTypes.node
  }
}

App.js

使用children可以让父组件中子组件内的内容显示,类似vue中的插槽

import React, { Component } from 'react';
import Wrap from './components/Wrap'

class App extends Component {
  render() {
    return (
      <div className="app">

        <Wrap hArr={(
          <React.Fragment>
            <h1>hello wrold</h1>
            <h2>hello wrold</h2>
            <h3>hello wrold</h3>
            <h4>hello wrold</h4>
          </React.Fragment>
        )}>

        </Wrap>

      </div>
    );
  }
}

export default App;

Context的使用

构建context的类型 const Context = React.createContext({})

$_开头的属性都是内部私有的属性在js中

Consumer:数据的消费者

Provider:数据的提供者

contextType保留在继承的Component中 所以函数式组件不能 组件.contextType 只能通过Consumer来使用数据

使用context

1.创建context 例:const ColorContext = React.createContext({/默认属性/color:‘blue’})

2.向整颗组件树提供共享的context数据 import ColorContext from ‘./data/…’ <ColorContext.Provider></ColorContext.Provider>

3.获取数据 通过Consumer

1.在data/BorderContext.js、ColorContext.js文件中构建context类型

React.createContext({})

import React from 'react'

// 1.构建context的类型
const BorderContext = React.createContext({
  border: '1px solid #999'
});

export default BorderContext;
import React from 'react'

// 1.构建context的类型
const ColorContext = React.createContext({
  color: 'blue'
});

export default ColorContext;

2.向整颗组件树提供共享的context数据

App.js

数据通过value值携带

import React, { Component } from "react";
import One from './components/One'
import Two from './components/Two'
import './style.css'
import ColorContext from './data/ColorContext'
import BorderContext from './data/BorderContext'

export default class App extends Component {
  
  render() {
    // 2.向整颗组件树提供共享的context数据
    return (
      <BorderContext.Provider value={{border: '5px dashed #68a'}}>
        <div>
          <ColorContext.Provider value={{color: 'red'}}>
            <One/>
            <Two/>
          </ColorContext.Provider>
          <Three/>
        </div>
      </BorderContext.Provider>
      
    )
  } 
}

3.获取数据

One.js\Two.js

获取数据的两种方式

1.通过context实例中的Consumer属性

注意:Consumer中包裹一个函数并返回内容 函数形参就是value传递过来的数据,可以嵌套使用获取多个value值,第二种方式就不行了

import React, { Component } from "react";
import ColorContext from "../data/ColorContext";
import BorderContext from "../data/BorderContext";

class One extends Component {
  render() {
    return (
      <div className="box">
        <h1>One</h1
      {/* 3.获得数据 */}
        <BorderContext.Consumer>
          {({ border }) => (
            <ColorContext.Consumer>
              {({ color }) => (
                <mark style={{ background: color, border: border }}>test</mark>
              )}
            </ColorContext.Consumer>
          )}
        </BorderContext.Consumer>
      </div>
    );
  }

  getDom = (value) => {
    return <mark style={{ background: value.color }}>test</mark>;
  };
}

export default One;

2.通过设置组件的contxetType属性

Two.contextType = 导入的那个context实例

在构造函数的第二个参数中获取通过父组件Provider中value属性共享过来的数据

import React, { Component } from 'react'
import ColorContext from '../data/ColorContext'

export default class Two extends Component {
  constructor(props, context) {
    super(props);
    console.log(context);
  }
  
  render() {
    return (
      <div className="box">
        <h1>Two</h1>
      </div>
    )
  }
}
// ColorContext是导入进来的context实例
Two.contextType = ColorContext;

组件传值

React中,数据的传递方式有以下几种

  1. 父传子 通过属性
  2. 子传父 通过事件
  3. 兄弟间传值 先传给父再传给兄弟
  4. 复杂关系 redux

父传子

在类组件中接收

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

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

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

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

需要注意的是,改成 函数式组件, props需要在子组件上通过形参的方式接收和使用,不再是通过this.props

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

// 函数式组件 通过props形参来接收
function HomeTop(props) {
    return (
        <h1>屋顶的颜色是 {props.acolor} 尺寸 {props.asize}</h1>
    )
}

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

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

子传父

通过触发事件的方式

1.在父组件中 给子组件挂载一个自定义事件 onChangeColor

<GreenBtn onChangeColor={this.changeColor}></GreenBtn>

2.子组件自己触发事件

const GreenBtn = (props) => {
  setInterval(() => {
    // 触发 父组件上的自定义事件
    props.onChangeColor("green");
  }, 1000);

  return (
    <button >绿色</button>
  )
}

3.父组件上监听到了事件触发

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

const GreenBtn = (props) => {

  setInterval(() => {
    // 触发 父组件上的自定义事件
    props.onChangeColor("green");
  }, 1000);

  return (
    <button >绿色</button>
  )
}


class App extends Component {
  state = {
    color: "red"
  }
// 监听事件触发
  changeColor = (color) => {
    this.setState({
      color
    })
  }
  render() {
    return (
      <div style={{ backgroundColor: this.state.color }}>
        <GreenBtn onChangeColor={this.changeColor}></GreenBtn>
      </div>
    )
  }
}

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

兄弟传值

思路是 组件A ->父组件->组件B

  1. 组件A通过触发事件,将数据传递个父组件
  2. 父组件通过属性props的方式将数据传递给子组件B
组件A
const GreenBtn = (props) => {
  const handleClick = () => {
    props.onChangeColor("green");
  }
  return (
    <button style={{ color: props.color }} onClick={handleClick}>绿色</button>
  )
}
父组件
class App extends Component {
  state = {
    color: "red"
  }
  changeColor = (color) => {
    this.setState({
      color
    })
  }
  render() {
    return (
      <div style={{ backgroundColor: this.state.color }}>
        <GreenBtn onChangeColor={this.changeColor} color={this.state.color}  ></GreenBtn>
        <RedBtn onChangeColor={this.changeColor} color={this.state.color} ></RedBtn>
      </div>
    )
  }
}
组件B
const RedBtn = (props) => {
  const handleClick = () => {
    props.onChangeColor("red");
  }
  return (
    <button style={{ color: props.color }} onClick={handleClick}>红色</button>
  )
}
完整代码
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

// 1 组件A
const GreenBtn = (props) => {
  const handleClick = () => {
    props.onChangeColor("green");
  }
  return (
    <button style={{ color: props.color }} onClick={handleClick}>绿色</button>
  )
}
// 2 组件B
const RedBtn = (props) => {
  const handleClick = () => {
    props.onChangeColor("red");
  }
  return (
    <button style={{ color: props.color }} onClick={handleClick}>红色</button>
  )
}

// 3 组件C
class App extends Component {
  state = {
    color: "red"
  }
  changeColor = (color) => {
    this.setState({
      color
    })
  }
  render() {
    return (
      <div style={{ backgroundColor: this.state.color }}>
        <GreenBtn onChangeColor={this.changeColor} color={this.state.color}  ></GreenBtn>
        <RedBtn onChangeColor={this.changeColor} color={this.state.color} ></RedBtn>
      </div>
    )
  }
}

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

生命周期

React组件的生命周期可以分为3种状态

  • 挂载时
  • 更新时
  • 卸载时

挂载

当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下

  • constructor()
  • static getDerivedStateFromProps() 了解即可
  • render()
  • componentDidMount()
constructor

组件的构造函数,在组件被创建的时候会执行,一般有以下作用:

1.通过给 this.state 赋值对象来初始化内部 state。(不要使用this.setState()来初始化数据)

this.state={key:val};

2.为事件处理函数绑定实例

this.handleClick=this.handleClick.bind(this);
render

render函数负责将模板渲染到页面,一般来说,当propsstate发生改变时,都会重新触发render,需要注意的是,不要在render函数中 修改propsstate,否则会导致死循环!!

componentDidMount

componentDidMount 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。一般用来在这里发送异步请求。

更新

当组件的 props 或 state 发生变化时会触发更新,会按顺序调用以下的生命周期事件

  • static getDerivedStateFromProps() 了解即可
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate() 了解即可
  • componentDidUpdate()
shouldComponentUpdate

propsstate发生改变时触发,返回true 则表示允许执行 render ,返回 false这表示 不允许运行render

接收两个参数,nextStatenextProps,表示修改后的 stateprops值。一般用在提高性能使用。如:

state中的某个值为偶数才触发render,为奇数则不触发。

renders

同上述说明

componentDidUpdate

componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法

当组件更新后,可以在此处对 DOM 进行操作。 如 判断是否触底了,再进行其他操作。

会接收3个参数

  • prevProps 修改前的props对象
  • prevState 修改前的state对象
  • snapshot 了解

卸载时

componentWillUnmount

在组件卸载及销毁之前直接调用,在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求等。也不要在该事件中 执行 setState操作,无效。

不用常用的生命周期事件

样式

react中可以使用以下方式为元素添加样式

1.行内 无法实现伪类

<div style={{backgroundColor:"red"}} >行内样式</div>

2.全局样式 导致全局污染

import './css/index.css'

3.styled-jsx

4.css module(推荐)

CSS module

1.安装node-sass 依赖包
npm install --save-dev node-sass
2.添加 App.module.scss 文件,文件名中 module必须加上,不然识别不了这个模块文件,加入以下内容
.app {
  .demo {
    color:red
  }
}
3.通过 import 方式引入 scss 文件
import AppCSS from "./App.module.scss"
4.添加样式
<div className={AppCSS.app}>
    <h1 className={AppCSS.demo}>123</h1>
</div>
使用css module,每个单独生成的样式都有单独的命名空间,样式局部生效

styled-jsx

实现类似vue中,局部样式

安装依赖

customize-cra 负责合并webpack配置文件

styled-jsx 负责 实现局部样式

react-app-rewired 负责重新改写 项目的运行命令

npm install customize-cra styled-jsx react-app-rewired --save
新建配置文件 config-overrides.js

填入以下内容

// config-overrides.js
const { override, addBabelPlugins } = require('customize-cra');
module.exports = override(
  ...addBabelPlugins(
    [
      "styled-jsx/babel"
    ]
  )
)

修改package.json配置文件的script字段

"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  }
在组件中 直接写样式
<div className="App">
    <Header></Header>
    <h1 className="hh11">我变颜色啦</h1>
    <style jsx>{
            `
    .hh11{
    background-color:red;
    }
    `
        }
    </style>
</div>
完整代码
import React from 'react';
import './App.css';

const Header = () => {
  return (
    <div>
      <h1 className="hh11">我没有变颜色哦</h1>
    </div>
  )
}

function App() {
  return (
    <div className="App">
      <Header></Header>
      <h1 className="hh11">我变颜色啦</h1>
      <style jsx>{
        `
       .hh11{
         background-color:red;
       }
       `
      }
      </style>
    </div>
  );
}

export default App;
添加less支持

1.添加依赖

npm install styled-jsx-plugin-less@alpha  less --save

2.修改配置文件 config-overrides.js

// config-overrides.js
const { override, addBabelPlugins } = require('customize-cra');
module.exports = override(
  ...addBabelPlugins(
    [
      "styled-jsx/babel",
      { "plugins": ["styled-jsx-plugin-less"] }
    ]
  )
)

3.编写less语法

<style jsx>
    {
        `
        .hh11{
            background-color:red;
            span{
            color:blue;
            }
        }
        `
    }
</style>

React 路由

React Router

英文官网

中文手册

安装 路由

npm  install react-router-dom --save

导入依赖

import { BrowserRouter as Router, Route, Link } from "react-router-dom";

完整代码

import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

function Index() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Index} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
  );
}

export default AppRouter;

说明

从上述案例也可以看出,在react-route-dom中,存在以下几个常用的对象

名称作用
BrowserRouter使用传统的url模式
HashRouter使用哈希路由的模式
Route用来装路由对应的内容
Link用来指定路由路径的
Swich只可以匹配到一个路由
Redirect路由重定向
exact精确匹配路由
match路由对象的一个存放路由参数的的属性
location路由对象的一个存放URL信息的属性
history路由对象的一个控制路由跳转的属性
component路由渲染的一种方式
render路由渲染的一种方式
children路由渲染的一种方式

案例 1

1.导入路由必备对象

// 1 导入 哈希路由 路由-内容标签 路由-链接标签
import { HashRouter as Router, Route, Link } from "react-router-dom";

function Index() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

3.使用路由

function AppRouter() {
  return (
     {/* 1 使用Router将Link 和 Route包含起来 */}
    <Router>
      <div className="route">
        <nav>
          <ul>
            {/* 2 导航链接 */}
            <li> <Link to="/">Home</Link> </li>
            <li> <Link to="/about/">About</Link> </li>
            <li> <Link to="/users/">Users</Link> </li>
          </ul>
        </nav>
        <div className="content">
          {/* 3 链接对应的内容 */}
          <Route path="/" exact component={Index} />
          <Route path="/about/" component={About} />
          <Route path="/users/" component={Users} />
        </div>
      </div>
    </Router>
  );
}

常见的路由标签和路由属性

React的路由默认匹配规则 类似:

if(link.indexOf("/")){
    ...
}
if(link.indexOf("/about/")){
    ...
}
    

当 在route上加上 exact属性后,变成

if( link === "/" ){
    ...
}
if( link === "/about/" ){
    ...
}

在 1 的基础上 不加 exactSwitch 后 变成了

<Switch>
    {/* 链接对应的内容 */}
    <Route path="/" component={Index} />
    <Route path="/about/" component={About} />
    <Route path="/users/" component={Users} />
</Switch>

它的原理类似:if-else if ...

if(link.indexOf("/")){
    ...
}
else if(link.indexOf("/about/")){
    ...
}

有时候,为了防止用户输入一个不存在的路由,我们会设置一个 404组件

function PageNotFound() {
  return <h2>404啦</h2>
}

  <Switch>
    <Route path="/" exact component={Index} />
    <Route path="/about/" component={About} />
    <Route path="/users/" component={Users} />
    {/* 设置一个404页面 */}
    <Route path="/404/" component={PageNotFound} />
  </Switch>

因为,用户不可能自己输入一个 url:404,用户更多的是输入一些错乱的路径 dfasdfsdf

因此,我们需要一个重定向 Redirect

import {Redirect } from "react-router-dom";

<Switch>
  <Route path="/" exact component={Index} />
  <Route path="/about/" component={About} />
  <Route path="/users/" component={Users} />
  <Route path="/404/" component={PageNotFound} />
  {/* 可以匹配到除以上的路由规则外的所有路由 */}
  <Redirect to="/404/" ></Redirect>
</Switch>

渲染方式

在React的中,有3种渲染方式

  • component 每次都会触发组件的对应的生命周期
  • render 内联模式渲染 性能会更高 props需要传递到函数内
  • children 会一直渲染 不管匹配模式
<Route path="/about/" component={About} />
<Route path="/about/" render={(props) => <About {...props} />}/>
<Route path="/about/" children={() => <About/>}/>

匹配参数

有时候我们需要在链接中和内容中传递参数

通过 route标签的 path属性来指定参数

<Route path="/users/:id/" exact component={UserDetail} />

通过 props属性中的 history对象来进行 逻辑跳转

const {id}=e.currentTarget.dataset;
props.history.push(`/users/${id}/`);

通过 props对象的match对象来获取参数

class UserDetail extends Component {
    render() {
        const { id } = this.props.match.params;
        return <h1>{id}</h1>
    }
}
<Route path="/one/:id" component={Main}/>

<Route path="/two" render={(props)=>{
        const obj = {
            a: 1,
            b: 2,
            c: 3,
            d: 4,
            val: '123'
        }

        return <All {...obj} {...props}/>

}}/>

Redux

redux简单例子实现

效果图

在这里插入图片描述

目录结构

在这里插入图片描述

主入口index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// 1. 引入一个 连接仓库和组件的连接组件
import { Provider } from "react-redux"
// 2. 引入仓库
import store from "./store"
//3. 配置到App节点上
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

App.js

import React, { Component } from 'react'
// 1. 引入连接器 将属性和行为  注入到组件中
import {connect} from "react-redux"
// 引入 actions
import { addAction,subAction} from "./store/actionCreator"
class App extends Component {
  render() {
    return (
      <div>
         <button onClick={this.props.addNum}>+</button>
         {this.props.num}
         <button onClick={this.props.subNum}>-</button>
      </div>
    )
  }
}
// 将仓库中的 数据 映射到 组件中
const mapStateToProps = (state)=>{
  return {
    num: state.numReducer.num
  }
}
// 将行为 注入到 组件中
const mapDispatchToProps = (dispatch)=>{
  return {
    // + 
    addNum:function(){
      dispatch(addAction);
    },
    subNum:function(){
      dispatch(subAction);
    }
    // - 
  }
}
export default connect(mapStateToProps,mapDispatchToProps)(App)

store/index.js

// 1. 引入 创建 仓库的 api
import { createStore } from "redux"
// 2. 引入 reducer 初始化的数据以及纯函数的操作
import rootReducer from "./reducer"
// 创建仓库 并且导出仓库
export default createStore(rootReducer);

store/reducer/index.js合并reducer

// 1. 引入合并reducer 的函数
import { combineReducers } from "redux"
//2. 引入合并的reducer
import numReducer from "./numReducer"
//3. 导出合并之后 reducer
export default combineReducers({numReducer:numReducer})

store/reducer/numReducer.js

import { ADD,SUB} from "../actionTypes"
const defaultState = {
    num:888
}
export default function(state=defaultState,action){
    // 纯函数操作
    let newState = Object.assign({},state);
    switch(action.type){
        case ADD:
            newState.num = state.num + action.payload.num;
            break;
        case SUB:
            newState.num = state.num + action.payload.num;
            break;
        default:
            break;
    }
    return newState;
}

store/actionCreator/index.js

提取

// { type:"ADD",payload:{ num:1 }}
import {ADD,SUB} from "../actionTypes"
const addAction = { type: ADD, payload: { num: 1 } };
const subAction = { type: SUB, payload: { num: -1 } };
export { addAction, subAction };

store/actionTypes/index.js

同上 可以提取也可以不提取

const ADD = "ADD"
const SUB = "SUB"
export { ADD,SUB}

Hook

react Hook 是react 新推出的功能

useState

正常版本 class 创建组件 定义state 用setState 来改变组件样式

class Example extends React.Component {
 
 constructor(props) {
 
  super(props);
 
  this.state = {
 
   count: 0
 
  };
 
 }
 
 render() {
 
  return (
 
   <div>
 
    <p>You clicked {this.state.count} times</p>
 
    <button onClick={() => this.setState({ count: this.state.count + 1 })}>
 
     Click me
 
    </button>
 
   </div>
 
  );
 
 }
 
}

无状态函数组件 则可以使用useState来添加状态

//1. 引入一个 hook useState
import React, { useState } from "react";

export default function App() {
  // num 是 相当于 类组件中 state 中的变量 setNum 改变变量的函数
  // useState 是 状态的钩子 括号里面的值 是 num 的初始值
  let [num, setNum] = useState(999);
  return (
    <div>
      <button
        onClick={(params) => {
          // setNum 相当于
          // this.setState({ num : num + 1})
          setNum(num + 1);
        }}
      >
        +
      </button>
      {num}
      <button
        onClick={(params) => {
          // this.setState({num:num-1})
          setNum(num - 1);
        }}
      >
        -
      </button>
    </div>
  );
}

多个state的情况

//1. useState 允许在函数式组件中使用  class 中的状态
// 引入 钩子函数 useState
import React, { useState } from "react";

export default function App() {
  // useState hook 中 有多个 state 的情况
  // 数字
  let [num, setNum] = useState(888); // 888 是 num 初始化值 setNum 改变 num的函数
  // 字符串
  let [title, setTitle] = useState("标题");
  // 对象
  let [todos, setTodos] = useState([
    { id: 1, text: "事项一", completed: true },
    { id: 2, text: "事项二", completed: true },
    { id: 3, text: "事项三", completed: true },
  ]);
  return (
    <div>
      {/* setState({ num: num+1}) */}
      <button
        onClick={() => {
          setNum(num + 1);
        }}
      >
        +
      </button>
      <h1>{title}</h1>
      {num}
      <button
        onClick={() => {
          setNum(num - 1);
        }}
      >
        -
      </button>
      {/* Todo list */}
      <ul>
        {
          todos.map(v=><li key={v.id}>{v.text}</li>)
        }
      </ul>
    </div>
  );
}

hook版本的todos

// 引入 useState hook
import React, { useState } from "react";

export default function App() {
  // let todos = [{
  //   id:1,
  //   text:"😀",
  //   completed:false
  // },{
  //   id:2,
  //   text:"☀",
  //   completed:false
  // }]
  // todos hook
  let [todos, setTodos] = useState([
    {
      id: 1,
      text: "😀",
      completed: false,
    },
    {
      id: 2,
      text: "☀",
      completed: false,
    },
  ]);
  // inputVal
  let [inputVal, setInputVal] = useState("");
  return (
    <div>
      <div>
        <label htmlFor="inp">输入</label>
        <input
          id="inp"
          value={inputVal}
          onChange={(e) => {
            console.log(e.target.value);
            //  this.setState({ inputVal: e.target.value})
            setInputVal(e.target.value);
          }}
        ></input>
        <button
          onClick={() => {
            if(!inputVal){
              alert("输入不能为空!!!");
              return false;
            }
            // 深拷贝
            let newTodos = JSON.parse(JSON.stringify(todos));
            newTodos.push({
              id: Date.now(),
              text: inputVal,
              completed: false,
            });
            // 3个
            setTodos(newTodos);
            console.log(newTodos);
          }}
        >
          添加 todos
        </button>
      </div>
      {/* todos 的列表的开始 */}
      <div>
        {todos.map((v, index) => (
          <div key={v.id}>
            {v.text}
            <button
              onClick={(params) => {
                let newTodos = JSON.parse(JSON.stringify(todos));
                // 删除todos
                newTodos.splice(index, 1);
                setTodos(newTodos);
              }}
            >
              删除
            </button>
          </div>
        ))}
      </div>
      {/* todos 的列表的结束 */}
    </div>
  );
}

useEffect

类组件中生命周期的实现
import React, { Component } from 'react'

export default class App extends Component {

  state = {
    num : 888
  }
  componentDidMount(){
    // 在组件 挂载到 dom 树上的时候 修改页面的标题
    document.title = "effect hook 例子";
  }
  // 组件更新的时候
  componentDidUpdate(){
    document.title = this.state.num;
  }
  handleClick= (params) => {
    this.setState({
      num: this.state.num + 1
    })
  }
  
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>+</button>
        {this.state.num}
      </div>
    )
  }
}

类组件移除事件的方式
import React, { Component } from "react";

class Demo extends Component {
  eventClick = (params) => {
    console.log("document 被点击了");
  }
  
  // 子组件中注册了一个事件
  componentDidMount() {
    document.addEventListener("click", this.eventClick);
  }
  // 组件卸载的时候 移除监听器
  componentWillUnmount(){
    document.removeEventListener("click",this.eventClick);
  }
  render() {
    return <div>子组件</div>;
  }
}

export default class App extends Component {
  state = {
    show: true,
  };
  handleClick = (params) => {
    this.setState({
      show: !this.state.show,
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>销毁组件</button>
        {this.state.show ? <Demo></Demo> : ""}
      </div>
    );
  }
}

在函数式组件中使用useEffect
// useEffect 是生命周期的 Hook
// 它能够为函数式组件提供 生命周期的功能
import React, { useState, useEffect } from "react";
export default function App() {
  let [num, setNum] = useState(888);
  let [title, setTitle] = useState("");
  // useEffect 其实就是 componentDidMount 和
  // componentDidUpdate 合体
  // 里面的回调函数 会在 componentDidMount 和componentDidUpdate 生命周期执行的时候运行
  // 关注点的分离
  // 调试时候会更方便
  // 设置标题
  useEffect(() => {
    if (num === 888) document.title = "😀b( ̄▽ ̄)d";
    else document.title = num;
  });
  // 添加监听时间
  useEffect(() => {
    document.addEventListener("click", function () {
      console.log("document 被点击了");
    });
  });
  return (
    <div>
      <button
        onClick={() => {
          setNum(num + 1);
          // document.title = num + 1;
        }}
      >
        +
      </button>
      <h1>{num}</h1>
    </div>
  );
}

vs code插件

vscode-styled-jsx 组件中的颜色代码高亮

ES7 React/Redux/GraphQL/… 快速创建组件等

Prettier shift+alt+f格式化代码

chrome插件

1.React Developer Tools

2.Redux DevTools

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值