React 起步

耦合度爆炸(划掉)函数式编程思想的具现化——React 框架


安装

  1. CDN 引入
  2. 本地安装
  3. 脚手架构建

共三种方法

CDN 引入

在 html 模板中引入至少三个 script 标签,示例如下

1
2
3
4
5
6
7
<!-- 加载 React。-->
<!-- 注意: 部署时,将 "development.js" 替换为 "production.min.js"。-->
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>

<!-- 加载我们的 React 组件。-->
<script src="like_button.js"></script>

本地安装

运行以下命令

1
npm install -g react react-dom

即可安装必要的两个包

在使用时通过

1
2
import React from 'react'
import ReactDOM from 'react-dom'

即可引入

脚手架构建

推荐使用该方法,但记得换源,不然就等着小时为单位的 installing 吧

换源命令如下

1
npm config set registry https://registry.npm.taobao.org

可以通过以下命令检查是否换源成功

1
npm config get registry

返回值是现在的源的路径

然后运行以下命令

1
npm install -g create-react-app

然后移动到 react 项目目录下,运行

1
create-react-app demo

即可创建一个 demo 文件夹,内含全套 react 项目

或是

1
create-react-app .

即可在当前目录下初始化一个 react 项目

开始

安装完成后,通过命令

1
npm run start

即可启动 react app,不出意外的话会自动打开 localhost:3000 并显示 react 默认页面

自带的热更新效果有限,想查看最新效果,最好还是手动刷新一下

打开 src/index.js 就可以看到主入口代码

暂时不需要考虑太多,只需要知道

  1. ReactDOM.render 函数负责渲染页面

  2. React.StrictMode 标签是一个占位符,不会渲染到页面上,实际渲染的部分只有该占位符内部的部分

  3. React.createElement 函数可以创建一个 react 元素

  4. 原则上模板必须是一个整体,不能是两个或多个并列的元素,但是可以使用 React.Fragment 标签做到整体的效果

    但是写 React.Fragment 也太多字母了,所以 react 提供了缩写 <>,与上述等效

    其对应的闭合标签是 </>

ReactDOM.render 的格式如下

1
ReactDOM.render(template, target)

第一个参数填写要渲染的模板

第二个参数填写模板要挂载到页面上的哪个位置

React.createElement 的格式如下

1
React.createElement(type, props, text)

第一个参数表示创建的类型,如 'div'

第二个参数表示该标签持有的属性,如 {className: 'container'}

第三个参数表示该标签内部的文本

模板

与 vue 的构造选项不同,react 采用独特的 JSX 语法来书写模板

index.js 中,我们可以看到,ReactDOM.render 函数实际渲染的部分是 <App /> 标签,该标签通过

1
import App from './App';

来引入 App 模块

那么我们打开 src/App.js,可以看到该 js 通过

1
export default App;

向外暴露了一个名为 App 的 function,该 function 返回一段形似 HTML 的语段,这个语段所采用的语法就是 JSX

JSX 语法要注意的地方有 3 个

  1. 总体上沿用 HTML 语法

  2. 需要使用 js 来表示的地方(如变量或函数),使用花括号 {} 来表示

    与 vue 不同,vue 是使用双花括号

    1
    {{}}

    来表示

  3. 可以添加任意自定义参数,但要注意有的参数有细微变化

    1. 原参数名 class,现在变为 className
    2. 原参数名 onclick,现在变为 onClick,其余事件同理
    3. 一般有断句的部分,都要同 2 一样变成驼峰,用到的话请查一下文档

需要注意的是,js 中的 return 后如果留空,则实际等效于 return undefined,所以要用一对圆括号来占这一行的位置,然后折行写 JSX

魔改

实现 +1

现在魔改一下 App 中的 JSX,来做一个简单的 +1 功能

显然,+1 需要一个变量来承载值,一个 button 来触发事件

容易得到以下 JSX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import './App.css';

let n = 0;

function App() {
return (
<div>
{n}
<button onClick={n += 1}>+1</button>
</div>
);
}

export default App;

但是这时候发现,无论如何点击 button,n 都不会变化!

这是因为 react 和 vue 不同,不会自动监听数据变化并刷新视图

如果想要刷新视图,需要手动调用 render 函数

那么容易得到以下变体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { render } from 'react-dom';
import './App.css';

let n = 0;

function add() {
n += 1;
render((
<div>
{n}
<button onClick={add}>+1</button>
</div>
), document.getElementById('root'))
}

function App() {
return (
<div>
{n}
<button onClick={add}>+1</button>
</div>
);
}

export default App;

现在点击页面上的 button,可以正常 +1 并重新渲染了,但是这也太丑了吧?!

代码优化

那么我们可以使用一个变量来保存我们的模板

