组件间通信
方式一: 通过props传递
- 共同的数据放在父组件上, 特有的数据放在自己组件内部(state)
- 通过props可以传递一般数据和函数数据, 只能一层一层传递
- 一般数据–>父组件传递数据给子组件–>子组件读取数据
- 函数数据–>子组件传递数据给父组件–>子组件调用函数
方式二: 使用消息订阅(subscribe)-发布(publish)机制
- 工具库: PubSubJS
- 下载: npm install pubsub-js --save
- 使用:
import PubSub from ‘pubsub-js’ //引入
PubSub.subscribe(‘delete’, function(msg ,data){ }); //订阅
PubSub.publish(‘delete’, data) //发布消息
方式三: redux
react-router4
react-router的理解
- react的一个插件库
- 专门用来实现一个SPA应用
- 基于react的项目基本都会用到此库
SPA的理解
- 单页Web应用(single page web application,SPA)
- 整个应用只有一个完整的页面
- 点击页面中的链接不会刷新页面, 本身也不会向服务器发请求
- 当点击路由链接时, 只会做页面的局部更新
- 数据都需要通过ajax请求获取, 并在前端异步展现
路由的理解
-
什么是路由?
a. 一个路由就是一个映射关系(key:value)
b. key为路由路径, value可能是function/component -
路由分类
a. 后台路由: node服务器端路由, value是function, 用来处理客户端提交的请求并返回一个响应数据
b. 前台路由: 浏览器端路由, value是component, 当请求的是路由path时, 浏览器端前没有发送http请求, 但界面会更新显示对应的组件 -
后台路由
a. 注册路由: router.get(path, function(req, res))
b. 当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据 -
前端路由
分为hash(带#)与history(不带#)
history模式的路由就是利用了BOM的history。 history模式会向服务器发出请求,但会因匹配不到路由所以会返回404,为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面,由前端负责路由的匹配与跳转
history库
a. 网址: https://github.com/ReactTraining/history
b. 管理浏览器会话历史(history)的工具库
c. 包装的是原生BOM中window.history和window.location.hash
history API
a. History.createBrowserHistory(): 得到封装window.history的管理对象
b. History.createHashHistory(): 得到封装window.location.hash的管理对象
c. history.push(): 添加一个新的历史记录
d. history.replace(): 用一个新的历史记录替换当前的记录
e. history.goBack(): 回退到上一个历史记录
f. history.goForword(): 前进到下一个历史记录
g. history.listen(function(location){}): 监视历史记录的变化
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>history test</title>
</head>
<body>
<p><input type="text"></p>
<a href="/test1" onclick="return push('/test1')">test1</a><br><br>
<button onClick="push('/test2')">push test2</button><br><br>
<button onClick="back()">回退</button><br><br>
<button onClick="forword()">前进</button><br><br>
<button onClick="replace('/test3')">replace test3</button><br><br>
<script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
<script type="text/javascript">
let history = History.createBrowserHistory() // 方式一
history = History.createHashHistory() // 方式二
// console.log(history)
function push (to) {
history.push(to)
return false
}
function back() {
history.goBack()
}
function forword() {
history.goForward()
}
function replace (to) {
history.replace(to)
}
history.listen((location) => {
console.log('请求路由路径变化了', location)
})
</script>
</body>
</html>
react-router相关API
组件
- < BrowserRouter>
- < HashRouter>
- < Route >
- < Redirect >
- < Link >
- < NavLink >
- < Switch >
其它 - history对象
- match对象
- withRouter函数
基本路由使用
准备
- 下载react-router: npm install --save react-router-dom@4
react-router 的工作方式
是在组件树顶层放一个 Router 组件,然后在组件树中散落着很多 Route 组件(注意比 Router 少一个“r”),顶层的 Router 组件负责分析监听 URL 的变化,在它之下的 Route 组件可以直接读取这些信息。
Router
BrowserRouter:使用于现代浏览器,支持H5 history API
HashRouter:常用于旧款浏览器。根据不同的hashType
MemoryHistory:常用于非DOM环境,例如React Native或者测试,History存于内存。
Route 组件
Route只是一个具有渲染方法的普通 React 组件,路由匹配成功则渲染该组件。
三个常用属性:
path:路由的匹配规则(可以省略)
exact:用于精确匹配路由(可以省略)
例如/会匹配到/user,如下就可以只匹配/了
<Route exact path="/" component={App} />
component:需要渲染的组件
引入方式:
import {Route, BrowserRouter as Router} from "react-router-dom";
示例
import {Route, BrowserRouter as Router} from "react-router-dom";
import A from 'A'
import B from 'B'
const routing = (
<Router>
<Route component={Home} >
<Route exact path="/a" component={A} />
<Route exact path="/b" component={B} />
</Router>
)
ReactDOM.render(routing, document.getElementById("root"));
Redirect
当用户访问某界面时,该界面并不存在,此时用Redirect重定向,重新跳到一个我们自定义的组件里,to属性指向要跳转的页面。也可以如下
<Route component={Notfound} />
Link 与 NavLink 组件
对于单页应用,需要在不同页面之间切换,往往需要一个导航栏,我们在这里也实现一个简单的导航栏。
NavLink 与 Link 的区别:
Link 用于导航站点上的不同路由。
NavLink是一种特殊的Link,用于将样式属性添加到当前路由。在一组NavLink中设置activeClassName 属性值为classname,则只有被选中的NavLink的class会有效
在App.js中,我们让网页由两个组件 Navigation 和 Content 组成, Navigation 就是导航栏,而 Content 是具体内容。
class App extends Component {
render() {
return (
<div className="App">
<Navigation />
<Content />
</div>
);
}
}
NavLink 组件,是基于 Link 组件,它有一个 activeClassName 属性,目的在于如果路由匹配成功,则为当前导航添加选中样式。
Navigation 组件:
import React from 'react';
class Navigation extends Component {
render() {
<div>
<NavLink activeClassName="active" to="/a">A</NavLink>
<NavLink activeClassName="active" to="/b">B</NavLink>
</div>
}
}
Content组件:
import React from "react";
import A from './A';
import B from './B';
import 404 from './404'
const routing = (
<Router>
<Switch>
<Route exact path="/a" component={A} />
<Route exact path="/b" component={B} />
<Route component={404} />
</Switch>
</Router>
);
Path 属性的 URL 参数
只要是/users/xxx的路由都会转到Users.jsx组件,该组件内部可以通过this.props.match.params.id获得path中的id值
react-ui
最流行的开源React UI组件库
material-ui(国外)
- 官网: http://www.material-ui.com/#/
- github: https://github.com/callemall/material-ui
ant-design(国内蚂蚁金服)
- PC官网: https://ant.design/index-cn
- 移动官网: https://mobile.ant.design/index-cn
- Github: https://github.com/ant-design/ant-design/
- Github: https://github.com/ant-design/ant-design-mobile/
ant-design-mobile使用入门
使用create-react-app创建react应用
npm install create-react-app -g
create-react-app antm-demo
cd antm-demo
npm start
搭建antd-mobile的基本开发环境
- 下载
npm install antd-mobile --save
- src/App.jsx
import React, {Component} from 'react'
// 分别引入需要使用的组件
import Button from 'antd-mobile/lib/button'
import Toast from 'antd-mobile/lib/toast'
export default class App extends Component {
handleClick = () => {
Toast.info('提交成功', 2)
}
render() {
return (
<div>
<Button type="primary" onClick={this.handleClick}>提交</Button>
</div>
)
}
}
- src/index.js
import React from 'react';
import ReactDOM from 'react-dom'
import App from "./App"
// 引入整体css
import 'antd-mobile/dist/antd-mobile.css'
ReactDOM.render(<App />, document.getElementById('root'))
- index.html
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
if(!window.Promise) {
document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>');
}
</script>
实现按需打包(组件js/css)
- 下载依赖包
yarn add react-app-rewired --dev
yarn add babel-plugin-import --dev - 修改默认配置:
package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom"
}
config-overrides.js
const {injectBabelPlugin} = require('react-app-rewired');
module.exports = function override(config, env) {
config = injectBabelPlugin(['import', {libraryName: 'antd-mobile', style: 'css'}], config);
return config;
};
- 编码
// import 'antd-mobile/dist/antd-mobile.css'
// import Button from 'antd-mobile/lib/button'
// import Toast from 'antd-mobile/lib/toast'
import {Button, Toast} from 'antd-mobile'