关于vue3:记录几个vue3-demo项目开发的问题

39次阅读

共计 4212 个字符,预计需要花费 11 分钟才能阅读完成。

结尾

最近在学习 vue3(应用开发层面),做了一些小 demo 我的项目,像 [md-editor-v3],vue3-admin 等。喜爱在 vue 我的项目中应用jsx 语法来编写,简直我的项目中都应用到了ts,某一些写法,在谷歌的时候没找到,所以想做一个小总结。

之前看他人探讨过在我的项目中使 ts 的态度,作者持保留态度,有些中央会减少代码量,如果不相熟的话,甚至会耽搁开发进度。然而从开发公共组件及某些复用组件来看,ts 会帮助开发,而且可能减少浏览性。

如果文章内容有谬误,请帮助指出。

ts 中的 props

该问题次要存在于应用了 ts 开发的我的项目中,不区别是否应用jsx,从 string、string/object 混合、array 角度演示(default 值没有写出)。

js 开发组件中,props 类型验证会像上面这样写

props: {
    propA: String,
    propB: [String, Object],
    propC: [Array]
  }

如果 A 只能是 a\b\c 三个中的一个,B 如果只能是 css 款式,对象就须要限度,C 只能是数据中存在 a/b/c 中的组合,你可能会用到validator。然而没有类型,对开发提醒不敌对。

于是应用 ts 替换一下写法,

import {PropType, CSSProperties} from 'vue';

props: {
  propA: String as PropType<'a' | 'b' | 'c'>,
  propB: [String, Object] as PropType<string | CSSProperties>,
  propC: [Array] as PropType<Array<'a' | 'b' | 'c'>>,
}

能领会到的不言而喻的益处就是上面这样:



懒人必备的代码提醒~

看过 ant-design-vue 的源码的话,会发现他们应用了一个叫 vue-types 的库来封装了类型,适合的能够间接应用这种形式,传送门

不必去给 setup 的第一入参定义类型,会和 props 抵触,感兴趣的能够试试。

jsx 中的插槽

这个在 vue3-admin 中有用 card 组件做过演示,传送门

开发组件须要留神的是,如果让组件更好的反对 jsx 的话,就须要思考组件的插槽内容是通过 props 传递的还是通过插槽的形式传递的,前者多呈现在 jsx 语法中,而后者则少数状况是 .vue 模板语法中。

剖析 card 组件

import {SetupContext, EmitsOptions,} from 'vue';

setup(props, ctx: SetupContext<EmitsOptions>) {return () => {
      // 插槽获取肯定在函数组件(render)办法外部,否则提醒:this will not track dependencies used in the slot
      const slotDefault = getSlot({ctx});
      const slotTitle = getSlot({props, ctx}, 'title');
      const slotFunc = getSlot({props, ctx}, 'func');

      const cardClass = classnames('vra-card', props.border && 'vra-card-border');
      const headerClass = classnames('vra-card-header', props.headerClass);
      const bodyClass = classnames('vra-card-body', props.bodyClass);

      return (<div class={cardClass}>
          {slotTitle && (<div class={headerClass} style={props.headerStyle}>
              <div class="vra-card-title">{slotTitle}</div>
              {slotFunc && <div class="vra-card-func">{slotFunc}</div>}
            </div>
          )}
          <div class={bodyClass} style={props.bodyStyle}>
            {slotDefault}
          </div>
        </div>
      );
    };
  }

getSlot 办法是用来依据名称来主动获取起源中的插槽内容的,不论是 vue 插槽的形式,还是 jsx 的属性的形式,代码:

  
import {ComponentPublicInstance, SetupContext, EmitsOptions} from 'vue';

/**
 * 获取指定插槽内容
 * 办法设定:vue 组件中 v -slot 优先级高于 props
 *
 * @param param0 组件实例 instance,SetupContext(setup 二入参),props
 * @param name 插槽名或者 props 名
 * @returns VNode
 */
