react

文章目录

react引入和基本使用

  1. 通过标签引入react
    基础的语法的学习和联系
  2. 通过脚手架的
  3. 特点:mvc框架
    - v>c>m>v
    - c就相当于mvvm的vm

通过标签引入react.js和react-dom.js

<!-- react.js:  react的核心基础语法 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- react-dom.js:  react虚拟dom的代码 -->
<!-- 这个js必须在react.js下面,其他的script标签随便 -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

JSX语法、使用JSX

JSX就是一个 JavaScript 的语法扩展

引入JSX
<div id="box"></div>


<!-- babel.js:  解析浏览器不用的js代码,在这里就是为了解析JSX -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<!-- 加type就是为了告诉babel这个script标签需要解释 -->
<script type="text/babel">
//react的代码
    //获取dom节点
    const oBox = document.querySelector('#box');
    //准备一个内容
    const a = '<div>hello word</div>'
    //将内容渲染到容器当中
    //ReactDOM是react-dom中的一个对象,就是react虚拟dom对象
    //ReactDOM.render(把谁, 渲染到虚拟dom里);
    ReactDOM.render(e(LikeButton), oBox);
</script>
JSX使用
  1. 多个元素时,必须有一个根组件
const a=<div> <p>hello world</p> <span>11111</span></div>
  1. 缩进不会影响,也可以在最外层加一个括号,方便书写时区分
const a=(<div> <p>hello world</p> <span>11111</span></div>)
  1. 使用单表签必须闭合:<img src='' alt='' />
  2. 在JSX的标签内部放变量,使用{}包裹起来。只有字符串和数字可以正常展示,其他数据类型的数据都不能正常展示
    - 数组、布尔要正常展示就要:.toString
    - json格式的数据要展示:JSON.stringify(json)
  3. 在JSX的标签属性里放变量,也是使用{},这里需要注意,如果变量原本就有引号,属性值里面就不用引号了
let src='https://www.baidu.com/img/flexible/logo/pc/result.png'
const a=<img src={src} />
  1. 给标签添加class,在直接使用的时候会出现警告——建议变成className。也就是把class替换成className
  2. 添加style,直接按照原本方式添加会报错——写成:style={{}}
    - style的内容不能时字符串,要是json对象,所以内层的{}是规定数据格式
    - 外部的{}是用来放变量的
//分开写
const s={width:"100px",height="100px"}
const a=<div style={s}></div>
//或者合起来
const a=<div style={{width:"100px",height="100px"}}></div>
  1. 添加事件,
    - 以前onclick变成onClick,也就是使用驼峰命名法。
    - 函数名使用{}包裹。
    - 不能直接在时间后面写js语句
let show=function(){alert(1)}
const a=<div onClick={show}></div>

组件化

函数式组件(UI组件)

写法:

//写成函数,返回要的内容
function Com(){
	return <div style={{width:"100px",height="100px"}}></div>
}
//也可以写成
const Com =()=>(<div style={{width:"100px",height="100px"}}></div>)
 ReactDOM.render(<Com></Com>, oBox);

注意:函数名必须首字母大写,使用的时候可以使用单表签

class(类)组件

所有class类组件都必须继承React.Component
写法:

class Com extends React.Component{
	constructor(){
		super()
	}
	render(){
			return <div style={{width:"100px",height="100px"}}></div>
	}
}

使用方法与函数式组件一样的,类名首字母大字,使用的时候可以使用单表签

两者区别

  1. 一般的函数组件没有生命周期,没有状态,有属性,this指向可能会出问题,一般用箭头函数
  2. class组件有生命周期,有状态,有属性,this指向不会出问题
  3. 加上hook的函数式组件就三个都有了

组件的复用

函数式组件实现复用

let Com= function(props){
	//这个地方的props就是所有的属性的一个json集合
	return <div title={props.tit} style={{ color:props.col}}>11111111<div>
}
//简写,一般用这个
let Com=props=>(
	 <div 
	 	title={props.tit} 
		 style={{ color:props.col}}>
		 11111111
	 <div>
)
 ReactDOM.render(<Com tit='标题' col='red'></Com>, oBox);

class组件的复用

class Com extends React.Component{
	render(){
	//这里的this里面有一个props的属性,里面是传过来的参数
		return (
			 <div 
			 	title={this.props.tit} 
				 style={{ color:this.props.col}}>
				 11111111
			 <div>
		)
	}
}
 ReactDOM.render(<Com tit='标题' col='red'></Com>, oBox);

