用单元测试读懂 vue3 中的 defineComponen

news2024/11/26 5:26:47

目录

前言:

I. 测试用例

II. 一些基础类型定义

III. 官网文档中的 props

V. 开发实践

VI. 全文总结


前言:

Vue3是一种流行的JavaScript框架,它在组件开发方面提供了更多的表现力和灵活性,通过使用defineComponent高阶函数,开发人员可以更加方便地编写Vue组件,从而提高开发效率。

在结合了 TypeScript 的情况下,传统的 Vue.extend 等定义方法无法对此类组件给出正确的参数类型推断,这就需要引入 defineComponent() 组件包装函数,其在 rfc 文档中的说明为:

https://composition-api.vuejs.org/api.html#setup
interface Data {
  [key: string]: unknown
}
 
interface SetupContext {
  attrs: Data
  slots: Slots
  emit: (event: string, ...args: unknown[]) => void
}
 
function setup(props: Data, context: SetupContext): Data
To get type inference for the arguments passed to setup(), the use of defineComponent is needed.

文档中说得相当简略,实际写起来难免还是有丈二和尚摸不着头脑的时候。

本文将采用与本系列之前两篇相同的做法,从单元测试入手,结合 ts 类型定义,尝试弄懂 defineComponent()  的明确用法。

考虑到篇幅和相似性,本文只采用 vue 2.x + @vue/composition-api 的组合进行说明,vue 3 中的签名方式稍有不同,读者可以自行参考并尝试。

I. 测试用例

在 @vue/composition-api 项目中,test/types/defineComponent.spec.ts 中的几个测试用例非常直观的展示了几种“合法”的 TS 组件方式 (顺序和原文件中有调整):

[test case 1] 无 props

  it('no props', () => {
    const App = defineComponent({
      setup(props, ctx) {
        //...
        return () => null
      },
    })
    new Vue(App)
    //...
  })

[test case 2] 数组形式的 props

  it('should accept tuple props', () => {
    const App = defineComponent({
      props: ['p1', 'p2'],
      setup(props) {
        //...
      },
    })
    new Vue(App)
    //...
  })

[test case 3] 自动推断 props

  it('should infer props type', () => {
    const App = defineComponent({
      props: {
        a: {
          type: Number,
          default: 0,
        },
        b: String, // 只简写类型
      },
      setup(props, ctx) {
        //...
        return () => null
      },
    })
    new Vue(App)
    //...
  })

[test case 4] 推断是否必须

组件选项中的 props 类型将被推断为 { readonly foo: string; readonly bar: string; readonly zoo?: string }

  it('infer the required prop', () => {
    const App = defineComponent({
      props: {
        foo: {
          type: String,
          required: true,
        },
        bar: {
          type: String,
          default: 'default',
        },
        zoo: {
          type: String,
          required: false,
        },
      },
      propsData: {
        foo: 'foo',
      },
      setup(props) {
        //...
        return () => null
      },
    })
    new Vue(App)
    //...
  })

[test case 5] 显式自定义 props 接口

  it('custom props interface', () => {
    interface IPropsType {
      b: string
    }
    const App = defineComponent<IPropsType>({ // 写明接口
      props: {
        b: {}, // 只简写空对象
      },
      setup(props, ctx) {
        //...
        return () => null
      },
    })
    new Vue(App)
    //...
  })

[test case 6] 显式接口和显式类型

it('custom props type function', () => {
    interface IPropsTypeFunction {
      fn: (arg: boolean) => void
    }
    const App = defineComponent<IPropsTypeFunction>({ // 写明接口
      props: {
        fn: Function as PropType<(arg: boolean) => void>, // 写明类型
      },
      setup(props, ctx) {
        //...
        return () => null
      },
    })
    new Vue(App)
    //...
  })

[test case 7] 从显式类型推断 props

  it('custom props type inferred from PropType', () => {
    interface User {
      name: string
    }
    const App = defineComponent({
      props: {
        user: Object as PropType<User>,
        func: Function as PropType<() => boolean>,
        userFunc: Function as PropType<(u: User) => User>,
      },
      setup(props) {
        //...
        return () => null
      },
    })
    new Vue(App)
    //...
  })

II. 一些基础类型定义

在阅读 defineComponent 函数的签名形式之前,为了便于解释,先来看看其关联的几个基础类型定义,大致理解其作用即可,毋需深究:

引自 vue 2.x 中的 options 基础类型接口

