当前位置:   article > 正文

vue3+ts组件库发布npm并实现按需引入_使用ts构建自己的npm方法库

使用ts构建自己的npm方法库

这里分两种情况来考虑打包我们的组件库:

  1. 整体打包:适宜包体积较小且使用时对于按需加载要求不是很高的项目(实现非常简单,无法按需加载)
  2. 按需加载打包:适宜包体积比较大或者子模块比较多,使用时需要使用到按需加载(实现相对复杂,能按需加载)

综上,我们最终采用第二种方式按需加载,这里对第一种方案也做一下介绍及实现

创建一个空项目

将组件相关代码移入新项目中,并保证组件能正常的使用

方案一

创建打包命令及入口文件

3、新建packages/index.ts

注册并导出所有组件

import BaseForm from '../src/components/BaseForm/index'
import BaseInput from '../src/components/BaseForm/components/BaseInput'
import BaseNumber from '../src/components/BaseForm/components/BaseNumber'
import BaseSelect from '../src/components/BaseForm/components/BaseSelect'
import BaseRadio from '../src/components/BaseForm/components/BaseRadio'
import BaseCheckbox from '../src/components/BaseForm/components/BaseCheckbox'
import BaseDate from '../src/components/BaseForm/components/BaseDate'
import BaseDateRange from '../src/components/BaseForm/components/BaseDateRange'
import BaseNumberRange from '../src/components/BaseForm/components/BaseNumberRange'
import BaseCascader from '../src/components/BaseForm/components/BaseCascader'
import BaseSwitch from '../src/components/BaseForm/components/BaseSwitch'
import BaseText from '../src/components/BaseForm/components/BaseText'
import BaseTable from '../src/components/BaseTable/index'
const components = [BaseForm, BaseTable, BaseInput, BaseNumber, BaseSelect, BaseRadio, BaseCheckbox, BaseDate, BaseDateRange, BaseNumberRange, BaseCascader, BaseSwitch, BaseText] // 组件集合
// const components = [BaseForm, BaseInput] // 组件集合
const install = function(Vue) {
  // 注册所有的组件
  components.forEach((item) => {
    Vue.component(item.name, item)
  })
}
// 如果是直接引入文件,就不用调用Vue.use()
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}
export default { install } // 必须要有导出
export {
  BaseForm as bsForm,
  BaseTable as bsTable,
  BaseInput as bsInput,
  BaseNumber as bsNumber,
  BaseSelect as bsSelect,
  BaseRadio as bsRadio,
  BaseCheckbox as bsCheckbox,
  BaseDate as bsDate,
  BaseDateRange as bsDateRange,
  BaseNumberRange as bsNumberRange,
  BaseCascader as bsCascader,
  BaseSwitch as bsSwitch,
  BaseText as bsText,
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

4、修改package.json

  • 增加scripts命令,对组件进行打包

“lib”: “vue-cli-service build --target lib ./packages/index.ts --name backstage-vue3 --dest lib”

  • 增加入口

“main”: “lib/backstage-vue3.umd.min.js”, // 文件目录根据lib打包目录之后来定

  • 如果需要发布到npm需要将private修改为false

“private”: false

5、排除第三方库打包

vue.config.js增加如下代码:

configureWebpack: {
  externals: {
    'element-plus': 'element-plus',
    vue: 'vue',
  },
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6、执行npm run lib命令,生产我们的lib文件夹

至此,通过npm run lib既可打包出来我们的组件

方案二

按需加载方式

方案一种因为我们打包入口是单一入口,打包出来的组件库是单文件的js,是没有办法实现按需加载需求的,只能通过如下方式:

import backstageVue3 from 'backstage-vue3'
import 'backstage-vue3/backstage-vue3.css'
Vue.use(backstageVue3)
  • 1
  • 2
  • 3

最终我们希望能实现,既能通过上述方式使用,另外也能按需引入组件,并且不需要手动按需引入组件对应样式

import { bsInput, bsSelect, bsForm, bsTable } from 'backstage-vue3'
  • 1

实现方式:

1、修改packages支持各个组件单独导出及整体导出

  • 新建packages/compoents,建立对应组件导出文件,这里举例bsInput组件

// packages/compoents/bsInput/index.ts

import BsInput from '../../../src/components/BaseForm/components/BaseInput'

BsInput.install = function(Vue) {
  Vue.component(BsInput.name, BsInput)
}

export default BsInput
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 修改packages/index.ts,这里主要是实现兼容方案一整体导入使用的方式
import BsForm from './components/bsForm'
import BsCascader from './components/bsCascader'
import BsCheckbox from './components/bsCheckbox'
import BsDate from './components/bsDate'
import BsDateRange from './components/bsDateRange'
import BsInput from './components/bsInput'
import BsNumber from './components/bsNumber'
import BsNumberRange from './components/bsNumberRange'
import BsRadio from './components/bsRadio'
import BsSelect from './components/bsSelect'
import BsSwitch from './components/bsSwitch'
import BsText from './components/bsText'
import BsTable from './components/bsTable'
import BsButtons from './components/bsButtons'
const components = [
  BsForm,
  BsCascader,
  BsCheckbox,
  BsDate,
  BsDateRange,
  BsInput,
  BsNumber,
  BsNumberRange,
  BsRadio,
  BsSelect,
  BsSwitch,
  BsText,
  BsTable,
  BsButtons,
] // 组件集合
const install = function(Vue) {
  // 注册所有的组件
  components.forEach((item) => {
    Vue.component(item.name, item)
  })
}
// 如果是直接引入文件,就不用调用Vue.use()
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

export default install
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

2、修改打包方式为多入口打包,单独将每个组件打成一个js文件,另外为了兼容方案一整体导入使用方式,也将packages/index.ts单独打包出来

  • package新建一个scripts命令:“lib”: “vue-cli-service build --mode npm”
  • 新增.env.npm环境变量文件
###
 # @Author: 陈宇环
 # @Date: 2023-05-26 11:21:51
 # @LastEditTime: 2023-05-26 11:21:54
 # @LastEditors: 陈宇环
 # @Description: 
### 
ENV = 'npm'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 修改vue.config.js打包配置

buildConfig

├─ build.js // 原来打包配置

└─ npmBuild.js // 多入口打包配置

// vue.config.js 
// 根据不同打包命令使用不同打包文件

const build = require('./buildConfig/build.js')
const npmBuild = require('./buildConfig/npmBuild.js')

module.exports = process.env.ENV === 'npm' ? npmBuild : build
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其中buildConfig/build.js为原来vue.config.js的配置内容:

/*
 * @Author: 陈宇环
 * @Date: 2023-05-26 10:50:26
 * @LastEditTime: 2023-05-26 11:09:14
 * @LastEditors: 陈宇环
 * @Description:
 */

const { defineConfig } = require('@vue/cli-service')
const path = require('path')
const resolve = (dir) => {
  path.resolve(__dirname, '../' + dir)
}

const config = defineConfig({
  transpileDependencies: true,
  productionSourceMap: false,
  configureWebpack: {
    resolve: {
      alias: {
        '@': resolve(__dirname, 'src'),
      },
    },
    externals: {
      'element-plus': 'element-plus',
    },
  },
  chainWebpack: (chain) => {
    const oneofsMap = chain.module.rule(/.js|jsx$/).oneOfs.store
    oneofsMap.forEach((item) => {
      item
        .use('babel-loader')
        .loader('babel-loader')
    })
  },
})

module.exports = config
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

buildConfig/npmBuild.js为我们本次多入口打包对应配置:

/*
 * @Author: 陈宇环
 * @Date: 2023-05-26 10:50:26
 * @LastEditTime: 2023-05-30 15:30:04
 * @LastEditors: 陈宇环
 * @Description:
 */

// const { defineConfig } = require('@vue/cli-service')
const path = require('path')
const fs = require('fs')
const { join } = path

const TerserPlugin = require('terser-webpack-plugin')

const resolve = (dir) => {
  return path.resolve(__dirname, '../' + dir)
}

/**
 * @desc 驼峰转横杠
 * @param {*} str
 */
function upperCasetoLine(str) {
  let temp = str.replace(/[A-Z]/g, function(match) {
    return '-' + match.toLowerCase()
  })
  if (temp.slice(0, 1) === '-') {
    temp = temp.slice(1)
  }
  return temp
}

// 将packages目录下的子目录组成如下格式数组'bs-form': resolve('packages/components/bsForm.ts')
function getComponentEntries(path) {
  const files = fs.readdirSync(resolve(path))
  console.log('files', files)

  const componentEntries = files.reduce((fileObj, item) => {
    //  文件路径
    const itemPath = join(path, item)
    //  在文件夹中
    const isDir = fs.statSync(itemPath).isDirectory()
    const [name, suffix] = item.split('.')

    //  文件中的入口文件
    if (isDir) {
      fileObj[`${upperCasetoLine(item)}`] = resolve(join(itemPath, 'index.ts'))
    } else if (suffix === 'js') {
      //  文件夹外的入口文件
      fileObj[name] = resolve(`${itemPath}`)
    }
    return fileObj
  }, {})
  return componentEntries
}

const config = {
  outputDir: resolve('lib'),
  configureWebpack: {
    resolve: {
      alias: {
        '@': resolve('src'),
      },
    },
    entry: {
      index: resolve('packages/index.ts'),
      ...getComponentEntries('packages/components'),
    },
    output: {
      //  文件名称
      filename: '[name]/index.js',
      //  构建依赖类型
      libraryTarget: 'umd',
      umdNamedDefine: false,
      //  依赖输出
      libraryExport: 'default',
      //  依赖名称
      library: 'ease-form',
    },
    externals: {
      'element-plus': 'element-plus',
      vue: 'vue',
    },
    optimization: {
      minimize: true,
      minimizer: [
        new TerserPlugin({
          terserOptions: {
            output: {
              comments: false, // 去掉注释
            },
          },
        }),
      ],
    },
  },
  css: {
    sourceMap: true,
    extract: {
      filename: '[name]/style.css',
    },
  },
  chainWebpack: (config) => {
    config.optimization.delete('splitChunks')
    config.plugins.delete('copy')
    config.plugins.delete('preload')
    config.plugins.delete('prefetch')
    config.plugins.delete('html')
    config.plugins.delete('hmr')
    config.entryPoints.delete('app')
  },
}

module.exports = config
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

3、执行打包,生成各个组件对应js及整体导出的js

npm run lib

执行结果:

至此,即可发布到npm了

我们这里使用的时候,想要按需引入单个组件,比如:bs-input

按上述执行结果,我们只能通过:

import bsInput from ‘backstage-vue3/lib/bs-input’

import ‘backstage-vue3/lib/bs-input/style.css’

每次这样使用是不是也忒麻烦了,我们可以通过babel来处理这样的问题。使用babel将

import { bsInput } from ‘backstage-vue3’

转化为:

import bsInput from ‘backstage-vue3/lib/bs-input’

import ‘backstage-vue3/lib/bs-input/style.css’

实现步骤

1、安装依赖: npm i babel-plugin-import -d-s

2、babel.config.js plugins增加

plugins: [

‘@vue/babel-plugin-jsx’,

[‘import’, { // 本次增加

libraryName: ‘backstage-vue3’,

libraryDirectory: ‘lib’,

style: (name) => {

if (notCssFile.includes(name)) {

return false

}

return ${name}/style.css

},

}, ‘backstage-vue3’],

],

至此,可以通过import { bsInput } from 'backstage-vue3’愉快的按需引入组件使用了

发布至npm

1、如果没有npm账号需要注册npm账号:https://www.npmjs.com/

2、切换npm源到官方镜像

查询当前镜像地址:
        npm get registry
设置淘宝镜像:
        npm config set registry http://registry.npm.taobao.org/
设置官方npm镜像:
        npm config set registry https://registry.npmjs.org/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3、在项目根目录打开cmd并登录npm

npm login

4、执行发布
npm publish

发布成功后 邮箱也会收到npm 发送的邮件提醒

5、确认发布成功

待优化问题

1.ts接口怎么获取

2.打包文件存在公共逻辑,如何抽离

源码及实现浅析

https://blog.csdn.net/junner443/article/details/131302051

作者:快落的小海疼

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/119062
推荐阅读
相关标签
  

闽ICP备14008679号