Vue 组件发布 NPM

547 阅读4分钟

基于Vue-cli4.x二次封装Element-ui组件并发布到npm

转载自:www.yuque.com/homacheuk/d…

其他: blog.csdn.net/shidouyu/ar…

  https://juejin.cn/post/6844904000655998984#heading-19
  
  https://juejin.cn/post/6844903926941089806#heading-29

需求

由于需要重新做一套基于el组件的公用组件,并且提供给多个子系统使用,因此这次选择将组件封装并发布到npm上。

正文

封装组件库的方式有很多,但我们从简单入手,直接使用vue-cli来创建,虽然有点冗余但是胜在方便调试组件。

安装vue-cli4并创建一个组件库项目

//安装脚手架
yarn global add @vue/cli
// 创建项目
vue create jz-vue-ui
  • 创建完成项目后(对创建流程不熟的小伙伴可以自行百度)我们就有了以下的项目目录。包含了基本项目需要的vuex、router等,这个因为我项目后期需要所以就增加了,可以看个人需要来确定是不是加上。

img

调整为

img

目录的结构我们参考下element的项目结构,需要将src重命名为examples, 并添加packages目录,用来存放我们的自定义组件.

但是cli默认会启动src下的服务,如果目录名变了,我们需要手动修改配置,vue-cli3中提供自定义打包配置项目的文件,我们只需要手动创建vue.config.js即可.我们具体修改如下:

1.首先修改入口文件地址为examples下的main.js,
2.其次将packages加入打包编译任务中
3.将@等路径昵称修改掉
const path = require('path') // 引入path模块
function resolve (dir) {
  return path.join(__dirname, dir) // path.join(__dirname)设置绝对路径
}
module.exports = {
  publicPath: './',
  // 1.更改入口和出口文件名
  pages: {
    index: {
      entry: 'examples/main.js',//修改入口
      template: 'public/index.html',
      filename: 'index.html'
    }

  },
  // 2.扩展 webpack 配置,使 packages 加入编译
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('./examples')) //3.修改快捷路径
      .set('components', resolve('./examples/components'))
      .set('views', resolve('./examples/views'))
      .set('assets', resolve('./examples/assets'))
    config.module
      .rule('js')
      .include
      .add('/packages/')
      .end()
      .use('babel')
      .loader('babel - loader')
      .tap(options => {
        // 修改它的选项...

        return options
      })
  },
  /* 输出文件目录:在npm run build时,生成文件的目录名称 */
  outputDir: 'jz-vue-ui',
  /* 放置生成的静态资源 (mixin、css、img、fonts) 的 (相对于 outputDir 的) 目录 */
  assetsDir: 'assets',
  /* 是否在构建生产包时生成 sourceMap 文件,false将提高构建速度 */
  productionSourceMap: false,
  /* 默认情况下,生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存,你可以通过将这个选项设为 false 来关闭文件名哈希。(false的时候就是让原来的文件名不改变) */
  filenameHashing: false,
  /* 代码保存时进行eslint检测 */
  lintOnSave: false,
  /* webpack-dev-server 相关配置 */
  devServer: {
    /* 自动打开浏览器 */
    open: true,
    /* 设置为0.0.0.0则所有的地址均能访问 */
    host: '192.168.0.142',
    port: 8080,
    https: false,
    hotOnly: false

  }
}

编写基于el的组件

  • 由于我们需要基于el来封装组件,因此我们先装下element-ui,按照网上说的直接在vue ui 上点击装也可以,这里我们使用传统的方式
yarn add element-ui -S

然后编写组件的时候直接使用按需引入会相对节省空间,因此我们按照官网的说法引入

yarn add babel-plugin-component -D

并在babel.config.js中添加

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    [
      'component',
      {
        libraryName: 'element-ui',
        styleLibraryName: 'theme-chalk'
      }
    ]
  ]
}
  • 下面我们拿一个Button组件来示范,这里只实现一个比较简单的组件。

    首先我们先在packages目录下新建一个Button目录,然后src里存放组件的源代码
    
<template>
  <div>
    <p>测试q</p>
    <Button>el-button</Button>
  </div>
</template>
<script>
import { Button } from 'element-ui'//按需引入
export default {
  name: 'jz-button',
  components: { Button }
}
</script>

Button的index.js里编写如下代码来作为vue的组件安装:

