React

JavaScript的超集

TypeScript

  1. TS的定位是静态类型语言
    • 在写代码阶段就能检查错误
  2. 类型系统是最好的文档
    • 增加了代码的可读、维护性
  3. 有一定的学习成本
    • 需要理解接口(Interfaces)、泛型(Generics)、类(classes)等
  4. TS最后被编译成JS

代码示例:

// src/test/Test01.tsx

import React, {Component} from 'react';

let name01: string = 'name01'  // 变量的值只能是字符串类型
let name02: string | number = 721  // 两种类型
let name03: any = true  // 任何类型

let list01: string[] = ['1', '2', '3']  // 数组的元素只能是字符串类型
let list02: (string | number)[] = ['1', 2, '3']  // 两种类型
let list03: any[] = [true, '1', 2]  // 任何类型
let list04: Array<string> = ['a', 'b', 'c']  // 泛型写法
let list05: Array<string | number> = [1, '2', 3]

interface Iobj01 {  // 接口:首字母大写且为I(规范)
    name: string,
    age: number,
    location?: string,  // ?:可选属性
    [propName: string]: any  // 其余不需要用到的属性会被归类
}

let obj01: Iobj01 = {  // 指定接口(遵循指定的数据类型,必须与接口一一对应,多了和少了都会报错)
    name: 'HQSY',
    age: 24
}

interface Ifunc {
    (args: string, kwargs: string, foo?: number): string
}

function func01(args: string, kwargs: string, foo?: number): string {  // 指定返回值类型
    console.log(args.substring(0, 1) + kwargs.substring(0, 1))
    return args.substring(0, 1) + kwargs.substring(0, 1)
}

let myName: string = func01('aaa', 'bbb')

let myFunc02: Ifunc = function func02(args, kwargs, foo) {
    console.log(args.substring(0, 1) + kwargs.substring(0, 1))
    return args.substring(0, 1) + kwargs.substring(0, 1)
}

interface Iobj02 {
    name: string,
    age: number,
    getName: (myName: string) => string  // 函数接口
}

let obj02: Iobj02 = {
    name: 'name',
    age: 24,
    getName: myName => myName
}

interface Iinit {
    dispatch: () => any
}

class Bus implements Iinit {  // 应用接口
    _list: any = []  // 下划线:规范上的不允许外界访问
    private _my: any = []  // 私有化(强制不允许外界访问,子组件也一样)
    public name: any = 'HQSY'  // 公有属性(谁都可以访问)
    protected age: any = 114514  // 只能自己和子组件使用

    public subscribe(cb: any) {  // 这里public写与不写都一样
        this._list.push(cb)
    }

    public dispatch() {
        this._list.forEach((cb: any) => {
            cb && cb()
        })
    }
}

function init(obj: Iinit) {
    obj && obj.dispatch()
}

let objA = new Bus()
init(objA)

export default class Test01 extends Component {
    render() {
        return (
            <div>
                HelloWorld!
            </div>
        );
    }
}
// src/test/Test02.tsx

import React, {Component} from 'react';

interface Ichild {
    name: string
}

class Child extends Component<Ichild, any> {
    render() {
        return (
            <div>
                {this.props.name}
            </div>
        )
    }
}

interface Istate {
    list: Array<string>
}

export default class Test02 extends Component<any, Istate> {  // 第一个约定属性,第二个约定状态
    state = {
        list: []
    }
    myRef = React.createRef<HTMLInputElement>()

    render() {
        return (
            <div>
                <input type="text" ref={this.myRef}/>
                <button onClick={() => {
                    console.log((this.myRef.current as HTMLInputElement).value)  // 断言一定是指定数据类型的
                    this.setState({
                        list: [...this.state.list, (this.myRef.current as HTMLInputElement).value]
                    })
                }}>Click
                </button>
                {
                    this.state.list.map(item => <li key={item}>
                        {item}
                    </li>)
                }
                <Child name={'name'}/>
            </div>
        );
    }
}
// src/test/Test03.tsx

