Skip to main content

· One min read
MengFei

npm & yarn

yarn info <pkg>  #查看远端仓库 pkg 包信息, 后面可跟 package.json 中字段查看具体信息
yarn info <pkg> version # 当前版本
yarn info <pkg> versions # 版本列表
yarn info <pkg> engines # 依赖的 node版本,或npm 版本, yarn 版本等等
yarn info <pkg> dependencies
yarn info <pkg> devDependencies
yarn info <pkg> peerDependencies
yarn why <pkg> # 查看项目有没有包 pkg, 会列出包版本和被谁依赖
yarn

· 5 min read
MengFei

React 16.8 加入的特性。 React hooks 允许开发者,在函数组件中,使用 state,和类似生命周期的功能。

基础 hooks

useState

概述:用来声明状态,和改变该状态的函数。

参数:状态初始值,或者一个返回初始值的函数。只会在组件出初次渲染时执行。

用法:

const [state, setState] = useState(initialState); // 参考Es6 数组的结构赋值

const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});

eg:

function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(0)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
</>
);
}

useEffect

概述:处理具有副作用的行为,例如(订阅,计时器,日志记录等。),并在函数组件渲染结束后运行。

参数:

  1. 第一个参数,接收一个可能具有副作用(订阅,计时器,日志记录等。)的函数。该函数的返回值,可能是一个清理函数。
  2. 第二个参数(可选),含有执行依赖的数组。当该参数为空数组时,该 effects 只在初次渲染后执行。

用法:

useEffect(
() => {
const subscription = props.source.subscribe();
return () => { // 组件被移除前执行清理函数
subscription.unsubscribe();
};
},
[props.source], // 依赖改变,订阅函数才会执行
);
useEffect(
() => {
const subscription = props.source.subscribe();
return () => { // 组件被移除前执行清理函数
subscription.unsubscribe();
};
},
[], // 初次渲染 执行
);

useContext

const MyContext = React.createContext(defaultValue);
const value = useContext(MyContext);

其他 hooks

useReducer

概述:在状态较复杂时使用.我的理解是,和 redux 类似的概念。因为工作一直在 使用 redux。所以这个概念,暂时不做深入了解。以后有机会用到的话,再深入学习。

useCallback

概述:类似 useMemo,返回一个 callback 函数,

const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
  • 测试中,遇到问题:依赖值没变,但会重新进行计算。
  • 有点没搞懂,这个 hook 的使用场景

useMemo

概述:返回一个计算值,只在依赖发生变化时计算。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useRef

概述:useRef 返回一个可变的 ref 对象。初始化时,初始化参数将被赋值给该对象的 current 属性。这个 ref 对象,存在在整个组件的生命周期内

最常用的用法,是用 ref 对象存储一个 dom 对象。eg:

function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}

但是,ref 对象并不是只能存储 dom 对象。它是一个通用容器。有些类似 类的实例属性。可以存储任意类型的值。eg:

function Timer() {
const intervalRef = useRef();

useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
return () => {
clearInterval(intervalRef.current);
};
});

// ...
}

useImperativeHandle

概述:在使用 ref 时,可以使用 useImperativeHandle,选择特定的实例属性暴漏给父组件。

必须结合 forwardRef 使用

function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

// 在父组件中拿到的ref引用,是一个 { focus:Function } 的对象。

useLayoutEffect

与 useEffect 类似,

useLayoutEffect 在 componentDidMount 和 componentDidUpdate 相同的阶段触发。

React 官方建议,优先使用 useEffect,只在使用 useEffect 遇到问题时,尝试使用 useLayoutEffect

useDebugValue

· 10 min read
MengFei

Typescript 使用总结及部分小技巧

备份一篇去年写的关于ts 使用的小文章。

1. Typescript 部分常用可能会被忽略的知识点

1.1 类型获取

1.1.1 typeof

使用 typeof 获取变量类型

const Setting = {
color: "red",
font: "xxx",
width: 100,
height: 100,
title: "xxxx",
};

type SettingType = typeof Setting;

function getPropX() {
return "xxx";
}

