最近调部门,改用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 (
<div>
</div>
)
}
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(<Router>
<Switch>
<Route path="/login" component={Login} />
<Route path="/admin/products" component={List} />
</Switch>
</Router>, 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(
<Router>
<Switch>
{ mainRoutes.map(route => {
// return <Route key={route.path} path={route.path} component={route.component} />
// 由于route里面的key也是path和component,所以可以直接用ES6延展运算符
return <Route key={route.path} {...route} />
}) }
<!-- 当找不到页面的时候就重定向到404 -->
<Redirect to="/404" />
</Switch>
</Router>,
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(
<Router>
<!-- 这里再加一个Route,把路由属性传过去 -->
<Route path="/admin" render={routeProps => <App {...routeProps} />} />
<Switch>
{ mainRoutes.map(route => {
return <Route key={route.path} {...route} />
}) }
<Redirect to="/404" />
</Switch>
</Router>,
document.getElementById('root')
);
然后在 App.js
里面添加如下代码:
import React from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import { adminRoutes } from './routes';
function App() {
return (
<div className="App">
<h1>这是App组件</h1>
<Switch>
{adminRoutes.map(route => {
return (
<Route
key={route.path}
path={route.path}
exact={route.exact}
render={routeProps => {
return <route.component {...routeProps} />
}}
/>
)
})}
<Redirect to="/404" />
</Switch>
</div>
)
}
export default App
React 里面的<Switch>
相当于 Vue 的<router-view>
问题:Vue 可以直接定义嵌套路由,React 可以吗
5. 搭建页面框架
1)组件中引入图片
import React from 'react';
import logo from "./logo.png";
function App() {
return (
<div className="logo">
<img src={logo} alt="logo" />
</div>
)
}
export default App
2)定义行内样式
import React from 'react';
function App() {
return (
<div style={{ background: "#fff" }}>
</div>
<div style={{ height: "100%", borderRight: 0 }}>
</div>
<div style={{ padding: "0 24px 24px" }}>
</div>
<div style={{ margin: "16px 0" }}>
</div>
)
}
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 (
<Menu
mode="inline"
defaultSelectedKeys={["1"]}
defaultOpenKeys={["sub1"]}
style={{ height: "100%", borderRight: 0 }}
>
{routes.map(route => {
return (
<Menu.Item
key={route.path}
onClick={p => props.history.push(p.key)}
>
<Icon type={route.icon}
{route.title}
</Menu.Item>
)
})}
</Menu>
)
}
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 (
<div>
<Button type="primary" size="small">修改</Button>
<Popconfirm
title="确定删除此项?"
onCancel={() => console.log("用户取消删除!")}
onConfirm={() => console.log("用户确认删除!")}
>
<Button style={{ margin: "0 1rem" }} size="small">删除</Button>
</Popconfirm>
</div>
)
}
}]
return (
<Card
title="商品列表"
extra={
<!-- 这里是扩展项 -->
<Button type="primary" size="small">
新增
</Button>
}
>
<Table columns={columns}
bordered
dataSource={dataSource}
/>
</Card>
)
}
export default List;
React 列表自定义字段类型通过 render 实现, 在 Vue 中则是通过 slot 实现
6. 函数组件和 Class 组件
函数组件适合一些比较简单的应用场景,只接受外面传进来的 props
, 没有自己的私有数据 state
和生命周期:
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
如果需要用到私有数据 state
和生命周期就要创建 Class 组件,Class 需要继承 React.Component
:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Class 组件的render()
方法返回的东西就是函数组件里面的内容在
render()
方法里面需要通过this
访问props
和state
7. 事件处理
在 React 中事件的命名采用小驼峰法,而在传统 HTML 中是纯小写。
<button onClick={activateLasers}>
Activate Lasers
</button>
在 React 中必须显式地使用 preventDefault
阻止默认事件,而在传统 HTML 中可以直接 return false
,代码如下:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
在这里,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 (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
如果觉得使用 bind
很麻烦,这里有两种方式可以解决。
b)使用 class fields
一种是可以使用 class fields 正确的绑定回调函数,Create React App 默认启用此语法:
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
c)使用箭头函数
另一种是可以在回调中使用箭头函数,此语法问题在于每次渲染 LoggingButton
时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
2)向事件处理函数传递参数
下面两种方式是等价的。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind
的方式,事件对象以及更多的参数会被隐式的进行传递。
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
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 = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
与运算符 &&
render() {
const count = 0;
return (
<div>
{ count && <h1>Messages: {count}</h1>}
</div>
);
}
之所以能这样做,是因为在 JavaScript 中,true && expression
总是会返回expression
, 而false && expression
总是会返回false
。因此,如果条件是true
,&&
右侧的元素就会被渲染,如果是false
,React 会忽略并跳过它。在上面的代码中,如果
count
的值为0,还是会进行渲染。
三元运算符
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
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 (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
父组件代码:
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 (
<div>
<!-- 绑定自定义事件处理函数 -->
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
在 React 中使用this.props.onTemperatureChange(e.target.value);
向外传递事件同时携带参数相当于 Vue 中的
this.$emit("onTemperatureChange", e.targetvalue)
10. react-redux
- redux三个基本原则:
①:store必须是唯一的
②:只有store可以改变自己的内容
③:reducer 必须是纯函数 - 只有store能改变自己内容说明在reducer里我们不能直接操作state,只能通过定义新变量copy state的值,然后对新变量进行操作并 return 出新变量,不允许直接改变state。
- 什么是纯函数?
给固定的输入,就一定会有固定的输出,并且不会有任何副作用。
所以对于异步函数(定时器、ajax数据请求等)、动态时间都不适意在reducer里订阅。 - store核心api:
crerteStore()
: 创建一个storestore.dispatch(action)
: 派发action,传递storestore.getState()
: 获取store的所有数据store.subscribe()
: 订阅store的变化,接收的回调函数在store改变时候就会自动执行
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。