容易得到以下变体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { render } from 'react-dom';
import './App.css';

let n = 0;
let template = (
<div>
{n}
<button onClick={add}>+1</button>
</div>
)

function add() {
n += 1;
render(template, document.getElementById('root'))
}

function App() {
return template;
}

export default App;

但是现在发现 +1 坏掉了!

为什么?因为 template 在声明的时候,就已经是一个静态的常量了,计算好了 n 的值,之后不会重新取得 n 的值

那延后 n 的取值行不行?容易想到用函数来装载模板,但是 JSX 支持吗?

答案是支持!render 函数的第一个参数可以是

  1. 一段 JSX 语段

  2. 一个返回 JSX 语段的函数,此时会自动执行函数取返回值

    但是此时 render 第一个参数必须写成标签形式

那么箭头函数就可以满足我们的目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { render } from 'react-dom';
import './App.css';

let n = 0;
let template = () => (
<div>
{n}
<button onClick={add}>+1</button>
</div>
)

function add() {
n += 1;
render(<template />, document.getElementById('root'))
}

function App() {
return template;
}

export default App;

这时发现——为什么控制台报错,页面不显示了!

答案是,我们向外暴露的是 App 函数,而 App 函数返回一个函数,在 index.js 中被调用时没有正常返回一段 JSX

于是我们把 JSX 移动到 App 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { render } from 'react-dom';
import './App.css';

let n = 0;
let App = () => (
<div>
{n}
<button onClick={add}>+1</button>
</div>
)

function add() {
n += 1;
render(<App />, document.getElementById('root'))
}

export default App;

成功了!注意,此时 App 一般是用作标签写法,所以最好是大写开头

现在我们得到了 +1 的功能和一段好看的代码

于是又带来了另一个问题——看起来似乎 render 的时候更新的是整个 template,真的吗?

部分重新渲染

显然 n 是变化的,但是 button 的逻辑似乎没有变化,这样不会过度渲染吗?

我们可以用开发者工具在页面上给 button 加一个 id,然后再点击一下试试,发现 button 还是带有 id!

那么我们就可以得到一个结论—— react 知道哪些要改,哪些不用改,在重新渲染的时候只会更新有变化的部分

可是 react 是怎么知道的呢?这就涉及虚拟 DOM 和 DOM diff 算法了

大致就是,react 将页面上的真实 DOM 解析到内存中,建立了一棵内存中的 DOM 树,然后每次重新渲染的时候都会先在内存中比较(其实 vue 也是这么做的)

此处不展开讲,有兴趣的话可以搜搜本站的文章,不过我可能还没写

条件与循环

条件

现在我想自动检测 n 是奇数还是偶数,怎么办呢

容易想到在 App 中加入条件判断

1
2
3
4
5
6
7
let App = () => (
<div>
{n}
{n % 2 === 0 ? '偶数' : '奇数'}
<button onClick={add}>+1</button>
</div>
)

这样就可以了

什么,你说想要 vue 那种,也可以吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { render } from 'react-dom';
import './App.css';

let n = 0;
let App = () => {
let fragment;
if (n % 2 === 0) {
fragment = '偶数';
} else {
fragment = '奇数';
}
return (
<div>
{n}
{fragment}
<button onClick={add}>+1</button>
</div>
)
}

function add() {
n += 1;
render(<App />, document.getElementById('root'))
}

export default App;

此时 App 兼有了更复杂的功能,一般称为函数组件

循环

想展示一个数组,又该怎么办呢

假设有以下数组

1
2
3
4
5
const array = [
{name: 'ringoer', age: 22},
{name: 'enatsu', age: 21},
{name: 'pecco', age: 20}
]

可以得到以下 JSX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { render } from 'react-dom';
import './App.css';

const array = [
{ name: 'ringoer', age: 22 },
{ name: 'enatsu', age: 21 },
{ name: 'pecco', age: 20 }
]

let App = () => (
<div>
<ul>
{array.map((person, index) => <li>{`第${index + 1}位:name = ${person.name}, age = ${person.age}`}</li>)}
</ul>
</div>
)


export default App;

注意,此时我们要循环得到多个 li 标签,所以要先用 {} 来进行 js 循环

然后在 return 的 li 标签中,又是新的 JSX 语法作用域,可以继续使用 {} 来取值

显示结果如下

1
2
3
· 第1位:name = ringoer, age = 22
· 第2位:name = enatsu, age = 21
· 第3位:name = pecco, age = 20

展望

上文中提到了函数组件,但并未详细说明

实际上,react 组件一般使用类组件和函数组件,其中又以函数组件为最方便且常用的写法

敬请期待吧


感谢阅读

--It's the end.Thanks for your read.--