手把手教你写一个Vue3组件库但是乞丐版
admin
2024-03-03 09:56:53
0

好久没写文章了,最近在研究一些组件库的实现方法,分享一下。在这我这篇文章之前其实已经有一篇文章讲了Vue如何打包组件库了(最底部),但是这篇文章一是没有源码二是Vue3和Vue2的组件库写法有点不一样,关于这个我研究了ElementPlus源码一下午,终于找到了正确的使用方法,所以就有了这篇文章。在翻看Element-plus源码的时候发现Element-plus的组件库基本架构与我们本质是一样的,所以本文可以放心食用。

关于使用组件库方式

随便打开一个Vue3的UI组件库,我们可以看到通常有两种引入方法。

  • 第一种:通过script的方式引入
  • 第二种:install后通过import的方法引入

无论第一种还是第二种,引入之后我们通常会这样

const app = createApp(App)
app.use(MyComponents)
app.mount('#app') 

对于第一种script标签引入方式,Vue2和Vue3有着不同的引入方法,下面会详细讲到。 之后我们便可以使用MyComponents里面的所有组件了。在这个app.use里面到底发生了什么,为什么会这样?让我们深入兔子洞一探究竟。(源码在文章最下方)

随便写两个组件

先搭建一个Vue项目,搭建方式可以用cli也可以用vite,我自己简单的通过webpack搭建了一个,这里不再赘述搭建过程。

下面给出两个Vue组件,之后我们将会做出一个包含这两个组件的丐版组件库

封装、构建

文章开头有讲到,组件的使用方式为app.use(我们的组件)所以我们打开官方文档看一下app.use的定义。cn.vuejs.org/api/applica…

下面给出代码实现

// src/index.js
import Hello from './component/Hello/Hello.vue';
import Hi from './component/Hi/Hi.vue';const components = [Hello,Hi,
];
const componentName = ['Hello','Hi',
];
const install = function (Vue) {components.forEach((component, index) => {Vue.component(componentName[index], component);});
};export default {install,
};if (typeof window !== 'undefined' && window.Vue) {install(window.Vue);} 

