从零搭建一个发布到npm的vue ui组件库支持按需引入

市面上有很多的UI组件库,比如iview、element、cobe ui等,但是每个公司的业务逻辑都不一样,很多通用的业务逻辑组件,所以这个时候就需要开发自己团队所需要的组件库。

以下内容轰炸,请留出足够时长观看。。。

技术栈

  1. vue cli3搭建项目
  2. npm组件将存放在npm
  3. webpack修改打包文件时需要一些webpack知识
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/229c7e4a4aea490fb029436cb45fff54.png

思路

  1. 搭建一个基础vue2项目框架
  2. 框架添加常用配置,自动化格式代码等
  3. 规划文件目录
  4. 配置不同打包配置以适应打包为组件库和demo
  5. 编写组件,组件需支持按需引用
  6. 编写demo
  7. 编写示例
  8. 发布到npm
  9. 使用npm发布的组件库项目需要做的处理

搭建一个基础vue2项目框架

  1. 搭建一个基础vue2项目框架
vue create xxx
  1. 框架添加常用配置,自动化格式代码等(非必选)
    创建项目的时候选择eslint+prettier,添加.eslintrc.js文件规定格式化规则,添加.eslintignore忽略eslint检测文件,vscode需安装插件Prettier - Code formatter。
    在这里插入图片描述

  2. 规划文件目录

...
|-- examples      // 原 src 目录,改成 examples 用作示例展示
|-- packages      // 新增 packages 用于编写存放组件
...

在这里插入图片描述

配置不同打包配置以适应打包为组件库和demo

这里选择处理方式是添加两个打包配置文件,以达到打出不同包的目的,根目录添加config目录存放打包文件。(因为修改了入口文件夹所以打包的时候需要把入口路径修改一下)

...
├─ config
│    ├─ config.demo.js              // 打包为demo配置文件
│    ├─ config.npm.js               // 打包为npm包配置文件
│    └─ utils.js                    // 打包配置工具方法
...
// config.demo.js 
const path = require('path')
const defaultSettings = require('../examples/settings')
function resolve(dir) {
  return path.join(__dirname, dir)
}
const name = defaultSettings.title || '组件库' // page title
const port = process.env.port || process.env.npm_config_port || 9528 // dev port

const demoBuildConfig = {
  // 修改入口
  pages: {
    index: {
      entry: 'examples/main.js',
      template: 'public/index.html',
      filename: 'index.html'
    }
  },
  productionSourceMap: false,
  devServer: {
    port: port,
    open: true,
    overlay: {
      warnings: false,
      errors: true
    }
  },
  configureWebpack: () => {
    return {
      name: name,
      resolve: {
        extensions: ['js', '.vue', '.json'],
        alias: {
          '@examples': resolve('../examples'),
          '@packages': resolve('../packages')
        }
      }
    }
  },
  chainWebpack(config) {
    config.plugins.delete('prefetch')
    config.module
      .rule('scss')
      .oneOf('vue')
      .end()
  }
}

module.exports = demoBuildConfig

// config.npm.js
const { resolve, getComponentEntries } = require('./utils')
const TerserPlugin = require('terser-webpack-plugin')

const npmBuildConfig = {
  // 输出文件目录
  outputDir: resolve('lib'),
  // webpack配置
  configureWebpack: {
    //  入口文件
    entry: getComponentEntries('packages'),
    //  输出配置 按需引入的关键。。。。
    output: {
      //  文件名称
      filename: '[name]/index.js',
      //  构建依赖类型
      libraryTarget: 'umd',
      umdNamedDefine: false,
      //  依赖输出
      libraryExport: 'default',
      //  依赖名称
      library: 'ws-grocery-store'
    },
    externals: {
      vue: {
        root: 'Vue',
        commonjs: 'vue',
        commonjs2: 'vue',
        amd: 'vue'
      }
    },
    optimization: {
      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 = npmBuildConfig
// utils.js
const fs = require('fs')
const path = require('path')
const join = path.join

const resolve = (dir) => path.join(__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
}

module.exports = {
  resolve,
  upperCasetoLine,
  /**
   * @desc 获取组件入口
   * @param {*} path
   */
  getComponentEntries(path) {
    const files = fs.readdirSync(resolve(path))

    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[`ws-${upperCasetoLine(item)}`] = resolve(join(itemPath, 'index.js'))
      } else if (suffix === 'js') {
        //  文件夹外的入口文件
        fileObj[name] = resolve(`${itemPath}`)
      }
      return fileObj
    }, {})

    return componentEntries
  }
}