注意:属性在react里面是只读的,不能进行修改
比如在class组件复用时,this.props.tit="bbb",是不合法的

状态(state)

类似vue里面data里的数据

基础函数式组件没有状态

状态:

class Com extends React.Component{
	constructor(){
		super()
		//状态可以看作类里面的数据
		this.state={
			a:1,
			b:'hello world'
		}
	}
	render(){
		return (
			<div>{this.state.a}--{this.state.b}</div>
		)
	}
}

修改状态

使用
this.setState({
a:‘10’
})

 class Com extends React.Component {
        constructor() {
            super()
            this.state = {
                a: "aaaa",
                b: "bbbb",
                isShow: true,
                isShow1: true
            }
        }
        //第一种:function=()=>{}
        //这里使用箭头函数的作用是让this指向这个类,而不是函数内部
        //如果直接使用函数的话,由于函数内部形成了一个域,this指向就会改变,那么this.setState({})就会报错
        fnClick1 = () => {
            this.setState({
                isShow1: !this.state.isShow1
            })
        }
		
		//第二种:function(){}:函数内不会用到class的this

		//第三种:使用bind()改变函数this指向:function(){}
        //这里没有使用箭头函数是因为,在事件绑定时,使用bind指定了该函数的this指向为class的this
        //改变this指向的三个函数
        //call()直接调用了、apply()直接调用了、bind()不会立即调用
        fnClick() {
            this.setState({
                isShow: !this.state.isShow
            })
        }
        //第四种:直接在事件里写箭头函数
        render() {
            return (
                <div>
                	<button onClick={() => {
                        this.setState({
                            isShow2: !this.state.isShow2
                        })
                    }}>{this.state.isShow2 ? '隐藏' : '显示'}</button>
                    <p style={{ display: this.state.isShow2 ? 'block' : 'none' }}>{this.state.c}</p>
                    
                    <button onClick={this.fnClick1}>{this.state.isShow1 ? '隐藏' : '显示'}</button>
                    <p style={{ display: this.state.isShow1 ? 'block' : 'none' }}>{this.state.a}</p>
                    
                    <button onClick={this.fnClick.bind(this)}>{this.state.isShow ? '隐藏' : '显示'}</button>
                    <p style={{ display: this.state.isShow ? 'block' : 'none' }}>{this.state.b}</p>
                </div>
            )
        }
    }
    ReactDOM.render(<Com />, oBox)

注意:
如果不通过this.setState({})去操作数据,是不能实现数据改变页面的数据也改变的,比如用一个一般变量放在页面,然后点击改变了这个变量,变量虽然改变了,但是页面上面的内容不会改变

想让页面更新的方法:

  1. ReactDOM.render()
  2. class组件里面的 this.setState({})

异步更新

原因:先打印再执行的count+1的操作

render() {
        return (
            <div>
                <p>{this.state.count}</p>
                <button onClick={this.addCount}>+1</button>
            </div>
        )
    }
 addCount = () =>{
		//这里会异步处理
      this.setState({
          count:this.state.count+1
      })
		//由于异步拿不到最新的值
      console.log(this.state.count)
  }

改为同步更新

使用回调函数

addCount = () =>{
    this.setState({
         count:this.state.count+1
     },()=>{
     //setState的回调函数
         console.log(this.state.count)
     })  
 }

使用setTimeOut

addCount = () =>{
 setTimeout(() => {
     this.setState({
         count:this.state.count+1
     })  
     //这里是同步的
     console.log(this.state.count);
 }, 0);
}

连续操作可能被合并

执行一次函数:count+1

 addCount = () =>{
     this.setState({
     //count:0+1
         count:this.state.count+1
     })  
     this.setState({
     //count:0+1
         count:this.state.count+1
     })  
      this.setState({
     //count:0+1
         count:this.state.count+1
     })  
 }

避免合并

执行一次函数:count+3

    addCount = () =>{
       this.setState((pre,props)=>{
           return {
               count:pre.count+1
           }
       }) 
       this.setState((pre,props)=>{
           return {
               count:pre.count+1
           }
       }) 
       this.setState((pre,props)=>{
           return {
               count:pre.count+1
           }
       }) 
   }

class组件内部事件绑定方法

第一种(this指向class):
function=()=>{}
onClick={this.fnClick1}

第二种(函数内不会用到class的this):function(){}
onClick={this.fnClick1}

第三种(使用bind()改变函数this指向,this指向class):function(){}

onClick={this.fnClick.bind(this)}

第四种(this指向class):

onClick={() => {
	   this.setState({
	        isShow2: !this.state.isShow2
	    })
}}