代码层面的功夫我们就做好了,现在我们需要修改一下webpack配置

 output: {path: path.join(__dirname, '/lib'),filename: () => '[name].js',libraryTarget: 'umd', // 用到的模块定义规范},externals: {vue: {root: 'Vue', // 通过 script 标签引入,此时全局变量中可以访问的是 Vuecommonjs: 'vue', // 可以将vue作为一个 CommonJS 模块访问commonjs2: 'vue', // 和上面的类似,但导出的是 module.exports.defaultamd: 'vue', // 类似于 commonjs,但使用 AMD 模块系统},}, 

文章最底下的链接中文章有说明libraryTargetexternals的作用,这里不再赘述。 之后将构建工具的入口设置为index.js构建。构建完成后我们将package.json的入口设置为我们的构建结果的入口文件,例如我的构建结果为lib/main.js。这样,一个超低配的组件库就封装好了,接下来我们测试一下。

本地测试

首先我们通过script脚本插入这一最简单的测试方法来进行测试



Vue3组件库

script引入!

渲染结果:

下面通过npm link和import的方式来测试组件库

npm link

首先在我们的组件库根目录运行

npm link 

之后,我们随便通过任意脚手架搭建一个vue项目,在它的根目录运行

npm link test_library 

这样我们就可以像使用普通组件库一样使用它了。

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import Hello from 'test_library'
const app = createApp(App)
app.use(Hello)
app.mount('#app')
// app.vue
 

细心的同学会发现,我们明明没有引入Hi组件,但却能使用这是为什么呢。其实import Hello from 'test_library'这个语句就相当于将lib/index.jsexport出来的对象进行了引入拿到的就是带有install的对象,这个install会在app.use()被调用,也就将两个组件都注册了。那么当我们不想要引入全部组件的时候该怎么做呢?答案是按需引入。

按需引入

所谓按需引入,就是要多少引入多少,例如这样import { Hi } from 'test_library'但是在没处理之前这样引用明显是不行的,毕竟我们的test_library本质上引入的是带有install的对象,所以根本没法拆分,对吧?

那该怎么办呢,答案是将入口变为多个,然后每个组件打包出自己专属的入口文件和css文件,这就开始做!

首先我们还是需要通过App.use()的方法来使用组件,这一点永远不会变,所以在每一个组件的组件文件夹下面写出他们自己的入口文件。

// src/component/Hello/index.js
import Hello from './Hello.vue';
Hello.install = function (Vue) {Vue.component('Hello', Hello);
};
export default Hello;// src/component/Hi/index.js
import Hi from './Hi.vue';
Hi.install = function (Vue) {Vue.component('Hi', Hi);
};
export default Hi; 

最后通过webpack的多入口打包功能,就可以做到分开打包了。

entry: {hello: './src/component/Hello/index.js',hi: './src/component/Hi/index.js',index: './src/index.js',},output: {path: path.join(__dirname, '/lib'),filename: () => '[name].js',libraryTarget: 'umd', // 用到的模块定义规范}, 

webpack的配置到这里还没算完,既然我们的vue组件进行了拆分,那每一个组件的css代码也要进行拆分才行,所以我们需要用到mini-css-extract-plugin这个插件。

const MiniCssExtractPlugin = require('mini-css-extract-plugin');plugins: [new VueLoaderPlugin(),new CleanWebpackPlugin(),new MiniCssExtractPlugin({filename: 'lib-style/[name].css',}),], 

最后的产物目录如下

接下来我们测试一下

import { createApp } from 'vue'
import App from './App.vue'
import Hi from 'test_library/lib/hi'
import 'test_library/lib/lib-style/hi.css'
const app = createApp(App)
app.use(Hi)
app.mount('#app') 

效果:

通过babel插件优化按需引入

上面的代码我们都实现的很好了,但是似乎有些不对劲,我们平时使用组件库的时候好像是这样的import { 组件a } from '组件库'。 但是也许有同学忘了,在你按需引入组件库的时候你通常还需要装一个babel插件,例如Element-plus里面会让你这样(element-plus.gitee.io/en-US/guide… ),这个插件做的事情就是将 import { 组件a } from '组件库转变成我们上面的引入方式。

现在让我们来使用一下,在我们用来测试组件库组件的项目中的babel.config.js文件加入这些代码

module.exports = {
···"plugins": [["component",{"libraryName": "test_library","styleLibrary": {"name": "lib-style", // same with styleLibraryName"base": false// if theme package has a base.css}}]]
···
} 

更改引用方式

import { createApp } from 'vue'
import App from './App.vue'
import { Hi } from 'test_library'
const app = createApp(App)
app.use(Hi)
app.mount('#app') 

查看最终效果

Vue2与Vue3到底有什么不同

我们都知道,在Vue2中我们创建vue实例的方法是new Vue(),而注册全局组件的方法是Vue.component(),所以由于这个特性我们只需要去简单的注册组件,那无论new多少个vue实例也是会有我们的注册组件的。所以在vue2组件库中如果我们想要在script中注册组件我们只需要,Vue.use(install)(这个install方法就是上面的install方法)。

但是在Vue3中,上面的办法行不通了,因为在Vue3中Vue.component()这个方法被移除了,取而代之的是createApp().component()这就导致了我们无法真正的全局注册并且更坏的情况是我们似乎没办法拿到这个create出来的vue实例。所以我们只能另辟蹊径通过文章中的方式来进行注册。具体Element-plus到底有没有写的和我一样,这个我无从得知代码阅读能力有限。但是可以肯定的是,当通过script引用他们的组件库时,window上却确实有ElementPlus这个对象。

// Vue2
import Hello from './component/Hello/Hello.vue';
import Hi from './component/Hi/Hi.vue';const components = [Hello,Hi,
];
const componentName = ['Hello','Hi',
];
const install = function (Vue) {components.forEach((component, index) => {Vue.component(componentName[index], component);});
};const VueComponents = { install }
if (typeof window !== 'undefined' && window.Vue) {Vue.use(install)
}
export default VueComponents
// Vue2在script中的使用,可以不用use,直接使用(因为已经注册)// Vue3
import Hello from './component/Hello/Hello.vue';
import Hi from './component/Hi/Hi.vue';const components = [Hello,Hi,
];
const componentName = ['Hello','Hi',
];
const install = function (Vue) {components.forEach((component, index) => {Vue.component(componentName[index], component);});
};const VueComponents = { install }if (typeof window !== 'undefined') {window.VueComponents = VueComponents
}
export default VueComponents
// Vue3在script中的使用,需要use


 

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

相关内容

热门资讯

“我真的撑不住了”,2000万... 5月14日、15日两天,知名搞笑博主“大连老湿王博文”,分别在微信公众号和小红书上发表长文,宣布断更...
原创 9... 邱 林 没有想到的是,日本对中东地区石油依赖度竟高达96%,其中,阿联酋占43%,沙特阿拉伯占39%...
华金策略:A股短期可能难大调整... 来源:市场资讯 来源:华金证券 投资要点 复盘历史,驱动TMT行情结束的核心因素是外部事件和政策偏空...
5月18日突然大跌,金价行情拐... 刚刷完5月18日凌晨的金价数据,伦敦金现直接暴跌113.8美元,报4537.83美元/盎司,单日跌幅...
深化资本与产业协同 打造AI智... 央广网北京5月18日消息(记者 郭彦伟)“这款熊猫医生AI机器人主要能帮助大家实现生命体征检测、AI...
实地调研深圳融资市场 细数贷款... 在当下经济发展节奏较快的深圳,各行各业的资金周转需求愈发普遍,从个体日常大额支出、家庭置业规划,到个...
上市公司交出近三年最好成绩单 ... 上市公司是经济高质量发展的重要微观基础,稳中向好的成绩单有力印证中国经济的强大韧性与活力。从上市公司...
接连吃罚单!这家券商债券业务“... 5月15日,国都证券及其债券从业人员收到了北京证监局发出的5份行政处罚。 罚单显示,因在公司债券承销...
原创 美... 特朗普本次的中国之行,其深远影响将直接牵动美国今年中期选举的最终走向,因此,他此番远渡重洋,无疑是怀...
AI高景气与盈利持续兑现 机构... 存储芯片指数日K线图   范雨露 制图 上周,全球主要股指普遍回调,A股市场同样冲高回落,创业板指创...
2026天津房交会暨“新房市集... 近日,2026天津房交会暨“新房市集”活动在津一·PARK正式启幕。此次房交会由天津市房地产市场服务...
原创 【... 各位朋友,最近是不是感觉金店门口的“今日金价”牌子,数字变得有点“刺眼”?没错,黄金它……真的跌了,...
原创 推... 俄罗斯财长安东·西卢安诺夫接受自家媒体采访,透露了两条重磅消息。 第一个:中俄双边贸易中,本币结算率...
兆易创新盘中涨停续创历史新高 ... 5月18日早盘,兆易创新盘中涨停,股价续创历史新高,报412.87元/股,成交金额超130亿元,A+...
原创 价... 过去三年价格战硝烟弥漫,汽车价格一降再降。 然而曾经杀得眼红的车企们,如今集体踩下刹车,汽车售价不降...
4月居民贷款大幅缩水近8000... 一边是楼市延续修复态势,“小阳春”行情持续演绎,重点城市二手房成交量大幅攀升;另一边是居民信贷数据的...
金价暴涨里的“套保”迷影,山东... 山东黄金冶炼业务。图源:企业官网 本报(chinatimes.net.cn)记者张蓓 黄指南 深圳报...
扬帆出海获佳绩!盐田区携手黄金... 2026年5月8日至10日 在马来西亚槟城举办的 “2026马来西亚黄金珠宝展销会”上 深圳市盐田区...
政策底与情绪顶:5月18日-2... 文/金透社 万捷 2026年5月第三周(5月11日-15日),A股市场走出了鲜明的分化格局。上证指数...
证监会重罚欺诈发行,广发证券被... 4.63亿元。 这是2026年5月,证监会对清越科技、元道通信两家公司欺诈发行、财务造假的罚款总额。...