当前
React版本为17.0.2,课程源自慕课网React 17 系统精讲 结合TS打造旅游电商平台 轻松掌握大厂前端核心技能
全局安装React脚手架
npm install -g create-react-app
初始化React项目
- npx create-react-app my-app
- cd my-app
- npm start
React暴露webpack配置
npm run eject / yarn eject
React默认把webpack相关的配置隐藏起来,如果需要更改webpack配置,需要执行相关命令。并且,暴露webpack配置的操作为不可逆(即暴露之后不可隐藏回去)。
初始化React-TypeScript项目
npx create-react-app react-ts-demo -template typescript
react-ts-demo为项目名称
React-JavaScript迁移为React-TypeScript
安装以下npm包,成功之后会生成ts.config.js文件。
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
配置React的CSS模组
一般情况下,
css文件与component文件放在同一个目录下,并且使用同样的命名方式。
1.
// 直接引入整个css文件 (容易有全局样式污染)
import './index.css'
<div className="app" />
2.
// css模块化引入组件
import style from './index.css'
<div className={style.app} />
css模组化引入需要额外的ts配置
在
src路径下新建custom.d.ts文件,写进以下代码:
declare module "*.css" {
const css: { [key: string]: string };
export default css;
}
- *.d.ts (.d.ts文件会自动被ts识别)
- 只包含类型声明 不包含逻辑
- 不会被编译 也不会被webpack打包
既然配置了css作为模块加载,那么对应的css文件名也应该以module为后缀。需要将css文件名都改为xx.module.css,同时,在引入的位置也需要更新一下。
这种命名规则是
css模块化的约定规范,配合typesciprt最好使用这种命名约定,否则,等会可能会出现一些意外。
vscode添加css模组化智能提示
- npm i typescript-plugin-css-modules --save-dev
- 在tsconfig.json中加入配置
"plugins": [{"name": "typescript-plugin-css-modules"}]
- 在项目根文件下新增
.vscode文件夹,在文件夹中新增settings.json文件,加入以下配置
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
React支持模块化引入的媒体资源类型
在
/node_modules/react-script/lib/ract-app.d.ts文件中,声明了react支持import的文件类型。
State与Props
区别:
props是组件对外的接口,state是组件对内的接口props是用于组件间数据传递,state是用于组件内部数据传递
State:
- 类组件中
constructor是唯一可以初始化state的地方 - 不可直接修改
state,必须通过setState修改state中的属性的值 setState的更新是异步的
Props:
props是只读的
React事件绑定绑定this指向的方法
- 在构造函数中绑定
constructor() {
this.handleClick = this.handleClick.bind(this);
}
- 在
onClick事件中使用箭头函数绑定
onClick={() => this.handleClick()}
- 在
onClick中使用bind绑定
onClick={this.handleClick.bind(this)}
- 在
class初始化时将事件函数定义为箭头函数
handleClick = () => {
console.log('this is:', this);
}
onClick={this.handleClick}
TS省略any类型声明的小技巧
在
tsconfig.json中添加以下配置
"noImplicitAny": false
对于setState异步更新和同步执行的理解
setState本身并不是异步,只是因为react的性能优化机制体现为异步。在react的生命周期函数或者作用域下为异步,在原生的环境下为同步。
React常用生命周期
- componentDidMount
- componentDidUpdate
- componentWillUnmount
Hook
HookReact 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
useState:来自官网的demo
// 引入 React 中的 `useState` Hook
import React, { useState } from 'react';
function Example() {
// 声明一个新的叫做 count 的 state 变量,初始值为0 ,以及一个更新 count 的方法 setCount
// useState 返回一个数组,采用数组解构的方式赋值给了 count ,setCount
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useEffect:
- 可以模拟
componentDidMount、componentDidUpdate、componentWillUnmount这三个生命周期函数的使用场景
// 模拟 componentDidMount,第二个参数传 空数组 ,ui更新之后便执行回调方法
useEffect(() => {
document.title = `点击${count}次`
}, [])
// 模拟 componentDidUpdate,第二个参数为数组,数组元素为需要追踪的 state 属性
// 一旦数组中有某个元素更新了,便会执行回调方法。
useEffect(() => {
document.title = `点击${count}次`
}, [count])
// 模拟 componentWillUnmount,在 useEffect 第一个参数中 return 一个方法。
useEffect(() => {
document.title = `点击${count}次`
return () => {
// 执行 useEffect 的清除逻辑
}
}, [count])
useEffect如果不传第二个参数会导致死循环,一直更新。
解决方法:第二个参数传一个空数组
useEffect不接受返回一个promise,若要使用async,await,则需要把代码做一下包装:
const [robotGallery, setRobotGallery] = useState<any>([])
const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<string>()
useEffect(() => {
const fetcnData = async () => {
setLoading(true)
try {
const res = await fetch("https://jsonplaceholder.typicode.com/users")
const data = await res.json()
setRobotGallery(data)
} catch (error: any) {
setError(error.message)
}
setLoading(false)
}
// 手动调用 fetcnData
fetcnData()
}, [])
Context与useContext
Context提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
// grandpa 组件
// 定义一个默认值
const defaultContextValue = {
username: '菜菜菜菜'
}
// 调用 createContext 创建一个 Context
export const appContext = React.createContext(defaultContextValue)
ReactDOM.render(
<React.StrictMode>
{/* 使用创建的 appContext.Provider 包裹住要接收值的组件 */}
<appContext.Provider value={defaultContextValue}>
<App />
</appContext.Provider>
</React.StrictMode>,
document.getElementById('root')
);
// father 组件不需操作
// grandson 组件
// 引入暴露出来的 appContext
import { appContext } from '../index'
// 使用 appContext.Consumer 包裹住要接收值的 jsx 代码
// 用 花括号 和 箭头函数 包裹住 jsx 代码
// { (value) = (jsx代码) }
const Robot : React.FC<RobotProps> = ({id, name, email}) => {
return (
<appContext.Consumer>
{(value) => (
<div className={styles.cardContainer}>
<img alt='robot' src={`https://robohash.org/${id}`} />
<h2>{name}</h2>
<p>{email}</p>
<p>作者:{value.username}</p>
</div>
)}
</appContext.Consumer>
)
}
使用
hooks的useContext可以使我们写法更加简洁
// 引入 useContext
import React, { useContext } from 'react';
const Robot : React.FC<RobotProps> = ({id, name, email}) => {
// 调用 useContext
const value = useContext(appContext)
return (
<div className={styles.cardContainer}>
<img alt='robot' src={`https://robohash.org/${id}`} />
<h2>{name}</h2>
<p>{email}</p>
{/* 接收 context 的值 */}
<p>作者:{value.username}</p>
</div>
)
}
高阶组件HOC
- 抽取重复代码,实现组件复用
- 条件渲染,控制组件的渲染逻辑(渲染劫持)
- 捕获/劫持被处理组件的声明周期
自定义Hook
Hook是函数- 命名以
use开头 - 内部可调用其他
Hook函数 - 并非
React的特性
React电商项目实战
项目初始化
// 脚手架初始化 react + ts 项目
npx create-react-app react-mall -template typescript
// 安装开发环境依赖包,css模块化引入
npm i typescript-plugin-css-modules --save-dev
// tsconfig.json 添加配置
"noImplicitAny": false, // 不声明 any 类型不报错
"plugins": [ // css模块化插件
{"name": "typescript-plugin-css-modules"}
]
// 根目录添加.vscode文件夹,文件夹中新建settings.json,添加以下配置
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
模块化引用css
- 将
App.css改为App.module.css - 在
App.tsx中修改为模块化引入,并修改使用位置
import styles from './App.module.css';
function App() {
return (
<div className={styles.App}>
<header className={styles["App-header"]}>
<img src={logo} className={styles["App-logo"]} alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
安装antd、@ant-design/icons组件库
npm i antd @ant-design/icons --save
全局引入antd-css文件遇到的问题
当
"react-scripts": "5.0.0"的版本为5.0.0时,按照antd官网引入css文件会报警告。该问题在ant-design的issue中有人提及,传送门,报错如下图所示:
WARNING in ./node_modules/antd/dist/antd.css (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[2]!./node_modules/source-map-loader/dist/cjs.js!./node_modules/antd/dist/antd.css)
Module Warning (from ./node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map: 'webpack://antd/./components/icon/style/index.less' URL is not supported
@ ./node_modules/antd/dist/antd.css 8:6-231 22:17-24 26:7-21 58:25-39 59:36-47 59:50-64 61:4-74:5 63:6-73:7 64:54-65 64:68-82 70:42-53 70:56-70 72:21-28 83:0-201 83:0-201 84:22-29 84:33-47 84:50-64
@ ./src/index.tsx 8:0-28
解决方法:用
import 'antd/dist/antd.min.css'代替import 'antd/dist/antd.css'。
模块化引入css如何在一个元素中添加两个类名
// 第一种
<div className=`${styles.['app-header']} ${styles['app-logo']}`></div>
// 第二种(推荐):https://www.npmjs.com/package/classnames
npm install classnames