React 学习手册
快速入门
react 使用 虚拟操作真实dom,需要自己建立虚拟dom 然后进行渲染,jsx 需要使用bebel,才能转化为js,也可以直接使用js但是需要使用具体的api ,可以间案例二,这里的虚拟dom是一般的 Object ,属性更少
具体的可见下面的案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 虚拟dom 的容器 -->
<div id="test">
</div>
<!-- 关键的依赖包 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
// 创建虚拟dom
const VDOM = <h1>Hello,react</h1>;
// 渲染虚拟dom到界面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 虚拟dom 的容器 -->
<div id="test">
</div>
<!-- 关键的依赖包 核心库放在前面-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script type="text/javascript"> /* babel 一定要放里面*/
// 创建虚拟dom const 不要放里面 标签 ,{属性},内容不能使用标签需要再加一个 createElement
const VDOM = React.createElement('h1',{id:'title'},'hello,react');
// 渲染虚拟dom到界面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
JSX 的基本规则
1.定义虚拟DOM,不要写引号
2.标签中混入js {}
3.样式的类名属性使用 className
4.内联样式 要用 style={{key:value}
5.只有一个根标签 和vue2 一样
6.标签必须闭合
7.小写字母开头识别为 html 标签,大写字母开头是react 组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
const Name='kiri';
const Age ='16';
const stu = <div className="info">
{/*注释要放在花括号里面*/}
<p>姓名:{Name.toLocaleLowerCase()}</p>
<p>年龄:{Age}</p>
</div>
ReactDOM.render(stu,document.querySelector('.box'));
</script>
</body>
</html>
遍历元素进行渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 虚拟dom 的容器 -->
<div id="test">
</div>
<!-- 关键的依赖包 核心库放在前面-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel"> /* babel 一定要放里面*/
// 创建虚拟dom const 不要放里面
const data=['Angular','React','Vue']
const VDOM = (
<div>
<h1>前端js框架</h1>
<ul>
{
data.map((item,index)=>{
return <li key={index}>{item}</li>})
}
</ul>
</div>
)
// 渲染虚拟dom到界面
ReactDOM.render(VDOM,document.getElementById('test'));
</script>
</body>
</html>
面向组件化编程
基于react 的开发者工具
先要在谷歌商店里面下载一个开发者工具
react developer tools
面向组件编程
可以跟着React 教程 | 菜鸟教程 (runoob.com)学习
函数式组件:用函数定义的组件称为函数式组件,用于简单组件
类式组件:用类定义的组件适用于复杂的组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 虚拟dom 的容器 -->
<div id="test">
</div>
<!-- 关键的依赖包 核心库放在前面-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel"> /* babel 一定要放里面*/
// 创建虚拟dom const 不要放里面
function Demo(){
return <h2>我是一个用函数定义的组件</h2>
}
// 渲染虚拟dom到界面 Demo 要使用大写开头
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
</body>
</html>
带参数的函数组件
function Demo(props){
return <h2>{props.name}!</h2>;
}
const element =<Demo name='tom'/>;
// 渲染虚拟dom到界面 Demo 要使用大写开头
ReactDOM.render(element,document.getElementById('test'));
混合子组件
function Name(props){
return <h1>网站的名字:{props.name}</h1>;
}
function Url(props){
return <h1>网站地址:{props.url}</h1>;
}
function Nickname(props){
return <h1>网站小名:{props.nickname}</h1>;
}
// 创建虚拟dom const 不要放里面
function Demo(props){
return(
<div>
<h2>用户名是:{props.name}</h2>
<Name name='冬日半岛'/>
<Url url="www.winterhalfland.top"/>
<Nickname nickname="个人网页"/>
</div>
)
}
const element =<Demo name='tom'/>;
// 渲染虚拟dom到界面 Demo 要使用大写开头
ReactDOM.render(element,document.getElementById('test'));
JS 的类基本使用 ,后面会用到
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
speak(){
console.log(`我叫${this.name},我的年龄是${this.age}`);
}
}
class Student extends Person{
constructor(name,age,grade){
super(name,age);
this.grade=grade;
}
speak(){
console.log(`我叫${this.name},我的年龄是${this.age},我在年级${this.grade}`);
}
study(){
console.log('我正在努力学习');
}
}
const p1=new Person('tom',18);
const p2= new Person('jerry',19);
const p3= new Student('tom',20,2);
p1.speak();
p2.speak();
p3.speak();
p3.study();
</script>
</body>
</html>
类组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
//创建类组件
class MyComponent extends React.Component{
// render会指向实例
render(){
return <h2>我是一个简单组件</h2>
}
}
ReactDOM.render(<MyComponent/>,document.querySelector('#test'));
</script>
</body>
</html>
类组件的 state 属性的设置和使用
//创建类组件
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state={data:new Date()};
}
render(){
return (
<div>
<h1>Hello,word</h1>
<h2>现在是{this.state.data.toLocaleTimeString()}.</h2>
</div>
)
}
}
ReactDOM.render(<MyComponent/>,document.querySelector('#test'));
添加生命周期函数
实现时间显示
//创建类组件
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state={data:new Date()};
}
componentDidMount(){
this.timerID=setInterval(()=>{
this.tick()
},1000)
}
componetWillUnmount(){
clearInterval(this.timerID)
}
tick(){
this.setState({
data:new Date()
})
}
render(){
return (
<div>
<h1>Hello,word</h1>
<h2>现在是{this.state.data.toLocaleTimeString()}.</h2>
</div>
)
}
}
ReactDOM.render(<MyComponent/>,document.querySelector('#test'));
可以使用子组件来实现 time 转化的功能
function FormatDate(props){
return <h2>现在是{props.data.toLocaleTimeString()}</h2>
}
<div>
<h1>Hello,word</h1>
<FormatDate date={this.state.date}/>
</div>
props的 默认值设置,设置默认值之后就不需要进行设置
class HelloMessage extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
HelloMessage.defaultProps = {
name: 'Runoob'
};
父子组件的props通讯,将数据放在父组件当中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test"></div>
<!-- 关键的依赖包 核心库放在前面-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
class Website extends React.Component{
constructor(){
super();
this.state={
name:"冬日半岛",
site:"https://www.winterhalfland.top"
}
}
render(){
return(
<div>
<Name name={this.state.name}/>
<Link site={this.state.site}/>
</div>
)
}
}
class Name extends React.Component{
render(){
return(
<h1>{this.props.name}</h1>
)
}
}
class Link extends React.Component{
render(){
return(
<a href={this.props.site}>
{this.props.site}</a>
)
}
}
const element = < Website/>;
ReactDOM.render(
element,
document.getElementById('test')
);
</script>
</body>
</html>
Props 验证----- 规定数据的类型
需要添加一个库
<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>
const title="冬日半岛";
// const title=123; // 如果 输入的类型不是想要的类型就会在控制台出现报错
class MyTitle extends React.Component{
constructor(){
super()
}
render(){
return(
<h2>{this.props.title}</h2>
)
}
}
MyTitle.propTypes={
title:PropTypes.string
};
ReactDOM.render(
<MyTitle title={title}/>,
document.querySelector('#test')
)
绑定事件
无参数的事件绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test">
</div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
class Taggle extends React.Component{
constructor(){
super()
this.active=this.active.bind(this);
}
active(e){
e.preventDefault();
console.log('点击成功');
}
render(){
return(
<a href='#' onClick={this.active}>点击</a>
)
}
}
ReactDOM.render(<Taggle/>,document.querySelector('#test'));
</script>
</body>
</html>
带有参数的事件绑定
class Taggle extends React.Component{
constructor(){
super()
this.state={name:'kiri'};
this.active=this.active.bind(this);
}
active(name,e){
e.preventDefault();
console.log(name+'点击成功');
}
render(){
return(
<a href='#' onClick={this.active.bind(this,this.state.name)}>点击</a>
)
}
}
ReactDOM.render(<Taggle/>,document.querySelector('#test'));
条件渲染
function Login(props){
return <h1>请登录!</h1>
}
function Sign(props){
return <h2>请注册!</h2>
}
function Taggle(props){
const islogin=props.islogin;
if(islogin){
return <Login/>;
}
else{
return <Sign/>;
}
}
ReactDOM.render(<Taggle islogin={islogin}/>,document.querySelector('#test'));
单向的state的修改
//进行单向的数据修改 这里react 的状态更新是异步的
const {count}=this.state
this.setState({count:count+1})
// setState 的更新状态是可以添加回调,回调是渲染之后进行调用的,这个回调可以使用更新后的状态
this.setState({count:count+1},()=>{console.log(this.state.count)})
// 函数式的setState 如果更新依赖于原来的状态就用函数式更好
this.setState((state,porps)=>{
return {count:state.count+1}
},()=>{console.log(this.state.count)})
// 不过实际情况下用useState hooks
元素变量
当元素变量进行变化的时候,切换不同的组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test">
</div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
const isLogged=false;
function Login(props){
return <h1>请登录!</h1>
}
function Sign(props){
return <h1>请注册!</h1>
}
class Taggle extends React.Component{
constructor(props){
super(props);
this.handleLoginclick=this.handleLoginclick.bind(this);
this.handleSignclick=this.handleSignclick.bind(this);
this.state={isLogged:props.isLogged};
}
handleLoginclick(){
this.setState({isLogged:false})
}
handleSignclick(){
this.setState({isLogged:true})
}
render(){
const isLogged=this.state.isLogged;
let button=null;
let title=null;
if(isLogged){
button = <button onClick={this.handleLoginclick}>已注册进行登录</button>
title=<Login/>
}else{
button = <button onClick={this.handleSignclick}>未注册进行注册</button>
title=<Sign/>
}
return(
<div>
{title}
{button}
</div>
)
}
}
ReactDOM.render(<Taggle isLogged={isLogged}/>,document.querySelector('#test'));
</script>
</body>
</html>
&& 字符串的短路作用和逻辑判断
function Taggle(props){
let unreadMessage=props.unreadMessage;
return(
<div>
<h1>Hello!</h1>
{unreadMessage.length>0 &&
<h2>
您有{unreadMessage.length}条信息未读
</h2>
}
</div>
)
}
let message=['React','vue','CommonJS'];
ReactDOM.render(<Taggle unreadMessage={message}/>,document.querySelector('#test'));
三目运算符
function Taggle(props){
let unreadMessage=props.unreadMessage;
return(
<div>
<h1>Hello!</h1>
{unreadMessage.length>0 ?
(<h2>
您有{unreadMessage.length}条信息未读
</h2>):(
<h2>
您没有信息未读!
</h2>
)
}
</div>
)
}
let message2=[];
let message=['React','vue','CommonJS'];
ReactDOM.render(<Taggle unreadMessage={message}/>,document.querySelector('#test'));
阻止组件渲染,通过条件判断返回是否返回组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.warnning{
color:red;
}
</style>
</head>
<body>
<div id="test">
</div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
function WarnBanner(props){
if(!props.warn){
return null;
}
return(
<div className="warnning">
警告!
</div>
)
}
class Taggle extends React.Component{
constructor(props){
super(props);
this.state={showWarning:true}
this.handleWarnClick=this.handleWarnClick.bind(this);
}
handleWarnClick(){
this.setState(
prevState=>({
showWarning:!prevState.showWarning
})
)
}
render(){
return (
<div>
<WarnBanner warn={this.state.showWarning}/>
<button onClick={this.handleWarnClick}>{this.state.showWarning?'隐藏':'显示'}</button>
</div>
)
}
}
ReactDOM.render(<Taggle />,document.querySelector('#test'));
</script>
</body>
</html>
React keys 和列表
const number=[1,2,3,4,5];
const listItems=number.map((numbers)=>
<li>{numbers}</li>
)
function Page(){
return (
<ul>
{listItems}
</ul>
)
}
ReactDOM.render(<Page />,document.querySelector('#test'));
变量数组的时候带keys, key可以是列表中独一无二的字符串,也可以是对象里面的元素,或者是列表里的索引号
function Page(props){
const number=props.numbers;
const listItems=number.map((numbers)=>
<li key={numbers.toString()}>{numbers}</li>
)
return (
<ul>
{listItems}
</ul>
)
}
const numbers=[1,2,3,4,5];
ReactDOM.render(<Page numbers={numbers} />,document.querySelector('#test'));
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
将key 用于子组件
function Page(props){
const number=props.numbers;
const listItems=number.map((numbers)=>
<ListItem key={numbers.toString()} value={numbers} />
)
return (
<ul>
{listItems}
</ul>
)
}
function ListItem(props){
return (
<li>{props.value}</li>
)
}
const numbers=[1,2,3,4,5];
ReactDOM.render(<Page numbers={numbers} />,document.querySelector('#test'))
key 应该唯一, 数组内容案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.warnning{
color:red;
}
</style>
</head>
<body>
<div id="test">
</div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
function Posts(props){
const posts=props.posts;
const SliderItems=(posts.map(post=>
<li key={post.id}>{post.title}</li>
))
const slider= (
<ul>
{SliderItems}
</ul>
)
const content=(posts.map(post=>
<div key={post.id}>
<h1>{post.title}</h1>
<h3>{post.content}</h3>
</div>
))
return (
<div>
{slider}
<hr/>
{content}
</div>
)
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(<Posts posts={posts} />,document.querySelector('#test'));
</script>
</body>
</html>
React 组件API
设置状态:setState 返回一个对象 {},可以是函数形式返回也可以是{}形式
替换状态:replaceState
设置属性:setProps
替换属性:replaceProps
强制更新:forceUpdate
获取DOM节点:findDOMNode
判断组件挂载状态:isMounted
// 使用 setState点击按钮进行数字的自增
class Counter extends React.Component{
constructor(props){
super(props);
this.state={number:0};
this.handleAdd=this.handleAdd.bind(this);
}
// 这里的绑定事件是setData 返回的是新的data值
handleAdd(){
this.setState(
function(state){
return{number:state.number+1}
}
)
}
render(){
const result=(
<h2> 现在加了
{this.state.number}个数了
</h2>
)
const btn =(
<button onClick={this.handleAdd}>点击
</button>
)
return(
<div>
{result}
{btn}
</div>
)
}
}
ReactDOM.render(<Counter />,document.querySelector('#test'));
常用的组件生命周期
常用的生命周期函数:
挂载时期:
constructor(): 在 React 组件挂载之前,会调用它的构造函数。getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。render(): render() 方法是 class 组件中唯一必须实现的方法。componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用
更新时期:
getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。shouldComponentUpdate():当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。render(): render() 方法是 class 组件中唯一必须实现的方法。getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。componentDidUpdate(): 在更新后会被立即调用。
卸载时期:
componentWillUnmount(): 在组件卸载及销毁之前直接调用。
显示时钟
class Tick extends React.Component{
constructor(props){
super(props);
this.state={date:new Date()}
}
componentDidMount(){
this.timerID=setInterval(
()=>this.tick(),100
)
}
componentWillUnmount(){
clearInterval(this.timeID);
}
tick(){
this.setState(
{
date:new Date()
}
)
}
render(){
return(
<h2>现在的时间是:{this.state.date.toLocaleTimeString()}</h2>
)
}
}
ReactDOM.render(<Tick/>,document.querySelector('#test'));
实现呼吸文字
class Hello extends React.Component{
constructor(props){
super(props);
this.state={opacity:1.0};
}
componentDidMount(){
this.timer=setInterval(function(){
let opacity=this.state.opacity;
opacity-=0.05;
if(opacity<0.1){
opacity=1.0
}
this.setState({
opacity:opacity
});
}.bind(this),100);
}
render(){
return(
<div style={{opacity:this.state.opacity}}> Hello {this.props.name}
</div>
)
}
}
ReactDOM.render(<Hello name="word"/>,document.querySelector('#test'))
React 表单实现
实现输入显示
class HelloMessage extends React.Component {
constructor(props){
super(props);
this.state={value:'welcome'};
this.bindChange=this.bindChange.bind(this);
}
bindChange(event){
this.setState({
value:event.target.value
})
}
render(){
return(
<div>
<input type="text" value={this.state.value} onChange={this.bindChange}/>
<h2>{this.state.value}</h2>
</div>
)
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('test')
);
select 下拉菜单
class Selector extends React.Component {
constructor(props){
super(props);
this.state={value:'',show:''};
this.bindChange=this.bindChange.bind(this);
this.bindSubmit=this.bindSubmit.bind(this);
}
bindSubmit(event){
event.preventDefault();
this.setState({
show: `your choice is ${this.state.value}`
})
}
bindChange(event){
this.setState({
value:event.target.value
})
}
render(){
return(
<div>
<form onSubmit={this.bindSubmit}>
<label>你喜欢的公司是</label>
<select value={this.state.value} onChange={this.bindChange}>
<option value="Google">Google</option>
<option value="Meta">Meta</option>
<option value="FaceBook">FaceBook</option>
</select>
<input type="submit" value="提交" />
</form>
<h3>{this.state.show}</h3>
</div>
)
}
}
ReactDOM.render(
<Selector />,
document.getElementById('test')
);
多个表单选项
输入是否进行点餐
class Selector extends React.Component {
constructor(props){
super(props);
this.state={
checked:false,
number:0,
message:''
}
this.bindChange=this.bindChange.bind(this);
this.bindSubmit=this.bindSubmit.bind(this);
}
bindChange(event){
const target=event.target;
const value=target.type==='checkbox' ? target.checked :target.value;
const name=target.name;
this.setState({
[name]:value
})
}
bindSubmit(event){
event.preventDefault();
this.setState(
{
message: this.state.checked?`${this.state.number}位顾客点餐成功`:`请您确定是否点餐`
}
)
}
render(){
return(
<div>
<h2>欢迎光临,你有几个人是否点菜?</h2>
<form onSubmit={this.bindSubmit}>
<label >人数:</label>
<input type="number" name="number" value={this.state.number} onChange={this.bindChange} min="1"/>
<br/>
<label >是否点餐:</label>
<input type="checkbox" name="checked" checked={this.state.checked} onChange={this.bindChange}/>
<br/>
<input type="submit" value="提交"/>
</form>
<h3>{this.state.message}</h3>
</div>
)
}
}
ReactDOM.render(
<Selector />,
document.getElementById('test')
);
父子组件的函数传递
父组件传递一个函数给子组件使用
class Content extends React.Component{
render(){
return(
<div>
<button onClick={this.props.handleClick}>点击</button>
<h2>{this.props.value}</h2>
</div>
)
}
}
class Hello extends React.Component {
constructor(props){
super(props);
this.state=(
{
value:'欢迎登录'
}
)
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
this.setState({
value:'登录成功'
})
}
render(){
return(
<div>
<Content handleClick={this.handleClick} value={this.state.value}>
</Content>
</div>
)
}
}
ReactDOM.render(
<Hello />,
document.getElementById('test')
);
react 的 refs
可以引用render 里面组件的一些属性和方法
class Hello extends React.Component{
constructor(props){
super(props);
}
handleClick(){
this.refs.myInput.focus();
}
render(){
return(
<div>
<input type="text" ref="myInput" />
<input type="button" value="点击我获取焦点" onClick={this.handleClick.bind(this)}/>
</div>
)
}
}
ReactDOM.render(
<Hello/>,document.getElementById('test')
);
React cli
React 脚手架的安装
安装react cli
打开cmd 输入 只需要输入一次
npm install -g create-react-app
在工程文件夹下cmd 输入
create-react-app xxxx
进入项目文件
cd xxx
启动项目
npm start
打包项目
npm run build
快速模拟部署打包项目
npm install -g serve
serve -s build
index.html 信息介绍
<head>
<meta charset="utf-8" />
<!-- 页签图标 -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 移动端布局 -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 移动端地址栏颜色,兼容不太好 -->
<meta name="theme-color" content="#000000" />
<!-- 网站描述 -->
<meta
name="description"
content="Web site created using create-react-app"
/>
<!-- apple 添加到主屏的图标 -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
应用夹克用于跨端,写前端页面,应用加壳的配置文件
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<!-- 不支持script提示 -->
<noscript>You need to enable JavaScript to run this app.</noscript>
页面的节点
<div id="root"></div>
-src
--index.js 入口文件
--App.js App组件
--reportWebVital记录页面性能
--APP.test.js 用于app组件的测试
--setipTest.js组件单元测试
--package.json 的说明文件
--package-lock 记录版本保持包的一致性
重新建立自己的项目
1.删除原来的public 和src
2.创建自己的src 和 public public 里面的关键 icon index.html 有关root 节点 src 里面的关键是 是App.js 以及index.js
3.index.js 包的引入 18版本以后写法如下
//引入reat 核心库
import React from 'react'
//引入ReactDom
import ReactDOM from 'react-dom/client';
// 引入App组件
import App from './App'
// 渲染App到页面
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
4.App.jsx组件的写法
import React from 'react'
export default class App extends React.Component{
render(){
return(
<div>
hello word!
</div>
)
}
}
5.建立一个component 文件夹,在文件建立建立子两组件 ,首字母大写
Welcome 组件和 Hello 组件
import React,{Component} from 'react'
export default class Welcome extends Component{
render(){
return(
<h2>weclome</h2>
)
}
}
import React,{Component} from 'react'
export default class Hello extends Component{
render(){
return(
<h2>hello</h2>
)
}
}
在app 里面使用模块化样式(可以将组件的样式scoped)局部颜色不会污染其他组件
-hello 文件夹
--index.jsx 组件
--index.module.css 模块化的样式
使用的时候如下,写成下面的形式
import React,{Component} from 'react'
import hello from './index.module.css'
export default class Hello extends Component{
render(){
return(
<h2 className={hello.title}>hello</h2>
)
}
}
多个案例学习的时候修改src名称 重新创建一个src文件夹
快捷生成组件
快速实现组件代码的生成:
1.在插件里面 找到 ES7 React/Redux/GraphQL/React-Native snippets 进行安装
2.直接在 jsx 文件里可以快捷创建基本的代码块
3.类组件rcc 函数组件rfc 其他快捷代码导入可见插件说明
实战案例Todolist
todolist案例的组件划分为Footer ,Header,Item,List
静态组件如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
/* 去除列表样式 */
ul,ol {
list-style: none;
}
/* a 标签去除下划线 */
a {
text-decoration: none;
}
/* 设置页面默认字体 */
body {
background-color: #ccc;
font-family: Arial, sans-serif;
}
.todo-container{
background-color: #fff;
width: 600px;
padding: 5px;
margin: 100px auto;
border-radius: 2px solid #ddd;
border-radius: 5px;
}
.todo-wrap{
background-color: #ddd;
padding: 15px;
border-radius: 2px solid #f40;
border-radius: 5px;
}
/* 头部的样式 */
.todo-header{
text-align: center;
}
.todo-header input{
margin:0 auto;
border-radius: 3px;
border:1px solid #ddd;
width:550px;
height: 20px;
padding: 5px;
font-size: 14px;
margin: 0 auto;
}
/* list和item */
.btn{
clear: both;
float: right;
color:#fff;
background-color: #da4f49;
border: 1px solid #bd362f;
border-radius: 3px;
}
.btn:hover{
background-color: #bd362f;
}
.todo-list{
width: 550px;
padding: 5px;
margin: 0 auto;
background: #fff;
border: 2px solid #fff;
}
.todo-item{
padding: 0 2px;
width: 550px;
height:20px;
font-size: 15px;
background-color: #fff;
border-radius: 2px;
border:1px solid #ddd;
margin: 2px auto;
}
/* Footer的样式 */
.todo-footer{
width: 560px;
margin-top:10px;
height: 25px;
background-color: #fff;
padding-left: 10px;
border: 1px solid #fff;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="todo-container">
<h2>欢迎使用todolist</h2>
<div class="todo-wrap">
<div class="todo-header">
<input type="text" placeholder="请你输入任务名称,按回车确定">
</div>
<ul class="todo-list">
<li class="todo-item" style="overflow: hidden;">
<label>
<input type="checkbox" />
<span>xxxx</span>
<button class="btn">删除</button>
</label>
</li>
<li class="todo-item" style="overflow: hidden;">
<label>
<input type="checkbox" />
<span>xxxx</span>
<button class="btn">删除</button>
</label>
</li>
</ul>
<div class="todo-footer">
<input type="checkbox">
<span>已完成2项/总共5项</span>
<button class="btn">清空所选</button>
</div>
</div>
</div>
</body>
</html>
实现基本的功能包括,回车输入,悬浮切换样式,勾选按键的使用改变原来的属性值
这里先复习一下父子组件的通讯 子组件传递数据给父组件使用 props 传递函数给子组件,父组件传递数据给子组件使用props就行
回车输入功能的实现
在拆分的Header 组件里面添加数据
1.读取输入的数据添加到数组todos里面 这里需要一个 先组装一个对象,传递个父组件 App.jsx
2.清空一下输入框
3.定义一个回调函数实现 这里使用onKeyUp事件来触发回调
Header.jsx
import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import './index.css'
export default class Header extends Component {
handleKeyUp=(event)=>{
const {keyCode,target}=event;
// 判断是否按下空格
if(keyCode!==13) return;
// 输入不能为空
if(target.value.trim()===''){
alert('输入不能为空');
return;
}
// 使用nanoid 进行唯一标记id
const newObj={id:nanoid(),name:target.value,done:false};
target.value='';
this.props.getInput(newObj);
}
render() {
return (
<div className="todo-header">
<input type="text" placeholder="请你输入任务名称,按回车确定" onKeyUp={this.handleKeyUp}/>
</div>
)
}
}
任务的组件功能实现
List.jsx 组件 主要用于传递todo数据 和一些 item 用到的函数
import React, { Component } from 'react'
import Item from '../Item/index'
import './index.css'
export default class List extends Component {
render() {
const todos=this.props.todos;
const handleupdate=this.props.handleupdate;
const handleDelete =this.props.handleDelete;
return (
<ul className="todo-list" style={{display:todos.length>0?'block':'none'}}>
{
todos.map((todo)=>{
return <Item key={todo.id} {...todo} handleupdate={handleupdate} handleDelete={handleDelete} />
})
}
</ul>
)
}
}
item.jsx 用来实现 删除,勾选等功能
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
// constructor(props){
// super(props);
// this.
// }
state={
mouse:false
}
handleClear(id){
return()=>{
if(window.confirm("你确定要删除吗?")){
this.props.handleDelete(id);
}
}
}
// 勾选按键
handleChecked(id){
return(event)=>{
const target=event.target;
// 获取按键的状态和id便于修改
this.props.handleupdate(id,target.checked);
}
}
// 鼠标移入移出,通过flag来改变样式
handleEnderAndLeave(flag){
return()=>{
this.setState({
mouse:flag
})
}
}
render() {
const{id,name,done}=this.props;
const {mouse}=this.state;
return (
<li className="todo-item" onMouseEnter={this.handleEnderAndLeave(true)}
onMouseLeave={this.handleEnderAndLeave(false)}
style={{backgroundColor:mouse?'#ddd':'#fff'}}>
<label >
<input type="checkbox" checked={done} onChange={this.handleChecked(id)}/>
<span> {name}</span>
<button className="btn" style={{display:mouse?'block':'none'}} onClick={this.handleClear(id)}>删除</button>
</label>
</li>
)
}
}
Footer 组件的勾选与全选
Footer.jsx
import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
handleClearAll=()=>{
const todos= this.props.todos;
if(todos.length!==0){
this.props.handleDeleteAll();
}
else{
alert('已经没有任务了')
}
}
handleAllChange=(event)=>{
this.props.handleAllCheck(event.target.checked);
}
render() {
const todos= this.props.todos;
// 统计所有的项目数量
const total=todos.length;
// 统计勾选的项目
const completed=todos.reduce((pre,todo)=> {return pre+(todo.done===true?1:0)},0);
return (
<div className="todo-footer">
<input type="checkbox" checked={(completed===total&&total!==0)?true:false} onChange={this.handleAllChange}/>
<span> 已完成{completed}项/总共{total}项</span>
<button className='btn btnclearall' onClick={this.handleClearAll}>清空已完成任务</button>
</div>
)
}
}
App.jsx
import React from 'react'
import './App.css';
// import Footer from './components/Footer';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
// import List from './components/List';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [{ id: '001', name: '吃饭', done: true },
{ id: '002', name: '睡觉', done: true },
{ id: '003', name: '写代码', done: true }]
}
this.getInput = this.getInput.bind(this);
this.handleupdate = this.handleupdate.bind(this);
}
// 全部清除完成的项目
handleDeleteAll=()=>{
const todos = this.state.todos;
const newtodos = todos.filter((todo) => {
return todo.done !== true;
})
this.setState({
todos: newtodos
});
}
//全部进行勾选切换
handleAllCheck=(newdone)=>{
const todos = this.state.todos;
const newtodos=todos.map((todo)=>{
return {...todo,done:newdone}
})
this.setState({
todos:newtodos
})
}
// 删除某一项
handleDelete=(id)=>{
const todos = this.state.todos;
const newtodos = todos.filter((todo) => {
return todo.id !== id;
})
this.setState({
todos: newtodos
});
}
//更新勾选
handleupdate(id, done) {
const { todos } = this.state;
const newtodos = todos.map((todo) => {
if (todo.id === id) {
return { ...todo, done }
}
else return todo;
})
this.setState({ todos: newtodos });
}
// 更新输入框输入
getInput(todoObj) {
const { todos } = this.state;
const newTodos = [todoObj, ...todos];
this.setState({ todos: newTodos });
}
render() {
const { todos } = this.state;
return (
<div className="todo-container">
<h2>欢迎使用todolist</h2>
<div className="todo-wrap">
<Header getInput={this.getInput}/>
<List todos={todos} handleupdate={this.handleupdate} handleDelete={this.handleDelete}/>
<Footer todos={todos} handleAllCheck={this.handleAllCheck} handleDeleteAll={this.handleDeleteAll}/>
</div>
</div>
)
}
}
进行React的跨域解决方案
在package.json里面添加代理资源目标的地址例如下面所示
"proxy":"http://localhost:5000"
1.优点:配置简单,请求资源的时候,请求资源的时候可以不加任何前缀
2.缺点:不能配置多个代理
创建代理配置文件
在src下创建代理配置文件
src/setupProxy.js
编写setupProxy 配置
const proxy =require('http-proxy-middleware')
module.exports=function(app){
app.use(
proxy('/api',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{'^/api1:''}
}),
proxy('/api2',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{'^/api1:''}
})
)
}
优点:可以配置多个代理
缺点:配置繁琐请求资源时要加前缀
github 用户查询案例
小技巧:解构赋值, {a:{b:data}}=obj2 a,b必须是原来对象里面有的名字 b 已经改名为 data
静态页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
/* 去除列表样式 */
ul,ol {
list-style: none;
}
/* a 标签去除下划线 */
a {
text-decoration: none;
}
/* 设置页面默认字体 */
body {
background-color: #ccc;
font-family: Arial, sans-serif;
}
.container{
width: 1000px;
background-color: #fff;
margin: 200px auto;
padding: 50px 0;
}
/* 输入组件 */
.search-box{
width: 800px;
height: 150px;
background-color: #ddd;
margin: auto;
border-radius: 5px;
border: 2px solid #ddd;
}
.title{
font-size:40px;
color: yellow;
margin: 20px 20px;
text-align: center;
}
.write {
width: 300px;
height:30px ;
margin: 0 auto;
}
.write button{
font-size: 18px;
}
.write input{
height: 20px;
line-height: 20px;
font-size: 20px;
padding: 5px;
}
/* list 组件 */
.list-box{
width: 800px;
margin: 20px auto;
background-color: #fff;
border: 2px solid #ccc;
border-radius: 3px;
padding: 10px;
display: flex;
flex-wrap: wrap;
}
/* item组件 */
.item{
width: 155px;
height: 150px;
border: 1px solid #ddd;
border-radius: 5px;
margin: 1px;
text-align: center;
}
.item img{
width: 150px;
height: 100px;
}
.item a{
font-size: 20px;
color: pink;
}
</style>
</head>
<body>
<div class="container">
<div class="search-box">
<h2 class="title">欢迎使用github检索</h2>
<div class="write">
<input type="text" placeholder="请输入用户名" class="search-write">
<button>提交</button>
</div>
</div>
<div class="list-box">
<div class="item">
<img src="../public/img/banner.jpg" alt="">
<a class="title">user</a>
</div>
</div>
</div>
</body>
</html>
Search 组件
Search 组件 Search.jsx
import React, { Component } from 'react'
import axios from 'axios'
import './index.css'
export default class Search extends Component {
//按键进行请求
state={items:[]};
handleClick=()=>{
const{keyWordElements:{value}}=this;
const values=value.trim();
if( values===''){
alert('输入不能为空');
return;
}
//发送请求
axios.get(`https://api.github.com/search/users?q=${values}`).then(
response=>{this.setState({
items:response.data.items
})
this.props.handleUpdata(response.data.items)
},
error=>{console.log('失败了')}
)
}
//回车进行请求
handleKeyup=(event)=>{
const{keyCode,target}=event;
if(keyCode!==13) return;
const value=target.value.trim();
if(value===''){
alert('输入不能为空');
return;
}
// 发起请求
axios.get(`https://api.github.com/search/users?q=${value}`).then(
response=>{
this.setState({
items:response.data.items
})
this.props.handleUpdata(response.data.items)
},
error=>{console.log('失败了')}
)
}
render() {
return (
<div className="search-box">
<h2 className="title">欢迎使用github检索</h2>
<div className="write">
<input type="text" placeholder="请输入用户名" ref={c=>this.keyWordElements=c}className="search-write" onKeyUp={this.handleKeyup}/>
<button onClick={this.handleClick}>提交</button>
</div>
</div>
)
}
}
List 的组件
list组件的拆分为item 子组件
import React, { Component } from 'react'
import Item from '../Item'
import './index.css'
export default class List extends Component {
render() {
const items=this.props.items
return (
<div className="list-box" >
{/* 这里进行了一个判断放哪个组件 */}
{ items.length>0?items.map((item)=>{
return <Item key={item.id} avatar={item.avatar_url} home={item.html_url} name={item.login}/>
}):<h2>等待加载中...</h2>
}
</div>
)
}
}
Item 子组件
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
render() {
return (
<div className="item">
<img src={this.props.avatar} alt=""/>
<a className="title" href={this.props.home}>{this.props.name}</a>
</div>
)
}
}
App.jsx 组件
App.jsx
import React from 'react'
import Search from './components/Search'
import List from './components/List'
import './App.css'
export default class App extends React.Component{
state={items:[],isFistLoading:true}
handleUpdata=(data)=>{
this.setState({
items:data,
isFistLoading:false
})
}
render(){
return(
<div className="container">
<Search handleUpdata={this.handleUpdata}/>
{this.state.isFistLoading?<div className='welcome'><h2 >等待搜索。。。</h2></div>:<List items={this.state.items}/>}
</div>
)
}
}
React 路由
安装依赖
这里下载的是web专用的api ,有RN 的需要自己找,新版本看后面的 react拓展
npm i react-router-dom@5
在App.jsx 里面使用
import {Link,BrowserRouter,Route} from 'react-router-dom'
import About from ./component/About //引入组件
路由的关键属性:
props:
history:
go :f(n) 指定跳转
goBack:f() 后退
goForward: f() 前进
push: f() push 模式进行跳转
replace:f() 模式进行跳转
location:
pathname:'/about' 记录当前的路径名
search:'' 记录search 参数
state:undefined 记录当前路由组件的state对象
match:
params:{} 记录params 的数据
path:'/about' 当前匹配的路由
url:'/about'
路由组件的参数传递和函数式跳转就是根据上述属性实现的
声明式路由
1,使用 标签进行组件切换 Link相对于特殊的a标签,路由尽量不大写,BrowserRouter 就是history模式,Route 注册路由,HashRouter 是使用hash 模式的 router 用于显示路由组件的地方
2, Route ,Link 标签外面还要包裹一个,这里的Route 和Link 要在一个BrowerRouter 里面,因此在 index.js 里面引入BrowerRouter 之后再包裹一个整个 app
3,路由注册 添加一个标签
4,通用一些的组件标签放在components 文件夹里,路由组件放在page组件里面
5 使用升级版的 Link -> NavLink 可以设置激活active样式 需要在里面设置 activeClassName 属性进行设置
import{NavLink,Route} from 'react-router-dom'
<NavLink activeClassName="active" to="/about">about</NavLink>
如果 NavLink 的active 样式不显示,就要在样式设置里面添加 !import
NavLink的封装
在componets里面建立一个组件 MyNavLink ,通过 prop 传递 里面的文字已经路由位置 的样式进行修改
用{...this.props} 接受 属性设置,使用{this.props.children} 接受标签里面的内容 写闭标签的时候可以使用children进行处理 可以直接接受 {...this.props}
MyNavLink.jsx
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
return (
<NavLink activeClassName="active" {...this.props} >{this.props.children}</NavLink>
)
}
}
App.jsx
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/search">Search</MyNavLink>
Switch注册路由
Switch 进行路由匹配,如果匹配到了路由就不会继续向下进行匹配,因此进行设计组件的时候就只有一个路由对应一个组件,具体来说就是用Switch标签包裹所有的Route标签
import{Route,Switch} from 'react-router-dom'
<Switch>
<Route path='/search' component={Search}></Route>
<Route path='/about' component={About}></Route>
</Switch>
在Route 里面 添加 exact={true} 开启严格匹配 触发的路由和注册路由的路径完全一致才会触发,没添加的时候如果触发的路由是 注册路由的子集就可注册,有必要才会开启
<Route exact={true} path='/search' component={Search}></Route>
默认路由设置
用于设置默认的路由 ,都没匹配到就会去默认路由
import{Route,Switch,Redirect} from 'react-router-dom'
在Route 后面添加 Redirect 标签
<Redirect to="/home"/>
路由嵌套
嵌套的子路由 要在NavLink 中的 to里面添加上父级路由 比如 “/home/news” (模糊匹配的情况下)如果开启了严格匹配时要在命名一个更加具体的路由匹配
<div className="search">
<h2 className="searchcontent">search的内容</h2>
<ul class="search-nav">
<li class="message"><MyNavLink to='/search/message'>Message</MyNavLink></li>
<li class="topics"><MyNavLink to='/search/topics'>Topics</MyNavLink></li>
</ul>
<Switch>
<Route path='/search/message' component={Message}></Route>
<Route path='/search/topics' component={Topics}></Route>
<Redirect to='/search/message'></Redirect>
</Switch>
</div>
路由传递参数
在link 标签,NavLink to 路径中添加参数
params 参数的传递参数
<li class="message">
<Link to={`/search/topics/description/${item.id}/${item.title}`}>{item.title}</Link>
</li>
//在route 里面进行接收
<Route path="/search/message/:id/:title" component={Detail}></Route>
// 得到的参数在 Detail 组件里面 this.props.match.params
const {id,title} =this.props.match.params
search 参数的接收
<li class="message">
<Link to={`/search/topics/description/?id=${item.id}&title=${item.title}`}>{item.title}</Link>
</li>
// 在Route 里面进行接收
<Route path="/search/topics" component={Detail}/>
// 在Detail 组件里面
// 引入query解析库,cli 自带的
npm install --save querystring-es3
import qs from 'querystring-es3'
const{search}=this.props.location
const {id,title}=qs.parse(search.slice(1))
state 参数传递
state 的参数传递是传递对象实现的
<li class="message">
<Link to={{ pathname:'/search/topics/description',state:{id:item.id,title:item.title}}}>{item.title}</Link>
</li>
// 在Route 里面进行接收的时候
<Route path="/search/topics" component={Detail}/>
// 接收 state 参数,防止清空缓存的时候出现报错
const {id,title}=this.props.loaction.state||{}
路由的replace 模式 和push 模式
repalce 模式 不会有进栈出栈的操作(没有回退),直接替代上一个路由,在link 里面添加replace 开启replace 默认的是 push模式
函数式路由
函数式声明的时候就是在回调函数里面调用 push 和replace方法 ,就是声明的格式变化了,注册和接收参数的方式没变和上面介绍的一致。
params 的传递方法
pushparams=(id,title)=>{
this.props.history.push(`/search/topics/description/${id}/${title}`)
}
replaceparams=(id,title)=>{
this.props.history.replace(`/search/topics/description/${id}/${title}`)
}
<button onClick={()=>this.pushparams(item.id,item.title)}>push 方法</button>
<button onClick={()=>this.replaceparams(item.id,item.title)}>replace方法</button>
search 的传递方法
pushsearch=(id,title)=>{
this.props.history.push(`/search/topics/description/?id=${id}&title=${title}`)
}
replacesearch=(id,title)=>{
this.props.history.replace(`/search/topics/description/?id=${id}&title=${title}`)
}
<button onClick={()=>this.pushsearch(item.id,item.title)}>push 方法</button>
<button onClick={()=>this.replacesearch(item.id,item.title)}>replace方法</button>
state 的传递方法
// 回调函数
pushstate=(id,title)=>{
this.props.history.push('/search/topics/description',{id:id,title:title})
}
replacestate=(id,title)=>{
this.props.history.replace('/search/topics/description',{id:id,title:title})
}
<button onClick={()=>this.pushstate(item.id,item.title)}>push 方法</button>
<button onClick={()=>this.replacestate(item.id,item.title)}>replace方法</button>
withRouter
如果一些非路由组件想要使用路由方法就需要使用 withRouter
import {withRouter} from 'react-router-dom'
// 通常类组件的暴露进行修改,改为 export default withRouter(类名)
class Header extends Component{
...
}
export default withRouter(Header)
路由实践案例
src
-components
--Header
---index.css
---index.jsx
-MyNavLink
--MyNavLink.jsx
-pages
--About
---index.jsx
---index.css
---Detail
----index.jsx
----index.css
---More
----index.jsx
--Search
---index.jsx
---index.css
---Message
----index.jsx
---Topics
----index.jsx
-----Message
------index.jsx
-App.jsx
-App.css
-index.js
React 组件库
国外使用material-ui 国内 ant-design ,arco
快速上手 antd
readux 的学习
1.学习文档
Redux 中文官网 - JavaScript 应用的状态容器,提供可预测的状态管理。 | Redux 中文官网
用途:用于管理公共数据,进行状态管理,类似于vuex
redux 核心概念
action 是action数据对象 ,React component 是React 对象
store(类似服务员 vue中的mutation) Reducer(类似厨师)处理数据对象返回结果对象进行处理
previousState 上一个状态,actioin 传递的数据对象
计数器 ,登录记录 案例--快速入门redux
静态实现的组件counter
Counter.jsx
import React from 'react'
export default class App extends React.Component{
state={
count:0
}
//添加设置数
increament=()=>{
const {keyvalue:{value}}=this;
const nums=value.trim();
this.setState({
count:this.state.count+nums*1
})
}
//减少设置数
decreament=()=>{
const {keyvalue:{value}}=this;
const nums=value.trim();
this.setState({
count:this.state.count>=nums?this.state.count-nums*1:0
})
}
//奇数的时候才可以减
decreamentOdd=()=>{
const {keyvalue:{value}}=this;
const nums=value.trim();
if(this.state.count%2!==0){
this.setState({
count:this.state.count>=nums?this.state.count-nums*1:0
})
}
}
// 进行异步减法
decreamentAsnc=()=>{
const {keyvalue:{value}}=this;
const nums=value.trim();
setTimeout(()=>{ this.setState({
count:this.state.count>=nums?this.state.count-nums*1:0
})},1000)
}
render(){
const {count}=this.state
return(
<div>
<h2>当前的数量是:{count} </h2>
<input type='number' min='0' defaultValue='0' ref={c=>this.keyvalue=c}/>
<button onClick={this.increament}>增加</button>
<button onClick={this.decreament}>减少</button>
<button onClick={this.decreamentOdd}>只有奇数可以减少</button>
<button onClick={this.decreamentAsnc}>只有异步可以减少</button>
</div>
)
}
}
redux 的快速入门
由于 原来的 有一个 createStore 已经废掉了因此使用最新的写法,用store 集中管理 reducer,store 通过 react-redux绑定到index.js
首先 导入依赖包
// toolkit 可以减少原来比较繁琐的
npm install @reduxjs/toolkit
// 下载redux的核心依赖包
npm install redux
// 下载 react-redux 用于连接的包
npm install react-redux
// 下载 redux开发工具,有必要再下
npm install --save-dev redux-devtools
建立一个redux 文件夹 建立 store_reducer.js 和store.js(处理具体的state变化)
store_reducer.js
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
// 初始状态
initialState: {
value: 0,
persons:[]
},
reducers: {
increment: (state, action) => {
state.value += action.payload;
},
decrement: (state, action) => {
state.value = state.value >action.payload
?state.value-action.payload
:0;
},
// 更新登录信息
addPerson: (state,action)=>{
state.persons=[action.payload,...state.persons]
}
}
})
// 每个 case reducer 函数会生成对应的 Action creators
export const { increment, decrement,addPerson} = counterSlice.actions
export default counterSlice.reducer
store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './store_reducer'
export default configureStore({
reducer: {
counter: counterReducer
}
})
index.js
//引入reat 核心库
import React from 'react'
//引入ReactDom
import ReactDOM from 'react-dom/client';
// 引入App组件
import App from './App'
// 渲染App到页面
import store from './redux/store';
// 应入providing包裹
import {Provider} from 'react-redux'
// 把store 放在 App 里面监听渲染,store 变化之后进行渲染
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
Counter.jsx 组件进行微调
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement } from '../redux/store_reducer'
export default function Counter() {
const dispatch = useDispatch();
const count = useSelector(state => state.counter.value);
const persons=useSelector(state => state.counter.persons);
const [incrementAmount, setIncrementAmount] = useState(1);
// 输入变化
const handleIncrementAmountChange = e => {
setIncrementAmount(Number(e.target.value));
};
const handleIncrement = () => {
dispatch(increment(incrementAmount));
};
const handleDecrement = () => {
dispatch(decrement(incrementAmount));
};
const handleDecrementOdd=()=>{
if(count%2!==0){
dispatch(decrement(incrementAmount));
}
}
const handleDecrementAsync=()=>{
setTimeout(()=>{
dispatch(decrement(incrementAmount));
},1000)
}
return (
<div>
<h2>当前的数量是:{count}</h2>
<h2> 最近的用户是:{persons.length>0?persons[0].name:'无用户'}</h2>
<input type="number" value={incrementAmount} onChange={handleIncrementAmountChange} min='1' />
<button onClick={handleIncrement}>增加</button>
<button onClick={ handleDecrement}>减少</button>
<button onClick={handleDecrementOdd}>奇数可减</button>
<button onClick={handleDecrementAsync}>异步减</button>
</div>
)
}
Person.jsx
import { useSelector, useDispatch } from 'react-redux'
import { addPerson } from '../redux/store_reducer'
import React, { useState } from 'react';
import { nanoid } from 'nanoid';
export default function Person() {
const dispatch = useDispatch();
const count = useSelector(state => state.counter.value);
const persons=useSelector(state => state.counter.persons);
const [formValues, setFormValues] = useState({
id: nanoid(),
name: '',
email: '',
password: '',
});
function handleInputChange(event) {
const { name, value } = event.target;
setFormValues(prevValues => ({ ...prevValues, [name]: value }));
}
function handleSubmit(event) {
event.preventDefault();
dispatch(addPerson(formValues));
setFormValues({
id: nanoid(),
name: '',
email: '',
password: '',
});
}
return (
<div>
<h2>这里是Person组件,counter的总和是{count}</h2>
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" name="name" value={formValues.name} onChange={handleInputChange} />
</label>
<br />
<label>
Email:
<input type="email" name="email" value={formValues.email} onChange={handleInputChange} />
</label>
<br />
<label>
Password:
<input type="password" name="password" value={formValues.password} onChange={handleInputChange} />
</label>
<br />
<button type="submit">Submit</button>
</form>
<hr />
<h3>登录记录</h3>
{
persons.length>0
?(<ul>
{persons.map((item,index)=>{
return(<li key={index}>{item.id}--{item.name}--{item.email}</li>)
})}
</ul>)
:(<h3>暂时没有记录</h3>)
}
</div>
);
}
App.jsx 进行引用就行了
import React from 'react'
import Counter from './components/Counter'
export default class App extends React.Component{
render(){
return(
<div>
hello
<Counter></Counter>
</div>
)
}
}
综述就可实现用redux 管理状态
react 拓展
react 组件的懒加载
这里以上面的路由案例为基础继续
1.先引入对应的lazy 和Suspense
import React, { Suspense, lazy } from 'react'
2.不同与以往的路由引入 写在引入的最下面
const About=lazy(()=>import ('./pages/About'))
// import About from './pages/About'
const Search=lazy(()=>import ('./pages/Search'))
3.在路由注册的时候添加 Suspense 标签包裹 ,fallback 里面为默认的组件(可以写一个自己建立的默认组件使用普通演示引入 fallcall 里面写上对应的标签引入 ),当网速比较差的时候加载 fallback组件
<Suspense fallback={<h1>Loading...</h1>}>
<Switch>
<Route path='/search' component={Search}></Route>
<Route path='/about' component={About}></Route>
<Redirect to='/search'></Redirect>
</Switch>
</Suspense>
基础Hooks
使用函数式组件和 提供的API hook 函数实现常用的功能,比如setState等
常用的hook
state: React.useState()
effect: React.useEffect()
ref: React.useRef()
useState的使用
state会进行内部保存,不会因为是函数式组件而被刷新
// 单个值的修改
// 从 useState 里面调用 useState
import React, { useState } from 'react'
// 设置初始值写在函数组件的上面部分
const [count,setCount] = useState(0);
// 函数里面直接改值
setCount(count+1)
// 回调返回的形式
setCount((count)=>{return count+1})
// 对象的修改
const [formValues, setFormValues] = useState({
id: nanoid(),
name: '',
email: '',
password: '',
});
// 在函数里进行多个对象值的修改
setFormValues({
id: nanoid(),
name: '',
email: '',
password: '',
});
useEffect
在函数组件里面不能直接使用生命周期钩子, useEffect 模拟生命周期 可以用来处理 初始会触发回调,变量更新也会触发回调 类似DidMount DidUpdate Unmount 的组合
import React, { useState,useEffect } from 'react'
// 在上半部分规定,进行监测state变量的变化,并且进行回调
useEffect(()=>{
...
},[])
// 第二个变量不规定值就会默认监听所有State可以添加要监听的值
useEffect(()=>{
...
},[count])
// 要实现一个unMount的效果就需要再返回一个回调这个返回的回调就是unmount 里面的
useEffect(()=>{
return ()=>{
...
}
},[count])
useRef
建立ref,并且获得里面的属性和成员方法
useRef 是创建一个ref实例 并且绑定在对应的元素上
import React, { useState,useEffect,useRef} from 'react'
// 在上面的部分规定 ref实例
const myRef= React.useRef()
// 在函数里面调用
console.log(myRef.current.vlaue)
// 与实际标签进行绑定
<input type="text" ref={myRef}>
Fragment
用于代替div 包裹最外部 减少div的引入,自动丢弃 Fragement 只能接收一个 key 参与遍历,用空的标签也可以实现该功能,但是需要更多的功能就不推荐了
import React,{Fragment} from 'react'
render(){
return(
<Fragment>
<Demo/>
</Fragment>
)
}
context 组件间的通讯
context 通讯一般用于 祖孙,代与代之间的通讯(是跨代的通讯)
// 创建 Context容器对象放在父子组件都可以用到的地方
const countContext =React.createContext()
// 渲染子组件的时候进行在外侧再包裹一层标签,数据也可以是一个对象
<countContext.Provider value={数据}>
<子组件/>
</countContext.Provider >
// 子组件,孙子组件都可以拿到value 值
// 类组件接收
// 后代声明接收context
static contextType=countContext
// 使用传递过来的数据
cosole.log(this.context)
// 类组件和函数组件接收方式
<h4>
<countContext.consumer>
{
value =>{
return `${value}`
}
}
</countContext.consumer>
</h4>
一般用 useContext
组件优化
1.只要执行setState 就会重新进行render
2.父组件重新render的时候,子组件也会重新渲染
3.合理的写法是只要setState 真正修改了值才会进行重新渲染
原因在于类组件中 shouldComponentUpdate 钩子总是返回 true ,应设置对比之前的和后面的状态return
只需要进行修改 继承的component就可解决上面的问题
// 类组件声明的时候
import React,{PureComponent} from 'react'
export default class Parent extends PureComponent{
}
// 这种方法seetState传入的对象 对象如果没有变就无法更新
// 在这种方法下最好不要进行 push,unshift 操作原来的对象 而是使用解构赋值 stu:[xxx,...stu]
组件的renderPropose(react 插槽)
// 在父组件中嵌套子组件
<A render={(data)=><B data={data}/>}>
//在父组件中的插入子组件的位置 data 可以放需要传递给的props参数
{this.props.render(data)}
React Router 6
推荐资料
Feature Overview v6.11.1 | React Router
React Router v6 完全指南 - 掘金 (juejin.cn)
引入关键依赖包
$ npm install react-router-dom
# or, for a React Native app
$ npm install react-router-native
Switch
Routes 和原来的 Switch 的作用一样 必须使用不然报错
Route 标签里面的 component ={About} 的属性 变为了 element={}
重定向的写法
抛弃 Redirect 引入了 Navigate标签
在 里面添加 <Route path='/' element={<Navigate to="/about”/>}> 重定向到 about 也可以使用replace
<Navigate to="/about”/> 一旦渲染就会跳转
// 判断某一个数是否进行重定向
<h3></h3>
{sum===1 ?<h4>sum的值为{sum}</h4>h4>:<Navigate to="/about" replace={true}>}
自定义类名不能用active了
function computedClassName({isActive}){
return isActive ? '类名1': '类名2'
}
<NavLink class={computedClassName}></NavLink>
路由表 useRoutes
新添加的 路由写法 路由表 useRoutes,同过先定义好的变量进行路由注册
import {useRoutes} from 'react-router-dom'
// 在函数组件里面定义路由表
const elements =useRoutes([
{
path:'/about',
element:<About/>
},
{
path:'/home',
element:<Home/>
},
{
path:'/',
element:<Navigate to='/about'/>
}
])
// 在注册路由的地方,直接进行如下定义就行
{elments}
// 实际使用中一般在一个文件Routes.js中直接 定义好所有的路由规则
export default [
{
path:'/about',
element:<About/>
},
{
path:'/home',
element:<Home/>
},
{
path:'/',
element:<Navigate to='/about'>
}
]
路由嵌套
在父组件中的定义子组件
// 声明组件的时候不要斜杠了
<NavLink to="news"></NavLink>
//在指定的路由组件位置
<Outlet/>
路由注册表的写法发生了变化
export default [
{
path:'/about',
element:<About/>,
children:[{
path:'news'
element:<News/>
}]
},
{
path:'/home',
element:<Home/>
},
{
path:'/',
element:<Navigate to='/about'>
}
]
组件传参
params 传递 ,传参和接收和原来5一致
import {useParams,useMatch} from 'react-router-dom'
const {xx,xxx,xxxx}=useParams() 接收传递的参数
const x =useMatch('/home/message/detail/:id/:title/:content') //可以写成完整的匹配项,得到一个对象
search 参数在获取的时候如下所示
import React from 'react'
import {useSearchParams,useLocation} from 'react-router-dom'
const [search,setSearch] =useSearchParams();
search.log(search.get('id')) //直接获得里面的search id 属性
SetSearch('id=008&title=gg&content=123') //用于修改更新参数
const x =useLocation() // 获取location 对象从location里面拿search
state 参数的获取
import {useLocation} from 'react-router-dom'
const {state:{id,title,content}}=useLocation()
前进后退
import {useNavigate} from 'react-router-dom'
const navigate =useNavigate()
// 后退
navigate(-1)
// 前进
navigate(1)