React特点:

  1. 单向数据流,单向渲染
  2. 虚拟DOM
  3. 组件化

Robot-Gallery小项目

创建 npx create-react-app robot-gallery --template typescript
将函数组件类型设置为React.FC, 可见其传入的泛型为P
image.png
通过泛型设置props的类型

  1. interface RobotProp {
  2. id: number
  3. name: string
  4. email: string
  5. }
  6. const Robot: React.FC<RobotProp> = (props) => {
  7. return <></>
  8. }

一个随机获取机器人头像的网站https://robohash.org/:id

  1. <img src={`https://robohash.org/${id}`} alt="robot" />

导入json文件

  1. import robots from './mockdata/robots.json'

CSS模块化

将css文件与tsx文件放在相同目录下,css文件以.module.css格式结尾
安装typescript-plugin-css-modules

  1. npm install typescript-plugin-css-modules --save-dev

在tsconfig.json中添加配置

  1. {
  2. "compilerOptions": {
  3. // ...
  4. "plugins":[
  5. {
  6. "name":"typescript-plugin-css-modules"
  7. }
  8. ]
  9. },
  10. // ...
  11. }

在项目根目录新建.vscode目录,新建settings.json文件,写入下列内容,让vscode能提示css对象

  1. {
  2. "typescript.tsdk": "node_modules/typescript/lib",
  3. "typescript.enablePromptUseWorkspaceTsdk": true
  4. }

如果你的typescript不能识别.css文件,需要新建custom.d.ts文件,写入下列内容
(create-react-app已经写好了.module.css的声明,所以用.module.css文件就行)

  1. declare module "*.css" {
  2. const css: {[key:string]:string};
  3. export default css;
  4. }

.d.ts文件只包含类型声明,不包含逻辑,不会被编译,也不会被webpack打包

模块化引入css

  1. import style from './index.css'
  2. <div className={style.app} />

直接引入css文件

  1. import './index.css'
  2. <div className="app" />

加载媒体资源与字体资源

媒体资源和字体资源(fonts/images/icons)都放在assets目录中
引入自定义字体
将.ttf字体文件放入fonts目录, 在index.css中引入字体

  1. @font-face {
  2. font-family: 'Slidefu';
  3. src: local('Slidefu'), url(./assets/fonts/Slidefu-Regular-2.ttf) format('truetype');
  4. }

使用字体

  1. h1 {
  2. font-family: 'Slidefu';
  3. font-size: 72px;
  4. }

class组件

  1. import React from 'react'
  2. import styles from './ShoppingCart.module.css'
  3. interface Props {}
  4. interface State {
  5. isOpen: Boolean
  6. }
  7. export class ShoppingCart extends React.Component<Props, State> {
  8. constructor(props: Props) {
  9. super(props)
  10. this.state = {
  11. isOpen: false,
  12. }
  13. }
  14. render() {
  15. return (
  16. <div className={styles.cartContainer}>
  17. <button
  18. className={styles.button}
  19. onClick={() => {
  20. this.setState({ isOpen: !this.state.isOpen })
  21. }}
  22. >
  23. 购物车(2件)
  24. </button>
  25. <div
  26. className={styles.cartDropDown}
  27. style={{ display: this.state.isOpen ? 'block' : 'none' }}
  28. >
  29. <ul>
  30. <li>robot 1</li>
  31. <li>robot 2</li>
  32. </ul>
  33. </div>
  34. </div>
  35. )
  36. }
  37. }

class组件需要import React
state只能在constructor中初始化
props是只读的immutable
Component的泛型如下
image.png

使用React-Icons添加购物车图标

react icons 官网
安装 yarn add react-icons
使用(可以通过size属性指定大小)

  1. import { FiShoppingCart } from 'react-icons/fi'
  2. class Xxx extends React.Component {
  3. render() {
  4. return <FiShoppingCart size="10px" />
  5. }
  6. }

事件处理

如果在onClick里直接传递event参数,可以看到React自动绑定了事件类型
image.png
把事件处理函数移到外面则不知道事件类型,将鼠标移至onClick上可推断事件类型
image.png
React会强制把this变成undefined

  1. handleClick(e: React.MouseEvent<HTMLButtonElement>) {
  2. console.log(this) // undefined
  3. this.setState({ isOpen: !this.state.isOpen })
  4. }

解决办法:

  1. 在onClick中bind this

    1. onClick={this.handleClick.bind(this)}
  2. 使用箭头函数

    1. handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    2. console.log(this)
    3. this.setState({ isOpen: !this.state.isOpen })
    4. }

    event.target V.S. event.currentTarget

  • **event.target** —— 引发事件的层级最深的元素。
  • **event.currentTarget**(=**this**)—— 处理事件的当前元素(绑定了事件处理程序的元素)

    1. handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    2. if((e.target as HTMLElement).nodeName === 'SPAN') {
    3. this.setState({ isOpen: !this.state.isOpen })
    4. }
    5. }

    异步处理-获取api数据

    使用https://jsonplaceholder.typicode.com/提供的假数据

    1. componentDidMount() {
    2. fetch('https://jsonplaceholder.typicode.com/users')
    3. .then((response) => response.json())
    4. .then((data) => {
    5. this.setState({ robotGallery: data })
    6. })
    7. }

    对于从api获取的数据,定义成any类型是没有问题的,因为返回的数据具有不确定性,
    不能为了使用Type而放弃JavaScript的灵活性

    1. interface State {
    2. robotGallery: any[]
    3. }

    setState是异步的还是同步的?

    React中的setState是同步执行,异步更新
    React不会每次setState都更新,这样太损耗性能,而会将多个setState的调用合并为一个来执行

    1. <button
    2. onClick={() => {
    3. this.setState({ count: this.state.count + 1 })
    4. this.setState({ count: this.state.count + 1 })
    5. this.setState({ count: this.state.count + 1 })
    6. this.setState({ count: this.state.count + 1 })
    7. this.setState({ count: this.state.count + 1 })
    8. console.log(this.state.count)
    9. }}
    10. >
    11. {this.state.count}
    12. </button>

    上面的代码中count只会更新一次
    setState接受第二个参数,在第二个参数中可以获取到更新后的state

    1. onClick={() => {
    2. this.setState({ count: this.state.count + 1 }, () =>
    3. console.log(this.state.count)
    4. )
    5. }}

    第一个参数使用回调函数,函数的第一次参数是上一个state, 由此可以使state即时更新

    1. onClick={() => {
    2. this.setState((preState, preProps) => {
    3. return { count: preState.count + 1 }
    4. })
    5. this.setState((preState, preProps) => {
    6. return { count: preState.count + 1 }
    7. })
    8. this.setState((preState, preProps) => {
    9. return { count: preState.count + 1 }
    10. })
    11. }}

    React生命周期

  • mounting: 创建虚拟DOM,渲染UI

  • Updating: 更新虚拟DOM,重新渲染UI
  • Unmounting: 删除虚拟DOM, 移除UI ```jsx // Mounting componentDidMount(){}

// Updating componentWillReceiveProps // 已废弃 state getDerivedStateFromProps(nextProps, prevState){} shouldComponentUpdate(nextProps, nextState){} componentDidUpdate(){}

// Unmounting componentWillUnmount(){} ```