此类型没太多好说的,就是我们熟悉的 Vue 2.x 组件 options 的定义:

// vue 2.x 项目中的 types/options.d.ts 
 
export interface ComponentOptions<
  V extends Vue,
  Data=DefaultData<V>,
  Methods=DefaultMethods<V>,
  Computed=DefaultComputed,
  PropsDef=PropsDefinition<DefaultProps>,
  Props=DefaultProps> {
  data?: Data;
  props?: PropsDef;
  propsData?: object;
  computed?: Accessors<Computed>;
  methods?: Methods;
  watch?: Record<string, WatchOptionsWithHandler<any> | WatchHandler<any>>;
 
  el?: Element | string;
  template?: string;
  // hack is for functional component type inference, should not be used in user code
  render?(createElement: CreateElement, hack: RenderContext<Props>): VNode;
  renderError?(createElement: CreateElement, err: Error): VNode;
  staticRenderFns?: ((createElement: CreateElement) => VNode)[];
 
  beforeCreate?(this: V): void;
  created?(): void;
  beforeDestroy?(): void;
  destroyed?(): void;
  beforeMount?(): void;
  mounted?(): void;
  beforeUpdate?(): void;
  updated?(): void;
  activated?(): void;
  deactivated?(): void;
  errorCaptured?(err: Error, vm: Vue, info: string): boolean | void;
  serverPrefetch?(this: V): Promise<void>;
 
  directives?: { [key: string]: DirectiveFunction | DirectiveOptions };
  components?: { [key: string]: Component<any, any, any, any> | AsyncComponent<any, any, any, any> };
  transitions?: { [key: string]: object };
  filters?: { [key: string]: Function };
 
  provide?: object | (() => object);
  inject?: InjectOptions;
 
  model?: {
    prop?: string;
    event?: string;
  };
 
  parent?: Vue;
  mixins?: (ComponentOptions<Vue> | typeof Vue)[];
  name?: string;
  // TODO: support properly inferred 'extends'
  extends?: ComponentOptions<Vue> | typeof Vue;
  delimiters?: [string, string];
  comments?: boolean;
  inheritAttrs?: boolean;
}

在后面的定义中可以看到,该类型被  @vue/composition-api  引用后一般取别名为 Vue2ComponentOptions 。

composition 式组件 options 类型基础接口

继承自符合当前泛型约束的 Vue2ComponentOptions,并重写了自己的几个可选属性:

interface ComponentOptionsBase<
  Props,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {}
>
  extends Omit<
    Vue2ComponentOptions<Vue, D, M, C, Props>,
    'data' | 'computed' | 'method' | 'setup' | 'props'
  > {
  data?: (this: Props, vm: Props) => D
  computed?: C
  methods?: M
}

setup 函数上下文类型接口

顾名思义,这就是 setup() 函数中第二个参数 context 的类型:

export interface SetupContext {
  readonly attrs: Record<string, string>
  readonly slots: { [key: string]: (...args: any[]) => VNode[] }
  readonly parent: ComponentInstance | null
  readonly root: ComponentInstance
  readonly listeners: { [key: string]: Function }
 
  emit(event: string, ...args: any[]): void
}

普通键值数据

export type Data = { [key: string]: unknown }

计算值选项类型

也是我们熟悉的 computed 选项键值对,值为普通的函数(即单个 getter)或 { getter, setter } 的写法:

export type ComputedOptions = Record<
  string,
  ComputedGetter<any> | WritableComputedOptions<any>
>

方法选项类型

export interface MethodOptions {
  [key: string]: Function
}

Vue 组件代理

基本就是为了能同时适配 options api 和类组件两种定义,弄出来的一个类型壳子:

// src/component/componentProxy.ts
 
// for Vetur and TSX support
type VueConstructorProxy<PropsOptions, RawBindings> = VueConstructor & {
  new (...args: any[]): ComponentRenderProxy<
    ExtractPropTypes<PropsOptions>,
    ShallowUnwrapRef<RawBindings>,
    ExtractPropTypes<PropsOptions, false>
  >
}
 
type DefaultData<V> = object | ((this: V) => object)
type DefaultMethods<V> = { [key: string]: (this: V, ...args: any[]) => any }
type DefaultComputed = { [key: string]: any }
 
export type VueProxy<
  PropsOptions,
  RawBindings,
  Data = DefaultData<Vue>,
  Computed = DefaultComputed,
  Methods = DefaultMethods<Vue>
