React
JavaScript的超集
TypeScript
- TS的定位是静态类型语言
- 在写代码阶段就能检查错误
- 类型系统是最好的文档
- 增加了代码的可读、维护性
- 有一定的学习成本
- 需要理解接口(Interfaces)、泛型(Generics)、类(classes)等
- 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/>)