type GetPropXType = typeof getPropX;
type GetPropXReturnType = ReturnType<typeof getPropX>;

1.1.2 索引类型查询操作符 keyof

const Setting = {
color: "red",
font: "xxx",
width: 100,
height: 100,
title: "xxxx",
};

// type SettingKey = 'color' | 'font' | 'width' | 'height' | 'title'
type SettingKey = keyof typeof Setting;

1.1.3 索引访问

使用 typeName['propName'] 格式读取属性类型

interface Person {
name: string;
age: string;
sex: "man" | "woman";
job?: string;
eat: (food: string) => void;
}

type eat = Person["eat"];

1.2 泛型的使用和定义

泛型其实就是类型变量,根据传入的类型,获取到需要的类型

添加泛型的位置:

  • 函数泛型 -- 在函数上添加泛型

auto import

  • 类泛型 -- 在类上添加泛型
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
  • 类型泛型 -- 在接口或类型别名(使用 type 或 interface 定义的类型)上添加泛型
// antd rowSelection 的类型
export interface TableRowSelection<T> {
type?: RowSelectionType;
selectedRowKeys?: Key[];
onChange?: (selectedRowKeys: Key[], selectedRows: T[]) => void;
getCheckboxProps?: (record: T) => Partial<Omit<CheckboxProps, "checked" | "defaultChecked">>;
onSelect?: SelectionSelectFn<T>;
onSelectMultiple?: (selected: boolean, selectedRows: T[], changeRows: T[]) => void;
/** @deprecated This function is meaningless and should use `onChange` instead */
onSelectAll?: (selected: boolean, selectedRows: T[], changeRows: T[]) => void;
/** @deprecated This function is meaningless and should use `onChange` instead */
onSelectInvert?: (selectedRowKeys: Key[]) => void;
selections?: INTERNAL_SELECTION_ITEM[] | boolean;
hideDefaultSelections?: boolean;
fixed?: boolean;
columnWidth?: string | number;
columnTitle?: string | React.ReactNode;
renderCell?: (
value: boolean,
record: T,
index: number,
originNode: React.ReactNode,
) => React.ReactNode | RcRenderedCell<T>;
}

// antd table columns 的类型
export declare type ColumnsType<RecordType = unknown> = (
| ColumnGroupType<RecordType>
| ColumnType<RecordType>
)[];

1.3 条件类型的使用和定义

常用的条件类型

更过预定义的条件类型 typescript/lib/lib.d.ts

- Partial<T> -- 使 T 中的所有属性变成可选属性
- Pick<T,K extends keyof T> -- 从 T 中挑选属性 K,组成新的类型
- Omit<T, K extends keyof any> -- 从 T 中删除属性 K,组成新的类型
- ReturnType<T extends (...args: any) => any> -- 获取函数的返回值类型
- Required<T> -- 使 T 中的所有属性变成必选属性
- Readonly<T> -- 使 T 中的所有属性变成只读属性
- Exclude<T, U> -- 从 T 中剔除可以赋值给 U 的类型。
- Extract<T, U> -- 提取 T 中可以赋值给 U 的类型。
- NonNullable<T> -- 从 T 中剔除 null 和 undefined。
- ReturnType<T> -- 获取函数返回值类型。
- InstanceType<T> -- 获取构造函数类型的实例类型。

2. React 项目中的 Typescript

此章节重点,类型定义和类型使用经验和小技巧

  1. 使用 vscode 开发 typescript 时,强大的智能提示和自动导入以及自动修复功能的使用。
  1. 类型的复用

2.1 自定修复和自动导入

2.1.1 书写时的智能提示和导入

跨文件,可导入的 类型 / 方法 / 变量 ...的智能提示,以及自动导入

auto import

2.1.2 自动 fix 的自动导入

单个修复导入 fix import

所有修复导入 fix import all

2.2 定义组件的 props 中的类型

2.2.1 props 的 interface 定义位置

props 的 interface 定义位置: 组件文件中

import * as React from "react";

interface IComNameProps {}