> = Vue2ComponentOptions<
  Vue,
  ShallowUnwrapRef<RawBindings> & Data,
  Methods,
  Computed,
  PropsOptions,
  ExtractPropTypes<PropsOptions, false>
> &
  VueConstructorProxy<PropsOptions, RawBindings>

组件渲染代理

代理上的公开属性,被用作模版中的渲染上下文(相当于 render 中的 this):

// src/component/componentProxy.ts
 
export type ComponentRenderProxy<
  P = {}, // 从 props 选项中提取的类型
  B = {}, // 从 setup() 中返回的被称作 RawBindings 的绑定值类型
  D = {}, // data() 中返回的值类型
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  PublicProps = P
> = {
  $data: D
  $props: Readonly<P & PublicProps>
  $attrs: Data
} & Readonly<P> &
  ShallowUnwrapRef<B> &
  D &
  M &
  ExtractComputedReturns<C> &
  Omit<Vue, '$data' | '$props' | '$attrs'>

属性类型定义

也就是 String、String[] 等:

export type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
 
type PropConstructor<T> =
  | { new (...args: any[]): T & object }
  | { (): T }
  | { new (...args: string[]): Function }

属性验证类型定义

export interface PropOptions<T = any> {
  type?: PropType<T> | true | null
  required?: boolean
  default?: T | DefaultFactory<T> | null | undefined
  validator?(value: unknown): boolean
}

兼容字符串和验证对象的 props 类型定义

export type ComponentPropsOptions<P = Data> =
  | ComponentObjectPropsOptions<P>
  | string[]
 
export type ComponentObjectPropsOptions<P = Data> = {
  [K in keyof P]: Prop<P[K]> | null
}
 
export type Prop<T> = PropOptions<T> | PropType<T>

III. 官网文档中的 props

因为 defineComponent 的几种签名定义主要就是围绕 props 进行的

Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:
Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})
https://cn.vuejs.org/v2/guide/components-props.html#Prop-%E7%B1%BB%E5%9E%8B

...到这里,我们只看到了以字符串数组形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
但是,通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:
props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}
为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:
Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

IV. defineComponent 函数签名

有了上面这些印象和准备,正式来看看 defineComponent() 函数的几种签名:

签名 1:无 props

这种签名的 defineComponent 函数,将适配一个没有 props 定义的 options 对象参数,

// overload 1: object format with no props
export function defineComponent<
  RawBindings,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {}
>(
  options: ComponentOptionsWithoutProps<unknown, RawBindings, D, C, M>
): VueProxy<unknown, RawBindings, D, C, M>

也就是其对应的 VueProxy 类型之 PropsOptions 定义部分为 unknown :

// src/component/componentOptions.ts 
 
export type ComponentOptionsWithoutProps<
  Props = unknown,
  RawBindings = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {}