import React, {Component} from 'react';

interface Iprops {
    title: string,
    cb: () => void  // 不需要返回值
}

class Navbar extends Component<Iprops, any> {
    render() {
        return (
            <div>
                {this.props.title}
                <button onClick={() => {
                    this.props.cb()
                }
                }>Click
                </button>
            </div>
        )
    }
}

class Sidebar extends Component<any, any> {
    render() {
        return (
            <div>
                Sidebar
            </div>
        )
    }
}

interface Istate {
    isShow: boolean
}

export default class Test03 extends Component<any, Istate> {
    state = {
        isShow: true
    }

    render() {
        return (
            <div>
                <Navbar title={'首页'} cb={() => {
                    this.setState({
                        isShow: false
                    })
                }
                }/>
                {this.state.isShow && <Sidebar/>}
            </div>
        );
    }
}
// src/test/Test04.tsx

import React, {useRef, useState} from "react";

interface Iprops {
    name: string
}

function Child(props: Iprops) {
    return (
        <div>
            {props.name}
        </div>
    )
}

// 第二种写法
// const Child:React.FC<Iprops> = (props: Iprops) => {
//     return (
//         <div>
//             {props.name}
//         </div>
//     )
// }

interface ISidebarProps {
    show: () => void
}
function Sidebar(props:ISidebarProps) {
    return (
        <div>
            <button onClick={() => {
                props.show()
            }}>Sidebar-Click</button>
        </div>
    )
}

export default function Test04() {
    const [isShow, setIsShow] = useState<boolean>(true)
    const [state, setState] = useState<string>('aaa')
    const myText = useRef<HTMLInputElement>(null)  // 必须给定初始值null
    const [list, setList] = useState<string[]>([])

    return (
        <div>
            {state.substring(0, 1).toUpperCase() + state.substring(1)}
            <button onClick={() => {
                setState('bbb')
            }}>Click
            </button>
            <input type="text" ref={myText}/>
            <button onClick={() => {
                console.log((myText.current as HTMLInputElement).value)
                setList([...list, (myText.current as HTMLInputElement).value])
            }}>Click
            </button>
            {
                list.map(item => <li key={item}>{item}</li>)
            }
            {isShow && <Child name={'name'}/>}
            <Sidebar show={() => {
                setIsShow(false)
            }}/>
        </div>
    )
}
// src/components/Redirect.tsx

import {useEffect} from "react";
import {useNavigate} from "react-router-dom";

interface IRedirect {
    to: string
}
export default function Redirect({to}:IRedirect) {
    const navigate = useNavigate()

    useEffect(() => {
        navigate(to, {replace: true})
    })

    return null
}
// src/components/Tabber.tsx

import React, {useEffect, useState} from "react";
import store from "../redux/store";

export default function Tabber() {
    const [state, setState] = useState({
        isShow: (store.getState() as any).isShow
    })

    useEffect(() => {
        store.subscribe(() => {
            console.log((store.getState() as any).isShow)
            setState({
                isShow: (store.getState() as any).isShow
            })
        })
    }, [])

    return (
        <div>
            {
                state.isShow && <ul>
                    <li>首页</li>
                    <li>内容</li>
                    <li>我的</li>
                </ul>
            }
        </div>
    )
}
// src/redux/store.ts

import {legacy_createStore as createStore} from 'redux'

interface Iaction {
    type: string,
    payload?: any
}

interface IprevState {
    isShow: boolean
}

const reducer: any = (prevState: IprevState = {
    isShow: true
}, action: Iaction) => {
    const {type} = action
    const newState = {...prevState}

    switch (type) {
        case 'show':
            newState.isShow = true
            return newState
        case 'hide':
            newState.isShow = false
            return newState
        default:
            return newState
    }
}

const store = createStore(reducer)

export default store
// src/router/index.tsx