export const getSlot = (
  {
    instance,
    ctx,
    props = {}}: {
    instance?: ComponentPublicInstance;
    ctx?: SetupContext<EmitsOptions>;
    props?: any;
  },
  name = 'default'
) => {const targetSlot = instance?.$slots[name] || ctx?.slots[name];
  return (targetSlot ? targetSlot(instance) : '') || props[name];
};

card 组件中正文内容 插槽获取肯定在函数组件(render)办法外部 ,该问题的解释为:因为插槽内容是咱们通过办法手动获取的,理解过setup 的就晓得,它在整个生命周期中,只会执行一次,无论后续是否有状态变更,如果在 setup 中获取到插槽内容,那么后续插槽内容如果有更新,则在 card 中不会有相应的扭转。相同,如果是在 setup 中的 render 函数或则在内部 render 函数中,每次状态变更,render函数均会执行,及保障了最新的插槽内容能获取到。

jsx 中的双向绑定

尽管可能实现应用指令,但还是不必做这个打算,jsx 中均能够用 js 语法来代替(坚固下根底也没故障,之前看过一个问答说 jsx 中怎么 v -if,答复让人深思:js 的 if else 都不会了么?)。

原生的输出标签可能应用 vModel 实现双向绑定机制,例如 input、textarea 等

import {defineComponent, reactive, watchEffect} from 'vue';

export default defineComponent({setup() {
    const test = reactive({a: 1});

    watchEffect(() => {console.log(test.a);
    });

    return () => <input vModel={test.a} />;
  }
});

演示:

tsx 中会提醒 vModel 不存在,这时就须要本人定义 type 了,作者没有应用这种形式所以没有进一步尝试。我的项目中都是自行绑定数据

setup() {const val = ref('')
  return () => <input value={val.value} onChang={({target}: Event) => (val.value = (target as HTMLTextAreaElement).value)} />
}

target 须要断言能力失常,这样看起来好像写的代码更多了,啊哈!

antd 的图标

这一项个别呈现在开发 admin 我的项目时,配置左侧菜单,像上面这样:

ant-design 当初 react ui 库和 vue ui 库都将 icon 剔进来了,独自引入一个依赖来应用 icon,按需加载,确实不便,可是,当初要实现在数据库中配置菜单对应的 icon,而后在获取路由配置的时候拿过去显示,办法就只有两种:

  1. @ant-design/icons-vue 的所有组件导入,再做一个枚举,在服务端配置组件名称,渲染时依据组件名称匹配组件,像这样:

key-element 文件

import {WifiOutlined, WomanOutlined} from '@ant-design/icons-vue'

export default {
  WifiOutlined: <WifiOutlined />,
  WomanOutlined: <WomanOutlined />
}

应用

const router = [{path: '/index',meta: { title: '首页', icon: 'WifiOutlined'},}]
// render
<div>{keyElement[routerItem.meta.icon] }</div>

这样不迷信,icon 太多。上面的办法得以优化。

  1. 原理差不多,也是将全副导入,然而不是本人手动导入:
import {h} from 'vue';
// 导入所有组件
import * as Icon from '@ant-design/icons-vue';

// 已获取到的配置
const router = [{path: '/index',meta: { title: '首页', icon: 'WifiOutlined'},}]

// render
<div>{h(Icon[router.meta.icon]) }</div>

不出意外应用了 ts 会提醒以下谬误:

(property) MenuType.iconName?: any Element implicitly has an ‘any’ type because expression of type ‘any’ can’t be used to index type…

这里须要给 meta.icon 指明类型:

import Icon from '@ant-design/icons-vue/lib/icons';

export interface MenuType {
  path: string;
  children?: Array<MenuType>;
  meta?: {icon?: keyof typeof Icon;}
  // 其余类型
  // ...
}

const router: MenuType[] = [{path: '/index',meta: { title: '首页', icon: 'WifiOutlined'}}]

vue3 的 h 办法和 react 的 creatElemet 办法相似。

结尾

临时写到这儿,前面想到再更新吧,都比拟根底,看一遍就晓得了。

正文完
 0