> = ComponentOptionsBase<Props, D, C, M> & {
  props?: undefined
  emits?: string[] | Record<string, null | ((emitData: any) => boolean)>
  setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>

在上面的测试用例中就是  [test case 1] 的情况。

签名 2:数组形式的 props

props 将被推断为 { [key in PropNames]?: any } 类型:

// overload 2: 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 = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
>(
  options: ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M>
): VueProxy<Readonly<{ [key in PropNames]?: any }>, RawBindings, D, C, M>

将 props 匹配为属性名组成的字符串数组:

// src/component/componentOptions.ts
 
export type ComponentOptionsWithArrayProps<
  PropNames extends string = string,
  RawBindings = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Props = Readonly<{ [key in PropNames]?: any }>
> = ComponentOptionsBase<Props, D, C, M> & {
  props?: PropNames[]
  emits?: string[] | Record<string, null | ((emitData: any) => boolean)>
  setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
在上面的测试用例中就是  [test case 2] 的情况。

签名3:非数组 props

// overload 3: object format with object props declaration
// see `ExtractPropTypes` in ./componentProps.ts
export function defineComponent<
  Props,
  RawBindings = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
>(
  options: HasDefined<Props> extends true
    ? ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M, Props>
    : ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M>
): VueProxy<PropsOptions, RawBindings, D, C, M>

这里要注意的是,如果没有明确指定([test case 5、6]) Props 泛型,那么就利用 ExtractPropTypes 从 props 中每项的 PropType 类型定义自动推断([test case 7]) 。

// src/component/componentOptions.ts
 
export type ComponentOptionsWithProps<
  PropsOptions = ComponentPropsOptions,
  RawBindings = Data,
  D = Data,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Props = ExtractPropTypes<PropsOptions>
> = ComponentOptionsBase<Props, D, C, M> & {
  props?: PropsOptions
  emits?: string[] | Record<string, null | ((emitData: any) => boolean) >
  setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
// src/component/componentProps.ts
export type ExtractPropTypes<
  O,
  MakeDefaultRequired extends boolean = true
> = O extends object
  ? { [K in RequiredKeys<O, MakeDefaultRequired>]: InferPropType<O[K]> } &
      { [K in OptionalKeys<O, MakeDefaultRequired>]?: InferPropType<O[K]> }
  : { [K in string]: any }
 
// prettier-ignore
type InferPropType<T> = T extends null
  ? 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`
    : T extends ObjectConstructor | { type: ObjectConstructor }
      ? { [key: string]: any }
      : T extends BooleanConstructor | { type: BooleanConstructor }
        ? boolean
        : T extends FunctionConstructor
          ? Function
          : T extends Prop<infer V>
            ? ExtractCorrectPropType<V> : T;

V. 开发实践

除去单元测试中几种基本的用法,在以下的 ParentDialog 组件中,主要有这几个实际开发中要注意的点:

  • 自定义组件和全局组件的写法

  • inject、ref 等的类型约束

  • setup 的写法和相应 h 的注入问题

  • tsx 中 v-model 和 scopedSlots 的写法

ParentDialog.vue

<script>
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>({
  // tsx 中自定义组件依然要注册
  components: {
    ChildTable
  },
  props: {
    show: {
      type: Boolean,
      default: false
    },
    specFn: {
      type: Function,
      default: noop
    }
  },
 
  // note: setup 须用箭头函数
  setup: (props, context) => {
 
    // 修正 tsx 中无法自动注入 'h' 函数的问题
    // eslint-disable-next-line no-unused-vars
    const h = getCurrentInstance()!.$createElement;
 
    const { emit } = context;
    const { specFn, show } = props;
 
    // filter 的用法
    const { withColon } = filters;
 
    // inject 的用法
    const pageType = inject<CompSpecType>('pageType', 'foo');
    const dictComponents = inject<Ref<DictComp[]>>('dictComponents', ref([]));
    
    // 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 = '请选择部件';
        return false;
      }
      for (let i = 0; i < model.length; i++) {
        const { brand_id, data } = model[i];
        if (!brand_id) {
          err2.value = '请选择品牌';
          return false;
        }
        if (
          formdata.value.model.some(
            (m, midx) => midx !== i && String(m.brand_id) === String(brand_id)
          )
        ) {
          err2.value = '品牌重复';
          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: 在 tsx 中,element-ui 等全局注册的组件依然要用 kebab-case 形式 ?????
    return () => (
      <CommonDialog
       
        title="新建"
        width="1000px"
        labelCancel="取消"
        labelSubmit="确定"
        vLoading={loading.value}
        show={show}
        onClose={onClose}
        onSubmit={onSubmit}
      >
        <el-form labelWidth="140px">
         <el-form-item label={withColon('部件类型')} required={true} error={err1.value}>
            <el-select
             
              model={{
                value: formdata.value.component_id,
                callback: (v: string) => {
                  formdata.value.component_id = v;
                  _lookupSpecs(v);
                }
              }}
            >
              {dictComponents.value.map((dictComp: DictComp) => (
                <el-option key={dictComp.id} label={dictComp.component_name} value={dictComp.id} />
              ))}
            </el-select>
          </el-form-item>
          {formdata.value.component_id ? (
              <el-form-item labelWidth="0" label="" required={true} error={err2.value}>
                <child-table
                  list={formdata.value.model}
                  onChange={(v: Spec['model']) => {
                    formdata.value.model = v;
                  }}
                  onError={(err: string) => {
                    err3.value = err;
                  }}
                  scopedSlots={{
                      default: (scope: any) => (
                        <p>{ scope.foo }</p>
                      )
                  }}
                />
              </el-form-item>
          ) : null}
        </el-form>
      </CommonDialog>
    );
  }
});
</script>
 
<style scoped>
</style>

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) => (<Bar/>) }} 写法

  • defineComponent 并不适用于函数式组件,应使用 RenderContext<interface> 解决

 作为一位过来人也是希望大家少走一些弯路,希望能对你带来帮助。(WEB自动化测试、app自动化测试、接口自动化测试、持续集成、自动化测试开发、大厂面试真题、简历模板等等),相信能使你更好的进步!

留【自动化测试】即可【自动化测试交流】:574737577(备注ccc)icon-default.png?t=N4P3http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=8hUKwUHn9HhVpR8qWhqfT2u-kU-3hpsF&authKey=47BBG1nwHVOka38EQeJevQFCP%2BeVEf%2Bpd8QqotS1%2FqyJdrGAo1A6%2BfS9ef3wJij2&noverify=0&group_code=574737577

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/649895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯安全联动头部企业携手验证“数字安全免疫力”模型框架

6月13日&#xff0c;腾讯安全联合IDC发布“数字安全免疫力”模型框架及《加强企业数字安全免疫力&#xff0c;助力数字时代下的韧性发展》白皮书&#xff0c;提出用免疫的思维应对新时期下安全建设与企业发展难以协同的挑战&#xff0c;围绕“数据”和“数字业务”建立三层由内…

视觉SLAM十四讲——ch8实践(视觉里程计2)

视觉SLAM十四讲----ch8的实践操作及避坑 0.实践前小知识介绍1. 实践操作前的准备工作2. 实践过程2.1 LK光流2.2 直接法 3. 遇到的问题及解决办法3.1 编译时遇到的问题 0.实践前小知识介绍 里程计的历史渊源是什么&#xff1f; 里程计是一种用来测量车辆或机器人行驶距离的装置…

Uniapp uni-app学习与快速上手

个人开源uni-app开源项目地址&#xff1a;准备中 在线展示项目地址&#xff1a;准备中 什么是uni-app uni&#xff0c;读 you ni&#xff0c;是统一的意思。 Dcloud即数字天堂(北京)网络技术有限公司是W3C成员及HTML5中国产业联盟发起单位&#xff0c;致力于推进HTML5发展构…

MP : Human Motion 人体运动的MLP方法

Back to MLP: A Simple Baseline for Human Motion Prediction conda install -c conda-forge easydict 简介 papercodehttps://arxiv.org/abs/2207.01567v2https://github.com/dulucas/siMLPe Back to MLP是一个仅使用MLP的新baseline,效果SOTA。本文解决了人类运动预测的问…

iconfont彩色图标

进入阿里巴巴矢量图标库iconfont-阿里巴巴矢量图标库&#xff0c;添加图标到项目&#xff0c;然后下载至本地 解压后的本地文件如下&#xff0c;核心的是 iconfont.eot 文件 2.打开电脑命令行&#xff0c;执行以下命令&#xff0c;全局安装 iconfont-tools 转换工具 npm insta…

创新指南 | 推动销售的17个可落地的集客式营销示例

无论您是开启集客式的营销有一段时间还是处于起步阶段&#xff0c;了解像您这样的企业是如何粉碎竞争对手的的集客式策略总是有帮助的。无论您的公司做什么&#xff0c;它所服务的行业&#xff0c;是B2B还是B2C &#xff0c;您都可以在这里找到许多可以使用的示例。 在本文中&…

rtklib短基线分析(香港基站)

1、下载香港基站 用filezilla下载&#xff0c;地址ftp://ftp.geodetic.gov.hk 下载hklt、hkkt&#xff0c;这两座站的观测值及星历&#xff0c;必须同一天。用crx2rnx工具解压。 2、打开rtklib的RTKPOST&#xff0c;输入文件&#xff0c;如下图所示。 选择static模式&#xf…

C语言进阶--指针(C语言灵魂)

目录 1.字符指针 2.指针数组 3.数组指针 4.数组参数与指针参数 4.1.一维数组传参 4.2.二维数组传参 4.3.一级指针传参 4.4.二级指针传参 5.函数指针 6.函数指针数组 7.指向函数指针数组的指针 8.回调函数 qsort函数 9.指针和数组笔试题 10.指针笔试题 前期要点回…

《机器学习公式推导与代码实现》chapter10-AdaBoost

《机器学习公式推导与代码实现》学习笔记&#xff0c;记录一下自己的学习过程&#xff0c;详细的内容请大家购买作者的书籍查阅。 AdaBoost 将多个单模型组合成一个综合模型的方式早已成为现代机器学习模型采用的主流方法-集成模型(ensemble learning)。AdaBoost是集成学习中…

python获取热搜数据并保存成Excel

python获取百度热搜数据 一、获取目标、准备工作二、开始编码三、总结 一、获取目标、准备工作 1、获取目标&#xff1a; 本次获取教程目标&#xff1a;某度热搜 2、准备工作 环境python3.xrequestspandas requests跟pandas为本次教程所需的库&#xff0c;requests用于模拟h…

牛客网基础语法51~60题

牛客网基础语法51~60题&#x1f618;&#x1f618;&#x1f618; &#x1f4ab;前言&#xff1a;今天是咱们第六期刷牛客网上的题目。 &#x1f4ab;目标&#xff1a;对每种的循环知识掌握熟练&#xff0c;用数学知识和循环结合运用熟练&#xff0c;对逻辑操作符运用熟练。 &am…

接口测试入门神器 —— Requests

起源 众所周知&#xff0c;自动化测试是软件测试爱好者毕生探索的课题。我认为&#xff0c;只要把 接口测试 做好&#xff0c;你的自动化测试就至少成功了一半。 应部分热情读者要求&#xff0c;今天跟大家一起了解 python 接口测试库- Requests 的基本用法并进行实践&#x…

【跑实验03】如何可视化GT边界框,如何选择边界框内部的边界框,如何可视化GT框和预测框,如何定义IoU阈值下的不同边界框?

文章目录 一、如何可视化GT边界框&#xff1f;二、GT框和预测框的可视化三、根据IoU阈值来选择 一、如何可视化GT边界框&#xff1f; from PIL import Image, ImageDrawdef draw_bboxes(image, bboxes, color"red", thickness2):draw ImageDraw.Draw(image)for bbo…

精雕细琢,Smartbi电子表格软件重构、新增、完善

Smartbi SpreadSheet电子表格软件自发布以来&#xff0c;我们一直关注着用户的诉求&#xff0c;也在不断地对产品进行改进和优化&#xff0c;确保产品能够持续满足用户需求。经过一段时间的努力&#xff0c;产品在各方面都有了明显的改进&#xff0c;接下来&#xff0c;让我们一…

全网最详细的postman接口测试教程,一篇文章满足你

1、前言   之前还没实际做过接口测试的时候呢&#xff0c;对接口测试这个概念比较渺茫&#xff0c;只能靠百度&#xff0c;查看各种接口实例&#xff0c;然后在工作中也没用上&#xff0c;现在呢是各种各样的接口都丢过来&#xff0c;总算是有了个实际的认识。因为只是接口的…

不写单元测试的我,被批了 ,怎么说?

我是凡哥&#xff0c;一年CRUD经验用十年的markdown程序员&#x1f468;&#x1f3fb;‍&#x1f4bb;常年被誉为职业八股文选手 最近在看单元测试的东西&#xff0c;想跟大家聊聊我的感受。单元测试这块说实在的&#xff0c;我并不太熟悉&#xff0c;我几乎不写单元测试&…

k8s 的 Deployment控制器

1. RS与RC与Deployment关联 RC&#xff08;Replication Controller&#xff09;主要作用就是用来确保容器应用的副本数始终保持在用户定义的副本数。即如果有容器异常退出&#xff0c;会自动创建新的pod来替代&#xff1b;而如果异常多出来的容器也会自动回收。K8S官方建议使用…

JDBC BasicDAO详解(通俗易懂)

目录 一、前言 二、BasicDAO的引入 1.为什么需要BasicDAO&#xff1f; 2.BasicDAO示意图 : 三、BasicDAO的分析 1.基本说明 : 2.简单设计 : 四、BasicDAO的实现 0.准备工作 : 1.工具类 : 2.JavaBean类 : 3.BasicDAO类 / StusDAO类 : 4.测试类 : 一、前言 第七节内容…

一文读懂物联网平台如何搞定80%以上的物联网项目

太卷了&#xff01;一套物联网平台就能搞定80%以上的项目&#xff1f;&#xff01; 在刚刚结束的AIRIOT4.0物联网平台发布会上&#xff0c;航天科技控股集团股份有限公司智慧物联事业部总经理田淼给出答案。 在主题演讲环节&#xff0c;田总以【80%的物联网项目服务商都会面临…

分组函数group by使用技巧

一、需求&#xff1a;获取销售版本组合 颜色&#xff08;属性名&#xff09; (黑色&#xff0c;白色…) 属性值集合 Datapublic static class ItemSaleAttrsVo{private Long attrId;private String attrName;//当前属性有多少种版本&#xff1a;黑色,白色,蓝色&#xff0c;这里…