react

237 阅读10分钟

react 开发者工具

react

用于构建用户界面的js库。

印记中文

中文文档,比如:react、react-router、redux、react-redux等

脚手架

npx 是 npm 自带的一个强大工具,它允许你直接运行 create-react-app 命令而不需要提前全局安装。它会在执行时自动下载最新版本,用完即焚,保证你每次创建项目使用的都是最新版。

npx create-react-app my-app
npx create-react-app my-app --template typescript // ts版本
npm install -g create-react-app
create-react-app my-app
npm create vite@latest my-app -- --template react
npm create vite@latest my-app -- --template react-ts // ts版本

淘宝镜像

npm config set registry https://registry.npmmirror.com

特点

组件化、声明式、使用虚拟DOM+diff算法减少与真实DOM的交互。

babel

es6转es5、jsx转js、默认开启了严格模式(use strict)

虚拟DOM

本质是object类型的对象、虚拟DOM比较轻(属性比较少),最终会被转为真实dom

创建虚拟dom的两种方式:

var VDOM = React.createElement('div', {id: 'lxh'}, React.createElement('span', {}, '不常用的创建方式'))
var VDOM = (
    <div id='lxh'>
        <span>jsx常用</span>
    </div>
)
ReactDOM.render(VDOM, document.getElementById('root'))

渲染页面

ReactDOM.render(<Component />, document.getElementById('root'))

react解析组件标签,找到对应的组件,调用该组件,将返回的虚拟dom转为真实dom,呈现页面

jsx

全称JavaScript xml,本质是React.createElement(component, props, children)方法的语法糖。

使用规则:

  • 不要写引号,比如<div id={lxh}>{lxh}</div>
  • 在标签中混入js表达式要用{}
  • 类名写className(因为es6中类定义的关键字是class,为了避免所以使用className)
  • 内联样式style={{key: value}}
  • 只能有一个根标签
  • 标签必须闭合
  • 标签名小写,则转html里的同名标签,没有则报错
  • 标签名大写,react渲染对象的组件,没有则报错
  • xml早期用于存储和传输数据
  • 注释 {/* content */}

jsx是 React.createElement() 的语法糖

{{}}

外层括号表示要写js表达式,内层括号表示要写对象

if

function xx({ isT }) {
// 在 JSX 外部使用
  if (isT) {
    return <h1>xxx</h1>;
  } else {
    return <h1>yyy</h1>;
  }
  
  return <> {isT ? <h1>xxx</h1> : <h1>yyy</h1>} </>
  return <> {isT && <h1>xxx</h1>} </>
  return <>
             {(() => {isT ? <h1>xxx</h1> : <h1>yyy</h1>})()} // 立即执行的函数
         </>
         
// if、switch都可以写 
  let msg
  if (isT) {
    msg = <h1>xxx</h1>;
  } else {
    msg = <h1>yyy</h1>;
  }
  return <div>{msg}</div>;

for

function xxx({ arr }) {
  return (
    <ul>
      {arr.map((item) => (
        <li key={item.id}>{item.text}</li> // 使用数组的map()方法(最推荐)
      ))}
    </ul>
  );
  
  // 在jsx外部使用
  const _arr = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    _arr.push(<li key={item.id}>{item.text}</li>);
  }
  return <ul>{_arr}</ul>;
  
  const _arr = [];
  for (const item of arr) {
    _arr.push(<li key={item.id}>{item.text}</li>);
  }
  return <ul>{_arr}</ul>;

  {
    arr.filter(item => item.isT).map(item => <li key={item.id}>{item.text}</li>)
  }
}

组件

  • 类式组件(适用于复杂组件(有State))
class LxhComponent {
    state = { bool: true }
    clickFun = () => {
        this.setState({ bool: !this.state.bool })
    }
    render() {
        return <div onClick={clickFun}>类式组件</div>
    }
}
  • 函数式组件(适用于简单组件(无state))当然现在可以使用hook函数了useState()
function LxhComponent () {
    return <div>函数式组件</div>
}

样式的模块化

