你好获取登录信息中




首页 主站 文章列表 分类列表
查看文章返回文章列表

vue组件库开发踩坑记

发布时间:2021-07-30 17:28:12 by:
最后更新时间:2021-07-30 17:29:36 by:



为了之后网站的更新,我着手做了一个自己的ui库。再之前测试打包上传到NPM然后再从其他项目中安装引入时,它似乎运行良好,所以我也就没太在意了。

可是,最近我更新了vue3版本之后,并且在另一个项目中实际使用时,它开始报错了,初步一看,似乎是因为我外面没有包提供Provide告知组件显示成什么样子的组件导致的,可是我有设置默认值啊。难道是vue-property-decorator的锅导致默认值没有拿到?于是回组件开发那边去掉外面包裹的组件进行尝试,发现是可以获取默认值的。

实在是没想到是怎么回事,于是我想既然你就是要有个Provide,那我这边也给你写那个组件好了。可是,当我自信的按下ctrl+s然后在浏览器上按下F5之后,页面彻底没了显示,同时控制台显示了一个这样的警告:

  1. Invalid VNode type: Symbol()

对于这个问题,我毫无头绪,只能上网去找,可是找到的几个情况都跟我这个不一样,所以并不能解决问题。于是我把代码恢复到了之前的状态,准备详细查看,然后我从一大堆error中找到了这样一条不起眼的警告:

  1. inject() can only be used inside setup() or functional components.

好家伙,这就去看vue-property-decorator那边Inject的实现:

  1. export function Inject(
  2. options: InjectOptions = Object.create(null),
  3. ): VueDecorator {
  4. return createDecorator((componentOptions, key) => {
  5. const originalSetup = componentOptions.setup
  6. componentOptions.setup = (props, ctx) => {
  7. const result = originalSetup?.(props, ctx)
  8. const injectedValue = inject(options.from || key, options.default)
  9. return {
  10. ...result,
  11. [key]: injectedValue,
  12. }
  13. }
  14. })
  15. }

嗯,确实是用setup做的啊,并没有什么问题。思来想去老久,没有想到啥答案。唯一能想到的是文档中提到了一句:


如果使用字符串 key 或非类型化 symbols,则需要显式声明注入值的类型:

  1. const foo = inject<string>('foo') // string | undefined`

但是我这样修改之后,问题仍然没法解决。

过了一天之后,想到那么vue是怎么判断我是否是在setup中调用的呢,会不会因为是因为vue3现在还不完善的bug?正好整个警告的点上是有堆栈信息的,所以我也就直接点击进去打了个断点一看,它的代码如下:

  1. function inject(key, defaultValue, treatDefaultAsFactory = false) {
  2. // fallback to `currentRenderingInstance` so that this can be called in
  3. // a functional component
  4. const instance = currentInstance || currentRenderingInstance;
  5. if (instance) {
  6. //...
  7. }
  8. else if ((process.env.NODE_ENV !== 'production')) {
  9. warn(`inject() can only be used inside setup() or functional components.`);
  10. }
  11. }

好了,首先确定了这个判断肯定是在vue调用setup时对currentInstance赋值,调用render时对currentRenderingInstance赋值,然后通过这两个变量可以获取到当前从属的是哪个组件。可是问题来了啊,为什么这个值会变成空的呢?回前几步继续调试看看。似乎没啥问题,每个组件都正常赋值了,包括这个组件也一样。那好,我多跟踪几步看是啥时候丢的。

可是跟踪了一圈好像没有其他地方会导致currentInstance变为空啊。似乎又没头绪你,然后我再调试了一次。嗯,等等,怎么还跳了个文件呢?再一看上面打开的文件才发现,好家伙,我们的组件创建都是在项目所属的文件夹下的node_modules中,可是一到inject这里却跳到了node_modules/@thestarweb/ui/node_modules中,去看了下,这个文件真实存在。

嗯,破案了。同一个模块在不同的文件中,因为闭包,或者说模块独立性的问题,他们的变量当然是独立的。那怎么解呢?为什么npm会给我安装两个同样的模块呢。个人觉得npm可能是为了防止冲突,特别是版本冲突,所以,每个模块的运行依赖都会独立安装(这也是个麻烦事,所以cnpm在这里会用很多的快捷方式,而不是完全独立安装一份新的,但是我用的还是npm装的,不是说cnpm不好,而是这样可能会把问题掩盖过去,然后就一直没修复)。那么我要怎么操作才能告诉npm,安装我这个库就必须安装vue,并且这个vue跟项目还是得共享同一个呢?那就找找现成的别人的写法咯,比如ElementUI的package.json文件,然后就会发现有peerDependencies这么一个东西,它就是告诉npm,依赖这个包的项目必须要包含的依赖,并且这个依赖会在项目一级。这样就完事了?提交代码,github-action开始自动给我打包发布,然后我收到了运行出错的邮件,显示错误内容如下:

  1. Error: node_modules/vue-class-component/dist/vue-class-component.d.ts(1,39): error TS2307: Cannot find module 'vue' or its corresponding type declarations.

peerDependencies的依赖并不会主动安装啊,那么的话,就只能在devDependencies中也加入这个依赖,这样这个依赖就必定会安装了,ElementUI也是这么做的。虽然感觉还是有点怪怪的,但是至少,现在我这个库终于是真实可用的了。

评论
    还没有评论
发表评论
正在等候用户中心返回数据
杂项
。。。