最近调部门,改用React开发了,整顿一些罕用知识点。
更多内容还请参考:
React 中文文档
Ant Design
1. 我的项目创立
# 创立我的项目npx create-react-app your-project-name# antdnpm 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";// 在组件中引入 antdimport { 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)