虚拟Dom的好处

生命周期

componentDidMount()
componentWillUnmount()
componentDidUpdate()

事件

事件对象
这里面的事件对象不是原生的事件对象,是react重新封装的,如果遇到了传参,事件对象在参数的后面

fnClick(a,ev) {
//a是形参,ev是事件对象
    console.log(a,ev)
}
fKeyUp(b,ev) {
//b是形参,ev是事件对象
    console.log(b,ev)
}

<input type="text" 
onKeyUp={this.fKeyUp("aaaa").bind(this)}
onInput={this.fnClick("bbbb").bind(this)} />

条件渲染

在返回内容的时候,可以根据条件,进行对应的返回
函数式组件:

 const Com = props => {
    if (props.sel) {
         return <div>内容2</div>
     } else {
         return <div>内容1</div>
     }
 }

列表渲染

通常有两种方法:

class Demo extends React.Component {
        constructor() {
            super()
            this.state = {
                arr: ["111", "222", "333", "444"]
            }
        }

  render() {
       let data = this.state.arr.map((v, i) => <li key={i}>{v}</li>)
       return (
           <div>
               <ul>
                   <li>方法一</li>
                   {data}
                   <li>方法二</li>
                   {this.state.arr.map((v, i) => <li key={i}>{v}</li>)}
               </ul>
           </div>
       )
   }
}

表单

表单元素的数据绑定

 class Demo extends React.Component {
        constructor() {
            super()
            this.state = {
                user: "",
                pass: "",
                msg: "",
                sel: "",
                sex: "",
                hobby: []
            }
        }
        inputChange = (ev) => {
            //设置state里的值为form表单里元素被选择的值
			
			//这里的判断是为了区分多选框和其他的元素
            if (ev.target.type == 'checkbox') {
                let hobby = this.state.hobby
                if (ev.target.checked) {
                    hobby.push(ev.target.value)
                } else {
                    hobby.splice(hobby.indexOf(ev.target.value), 1)
                }
                this.setState({
                    [ev.target.name]: hobby
                })
            } else {
                this.setState({
                    [ev.target.name]: ev.target.value
                })
            }
        }
        submit(ev) {
            ev.preventDefault()
            console.log(this.state)
        }
        render() {
            return (
                <div>
                    <form action="" onSubmit={this.submit.bind(this)}>
                    	//非受控元素
                        <input onChange={this.inputChange} type="text" name="user" />
                        <input onChange={this.inputChange} type="text" name="user" defaultValue="1" />
                        //受控元素
                        <input onChange={this.inputChange} type="text" value={this.state.user} name="user" />
                        

                        <input onChange={this.inputChange} type="text" name="pass" />

                        <textarea name="msg" onChange={this.inputChange}></textarea>

                        <select name="sel" onChange={this.inputChange}>
                            <option value="css">css</option>
                            <option value="html">html</option>
                            <option value="js">js</option>
                            <option value="vue">vue</option>
                        </select><input onChange={this.inputChange} type="radio" name="sex" value="man" /><input onChange={this.inputChange} type="radio" name="sex" value="woman" />

                        篮球<input type="checkbox" onChange={this.inputChange} name="hobby" value='篮球' />
                        排球<input type="checkbox" onChange={this.inputChange} name="hobby" value='排球' />
                        网球<input type="checkbox" onChange={this.inputChange} name="hobby" value='网球' />
                        <button>提交</button>
                    </form>
                </div>
            )
        }
    }

受控表单

只要使用了value,就是受控value,必须加事件控制数据

<input onChange={this.inputChange} type="text" value={this.state.user} name="user" />

非受控表单

没有value属性和selected(不推荐使用,最好用value)属性
有defaultValue属性

defaultValue和value不能同时使用

子组件

 class Child extends React.Component {
        render() {
            return (
                <div style={{ color: this.props.col }}>
                    我是子组件
                </div>
            )
        }
    }
    class Demo extends React.Component {
        render() {
            return (
                <div>
                    <h1>我是父组件</h1>
                    //使用子组件
                    <Child />
                    <Child />
                </div>
            )
        }
    }

父子组件传递数据

父组件传递给子组件

通过属性的方式传递
 class Child extends React.Component {
        render() {
            return (
                <div style={{ color: this.props.col }}>
                    我是子组件
                </div>
            )
        }
    }