import Button from './src/index.vue'

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

export default Button

接下来我们在packages的入口文件中导入组件并安装导出)

这里需要注意,因为由于el有样式的css,且我们用的按需引入,因此要在这里去引入下

import 'element-ui/lib/theme-chalk/index.css'//引入el的样式
import Button from './Button/index'
import Input from './Input/index'
// 存储组件列表
const components = [Button]
// 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,则所有的组件都将被注册

const install = function (Vue) {
  // 判断是否安装

  if (install.installed) return
  // 遍历注册全局组件
  components.forEach(component => {
    Vue.component('jz-button', component)
  })
}
// 判断是否是直接引入文件

if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

export default {//引入全部
  // 导出的对象必须具有 install,才能被 Vue.use() 方法安装
  install

}
export {//局部引入
  Button,
  Input
}

测试代码

我们要想看到自己写的组件效果,可以将组件导入到examples目录下的main.js中,其本质就是一个项目的开发目录,我们只需要按照如下方式导入即可:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import jzUI from '../packages/index'
Vue.config.productionTip = false
Vue.use(jzUI) // 这里我们为了方便直接引入全部
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

接下来我们就可以在项目中使用我们的组件了

<template>
  <div id="app">
     <jz-button></jz-button>
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style lang="scss">
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &.router-link-exact-active {
      color: #42b983;
    }
  }
}
</style>

img

配置package.json文件

作为一个组件库,我们必须按照npm的发包规则来编写我们的package.json, 正常情况下我们需要配置

package文件的description,keywords等,具体介绍如下:

  • description 组件库的描述文本

  • keywords 组件库的关键词

  • license 许可协议

  • repository 组件库关联的git仓库地址

  • homepage 组件库展示的首页地址

  • main 组件库的主入口地址(在使用组件时引入的地址)

  • private 声明组件库的私有性,如果要发布到npm公网上,需删除该属性或者设置为false

  • publishConfig 用来设置npm发布的地址,这个配置作为团队内部的npm服务器来说非常关键,可以设置为私有的npm仓库

注意:由于我们使用的是不打包的模式,直接main入口路径修改为修改为jz-vue-ui/package,调用时的引入路径可以简化调用

  //我们先使用最基础的这几个属性即可
  "name": "jz-vue-ui",
  "version": "0.1.9",
  "private": false,
  "description": "jz前端公用组件库",
  "main": "jz-vue-ui/package",

其次是需要配置打包的命令,这个我们也先给他配上,虽然我们本次并不使用他

 "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",json
    "lint": "vue-cli-service lint",
    //包名 及打包入口
    "lib": "vue-cli-service build --target lib --name jz-vue-ui --dest lib packages/index.js"
  },

最后我们需要创建一个.npmignore忽略掉一些不需要上传的文件减少大小

# 忽略目录
examples/
#packages/
#public/

# 忽略指定文件
vue.config.js
babel.config.js
*.map

发布到npm

  • 这次我们使用非打包发布,因为我们组件库可能还会用到很多第三方库及资源,使用这种方式可以避免资源加载错误

打包发布和非打包发布对比如下:

img

  • 发布到npm的方法也很简单, 首先我们需要先注册去npm官网注册一个账号(记住要验证邮箱), 然后控制台登录即可,最后我们执行npm publish即可.具体流程如下
// 登录,并按提示输入账号密码
 npm login
 // 发布 每次发布需要提高版本号
 npm publish 

发布之后效果如下:

img

在项目中使用

  • 现在我们创建一个新的vue项目并引入我们的组件库
//安装组件库
yarn add jz-vue-ui 
  • 由于在配置main将引入路径修改为:jz-vue-ui/package,因此这里的引入可以直接使用jz-vue-ui
在main.js中引入
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { Input } from 'jz-vue-ui'
import './css/index.scss' // 覆盖css需要放在后面
Vue.config.productionTip = false
Vue.use(Input)
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
  • 这里我们会注意到一个问题,引入的路径并不止我们的组件库名,这是因为我们是没打包直接发布,因此需要手动去找到库的入口js,实际上安装完成后的组件库在node_modules中是这样的:

img

  • 接下来我们来使用它,其实使用方法和调试的时候差不多
<template>
  <div id="app">
    <jz-input></jz-input>
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>