npx --ignore-existing create-react-app my-app
yarn add react-router-dom --save
yarn add react-router-config -S
yarn add antd
1.静态页面引入react
<!-- 需要先移入react.js核心库 -->
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<!-- 引入react-dom.js 用来控制元素操作 -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<!-- 引入jsx 用来控制元素操作 -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 在 -->
// jsx语法 ,遇到{}的时候就进行js解析,遇到HTML标签的时候,就按照HTML的方式解析
// ReactDOM.render 的第一个参数必须是单标签,不能有兄弟标签,这个参数是一个dom结构,
//只是在该dom中可以进行数据渲染
// 第二个参数是第一个参数的dom结构渲染的目标
ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById('test'));
不引入jsx的时候,也可以通过 React.createElement创建标签,ReactDOM.render渲染页面
// React.createElement 创建 一个标签 三个参数,分别是元素标签,属性,内容
// 第二个参数不设置属性的haul,不能省略,要用null 占位
let li1 = React.createElement('li', null, '哈哈');
let li2 = React.createElement('li', null, '哈哈2');
let li3 = React.createElement('li', { className: 'li3' }, '哈哈3');
// 如果内容较多,第三个参数可以是一个数组
let ul = React.createElement('ul', null, [li1, li2, li3]);
let test = document.getElementById('test');
ReactDOM.render(ul, test);
2.设置样式
// 设置样式,实际上就是通过js设置了一个类似css的对象,然后通过标签的style属性引入,注意,
引入的时候需要添加{},因为是通过js解析的,如果遇到了需要设置数据的属性,如果只写一个数字,
则单位默认是px,则不用加引号,如果添加单位是px,必须加引号
// 添加类名 使用className=""来引入类名
{/* 这是render()方法中的注释,快捷键是Ctrl+/ */}
let styles = {
color: '#f00',
fontSize: 30,
};
let arr = [
<p style={styles}>恨不抗日死</p>,
<p className="aa">留作近日休</p>,
];
let test = document.getElementById('test');
ReactDOM.render(<div>{arr}</div>, test);
// 行间设置样式外边的{}代表里边是变量,内部的{}代表的是一个对象
ReactDOM.render(
<div
style={{
color: '#04be02',
fontWeight: 'bold',
textDecoration: 'underline',
}}
>
{arr}
</div>,
test
);
3.点击事件(非组件)(全局中设置事件)
- 通过bind函数修改this指向
function show(str) {
alert(str + '逆战逆战狂野');
}
let test = document.getElementById('test');
// react 事件用小驼峰表示
// 如果使用参数,则使用bind方法,第一个参数是this,代表当前函数的this,
第二个参数是方法传入的this
ReactDOM.render(
<div>
<button onMouseUp={show.bind(this, 'come on!')}>点击</button>
</div>,
test
);
// let arr = ['香九龄', '能温习', '弟于长'];
// let list = arr.map((item, index) => <li key={index}>{item}</li>);
// ReactDOM.render(<ul>{list}</ul>, test);
4.组件对象
组件,在react也叫类,他是react的核心部分,声明的首字母必须大写
声明方式 使用es6的方式,通过继承 React.Component 类来声明组件
在组件中通过 this.props 来获取所有组件上属性的集合
通过 this.props.aa 来获取组件上aa属性的值
属性是可以展示,但是不可修改
- 函数组件
//组件的使用,可以使用单标签,也可以使用双标签
//通过函数的方式设置组件
//特点:属性以参数的形式传递
缺点:没有state和ref属性
function Abc(props) {
return (
<div>
<h1>少壮不努力,{props.str}</h1>
</div>
);
}
ReactDOM.render(
<div>
<HtmlDom />
// 向属性中传值,获取属性的值
<Abc str="面试被嫌弃" />
</div>,
app
);
- 对象组件实例
class HtmlDom extends React.Component {
//老方法es6后就弃用了 getDefaultProps(){
// return {
// name:'李蓝'
// }
// }
constructor(props) {
super(props);
console.log(this);
// 这里属性是只读的,所以不能直接在 组件中书写属性
// this.props.name = '李蓝';
}
//组件的render方法
render() {
return (
<div>
<h1>月下独酌</h1>
<p>你把书框图一醉</p>
<p>{this.props.name}</p>
</div>
);
}
}
// 全局设置默认属性
HtmlDom.defaultProps = {
name: '李蓝',
};
//组件的使用,可以使用单标签,也可以使用双标签
ReactDOM.render(
<div>
<HtmlDom />
<Abc str="面试被嫌弃" />
</div>,
app
);
类组件引入默认属性除了在全局设置ReactDOM.defaultProps之外,还可以通过npm安装prop-types
import propTypes from 'prop-types';//导入之前要提前npm,yarn,或npm的淘宝镜像均可下载
//组件类内部
class PostItem extends React.Component{
constructor(props){
super(props)
this.state = {}
}
给 自定义类组件本身添加静态方法,不是给类实例添加静态方法
//设置默认属性
static defaultProps = {
name:‘点击我‘,
color:'red'
}
//设置默认数据类型 都是外侧方法小驼峰,默认类型内部大驼峰
static propTypes = {
name:PropTypes.stringstring/number/bool/func/object.isRequired
}
}
5.props属性,props.children
this.props 是整个组件的属性集合,如果需要获取到标签内部嵌套的内容,则使用
this.props.children 来获取嵌套的内容, 获取的是一个数组,通过,map方法可以进行循环输出
*/
class Fu extends React.Component {
render() {
return (
<div>
<Son fu={this.props.name}></Son>
<div>梁山好喊</div>
{this.props.children.map((item) => {
return item;
})}
</div>
);
}
}
class Son extends React.Component {
render() {
return <div>{this.props.fu}</div>;
}
}
let name = '李俊';
let arr = [<p>恨不抗日死</p>, <p>留作今日羞</p>];
//下边的arr 在组件标签内传入数组,父组件内部通过this.props.children对外边传入的参数进行遍历,
达到类似于插槽的效果
//父标签内传入变量,在组件内渲染
ReactDOM.render(<Fu name={name}>{arr}</Fu>, app);
6.组件的ref属性,内部refs.aa 用以获取元素节点
// refs是一个比较特殊的属性,他是当前组件中所有带有ref 属性元素的集合,通过this.refs.aa可以
获取到ref=aa 的组件实体,进而对该组件进行操作
//点击按钮获取到Son组件所有的refs内部的ref=hai的组件节点,触发child内部的show 方法
class Son extends React.Component {
act = () => {
console.log(this.refs.hai);
this.refs.hai.show();
};
render() {
return (
<div>
<Child ref="hai"></Child>
<p>滴自己的汗,吃别人的饭</p>
<button onClick={this.act}>点击</button>
</div>
);
}
}
class Child extends React.Component {
show() {
alert('中国人的骨气');
}
render() {
return (
<div>
<p>面朝大海,春暖花开</p>
</div>
);
}
}
const app = document.getElementById('app');
ReactDOM.render(<Son></Son>, app);
7.组件的state 状态属性
// 状态 state ,实质上是一种特殊的属性,他和props的区别是
// state 可以改变
// props 不可以改变
class State extends React.Component {
// constructor() {
// super();
// 设置状态的值
// this.state = { pro: true };
// }
// 或者
state = {
pro: true,
};
changePro = () => {
this.setState({
pro: !this.state.pro,
});
};
render() {
return (
<div>
<p>{this.state.pro ? '海枯石烂' : '大难临头各自飞'}</p>
<button onClick={this.changePro}>点击</button>
</div>
);
}
}
ReactDOM.render(<State />, app);
8.设置数据双向绑定
//核心是input 设置input事件,取到input的value值再修改state中val的值达到数据双向绑定
let app = document.querySelector('#app');
class Bind extends React.Component {
constructor() {
super();
this.state = {
val: '旗开得胜',
};
}
change = (e) => {
let newVal = e.target.value;
this.setState({
val: newVal,
});
};
render() {
return (
<div>
<input type="text" value={this.state.val} onInput={this.change} />
<p>{this.state.val}</p>
</div>
);
}
}
ReactDOM.render(<Bind />, app);
9.react跨域设置代理
- 方法1
- 方法2需要访问多个服务器的时候需要在src文件夹下新建setupProxy.js进行配置
说明:浏览器中有ajax引擎,平常我们发送的ajax请求都是在浏览器中进行请求的。ajax本身存在跨域问题,我们发送请求都是能直接发送到服务器上,被服务器接收到的,但是服务器返回的数据到我们的浏览器页面就会因为跨域问题而无法接收到数据。我们去解决跨域问题就是通过一个代理,也就是中间件去达到跨域(这里的代理实质上是创建一个小型服务器,这个服务器的域名和端口就是我们页面本身的域名和端口,服务器之间发送数据是不存在跨域的(ps:服务器没有ajax引擎),我们使用代理把要访问的服务器域名端口转化为本机的域名端口,本机访问自身是不存在跨域的,也就完美的解决了跨域问题)
10 函数组件 useState 设置默认状态
//引入需要的模块
import React,{ useState } from 'react';
// 使用函数方式创建组件,注意首字母大写
function ActState(){
// 声明一个状态 count,设置状态的方式是 setCount(),count的默认值是0,即 useState(0)
const [count,setCount] = useState(0);
//设置累加函数
function add(){
return setCount(count+1); //设置count的值+1
}
// return 返回组件的dom结构,注意return(dom结构)
return (<div>
<p>{count}</p>
{/* <button onClick={()=>{setCount(count+1)}}>点击改变值</button> */}
<button onClick={add}>点击改变值</button>
</div>);
}
//导出组件
export default ActState;
11.useEfect函数组件监听状态使其动态绑定状态
//efffect 影响
import React,{ useState,useEffect } from 'react';
/*
useEffect 当没有参数的时候,会监听所有的状态变化,如果任意一个状态发生改变,那么它的回调函数就会执行,切记,此时不能在回调函数中设置 状态的改变,不然就会无限循环
当有参数的时候,只是监听参数中状态的改变,可以设置其他状态
当有参数且参数为空的时候,只有在第一次渲染(mount)和卸载(unmount)的时候执行
*/
function Effect(){
const [num,setNum]=useState(0);
//直接使用useEffect,不设置参数
// useEffect(()=>{
// document.getElementById('txt').innerHTML = num;
// // setNum(num+1); 不能在此设置改变
// });
//设置参数,此时监听的是num的变化,可以设置其他状态改变
useEffect(()=>{
document.getElementById('txt').innerHTML = num;
// setNum(num+1); 不能设置监听的那个状态的改变
},[num]);
return (<div>
<p id="txt"></p>
<p>{num}</p>
<button onClick={()=>setNum(num+1)}>点就添加</button>
</div>);
}
export default Effect;
12. 动态添加类名
import React,{ useState } from 'react';
import './tag.css';
import classnames from 'classnames'
/*
动态添加类名
使用 classnames 模块
该模块需要安装
yarn add classnames -S (--save)
设置多个class名
<div className={classnames('wp','lv')}></div>
设置一个固定,一个根据color布尔值显示或者影藏,布尔值为false不加载class名,否则加载
<div className={classnames('wp',{'lv':color})}></div>
设置所有class名根据布尔值显示或隐藏, 布尔值为false不加载class名,否则加载
<div className={classnames({'lv':color,'wp':color})}></div>
*/
function Tag(){
const [color,setColor] = useState(false);
const [num,setNum] = useState(1);
return (<div>
<div className={classnames('wp','lv')}></div>
<div className={classnames('wp',{'lv':color})}></div>
<div className={classnames({'lv':color,'wp':color})}></div>
<button onClick={()=>setColor(!color)}>点击隐藏</button>
<div className="tabs">
事件传参可以直接在标签上写箭头函数或者用bind(this)
<span className={classnames({'on':num===1})} onClick={()=>setNum(1)}>积极向上</span>
<span className={classnames({'on':num===2})} onClick={()=>setNum(2)}>努力进取</span>
<span className={classnames({'on':num===3})} onClick={()=>setNum(3)}>永远不困</span>
<span className={classnames({'on':num===4})} onClick={()=>setNum(4)}>都是梦话</span>
</div>
</div>);
}
export default Tag;
13.函数组件父子传值通过props
import React,{ useState } from 'react';
import Son1 from './son1';
import Son2 from './son2';
import Son3 from './son3';
function Fu(props){
const [str,setStr] = useState('前程似锦');
function getMsg(info){
console.log(123);
setStr(info);
}
return(<div>
<p>{str}</p>
<p>{props.name}</p>
<Son1 tag={props.name} postData={getMsg}>
//类似插槽 直接向子组件放内容
<p>新婚别</p>
<p>兔丝附蓬麻,引蔓故不长</p>
<p>嫁女与征夫,不如弃路旁</p>
</Son1>
<Son2></Son2>
<Son3></Son3>
</div>);
}
export default Fu;
子组件
import React,{ useState } from 'react';
function Son1(props){
return(<div>
//父传子,直接通过属性传出,属性接收
<p>{props.tag}</p>
//箭头函数修改函数this指向外部元素节点
//这里是子传父 父级写方法,通过属性把方法传出,子级通过属性获取传参
<button onClick={()=>props.postData('毛主席')}>点击传值</button>
{props.children}
</div>);
}
export default Son1;
14.函数组件兄弟传值通过bus
import events from 'events'
export default new events.EventEmitter();
//导出事件模块的EventEmitter()实例,方便使用事件触发器和事件监听器
两个子组件中分别引入该事件触发器,一个监听事件
import React,{ useState } from 'react';
import Bus from '../util/bus.js'
function Son2(props){
const [msg,setMsg]=useState('浮光注意');
function postMsg(){
//两个事件名要一致,这里发送事件
Bus.emit('info',msg);
}
return(<div>
<p>son2 {msg}</p>
<button onClick={postMsg}>点击传值给son3</button>
</div>);
}
export default Son2;
import React,{ useState } from 'react';
import Bus from '../util/bus.js'
console.log(Bus);
function Son3(props){
const [str,setStr]=useState('岳飞');
//这里接收事件
Bus.on('info',data=>{
setStr(data);
});
return(<div>
<p>son3 {str}</p>
</div>);
}
export default Son3;
15.ajax-axios请求配置,和fetch请求
import axios from 'axios';
// 配置基本路径
const http = axios.create({
baseURL: 'https://cnodejs.org/api/v1'
});
//请求拦截器 参考axios-http.com
http.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 在函数内部给config设置自定义拦截
config.headers.aa = 456465;
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
export default http;
import React, { useState } from 'react';
import $ajax from '../util/http';
function Ajax() {
const [list, setList] = useState([]);
function add() {
// fetch请求('url', { method: '', data: '' }).then(res => res.json()).then(res => res)
// 需要先转成json
fetch('https://cnodejs.org/api/v1/topics').then(res => {
return res.json();
}).then(res => {
console.log(res.data);
setList([...res.data]);
});
}
function addAjax() {
$ajax.get('/topics').then(res => {
console.log(res.data.data);
setList([...res.data.data]);
});
}
return (<div>
{
list.length <= 0 ?
<div>数据为空</div> :
<ul>
{
list.map((item, index) => {
return <li key={index}>
<p>{item.title}</p>
{/* <img width="200" height="200" src={item.author.avatar_url} /> */}
</li>
})
}
</ul>
}
<button onClick={add}>点击添加fetch</button>
<button onClick={addAjax}>点击添加axios</button>
</div>);
}
export default Ajax;
16.类式组件传值(props传值)
- 属性上不写参数,状态写在哪,方法就在哪里调用
- 父传子,直接属性传值即可
- 子传父,父写方法,并以属性的形式传出,子再写方法,在子方法中执行传过来的函数
- 兄弟传值,找一个公共的父级组件,通过父写方法作为属性传出,后边和子传父一样
case:父组件
import React, { Component } from 'react'
import './App.css'
import { nanoid } from 'nanoid'
import Header from './components/header'
import Content from './components/content'
import Footer from './components/footer'
export default class App extends Component {
state = {
list: [{
id: nanoid(), name: '吃饭', done: true
}, {
id: nanoid(), name: '睡觉', done: false
}, { id: nanoid(), name: '打豆豆', done: false }
]
}
//子传父,fu方法
sendMsg = (obj) => {
const { list } = this.state
const newList = [obj, ...list]
this.setState({
list: newList
})
}
updateId = (id, done) => {
const { list } = this.state
const newList = list.map(element => {
if (element.id === id) {
return { ...element, done: done }
} else {
return element
}
})
this.setState({
list: newList
})
}
render() {
const { list } = this.state
return (
<div className="container">
<div className="wp">
<Header sendMsg={this.sendMsg} />
<Content list={list} updateId={this.updateId} />
<Footer />
</div>
</div>
)
}
}
case:子组件Header
import React, { Component } from 'react'
import { nanoid } from 'nanoid'
import PropTypes from 'prop-types'
export default class Header extends Component {
static propTypes = {
sendMsg: PropTypes.func.isRequired
}
getMsg = (e) => {
const { target, keyCode } = e;
if (target.value.trim() === '') {
alert('输入内容不能为空')
return
}
if (keyCode === 13) {
const obj = {
id: nanoid(),
name: target.value,
done: false
}
子组件中执行父传过来的函数
this.props.sendMsg(obj)
target.value = ''
}
}
render() {
return (
<div className="header">
<input type="text" placeholder="请输入您的任务名称,按回车键确认" onKeyUp={this.getMsg} />
</div>
)
}
}
17.类组件传值.通过发布订阅传值
api:https://www.npmjs.com/package/pubsub-js
安装
yarn add pubsub-js
导入
import PubSub from 'pubsub-js'
// or when using CommonJS 注册
const PubSub = require('pubsub-js');
订阅(接收数据)这里的aa类似于settimeout的返回值,方便取消订阅
var aa = PubSub.subscribe('name1', callback);
发布(发送数据)
PubSub.publish('name1', 'hello world!');
取消订阅数据(可取消多条)
PubSub.unsubscribe(aa,bb,cc);
18react路由基础
参考:https://reactrouter.com/web/guides/quick-start
import logo from './logo.svg';
import './App.css';
/*
要使用路由,需要先引入 react-router-dom 模块,该模块需要安装
yarn add react-router-dom --save
npm install react-router-dom --save
*/
import {
HashRouter as Router,
Route,
Link,
Switch,
NavLink,
Redirect,
} from 'react-router-dom';
/*
Link 组件,设置路由的触发组件,它具有一个 to属性,该属性用来设置路由的路径
Route 组件,用来显示路由路径匹配到的内容,具体工作流程是,当监听到 地址栏中的地址发生变化以后,
所有的Route组件中path属性的值会和地址栏中的路径进行匹配,如果匹配成功,则显示当前Route组件中
所绑定的组件内容
例如
<Route path="设置的路由路径">
<组件名></组件名>
</Route>
<Route path="设置的路由路径" component={组件名}></Route>
<Route path="设置的路由路径" render={<组件名></组件名>}></Route>
以上三种方式都可以渲染组件
如果Route的path属性为空,则表示匹配所有的路由地址
BrowserRouter ,HashRouter 设置路由的时候,路由的所有操作,必须包含在 他们两种组件中的一个里面,
他们的区别是
HashRouter 的地址栏中的路径里面含有#
BrowserRouter 的地址栏中的路径里面没有#
Switch 组件,使用Switch组件以后,他会渲染出第一个匹配到的路由path所对应的组件,其他的组件不会被渲染
给Route组件添加 一个属性 exact(内部返回一个bol) ,表示精确匹配
switch只匹配到第一个存在的路径URL,不完全一致。exact完全精确匹配
NavLink 组件,他和Link都被渲染成了a标签,不同的是NavLink组件具有一个class名 class="active"
Redirect 组件,表示重定向,他具有from和to两个属性,from表示当前访问的路径地址,to表示如果用户
访问from的地址,跳转到哪一个路径
例如 <Redirect from="/abc" to="/"></Redirect> ,用户如果访问 /abc,则跳转到 /
动态路由,也可以称作路径传值,就是把数据书写在路径上,传递给对应的组件
<Link to="/list/123/日本">列表</Link>
<Route path="/list/:id/:name">
<List></List>
<Route>
可以在List组件中获取到id的值为123
通过 router的 hooks 中的 useParams() 获取
useLocation 获取路由的信息,它包含的信息为
hash:'' #后面的内容
search:'' ?后面的内容 a=b&c=d
pathname:'/a/d/'当前路由的路径
state:传递的数据
useHistory 获取当前应用的历史记录,具有如下方法
go()
goBack()
push() 注意,可以用来跳转到新页面 例如:history.push('/about')
*/
import Index from './components/index';
import About from './components/about';
import List from './components/list';
import Login from './components/login';
import Com404 from './components/com404';
const login = localStorage.getItem('login');
function App() {
return (
<div>
<Router>
<Link to="/">首页</Link>
<NavLink to="/about">关于我们</NavLink>
<Link
to={{
pathname: '/list/123/岗村宁次?a=1&b=2&c=3#456',
state: { num: 1000 },
}}
>
列表
</Link>
<Link to="/list/123/岗村宁次?a=1&b=2&c=3#456">列表</Link>
<Switch>
<Route path="/" component={Index} exact></Route>
<Route path="/login">
<Login></Login>
</Route>
<Route
path="/about"
render={() =>
login ? <About></About> : <Redirect to="/login"></Redirect>
}
></Route>
<Route path="/list/:id/:name">
<List></List>
</Route>
<Redirect from="/abc" to="/"></Redirect>
<Route path="*" render={() => <Com404></Com404>}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
19 react 路由配置(类似vue 的routes)
import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Switch, Route, Link, Redirect } from 'react-router-dom';
//引入历史记录
import { useHistory } from "react-router-dom";
let history = useHistory();
//yarn add react-router-config -S 引入后,可以把各个组件以数组的形式配置,
// 引入 renderRoutes 模块,用来渲染配置后的路由对象
import { renderRoutes } from 'react-router-config';
import Index from './components/index';
import List from './components/list';
import Son1 from './components/son1';
import Son2 from './components/son2';
import { Button } from 'antd';
// @import '~antd/dist/antd.css';app.css引入ant公共样式
//配置路由
const routes = [
{
path: '/index',
component: Index, //写成这种形式,子组件中可以完全获取组件信息,// props.route.routes
获取这个组件上的子路由render形式只返回一个对象
// render:()=><Index></Index>,
routes: [
{
path: '/index/son1',
component: Son1,
},
{
path: '/index/son2',
component: Son2,
},
],
},
{
path: '/',
exact: true, //设置精确匹配
render: () => <Redirect to="/index"></Redirect>,
},
{
path: '/list',
component: List,
},
];
function App() {
return (
<div>
<BrowserRouter>
{/* 配置该路由只需 <BrowserRouter> 内部引入link,并用{renderRoutes(routes)}方法渲染即可
子路由需要用上边link,然后 {renderRoutes(props.route.routes)}方法渲染即可 */}
<Link to="/index">首页</Link>
<Link to="/list">列表</Link>
{renderRoutes(routes)}
</BrowserRouter>
<br />
<hr />
<Button>按钮</Button>
</div>
);
}
export default App;