zoukankan      html  css  js  c++  java
  • 使用React+TypeScript构建自己的组件库

    TypeScript基础

    数据类型

    ECMAScript标准定义了8种数据类型

    1. Boolean
    2. Null
    3. Undefined
    4. Number
    5. BigInt
    6. String
    7. Symbol
    8. Object

    interface接口

    interface的主要作用如下:

    1. 对对象的形状(shape)进行描述
    2. 对类(class)进行抽象
    3. Duck Typing(鸭子类型)
    interface Person {
      readonly id: number;
      name: string;
      age?: number;
    }
    

    函数表达式

    const add: (x: number, y: number, z: number) => number = function (x, y, z): number {
      return x + y + z;
    }
    

    面向对象的三大特性:封装,继承,多态

    • 封装:当我们使用类中的某个方法时,我们无需知道类中的具体实现细节
    • 继承:子类可以继承父类,让子类具有父类的属性以及方法
    • 多态:子类可以覆盖父类中的方法,从而让子类和父类的实例表现出不同的特性
    class Animal {
      name: string;
    
      constructor(name: string) {
        this.name = name;
      }
    
      run() {
        console.log(`${this.name} is running`)
      }
    }
    
    class Cat extends Animal {
      constructor(name: string) {
        super(name);
        // 重写父类的构造方法,首先需要使用super调用父类的构造函数
        console.log(this.name);
      }
    
      run () {
        console.log('miao~');
        // 当在子类中需要调用父类的方法时,也可以使用super来调用
        super.run();
      }
    }
    

    类中属性或者方法的访问修饰符

    • private: 仅仅能够在当前类的内部访问,在子类或者实例中都无法访问
    • protected: 可以在当前类内部和子类中访问,无法在实例中访问
    • public: 默认的访问修饰符,能够在类,子类,实例中都可以访问到
    • readonly: 用来修饰类中不可变的属性
    • static: 可以直接用类名来访问其修饰的属性和方法

    接口interface

    当多个class需要都需要实现某些相同的方法时,我们可以使用interface来实现

    interface Radio {
      switchRadio() :void;
    }
    
    class Car implements Radio {
      switchRadio() {}
    }
    
    class Phone implements Radio {
      switchRadio() {}
    }
    

    常量枚举

    const enum Direction {
        Up = 'UP',
        Down = 'DOWN',
        Left = 'LEFT',
        Right = 'RIGHT'
    }
    

    泛型约束

    可以使用extends关键字来对泛型参数进行限制

    interface IWithLengh {
      length: number;
    }
    
    function echoWithLength<T extends IWithLengh>(arg: T): T {
      console.log(arg.length);
      return arg;
    }
    
    const str = echoWithLength('abc');
    const obj = echoWithLength({ length: 1 });
    const arr = echoWithLength([1, 2])
    

    类也可以使用泛型来约束

    class Queue<T> {
      private data: T[] = [];
      push(item: T): void {
        this.data.push(item);
      }
      pop(): T {
        return this.data.pop();
      }
    }
    const queue = new Queue<number>();
    queue.push(1);
    console.log(queue.pop().toFixed(2));
    
    const queue2 = new Queue<string>()
    

    接口也可以使用泛型来约束

    interface KeyPair<T, U> {
        key: T;
        value: U;
    }
    

    使用泛型来约束函数

    interface IPlus<T> {
      (a: T, b: T): T;
    }
    function plus(a: number, b: number): number {
      return a + b;
    }
    const a: IPlus<number> = plus
    

    类型别名

    类型别名多用于联合类型中

    type NameResolver = () => string;
    type NameOrResolver = string | NameResolver;
    function getName(n: NameOrResolver): string {
      if (typeof n === 'string') {
        return n;
      } else {
        return n();
      }
    }
    

    类型断言

    function getLength(input: string | number): number {
      if ((<string>input).length) {
        return (<string>input).length;
      } else {
        return input.toString().length;
      }
    }
    

    React基础

    使用脚手架工具搭建项目

    首先使用create-react-app搭建项目

    npx create-react-app ts-with-react --typescript
    

    简单的函数式组件

    interface IHelloProps {
      message: string;
    }
    const Hello: React.FC<IHelloProps> = (props) => {
      return <h2>{ props.message }</h2>
    }
    Hello.defaultProps = {
      message: 'Hello World'
    }
    

    React Hooks解决的问题

    • 组件很难复用状态逻辑
    • 复杂组件难以理解,尤其是生命周期函数

    useState

    该hook相当于组件内的状态

    const LikeButton: React.FC = () => {
      const [like, setLike] = useState(0)
      const [status, setStatus] = useState(true)
      return (
        <>
        <button onClick={() => { setLike(like + 1)}}>
          {like}
        </button>
        <button onClick={() => { setStatus(!status) }}>
          {String(status)}
        </button>
        </>
      )
    }
    

    useEffect

    该hook默认会在第一次渲染完成和每次界面更新时执行,相当于class组件中的componentDidMountcomponentDidUpdate

    useEffect(() => {
      document.title = `点击了${like}次`
    })
    

    使用useEffect的返回值清除副作用

    const LikeButton: React.FC = () => {
      const [position, setPosition] = useState({ x: 0, y: 0 })
      useEffect(() => {
        const updatePostion = (e: MouseEvent) => {
          setPosition({
            x: e.clientX,
            y: e.clientY
          })
        }
        document.addEventListener('click', updatePostion)
        return () => {
          // 会在下一次更新界面之后,重新添加此effect之前执行
          document.removeEventListener('click', updatePostion)
        }
      })
    
      return <p>X: {position.x}, Y: {[position.y]}</p>
    }
    

    控制useEffect执行时机,这里需要使用到useEffect的第二个参数。第二个参数是一个数组,可以填入依赖项,当这些依赖项发生变化时,才会去执行useEffect。当第二个参数为空数组,显然这种情况是没有依赖可以变化的,因此这种情况的useEffect仅仅会在组件加载和卸载时执行一次。

    useEffect(() => {
      document.title = `点击了${like}次`
    }, [like])
    

    自定义hook

    自定义hook需要以use开头

    const useMousePosition = () => {
      const [position, setPosition] = useState({ x: 0, y: 0 })
      useEffect(() => {
        const updatePostion = (e: MouseEvent) => {
          setPosition({
            x: e.clientX,
            y: e.clientY
          })
        }
        document.addEventListener('click', updatePostion)
        return () => {
          document.removeEventListener('click', updatePostion)
        }
      }, [])
      return position
    }
    

    使用一个自定义hook

    const position = useMousePosition()
    

    使用自定义hooks封装一个请求公共hooks

    import { useState, useEffect } from 'react'
    import axios from 'axios'
    const useURLLoader = (url: string, deps: any[] = []) => {
      const [data, setData] = useState<any>(null)
      const [loading, setLoading] = useState(false)
      useEffect(() => {
        setLoading(true)
        axios.get(url).then(result => {
          setData(result.data)
          setLoading(false)
        })
      }, deps)
      return data
    }
    export default useURLLoader
    

    完成组件库

    完成一个组件库需要考虑的问题

    • 代码结构
    • 样式解决方案
    • 组件需求分析和编码
    • 组件测试用例分析和编码
    • 代码打包输出和发布
    • CI/CD,文档生成等

    CSS解决方案

    • inline css
    • css in js
    • styled component
    • sass/less

    未完待续....2020年06月15日19:18:23

  • 相关阅读:
    RabbitMQ学习笔记
    Eclipse下JRebel的安装和基本使用
    通过HTTP响应头让浏览器自动刷新
    CentOS6.5安装Jenkins
    Windows快捷操作技巧
    关于代码压缩混淆加密整理;
    一款优雅的小程序拖拽排序组件实现
    记一次 Mac CA证书 问题
    微信小程序-Swiper和下拉刷新组件
    WeUI Picker组件 源代码分析
  • 原文地址:https://www.cnblogs.com/guolizhi/p/13135863.html
Copyright © 2011-2022 走看看