const ComName: React.FunctionComponent<IComNameProps> = (props) => {
return <div>ComName</div>;
};

export default ComName;

2.2.2 导出可能会用到的类型(interface/type/enum)

大部分组件的 props 的 interface, 可以使用 export 导出,方便在使用组件时,部分类型的使用(可以参考下一章节:传递组件 props 时,使用到的类型)。

export interface IComNameProps {}

2.2.3 使用和查找已定义的类型

定义组件 props 属性时,引用类属性,优先考虑使用已定义的类型(善用 vsocde 强大的引用提示,以及自动引入功能)

import { Store } from "antd/lib/form/interface";
import { ResourceSaveDto } from "@/client/services/umgt/resource";

export interface PopupStore extends Store, ResourceSaveDto {}

2.3 传递组件 props 时,使用到的类型

上面定义并导出好组件和组件的类型后,在使用此组件时,可同时引入可能用到的类型

大部分组件的 props 传递时,需要的数据类型,都可以通过鼠标放上去的提示来找到。

  1. 直接使用和引入单个 props 属性类型
  2. 引入整个组件 props 类型,使用 interfaceName['propsName]

类型查找

2.4 Vsocde 中的重构

2.4.1 引用查找

引用查找

2.4.2 引用重命名

邮件重命名或者 F2 重命名

引用查找

2.5 使用 code snippets

给大家推荐的 React Ts Extension Pack 包里,包含了两个下载量比较大的 react 语法段的包

语法片段包

2.4.1 ES7 React/Redux/React-Native/JS snippets

imp→    import moduleName from 'module'
imd→ import { destructuredModule } from 'module'
imr→ import React from 'react'

edf→ export default (params) => { }
cp→ const { } = this.props
cs→ const { } = this.state

------------------------------------------------------------------
rcc→
import React, { Component } from 'react'

export default class FileName extends Component {
render() {
return <div>$2</div>
}
}

------------------------------------------------------------------
rcep→
import React, { Component } from 'react'
import PropTypes from 'prop-types'

export class FileName extends Component {
static propTypes = {}

render() {
return <div>$2</div>
}
}
-----------------------------------------------------------------
rafcp→
import React from 'react'
import PropTypes from 'prop-types'

const $1 = (props) => {
return <div>$0</div>
}

$1.propTypes = {}

export default $1

export default $1

-----------------------------------------------------------------
rcredux→
import React, { Component } from 'react'
import { connect } from 'react-redux'

export class FileName extends Component {
render() {
return <div>$4</div>
}
}

const mapStateToProps = (state) => ({})

const mapDispatchToProps = {}

export default connect(mapStateToProps, mapDispatchToProps)(FileName)

2.4.2 Typescript React Code snippets

// tsrsfc   stateless functional component
import * as React from "react";

interface IAppProps {}

const App: React.FunctionComponent<IAppProps> = (props) => {};

export default App;
-----------------------------------------------------------------
// tsrcc→ class component skeleton
import * as React from "react";

export interface IAppProps {}

export default class App extends React.Component<IAppProps> {
public render() {
return <div />;
}
}
-----------------------------------------------------------------
// tscntr→ react redux container skeleton
import * as React from "react";
import { connect } from "react-redux";

export interface IAppProps {}

class App extends React.Component<IAppProps> {
public render() {
return <div />;
}
}

const mapState2Props = (state) => {
return {};
};

export default connect(mapState2Props)(App);


3 React Redux 结合 Typescript

Redux Typescript 基础写法

· 6 min read
MengFei

很多同学在刚开始学习写ts的时候,不太清楚如何写类型,以及何时应该写类型,又该何时使用类型。 这里引出了三个问题:

  1. 如何定义类型
  2. 何时定义类型
    1. 数据产生,和数据获取的地方。
    2. 比如定义请求函数的地方, 独立组件props的定义,都是经典且基础的需要定义类型的地方
  3. 何时使用类型,即引用类型
    1. 数据使用的地方,数据关联的地方,这些地方,应该优先引用上述定义的类型,而无需重复定义类型。
    2. 例如使用请求来的数据的时候,使用请求函数定义时定义的类型
    3. 组件使用的时候,使用组件定义时定义的类型。
    4. 数据关联的地方,指比如两个接口,获取的数据有一定的包含关系,可以使用类型继承,来引用类型。

类型使用和定义,应该遵循,定义尽量少的类型,增加已定义类型的复用性原则。

除了这三个问题外,还有一个练习太少的问题,很多老的项目,并没有使用Ts 编写。那么除了自己的ts 练习项目外, 还可以在哪里熟悉 ts类型的写法呢。

这里向大家介绍一个可能很多人都见过,并且写过的东西, ------------------- JSDOC。 我们可以使用JSDOC, 定义各种类型,以及可以被vscode良好的支持,给出边界的类型代码提示。

下面这种注释,估计大部分前端开发者都见过,并且都写过,但可能了解的并不是很多。下面我们对JSDOC是什么, 以及有什么作用来做个介绍

/**
* 函数Desc
* @param {string} params1
* @param {number} params2
*/
function name(params1, params2) {

}

JSDOC 介绍

JSDoc是一个根据javascript文件中注释信息,生成JavaScript应用程序或库、模块的API文档 的工具。你可以使用他记录如:命名空间,类,方法,方法参数等。类似JavaDoc和PHPDoc。现在很多编辑器或IDE中还可以通过JSDoc直接或使用插件生成智能提示。从而使开发者很容易了解整个类和其中的属性和方法,并且快速知道如何使用,从而提高开发效率,降低维护成本。

是标记变量、入参、返回值的类型,稍微弥补了 JavaScript 没有静态类型,不支持类型标注的问题。基于 JSDoc 的类型注释,Google Closure Compiler 可以分析优化代码,VS Code 之类的代码编辑器可以在不使用 TypeScript 的情况下实现代码提示。

历史

Michael Mathews 在 2001 年创建 JSDoc 项目,它是最早的 JavaScript 文档工具,注释语法借鉴自 Javadoc(很多文档工具都使用了类似 Javadoc 注释语法,比如 C++ 的 Doxygen、 PHP 的 phpDocumentor)。截止 2020 年经历了 4 个大版本更新:

  • 2001 JSDoc (JSDoc.pm): 使用 Perl 编写,代码托管在 SourceForge
  • 2007 JsDoc Toolkit 1.0: 使用 JavaScript 编写,基于 Rhino,运行在 Java 平台,代码托管在 Google Code
  • 2008 JsDoc Toolkit 2.0: 同 1.0
  • 2011 JSDoc 3: 基于 Node.js,代码托管在 GitHub

编译文档用法

npm i -g jsdoc 
jsdoc yourjavascriptfile.js

常用标注

  • 类型
    • @type
    • @typedef
    • @property/@prop
    • @template
    • @enum
  • 函数
    • @param/@argument/@arg
    • @returns/@return
    • @throws
    • @deprecated
  • 类和继承
    • @constructor/@class
    • @this
    • @extends
    • @public
    • @private
    • @protected
    • @readonly
  • 文档
    • @fileoverview
    • @author
    • @example
    • @description
    • @see
    • @link
    • @license
    • @preserve

使用示例

定义类型:

/**
* desc desc desc
* @typedef {string|number} TypeName
* /
/**
* desc desc desc
* @typedef {Object} ObjectTypeName
* @property {string} name
* @property {number} age
* /

/**
* desc desc desc
* @typedef {{
* name:string;
* age:number;
* count: string | number;
* }} ObjectTypeName
* /

使用类型

/**@type {TypeName} **/
let a = '';


/**@type {ObjectTypeName} **/
let obj = {};

JSDOC 中类型和 Typescipt中类型的区别

  1. 不支持泛型。
  2. JSDOC 本质就是注释, 无法支持, ts的类型检查。只能提供类型提示。

JSDOC 好处

  1. 注释更加规范化,
  2. 让js 具备类型提示,代码提示。
  3. 增加js代码可读性,可维护性。
  4. 可以直接生成 api 文档。

使用JSDOC 的开源项目