如何封装Vue组件并上传到npm

发布于:2024-04-19 ⋅ 阅读:(16) ⋅ 点赞:(0)

前言

环境准备

1.注册npm账号:npm | Home (npmjs.com)

2.保证当前环境安装了vue、webpack、node,以下工作将在该环境下进行(没有的小伙伴自行百度安装哈~)

3.一下用到的环境版本

  • webpack:v5.1.4
  • node:v12.10.0
  • vue:v2.6.14

4.创建一个基于webpack的vue项目,这个项目将会是我们的组件项目了。

目录结构构建

在我们日常开发的项目中,通常的目录结构可能会像下图,我们一般会在根目录下的src/views中进行我们所有页面的开发,App.vue作为主页面引入。

但是呢,我们现在是想要进行组件开发,那么在项目的目录结构上可根据个人习惯进行一定的调整和更改如下:

 这里我说明一下主要的目录

  • components:存放你所需要开发的所有组件的目录
  • components/lib:存放组件源码
  • components/css:存放可能需要用到的css样式(自定义)
  • components/lib/xxx/index.js:将组件注册魏全局组件
  • examples:最初项目的src,我们可以一边开发一边调试

注意:更改上述目录之后,配置文件也需要进行相应的改变,因为在webpack中,默认以sec/main.js作为入口文件,现我们已将scr->example,所以入口文件应该进行相应的修改

组件开发

这里以Loading组件为例,

  • components/lib/Loading/src/Loading.vue:组件源码
  • components/lib/Loading/src/index.js:单个注册全局组件
  • components/css/loading.scss:存放Loading组件的样式(这里使用的是scss方式,后面关于打包会提到)
  • components/lib/index.js:所有组件的全局注册

代码实例如下:

components/lib/Loading/src/Loading.vue

<template>
	<div class="beva-loading">
		<img class="loading-icon" :src="imgSrc" alt="">
	</div>
</template>
<script>
	export default {
  name: 'Loading',
  props: {
    imgSrc: {
      type: String,
      default: ''
    },
  },
  data() {
		return {}
	}
}
</script>

components/lib/Loading/src/index.js

import Loading from './src/Loading.vue'

Loading.install = function (Vue) { 
  Vue.component(Loading.name, Loading)
}
export default Loading

components/css/loading.scss

	.beva-loading {
	  background-color: rgba(0, 0, 0, 0.7);
	  border-radius: 0.4rem;
	  color: #ffffff;
	  position: fixed;
	  z-index: 99;
	  width: 7.46rem;
	  height: auto;
	  padding: 0.8rem;
	  text-align: center;
	  top: 20vh;
	  box-sizing: border-box;
	  margin-left: -3.73rem;
	  left: 50%;
	}
	.loading-icon {
	  width: 4rem;
	  height: 4rem;
	  animation: rotatingright 1s linear infinite;
    }
	/*自定义动画类----顺时针旋转(使用这个动画的时候才设置动画执行时间)*/
	@keyframes rotatingright {
	  transform-origin: 50% 50%;
	  0% {
	    transform: rotate(0deg);
	  }
	  50% {
	    transform: rotate(180deg);
	  }
	  100% {
	    transform: rotate(360deg);
	  }
	}

	/*自定义动画类----逆时针旋转(使用这个动画的时候才设置动画执行时间)*/
	@-webkit-keyframes rotatingleft {
	  0% {
	    -webkit-transform: rotate(0deg);
	  }
	  50% {
	    -webkit-transform: rotate(-180deg);
	  }

	  100% {
	    -webkit-transform: rotate(-360deg);
	  }
	}

components/lib/index.js

// 将组件库中定义的所有组件引进来,然后再导出
import Demo from './demo';
import Card from './card';
import Loading from './Loading';

const components = {
  Demo,
  Card,
  Loading
}
const install = function (Vue) { 
  // 判断是否安装,安装过就不继续往下执行
  if (install.installed) return
  Object.keys(components).forEach(component => { 
    Vue.component(components[component].name, components[component])
  })
}
export default {
  install
}

新建webpack.components.js文件,其中主要是对组件打包的配置

1.对打包输出的文件进行处理

2.如果用到了scss、css,图片,则还需要对这些文件进行额外处理

主要配置如下(其中有相关的注释,有兴趣可以自行查看)

