imtoken钱包官网下载 to|definecomponent
imtoken钱包官网下载 to|definecomponent
vue3中defineComponent 的作用详解 - 掘金
vue3中defineComponent 的作用详解 - 掘金
首页 首页
沸点
课程
直播
活动
竞赛
商城
APP
插件 搜索历史
清空
创作者中心
写文章 发沸点 写笔记 写代码 草稿箱 创作灵感
查看更多
会员
登录
注册
vue3中defineComponent 的作用详解
前端_小姜
2023-02-10
7,983
这篇文章主要介绍了vue3中defineComponent 的作用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的。
我都知道普通的组件就是一个普通的对象,既然是一个普通的对象,那自然就不会获得自动的提示,
import { defineComponent } from 'vue'
const component = {
name: 'Home',
props:{
data: String,
},
setup // 没有该有的提示,这非常的不友好
}
export default component
但是当我们加上 defineComponent() 之后,就完全不一样了,可以获得自动提示,vue2、vue3的自动提示都有
import { defineComponent } from 'vue'
const component = {
name: 'Home',
props:{
data: String,
},
setup(){
// setup 可接受两个参数,一个props,和 context
}
}
export default component
接下来看看 setup 中的两个参数 props 与 context ,
props指组件传递来的参数,并且ts可以推论出props的类型.props也就是 vue2 中组件中的 props
context 有三个属性 attrs slots emit 分别对应vue2中的attrs属性、slots插槽、$emit发送事件
import { defineComponent } from 'vue'
const component = {
name: 'Home',
props:{
data: String,
},
setup(props, context){
// props.data
// context.attrs context.slots context.emit
}
}
export default component
扩展知识:
vue3之组件结构(defineComponent,setup函数)
在vue3中,对组件整体结构做一些调整,先看一个组件案例:
import {ref, reactive, defineComponent, Ref, onMounted} from "vue";
import {settingsStore} from "/@/store/module/settings";
import {IRoleList} from "/@/interface/role/list.interface";
import {IHttpResult} from "/@/interface/common.interface";
import { ILogListParams } from "/@/interface/settings/log.interface";
export default defineComponent({
name: "LogList",
setup() {
const logList: Ref
const columns = [
...
];
const pagination = ref({
"show-quick-jumper": true,
total: 100,
current: 1,
"show-size-changer": true,
"show-total": (total: number, range: number[]) => `${range[0]}-${range[1]} 共 ${total} 条`,
"pageSize": 10
});
const columnsList = ref(columns);
const params: ILogListParams = reactive({
page: 1,
pageSize: 10
});
onMounted(() => {
findLogList();
});
/*查询日志列表*/
const findLogList = () => {
settingsStore.findLogList(params).then((res: IHttpResult) => {
const data = res.data;
pagination.value.total = data.total;
logList.value = data.list;
});
};
/*修改状态*/
const onChange = (pagination: {current: number, pageSize: number}) => {
params.page = pagination.current;
params.pageSize = pagination.pageSize;
};
/*删除*/
const onDelete = (id: number) => {
alert(id);
};
return {
columnsList,
logList,
onDelete,
onChange,
pagination
};
}
});
从上面组件代码中,可以看出在vue3中没有this对象, 所有的逻辑代码都写在setup方法里面.
若是要在HTML模板页面中使用变量或者方法, 需要在setup方法return出去.
setup是Vue3 的一大特性函数, 它有几个特性:
1、setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数
2、setup函数是 Composition API(组合API)的入口
3、在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
setup函数的注意点:
vue3虽然支持vue2.x版本的写法,但也有一些要注意的地方
1、由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法
2、由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined
3、setup函数只能是同步的不能是异步的
上面的组件中用defineComponent包裹了组件;
defineComponent函数,只是对setup函数进行封装,返回options的对象;
defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断 。
defineComponent可以给组件的setup方法准确的参数类型定义.
defineComponent 可以接受显式的自定义 props 接口或从属性验证对象中自动推断
defineComponent 可以正确适配无 props、数组 props 等形式
引入 defineComponent() 以正确推断 setup() 组件的参数类型
延申:
文档中第一句话很容易看懂,说白了就是从实现上看,用了或者跟不用这个api没多大区别但是呢,第二句话说的好像又有一些区别,不过我没太看懂。。。于是我自己的第一个问题就来了:
问题一:defineComponent 这个API用起来到底和不用有什么区别???
1. 显示 Vue Options 提示。
这个API一般是在ts或者tsx文件中使用的,所以,当我们创建了一个这种类型的文件后,它是不知道我们是要去写 vue 实例代码的,所以在这种情况下,我们需要defineComponent来帮我们做内部的一些options的提示,我们可以看一个使用了defineComponent和没有使用defineComponent的例子:
当然这背后的原理是利用 TypeScript 定义了defineComponent 参数类型实现的。
2. 给予正确的参数类型推断。
拿 setup 来说,defineComponent 可以为 setup 函数的 props 传参做出正确的类型推断,看下图:
如果没有使用 defineComponent 的话,是没有办法推断出来的,需要自己显式地去定义类型。
3. 可返回一个合成类型的构造函数。
这也是官方文档中所说的,我在代码中尝试了一下,发现确实可以在其返回的构造函数中去定义一些钩子和属性等,如下图:
这就是目前我对这个API的一些理解吧~后续有新发现再补充
到此这篇关于vue3中defineComponent 的作用的文章就介绍到这了,
更新补充:
前端_小姜
前端小菜鸡
50
文章
31k
阅读
6
粉丝 目录 收起
延申:
问题一:defineComponent 这个API用起来到底和不用有什么区别???
1. 显示 Vue Options 提示。
2. 给予正确的参数类型推断。
3. 可返回一个合成类型的构造函数。
defineComponent | Vue3
defineComponent | Vue3
Vue3
Github
Github
阅前必读 开篇词 | 为什么要学习源码前置知识 ProxySet、Map、WeakSet、WeakMapCompositionTypeScriptSpec语法全局概览 目录结构createAppdefineComponenthnextTickExample 基本范例响应式系统 整体概览reactivereactive.specrefref.specbaseHandlerseffecteffect.speccomputedcomputed.spec编绎模块 compilerparse.speccompile.specRuntime runtime # defineComponent 实现方式的 defineComponent 只是返回传递给它的对象。但是,在类型方面,返回的值具有一个合成类型的构造函数,用于手动渲染函数、 TSX 和 IDE 工具支持 # 从一个例子开始 import { defineComponent } from 'vue'
const MyComponent = defineComponent({
data() {
return { count: 1 }
},
methods: {
increment() {
this.count++
}
}
})
console.log(`MyComponent:${MyComponent}`)
1234567891011121314亲自试一试
←
createApp
h
→
MIT Licensed | Copyright @ 2020-2021 Vue3js.cn 京ICP备15001338号-6
Vue 中的 defineComponent - 掘金
Vue 中的 defineComponent - 掘金
首页 首页
沸点
课程
直播
活动
竞赛
商城
APP
插件 搜索历史
清空
创作者中心
写文章 发沸点 写笔记 写代码 草稿箱 创作灵感
查看更多
会员
登录
注册
Vue 中的 defineComponent
滴滴前端技术团队
2021-08-10
27,519
关注
@滴滴出行
作者:崔静
defineComponent 本身的功能很简单,但是最主要的功能是为了 ts 下的类型推到。对于一个 ts 文件,如果我们直接写
export default {}
这个时候,对于编辑器而言,{} 只是一个 Object 的类型,无法有针对性的提示我们对于 vue 组件来说 {} 里应该有哪些属性。但是增加一层 defineComponet 的话,
export default defineComponent({})
这时,{} 就变成了 defineComponent 的参数,那么对参数类型的提示,就可以实现对 {} 中属性的提示,外还可以进行对参数的一些类型推导等操作。
但是上面的例子,如果你在 vscode 的用 .vue 文件中尝试的话,会发现不写 defineComponent 也一样有提示。这个其实是 Vetur 插件进行了处理。
下面看 defineComponent 的实现,有4个重载,先看最简单的第一个,这里先不关心 DefineComponent 是什么,后面细看。
// overload 1: direct setup function
// (uses user defined props interface)
export function defineComponent
setup: (
props: Readonly
ctx: SetupContext
) => RawBindings | RenderFunction
): DefineComponent
defineComponet 参数为 function, function 有两个参数 props 和 ctx,返回值类型为 RawBindings 或者 RenderFunction。defineComponet 的返回值类型为 DefineComponent
类似 props 用法的简易 demo 如下,我们给 a 传入不同类型的参数,define 返回值的类型也不同。这种叫 Generic Functions
declare function define
const arg1:string = '123'
const result1 = define(arg1) // result1:string
const arg2:number = 1
const result2 = define(arg2) // result2: number
类似 RawBindings 的简易 demo如下: setup 返回值类型不同,define 返回值的类型也不同
declare function define
const arg1:string = '123'
const resul1 = define(() => {
return arg1
})
const arg2:number = 1
const result2 = define(() => {
return arg2
})
由上面两个简易的 demo,可以理解重载1的意思,defineComponet 返回类型为DefineComponent
declare function define
可以根据运行时传入的参数,来动态决定 T 的类型 这种方式也是运行时类型和 typescript 静态类型的唯一联系,很多我们想通过运行时传入参数类型,来决定其他相关类型的时候,就可以使用这种方式。
接着看 definComponent,它的重载2,3,4分别是为了处理 options 中 props 的不同类型。看最常见的 object 类型的 props 的声明
export function defineComponent<
// the Readonly constraint allows TS to treat the type of { required: true }
// as constant instead of boolean.
PropsOptions extends Readonly
RawBindings,
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record
EE extends string = string
>(
options: ComponentOptionsWithObjectProps<
PropsOptions,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE
>
): DefineComponent
和上面重载1差不多的思想,核心思想也是根据运行时写的 options 中的内容推导出各种泛型。在 vue3 中 setup 的第一个参数是 props,这个 props 的类型需要和我们在 options 传入的一致。这个就是在ComponentOptionsWithObjectProps中实现的。代码如下
export type ComponentOptionsWithObjectProps<
PropsOptions = ComponentObjectPropsOptions,
RawBindings = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions,
EE extends string = string,
Props = Readonly
Defaults = ExtractDefaultPropTypes
> = ComponentOptionsBase<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE,
Defaults
> & {
props: PropsOptions & ThisType
} & ThisType<
CreateComponentPublicInstance<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
Props,
Defaults,
false
>
>
export interface ComponentOptionsBase<
Props,
RawBindings,
D,
C extends ComputedOptions,
M extends MethodOptions,
Mixin extends ComponentOptionsMixin,
Extends extends ComponentOptionsMixin,
E extends EmitsOptions,
EE extends string = string,
Defaults = {}
>
extends LegacyOptions
ComponentInternalOptions,
ComponentCustomOptions {
setup?: (
this: void,
props: Props,
ctx: SetupContext
) => Promise
//...
}
很长一段,同样的先用一个简化版的 demo 来理解一下:
type TypeA
a: T1,
b: T2,
c: T3
}
declare function define
const result = define({
a: '1',
b: 1,
c: {}
}) // result: string
根据传入的 options 参数 ts 会推断出 T1,T2,T3的类型。得到 T1, T2, T3 之后,可以利用他们进行其他的推断。稍微改动一下上面的 demo,假设 c 是一个函数,里面的参数类型由 a 的类型来决定:
type TypeA
type TypeB
a: T1
b: T2,
c: (arg:T1)=>{}
}
const result = define({
a: '1',
b: 1,
c: (arg) => { // arg 这里就被会推导为一个 string 的类型
return arg
}
})
然后来看 vue 中的代码,首先 defineComponent 可以推导出 PropsOptions。但是 props 如果是对象类型的话,写法如下
props: {
name: {
type: String,
//... 其他的属性
}
}
而 setup 中的 props 参数,则需要从中提取出 type 这个类型。所以在 ComponentOptionsWithObjectProps 中
export type ComponentOptionsWithObjectProps<
PropsOptions = ComponentObjectPropsOptions,
//...
Props = Readonly
//...
>
通过 ExtracPropTypes 对 PropsOptions 进行转化,然后得到 Props,再传入 ComponentOptionsBase,在这个里面,作为 setup 参数的类型
export interface ComponentOptionsBase<
Props,
//...
>
extends LegacyOptions
ComponentInternalOptions,
ComponentCustomOptions {
setup?: (
this: void,
props: Props,
ctx: SetupContext
) => Promise
这样就实现了对 props 的推导。
this 的作用
在 setup 定义中第一个是 this:void 。我们在 setup 函数中写逻辑的时候,会发现如果使用了 this.xxx IDE 中会有错误提示
Property 'xxx' does not exist on type 'void'
这里通过设置 this:void来避免我们在 setup 中使用 this。
this 在 js 中是一个比较特殊的存在,它是根据运行上上下文决定的,所以 typescript 中有时候无法准确的推导出我们代码中使用的 this 是什么类型的,所以 this 就变成了 any,各种类型提示/推导啥的,也都无法使用了(注意:只有开启了 noImplicitThis 配置, ts 才会对 this 的类型进行推导)。为了解决这个问题,typescript 中 function 的可以明确的写一个 this 参数,例如官网的例子:
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () => Card;
}
let deck: Deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
// NOTE: The function now explicitly specifies that its callee must be of type Deck
createCardPicker: function (this: Deck) {
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
};
},
};
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
明确的定义出在 createCardPicker 中的 this 是 Deck 的类型。这样在 createCardPicker 中 this 下可使用的属性/方法,就被限定为 Deck 中的。
另外和 this 有关的,还有一个 ThisType。
ExtractPropTypes 和 ExtractDefaultPropTypes
上面提到了,我们写的 props
{
props: {
name1: {
type: String,
require: true
},
name2: {
type: Number
}
}
}
经过 defineComponent 的推导之后,会被转换为 ts 的类型
ReadOnly<{
name1: string,
name2?: number | undefined
}>
这个过程就是利用 ExtractPropTypes 实现的。
export type ExtractPropTypes
? { [K in RequiredKeys
{ [K in OptionalKeys
: { [K in string]: any }
根据类型中清晰的命名,很好理解:利用 RrequiredKeys
{
name1
} & {
name2?
}
然后每一组里,用 InferPropType
InferPropType
在理解这个之前,先理解一些简单的推导。首先我们在代码中写
props = {
type: String
}
的话,经过 ts 的推导,props.type 的类型是 StringConstructor。所以第一步需要从 StringConstructor/ NumberConstructor 等 xxConstrucror 中得到对应的类型 string/number 等。可以通过 infer 来实现
type a = StringConstructor
type ConstructorToType
type c = ConstructorToType // type c = String
上面我们通过 ():infer V 来获取到类型。之所以可以这样用,和 String/Number 等类型的实现有关。javascript 中可以写
const key = String('a')
此时,key 是一个 string 的类型。还可以看一下 StringConstructor 接口类型表示
interface StringConstructor {
new(value?: any): String;
(value?: any): string;
readonly prototype: String;
fromCharCode(...codes: number[]): string;
}
上面有一个 ():string ,所以通过 extends {(): infer V} 推断出来的 V 就是 string。
然后再进一步,将上面的 a 修改成 propsOptions 中的内容,然后把 ConstructorToType 中的 infer V 提到外面一层来判断
type a = StringConstructor
type ConstructorType
type b = a extends {
type: ConstructorType
required?: boolean
} ? V : never // type b = String
这样就简单实现了将 props 中的内容转化为 type 中的类型。
因为 props 的 type 支持很多中写法,vue3 中实际的代码实现要比较复杂
type InferPropType
? any // null & true would fail to infer
: T extends { type: null | true }
? any
// As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
// 这里单独判断了 ObjectConstructor 和 BooleanConstructor
: T extends ObjectConstructor | { type: ObjectConstructor }
? Record
: T extends BooleanConstructor | { type: BooleanConstructor }
? boolean
: T extends Prop
// 支持 PropOptions 和 PropType 两种形式
type Prop
interface PropOptions
type?: PropType
required?: boolean
default?: D | DefaultFactory
validator?(value: unknown): boolean
}
export type PropType
type PropConstructor
| { new (...args: any[]): T & object } // 可以匹配到其他的 Constructor
| { (): T } // 可以匹配到 StringConstructor/NumberConstructor 和 () => string 等
| PropMethod
// 对于 Function 的形式,通过 PropMethod 构造成了一个和 stringConstructor 类型的类型
// PropMethod 作为 PropType 类型之一
// 我们写 type: Function as PropType<(a: string) => {b: string}> 的时候,就会被转化为
// type: (new (...args: any[]) => ((a: number, b: string) => {
// a: boolean;
// }) & object) | (() => (a: number, b: string) => {
// a: boolean;
// }) | {
// (): (a: number, b: string) => {
// a: boolean;
// };
// new (): any;
// readonly prototype: any;
// }
// 然后在 InferPropType 中就可以推断出 (a:number,b:string)=> {a: boolean}
type PropMethod
T extends (...args: any) => any // if is function with args
? {
new (): TConstructor;
(): T;
readonly prototype: TConstructor
} // Create Function like constructor
: never
RequiredKeys
这个用来从 props 中分离出一定会有值的 key,源码如下
type RequiredKeys
[K in keyof T]: T[K] extends
| { required: true }
| { default: any }
// don't mark Boolean props as undefined
| BooleanConstructor
| { type: BooleanConstructor }
? K
: never
}[keyof T]
除了明确定义 reqruied 以外,还包含有 default 值,或者 boolean 类型。因为对于 boolean 来说如果我们不传入,就默认为 false;而有 default 值的 prop,一定不会是 undefined
OptionalKeys
有了 RequiredKeys, OptionsKeys 就很简单了:排除了 RequiredKeys 即可
type OptionalKeys
ExtractDefaultPropTypes 和 ExtractPropTypes 类似,就不写了。
推导 options 中的 method,computed, data 返回值, 都和上面推导 props 类似。
emits options
vue3 的 options 中新增加了一个 emits 配置,可以显示的配置我们在组件中要派发的事件。配置在 emits 中的事件,在我们写 $emit 的时候,会作为函数的第一个参数进行提示。
对获取 emits 中配置值的方式和上面获取 props 中的类型是类似的。$emit的提示,则是通过 ThisType 来实现的(关于 ThisType 参考另外一篇文章介绍)。下面是简化的 demo
declare function define
emits: T,
method?: {[key: string]: (...arg: any) => any}
} & ThisType<{
$emits: (arg: T) => void
}>):T
const result = define({
emits: {
key: '123'
},
method: {
fn() {
this.$emits(/*这里会提示:arg: {
key: string;
}*/)
}
}
})
上面会推导出 T 为 emits 中的类型。然后 & ThisType ,使得在 method 中就可以使用 this.$emit。再将 T 作为 $emit 的参数类型,就可以在写 this.$emit的时候进行提示了。
然后看 vue3 中的实现
export function defineComponent<
//... 省却其他的
E extends EmitsOptions = Record
//...
>(
options: ComponentOptionsWithObjectProps<
//...
E,
//...
>
): DefineComponent
export type ComponentOptionsWithObjectProps<
//..
E extends EmitsOptions = EmitsOptions,
//...
> = ComponentOptionsBase< // 定义一个 E 的泛型
//...
E,
//...
> & {
props: PropsOptions & ThisType
} & ThisType<
CreateComponentPublicInstance< // 利用 ThisType 实现 $emit 中的提示
//...
E,
//...
>
>
// ComponentOptionsWithObjectProps 中 包含了 ComponentOptionsBase
export interface ComponentOptionsBase<
//...
E extends EmitsOptions, // type EmitsOptions = Record
EE extends string = string,
Defaults = {}
>
extends LegacyOptions
ComponentInternalOptions,
ComponentCustomOptions {
//..
emits?: (E | EE[]) & ThisType
}
export type ComponentPublicInstance<
//...
E extends EmitsOptions = {},
//...
> = {
//...
$emit: EmitFn
//...
}
在一边学习一边实践的时候踩到一个坑。踩坑过程:将 emits 的推导过程实现了一下
export type ObjectEmitsOptions = Record<
string,
((...args: any[]) => any) | null
>
export type EmitsOptions = ObjectEmitsOptions | string[];
declare function define
然后用下面的方式来验证结果
const emit = ['key1', 'key2']
const a = define(emit)
看 ts 提示的时候发现,a 的类型是 const b: string[] & ThisType
纠结好久,最终发现写法的不同:用下面写法的话推导出来结果一致
define(['key1', 'key2'])
但是用之前的写法,通过变量传入的时候,ts 在拿到 emit 时候,就已经将其类型推导成了 string[],所以 define 函数中拿到的类型就变成了 string[],而不是原始的 ['key1', 'key2']
因此需要注意:在 vue3 中定义 emits 的时候,建议直接写在 emits 中写,不要提取为单独的变量再传给 emits
真的要放在单独变量里的话,需要进行处理,使得 '[key1', 'key2'] 的变量定义返回类型为 ['key1', 'key2'] 而非 string[]。可以使用下面两种方式:
方式一
const keys = ["key1", "key2"] as const; // const keys: readonly ["key1", "key2"]
这种方式写起来比较简单。但是有一个弊端,keys 为转为 readonly 了,后期无法对 keys 进行修改。
参考文章2 ways to create a Union from an Array in Typescript
方式二
type UnionToIntersection
type LastOf
type Push
type UnionToTuple
declare function tuple
const c = tuple(['key1', 'key2']) // const c: ["key1", "key2"]
首先通过 arr: T[] 将 ['key1', 'key2'] 转为 union,然后通过递归的方式, LastOf 获取 union 中的最后一个,Push到数组中。
mixins 和 extends
vue3 中写在 mixins 或 extends 中的内容可以在 this 中进行提示。对于 mixins 和 extends 来说,与上面其他类型的推断有一个很大的区别:递归。所以在进行类型判断的时候,也需要进行递归处理。举个简单的例子,如下
const AComp = {
methods: {
someA(){}
}
}
const BComp = {
mixins: [AComp],
methods: {
someB() {}
}
}
const CComp = {
mixins: [BComp],
methods: {
someC() {}
}
}
对于 CComp 中的 this 的提示,应该有方法 someB 和 someA。为了实现这个提示,在进行类型推断的时候,需要一个类似下面的 ThisType
ThisType<{
someA
} & {
someB
} & {
someC
}>
所以对于 mixins 的处理,就需要递归获取 component 中的 mixins 中的内容,然后将嵌套的类型转化为扁平化的,通过 & 来链接。看源码中实现:
// 判断 T 中是否有 mixin
// 如果 T 含有 mixin 那么这里结果为 false,以为 {mixin: any} {mixin?: any} 是无法互相 extends 的
type IsDefaultMixinComponent
? ComponentOptionsMixin extends T ? true : false
: false
//
type IntersectionMixin
? OptionTypesType<{}, {}, {}, {}, {}> // T 不包含 mixin,那么递归结束,返回 {}
: UnionToIntersection
// ExtractMixin(map type) is used to resolve circularly references
type ExtractMixin
Mixin: MixinToOptionTypes
}[T extends ComponentOptionsMixin ? 'Mixin' : never]
// 通过 infer 获取到 T 中 Mixin, 然后递归调用 IntersectionMixin
type MixinToOptionTypes
infer P,
infer B,
infer D,
infer C,
infer M,
infer Mixin,
infer Extends,
any,
any,
infer Defaults
>
? OptionTypesType
&
IntersectionMixin
IntersectionMixin
: never
extends 和 mixin 的过程相同。然后看 ThisType 中的处理
ThisType<
CreateComponentPublicInstance<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
Props,
Defaults,
false
>
>
export type CreateComponentPublicInstance<
P = {},
B = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = {},
PublicProps = P,
Defaults = {},
MakeDefaultsOptional extends boolean = false,
// 将嵌套的结构转为扁平化的
PublicMixin = IntersectionMixin
// 提取 props
PublicP = UnwrapMixinsType , // 提取 RawBindings,也就是 setup 返回的内容 PublicB = UnwrapMixinsType // 提取 data 返回的内容 PublicD = UnwrapMixinsType PublicC extends ComputedOptions = UnwrapMixinsType EnsureNonVoid PublicM extends MethodOptions = UnwrapMixinsType EnsureNonVoid PublicDefaults = UnwrapMixinsType EnsureNonVoid > = ComponentPublicInstance< // 上面结果传给 ComponentPublicInstance,生成 this context 中的内容 PublicP, PublicB, PublicD, PublicC, PublicM, E, PublicProps, PublicDefaults, MakeDefaultsOptional, ComponentOptionsBase > 以上就是整体大部分的 defineComponent 的实现,可以看出,他纯粹是为了类型推导而生的,同时,这里边用到了很多很多类型推导的技巧,还有一些这里没有涉及,感兴趣的同学可以去仔细看下 Vue 中的实现。 滴滴前端技术 @滴滴出行 63 文章 501k 阅读 6.5k 粉丝 目录 收起 ExtractPropTypes 和 ExtractDefaultPropTypes emits options mixins 和 extends 友情链接: 风流小相师笔趣阁 超级傻婿奶爸 闯关东:我!朱传武誓做铁血军神 亿万富翁意外重生1988 主角叫夜帝,都市小说 react父组件触发子组件方法 vue3中的defineComponent详解 - 掘金 首页 首页 沸点 课程 直播 活动 竞赛 商城 APP 插件 搜索历史 清空 创作者中心 写文章 发沸点 写笔记 写代码 草稿箱 创作灵感 查看更多 会员 登录 注册 vue3中的defineComponent详解 前端_小姜 2023-03-30 400 vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的。最主要的功能是为了 ts 下的类型推断 下面是我想通过了解一段项目代码的意义来理解defineComponent 下面是我在vue3项目中看到的真实情况: 1、没有defineComponent时候: 1、有defineComponent时候: 以上就是我刚接触vue3和ts没多久时在项目中实际遇到的问题,但是搞半天我还是没有弄明白我疑惑的地方,下面是使用GPT3.5 Model了解的结果,多少有点清楚了: import { defineComponent } from 'vue'; export default defineComponent({ name: 'MyComponent', props: { message: { type: String, required: true }, count: { type: Number, default: 0 } }, data() { return { foo: 'bar', baz: 123 }; }, methods: { handleClick() { this.baz++; } }, computed: { computedMessage() { return this.message.toUpperCase(); } }, template: ` Count: {{ count }} Baz: {{ baz }} vue3中的defineComponent详解 - 掘金
{{ computedMessage }}
`
});
在这个例子中,我们使用defineComponent函数来定义一个名为MyComponent的组件。在props属性中,我们明确指定了message和count属性的类型,并且将message属性标记为必需的。在data方法中,我们返回了一个对象,明确指定了foo和baz属性的类型。在methods方法中,我们定义了一个handleClick方法。在computed属性中,我们定义了一个computedMessage计算属性。最后,在template中,我们使用了这些属性和方法来渲染组件的模板。
前端_小姜
前端小菜鸡
50
文章
32k
阅读
6
粉丝 目录 收起
下面是我在vue3项目中看到的真实情况:
1、没有defineComponent时候:
1、有defineComponent时候:
全局 API:常规 | Vue.js
全局 API:常规 | Vue.js
直接跳到内容Vue.js搜索主导航文档 深度指南互动教程示例快速上手术语表错误码参照表Vue 2 文档从 Vue 2 迁移API演练场生态系统 资源合作伙伴主题UI 组件证书找工作T-Shirt 商店官方库Vue RouterPinia工具链指南视频课程Vue MasteryVue School帮助Discord 聊天室GitHub 论坛DEV Community动态博客Twitter活动新闻简报关于 常见问题团队版本发布社区指南行为规范纪录片赞助合作伙伴English 日本語 Українська Français 한국어 Português বাংলা Italiano 帮助我们翻译!githubtwitterdiscord外观githubtwitterdiscord菜单本页目录 侧边栏导航全局 API应用实例通用组合式 APIsetup()响应式: 核心响应式: 工具响应式: 进阶生命周期钩子依赖注入选项式 API状态选项渲染选项生命周期选项组合选项其他杂项组件实例内置内容指令组件特殊元素特殊 Attributes单文件组件语法定义
vue
import { nextTick } from 'vue'
export default {
data() {
return {
count: 0
}
},
methods: {
async increment() {
this.count++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
}
}
参考 this.$nextTick()defineComponent() 在定义 Vue 组件时提供类型推导的辅助函数。类型ts// 选项语法
function defineComponent(
component: ComponentOptions
): ComponentConstructor
// 函数语法 (需要 3.3+)
function defineComponent(
setup: ComponentOptions['setup'],
extraOptions?: ComponentOptions
): () => any为了便于阅读,对类型进行了简化。详细信息第一个参数是一个组件选项对象。返回值将是该选项对象本身,因为该函数实际上在运行时没有任何操作,仅用于提供类型推导。注意返回值的类型有一点特别:它会是一个构造函数类型,它的实例类型是根据选项推断出的组件实例类型。这是为了能让该返回值在 TSX 中用作标签时提供类型推导支持。你可以像这样从 defineComponent() 的返回类型中提取出一个组件的实例类型 (与其选项中的 this 的类型等价):tsconst Foo = defineComponent(/* ... */)
type FooInstance = InstanceType
const Comp = defineComponent(
(props) => {
// 就像在
复制VI. 全文总结引入 defineComponent() 以正确推断 setup() 组件的参数类型defineComponent 可以正确适配无 props、数组 props 等形式defineComponent 可以接受显式的自定义 props 接口或从属性验证对象中自动推断在 tsx 中,element-ui 等全局注册的组件依然要用 kebab-case 形式在 tsx 中,v-model 要用 model={{ value, callback }} 写法在 tsx 中,scoped slots 要用 scopedSlots={{ foo: (scope) => ( TypeScript 与选项式 API | Vue.js 直接跳到内容Vue.js搜索主导航文档 深度指南互动教程示例快速上手术语表错误码参照表Vue 2 文档从 Vue 2 迁移API演练场生态系统 资源合作伙伴主题UI 组件证书找工作T-Shirt 商店官方库Vue RouterPinia工具链指南视频课程Vue MasteryVue School帮助Discord 聊天室GitHub 论坛DEV Community动态博客Twitter活动新闻简报关于 常见问题团队版本发布社区指南行为规范纪录片赞助合作伙伴English 日本語 Українська Français 한국어 Português বাংলা Italiano 帮助我们翻译!githubtwitterdiscord外观githubtwitterdiscord菜单本页目录 API 风格偏好选项式组合式?侧边栏导航开始简介快速上手基础创建一个应用模板语法响应式基础计算属性类与样式绑定条件渲染列表渲染事件处理表单输入绑定生命周期侦听器模板引用组件基础深入组件注册Props事件组件 v-model透传 Attributes插槽依赖注入异步组件逻辑复用组合式函数自定义指令插件内置组件TransitionTransitionGroupKeepAliveTeleportSuspense应用规模化单文件组件工具链路由状态管理测试服务端渲染 (SSR)最佳实践生产部署性能优化无障碍访问安全TypeScript总览TS 与组合式 APITS 与选项式 API进阶主题使用 Vue 的多种方式组合式 API 常见问答深入响应式系统渲染机制渲染函数 & JSXVue 与 Web Components动画技巧本页目录当前页面的目录为组件的 props 标注类型为组件的 emits 标注类型为计算属性标记类型为事件处理函数标注类型扩展全局属性扩展自定义选项赞助位成为赞助商TypeScript 与选项式 API 这一章假设你已经阅读了搭配 TypeScript 使用 Vue 的概览。TIP虽然 Vue 的确支持在选项式 API 中使用 TypeScript,但在使用 TypeScript 的前提下更推荐使用组合式 API,因为它提供了更简单、高效和可靠的类型推导。为组件的 props 标注类型 选项式 API 中对 props 的类型推导需要用 defineComponent() 来包装组件。有了它,Vue 才可以通过 props 以及一些额外的选项,比如 required: true 和 default 来推导出 props 的类型:tsimport { defineComponent } from 'vue' export default defineComponent({ // 启用了类型推导 props: { name: String, id: [Number, String], msg: { type: String, required: true }, metadata: null }, mounted() { this.name // 类型:string | undefined this.id // 类型:number | string | undefined this.msg // 类型:string this.metadata // 类型:any } })然而,这种运行时 props 选项仅支持使用构造函数来作为一个 prop 的类型——没有办法指定多层级对象或函数签名之类的复杂类型。我们可以使用 PropType 这个工具类型来标记更复杂的 props 类型:tsimport { defineComponent } from 'vue' import type { PropType } from 'vue' interface Book { title: string author: string year: number } export default defineComponent({ props: { book: { // 提供相对 `Object` 更确定的类型 type: Object as PropType required: true }, // 也可以标记函数 callback: Function as PropType<(id: number) => void> }, mounted() { this.book.title // string this.book.year // number // TS Error: argument of type 'string' is not // assignable to parameter of type 'number' this.callback?.('123') } })注意事项 如果你的 TypeScript 版本低于 4.7,在使用函数作为 prop 的 validator 和 default 选项值时需要格外小心——确保使用箭头函数:tsimport { defineComponent } from 'vue' import type { PropType } from 'vue' interface Book { title: string year?: number } export default defineComponent({ props: { bookA: { type: Object as PropType // 如果你的 TypeScript 版本低于 4.7,确保使用箭头函数 default: () => ({ title: 'Arrow Function Expression' }), validator: (book: Book) => !!book.title } } })这会防止 TypeScript 将 this 根据函数内的环境作出不符合我们期望的类型推导。这是之前版本的一个设计限制,不过现在已经在 TypeScript 4.7 中解决了。为组件的 emits 标注类型 我们可以给 emits 选项提供一个对象来声明组件所触发的事件,以及这些事件所期望的参数类型。试图触发未声明的事件会抛出一个类型错误:tsimport { defineComponent } from 'vue' export default defineComponent({ emits: { addBook(payload: { bookName: string }) { // 执行运行时校验 return payload.bookName.length > 0 } }, methods: { onSubmit() { this.$emit('addBook', { bookName: 123 // 类型错误 }) this.$emit('non-declared-event') // 类型错误 } } })为计算属性标记类型 计算属性会自动根据其返回值来推导其类型:tsimport { defineComponent } from 'vue' export default defineComponent({ data() { return { message: 'Hello!' } }, computed: { greeting() { return this.message + '!' } }, mounted() { this.greeting // 类型:string } })在某些场景中,你可能想要显式地标记出计算属性的类型以确保其实现是正确的:tsimport { defineComponent } from 'vue' export default defineComponent({ data() { return { message: 'Hello!' } }, computed: { // 显式标注返回类型 greeting(): string { return this.message + '!' }, // 标注一个可写的计算属性 greetingUppercased: { get(): string { return this.greeting.toUpperCase() }, set(newValue: string) { this.message = newValue.toUpperCase() } } } })在某些 TypeScript 因循环引用而无法推导类型的情况下,可能必须进行显式的类型标注。为事件处理函数标注类型 在处理原生 DOM 事件时,应该为我们传递给事件处理函数的参数正确地标注类型。让我们看一下这个例子:vue import { defineComponent } from 'vue' export default defineComponent({ methods: { handleChange(event) { // `event` 隐式地标注为 `any` 类型 console.log(event.target.value) } } }) 没有类型标注时,这个 event 参数会隐式地标注为 any 类型。这也会在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 时抛出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,在访问 event 上的属性时你可能需要使用类型断言:tsimport { defineComponent } from 'vue' export default defineComponent({ methods: { handleChange(event: Event) { console.log((event.target as HTMLInputElement).value) } } })扩展全局属性 某些插件会通过 app.config.globalProperties 为所有组件都安装全局可用的属性。举例来说,我们可能为了请求数据而安装了 this.$http,或者为了国际化而安装了 this.$translate。为了使 TypeScript 更好地支持这个行为,Vue 暴露了一个被设计为可以通过 TypeScript 模块扩展来扩展的 ComponentCustomProperties 接口:tsimport axios from 'axios' declare module 'vue' { interface ComponentCustomProperties { $http: typeof axios $translate: (key: string) => string } }参考:对组件类型扩展的 TypeScript 单元测试类型扩展的位置 我们可以将这些类型扩展放在一个 .ts 文件,或是一个影响整个项目的 *.d.ts 文件中。无论哪一种,都应确保在 tsconfig.json 中包括了此文件。对于库或插件作者,这个文件应该在 package.json 的 types 属性中被列出。为了利用模块扩展的优势,你需要确保将扩展的模块放在 TypeScript 模块 中。 也就是说,该文件需要包含至少一个顶级的 import 或 export,即使它只是 export {}。如果扩展被放在模块之外,它将覆盖原始类型,而不是扩展!ts// 不工作,将覆盖原始类型。 declare module 'vue' { interface ComponentCustomProperties { $translate: (key: string) => string } }ts// 正常工作。 export {} declare module 'vue' { interface ComponentCustomProperties { $translate: (key: string) => string } }扩展自定义选项 某些插件,比如 vue-router,提供了一些自定义的组件选项,比如 beforeRouteEnter:tsimport { defineComponent } from 'vue' export default defineComponent({ beforeRouteEnter(to, from, next) { // ... } })如果没有确切的类型标注,这个钩子函数的参数会隐式地标注为 any 类型。我们可以为 ComponentCustomOptions 接口扩展自定义的选项来支持:tsimport { Route } from 'vue-router' declare module 'vue' { interface ComponentCustomOptions { beforeRouteEnter?(to: Route, from: Route, next: () => void): void } }现在这个 beforeRouteEnter 选项会被准确地标注类型。注意这只是一个例子——像 vue-router 这种类型完备的库应该在它们自己的类型定义中自动执行这些扩展。这种类型扩展和全局属性扩展受到相同的限制。参考:对组件类型扩展的 TypeScript 单元测试在 GitHub 上编辑此页 前一篇TS 与组合式 API下一篇 使用 Vue 的多种方式TypeScript 与选项式 API已经加载完毕 TypeScript 与组合式 API | Vue.js 直接跳到内容Vue.js搜索主导航文档 深度指南互动教程示例快速上手术语表错误码参照表Vue 2 文档从 Vue 2 迁移API演练场生态系统 资源合作伙伴主题UI 组件证书找工作T-Shirt 商店官方库Vue RouterPinia工具链指南视频课程Vue MasteryVue School帮助Discord 聊天室GitHub 论坛DEV Community动态博客Twitter活动新闻简报关于 常见问题团队版本发布社区指南行为规范纪录片赞助合作伙伴English 日本語 Українська Français 한국어 Português বাংলা Italiano 帮助我们翻译!githubtwitterdiscord外观githubtwitterdiscord菜单本页目录 API 风格偏好选项式组合式?侧边栏导航开始简介快速上手基础创建一个应用模板语法响应式基础计算属性类与样式绑定条件渲染列表渲染事件处理表单输入绑定生命周期侦听器模板引用组件基础深入组件注册Props事件组件 v-model透传 Attributes插槽依赖注入异步组件逻辑复用组合式函数自定义指令插件内置组件TransitionTransitionGroupKeepAliveTeleportSuspense应用规模化单文件组件工具链路由状态管理测试服务端渲染 (SSR)最佳实践生产部署性能优化无障碍访问安全TypeScript总览TS 与组合式 APITS 与选项式 API进阶主题使用 Vue 的多种方式组合式 API 常见问答深入响应式系统渲染机制渲染函数 & JSXVue 与 Web Components动画技巧本页目录当前页面的目录为组件的 props 标注类型为组件的 emits 标注类型为 ref() 标注类型为 reactive() 标注类型为 computed() 标注类型为事件处理函数标注类型为 provide / inject 标注类型为模板引用标注类型为组件模板引用标注类型赞助位成为赞助商TypeScript 与组合式 API 这一章假设你已经阅读了搭配 TypeScript 使用 Vue 的概览。为组件的 props 标注类型 使用 这被称之为“运行时声明”,因为传递给 defineProps() 的参数会作为运行时的 props 选项使用。然而,通过泛型参数来定义 props 的类型通常更直接:vue const props = defineProps<{ foo: string bar?: number }>() 这被称之为“基于类型的声明”。编译器会尽可能地尝试根据类型参数推导出等价的运行时选项。在这种场景下,我们第二个例子中编译出的运行时选项和第一个是完全一致的。基于类型的声明或者运行时声明可以择一使用,但是不能同时使用。我们也可以将 props 的类型移入一个单独的接口中:vue interface Props { foo: string bar?: number } const props = defineProps 这同样适用于 Props 从另一个源文件中导入的情况。该功能要求 TypeScript 作为 Vue 的一个 peer dependency。vue import type { Props } from './foo' const props = defineProps 语法限制 在 3.2 及以下版本中,defineProps() 的泛型类型参数仅限于类型文字或对本地接口的引用。这个限制在 3.3 中得到了解决。最新版本的 Vue 支持在类型参数位置引用导入和有限的复杂类型。但是,由于类型到运行时转换仍然基于 AST,一些需要实际类型分析的复杂类型,例如条件类型,还未支持。您可以使用条件类型来指定单个 prop 的类型,但不能用于整个 props 对象的类型。Props 解构默认值 当使用基于类型的声明时,我们失去了为 props 声明默认值的能力。这可以通过 withDefaults 编译器宏解决:tsexport interface Props { msg?: string labels?: string[] } const props = withDefaults(defineProps msg: 'hello', labels: () => ['one', 'two'] })这将被编译为等效的运行时 props default 选项。此外,withDefaults 帮助程序为默认值提供类型检查,并确保返回的 props 类型删除了已声明默认值的属性的可选标志。非 对于运行时声明,我们可以使用 PropType 工具类型:tsimport type { PropType } from 'vue' const props = defineProps({ book: Object as PropType })其工作方式与直接指定 props 选项基本相同:tsimport { defineComponent } from 'vue' import type { PropType } from 'vue' export default defineComponent({ props: { book: Object as PropType } })props 选项通常用于 Options API,因此你会在选项式 API 与 TypeScript 指南中找到更详细的例子。这些例子中展示的技术也适用于使用 defineProps() 的运行时声明。为组件的 emits 标注类型 在 类型参数可以是以下的一种:一个可调用的函数类型,但是写作一个包含调用签名的类型字面量。它将被用作返回的 emit 函数的类型。一个类型字面量,其中键是事件名称,值是数组或元组类型,表示事件的附加接受参数。上面的示例使用了具名元组,因此每个参数都可以有一个显式的名称。我们可以看到,基于类型的声明使我们可以对所触发事件的类型进行更细粒度的控制。若没有使用 没有类型标注时,这个 event 参数会隐式地标注为 any 类型。这也会在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 时报出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,你在访问 event 上的属性时可能需要使用类型断言:tsfunction handleChange(event: Event) { console.log((event.target as HTMLInputElement).value) }为 provide / inject 标注类型 provide 和 inject 通常会在不同的组件中运行。要正确地为注入的值标记类型,Vue 提供了一个 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型:tsimport { provide, inject } from 'vue' import type { InjectionKey } from 'vue' const key = Symbol() as InjectionKey provide(key, 'foo') // 若提供的是非字符串值会导致错误 const foo = inject(key) // foo 的类型:string | undefined建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入。当使用字符串注入 key 时,注入值的类型是 unknown,需要通过泛型参数显式声明:tsconst foo = inject import { ref, onMounted } from 'vue' const el = ref onMounted(() => { el.value?.focus() }) 可以通过类似于 MDN 的页面来获取正确的 DOM 接口。注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null。为组件模板引用标注类型 有时,你可能需要为一个子组件添加一个模板引用,以便调用它公开的方法。举例来说,我们有一个 MyModal 子组件,它有一个打开模态框的方法:vue import { ref } from 'vue' const isContentShown = ref(false) const open = () => (isContentShown.value = true) defineExpose({ open }) 为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:vue import MyModal from './MyModal.vue' const modal = ref const openModal = () => { modal.value?.open() } 注意,如果你想在 TypeScript 文件而不是在 Vue SFC 中使用这种技巧,需要开启 Volar 的 Takeover 模式。如果组件的具体类型无法获得,或者你并不关心组件的具体类型,那么可以使用 ComponentPublicInstance。这只会包含所有组件都共享的属性,比如 $el。tsimport { ref } from 'vue' import type { ComponentPublicInstance } from 'vue' const child = ref Global API: General | Vue.js Skip to contentVue.jsSearchMain NavigationDocs GuideTutorialExamplesQuick StartGlossaryError ReferenceVue 2 DocsMigration from Vue 2APIPlaygroundEcosystem ResourcesPartnersThemesUI ComponentsCertificationJobsT-Shirt ShopOfficial LibrariesVue RouterPiniaTooling GuideVideo CoursesVue MasteryVue SchoolHelpDiscord ChatGitHub DiscussionsDEV CommunityNewsBlogTwitterEventsNewslettersAbout FAQTeamReleasesCommunity GuideCode of ConductThe DocumentarySponsorPartners简体中文 日本語 Українська Français 한국어 Português বাংলা Italiano Help Us Translate!githubtwitterdiscordAppearancegithubtwitterdiscordMenuOn this page Sidebar NavigationGlobal APIApplicationGeneralComposition APIsetup()Reactivity: CoreReactivity: UtilitiesReactivity: AdvancedLifecycle HooksDependency InjectionOptions APIOptions: StateOptions: RenderingOptions: LifecycleOptions: CompositionOptions: MiscComponent InstanceBuilt-insDirectivesComponentsSpecial ElementsSpecial AttributesSingle-File ComponentSyntax Specification vue import { nextTick } from 'vue' export default { data() { return { count: 0 } }, methods: { async increment() { this.count++ // DOM not yet updated console.log(document.getElementById('counter').textContent) // 0 await nextTick() // DOM is now updated console.log(document.getElementById('counter').textContent) // 1 } } } See also this.$nextTick()defineComponent() A type helper for defining a Vue component with type inference.Typets// options syntax function defineComponent( component: ComponentOptions ): ComponentConstructor // function syntax (requires 3.3+) function defineComponent( setup: ComponentOptions['setup'], extraOptions?: ComponentOptions ): () => anyType is simplified for readability.DetailsThe first argument expects a component options object. The return value will be the same options object, since the function is essentially a runtime no-op for type inference purposes only.Note that the return type is a bit special: it will be a constructor type whose instance type is the inferred component instance type based on the options. This is used for type inference when the returned type is used as a tag in TSX.You can extract the instance type of a component (equivalent to the type of this in its options) from the return type of defineComponent() like this:tsconst Foo = defineComponent(/* ... */) type FooInstance = InstanceType const Comp = defineComponent( (props) => { // use Composition API here like in const count = ref(0) return () => { // render function or JSX return h('div', count.value) } }, // extra options, e.g. declare props and emits { props: { /* ... */ } } )The main use case for this signature is with TypeScript (and in particular with TSX), as it supports generics:tsxconst Comp = defineComponent( // use Composition API here like in const count = ref(0) return () => { // render function or JSX return TypeScript 与选项式 API | Vue.js
TypeScript 与组合式 API | Vue.js
Global API: General | Vue.js
}
},
// manual runtime props declaration is currently still needed.
{
props: ['msg', 'list']
}
)In the future, we plan to provide a Babel plugin that automatically infers and injects the runtime props (like for defineProps in SFCs) so that the runtime props declaration can be omitted.Note on webpack Treeshaking Because defineComponent() is a function call, it could look like that it would produce side-effects to some build tools, e.g. webpack. This will prevent the component from being tree-shaken even when the component is never used.To tell webpack that this function call is safe to be tree-shaken, you can add a /*#__PURE__*/ comment notation before the function call:jsexport default /*#__PURE__*/ defineComponent(/* ... */)Note this is not necessary if you are using Vite, because Rollup (the underlying production bundler used by Vite) is smart enough to determine that defineComponent() is in fact side-effect-free without the need for manual annotations.See also Guide - Using Vue with TypeScriptdefineAsyncComponent() Define an async component which is lazy loaded only when it is rendered. The argument can either be a loader function, or an options object for more advanced control of the loading behavior.Typetsfunction defineAsyncComponent(
source: AsyncComponentLoader | AsyncComponentOptions
): Component
type AsyncComponentLoader = () => Promise
interface AsyncComponentOptions {
loader: AsyncComponentLoader
loadingComponent?: Component
errorComponent?: Component
delay?: number
timeout?: number
suspensible?: boolean
onError?: (
error: Error,
retry: () => void,
fail: () => void,
attempts: number
) => any
}See also Guide - Async ComponentsdefineCustomElement() This method accepts the same argument as defineComponent, but instead returns a native Custom Element class constructor.Typetsfunction defineCustomElement(
component:
| (ComponentOptions & { styles?: string[] })
| ComponentOptions['setup']
): {
new (props?: object): HTMLElement
}Type is simplified for readability.DetailsIn addition to normal component options, defineCustomElement() also supports a special option styles, which should be an array of inlined CSS strings, for providing CSS that should be injected into the element's shadow root.The return value is a custom element constructor that can be registered using customElements.define().Examplejsimport { defineCustomElement } from 'vue'
const MyVueElement = defineCustomElement({
/* component options */
})
// Register the custom element.
customElements.define('my-vue-element', MyVueElement)See alsoGuide - Building Custom Elements with VueAlso note that defineCustomElement() requires special config when used with Single-File Components.Edit this page on GitHub PreviousApplicationNext setup()Global API: General has loaded
vue3中defineComponent 的作用详解_definecomponent作用-CSDN博客
>vue3中defineComponent 的作用详解_definecomponent作用-CSDN博客
vue3中defineComponent 的作用详解
包小志
已于 2023-09-28 10:39:52 修改
阅读量7.9k
收藏
9
点赞数
4
分类专栏:
vue框架
文章标签:
javascript
vue.js
前端
于 2023-03-27 10:08:01 首次发布
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44860226/article/details/129789545
版权
vue框架
专栏收录该内容
11 篇文章
0 订阅
订阅专栏
defineComponent基本用法
vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的。
我都知道普通的组件就是一个普通的对象,既然是一个普通的对象,那自然就不会获得自动的提示,
import { defineComponent } from 'vue'
const component = {
name: 'Home',
props:{
data: String,
},
setup // 没有该有的提示,这非常的不友好
}
export default component
但是当我们加上 defineComponent() 之后,就完全不一样了,可以获得自动提示,vue2、vue3的自动提示都有
import { defineComponent } from 'vue'
const component = {
name: 'Home',
props:{
data: String,
},
setup () {
// setup 可接受两个参数,一个props,和 context
}
}
export default component
接下来看看 setup 中的两个参数 props 与 context ,
props指组件传递来的参数,并且ts可以推论出props的类型.props也就是 vue2 中组件中的 propscontext 有三个属性 attrs slots emit 分别对应vue2中的attrs属性、slots插槽、$emit发送事件
import { defineComponent } from 'vue'
const component = {
name: 'Home',
props:{
data: String,
},
setup(props, context){
// props.data
// context.attrs context.slots context.emit
}
}
export default component
扩展知识:
vue3之组件结构(defineComponent,setup函数)
在vue3中,对组件整体结构做一些调整,先看一个组件案例:
import {ref, reactive, defineComponent, Ref, onMounted} from "vue";
import {settingsStore} from "/@/store/module/settings";
import {IRoleList} from "/@/interface/role/list.interface";
import {IHttpResult} from "/@/interface/common.interface";
import { ILogListParams } from "/@/interface/settings/log.interface";
export default defineComponent({
name: "LogList",
setup() {
const logList: Ref
const columns = [
...
];
const pagination = ref({
"show-quick-jumper": true,
total: 100,
current: 1,
"show-size-changer": true,
"show-total": (total: number, range: number[]) => `${range[0]}-${range[1]} 共 ${total} 条`,
"pageSize": 10
});
const columnsList = ref(columns);
const params: ILogListParams = reactive({
page: 1,
pageSize: 10
});
onMounted(() => {
findLogList();
});
/*查询日志列表*/
const findLogList = () => {
settingsStore.findLogList(params).then((res: IHttpResult) => {
const data = res.data;
pagination.value.total = data.total;
logList.value = data.list;
});
};
/*修改状态*/
const onChange = (pagination: {current: number, pageSize: number}) => {
params.page = pagination.current;
params.pageSize = pagination.pageSize;
};
/*删除*/
const onDelete = (id: number) => {
alert(id);
};
return {
columnsList,
logList,
onDelete,
onChange,
pagination
};
}
});
从上面组件代码中,可以看出在vue3中没有this对象, 所有的逻辑代码都写在setup方法里面,若是要在HTML模板页面中使用变量或者方法, 需要在setup方法return出去.
setup是Vue3 的一大特性函数, 它有几个特性:
setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数setup函数是 Composition API(组合API)的入口在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
setup函数的注意点: vue3虽然支持vue2.x版本的写法,但也有一些要注意的地方
由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefinedsetup函数只能是同步的不能是异步的 (1)上面的组件中用defineComponent包裹了组件; (2)defineComponent函数,只是对setup函数进行封装,返回options的对象; (3)defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断 。 (4)defineComponent可以给组件的setup方法准确的参数类型定义. (5)defineComponent 可以接受显式的自定义 props 接口或从属性验证对象中自动推断 (6)defineComponent 可以正确适配无 props、数组 props 等形式 (7)引入 defineComponent() 以正确推断 setup() 组件的参数类型
关注博主即可阅读全文
优惠劵
包小志
关注
关注
4
点赞
踩
9
收藏
觉得还不错?
一键收藏
知道了
0
评论
vue3中defineComponent 的作用详解
vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的。从上面组件代码中,可以看出在vue3中没有this对象, 所有的逻辑代码都写在setup方法里面,若是要在HTML模板页面中使用变量或者方法, 需要在setup方法return出去.我都知道普通的组件就是一个普通的对象,既然是一个普通的对象,那自然就不会获得自动的提示,
复制链接
扫一扫
专栏目录
Vue3 — Composition API(其它部分二)
m0_48942526的博客
05-29
123
# toRef
为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
应用: 当要将 某个prop 的 ref 传递给复合函数时,toRef 很有用
state:{{ state }}
age:{{ age }}
money:{{ mon
vue3中使用defineComponent封装hook实现模板复用
小马甲
12-29
571
vue3中使用defineComponent封装hook实现模板复用
参与评论
您还未登录,请先
登录
后发表或查看评论
vue3中的defineComponent作用
晚风的博客
06-08
1万+
vue3中的defineComponent作用
3狐狸分奶酪——小学生学习课件
11-23
3狐狸分奶酪——小学生学习课件
Vue3 - defineComponent解决了什么?
热门推荐
Moon Star
11-04
10万+
defineComponent函数,只是对setup函数进行封装,返回options的对象;
export function defineComponent(options: unknown) {
return isFunction(options) ? { setup: options } : options
}
defineComponent最重要的是:在TypeScript下,给予了组件正确的参数类型推断 。
defineComponent测试用例
test/types/defin..
vue3.0 defineComponent、resolvComponent等
weixin_43294560的博客
05-27
1万+
1、defineComponent
从实现上看:defineComponent只返回传递给它的对象
就类型而言:返回的值有一个合成类型的构造函数,用于手动渲染函数、TSX 和 IDE 工具支持。
export default defineComponent({
...
})
2、defineAsyncComponent
创建一个只有在需要时才会加载的异步组件。可以接受一个返回Promise的工厂函数
const AsyncComp = defineAsyncComponent(
Vue3 入门
weixin_45763636的博客
09-05
435
快速入门VUE3的一些知识点
Vue3动态组件component详解
qq_45861961的博客
08-15
1万+
这样就展示了一个动态组件的完整用法,可以方便地在不同组件间进行切换并传递数据。动态组件非常适合需要根据不同条件展示不同内容的场景,掌握它可以更灵活地构建组件。动态组件component是Vue中非常实用的一个功能,它可以根据条件动态切换不同的组件。本文将详细介绍其用法。这样就可以通过修改currentComponent的值,来动态切换ComponentA和ComponentB了。需要注意的是,对于动态组件,Vue会在组件切换时销毁前一个组件实例并创建新实例。所以切换动态组件时不会保留组件的状态。
vue3之组件结构(defineComponent,setup函数)
m0_67394360的博客
03-02
2074
在vue3中,对组件整体结构做一些调整,先看一个组件案例:
import {ref, reactive, defineComponent, Ref, onMounted} from "vue";
import {settingsStore} from "/@/store/module/settings";
import {IRoleList} from "/@/interface/role/list.interface";
import {IHttpResult} from "/@/interface/co
vue 中的 render 函数作用详解
10-15
主要介绍了vue 中的 render 函数作用,通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
vue3中使用tinyMCE
07-01
vue3中使用tinyMCE的demo,setup语法糖写法,解压后请删除node_model包后重新cnpm i
component.vue-modal:基于`Vue 3`的`API`式弹窗组件
04-22
Modal Componet For Vue 3
基于Vue 3的API式弹窗组件
安装(Install)
npm install @gopowerteam/vue-modal --save
// OR
yarn add @gopowerteam/vue-modal
App.vue
[removed]
import { ModalProvider } from "@gopowerteam/vue-modal";
export default defineComponent({
components: {
ModalProvider,
}
});
[removed]
vue-tiny-validator:Vue 3的微小表单验证工具
05-18
Vue Tiny验证器
微小的验证库(压缩后的<0.7K)没有依赖于作为构建基块的Vue 3。 受vee-validate和vuetify的验证启发。
在构建或样式化表单组件时不具干扰性。 它只是钩住了modelValue并给您显示错误消息。
重置特定字段或整个表单的验证错误。
将自己的规则构建为返回true或错误消息的简单函数。 您可以控制一切,包括i18n。
支持异步规则(例如,检查数据库中是否已存在字段值)。
完全输入。
安装
npm i vue-tiny-validator
# or
yarn add vue-tiny-validator
用法
父组件(窗体):
import { defineComponent , ref } from 'vue'
import { useForm } from 'vue-tiny-validator'
export default
vue中的scope使用详解
08-29
主要介绍了vue中的scope使用详解,需要的朋友可以参考下
vue3中tsx语法一些了解
qq_20623665的博客
04-02
675
子组件接收,使用setup函数的第二个参数context的slots拿到插槽模板数据。//defineComponent内setup外可以定义类型。emit :抛出事件实现子传父emit('事件',值)arrts:接收props没有接收的绑定数据。expose:向父组件暴露当前组件方法。设置则可以在内部创建多个节点。props:接收父传子,定义类型。return 返归html结构。slot:接受父组件传递插槽。首先直接创建tsx文件。直接使用组件函数的写法。中,在子组件设置插槽。
【Vue】vue3数据绑定
瑞新の博客:bennyrhys
11-21
1685
Vue2
分析当前应用环境
企业老项目要用,还是需要掌握的
类型项目
vue2为主,感兴趣可以了解
代码结构
mounted生命周期函数,页面加载完
vue3取代生命周期函数的应用
效果图
import { defineComponent, onMounted} from 'vue'; // vue3从这里引入生命周期函数
import axios from 'axios'; // 1引入库
export default defineComponent({
name: 'Home',
s.
vue3中defineComponent和defineAsyncComponent的区别及使用场景
最新发布
weixin_43995143的博客
02-23
917
是 Vue 3 中的一个函数,用于定义一个组件。它是 Vue 3 的组合式 API 的一部分,提供了一种更加灵活和组织化的方式来定义组件。在 Vue 2 中,我们通常使用一个对象来定义组件,而在 Vue 3 中,函数提供了更多的类型支持和更好的集成。
vue3中definecomponent作用
06-28
Vue3中的defineComponent函数是用来定义组件的函数,它可以接收一个组件选项对象作为参数,然后返回一个组件对象。这个组件对象可以被注册到Vue应用中,从而可以在应用中使用。 defineComponent函数的作用是将组件...
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
包小志
CSDN认证博客专家
CSDN认证企业博客
码龄5年
暂无认证
198
原创
1万+
周排名
3万+
总排名
15万+
访问
等级
2089
积分
1056
粉丝
77
获赞
13
评论
345
收藏
私信
关注
热门文章
node.js中Express简介
8417
vue3中defineComponent 的作用详解
7909
@PostMapping和@GetMapping使用详解
7521
npm与包及npm包下载速度慢问题的解决
6318
ZIP压缩输入/输出流
4953
分类专栏
CSS3知识
MySQL知识
56篇
nodeJS知识
12篇
java基础知识
19篇
Maven知识
6篇
SpringBoot知识
23篇
Git
1篇
Docker
15篇
Hadoop
3篇
Redis
13篇
RabbitMQ
14篇
javaScript基础知识
10篇
vue框架
11篇
VsCode使用知识
3篇
网络基础知识
2篇
Nginx
1篇
webpack知识
2篇
数据结构与算法
1篇
最新评论
vue3中defineComponent 的作用详解
weixin_45873965:
请问下,加了defineComponent之后会有什么提示呢不太了解?
百度地图的常用事件和方法
包小志:
map.addEventListener() 文章中有示例仔细看看
百度地图的常用事件和方法
王,:
在哪里添加这个方法?
网络基础面试题
Dream of maid:
博主的文章细节很到位,兼顾实用性和可操作性,对我有很大帮助,已经关注持续学习,也希望博主能来我的博客指点一二,感谢感谢
@PostMapping和@GetMapping使用详解
包小志:
最好加上吧
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
hadoop入门——大数据应用场景及分布式与集群的概念
Hadoop入门——数据分析基本步骤
Hadoop入门——企业数据分析的方向
2023年157篇
2022年43篇
目录
目录
分类专栏
CSS3知识
MySQL知识
56篇
nodeJS知识
12篇
java基础知识
19篇
Maven知识
6篇
SpringBoot知识
23篇
Git
1篇
Docker
15篇
Hadoop
3篇
Redis
13篇
RabbitMQ
14篇
javaScript基础知识
10篇
vue框架
11篇
VsCode使用知识
3篇
网络基础知识
2篇
Nginx
1篇
webpack知识
2篇
数据结构与算法
1篇
目录
评论
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
个
红包个数最小为10个
红包总金额
元
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
0
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值