【React+ts】 react项目中引入bootstrap、ts中的接口
一、在react项目中引入bootstrap
这个直接用npm下载包然后在index.js中引入就可以了。
npm install bootstrap react-bootstrap
后面那个必须要下载,应该有什么联动的包要用。
然后在index.tsx中引入
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap";
二、ts中的interface接口
2.1 对象类型接口
ts中的interface是用来描述对象或函数的。是一种代码协作必须遵守的契约。我的理解就是规范里面的数据,比如说,在实例化的时候哪些东西必须要,哪些东西可以不要,以及哪些东西可以任意类型。
里面的数据可以定义以下几种类型。
- 确定类型:必须要有的数据。
- 可选属性:可以没有的属性。
- 任意属性:说明属性类型可以更改,名字可定义。
- 只读属性:实例化后不可更改。
interface Person {
age: number, // 确定属性
name?: string, // 可选属性(加问号即可)
[propName: string]: any, // 任意属性
readonly sex: string, // 只读属性
}
2.2 函数类型接口
列出参数列表和返回值类型的定义。
// 函数类型接口
interface Func {
// 定义接收两个必选 number 类型参数,和一个可选字符串参数 desc,该函数返回 number 类型
(x: number, y: number, desc?: string): number
}
三、react中定义组件的方式
有两种定义组件方式,一种是函数式组件,一种是类式组件。
3.1函数式组件
//基本定义方式
import React from "react";
export default function Discovery() {
return <div>发现页</div>;
}
//组件传参数
import React from "react";
export default function Discovery(props: any) {
console.log(props);
return <div>{props.text}</div>;
}
//类型定义,说明是个函数式组件也可以简写FC
import React, { memo } from "react”;
Import type {FunctionComponent} from "react”;
const Discovery: FunctionComponent<any> = (props) => {
console.log(props);
return <div>{props.text}</div>;
};
//memo函数的作用是判断组件是否发生变化,如果没有发生变化则不重新渲染
export default memo(Discovery);
3.2 类式组件
//基本定义方式
import React, { Component } from "react";
class Discovery extends Component {
render() {
return <div>发现页</div>;
}
}
export default Discovery;
//类式组件传参
import React, { Component } from "react”;
//这里两个any定义的一个是props type,一个是state type
class Discovery extends Component<any, any> {
constructor(props: any) {
super(props);
console.log(props);
this.state = { text: props.text };
}
render() {
return <div>{this.props.text}</div>;
}
}
export default Discovery;
3.3interface与类式组件的互动
ReactNode和ReactElement 与 children属性
这里的ReactNode包含了ReactElement,如下图所示。
InterfaceTest.tsx
import React, { ReactNode } from "react";
interface Iprops {
name: string;
age: number;
children: ReactNode;
height?: number;
}
export default function InterfaceTest(props: Iprops) {
return (
<div>
<div>name:{props.name}</div>
<div>age:{props.age}</div>
{/* 三目运算符来条件渲染 */}
{props.height ? <div>height:{props.height}</div> : null}
<div>{props.children}</div>
</div>
);
}
Discovery.tsx
import React, { Component } from "react";
import InterfaceTest from "./InterfaceTest";
class Discovery extends Component<any, any> {
constructor(props: any) {
super(props);
console.log(props);
this.state = { text: props.text };
}
render() {
return (
<div>
{this.props.text}
<InterfaceTest name="Carling" age={21} height={187}>
<div>这里是子元素</div>
</InterfaceTest>
</div>
);
}
}
export default Discovery;
3.4代码中遇到的问题
(1)如果是类组件,子组件是否重新渲染由props和state决定;如果子组件是函数式组件,那么只要父组件渲染,子组件就会无条件进行渲染。
answer:使用memo函数对组件进行包裹。这个方法只能解决子组件属性不是函数时的情况,如果子组件是函数,则需要使用useCallback进行包裹。
这个问题出现的原因是因为,函数式组件每次重新渲染时,都会把函数体里的所有代码执行一遍。
useCallback函数
let newFunction = useCallback(oldFunction,[dependentValue])
我的理解是只有当后面的依赖值发生变化时,前面的函数才会被运行。否则返回一样的值。
因此如果传的是空数组则oldFunction只会被定义一次。
export default () => {
console.log("父组件");
const [count, setCount] = useState(1);
let changeCount = () => {
setCount(count + 1);
}
let increment = useCallback(()=>{
console.log("increment");
},[]) // 该函数永远不会重新定义(没有依赖)
return (
<>
<h1>useCallback</h1>
<p>{count}</p>
<input type="button" value="修改count" onClick={changeCount} />
<hr/>
<SonFn onMyClick={increment} />
</>
)
}
四、组件的懒加载–webpack中的分包处理,性能优化问题–哪个需要查看下载哪个。
这个的原理就是将所有组件拆分,不放在一个文件里,哪个需要被加载去服务器下载哪个文件。
这里使用的是react库中的lazy函数,用来引入组件。
路由表定义:
import React, { lazy } from "react";
import { Navigate } from "react-router-dom";
//懒加载定义
const Discovery = lazy(() => import("../components/Discovery"));
const My = lazy(() => import("../components/My"));
const Follow = lazy(() => import("../components/Follow"));
const Mall = lazy(() => import("../components/Mall"));
const Musician = lazy(() => import("../components/Musician"));
const Recommended = lazy(() => import("../components/Recommended"));
const Download = lazy(() => import("../components/Download"));
const routes: any[] = [
{
path: "/discovery",
element: <Discovery text="发现页" />,
},
{
path: "/my",
element: <My />,
},
{
path: "follow",
element: <Follow />,
},
{
path: "mall",
element: <Mall />,
},
{
path: "musician",
element: <Musician />,
},
{
path: "recommended",
element: <Recommended />,
},
{
path: "download",
element: <Download />,
},
{
path: "/",
element: <Navigate to="/discovery" />,
},
];
export default routes;
使用路由表的时候要用Suspense组件进行包裹,Suspense组件是通过捕获异常来进行实现的,没看之前猜测是,捕获到异常就暂停该组件的渲染,等到完成再渲染。
工作原理:
使用React的Fiber架构进行实现。Fiber架构允许React将渲染工作拆分为一个个可中断的任务单元。当遇到需要暂停的操作时,Fiber可暂停当前任务,并显示fallback—Suspense组件中定义的在等待时渲染的过渡组件。
- 当组件抛出异常时,React标记该Fiber节点为“suspended”状态
- 触发重新渲染,一旦该Promise完成,React会尝试重新渲染组件。
- 等待Promise完成的期间会渲染fallback。
用Suspense组件将所有懒加载的组件全包裹了就可以。
import React, { Suspense } from "react";
import routes from "./router/index";
import { NavLink, useRoutes } from "react-router-dom";
function App() {
const element = useRoutes(routes);
return (
<div className="App">
<nav className="navbar navbar-dark bg-primary">
<div className="container-fluid">
<NavLink className="navbar-brand" to="/discovery">
发现音乐
</NavLink>
<NavLink className="navbar-brand" to="/my">
我的音乐
</NavLink>
<NavLink className="navbar-brand" to="/follow">
关注
</NavLink>
<NavLink className="navbar-brand" to="/mall">
商城
</NavLink>
<NavLink className="navbar-brand" to="/musician">
音乐人
</NavLink>
<NavLink className="navbar-brand" to="/recommended">
云推歌
</NavLink>
<NavLink className="navbar-brand" to="/download">
下载客户端
</NavLink>
</div>
</nav>
<Suspense fallback="loading…">
{element}
</Suspense>
</div>
);
}
export default App;