const { VueLoaderPlugin } = require('vue-loader');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const glob = require('glob')
const path = require('path')
const list = {}// 动态导入多个组件的入口文件
// 封装组件的一些配置,因为组件打包是单独打包的,所以需要单独配置
async function makeList (dirPath, list) {
  // 拿到所有的入口文件的路径
  const files = await glob.sync(`${dirPath}/**/index.js`)//[ 'components/lib/button/index.js', 'components/lib/icon/index.js']
  // 取出上一级目录的文件名
  files.forEach(file => {
    let name = file.split('/')[2]
    // 判断name是否有后缀名,有的话去除
    if (name.includes('.')) {
      name = name.split('.')[0]
    }
    list[name] = `./${file}`
  })
  // console.log(list)
}
makeList('components/lib', list)
module.exports = {
  entry: list,
  mode:'development',
  output: {
    filename: '[name].umd.js',// webpack打包的时候会将name替换成入口的name名字,例如card.umd.js
    path: path.resolve(__dirname, 'dist'),
    library:'mui',
    libraryTarget: 'umd',// 指定输出格式,umd是一种模块,兼容了CommonJS、AMD、CMD
  },
  plugins: [
    new VueLoaderPlugin(),
    new CleanWebpackPlugin()
  ],
  module: {
    // 配置rules,即什么样的文件,使用什么样的loader
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: 'vue-loader',
          }
        ]
      },
      {
        test: /\.css$/,
        use: ['style-loader','css-loader']
      },// 这里只处理了css,而项目中组件中使用了scss文件,对于scss文件的处理这里使用的是gulp,下面会提到
      {
        test: /\.(png|jpg|gif|svg)$/i, // 匹配图片文件格式
        type: 'asset/resource', // 使用内置的asset模块处理资源文件
        generator: {
          filename: 'images/[name][ext]', // 输出的文件名格式
        },
      },
    ],
  },
}
  

ps:对于组件中使用的scss文件的处理,这里用到的是gulp,具体设置如下:

新建gulpfile.js,对css文件单独打包(下方用到的第三方依赖需要自定下载

const gulp = require('gulp');
// 需要把样式代码导入
const sass = require('gulp-sass')(require('sass'));
// 对css代码进行压缩
const minifyCSS = require('gulp-minify-css');
gulp.task('sass', async function () {
  return gulp.src('components/css/**/*.scss')
    .pipe(sass())// 将scss代码转换成css代码
    .pipe(minifyCSS())// 压缩css代码
    .pipe(gulp.dest('dist/css'))// 输出到打包目录
})

接下来就是新增打包命令:

在package.json文件中

最后的打包文件如下:

上传npm

修改package.json文件

在上传npm之前呢,我们有必要对这个组件库信息进行相应的配置,因为npm上发布的包是根据package.json的文件进行匹配的,所以,进行如下信息修改,一般指定如下信息

  • 删除私有属性private
  • description:描述
  • main:入口文件
  • keywords:关键字(方便用户搜索)
  • author:作者信息
  • files:望发布的文件目录(不需要将所有文件都上传到npm中)

距离如下:

添加md文件

打包&&发布

由于这里封装组件的方式是打包发布的方式,所以切记在发布之前进行打包,生成dist文件!

维护版本

手动修改package.json中的version或者执行npm version patch生成迭代一个版本

执行打包命令

npm run build

登陆npm

注意这一需要登陆官方仓库,如果之前连接的是淘宝镜像需要线切换回来。下面是查看仓库源和切换仓库的命令。

npm config get registry  // 查看仓库源
npm config set registry https://registry.npm.taobao.org
npm config set registry http://registry.npmjs.org 

 登陆npm,输入账号、密码、邮箱

npm login

 发布

npm publish

测试组件

安装

npm i vue-library-ui

引入&&使用 

 页面

 扩展

我在这个的基础上封装了另外一个组件vue2-edit-cron,主要是可以快速构建cron表达式,有兴趣也可以看看~:vue2-edit-cron - npm (npmjs.com)

总结

        该文组件封装的方式其实是打包发布的方式,这种方式是将装好的组件最终打包成一个或者多个js文件发布。这种方式使得开发和调试时更接近于一个前端项目。但是一旦引入图片等静态资源需要同个BASE64的方式打包到js,而对于字体一类较大的静态资源则根本无法引用

适用范围:没有或极少的依赖第三方插件、图片的组件的封装或JS方法的封装

后面经过了解知道,其实还有另外一种方式即,非打包方式,具体的对比如下:

打包发布 非打包发布
webpack 需要配置 无需配置
发布 发布前需要打包 发布前无需打包
引用静态文件 较小的图片可以通过BASE64方式打包仅js文件 随意使用
引用第三方依赖 可以引用,但如果第三方依赖包含较多的静态文件时可能会出现引用不到的情况 随意引用
被应用的文件 一个打包好的js 组件的入口文件
调试方法 在组件项目中即可调试 需要在引用组件的项目中的node_module中对用模块中调试