目录
1.React概述
1.1React简介
- React 是一个 MVC 框架
- React 主要是用来构建 UI
- React 是起源于Facebook的内部项目,用于构建 Instagram 网站,在 2013.05 开源
React 特点:
- 声明式:使用 React 编写UI界面和写HTML几乎一样
- 高效: React通过对DOM的模拟,最大限度地减少与DOM的交互
- 灵活:React可以与已知的库或框架很好地配合
1.2 项目搭建
# 创建 react 项目,项目名是 myapp
npx create-react-app myapp
# 进入my-app 目录
cd myapp
# 启动项目
npm start
注意事项:
-
项目名称中不能有大写字母
-
npx 是 npm v5.2.0 之后提供的命令,使用npx后不需要先全局安装脚手架
-
要在项目根目录下启动项目
1.3项目结构说明
修改项目端口号:在 package.json 文件中修改 scripts 节点
1.4 React 基本使用
- 导入 react 和 react-dom 两个包
- 使用 JSX 语法创建 react 元素
- 调用 ReactDOM.render() 方法将元素渲染到页面中
index.js 文件
// 1. 导入 react 和 react-dom 两个包
import React from 'react'
import ReactDOM from 'react-dom'
// 2. 创建 react 元素
const jsx = (
<h1>Hello World</h1>
)
// 3. 调用 ReactDOM.render() 方法将元素渲染到页面中
// 参数1: react元素
// 参数2: html 页面中的 DOM 元素
ReactDOM.render(jsx, document.querySelector('#app'))
index.html 文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
2. JSX
2.1 JSX简介
JSX 是 JavaScript XML 的简写,就是在 JavaScript 中写 XML 格式的代码(目前我只需要写 HTML)
优势: 和 HTML 一样,简单直观
注意点: 使用小括号包裹 JSX,避免 JS 格式化时自动插入分号
const jsx = (
<div>
<h1>用户信息</h1>
<ul>
<li>用户名: admin</li>
<li>密码: 123456</li>
</ul>
</div>
)
2.2 JSX中的表达式
在 JSX 中,可以在 { } 中嵌入表达式
- 变量 (除了对象都可以)
- 算数表达式
- 函数表达式
- jsx自身也能作为表达式
不能出现在 { } 中的东西
- 变量中保存了一个对象 (只有 style 中可以使用对象)
- if/for 等流程控制不能出现在 { } 中
import React from 'react'
import ReactDOM from 'react-dom'
// 目标: 掌握 { } 中能使用哪些表达式
const a = 10
const b = 3
const arr = ['吃', '喝', '玩', '乐']
const flag = false
const fn = () => {
console.log('这是一个函数')
}
const subJsx = <div>我是另一个JSX</div>
const obj = {
a: 'Hi',
b: '~~'
}
// 必须有一个根节点
const jsx = (
<div>
<div>{ a + b }</div>
<div>{ a - b }</div>
<div>{ a * b }</div>
<div>{ a / b }</div>
<div>{ a % b }</div>
<div>{ arr }</div>
<div>{ flag ? '真的' : '假的' }</div>
<div>{ fn() }</div>
<div>{ subJsx }</div>
{/* 对象和流程控制都不能写在 {} 之间 */}
{/* <div>{ obj }</div> */}
{/* <div>{ for (var i = 0; i < 10; i++) {} }</div> */}
</div>
)
ReactDOM.render(jsx, document.querySelector('#app'))
2.3 条件渲染
import React from 'react'
import ReactDOM from 'react-dom'
const flag = false
const show = (flag) => {
if (flag) {
return (
<div>我是真的</div>
)
} else {
return (
<div>我是假的</div>
)
}
}
const s = show(flag)
ReactDOM.render(s, document.querySelector('#app'))
2.4循环渲染
核心思想:
- 使用数组的 map 方法循环将每个单元的数据改造成 jsx ,并保存在一个新的数组中
- 在 { } 中的数组是可以直接被循环渲染的
- 注意: 要使用 key 绑定每一次循环,key 值要唯一
import React from 'react-dom'
import ReactDOM from 'react-dom'
// 目标: 将数组中的数据渲染到表格的 tbody 中
const userAry = [
{id: 1, username: 'zs', age: 20, gender: 1},
{id: 2, username: 'ls', age: 22, gender: 0},
{id: 3, username: 'ww', age: 21, gender: 1}
]
const jsx = (
<table border="1" width="500" align="center">
<thead>
<tr>
<th>id</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
</tr>
</thead>
<tbody>
/* 使用 map 将数组中每个单元都改造成 tr-td-数据 组成的jsx */
/* {} 中能直接遍历数组,就能显示成表格 */
{
userAry.map(item => {
return (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.username}</td>
<td>{item.age}</td>
<td>{item.gender === 1 ? '男' : '女'}</td>
</tr>
)
})
}
</tbody>
</table>
)
ReactDOM.render(jsx, document.querySelector('#app'))
2.5 理解循环
// map的作用是对数组中的每个单元进行加工
// map循环结束之后会得到一个新的数组
// {} 能直接循环数组并渲染每个单元
const hobbies = ['吃', '喝', '玩', '乐']
const arr = hobbies.map(item => {
return <div>{item}</div>
})
const jsx = (
<div>{ arr }</div>
)
2.6 样式处理
两种方式:
- 行内样式 — style
- 类名 — className
- style 方式: 对象形式设置样式 (不推荐)
const jsx = <div style={ {color: 'red', fontSize: '32px'} }>Hello World</div>
- className方式
- className 就是标签中的 class属性,在 jsx 中必须使用 className
- css样式写在一个独立的文件内
- 使用 import 导入css文件
import React from 'react'
import ReactDOM from 'react-dom'
// 目标: 将 Hello World 字符串变为红色,字体大小为32px
import './assets/css/index.css'
const jsx = <div className='title'>Hello World</div>
ReactDOM.render(jsx, document.querySelector('#app'))
2.7 总结
- JSX 是在js代码中编写html结构,是React声明式的一个体现
- JSX 中可以使用各种表达式,表达式要写在 { } 中
- JSX 中条件渲染是用函数完成的,在函数内计算出最终的 JSX 再在 { } 中调用该函数
- JSX 中的循环渲染使用 map 方法
- JSX 中使用样式有 style 和 className 两种方式
- style中使用对象方式设置样式;
- className中来设置类名,样式写在另一个文件中,切记使用 import 调出css文件
3. React 组件基础
- 组件是React中最重要的技术,使用 React 就是在使用各种组件
- 组件表示页面中的部分功能
- 通常,一个完整的页面时由多个组件组合而成的
- 特点: 可复用、可组合、独立
3.1 使用函数创建组件
- 使用函数创建的组件叫做 函数组件
- 函数名称首字母必须以大写字母开头
- 函数组件必须有返回值,而且是 JSX 结构; 返回值可以为 null, 意思是不渲染任何内容
- 使用函数名作为组件标签名
import React from 'react'
import ReactDOM from 'react-dom'
// 定义函数组件
function MyComp () {
return (
<div>这是我的第一个函数组件</div>
)
}
// 可以直接渲染组件
// ReactDOM.render(<MyComp></MyComp>, document.querySelector('#app'))
// 如果组件中没有其他子节点,可以改为单标签形式
ReactDOM.render(<MyComp />, document.querySelector('#app'))
3.2 使用类创建组件
- 使用 class 创建的组件叫做 类组件
- 类名需要大写,必须继承 React.Component 父类
- 继承父类就意味着能够调用父类中提供的属性和方法
- 类组件必须要有 render 方法,返回 JSX
import React from 'react'
import ReactDOM from 'react-dom'
class Hello extends React.Component {
render () {
return (
<div>这是我的第一个类组件</div>
)
}
}
ReactDOM.render(<Hello></Hello>, document.querySelector('#app'))
3.3 组件抽离
将每个组件放到单独的js文件中,组件就会更加易于开发和维护
目标: 创建一个独立的 MyCom 组件,并在 index.js 使用该组件
步骤:
-
创建 MyCom.js 文件 (组件文件,内部可以使用函数组件或者类组件)
-
在 index.js 文件中导入 MyCom.js 文件,再渲染组件
代码实现:
1)创建 MyCom.js 文件 (组件文件,内部可以使用函数组件或者类组件)
在 src 下创建 components 目录,专门存放组件
// 1. 导入 React 包
import React from 'react'
// 2. 创建一个类组件 (函数组件同理)
class MyCom extends React.Component {
render () {
return (
<div>我是一个类组件--已经被抽离出来了</div>
)
}
}
// 定义一个函数组件
const MyCom1 = () => {
return (
<div>MyCom1--函数组件</div>
)
}
// 3. 导出组件
export default {
MyCom,
MyCom1
}
2)在 index.js 文件中导入 MyCom.js 文件,再渲染组件
import React from 'react'
import ReactDOM from 'react-dom'
// 目标: 创建一个独立的 MyCom 组件,并在 index.js 使用该组件
import Com from './components/Mycom'
ReactDOM.render(<Com.Mycom />, document.querySelector('#app'))
3.4 事件处理
-
语法: on+事件名称={事件处理程序}, 例如:
onClick={()=>{}}
-
注意: 事件名必须用驼峰命名法,例如: onMouseEnter、onKeyUp
目标:创建组件,内部包含一个按钮,并给按钮绑定点击事件
import React from 'react'
import ReactDOM from 'react-dom'
// 目标: 在按钮上注册一个点击事件,弹出一个对话框
class Show extends React.Component {
// 定义事件处理函数
handleClick () {
alert('我被点了一下')
}
render () {
return (
<button onClick={this.handleClick}>点我</button>
)
}
}
ReactDOM.render(<Show />, document.querySelector('#app'))
3.5 事件对象
- 事件处理函数中的参数就是事件对象
- React 中的事件对象叫做 合成事件(对象),能兼容所有浏览器
import React from 'react'
import ReactDOM from 'react-dom'
// 目标:为文本框绑定键盘弹起事件,终端输出当前按键的ascii码
class Show extends React.Component {
// 事件处理函数中的参数就是事件对象
handleKeyUp (e) {
console.log(e.keyCode)
}
render () {
return (
<input type="text" onKeyUp={this.handleKeyUp} />
)
}
}
ReactDOM.render(<Show />, document.querySelector('#app'))
3.6 函数形式实现事件
目标: 使用函数方式定义组件,实现键盘弹起事件输出ascii码
import React from 'react'
import ReactDOM from 'react-dom'
const handleKeyUp = (e) => {
console.log(e.keyCode)
}
const Show = () => {
return (
<input type="text" onKeyUp={handleKeyUp} />
)
}
ReactDOM.render(<Show />, document.querySelector('#app'))
4. 有状态和无状态组件
- 函数组件叫做无状态组件;类组件叫做有状态组件
- 状态 (state),组件中使用的数据都保存在 state 中,并且是组件的私有数据
- 函数组件只负责数据展示(静)
- 类组件有状态,负责动态展示 UI 界面 (动)
4.1 state的基本使用
state用来为组件绑定私有数据,有两种绑定形式
第一种: 在构造函数中定义 state
第二种: 在类中直接定义state (推荐)
案例: 在类组件中定义私有数据,并在页面上渲染出来
import React from 'react'
import ReactDOM from 'react-dom'
// 目标: 使用 state 定义数据,并在页面上展示出来
class Hero extends React.Component {
// 定义state (数据)
state = {
name: '安琪拉',
level: 1,
hp: 680,
mp: 320
}
render () {
// 使用 {} 调用数据
return (
<div>
<ul>
<li>英雄: {this.state.name}</li>
<li>等级: {this.state.level}</li>
<li>血量: {this.state.hp}</li>
<li>蓝量: {this.state.mp}</li>
</ul>
<button>升1级</button>
</div>
)
}
}
ReactDOM.render(<Hero />, document.querySelector('#app'))
4.2 修改数据
setState() 方法用来修改数据
state = {
name: 'zs',
age: 20
}
this.setState({
name: 'ls',
age: 25
})
目标:上例中button按钮上注册点击事件,每升一级,hp增加100,mp增加120
核心: 修改数据需要使用到 setState 方法,方法内是一个对象参数,要修改哪个数据写哪个数据即可
this.setState({
level: this.state.level + 1,
hp: this.state.hp + 100,
mp: this.state.mp + 120
})
注意:千万别习惯性写成 this.state.level = this.state.level + 1
或者 this.state.level += 1
代码实现:
import React from 'react'
import ReactDOM from 'react-dom'
class Hero extends React.Component {
// 定义state (数据)
state = {
name: '安琪拉',
level: 1,
hp: 680,
mp: 320
}
render () {
// 使用 {} 调用数据
return (
<div>
<ul>
<li>英雄: {this.state.name}</li>
<li>等级: {this.state.level}</li>
<li>血量: {this.state.hp}</li>
<li>蓝量: {this.state.mp}</li>
</ul>
<button onClick={() => {
this.setState({
level: this.state.level + 1,
hp: this.state.hp + 100,
mp: this.state.mp + 120
})
}}>升1级</button>
</div>
)
}
}
ReactDOM.render(<Hero />, document.querySelector('#app'))
4.3 this 指向
- 如果将 onClick 指定的事件函数独立出来定义成一个函数则会报错
- 原因: this 的指向不同
- 事件函数直接定义在类当中,this指向 undefined
- 事件函数写在 onClick 中,this指向 Hero 组件
- 解决:
- 使用箭头函数代替传统函数
- 使用bind方法修改this指向
this.upLevel = this.upLevel.bind(this)
- call、apply、bind
箭头函数方式:
import React from 'react'
import ReactDOM from 'react-dom'
class Hero extends React.Component {
// 定义state (数据)
state = {
name: '菊花信',
level: 1,
hp: 680,
mp: 320
}
// 使用箭头函数代替传统函数方式,调整this的指向
upLevel = () => {
this.setState({
level: this.state.level + 1,
hp: this.state.hp + 100,
mp: this.state.mp + 120
})
}
render () {
// 使用 {} 调用数据
return (
<div>
<ul>
<li>英雄: {this.state.name}</li>
<li>等级: {this.state.level}</li>
<li>血量: {this.state.hp}</li>
<li>蓝量: {this.state.mp}</li>
</ul>
<button onClick={this.upLevel}>升1级</button>
</div>
)
}
}
ReactDOM.render(<Hero />, document.querySelector('#app'))
bind方式:
import React from 'react'
import ReactDOM from 'react-dom'
// 目标: 使用点击事件,每升一级,血量增加100,蓝量增加120
class Hero extends React.Component {
constructor () {
super()
// 定义state (数据)
this.state = {
name: '安琪拉',
level: 1,
hp: 680,
mp: 320
}
// 使用传统函数方式,则需要使用 bind 方法修改 this 指向
// 修改upLevel的this指向
// 注意不能马上执行该函数,所以只能用 bind
// 此处的this指的是App类的实例对象
this.upLevel = this.upLevel.bind(this)
}
// 使用传统方式定义事件函数时,函数内部的this是undefined
upLevel () {
this.setState({
level: this.state.level + 1,
hp: this.state.hp + 100,
mp: this.state.mp + 120
})
}
render () {
// 使用 {} 调用数据
return (
<div>
<ul>
<li>英雄: {this.state.name}</li>
<li>等级: {this.state.level}</li>
<li>血量: {this.state.hp}</li>
<li>蓝量: {this.state.mp}</li>
</ul>
<button onClick={this.upLevel}>升1级</button>
</div>
)
}
}
ReactDOM.render(<Hero />, document.querySelector('#app'))
5. 受控组件
5.1 受控组件介绍
- 受控组件通常指的是表单,因为表单是可输入的,必须有对应的状态与之绑定
- React 将 state与表单的 value值绑定到一起
- 给表单元素绑定change事件,将表单元素的值设置为state的值,接收表单值变化
核心代码:
// 1. state 中设置数据
state = {
username: 'admin'
}
// 2. 表单域中将 state中的数据与value绑定
// 3. 文本框每次内容发生变化都会触发 change 事件,可以在事件函数中将文本框中的最新数据更新到 state 中
<input
type="text"
value={this.state.username} //value与username绑定,所以文本框中默认写入 admin
onChange={this.handleText}
/>
// 事件函数中调用 setState 将数据进行保存
// 参数: e 是事件对象
// e.target 中保存了标签的 DOM 对象
// e.target.value 保存了input中的value
// e.target.name 保存了input的name值
handleText = (e) => {
this.setState({
username: e.target.value
})
}
案例:获取用户名文本框中的内容
import React from 'react'
import ReactDOM from 'react-dom'
// 目标: 点击登录按钮,将文本框中的值输出到终端
class App extends React.Component {
state = {
account: ''
}
checkLogin = () => {
console.log(this.state)
}
// 文本框内容变化时数据的处理函数
handleAccount = e => {
console.log(e)
this.setState({account: e.target.value})
}
render () {
return (
<div>
账号: <input type="text" value={this.state.account} onChange={this.handleAccount} /><br />
<button onClick={this.checkLogin}>登录</button>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
5.2 不同域数据的获取方式
- 每个域都要将 value 和 state 中对应的属性绑定
- 每个域都要绑定 onChange 事件
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
state= {
username: '张三',
city: 'xa',
sign:'是个好玩的地方'
}
// handleText事件能够监听文本框中发生的变化
handleText =async (e)=>{
await this.setState({
username:e.target.value
})
console.log(this.state.username);
}
handleSelect =async e =>{
await this.setState({
city:e.target.value
})
console.log(this.state.city);
}
handleArea = async e =>{
await this.setState({
sign:e.target.value
})
console.log(this.state.sign);
}
render () {
return (
<div>
姓名: <input type="text" value={this.state.username} onChange={this.handleText}/><br />
城市:<select value={this.state.city} onChange={this.handleSelect}>
<option value='bj'>北京</option>
<option value='sh'>上海</option>
<option value='xa'>西安</option>
</select><br/>
简介:<textarea value={this.state.sign} onChange={this.handleArea}></textarea>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
5.3 合并处理
核心: e.target
- e.target.value : 保存了标签中的value值
- e.target.name:保存了标签中的name值
解决方案: 每个域中name属性绑定 state 中对应的属性, onChange指定一个函数统一处理
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
state= {
username: '张三',
city: 'xa',
sign:'是个好玩的地方'
}
// e.target是对应域的DOM对象
// DOM value 保存了数据 name 保存了域的名称
handleText =async (e)=>{
const { name,value } = e.target
// console.log(name,value);
await this.setState({
// username:e.target.value
[name]: value
})
console.log(this.state);
}
render () {
return (
<div>
姓名: <input name='username' type="text" value={this.state.username} onChange={this.handleText}/><br />
城市:<select name='city' value={this.state.city} onChange={this.handleText}>
<option value='bj'>北京</option>
<option value='sh'>上海</option>
<option value='xa'>西安</option>
</select><br/>
简介:<textarea name='sign' value={this.state.sign} onChange={this.handleText}></textarea>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
6. 非受控组件(了解)
使用步骤:
- 在构造函数中调用 React.createRef() 方法创建一个 ref 对象
- 将 ref 对象绑定到对应的域
- 通过ref 对象获取域的值
- 该方式是一种直接操作DOM的方式,不推荐
import React from 'react'
import ReactDOM from 'react-dom'
class FormData extends React.Component {
constructor () {
super()
//1. 在构造方法中创建 ref 对象
this.txtRef = React.createRef()
}
getData = () => {
console.log(this.txtRef.current.value)
}
render () {
return (
<div>
{/* 在域中使用 ref属性 绑定 ref对象 */}
<input type="text" ref={this.txtRef} /><br />
<button onClick={this.getData}>获取数据</button>
</div>
)
}
}
ReactDOM.render(<FormData />, document.querySelector('#app'))
7. 综合案例
目标: 完成表单添加英雄,表格渲染英雄功能,表格删除英雄功能,表格点击修改数据渲染到表单确认修改表单数据更新到表格功能。
import React from 'react'
import ReactDOM from 'react-dom'
import './assets/css/cmt.css'
class Comment extends React.Component {
// 定义表格中使用的数组
state = {
heros: [
{ id: 1, heroname: '艾希', nickname: '寒冰射手', age: 180, gender: '女'},
{ id: 2, heroname: '盖伦', nickname: '德玛西亚之力', age: 26, gender: '男'},
{ id: 3, heroname: '瑞兹', nickname: '符文法师', age: 1200, gender: '男'},
],
// 定义表单中使用数据
id: '',
heroname: '',
nickname: '',
age: '',
gender: '',
}
// 确认修改表单数据
editData=()=>{
const {heros} = this.state
this.state.heros.map((item)=>{
if(item.id === this.state.id){
item.heroname = this.state.heroname
item.nickname = this.state.nickname
item.age = this.state.age
item.gender = this.state.gender
}
})
this.setState({
heros,
// 清空表单
id: '',
heroname: '',
nickname: '',
age: '',
gender: ''
})
}
// 删除表格中的数据
deleteData = (e) => {
console.log(e.target.value);
const {heros} = this.state
const isIndex = (item) => item.id === e.target.value;
heros.splice(heros.findIndex(isIndex),1)
// console.log(heros.splice(heros.findIndex(isIndex),1));
this.setState({
heros:heros
})
}
// 修改表格数据渲染到表单中
// handleData = () => {
// let handleHero = this.state.heros.map(item => {
// this.setState({
// id: item.id,
// heroname: item.heroname,
// nickname: item.nickname,
// age: item.age,
// gender: item.gender
// })
// return handleHero
// })
// }
// 将渲染表格代码单独抽取出来
renderList = () => {
// 判断英雄数组中是否有数据
if (this.state.heros.length === 0) {
// 没有则返回 暂无英雄数据 信息
return (
<tr>
<td colSpan="5" align="center">暂无英雄数据</td>
</tr>
)
} else {
// 有则使用map方法将数组单元数据全部重组, 再返回
let tmpAry = this.state.heros.map(item => {
// 修改表格数据显示到表单中
let handleData = ()=>{
this.setState({
id: item.id,
heroname: item.heroname,
nickname: item.nickname,
age: item.age,
gender: item.gender
})
return handleData
}
// 删除表格中的英雄数据
// let deleteData = ()=> {
// const {heros} = this.state
// this.state.heros.map((data) =>{
// if (data.id === item.id){
// // let arr = heros.splice(data ,1)
// const isIndex = (item) => item.id === data.id;
// heros.splice(heros.findIndex(isIndex),1)
// // console.log(heros.splice(heros.findIndex(isIndex),1));
// }
// this.setState({
// })
// })
// }
return(
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.heroname}</td>
<td>{item.nickname}</td>
<td>{item.age}</td>
<td>{item.gender}</td>
<td>
<button onClick={handleData}>修改</button>
<button value={item.id} onClick={this.deleteData}>删除</button>
</td>
</tr>
)
})
return tmpAry
}
}
addHero = () => {
// 1. 解构所有数据
const {heros, heroname, nickname, age, gender} = this.state
if (heroname.trim() === '' || nickname.trim() === '' || age.trim === '' || gender.trim() === '') {
alert('请完整填写表单')
return
}
// 2. 构建一个新的英雄对象
const obj = {
id: heros.length + 1,
heroname,
nickname,
age,
gender
}
// 3. 将新对象追加到数组最后
heros.push(obj)
// 4. 更新整个数组则会直接更新页面, 重置表单
this.setState({
heros,
heroname: '',
nickname: '',
age: '',
gender: ''
})
}
handleForm = e => {
const {name, value} = e.target
this.setState({
[name]: value
})
}
render () {
return (
<div className="container">
<div className="form-box">
<h3>添加新英雄</h3>
姓名: <input type="text" name="heroname" value={this.state.heroname} onChange={this.handleForm} /><br />
昵称: <input type="text" name="nickname" value={this.state.nickname} onChange={this.handleForm} /><br />
年龄: <input type="text" name="age" value={this.state.age} onChange={this.handleForm} /><br />
性别: <input type="text" name="gender" value={this.state.gender} onChange={this.handleForm} /><br />
<button onClick={this.addHero}>添加</button>
<button onClick={this.editData}>确定修改</button>
</div>
<div className="table-box">
<table border="1" align="center" width="400">
<caption><h3>英雄列表</h3></caption>
<thead>
<tr>
<th>id</th>
<th>姓名</th>
<th>昵称</th>
<th>年龄</th>
<th>性别</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{this.renderList()}
</tbody>
</table>
</div>
</div>
)
}
}
ReactDOM.render(<Comment />, document.querySelector('#app'))