// vue.config.js 
// 根据不同打包命令使用不同打包文件
const demoBuildConfig = require('./config/config.demo')
const npmBuildConfig = require('./config/config.npm')

module.exports = process.env.ENV === 'npm' ? npmBuildConfig : demoBuildConfig

编写组件

以上已做完相关配置,小喘一口气,可以开始愉快的编写组件了,接下来编写组件和常规项目抽组件并没什么区别。。。

1、创建一个新组件
  1. 在 packages 目录下,所有的单个组件都以文件夹的形式存储,所以这里创建一个目录button
  2. 在button文件夹创建button.scss、button.vue、创建 index.js 文件对外提供对组件的引用
  3. 在 packages 目录下创建index.js文件对外提供对组件的引用

在这里插入图片描述

// /packages/button/index.js
// 导入组件,组件必须声明 name
import button from './button.vue'

// 为组件提供 install 安装方法,供按需引入
button.install = function(Vue) {
  Vue.component(button.name, button)
}

// 默认导出组件
export default button

2、整合所有的组件,对外导出,即一个完整的组件库

修改 /packages/index.js 文件,对整个组件库进行导出。

import button from './button'
import card from './card'

// 存储组件列表
const components = [
  button,
  card
]

// 定义install 方法,接受Vue作为参数。如果使用use注册插件,则所有的组件都将被注册
const install = Vue => {
  // 判断是否安装
  if (install.installed) return
  components.forEach(Component => {
    Vue.component(Component.name, Component)
  })
}

//  如果浏览器环境且拥有全局Vue,则自动安装组件
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

export default {
  install,
  ...components
}

编写示例

走到这了,就和常规流程一样了,这个示例就是为了在编写的时候方便观察组件样式交互效果,examples这个文件夹作为示例或者组件ui文档项目使用。。

1、在示例中导入组件
  1. 全局导入
import Vue from 'vue'
import App from './App.vue'

// 导入组件库
import WsButton from '@packages/index'
// 注册组件库
Vue.use(WsButton)

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')
  1. 局部导入
// Home.vue
import WsButton from '@packages/button/button.vue'
import WsCard from '@packages/card/card.vue'

export default {
  name: 'HomeView',
  components: {
    WsButton,
    WsCard
  }
}
2、在示例中使用组件库中的组件

全局注册直接使用即可,局部导入需要引用在components中注册

<template>
  <div class="home">
    <ws-button />
    <br>
    <br>
    <br>
    <ws-card />
  </div>
</template>

发布到npm和发布组件说明(demo)

到这一步组件库已经开发完成了,剩下的就是把组件库发布到npm上供后续使用和发布组件示例和文档说明。

  1. package.json 中新增编译为库的命令
    在库模式中,Vue是外置的,这意味着即使在代码中引入了 Vue,打包后的文件也是不包含Vue的。在scripts中新增命令npm run lib、 npm run lib:build,不需要按需引用时直接只用lib打包。
  • –target: 构建目标,默认为应用模式。这里修改为 lib 启用库模式。
  • –dest : 输出目录,默认 dist。这里我们改成 lib。
  • [entry]: 最后一个参数为入口文件,默认为 src/App.vue。这里我们指定编译 packages/ 组件库目录。
"scripts": {
    "lib": "vue-cli-service build --target lib --name ws-grocery-store --dest lib packages/index.js",
    "lib:build": "vue-cli-service build  --mode npm"
  },
  1. 执行编译库命令(选择一种即可)
npm run lib

在这里插入图片描述

npm run lib:build

在这里插入图片描述

  1. 执行正常编译项目命令
npm run build

