React 新手村秘籍

100 阅读4分钟

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>&nbsp;&nbsp;已完成{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

组件总览 - Ant Design

快速上手 antd

readux 的学习

1.学习文档

Redux 中文官网 - JavaScript 应用的状态容器,提供可预测的状态管理。 | Redux 中文官网

用途:用于管理公共数据,进行状态管理,类似于vuex

redux 核心概念

reudx 原理

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}/>
                &nbsp;
                <button onClick={this.increament}>增加</button> &nbsp;
                <button onClick={this.decreament}>减少</button> &nbsp;
                <button onClick={this.decreamentOdd}>只有奇数可以减少</button>&nbsp;
                <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.actionsexport 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' />
            &nbsp;
            <button onClick={handleIncrement}>增加</button> &nbsp;
            <button onClick={ handleDecrement}>减少</button> &nbsp;
             <button onClick={handleDecrementOdd}>奇数可减</button>&nbsp;
             <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)