默认情况下,我们不能在函数组件上使用 ref
属性,因为它们没有实例。
如果要在函数组件中使用 ref
,可以使用 forwardRef
包裹组件函数使用(可与 useImperativeHandle
结合使用)。
被 forwardRef 包裹的组件函数除 props
,还要多传入第二个参数:ref
,即从外部传入的 ref。
useImperativeHandle
接收三个参数,第一个是 forwardRef
传入的 ref
;第二个参数是 handle
函数,返回要向外暴露的值,通常是对象;第三个参数是依赖数组
,根据依赖变化执行更新,非必需参数。
import { useRef, forwardRef, Ref, useImperativeHandle, ElementRef, useEffect } from "react";
// 组件,有两种定义类型的方式
// 组件1,一种是使用 forwardRef 的泛型
// forwardRef 泛型第一个参数是 Ref 类型,
// 第二个参数是 props 类型,不传时默认类型为{},
// 注意,forwardRef 泛型与内部包裹函数的参数顺序恰恰相反,易造成歧义
const Component1 = forwardRef<HTMLInputElement, {}>((props, ref) => <input ref={ref} />);
// 组件2,另一种是是在函数参数上直接定义类型, **该方式验证有效**
// 注意,在函数参数上定义类型时,ref 参数类型需要使用Ref泛型包裹,而 forwardRef 泛型则不需要
const type useImperativeHandle返回的类型 = React.Ref<{
sayHello: () => void
}>
const Component2 = forwardRef(
// ref 类型使用了 Ref 泛型包裹
(props: {}, ref: useImperativeHandle返回的类型) => {
// ref 为第一个参数,返回带有 sayHello 方法对象的函数为第二个参数
us
create-react-app webpack
配置而不使用 'eject'
并且不创建 react-scripts
的分支。create-react-app
的所有好处。您可以根据需要添加插件、加载程序。react-app-rewired
, 您需要安装它才能customize-cra
工作, 增加babel-plugin-import
配置动态引入Antd
组件antd
、antd-mobile
、lodash
、material-ui
等。
npx create-react-app my-app --template typescript
或者
yarn create react-app my-app --template typescript
antd
和定制化Create-React-App
项目的两个包
npm install antd --save
npm install babel-plugin-import react-app-rewired customize-cra --save-dev
根目录创建config-overrides.js
,用customized-cra
来增加babel-plugin-import
配置动态引入Antd
组件
const { override, fixBabelImports } = require('customize-cra');
module.exports = override(fixBabelImports('import', {
libraryName: 'antd',
lib
// scanMixin.js
export default {
data() {
return {
code: '',
lastTime: '',
nextTime: '',
lastCode: '',
nextCode: '',
dtmainId: '',
};
},
activated() {
window.addEventListener('keypress', this.scanEventHandler, false);
},
beforeDestroy() {
window.removeEventListener('keypress', this.scanEventHandler, false);
},
deactivated() {
window.removeEventListener('keypress', this.scanEventHandler, false);
},
methods: {
scanEventHandler(e) {
if (window.event) { // IE
this.nextCode = e.keyCode;
} else if (e.which) { // Netscape/Firefox/Opera
this.nextCode = e.which;
}
if (e.which === 13) { // 键盘回车事件
if (this.code.length < 3) return; // 扫码枪的速度很快,手动输入的时间不会让code的长度大于2,所以这里不会对扫码枪有效
console.log('扫码结束。');
console.log('条形码:', this.code);
this.parseQRCode(this.code); // 获取到扫码枪输入的内容,做别的操作
this.code = '';
this.lastCode
Type ‘undefined’ is not assignable to type ‘Function’.
null
和 undefined
是其它类型(包括 void)的子类型,可以赋值给其它类型(如:数字类型),赋值后的类型会变成 null
或 undefined
使用第三方/自己的代码声明里没有定义 null | undefined
, 可强制转换值的类型
语法
<类型>值
或者(tsx中只能使用这种方式)
值 as 类型
module.ts
和module.d.ts
在同一个文件夹中(重名),编译器会跳过module.d.ts
文件,所以你的声明将不被考虑。重命名d.ts
文件或将其移动到另一个文件夹。如果您有合适的模块,这种方法很好,但是如果您想在模块之间共享类型,最好使用import .. from ..
.d.ts
文件中有import
或export
,它就会成为一个模块,并且该模块中的所有内容都必须由使用者随后导入。node_modules/@types/foo/index.d.ts
文件,存放 foo
模块的声明文件。不太建议用这种方案,一般只用作临时测试。创建一个 types
目录,专门用来管理自己写的声明文件,将 foo
的声明文件放到 types/foo/index.d.ts
中。这种方式需要配置下 tsconfig.json
中的 paths
和 baseUrl
字段。
// path/to/project
├── src
| └── foo
| └── index.ts
├── types
| └── foo
| └── index.d.ts
└── tsconfig.json
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": "./",
"paths": {
"*": ["types/*"]
}
}
}
declare namespace 模块名称 { // 模块名称加了引号为外部模块, 不加为作用域
// 从TS2.9开始,我们可以使用import()语法
type User = import('./user').User;
export interface Request {
user: User;
target: User;
friend: User;
}
export class SuperUser implements User {
type FilterOptional<T> = Pick<
T,
Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? K : never
}[keyof T],
undefined
>
>
type FilterNotOptional<T> = Pick<
T,
Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? never : K
}[keyof T],
undefined
>
>
type PartialEither<T, K extends keyof any> = {
[P in Exclude<keyof FilterOptional<T>, K>]-?: T[P]
} & { [P in Exclude<keyof FilterNotOptional<T>, K>]?: T[P] } & {
[P in Extract<keyof T, K>]?: undefined
}
type Objects = {
[name: string]: any
}
type EitherOr<O extends Objects, L extends string, R extends string> = (
| PartialEither<Pick<O, L | R>, L>
| PartialEither<Pick<O, L | R>, R>
) &
Omit<O, L | R>
// a、b二选一,并且必须传递一个
type RequireOne = EitherOr<
{
a: number;
b: string;
},
'a',
'b'
>;
// a、b二选一,或者都不传
type RequireOneOrEmpty = EitherOr<
{
a?: number;
b?: string;
},
'a',
'b'
>;
interfac
接口主要用于类型检查,他只是一个结构契约,定义了具有相似的名称和类型的对象结构。除此之外,接口还可以定义方法和事件
不同与intetrface只能定义对象类型,type声明任何类型,包括定义基础类型,联合声明或交叉类型
定义类型范围
interface只能定义对象类型,而type声明可以声明任何类型,包括基础类型、联合类型或交叉类型
//基本数据类型
type person = string
//联合类型
interface Dog {
name: string;
}
interface Cat {
age: number;
}
type animal = Dog | Cat
//元组类型
interface Dog {
name: string;
}
interface Cat {
age: number;
}
type animal = [Dog, Cat]
// 交叉类型
type Person = {
name: string
}
type User = Person & { age: number }
// type & interface
interface Person {
name: string
}
type User = {age: number} & Person
扩展性
接口可以extends、implements,从而扩展多个接口或类。类型没有扩展功能
// interface extends interface
interface Person {
name: string
}
interface User extends Person {
<!-- 空页面,负责中转到目标页面 -->
<template>
<div></div>
</template>
<script>
export default {
name: 'refresh',
data () {
return {}
},
beforeRouteEnter (to, from, next) {
next(vm => {
const data = {
...from,
query: { ...from.query, ...to.query },
params: { ...from.params, ...to.params }
}
vm.$router.replace(data)
})
}
}
</script>
this.$router.push({
name: routerName,
params: { aa: 2 }, // params不在浏览器url里显示,刷新页面参数消失
})