在这里插入图片描述

  1. 配置 package.json 文件中发布到 npm 的字段
  • name: 包名,该名字是唯一的。可在 npm 官网搜索名字,如果存在则需换个名字。
  • version: 版本号,每次发布至 npm 需要修改版本号,不能和历史版本号相同。
  • description: 描述。
  • main: 入口文件,该字段需指向我们最终编译后的包文件。
  • keyword:关键字,以空格分离希望用户最终搜索的词。
  • author:作者
  • private:是否私有,需要修改为 false 才能发布到 npm
  • license: 开源协议

参考设置

"name": "ws-grocery-store",
  "version": "0.0.10",
  "description": "charles的杂货铺 各种轮子合集",
  "author": "charles_ws",
  "main": "lib/index/index.js",
  "keyword": "ws-grocery-store vue",
  "private": false,
  1. 添加.npmignore 文件,设置忽略发布文件
    我们发布到 npm 中,只有编译后的 lib 目录、package.json、README.md才是需要被发布的。所以我们需要设置忽略目录和文件。
    和 .gitignore 的语法一样,具体需要提交什么文件,看各自的实际情况。
# 忽略目录
examples/
packages/
public/

# 忽略指定文件
vue.config.js
babel.config.js
*.map
  1. 登录到npm
    首先需要到npm注册一个账号,注册成功后在终端执行登录命令,输入用户名、密码、邮箱即可登录
    npm官网网址
npm login

登录成功之后 执行发布命令

npm publish
  1. 发布成功
    发布成功后,在npm官网就可以搜索到,有的博主说需要等待几分钟,目前还未遇到,发布就可以搜到,保险起见,可以稍等几分钟后搜索。
    在这里插入图片描述

使用新发布的组件库

安装

npm install ws-grocery-store -S

使用

  1. 全局引用
// 全局注册
//在 main.js 引入并注册
import WsButton from 'ws-grocery-store'
Vue.use(WsButton)

# 在组件中使用
<template>
  <ws-button />
</template>
  1. 按需引用
// babel.config.js配置
module.exports = {
  //...
  plugins: [
    [
      "import",
      {
        "libraryName": "ws-grocery-store",
        "style": (name) => {
            return `${name}/style.css`;
        }
      }
    ]
  ]
}

// 在组件中使用
<template>
  <ws-button />
</template>
<script>
import { WsButton } from '@/api/index'
export default {
  name: 'xxx',
  components: {
  	WsButton
  }
}
</script>

到此就全部结束了,这个框架也是边查边搭,有问题或者好的想法欢迎留言评论~

项目结构

在这里插入图片描述

项目地址

Gitee地址:https://gitee.com/Charles_ws/ws-grocery-store

npm地址:https://www.npmjs.com/package/ws-grocery-store

参考文章

掘金:https://juejin.cn/post/6844903687668629518
掘金:https://juejin.cn/post/6844904147049775118

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
要按引入 antd-vue2.6,你要进行以下配置步骤: 1. 首先,安装 babel-plugin-import 插件,该插件可以按引入 antd-vue2.6 的组件。你可以使用以下命令进行安装: ``` npm install babel-plugin-import --save-dev ``` 2. 在项目的根目录下找到 babel.config.js 文件,并添加以下代码: ```javascript module.exports = { plugins: [ ['import', { libraryName: 'ant-design-vue', libraryDirectory: 'es', style: true }] ] } ``` 3. 接下来,在你的 main.js 文件中引入 antd-vue2.6 的样式文件。你可以使用以下代码: ```javascript import 'ant-design-vue/dist/antd.css' ``` 4. 现在,你可以在要使用 antd-vue2.6 组件的地方直接按引入了。例如,如果你要使用 Button 组件,你可以在文件的开头添加以下代码: ```javascript import { Button } from 'ant-design-vue' ``` 5. 最后,在你的 Vue 实例中注册 Button 组件。你可以使用以下代码: ```javascript Vue.use(Button) ``` 这样,你就成功地进行了 antd-vue2.6 的按引入配置。请注意,如果你要使用其他组件,只要按照相同的步骤进行配置即可。参考中的更详细讲解以获取更多信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [搭建vue项目 -- vue-cli + Element UI + ant-design-vue + 各种安装包详解](https://blog.csdn.net/elephant_my/article/details/114264294)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值