import React from "react";
import {useRoutes} from "react-router-dom";
import Redirect from '../components/Redirect'

const LazyLoad = (path: string) => {
    const Comp = React.lazy(() => import(`../${path}`))

    return (
        <React.Suspense fallback={<>加载ing...</>}>
            <Comp/>
        </React.Suspense>
    )
}

export default function Mrouter() {
    const element = useRoutes([
        {
            path: '/films',
            element: LazyLoad('views/Films')
        },
        {
            path: '/centers',
            element: LazyLoad('views/Centers')
        },
        {
            path: '/cinemas',
            element: LazyLoad('views/Cinemas')
        },
        {
            path: '/detail/:id',
            element: LazyLoad('views/Detail')
        },
        {
            path: '/',
            element: <Redirect to={'/films'}/>
        },
        {
            path: '*',
            element: LazyLoad('views/NotFound')
        }
    ])

    return (
        element
    )
}
// src/views/Centers.tsx

import React from "react";

export default function Centers() {
    return (
        <div>
            Centers
        </div>
    )
}
// src/views/Cinemas.tsx

import React from "react";

export default function Cinemas() {
    return (
        <div>
            Cinemas
        </div>
    )
}
// src/views/Detail.tsx

import React, {useEffect} from "react";
import {useParams} from "react-router-dom";
import store from "../redux/store";

export default function Detail() {
    const params = useParams()

    useEffect(() => {
        console.log(params.id)
        store.dispatch({
            type: 'hide'
        })
        return () => {
            store.dispatch({
                type: 'show'
            })
        }
    }, [params])

    return (
        <div>
            Detail
        </div>
    )
}
// src/views/Films.tsx

import React, {useEffect, useRef, useState} from "react";
import axios from "axios";
import {useNavigate} from "react-router-dom";
import {Swiper, Button} from "antd-mobile";
import { SwiperRef } from 'antd-mobile/es/components/swiper'

interface Interface {
    filmId: number,
    name: string,
    poster: string
}
export default function Films() {  // 预置接口
    const [state, setState] = useState({
        list: [{
            filmId: 1,
            name: '',
            poster: ''
        }]
    })
    const navigate = useNavigate()
    const Ref = useRef<SwiperRef>(null)

    useEffect(() => {
        axios.get(`/test.json`).then(res => {
            console.log(res.data.films)
            setState({
                list: res.data.films
            })
        }).catch(err => console.log(err))
    }, [])

    return (
        <div>
            <Swiper autoplay={true} loop={true} ref={Ref}>
                {
                    state.list.map((item: Interface) =>
                        <Swiper.Item key={item.filmId}>
                            <img src={item.poster} alt={item.name}
                                 style={{width: '100%', height: '200px'}}/>
                        </Swiper.Item>
                    )
                }
            </Swiper>
            <Button color={'danger'} onClick={() => {
                Ref.current?.swipePrev()  // 如果前面为假那么后面就不会执行,反之则执行
            }}>上一个</Button>
            <Button color={'primary'} onClick={() => {
                Ref.current?.swipeNext()
            }}>下一个</Button>
            <ul>
                {
                    state.list.map((item: Interface) => <li key={item.filmId} onClick={() => {
                        navigate(`/detail/${item.filmId}`)
                    }}>
                        {item.name}
                    </li>)
                }
            </ul>
        </div>
    )
}
// src/views/NotFound.tsx

import React from "react";

export default function NotFound() {
    return (
        <div>
            404
        </div>
    )
}
// src/App.tsx

import React from "react";
import {BrowserRouter} from 'react-router-dom'
import Mrouter from "./router";
import Tabber from "./components/Tabber";

export default function App() {
    return (
        <BrowserRouter>
            <Mrouter/>
            <Tabber/>
        </BrowserRouter>
    )
}
// src/index.tsx

import React from "react";
import {createRoot} from "react-dom/client";
import App from './App'

const root: any = document.getElementById('root')
createRoot(root).render(<App/>)