最近调部门,改用React开发了,整理一些常用知识点。
更多内容还请参考:
React 中文文档
Ant Design
1. 项目创建
# 创建项目
npx create-react-app your-project-name
# antd
npm i antd
# 路由
npm i react-router-dom
2. 启动服务
npm start
Vue 脚手架构建的项目使用
npm run serve
3. 安装插件
VS Code安装ES 7,非常方便生成组件代码
创建Login.js
文件输入
rfce
按回车,就可以初始化一个组件代码
import React from 'react'
function Login() {
return (
)
}
export default Login
4. 创建路由
1)简单实现
打开根目录中的 index.js
文件,引入路由模块,添加如下代码:
import React from 'react';
import ReactDOM from 'react-dom';
// 引入路由模块,使用哈希路由
import { HashRouter as Router, Switch, Route } from 'react-router-dom';
import './index.css';
// 引入组件
// 不用自己引入,当用到的时候,编辑器会自动添加
import Login from './pages.Login';
import List from './pages/admin/products/List';
ReactDOM,render(
, document.getElementById('root'));
React 里面的index.js
相当于 Vue 的main.js
文件
2)正常用法
在开发中一般不会这样写,而是在根目录下建一个 routes
目录,在 routes
目录下建 index.js
文件,添加如下代码:
import Login from "../pages/Login";
import Index from "../pages/admin/dashboard/Index";
import List from "../pages/admin/products/List";
import Edit from "../pages/admin/products/Edit";
// 普通页面路由
export const mainRoutes = [{
path: '/login',
component: Login
}, {
path: '/404',
component: PageNotFound
}]
// 需要鉴权的路由
export const adminRoutes = [{
path: '/admin/dashboard',
component: Index
}, {
path: '/admin/products',
component: List,
exact: true // 路由path和下面重复了,需要设置一下,只匹配当前path
}, {
path: '/admin/products/edit/:id',
component: Edit
}]
然后 index.js
文件可以改成下面这样:
import React from 'react';
import ReactDOM from 'react-dom';
// 引入路由模块,使用哈希路由
import {
HashRouter as Router,
Switch,
Route,
Redirect
} from 'react-router-dom';
import './index.css';
import { mainRoutes } from "./routes";
ReactDOM,render(
{ mainRoutes.map(route => {
// return
// 由于route里面的key也是path和component,所以可以直接用ES6延展运算符
return
}) }
,
document.getElementById('root')
);
3)嵌套路由
当访问的地址以 /admin
开头的时候,都走 App
这个容器。
在 index.js
文件中再加一个 Route
,代码如下:
import React from 'react';
import ReactDOM from 'react-dom';
import {
HashRouter as Router,
Switch,
Route,
Redirect
} from 'react-router-dom';
import './index.css';
import App from "./App";
import { mainRoutes } from "./routes";
ReactDOM,render(
} />
{ mainRoutes.map(route => {
return
}) }
,
document.getElementById('root')
);
然后在 App.js
里面添加如下代码:
import React from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import { adminRoutes } from './routes';
function App() {
return (
这是App组件
{adminRoutes.map(route => {
return (
{
return
}}
/>
)
})}
)
}
export default App
React 里面的相当于 Vue 的
问题:Vue 可以直接定义嵌套路由,React 可以吗
5. 搭建页面框架
1)组件中引入图片
import React from 'react';
import logo from "./logo.png";
function App() {
return (
)
}
export default App
2)定义行内样式
import React from 'react';
function App() {
return (
)
}
export default App
在 React 中{}
内部其实就是 JS 表达式样式其实是以 JS 对象形式定义的
style 看上去是双大括号,但其实两层括号是不一样的,外层括号代表里面定义的是 JS 表达式,内层括号代表 JS 对象
3)路由点击跳转
import React from 'react';
// 在组件中使用路由需要用到 withRouter 插件,否则会报错
import { withRouter } from "react-router-dom";
// 在组件中引入 antd
import { Layout, Menu, Breadcrumb, Icon } from "antd";
import { adminRoutes } from "../../routes/index";
const routes = adminRoutes.filter(route => route.isShow);
function Index() {
return (
)
}
export default withRouter(Index); // 这里需要把组件套进去
问题:Vue 可以直接通过全局方法this.$router.push()
实现路由跳转,React 没有吗问题:React 的 antd 组件能否全局注册
4)列表页面搭建
import React from 'react';
import { Card, Table, Button, Popconfirm } from "antd";
// 数据源
const dataSource = [{
id: 1,
name: "Jack",
price: 8888
}, {
id: 2,
name: "Pony",
price: 2333
}, {
id: 3,
name: "Eric",
price: 9999
}]
function List() {
// 表头字段
const columns = [{
title: '序号',
key: "id",
width: 80,
align: "center",
render: (txt, record, index) => index + 1 // 自定义字段类型
}, {
title: "名字",
dataIndex: "name" // 对应 dataSource 里面的 name 字段
}, {
title: "价格",
dataIndex: "price" // 对应 dataSource 里面的 price 字段
}, {
title: "操作",
render: (txt, record, index) => {
return (
console.log("用户取消删除!")}
onConfirm={() => console.log("用户确认删除!")}
>
)
}
}]
return (
}
>
)
}
export default List;
React 列表自定义字段类型通过 render 实现, 在 Vue 中则是通过 slot 实现
6. 函数组件和 Class 组件
函数组件适合一些比较简单的应用场景,只接受外面传进来的 props
, 没有自己的私有数据 state
:
function Clock(props) {
return (
Hello, world!
It is {props.date.toLocaleTimeString()}.
);
}
如果需要用到私有数据 state
就要创建 Class 组件,Class 需要继承 React.Component
:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}
Class 组件的render()
方法返回的东西就是函数组件里面的内容在
render()
方法里面需要通过this
访问props
和state
7. 事件处理
在 React 中事件的命名采用小驼峰法,而在传统 HTML 中是纯小写。
在 React 中必须显式地使用 preventDefault
阻止默认事件,而在传统 HTML 中可以直接 return false
,代码如下:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
Click me
);
}
在这里,
e
是一个合成事件。React 事件与原生事件不完全相同。
1)事件处理函数 this 的绑定
a)在构造器中绑定 this
在 JavaScript 中,class 的方法默认不会绑定 this
。如果你忘记绑定 this.handleClick
并把它传入了 onClick
,当你调用这个函数的时候 this
的值为 undefined
。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
);
}
}
如果觉得使用 bind
很麻烦,这里有两种方式可以解决。
b)使用 class fields
一种是可以使用 class fields 正确的绑定回调函数,Create React App 默认启用此语法:
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
);
}
}
c)使用箭头函数
另一种是可以在回调中使用箭头函数,此语法问题在于每次渲染 LoggingButton
时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
);
}
}
2)向事件处理函数传递参数
下面两种方式是等价的。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind
的方式,事件对象以及更多的参数会被隐式的进行传递。
8. 条件渲染
基础用法:
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {isLoggedIn: false};
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = ;
} else {
button = ;
}
return (
{button}
);
}
}
与运算符 &&
render() {
const count = 0;
return (
{ count && Messages: {count}
}
);
}
之所以能这样做,是因为在 JavaScript 中,true && expression
总是会返回expression
, 而false && expression
总是会返回false
。因此,如果条件是true
,&&
右侧的元素就会被渲染,如果是false
,React 会忽略并跳过它。在上面的代码中,如果
count
的值为0,还是会进行渲染。
三元运算符
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
{isLoggedIn
?
:
}
);
}
9. 状态提升
实际上就是子组件向父组件传参
子组件代码:
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
// 向外传递自定义事件
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
);
}
}
父组件代码:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
);
}
}
在 React 中使用this.props.onTemperatureChange(e.target.value);
向外传递事件同时携带参数相当于 Vue 中的
this.$emit("onTemperatureChange", e.targetvalue)