React简介

是什么

用于构建用户界面的JavaScript

以前编写页面的流程

  • 发送请求获取数据
  • 处理数据(过滤、整理格式等)
  • 操作DOM呈现页面

React核心:将数据渲染成HTML视图

谁开发的

Facebook开发,且开源

  • 起初由Facebook的软件工程师Jordan Walke创建
  • 于2011年部署于Facebooknewsfeed
  • 随后在2012年部署于Instagram
  • 2013年5月宣布开源

为什么要学

  • 原生JS操作DOM繁琐、效率低(DOM-API操作UI
  • 使用原生JS直接操作DOM,浏览器会进行大量的重绘重排
  • 原生JS没有组件化编码方案,代码复用率低

React特点

  • 采用组件化模式、声明式编码,提高开发效率及组件复用率
  • React Native中可以使用React语法进行移动端开发
  • 使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互

前置知识

  • 判断this指向
  • class
  • ESNext
  • npm
  • 原型与原型链
  • 数组常用API
  • 模块化

React入门

官网

英文官网:https://reactjs.org/

中文官网:https://zh-hans.reactjs.org/

React基本使用

相关js

版本变更:

https://github.com/facebook/react/blob/main/CHANGELOG.md

目前最新版本为18.2.0 (June 14, 2022)

本教程版本为16.8,有点旧了,但学个基础语法是够用的

HelloReact案例

https://reactjs.org/docs/add-react-to-a-website.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<!-- 准备容器 -->
<div id="test"></div>
<!-- 核心库要先引入 -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<!-- type类型要写babel -->
<script type="text/babel">
// 1.创建虚拟DOM
const VDOM = <h1>Hello, React</h1>; // 不要写引号
// 2.渲染虚拟DOM到页面
// ReactDOM.render(VDOM, domContainer) // React18中ReactDOM.render方法不再支持
const domContainer = document.querySelector("#test");
const root = ReactDOM.createRoot(domContainer);
root.render(VDOM);
</script>
</body>

虚拟DOM的两种创建方式

方式一:JSX

1
const VDOM = <h1>Hello, React</h1>;

方式二:JS

1
const VDOM = React.createElement('h1', {id: 'title'}, 'Hello, React2')

需求:h1里包一个span,内容不变

JSX

1
const VDOM = <h1><span>Hello, React</span></h1>;

JS

1
const VDOM = React.createElement('h1', {id: 'title'}, React.createElement('span', {}, 'Hello, React2'))

四层标签嵌套,JS写法就很痛苦了

JSX作用:更流畅的创建虚拟DOM,就是原始JS创建虚拟DOM的语法糖

1
2
3
4
5
const VDOM = (
<h1>
<span>Hello, React</span>
</h1>
);

babel其实就是讲jsx语法转化成了createElement语法了

虚拟DOM与真实DOM

虚拟DOM

  • 就是一般对象

  • 虚拟DOM比较轻,真实DOM比较重。因为虚拟DOMReact内部在用,无需那么多的属性

  • 虚拟DOM最终会被React转化为真实DOM,呈现在页面上

    image-20230112154603832

真实DOM

image-20230112154745681

  • console.dir看下真实DOM身上的属性,有很多很多

    image-20230112155151094

JSX语法

  • 全称:JavaScript XML
  • React定义的一种类似于XMLJS扩展语法:JS XML
  • 本质是React.createElement(co)
  • 语法规则
    • 定义虚拟DOM时,无需引号
    • 标签中混入JS表达式时,要用{}
    • 样式的类名指定,用className,不能用class
      • classES6中的关键字
    • 内联样式,要用style={{key: value}}的形式
      • 外层的{}表示要写js表达式
      • 内层的{}表示一个对象
      • key如果是多个单词,采用小驼峰
      • value是字符串,要加引号
    • 只能有一个根标签
    • 标签必须闭合
    • 标签首字母
      • 小写开头:将标签转为HTML中的同名元素,若无同名元素,则报错
      • 大写开头:则认为是组件

React面向组件编程

前置准备

安装React开发插件,edge浏览器插件市场搜索并安装:

React Developer Tools - Microsoft Edge Addons

image-20230113102015048

安装成功后,打开https://www.meituan.com/,美团PC官网就是用React写的

审查元素,有两个额外的标签页:

image-20230113102723136

基本理解和使用

函数式组件

1
2
3
4
5
6
7
8
9
10
11
12
<script type="text/babel">
// 1.创建函数式组件,组件是结构、样式等资源的集合
function Demo() { // 首字母必须大写
return <h2>我是用函数定义的组件(适用于简单组件的定义)</h2>; // 返回最基础的一个结构
}

// 2.渲染组件到页面
const container = document.querySelector("#test");
const root = ReactDOM.createRoot(container);
root.render(<Demo/>); // 要写成闭合标签

</script>

Demo组件中,如果打印this,值是undefined,因为babel转化开启了严格模式

[Babel 中文网 · Babel - 下一代 JavaScript 语法的编译器 (babeljs.cn)](https://www.babeljs.cn/repl#?browsers=defaults%2C not ie 11%2C not ie_mob 11&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABAEQKYFs4AoCUiDeiA9EYoGmZg6tqCz1oKP6g3hmDkmoJmKAUIogE6pQgdIA8ACwBMAPkCIRoHozQBSugX8VADqaAs7UCScoBC3QCN-gN7lAEP-AgBOmA4uUAB3oFVlLapWBIf_5ERogNzFSgFfjAe2qAAc0Bf6oAEPVYAA5QCo5QGW_QBDzFgBfIA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-2&prettier=false&targets=&version=7.20.12&externalPlugins=&assumptions={})

image-20230113110014236

执行root.render(<Demo/>),发生了什么

  • React解析组件标签,找到组件
  • 发现组件是函数定义,调用该函数,将返回的虚拟DOM转为真实DOM,呈现在页面中

类语法

类式组件

1
2
3
4
5
6
7
8
9
10
11
12
<script type="text/babel">
// 1.创建类式组件,必须要继承React.Component
class Demo extends React.Component {
render() { // 必须要有render函数
return <h2>我是用类定义的组件(适用于复杂组件的定义)</h2>
}
}
// 2.渲染组件到页面
const container = document.querySelector("#test");
const root = ReactDOM.createRoot(container);
root.render(<Demo/>); // 这里的render和上面的render没有任何关系,只是重名
</script>

render函数是在Demo的原型对象上

image-20230113111908803

render函数要被调用,肯定要new一个实例的,React默认创建了组件实例

还是回到

执行root.render(<Demo/>),发生了什么

这个问题:

  • React解析组件标签,找到组件

  • 发现组件是类定义,new出来类的实例,并通过该实例调用原型的render方法

    • render中的this,指向该实例,通常称之为组件实例对象或组件对象

      image-20230113112412717

  • render返回的虚拟DOM转为真实DOM,呈现在页面中

组件实例三大核心属性

state

  • state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
  • 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)

注意点:

  • 组件中的render方法中的this为组件实例对象
  • 组件自定义的方法中,thisundefined如何解决?
    • 强制绑定this:通过函数对象的bind()
    • 箭头函数
  • 状态数据,不能直接修改或更新

定义state

在类组件的构造器里初始化state

1
2
3
4
5
6
7
8
9
10
11
12
class Demo extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: false
}
}
render() {
console.log(this)
return <h2>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}
}