以 import styles from'./xxx.module.css' 的方式导入,然后通过 styles.btn 引用类名

state

state是一个对象

类式组件:修改state,使用setState,修改后的state会和原state合并
        state = {};
        对象式:this.setState({}, [callback])
        函数式:函数式会接收到两个参数,state、props
               this.setState((state, props) => {}, [callback])
函数式组件:使用useState
    let [lxh, setLxh] = useState({name: "lxh", age: 24});
    修改state:修改后的state会覆盖原state
        第一种写法:setLxh({...lxh, age: 25});
        第二种写法:setLxh((value) => newValue);
        
    参数:第一次初始化的值在内部做缓存

props

props是一个只读对象

类式组件:vscode快捷键(rcep)
    第一种:(常用)
        // 限制类型
        static propTypes = {
                   name: PropTypes.string.isRequired,
                   fun: PropTypes.func,
                   boo: PropTypes.bool,
        }
       // 设置默认值
       static defaultProps = {
            name: "lxh",
       }
       
   第二种:
       组件名.propTypes = {}
       组件名.defaultProps = {}
函数式组件:vscode快捷键(rafcp)
    组件名.propTypes = {}
    组件名.defaultProps = {}

children props、render props

相当于插槽slot

children props

<A>
    <B></B>
</A>
{this.props.children}

<div children='xxx'></div>
<div>xxx</div>
问题:如果B组件需要A组件的数据,做不到

render props

三个组件:
parent组件:<A render={(data) => <B data={data}></B>} />
A组件:{this.props.render(内部state数据data)}
B组件:读取A组件传过来的数据{this.props.data}

constructor构造器

可写可不写

  • 接收props,并且传给super(props),这样在构造器里就能拿到props
  • 不接收props,在构造器里props就是undefined

ref

请勿过度使用ref

类式组件

第一种:字符串(不推荐,过时,效率不高),标签<input ref='xxx' />,通过this.refs.xxx获取

第二种:回调函数形式
    如果是内联函数,在更新过程中会执行两次:
        第一次传的值是null
        第二次才传的DOM节点
    使用标签<input ref={c => this.名称 = c} />
    获取this.名称
    这种情况无关要紧,可以写内联函数
    如果需要避免执行两次:
        不写内联函数,写成class绑定函数的形式
        <input ref={this.xxx} />
        xxx = (e) => {
            this.yyy = e
            console.log(e)
        }
        
第三种:React.createRef,专人专用,如果使用多次,需要定义多次
<input ref={this.xxx} />
this.xxx = React.createRef()
this.xxx.current.value // 获取输入框的值

函数式组件

使用useRef()
const lxh = React.useRef()
lxh.current.value

context

常用于祖组件和后代组合间通信

const lxhContext = React.createContext() // 创建context容器对象
<lxhContext.Provider value={数据}> // 渲染子组件
    子组件
</lxhContext.Provider>

// 后代组件读取数据
    // 第一种方式:只适用于类式组件
    static contextType = lxhContext // 声明接收context,才能获取到
    this.context // 读取context中的value数据

    // 第二种方式:函数组件与类式组件都可以
    <lxhContext.Consumer>
        {
            value => {
                return `${value}` // value就是context中的value数据
            }
        }
    </lxhContext.Consumer>
    const ctx = useContext(lxhContext) // 可以使用useContext获取数据

受控组件、非受控组件

表单中收集数据
受控组件:通过input提供的onChange()事件
<input onChange={this.xxx('yyy')} />
xxx = (name) => {
    return (e) => {
        this.setState({[name]: e.target.value})    
    }
}

<input onChange={(e) => this.xxx('yyy', e)} />
xxx = (name, e) => {
    this.setState({[name]: e.target.value})
}

非受控组件:现取现用,操作DOM(ref),获取数据

推荐使用受控组件

高阶函数、函数的柯里化、纯函数


高阶函数:函数的参数是函数,函数的返回值是函数。符合其一就是高阶函数。
例如:Promise()、setTimeout()、map()等