class Demo extends React.Component {
   constructor() {
       super()
       this.state = {
           col1: 'red',
           col2: 'pink'
       }
   }
   render() {
       return (
           <div>
               <h1>我是父组件</h1>
               //这里的col1和col2是父组件的数据
               //通过使用组建的时候将父组件的数据作为属性的值传递给子组件
               <Child col={this.state.col1} />
               <Child col={this.state.col2} />
           </div>
       )
   }
  }
通过html结构传递
class Child extends React.Component {
        constructor() {
            super()
            this.state = {
                msg: "我是子组件传给父组件的数据"
            }
        }
        fClick() {
            this.props.fn(this.state.msg)
        }
        render() {
        	//这里通过结构的方式拿到数据
        	//属性里的值和函数、html里的值都存在props里面
            let { tit, children } = this.props
            return (
                <div style={{ color: this.props.col }}>
                     我是子组件
                    <button onClick={this.fClick.bind(this)}>向父组件传递数据</button>
                    //另一种方式接收父组件的数据(html结构)
                    {this.props.children}
                    
                    //解构赋值
                    <p>解构赋值下的:{tit}</p>
                    {children}
                </div>
            )
        }
    }
  class Demo extends React.Component {
      constructor() {
          super()
          this.state = {
              col1: 'red',
              col2: 'pink'
          }
      }

      fn(msg) {
          console.log(msg)
      }
      render() {
          return (
              <div>
                  <h1>我是父组件</h1>
                  <Child fn={this.fn} col={this.state.col1} tit="12345">
                      <ul>
                          <li>aaaa</li>
                          <li>bbbb</li>
                          <li>cccc</li>
                      </ul>
                  </Child>
                  <Child fn={this.fn} col={this.state.col2} />
              </div>
          )
      }
  }
也可以将html结构封装成一个组件,作为参数传递给子组件

子组件传递给父组件

与父传子相似,但是使用函数作为参数值

 class Child extends React.Component {
        constructor() {
            super()
            this.state = {
                msg: "我是子组件传给父组件的数据"
            }
        }
        //这里的props里面不仅可以接收传过来的数据,也可以接收传过来的函数
        //调用传过来的fn,并传入子组件的数据,传到父组件
        fClick() {
            this.props.fn(this.state.msg)
        }
        render() {
            return (
                <div style={{ color: this.props.col }}>
                    我是子组件
                    //点击这个按钮触发子组件点击事件
                    <button onClick={this.fClick.bind(this)}>向父组件传递数据</button>
                </div>
            )
        }
    }
    class Demo extends React.Component {
        constructor() {
            super()
            this.state = {
                col1: 'red',
                col2: 'pink'
            }
        }

        fn(msg) {
            console.log('父组件接收到的子组件的参数:'+msg)
        }
        render() {
            return (
                <div>
                    <h1>我是父组件</h1>
                    <Child fn={this.fn} col={this.state.col1} />
                    <Child fn={this.fn} col={this.state.col2} />
                </div>
            )
        }
    }

关于react的props上面的属性和函数

props除了能接收到使用时传过来的属性,还有其他属性

props.children

返回的是,使用组件时,标签内部所有的子组件

history对象

history对象里面保存着用户的上网记录,有以下方法和属性
- length:历史纪录的数量
- go():在历史纪录中任意跳转,可以是数字
- back():移动到上一个网址
- forward():移动到下一个网址

location对象

与当前窗口加载的文档有关的信息
- hash:URL中的hash
- host:服务器名称端口号
- hostname:服务器名称
- pathname、port、protocol、search(?后面的参数)

match对象

URL与Route,匹配时Route创建match作为props中的一个属性传递给被渲染的组件;
- params:params是从匹配的URL中解析出的path中的参数
- isExact: 布尔值,完全匹配时为true,反之为false
- path: Route的path属性,构建嵌套路由时会使用
- url: URL的匹配部分

脚手架

安装 :npx create-react-app my-app
npx:它是 npm 5.2+ 附带的 package 运行工具。用它就不用一直更新npm了,

进入项目:cd my-app
运行项目:npm start或者yarn start(会自动检测电脑是yarn主导还是npm,然后提醒使用哪一个)

目录结构

脚手架样式部分

css文件的引入方式:import 'css文件的相对路径'

配置sass
	- sass已经在create-react-app里面配置过了,要使用的话,下载`node-sass`就好了
	- 把css文件后缀变成`.sass`
配置less

下载less:yarn add less less-loader

配置:

  1. 把配置文件暴露出来:npm run ejectyarn eject(要确保工作区里面没有东西,不然就会创建不成功,弹出提示)
  2. 修改webpack.config.js
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

改为

const sassRegex = /\.less$/;
const sassModuleRegex = /\.module\.less$/;