image-20230113143950939

事件绑定

React重新封装了各种事件,onclick对应onClick,其它类似

1
2
3
4
5
6
7
return <h2 onClick={demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h2> // 不要加括号


// 最外层定义demo方法,但获取不到state
function demo() {
console.log('标题被点击了')
}

如果将方法定义在外层,获取不到state,可以将方法写在类里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Demo extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: false
}
}

// 函数写在class里面,通过this调用
demo() {
console.log('标题被点击了')
console.log(this) // undefined
}

render() {
console.log(this)
const { isHot } = this.state
return <h2 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
}
}

但是:

class类中定义的方法, 都在局部开启了严格模式

由于点击事件,不是通过实例调用,而是作为回调,直接调用,所以内部thisundefined

解决类中this指向问题:

1
2
3
4
5
6
7
constructor(props) {
super(props)
this.state = {
isHot: false
}
this.demo = this.demo.bind(this) // 将原型上的方法挂载到自身上
}

修改state

状态不可直接更改,类似于原生小程序的setDataReact中使用组件实例原型的原型上的setState方法,是一个合并的操作

image-20230113154252261

1
2
3
4
5
6
7
8
demo() {
console.log('标题被点击了')
const {isHot} = this.state
this.setState({
isHot: !isHot
})
console.log(this)
}

state的每次变化:

  • 构造器只调用一次

  • render函数初始化会被调用一次,state变化时也会重新调用(1+N次)

  • demo回调,点几次调用几次

state的简写方式

标准写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Demo extends React.Component {
constructor(props) {
super(props)
console.log('constructor')
this.state = {
isHot: false
}
this.demo = this.demo.bind(this)
}

demo() {
const {isHot} = this.state
this.setState({
isHot: !isHot
})
}

render() {
const { isHot } = this.state
return <h2 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
}
}
简写
  • 回调函数的简写

    • 箭头函数,没有this,如果写了this则向外层作用域寻找
  • 初始化状态的简写

    • 类中可以直接写赋值语句

      1
      2
      3
      class Demo {
      a = 1 // 往实例上追加a,值为1
      }
  • 简写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Demo extends React.Component {
    state = {
    isHot: false
    }

    demo = () => {
    const { isHot } = this.state
    this.setState({
    isHot: !isHot
    })
    }

    render() {
    const { isHot } = this.state
    return <h2 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
    }
    }

props

  • 每个组件对象,都会有propsproperties)的简写
  • 组件标签的所有属性都保存在props

props的基本使用

refs与事件处理

收集表单数据

组件生命周期

虚拟DOMDOM Diff算法

React应用(基于React脚手架)

React Ajax

React Router

React UI组件库

Redux