函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式
    this.lxh(1)(2)
    lxh = (a) => {
        return (b) => {
            // a = 1、b = 2
        }
    }
    
纯函数:只要是同样的输入(实参),必定得到同样的输出(返回值)

生命周期

类式组件

used.png

render() {
    // 必须使用
}
componentDidMount() {
    // 常用,一般用于开启定时器、发送网络请求、订阅消息
}
componentWillUnmount() {
    // 常用:一般开闭定时器、取消订阅消息
}
componentWillReceiveProps(props) {
    /**
     * 第一次接收props时,不执行,props改变时,执行,即将移除此生命周期
     * 使用:UNSAFE_componentWillReceiveProps() {}
     **/
}
componentWillMount() {
    // 即将移除此生命周期,使用:UNSAFE_componentWillMount() {}
}
componentWillUpdate() {
    // 即将移除此生命周期,使用:UNSAFE_componentWillUpdate
}
shouldComponentUpdate() {
    // 必须写返回值,不然报错
    return true // 不写这个生命周期,默认返回true
}
 this.forceUpdate() // 强制更新
 卸载组件:
   ReactDOM.unmountComponentAtNode(document.getElementById('xxx'))
   触发componentWillUnmount(),但不能卸载createRoot()创建的组件
   
   createRoot()创建的组件卸载方式:
     const ROOT = createRoot(document.getElementById('xxx'))
     (注意:后面加render函数这样写:ROOT.render())
     export default ROOT
     import root from '../index'
     root.unmount()

new.png

    废弃了3个,新增了2个
    过期的生命周期:
        UNSAFE_componentWillMount()
        UNSAFE_componentWillReceiveProps()
        UNSAFE_componentWillUpdate()

    getDerivedStateFromProps(props, state) {
        /**
         * 不常用
         * 若state的值在任何时候都取决于props,就可以使用
         * 缺点:派生状态会导致代码冗余,并使组件难以维护
         * 使用时,前面加static
         **/
         return props // 必须返回一个state对象 或者 null,是对象与state合并
    }
    
    getSnapshotBeforeUpdate() {
        /**
         * 不常用
         * 组件能在发生更改之前从DOM中捕获一些信息(如滚动位置),它的返回值传递给componentDidUpdate()
         * 必须和componentDidUpdate一起使用,不然报错
         * 必须返回一个值或者null,不能返回undefined
         **/
         return "snapshotValue" // 传给componentDidUpdate的第三个参数
    }
    
    componentDidUpdate(preProps, preState, snapshotValue) {
        // 更新完成
    }
    
    例子:
    import React, { Component } from "react";

    class Home extends Component {
      state = { arr: [] };
      componentDidMount() {
        let timer = setInterval(() => {
          this.setState(
            (state) => ({
              arr: ["新闻" + (state.arr.length + 1), ...state.arr],
            }),
            () => {
              if (this.state.arr.length >= 20) {
                clearInterval(timer);
                timer = null;
              }
            }
          );
        }, 1000);
      }
      
      componentWillUnmount() {}
      
      getSnapshotBeforeUpdate() {
        return this.refs.refStr.scrollHeight;
      }
      
      componentDidUpdate(preProps, preState, snapshot) {
        this.refs.refStr.scrollTop += this.refs.refStr.scrollHeight - snapshot;
      }
      
      render() {
        return (
          <div
            style={{
              height: 100,
              width: 200,
              backgroundColor: "pink",
              overflow: "auto",
            }}
            ref="refStr"
          >
            {this.state.arr.map((item, index) => {
              return (
                <div style={{ height: 20 }} key={index}>
                  {item}
                </div>
              );
            })}
          </div>
        );
      }
    }

    export default Home;

函数式组件

使用useEffect,可以在函数式组件中执行副作用操作(用于模拟类式组件里的生命周期钩子)
主要用于:
    发送ajax请求
    设置订阅、启动定时器
    手动更改真实DOM
