记录封装npm业务组件遇到的坑

背景

基于vue2已经开发了一个选择器web app,是单页应用。后续需求是要把这个spa变成一个npm组件,因为要考虑其他项目的使用。以下是一些改造的心得以及遇到的坑以及解决方案。

改造前的技术栈

vue2.7.x + @vue/cli-service + vue-i18n + vue-router + vuex + elementUI2.x + axios

除此之外,还安装了一些公司内部开发的包,影响的范围是在打包的时候会对文件做一些操作

评估与改造

平时如果只是封装一个sdk,就只是一些js代码,最多也就把必要的第三方npm包也打包进去,npm包体积不会太大。

但如果是业务组件,要考虑包的大小,考虑到宿主项目的UI框架不兼容,所以也要把UI框架也要打进去,至于其他第三方包,能省则省,该externals掉就externals掉。

盘点了下,以下是可以通过改代码,把以下第三方包直接省掉,以达到减少包大小的效果:

vue-router,我们可以用component来替换不同的组件,这样就可以平替掉vue-router

vuex,可以在根组件使用provide,为了在子组件、孙子组件inject后使用保留响应式,需要provide时,直接传this。如果有看过ElementUI2.x源码的同学肯定不陌生,这样就可以平替掉vuex

js 复制代码
// 父组件
data() {
    return {
        a: 1,
        b: 2
    }
},
provide() {
    return {
        docSelector: this
    }
},
js 复制代码
// 子组件
inject: ['docSelector'],
methods: {
    test() {
        console.log(this.docSelector.a)
        this.docSelector.b = 3
    }
}

vue-i18n,自己实现一个class,一个属性是用记录当前使用的多语言,一个方法是根据keyword读取多语言文件的字段返回值。以及需要写一个方法类似$t作为mixins导入到每个SFC,这样子就可以平替掉vue-i18n

遇到的问题

npm包使用的vue版本跟宿主项目的vue版本不一致导致的坑

这里直接先说结论:npm包需要externals的包,版本要尽可能的低,以兼容宿主项目的包。几个例子,npm项目[email protected],而宿主项目的vue2.6.x,这时候,宿主项目打包出来的产物,会有2份vue代码。因为版本不兼容。

经过评估,因为我们的npm项目依赖了2.6.x的Observer属性,所以npm项目需要从[email protected]降级到[email protected],并且配置package.jsonpeerDependencies,这个字段表示某个包期望宿主项目中已经存在的依赖,而不是由包本身直接安装。而且npm包打包的时候也要配置externals掉。

json 复制代码
{
  "peerDependencies": {
    "vue": ">2.6.0"
  }
}

以这个配置为例:

  1. 如果宿主项目的vue版本>2.6.0,打包之后,就只会有一份vue代码;
  2. 如果宿主项目的vue版本<2.6.0,打包之后,就会有两两份vue代码;
  3. 如果使用npm link本地调试,当且仅当npm包的vue版本 === 宿主项目的vue版本,才会打包一份vue代码,否则会有两份vue代码。举例子说,npm项目[email protected]以及宿主项目[email protected],打包只会有一份vue代码;npm项目[email protected],宿主项目[email protected],打包就会有两份vue代码。

为什么要考虑这个问题?因为遇到了以下的问题,也经历了好几天的debug,通过debugger仔细观察才知道有两份vue,才去思考为什么。

问题表现:

  1. 在项目内写的测试demo,可以正常展示el-tooltipel-loadingel-messages组件,但是在宿主项目项目npm link来加载本地包时,其他UI组件都可以正常展示,但是这几个组件不能正常显示。
  2. 发布正式npm包到私有npm仓库,宿主项目有些安装之后是正常显示,有些是不能正常显示对应的组件

看了el-tooltipel-loadingel-messages还有类似的源码,发现了会在SFC引入import Vue from 'vue',去做一些实现,譬如el-tooltip就是通过这个方法去做一些trick影响子组件的渲染顺序。所以这里的vue就会以npm项目的vue版本为准,所以如果版本对不上,就会出现两份。