'sass-loader'改为'less-loader'
运行yarn start

这里如果报错,
在这里插入图片描述
就是l下载的ess版本过高
就下载低版本:yarn add less less-loader@6

如果想要样式文件模块化,不受外界影响,文件后缀名:.modul.less
在引入的时候也要加

重置样式库(清除标签默认样式)

写在index.css(样式文件)或者App.css(样式文件)最上面:@import-normalize;

后置css

之前很多css3的样式,在不同浏览器中没有统一,很多css都要加样式

后置css会解决一些浏览器出现的不兼容的情况
比如:

.App {
  display: flex;
  flex-direction: row;
  align-items: center;
}

就变成:

.App {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
  -ms-flex-direction: row;
  flex-direction: row;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
}
图片

像这样:

import { ReactComponent as Logo } from './logo.svg';

function App() {
  return (
    <div>
      {/* Logo is an actual React component */}
      <Logo />
    </div>
  );
}

hook(钩子)

hook的出现弥补了函数式组件的功能缺陷,没有生命周期、状态

引入钩子:import React, { useState } from 'react';

状态(state)的hook:userState

使用:
userState是一个函数,调用后(userState())会得到一个数组,调用的时候可以传入一个初始值
数组的第一项:状态的名字
数组的第二项:修改状态的方法

import React, { useState } from 'react'
//import { useState } from 'react'	//也可以
function Hook(){
	//解构赋值
	const [n,setN]=userState(1)
	const [m,setM]=userState({a:1})
	const addM=()=>{
		//对象和数组会浅拷贝,指向同一片地址
		//这里可能会认为修改了newM,m没有修改,页面渲染的时候会出现反应不及时,所以采用深拷贝。
		//let newM=m
		
		//解构赋值是深拷贝(数组和对象都可以)
		let newM={...m}
		newM.b=2
		//这一步之后,m被改为{a:1,b:2}
		setM(newM)
	}
	return(
		<div>
			<p>{n}</p>
			//将n值修改为10
			<button onClick={()=>{setN(10)}}></button>
			<p>{JSON.stringify(m)}</p>
			<button onClick={addM}></button>

		</div>
	)
}

生命周期的hook:useEffect

引入:import { useState ,useEffect} from 'react'

useEffect,也是一个函数,直接调用
第一个参数 一个回调函数(初始化会执行,数据改变会执行)
第二个参数 一个数组,里面放着要监听的数据,当这个数组为空,只有页面加载的时候会执行

以下内容写在函数组件内部:

//初始化、数据改变都会监听
const [n,setN]=userState(1)
useEffect(()=>{
	//数据初始化执行——componentDidMount()
	//数据改变也会执行——componentDidUpdate
	console.log(1)
})

//n改变的时候会执行
useEffect(()=>{
	console.log(1)
},[n])

//只有组件加载执行
useEffect(()=>{
	console.log(1)
},[])

//组件销毁时执行
useEffect(()=>{
	console.log(1)
	return ()=>{
		//组件销毁时执行
	}
},[])

userRef操作DOM元素

因为不允许直接操作DOM,可以通过唯一的这个接口操作DOM

class组件操作DOM

import React from 'react'
class List extends React.Component{
	constructor(){
		super()
		this.ri=React.createRef()
	}
	render(){
		return(
			<ul ref={this.ri}>
				<li>1</li>
				<li>1</li>
			</ul>
		)
	}
}

函数式组件操作DOM

useRef是一个函数,调用得到ref的名字
以下内容在函数组件内部:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    //inputEl.current就是input这个Dom节点
    console.log(inputEl.current)
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

路由(react-router)

下载路由模块:npm add react-router-dom@6yarn add react-router-dom@6

三大块内容:
react-router 核心内容
react-router-dom 非app的路由
react-router-native app版本

所有的内容都是组件

react-router-dom(非app)

有以下组件:

路由种类组件:
<BrowserRouter></BrowserRouter>:历史路由,需要后台配合使用(监控地址栏的变化)
<HashRouter><HashRouter>:以锚点的形式跳转(地址为:…:端口/#/跳转至的组件,监控锚点后面的数据的变化)

hash路由的原理

通过给窗口绑定hashchange事件监听锚点的变化,根据不同的锚点渲染不同的组件:

import React,{Component}from 'react';
import ReactDOM from 'react-dom';
import './index.css';

const Home=()=><div>Home组件</div>
const About=()=><div>About组件</div>

class App extends Component {
  //取锚点值(hash值):window.location.hash
  constructor() {
    super()
    this.state = {
      hash:window.location.hash
    }
  }
  componentDidMount() {
    //组件一加载完,就绑定‘hashchange’事件,监听hash值的变化,
    //当hash值发生改变的时候就会触发
    window.addEventListener('hashchange', () => {
      this.setState({
        hash:window.location.hash
      })
    })
  }
  render() {
    let RouteView = null
    switch (this.state.hash) {
      case '#/home':
        RouteView=<Home/>
        break
      case '':
        RouteView=<Home/>
        break
      case '#/about':
        RouteView=<About/>
        break
    }
    return (
      <div>
        <h2>模拟一下锚点路由</h2>
        <ul>
          <li><a href='#/home'>首页</a></li>
          <li><a href='#/about'>关于我们</a></li>
        </ul>
        {RouteView}
      </div>
    )
  }
}

ReactDOM.render(
    <App />,
  document.getElementById('root')
);
history模式

窗口绑定popstate事件,监听有没有跳转到历史记录,这里是通过h5的History接口来操作浏览器会话历史记录,History对象是一个底层接口,不继承于任何的接口。

具体总结在另一篇笔记里
参考:https://blog.csdn.net/weixin_33747129/article/details/88205861

其他组件

包裹Route:<Routes></Routes>:用来装路由指示标签

路由配置组件:
<Route></Route>
有以下属性

	path:与Link(NavLink)里to的值一样,`path='/home'`
	element:当path匹配到Link里的to时,应该展示的组件,`element={<Home />}`
	

路由按钮:<Link to='跳转的路径'></Link><Link to='/home '></Link>

组件展示内容的占位组件:<Outlet />

综上,在App.js里写一个最简单的路由:

//要从react-router-dom引入Link、Routes、Route、HashRouter模块
import {Link,Routes,Route,HashRouter} from 'react-router-dom'
//要引入Home、List组件
import Home from './component/Home/Home'
import List from './component/List /List'

function App(){
	return(
		<div>
			<HashRouter>
				<Link to='/home '></Link>
				<Link to='/list'></Link>
				<Routes>
					<Route path='/home' element={<Home />}></Route>
					<Route path='/list' element={<List/>}></Route>
				</Routes>
			</HashRouter>
		</div>
	)
}

要实现所有页面都可以写路由标签(Routes)和跳转按钮,就把<HashRouter></HashRouter>或者<BrowserRouter></BrowserRouter>写在最外层的index.js里面,并且将<App/>组件包裹起来

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import {Link,Routes,Route,HashRouter} from 'react-router-dom'
ReactDOM.render(
	<HashRouter>
		<App/>
	<HashRouter>
)

App组件内部(下面代码的App组件也是这样):

import './App.css';
import { Link,Outlet} from 'react-router-dom'

function App() {
  return (
    <div className="App">
      <p>11-25</p>
      <Link to='/home'>首页</Link>
      <Link to='/list'>列表页</Link>
      <Outlet/>
    </div>

  );
}

export default App;

要将路由的配置也放在index.js里面,配置/对应App组件,其他组件为子路由
进入页面就展示某个组件的内容:将path换为index,element里写要展示的组件

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { Routes,Route,HashRouter} from 'react-router-dom'
ReactDOM.render(
	<HashRouter>
		<Routes>
		//'/'就匹配<App/>组件,其他的为子路由
	      <Route path='/' element={<App />}>
	      	//进入页面展示的组件
	        <Route index element={<Home/>}></Route>
	        <Route path='home' element={<Home />}></Route>
	        <Route path='list' element={<List />}></Route>
	        //如果地址不匹配就匹配这个
	        <Route path='*' element={<div>没有这个页面</div>}></Route>
	      </Route>
	    </Routes>
	</HashRouter>
)

当地址/后面的地址不能识别的时候提示:

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { Routes,Route,HashRouter} from 'react-router-dom'
ReactDOM.render(
	<HashRouter>
		<Routes>
	      <Route path='/' element={<App />}>
	        <Route index element={<Home/>}></Route>
	        <Route path='home' element={<Home />}></Route>
	        <Route path='list' element={<List />}></Route>
	        //这里跟<Outlet/>组件对应起来,当地址内容不能匹配,就用这个占位组件展示element的内容
	        <Route path='*' element={
	        	//这里也可以是一个组价
	        	<div>暂无此页面</div>
	        }></Route>
	      </Route>
	    </Routes>
	<HashRouter>
)

添加子路由

就在父路由下写Route标签和对应组件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.less';
import App from './App';
import { Routes, Route, HashRouter } from "react-router-dom"
import Home from './component/Home/Home'
import List from './component/List/List'
import HotList from './component/List/HotList'
import Military from './component/List/Military'
import Enter from './component/List/Enter'

ReactDOM.render(
  <HashRouter>
    <Routes>
      <Route path='/' element={<App />}>
        <Route index element={<Home/>}></Route>
        <Route path='home' element={<Home />}></Route>
        
        <Route path='list' element={<List />}>
        	//进入List组件默认展示的子组件的路由配置
          <Route index element={<HotList />}></Route>
         	//配置子路由
          <Route path='hotList' element={<HotList />}>
           	 <Route path=':id' element={ <HotList/>}/>
          </Route>
          <Route path='military' element={<Military/>}></Route>
          <Route path='enter' element={<Enter/>}></Route>
        </Route>
      </Route>
    </Routes>
  </HashRouter>,
  document.getElementById('root')
);
在父组件里面写Link按钮

这里使用了新的组件`<NavLink></NavLink>`:可以在对应样式文件里配置active类,从而改变被点击的按钮的样式,不配置也会有个默认的active类

```javascript
import './List.modul.less'
import { Outlet,NavLink} from 'react-router-dom'

function List() {
    return (
        <div>
            <p>我是List</p>
            <p>路由里传数据</p>
            //配置
            <NavLink to='/list/hotList/12'>热点</NavLink>
            <p>link里传数据:</p>
            <NavLink to='/list/enter?a=1&b=2'>娱乐</NavLink>
            <NavLink to='/list/military'>军事</NavLink>
            <Outlet/>
        </div>
    )
}
export default List;

路由传参

路由配置时传参

在路由配置时传入参数名
path=’:参数名’

 <Route path='hotList' element={<HotList />}>
 	<Route path=':id' element={ <HotList/>}/>
</Route>

在NavLink里面传入参数值

<NavLink to='/list/hotList/12'>热点</NavLink>
接收数据,使用useParams组件

在组件里接收数据时

import { useParams} from 'react-router-dom'
function HotList() {
//a里面就是传过来的参数
    let a = useParams()
    return (
        <div>
            我是HotList------{ a.id}
        </div>
    )
}

直接在NavLink里面传参

类似get请求传参

<NavLink to='/list/enter?a=1&b=2'>娱乐</NavLink>
获取参数,使用useSearchParams组件
import { useSearchParams} from 'react-router-dom'
function Enter() {
//这里的a是一个对象
    let [a]=useSearchParams()
    return (
        <div>
            我是Enter----获取b:{a.get('b')}-----获取a:{ a.get('a')}
        </div>
    )
}

js跳转页面,useNavigate组件

使用useNavigate通过js代码跳转页面
跳转的时候也可以传送数据:

import { useNavigate} from 'react-router-dom'
function Military() {
    let navi=useNavigate()
    const go = () => {
    	//动态路由传数据
        navi('/list/hotList/20')
    }
    const goE = () => {
    	//直接传数据
        navi('/list/enter?a=11111&b=2222222')
     }
    return (
        <div>
            我是Military
            <button onClick={ go}>去热点</button>
            <button onClick={ goE}>去娱乐</button>
        </div>
    )
}
export default Military;

动态延迟加载

就是延迟加载组件,等到要用的时候再加载,这样项目的启动速度就会快很多

使用前:import OtherComponent from './OtherComponent';
使用后:const OtherComponent = React.lazy(() => import('./OtherComponent'));

使用懒加载之后,在使用组件的时候不能直接写组件标签,而是要以以下方式使用:

要使用<Suspense></Suspense>组件,这个组件由react模块解构赋值而来:import React, { Suspense } from 'react';

<div>Loading...</div>}>也可以简写成<>...</>

import React, { Suspense } from 'react';
 <Suspense fallback={<div>Loading...</div>}>
   <OtherComponent />
 </Suspense>

注意,在路由的element里面也是这么使用组件的

import React, { Suspense } from 'react';
const HotList= React.lazy(() => import('./HotList'))
<Route path='hotList' element={
	<Suspense fallback={<div>Loading...</div>}>
	   <HotList/>
	 </Suspense>
} />

请求、跨域、封装请求

fetch

也是原生的工具

因原生的ajax不能直接使用, 而且原本的ajax没有promise,所以fetch诞生了

基本写法:

let data={a:1}
//url=http://ip/请求地址
fetch(url,{
	method:'get/post',
	//get请求的数据只能在请求地址里传:http://ip/请求地址?a=1&b=2
	//post请求传数据用body:{}
	body:JSON.stringify(data)
	//这里的请求头用application/json
	headers:{
		'Content-Type': 'application/json',
	}
}).then((res)=>{
	//第一个then是要确定返回的数据的数据类型
	return res.json()
}).then((res)=>{
	//这里的res才是数据
})

fetch的封装

一个http.js文件

get方式的数据放在请求地址里
post方式的数据既可以放在请求体里(data),也可以放在请求地址里(parmas)

const baseURL='/api'

const headers={
	'Content-Type': 'application/json',
}
//封装一个将json格式的data转化为字符串的函数
function jsonToUrl(data){
	//遍历json数据用 for-in,name是key,访问key的值用data[name]
	//不能用data.name
	for(let name in data){
		arr.push(name+'='+data[name])
	}
	return arr.join('&')
}
function http({url,datat={},method='get',parmas={}}){
	//如果没有请求地址,函数不再继续执行
	if(!url){return}
	
	//如果有token,就将token封装到headers
	if(sessionStorage.getItem('token')){
		header['token']=sessionStorage.getItem('token')
	}

	//如果是get请求
	if(method.toLowerCase=='get'){
		return fetch(baseURL+url+'?'+jsonToUrl(parmas),{}).then((res)=>res.json())
	}else{
		//如果是post请求
		return fetch(baseURL+url+'?'+jsonToUrl(parmas),{method,headers,body:JSON.stringify(data)}).then((res)=>res.json()).then((res)=>res.json())
	}
}
export default http

解决跨域问题

在webpackDevServer.config.js文件里找proxy,配置proxy

proxy:{
	'/api':{
		target:'http://ip',
		changeOrigin:true,
		pathRewrite:{
			//以/api开头的请求将/api置为空
			'^/api':''
		}
	}
}

配置完成后要重启项目

高阶组件HOC

高阶函数(HOF):把函数当作函数参数传进去,经过加工在返回一个函数
高阶组件:把组件当作组件参数传进去,经过加工在返回一个组件

//这里的Com是一个组件形参
function HOC(Com){
	//返回一个组件
	return function(){
		return(
			<div>
				<h1>我是加工后的组件</h1>
				<Com/>
			</div>
		)
	}
}

使用

import HOC from './HOC'
import Enter from './Enter'
function List(){
	return(
		<div>
			{HOC(<Enter />)}
		<.div>
	)
}

UI框架

动态渲染icon

//引入antd-icon
import * as Icon from '@ant-design/icons
import React from "react";
//创建节点的方法
iconBC(name){
    console.log(name);
    return React.createElement(Icon[name]);
}

allMenu.map((subMenu) => {
    return (
        <Menu.Item key={subMenu.url} icon={this.iconBC(subMenu.icon)}>
            {subMenu.name}
        </Menu.Item>
    )
})

状态管理

flex:是一种思想
redux:也是一个思想
react-redux:
mobx:

mobX

安装:
npm install mobx -Dyarn add mobx -D:提供数据管理数据
npm install mobx-reactyarn add mobx-react:关联react,数据变了,让页面更新

基础用法:

import { makeAutoObservable } from "mobx";

class Store {
  constructor() {
    makeAutoObservable(this);
  }
  //全局变量和改变它的方法
  breadBounch = "";
  setBounch(bounchName) {
    this.breadBounch = bounchName;
  }
}
let store = new Store();
export default store;

导入函数并将全局变量要使用的范围s

React.createElement(xxx)

创建一个组件

react+小程序——taro

结构布局:小程序的布局
页面接口:Taro.xxx
页面语法:react

安装及其使用

使用npx下载cli:npx @tarojs/cli init 项目名称
选择模板:
默认模板就是类组件模板
default-youshu:统计网站访问量
taro-hooks:
taro-ui:自带ui

运行编译:npx taro build --type weapp --watch

微信小程序打开项目,打开dist文件

使用

使用小程序的组件:

// 引入小程序的组件
import { View, Text, Button, Image } from "@tarojs/components";

使用hook:

// 引入hook
import { useEnv, useNavigationBar, useModal, useToast } from "taro-hooks";

引入图片:import logo from "./hook.png";

调整全局配置:app.config.js
单页面配置:pageName.config.js

页面逻辑:pageName.jsx

尺寸

最好使用px和百分比,小程序会自动转化为rpx,具体参考文档

taro-ui

下载:yarn add taro-ui

小程序打包

打包成H5(html、js、css):npx taro build --type h5
发布可以打包然后放在hbuilder里面发布(uni-app可以直接在hbuilder里面打包和发布)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值