用法:
    useEffect(() => {
        // 在此可是执行任意带副作用操作
        return () => {
            // 在组件卸载前执行,比如清除定时器、取消订阅
        }
    }, []) // 如果指定的是[],回调函数只会在render()后执行
    // 没有写[],就是监测所有state状态
    /**
     * 可以把useEffect看成是三个生命周期的组合
     * componentDidMount
     * componentDidUpdate
     * componentWillUnmount
     **/

路由

SPA:单页面应用,整个应用只有一个完整的页面,点击页面的链接,不会刷新页面,只会做页面的局部更新。

路由:一个路由就是一个映射关系(key:value)
     key:路径、value:component或function
         
前端路由:浏览器端路由,value是component,用于展示页面内容
         注册路由:<Route to='' component='' /> v5的方式
                  <Route to='' element='' /> v6的方式
         工作过程:当浏览器的path等于对应的路由时,当前的路由就会变成对应的组件。

后端路由:value是function,用于处理客户端提交的请求
         注册路由:router.get(path, (req, res) => {})
         工作过程:当node接收到一个请求时,根据请求的路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据。
    

react-router-dom

安装:npm i -S react-router-dom@5 // v5的版本,现在默认安装的是v6
     npm i -S react-router-dom // v6版本
     
浏览器:强制刷新,按住shift,点刷新