样式隔离

  1. 如果UI库自带修改UI库css名前缀,就直接改,不行的话可以写插件,用ast改。见这里:github.com/JimFirst/ch...
  2. 如果有全局reset样式,也要手动改css名前缀
  3. vue sfc记得加上scope -><script scoped>

知识碎片补充

  1. 项目是用公司内部的cli脚手架搭建的,它实现了一键安装vue-router vuex vue-i18n,但是是使用了require来导入几个文件实现的。我发现哪怕我没有引用这三个包,但是最终打包还是会打进去,没有tree-shake掉,所以我自己改掉了用require引用的方法,才可以把这些包去掉。
  2. 之前都是直接配置webpack配置,对@vue/service配置不是特别熟悉,所以遇到问题都是直接看源码+debugger,基本都可以解决问题。记录一些代码片段:
json 复制代码
// package.json
"build:prod": "cross-env NODE_ENV=prod vue-cli-service build --mode production --report --skip-plugins xxxx,yyyy --target lib --name zzzzz --dest libdist libsrc/index.js",
  • 上文提到项目依赖了内部的一些npm包,而且会影响打包产物。如果需要跳过这些插件,我发现vue-cli-service官方的文档也没有介绍--skip-plugins这个字段是可以跳过某些插件的,我也是看源码才知道
js 复制代码
const env = process.env.NODE_ENV;
const path = require("path");
module.exports = {
  configureWebpack: (config) => {
    // 项目入口配置
    config.entry = {
      app: path.resolve(__dirname, "./main.js"),
    };
    config.mode = "production";
  },
  chainWebpack: (config) => {
    // 删除名为 svgs 的规则
    config.module.rules.delete("svg");
    // 添加新的 svgs 规则
    config.module
      .rule("svg")
      .test(/\.(svg)(\?.*)?$/)
      .type("asset/inline");

    // 删除名为 images 的规则
    config.module.rules.delete("images");
    // 添加新的 images 规则
    config.module
      .rule("images")
      .test(/\.(png|jpe?g|gif|webp|avif)(\?.*)?$/)
      .type("asset/inline");

    // 删除名为 fonts 的规则
    config.module.rules.delete("fonts");
    // 添加新的 fonts 规则
    config.module
      .rule("fonts")
      .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
      .type("asset/inline");
  },
};
  • 其实这些图片/字体资源应该要上CDN,因为是内部项目,没有CDN资源申请,所以需要把所有图片/字体都打包进去包里,而且不是抽出来。可以去webpack官网对asset/inline的描述:webpack.js.cn/guides/asse...

后续

后续的需求是希望不同的项目都可以接入这个组件,所以后续需要考虑改造成web component,已知有以下问题需要解决:props传参失去了响应式,不支持appendbody所以要做对应的处理。

相关推荐
尘风-随手记1 小时前
npm报错‘proxy‘ config is set properxy. See: ‘npm help config‘
前端·rust·npm
MonkeyKing_sunyuhua11 小时前
npm WARN EBADENGINE required: { node: ‘>=14‘ }
前端·npm·node.js
赵啸林17 小时前
npm 安装 pnpm 的详细步骤及注意事项
前端·npm·node.js
喆星时瑜17 小时前
npm 命令使用文档
前端·npm·node.js
斯~内克20 小时前
解密 `npm run dev`:从命令行到浏览器热更新的完整旅程
前端·npm
喆星时瑜1 天前
pnpm 命令使用文档
前端·npm·node.js
腥臭腐朽的日子熠熠生辉2 天前
nvm 安装某个node.js版本后不能使用或者报错,或不能使用npm的问题
前端·npm·node.js
uglyduckling04123 天前
小程序构建NPM失败
前端·小程序·npm
伟笑3 天前
npm 报错 unable to resolve dependency tree
前端·npm·node.js
大波V53 天前
mac npm run dev报错 error:0308010C:digital envelope routines::unsupported
前端·macos·npm