Vue3のdefineComponentは何をするのですか?
defineComponent 関数は、setup 関数を単純にラップして、オプションのオブジェクトを返します。
export function defineComponent(options: unknown) {
return isFunction(options) ? { setup: options } : options
}
defineComponentの最も重要な点は、TypeScriptの下で正しいパラメータの型推論をコンポーネントに与えることである。
defineComponent オーバーロードされた関数
1: 直接設定機能
// overload 1: direct setup function
// (uses user defined props interface)
export function defineComponent<Props, RawBindings = object>(
setup: (
props: Readonly<Props>,
ctx: SetupContext
) => RawBindings | RenderFunction
): DefineComponent<Props, RawBindings>
// overload 2: object format with no props
// (uses user defined props interface)
// return type is for Vetur and TSX support
export function defineComponent<
Props = {},
RawBindings = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions,
EE extends string = string
>(
options: ComponentOptionsWithoutProps<Props,RawBindings,D,C,M,Mixin,Extends,E,EE>
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]? : any }
// return type is for Vetur and TSX support
export function defineComponent<
PropNames extends string,
RawBindings,
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
options: ComponentOptionsWithArrayProps<
PropNames,
RawBindings, ... >
): DefineComponent<
Readonly<{ [key in PropNames]? : any }>,
RawBindings,... >
// overload 4: object format with object props declaration
// see `ExtractPropTypes` in . /componentProps.ts
export function defineComponent<
// the Readonly constraint allows TS to treat the type of { required: true }
// as constant instead of boolean.
PropsOptions extends Readonly<ComponentPropsOptions>,
RawBindings,
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
options: ComponentOptionsWithObjectProps<
PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
<イグ
2: プロップスなしのオブジェクト形式
<script lang="tsx">
import { noop, trim } from 'lodash';
import {
inject, Ref, defineComponent, getCurrentInstance, ref
} from '@vue/composition-api';
import filters from '@/filters';
import CommonDialog from '@/components/CommonDialog';
import ChildTable, { getEmptyModelRow } from '. /ChildTable.vue';
export interface IParentDialog {
show: boolean;
specFn: (component_id: HostComponent['id']) => Promise<{ data: DictSpecs }>;
}
export default defineComponent<IParentDialog>({
// Custom components in tsx still have to be registered
components: {
ChildTable
},
props: {
show: {
type: Boolean,
default: false
},
specFn: {
type: Function,
default: noop
}
},
// note: setup must use the arrow function
setup: (props, context) => {
// Fix a problem in tsx where the 'h' function could not be injected automatically
// eslint-disable-next-line no-unused-vars
const h = getCurrentInstance()! $createElement;
const { emit } = context;
const { specFn, show } = props;
// usage of filter
const { withColon } = filters;
// usage of inject
const pageType = inject<CompSpecType>('pageType', 'foo');
const dictComponents = inject<Ref<DictComp[]>>('dictComponents', ref([]));
// type constraints for ref
const dictSpecs = ref<DictSpecs>([]);
const loading = ref(false);
const _lookupSpecs = async (component_id: HostComponent['id']) => {
loading.value = true;
try {
const json = await specFn(component_id);
dictSpecs.value = json.data;
} finally {
loading.value = false;
}
};
const formdata = ref<Spec>({
component_id: '',
specs_id: '',
model: [getEmptyModelRow()]
});
const err1 = ref('');
const err2 = ref('');
const _doCheck = () => {
err1.value = '';
err2.value = '';
const { component_id, specs_id, model } = formdata.value;
if (!component_id) {
err1.value = 'Please select a component';
return false;
}
for (let i = 0; i < model.length; i++) {
const { brand_id, data } = model[i];
if (!brand_id) {
err2.value = 'Please select a brand';
return false;
}
if (
formdata.value.model.some(
(m, midx) => midx ! == i && String(m.brand_id) === String(brand_id)
)
) {
err2.value = 'Brand duplicate';
return false;
}
}
return true;
};
const onClose = () => {
emit('update:show', false);
};
const onSubmit = async () => {
const bool = _doCheck();
if (!bool) return;
const params = formdata.value;
emit('submit', params);
onClose();
};
// note: in tsx, globally registered components such as element-ui still have to use the kebab-case form ????
return () => (
<CommonDialog
class="comp"
title="New"
width="1000px"
labelCancel="Cancel"
labelSubmit="OK"
vLoading={loading.value}
show={show}
onClose={onClose}
onSubmit={onSubmit}
>
<el-form labelWidth="140px" class="create-page">
<el-form-item label={withColon('PartType')} required={true} error={err1.value}>
<el-select
class="full-width"
model={{
value: formdata.value.component_id,
callback: (v: string) => {
formdata.value.component_id = v;
_lookup
<イグ
3: 配列プロップス宣言のあるオブジェクト形式
// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]? : any }
// return type is for Vetur and TSX support
export function defineComponent<
PropNames extends string,
RawBindings,
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
options: ComponentOptionsWithArrayProps<
PropNames,
RawBindings, ... >
): DefineComponent<
Readonly<{ [key in PropNames]? : any }>,
RawBindings,... >
<イグ
4: object props 宣言のある object format
// overload 4: object format with object props declaration
// see `ExtractPropTypes` in . /componentProps.ts
export function defineComponent<
// the Readonly constraint allows TS to treat the type of { required: true }
// as constant instead of boolean.
PropsOptions extends Readonly<ComponentPropsOptions>,
RawBindings,
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
options: ComponentOptionsWithObjectProps<
PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
<イグ
開発実践
ユニットテストでの基本的な使い方に加え、以下のParentDialogコンポーネントは、実用的な開発において、以下のような留意すべき点があります。
カスタムコンポーネントとグローバルコンポーネントの書き方
inject、refなどの型制約。
書き込み設定と対応するhインジェクションの問題
tsxでのv-modelとscopedSlotsの書き方
ParentDialog.vue
<script lang="tsx">
import { noop, trim } from 'lodash';
import {
inject, Ref, defineComponent, getCurrentInstance, ref
} from '@vue/composition-api';
import filters from '@/filters';
import CommonDialog from '@/components/CommonDialog';
import ChildTable, { getEmptyModelRow } from '. /ChildTable.vue';
export interface IParentDialog {
show: boolean;
specFn: (component_id: HostComponent['id']) => Promise<{ data: DictSpecs }>;
}
export default defineComponent<IParentDialog>({
// Custom components in tsx still have to be registered
components: {
ChildTable
},
props: {
show: {
type: Boolean,
default: false
},
specFn: {
type: Function,
default: noop
}
},
// note: setup must use the arrow function
setup: (props, context) => {
// Fix a problem in tsx where the 'h' function could not be injected automatically
// eslint-disable-next-line no-unused-vars
const h = getCurrentInstance()! $createElement;
const { emit } = context;
const { specFn, show } = props;
// usage of filter
const { withColon } = filters;
// usage of inject
const pageType = inject<CompSpecType>('pageType', 'foo');
const dictComponents = inject<Ref<DictComp[]>>('dictComponents', ref([]));
// type constraints for ref
const dictSpecs = ref<DictSpecs>([]);
const loading = ref(false);
const _lookupSpecs = async (component_id: HostComponent['id']) => {
loading.value = true;
try {
const json = await specFn(component_id);
dictSpecs.value = json.data;
} finally {
loading.value = false;
}
};
const formdata = ref<Spec>({
component_id: '',
specs_id: '',
model: [getEmptyModelRow()]
});
const err1 = ref('');
const err2 = ref('');
const _doCheck = () => {
err1.value = '';
err2.value = '';
const { component_id, specs_id, model } = formdata.value;
if (!component_id) {
err1.value = 'Please select a component';
return false;
}
for (let i = 0; i < model.length; i++) {
const { brand_id, data } = model[i];
if (!brand_id) {
err2.value = 'Please select a brand';
return false;
}
if (
formdata.value.model.some(
(m, midx) => midx ! == i && String(m.brand_id) === String(brand_id)
)
) {
err2.value = 'Brand duplicate';
return false;
}
}
return true;
};
const onClose = () => {
emit('update:show', false);
};
const onSubmit = async () => {
const bool = _doCheck();
if (!bool) return;
const params = formdata.value;
emit('submit', params);
onClose();
};
// note: in tsx, globally registered components such as element-ui still have to use the kebab-case form ????
return () => (
<CommonDialog
class="comp"
title="New"
width="1000px"
labelCancel="Cancel"
labelSubmit="OK"
vLoading={loading.value}
show={show}
onClose={onClose}
onSubmit={onSubmit}
>
<el-form labelWidth="140px" class="create-page">
<el-form-item label={withColon('PartType')} required={true} error={err1.value}>
<el-select
class="full-width"
model={{
value: formdata.value.component_id,
callback: (v: string) => {
formdata.value.component_id = v;
_lookup
全文要約
defineComponent()を導入し、setup()コンポーネントのパラメータタイプを正しく推測できるようにした。
defineComponentは、propsなし、配列propsなどのフォームに正しく適応します。
defineComponentは、明示的なカスタムプロップインターフェース、またはプロパティ検証オブジェクトから自動的に推論されることを受け入れることができます。
tsxでは、element-uiのようなグローバルに登録されたコンポーネントは、まだkebab-case形式を使用しなければなりません。
tsxでは、v-modelはmodel={でモデル化することになっています。
<未定義
{ value, callback }} と記述します。
tsxでは、スコープ付きスロットはscopedSlots={で記述します。
<未定義
{ foo: (スコープ) => () }} 書き込み
defineComponent は機能的なコンポーネントには適用されないので、RenderContext を使用して解決する必要があります。
関連
-
vue.cli3はクロスドメイン問題を解決する XMLHttpRequestへのアクセスは「http://192.168.88.228/login/Login?phone=19939306484&pass」で。
-
TypeError: 未定義のプロパティ 'xxxx' を読み取ることができません。
-
VueComponent で未定義のプロパティ '$el' を読み取ることができません。
-
vue project Error: モジュール 'xxx' が見つからない クラスエラー解決策
-
無効なHostヘッダー サーバードメインのアクセス問題
-
[Intervention] パッシブイベントリスナー内部で、ターゲットがapparentsとして扱われるため、preventDefaultできない。
-
Uncaught (in promise)の解決策、考えられる原因
-
(Solved) vue3 uses axios to report Uncaught TypeError: Uncaught TypeError: Cannot convert undefined or null to object axios.js:1308エラー
-
vue[packaged chunk-vendors.xxx.js file is too large, vue project performance optimization solutions, front-end performance optimization knowledge summary].
-
vue__WEBPACK_IMPORTED_MODULE_0__.default is not constructor error.
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Solve モジュールのビルドに失敗しました。Error: ENOENT: no such file or directory, scandir 'E:\webnode_modules_nod
-
ERROR コマンドは失敗しました: npm install --loglevel error --registry=https://registry.npm.taobao.org --di
-
Vue-Routerのアップグレードによる捕捉されない(promiseにおける)問題
-
Vueプロジェクトを開くためのVSCode、解决无法对@/assets/aaa.vue指向的文件使用跳转、ほぼすべてのwebpackプロジェクトに対応しています。
-
vue实现PC端分辨率适配
-
axiosが定義されていない」を解決する。
-
Vue warn]を解決する。Vueの警告]: Error in created hook: "ReferenceError: axios is not defined "問題を解決する。
-
uView Ui "TypeError: 未定義またはNULLをオブジェクトに変換できません"
-
vite require は定義されていません。
-
vue 3 プロセスが定義されていない