文章目录
react引入和基本使用
- 通过标签引入react
基础的语法的学习和联系 - 通过脚手架的
- 特点: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使用
- 多个元素时,必须有一个根组件
const a=<div> <p>hello world</p> <span>11111</span></div>
- 缩进不会影响,也可以在最外层加一个括号,方便书写时区分
const a=(<div> <p>hello world</p> <span>11111</span></div>)
- 使用单表签必须闭合:
<img src='' alt='' />
- 在JSX的标签内部放变量,使用
{}
包裹起来。只有字符串和数字可以正常展示,其他数据类型的数据都不能正常展示
- 数组、布尔要正常展示就要:.toString
- json格式的数据要展示:JSON.stringify(json)
- 在JSX的标签属性里放变量,也是使用
{}
,这里需要注意,如果变量原本就有引号,属性值里面就不用引号了
let src='https://www.baidu.com/img/flexible/logo/pc/result.png'
const a=<img src={src} />
- 给标签添加class,在直接使用的时候会出现警告——建议变成className。也就是把class替换成className
- 添加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>
- 添加事件,
- 以前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>
}
}
使用方法与函数式组件一样的,类名首字母大字,使用的时候可以使用单表签
两者区别
- 一般的函数组件没有生命周期,没有状态,有属性,this指向可能会出问题,一般用箭头函数
- class组件有生命周期,有状态,有属性,this指向不会出问题
- 加上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({})去操作数据,是不能实现数据改变页面的数据也改变的,比如用一个一般变量放在页面,然后点击改变了这个变量,变量虽然改变了,但是页面上面的内容不会改变
想让页面更新的方法:
- ReactDOM.render()
- 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
配置:
- 把配置文件暴露出来:
npm run eject
或yarn eject
(要确保工作区里面没有东西,不然就会创建不成功,弹出提示) - 修改
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@6
或yarn 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 -D
或yarn add mobx -D
:提供数据管理数据
npm install mobx-react
或yarn 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里面打包和发布)