BrowserRouter 放在顶层,可以确保应用中任何组件(无论嵌套多深)都能使用路由钩子(如 useNavigateuseLocation)和组件(如 <Link><Routes>

v5

// NavLink用于高亮,它的属性activeClassName(默认值:active)用于选中的样式
<NavLink activeClassName='' className='' to='' replace></NavLink>
<Link className='' to='' replacee></Link> // Link没有activeClassName属性,默认是push模式

<NavLink>content</NavLink>  // children,通过props来获取,两个相同
<NavLink children='content'></NavLink>

// 解决样式丢失问题
<link href='./css/bootstrap.css' />
第一种解决:<link href='/css/bootstrap.css' /> // 不用用./css...
第二种解决:<link href='%PUBLIC_URL%/css/bootstrap.css' />
第三种解决:用hash模式

<Switch></Switch> // Switch用于匹配到适合的第一个path时,就不往下匹配了(v6已经移除)

<Route path='' component={xxx} exact></Route> // 严格匹配exact,不要随便开启,有时候开启不能匹配到二级路由

<Redirect to=''></Redirect> // Redirect重定向,一般写在路由的最下方

路由传参:
// params方式:刷新不会丢失
<Link to={`/home/${id}`}></Link>
<Route path='/home/:id' /> // 需要占位
// 获取参数通过props.match.params
this.props.history.push(`/home/${id}`) // 编程式
this.props.history.replace(`/home/${id}`) // 编程式

// search方式(相当于ajax的query参数)刷新不会丢失
<Link to={`/home/?id=${id}&title=${title}`}></Link>
this.props.history.push(`/home?id=${id}`) // 编程式
this.props.history.replace(`/home?id=${id}`) // 编程式
/**
 * 不需要声明接收
 * 获取参数通过props.location.search,比如拿到'?id=xxx&title=lxh'
 * react默认安装了querystring
 */
import qs from 'querystring';
const { search } = this.props.location
const { id, title } = qs.parse(search.slice(1)) // 需要去掉前面的问号

// state方式:history模式刷新不会丢失,hash模式刷新会丢失
<Link to={{pathname: '/home', state: {id: 'xxx'}}}></Link>
// 获取参数通过props.location.state
this.props.history.push({path: '/home', state: {id: ''}}) // 编程式
this.props.history.replace({path: '/home', state: {id: ''}}) // 编程式

history里的其他方法
this.props.history.go(-1)
this.props.history.goBack() // 后退
this.props.history.goForward() // 前进

// 一般组件使用路由的跳转,使用withRouter()
src/router/index.js
import React, { lazy } from "react";
const Home = lazy(() => import("../pages/home")); // 路由懒加载lazy,Suspense配合路由懒加载使用
const Login = lazy(() => import("../pages/login"));
const Error = lazy(() => import("../pages/error"));
// 无权限
export const commonRouter = [
    {
      path: "/login",
      component: Login,
      exact: true,
      meta: {
          title: "登录",
      }
    },
    {
      path: "/404",
      component: Error,
      exact: true,
      meta: {
          title: "404",
      }
    },
];
// 有权限
export const authRouter = [
    {
      path: "/",
      component: Home,
      exact: true,
      meta: {
          title: "首页",
      }
    },
];

src/app.jsx
import React, { Suspense } from "react";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import { commonRouter, authRouter} from "./router";
import Layout from "./layout";

const App = () => {
  return (
    <BrowserRouter>
        <Switch>
            // Suspense配合路由懒加载lazy,fallback里写组件
            <Suspense fallback={<>loading...</>}>
                // 无权限
                {
                   commonRouter.map(item => (<Route
                                   key={item.path}
                                   exact={item.exact}
                                   render={(props) => {
                                       document.title = item.meta.title;
                                       return (<item.component {...props} />)
                                   }}
                               />)
                   ) 
                }
                // 有权限
                {
                   authRouter.map(item => (<Route
                                   key={item.path}
                                   exact={item.exact}
                                   render={(props) => {
                                       document.title = item.meta.title;
                                       sessionStorage.getItem("token")
                                       ? <Redirect to="/login" />
                                       : <Layout><item.component {...props} /><Layout>
                                   }}
                               />)
                   )
                }
                <Redirect to="/404" />
            </Suspense>
        </Switch>
    </BrowserRouter>
  );
};

v6

  • Switch移除了,Routes代替,Route必须被Routes包裹,不然报错
  • Redirect移除了,Navigate重定向
  • useInRouterContext判断组件是否是包路由包裹,返回值true、false
  • useNavigationType返回当前的导航类型,返回值POP(刷新)、PUSH、REPLACE
  • useOutlet获取当前组件中渲染的嵌套路由
  • useResolvedPath给一个URL值,解析其中的path、search、hash值
<Route path='' element={<Lxh />} caseSensitive /> // component被element代替,caseSensitive区分path的大小写
<Route path='' element={<Navigate to='' />} />
<Navigate to='' replace></Navigate>

NavLink的高亮样式,yyy选中的样式,写法不一样了,子组件高亮,不希望父组件高亮,父组件加end属性
<NavLink className={({isActive}) => isActive ? 'xxx yyy' : 'xxx'}></NavLink>
const xxxClassName = ({isActive}) => isActive ? 'xxx yyy' : 'xxx';
<NavLink className={xxxClassName} end></NavLink>

import {useRoutes} from "react-router-dom"; // useRoutes路由表
useRoutes([
    {
        path: '/home/:id',
        element: <Home />,
    },
    {
        path: '/about',
        element: <About />,
        children: [
            {
                path: 'message', // 不写斜杠/
                element: < />,
            },
        ]
    },
    {
        path: '*',
        element: <Navigate to='/home' />,
    },
])
/**
 * <NavLink to='message'></NavLink> // 跳转子路由的写法
 * <NavLink to='./message'></NavLink> // 跳转子路由的写法
 * <Route index element={<Components />} /> 嵌套路由index是默认路由
 * Outlet指定路由组件呈现的位置
 */

路由传参的方式:3种
params方式
<Link to={`/home/${id}`}></Link>
// 需要声明Route或useRoutes里:id
// 获取参数使用hooks函数,useParams
import { useParams } from 'react-router-dom';
const { id } = useParams()

search方式:相当于(query)
<Link to={`/home?id={id}&title=${title}`}></Link>
// 不需要声明
// 获取参数useSearchParams
import { useSearchParams } from 'react-router-dom';
const [search, setSearch] = useSearchParams() // 返回一个数组
const id = search.get('id') // 通过get获取对应的参数值
setSearch('id=xxx&title=yyy') // 更新search的参数

state方式:
<Link to=`/home` state={{id: ''}}></Link>
// 不需要声明
// 获取参数useLocation
import { useLocation } from 'react-router-dom';
const { state: { id } } = useLocation(); // 解构赋值直接使用id

编程式路由导航
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
navigate('/home', {
    replace: true,
    state: {
        id: 'xxx',
    }
});
navigate(1) // 前进
navigate(-1) // 后退

用的不多的hooks函数
import { useMatch,
         useInRouterContext,
         useNavigationType,
         useOutlet,
         useResolvedPath,
         } from 'react-router-dom';
useMatch('/home/:id') // 必须传入路径
useInRouterContext() // 返回Boolean值,查看是否是Route包裹的组件
useNavigationType() // 返回PUSH、REPLACE、POP,查看当前的导航类型,刷新后就变成的POP
useOutlet() // 查看当前组件的嵌套路由,返回null说明还没有挂载
useResolvedPath('/user/#/asd?id=xxx&title=yyy') // 传入一个url值,返回path、hash、search值
src/router/index.js
import React, { lazy } from "react";
import { useRoutes, Navigate, Outlet } from "react-router-dom";

const Home = lazy(() => import("../pages/home"));
const Login = lazy(() => import("../pages/login"));
const Error = lazy(() => import("../pages/error"));

export default () => {
  return useRoutes([
    {
      path: "/login",
      element: <Login />,
    },
    {
      path: "/",
      element: <Home><Outlet /></Home>, // Outlet可写组件里
      children: [
        {
          path: "table",
          element: <Table />,
        },
      ],
    },
    {
      path: "/404",
      element: <Error />,
    },
    {
      path: "*",
      element: <Navigate to='/404' />,
    },
  ]);
};

src/app.jsx
import React, { Suspense } from "react";
import { BrowserRouter as Router } from "react-router-dom";
import RouterAll from "./router";

const App = () => {
  return (
    <Router>
      <Suspense fallback={<>loading...</>}>
        <RouterAll></RouterAll>
      </Suspense>
    </Router>
  );
};

export default App;

类式组件跳转

// tsx版
import React, { ComponentClass } from "react";
import {
  NavigateFunction,
  useLocation,
  useNavigate,
  useParams,
} from "react-router";

export interface IRouteProps<Params = any, State = any> {
  location: State;
  navigate: NavigateFunction;
  params: Params;
}

export function withRouter<P extends IRouteProps>(Child: ComponentClass<P>) {
  return (props: Omit<P, keyof IRouteProps>) => {
    const location = useLocation();
    const navigate = useNavigate();
    const params = useParams();
    return (
      <Child
        {...(props as P)}
        navigate={navigate}
        location={location}
        params={params}
      />
    );
  };
}

// 在组件里使用
import { withRouter, IRouteProps } from "../classRouter.tsx";

class Tags extends Component<IRouteProps> {
    render() {
        return <>
            <button onClick={() => {
                this.props.navigate('/home')
            }}>跳转</button>
        </>
    }
}

export default withRouter(Tags)

Fragment

import React, { Fragment } from "react";
// 循环遍历使用Fragment,它只有一个属性key
<Fragment key={1}></Fragment>
<></> // 不能接收任何属性,一般用在根标签

PureComponent、memo

PureComponent

 重写了:
        shouldComponentUpdate(nextProps, nextState) {
        return 布尔值
    }
    /**
     * 只有props和state数据改变了,才会返回true
     * 注意:只是进行了props和state数据的浅比较,如果只是数据对象内部数据改变了,返回false
     * 不要直接修改state数据,而是要产生新数据
     * 比如:state = {name: "lxh", arr: [1,2,3]}
     *      修改1:let obj = this.state
     *            obj.name = "LXH"
     *            this.setState(obj) // 这样会返回false
     *      修改2:let {arr} = this.state
     *            arr.push(4)
     *            this.setState({arr}) // 返回false
     *            this.setState({arr: [...arr, 4]}) // 返回ture
     *            // 不直接修改,产生新数据
     **/ 

memo

主要用于函数组件
import React, { memo } from "react";
const Child = () ={
    return (<div>child</div>)
}

export default memo(Child)

useCallbackusememo

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

useRef

Hook API 索引

错误边境

错误边境:只捕获后代组件生命周期产生的错误。
当子组件出现错误的时候,触发getDerivedStateFromError调用,并携带错误信息

state = {hasError: ""}
static getDerivedStateFromError(error) {
    // error是错误信息
    return {hasError: error}
}
render() {
    return (
        <>
            {this.hasError ? <>当前网络不稳定,请稍后再试</> : <Child />}
        </>
    )
}

注意:错误边境只适用于生产环境

componentDidCatch() {
    // 渲染组件时出错,执行
    // 主要用于统计页面的错误,发送请求给后台,反馈给服务器,通知编码人员进行bug的解决
}
使用方式:getDerivedStateFromError 配合 componentDidCatch

react+typescript写法

安装@types/下,如:npm i @types/react-router-dom

import React, { Component, createRef } from "react";

interface propsType {
  id?: string;
  [propsName: string]: any;
}
interface stateType {
  name: string;
}

// 第一个props属性约定类型,第二个state状态约定类型,不写占位可以使用any<any,any>
class Tags extends Component<propsType, stateType> {
  state = {
    name: "liaoxianhui",
  };
  refName = createRef<HTMLInputElement>();
  render() {
    return (
      <div>
        <input type="text" ref={this.refName} />
        <button
          onClick={() => {
            // as 断言 , ? 或者 ! 都可以三种方式
            console.log((this.refName.current as HTMLInputElement).value);
          }}
        >
          获取输入框的值
        </button>
      </div>
    );
  }
}

export default Tags;
import React, { FC, useRef, useState } from "react";

// 方法1:约定props里的属性类型
interface propsTypeOne {
  id?: string;
}

// 方法2
interface propsTypeTwo {
  id?: string;
  name?: () => void;
  [propName: string]: any;
}

// const Form = (props: propsTypeOne) => { // 方法1
const Form: FC<propsTypeTwo> = (props) => {
  // 方法2
  const [name, setName] = useState<string>("lxh");
  const refName = useRef<HTMLInputElement>(null);
  return (
    <div>
      <input type="text" ref={refName} />
      <button
        onClick={() => {
          // as 断言 , ? 或者 ! 都可以三种方式
          console.log(refName.current?.value);
        }}
      >
        获取输入框的值
      </button>
    </div>
  );
};

export default Form;

消息订阅与发布机制

插件PubSubJs

安装:npm install pubsub-js
     yarn add pubsub-js
     yarn add -D @types/pubsub-js // ts版本
     
引入:import PubSub from 'pubsub-js';

发布:PubSub.publish("xxx","data")

订阅:const yyy = PubSub.subscribe("xxx",(msg, data)=>{ // msg消息名、date数据
        console.log(date)
     })
     
取消订阅:PubSub.unsubscribe(yyy);  // 使用 yyy 取消单个订阅
         PubSub.unsubscribe('yyy');  // 取消所有对 'yyy' 的订阅

跨域

// http-proxy-middleware react下载好了
const { createProxyMiddleware } = require('http-proxy-middleware') // react18中使用
// const createProxyMiddleware = require('http-proxy-middleware') // react17中使用

module.exports = function (app) {
    app.use(
        createProxyMiddleware('/api1', { // 遇到/api1的请求,触发该代理
            target: 'http://loaclhost:5000', // 后端请求的基础路径
            changeOrigin: true, // 控制服务器收到的请求头中host字段的值
            pathRewrite: { // 去除请求前缀
                '^/api1': ''
            }
        })
    )

    app.use(
        createProxyMiddleware('/api2', {
            target: 'http://localhost:6000',
            changeOrigin: true,
            pathRewrite: {
                '/api2': ''
            }
        })
    )
}

serve

测试打包好的项目在静态服务器,打包好后会提示serve -s build

  • 安装:npm i -g serve
  • 使用:serve -s build
  • 使用指定端口号:serve -s build -l 4000

vue2

vue3

fetch

不定期更新,目前结束,谢谢