✍🏼作者:周棋洛,计算机学生
♉星座:金牛座
🏠主页:点击查看更多
🌐关键:vue2
组件封装
npm发包
1. 前言 🍃
你好!我是王子周棋洛,好久没写文了。
今天带大家 封装
一个类似 element-ui
的全局通知组件,并发布到 npm
后安装使用自己封装的组件。go,go,go🎉
2. 我为什么要封装通知插件 ❓
原因1:现成UI组件不适合我。
我之前使用 element-ui 组件库时,它的通知组件调用方式是传入配置对象
。代码如下:
this.$notify({
title: '警告',
message: '这是一条警告的提示消息',
type: 'warning'
});
可能官方考虑到配置项很多,就采用了配置对象的形式,但是我写一些小型网站时,这样写显得非常麻烦
。
首先呢,如果写一行,代码可读性
很差。如果写多行,显得很冗余
。
其次,配置对象,每次都要写重复的对象名
,比如:title,message,type,这对于简单的通知来说显得很不友好
!当然更加复杂的通知,采用配置对象的形式是没问题的。
因为对我来说通知是一个频繁使用的组件
。于是我改用了函数式调用
,通过函数声明通知类型,参数限制至多三个,不然函数式就意义不大了。下面是本组件最终的调用代码:
this.$notify.success("成功消息");
我想说对于要求不多的通知组件,这样的api,一行就是一个消息,才是我想要的,哈哈。
其次还有个原因,我在写网站时,或者软件都会用到通知组件,我每次都是搬代码,太不优雅了,于是,我就去了解了一下 npm发包
我已经发布到了npm。
学完本篇文章,你将学会如何封装一个全局消息组件,并学会发布你编写的组件到互联网,这是很有成就感
的事情哦,快学起来吧!
npm地址:https://www.npmjs.com/package/zhouql-notify
3. 初始化vue空项目 💻
使用vue-cli
初始化一个vue空项目。
vue create app
这里只使用了一个css框架 less。
4. 通知组件封装 ⏰
以下是项目初始化后 package.json
文件的信息。
4.1 整理项目结构 📖
首先呢,我们需要整理一下项目结构,以及修改一下基础配置。
打开 App.vue
文件,把不需要的内容删除。
删除前:
删除后:
删除默认组件 HelloWorld.vue
打开 vue.config.js
,添加如下配置。
lintOnSave: false, // 关闭eslint检查
productionSourceMap: false, // 生产环境是否要生成 sourceMap
publicPath: './', // 部署应用包时的基本 URL
assetsDir: 'assets', // 放置静态文件夹目录
好了,整理工作完成,下面着手封装组件。
4.2 基础结构搭建 🌳
在 components
文件夹下新建一个 notify
文件夹作为组件的文件夹,然后在 notify文件夹
下新建一个 Notify.vue
单文件组件和一个入口 index.js
文件,如下:
Notify.vue
写一下基础结构,一个div盒子,里面有一个字体图标用来标记消息类型和一个通知内容。
<template>
<div class="z-notify">
<!-- icon字体图标,消息类型 -->
<i></i>
<!-- 消息内容 -->
<span></span>
</div>
</template>
<script>
export default {
name: "notify"
}
</script>
<style lang="less">
</style>
4.3 字体图标引入 🎄
我是在 iconfont.cn
网站找的图标,大家可以根据需要自行选择。
因为有彩色图标,可以修改项目设置,下图是我的配置供参考。
选完图标之后,下载到本地即可,然后解压后打开,只需要我下图框选的几个文件就可以了。
在 components
文件夹下新建一个 fonts
文件夹用来存放字体图标,然后将上面框选的文件复制粘贴到文件夹下。如下:
Notify.vue
引入字体图标:
<style lang="less">
// 引入字体图标
@import url("../fonts/iconfont.css");
</style>
好了,至此字体图标已经完成了。
4.4 修改组件结构 🍕
下面我们简单修改一下 Notify.vue
,因为我之前字体图标修改了配置,就是这两个:
所以我在使用是类名是 zfont z-chenggong
,这里需要注意一下。
<template>
<div class="z-notify">
<!-- icon字体图标,消息类型 -->
<i class="zfont z-chenggong"></i>
<!-- 消息内容 -->
<span>你好,通知组件</span>
</div>
</template>
<script>
export default {
name: "notify"
}
</script>
<style lang="less">
// 引入字体图标
@import url("../fonts/iconfont.css");
</style>
4.5 插件核心逻辑 🍉
编写 notify
文件夹下的 index.js
入口文件,首先定义一个全局变量避免组件多次创建,然后实现 install
方法,因为 Vue.use()
会调用 install
,那自然我们就可以在 intsall
方法参数里获取到 Vue实例了。
我们通过 Vue.extend
创建一个组件构造器,传入之前编写好的组件 Notify.vue
,这样组件构造器就定义好了,使用 new
关键字对组件进行实例化得到一个组件实例 notifyInstanse
。
然后将组件实例挂载到DOM,至此插件的核心逻辑就完成了。
import Notify from './Notify.vue'
// 单实例
let notifyInstanse = null;
const NotifyPlugin = {
install(Vue) {
if (!notifyInstanse) {
// Vue.extend() 方法创建了一个名为 NotifyComponent 的组件构造器。
// 传入的参数是一个名为 Notify 的 Vue 组件。这个组件定义了通知组件的模板、样式和行为。
// 只不过是组合了一下组件创建。
const NotifyComponent = Vue.extend(Notify);
// new NotifyComponent() 创建一个 notifyInstanse 对象,表示一个通知组件实例。
notifyInstanse = new NotifyComponent();
// 将通知组件挂载到DOM中
const container = document.createElement("div");
document.body.appendChild(container);
notifyInstanse.$mount(container);
}
}
}
export default NotifyPlugin
下面我们在 main.js
中引入插件并使用。
import Vue from 'vue'
import App from './App.vue'
import NotifyPlugin from './components/notify'
Vue.use(NotifyPlugin)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
启动项目并在浏览器打开,可以看到组件成功挂在到DOM。
4.6 美化通知样式 🌂
好嘞,下面呢,就是编写一下组件的样式和动画,基础的css知识,下面是完整的css代码,使用了固定定位并做了居中处理,top:-32px;用于初始化时隐藏通知,因为使用者传入的通知可能很长,这时候需要处理,我这里限制了通知最宽为70%,如果文字超了则隐藏并显示三个小点,具体代码是这样:
// 超出隐藏
overflow: hidden;
// 文字超出显示省略号
text-overflow: ellipsis;
// 不换行
white-space: nowrap;
因为字体图标大小表现不一,所以我对字体图标做了单独的大小调整,使得整体协调,最后定义了加载字体图标的旋转动画,为了方便使用动画,我定义了一个类名 rotate-icon
<style lang="less">
// 引入字体图标
@import url("../fonts/iconfont.css");
.z-notify {
// 固定定位并居中
position: fixed;
left: 50%;
transform: translateX(-50%);
// 默认隐藏
top: -32px;
height: 30px;
max-width: 70%;
line-height: 30px;
text-align: center;
background: #e8e8e8;
padding: 0px 16px;
border-radius: 30px;
transition: transform 0.55s;
display: flex;
align-items: center;
i {
display: block;
display: flex;
align-items: center;
justify-content: center;
width: 15px;
height: 15px;
margin-left: -5px;
&::before {
display: flex;
align-items: center;
justify-content: center;
width: 15px;
height: 15px;
}
}
span {
color: #213547;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
user-select: none;
padding-left: 4px;
font-size: 13px;
vertical-align: middle;
}
// 字体图标大小表现不一,微调
.z-jiazai_shuang {
font-size: 17px;
}
.z-chenggong {
font-size: 13px;
}
.z-icon-test1 {
font-size: 15px;
}
.z-jinggao {
font-size: 15px;
}
.z-tongzhi {
font-size: 13px;
}
.z-bug {
font-size: 14px;
}
.z-kaixin {
font-size: 14px;
}
.z-yinliao2 {
font-size: 15.5px;
}
.z-jiazai1 {
font-size: 20px;
}
.z-jiazai3 {
font-size: 15px;
}
.z-jiazai2 {
font-size: 13px;
}
}
// 加载时的字体图标动画
@keyframes rotate-icon {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(1turn);
}
}
// 加载时的字体图标类名
.rotate-icon {
animation: rotate-icon 0.85s linear infinite;
}
</style>
最终效果是这样的:
4.7 实现通知方法 🚥
还记得在实现插件核心逻辑时的 intsall
方法吗?
install(Vue) {
}
我们想通过 this.$notify.success("成功消息")
这样的方式调用,上面可以拿到Vue实例了,是不是给实例原型添加属性就可以了。
// 添加全局方法
Vue.prototype.$notify = {
success(message, duration) {
notifyInstanse.showNotify(message, 'success', duration);
},
warning(message, duration) {
notifyInstanse.showNotify(message, 'warning', duration);
},
info(message, duration) {
notifyInstanse.showNotify(message, 'info', duration);
},
error(message, duration) {
notifyInstanse.showNotify(message, 'error', duration);
},
loading(message, duration = 1000 * 60 * 60 * 24) {
if (arguments.length == 1) {
notifyInstanse.showNotify(message, 'loading', 1000 * 60 * 60 * 24);
return
}
if (typeof arguments[1] == 'string') {
notifyInstanse.showNotify(message, 'loading', 1000 * 60 * 60 * 24, arguments[1]);
return
}
if (typeof arguments[1] == 'number') {
if (typeof arguments[2] == 'string') {
notifyInstanse.showNotify(message, 'loading', duration, arguments[2]);
} else {
notifyInstanse.showNotify(message, 'loading', duration);
}
}
},
bug(message, duration) {
notifyInstanse.showNotify(message, 'bug', duration);
},
happy(message, duration) {
notifyInstanse.showNotify(message, 'happy', duration);
},
free(message, duration) {
notifyInstanse.showNotify(message, 'free', duration);
},
close() {
notifyInstanse.closeNotify();
}
}
上面代码呢,我们给Vue原型添加了$notify对象,对象中编写了我们需要的通知方法,然后呢我们通过通知组件实例 notifyInstanse
去调用他的 showNotify
方法去打开通知,closeNotify
方法关闭通知。
打开通知时,第一个参数是通知内容,第二个就是消息类类型,这里就直接通过函数名 包装
了一层,还有延时参数,以及对loading的特殊处理,因为 javascript
没有函数重载的语法,不过我们可以通过判断参数类型分别进行处理,以 模拟函数重载
的场景。
下一步呢,我们只需在组件中实现 showNotify
和 closeNotify
就完事了。
index.js
的全部代码如下:
import Notify from './Notify.vue'
// 单实例
let notifyInstanse = null;
const NotifyPlugin = {
install(Vue) {
if (!notifyInstanse) {
// Vue.extend() 方法创建了一个名为 NotifyComponent 的组件构造器。
// 传入的参数是一个名为 Notify 的 Vue 组件。这个组件定义了通知组件的模板、样式和行为。
// 只不过是组合了一下组件创建。
const NotifyComponent = Vue.extend(Notify);
// new NotifyComponent() 创建一个 notifyInstanse 对象,表示一个通知组件实例。
notifyInstanse = new NotifyComponent();
// 将通知组件挂载到DOM中
const container = document.createElement("div");
document.body.appendChild(container);
notifyInstanse.$mount(container);
}
// 添加全局方法
Vue.prototype.$notify = {
success(message, duration) {
notifyInstanse.showNotify(message, 'success', duration);
},
warning(message, duration) {
notifyInstanse.showNotify(message, 'warning', duration);
},
info(message, duration) {
notifyInstanse.showNotify(message, 'info', duration);
},
error(message, duration) {
notifyInstanse.showNotify(message, 'error', duration);
},
loading(message, duration = 1000 * 60 * 60 * 24) {
if (arguments.length == 1) {
notifyInstanse.showNotify(message, 'loading', 1000 * 60 * 60 * 24);
return
}
if (typeof arguments[1] == 'string') {
notifyInstanse.showNotify(message, 'loading', 1000 * 60 * 60 * 24, arguments[1]);
return
}
if (typeof arguments[1] == 'number') {
if (typeof arguments[2] == 'string') {
notifyInstanse.showNotify(message, 'loading', duration, arguments[2]);
} else {
notifyInstanse.showNotify(message, 'loading', duration);
}
}
},
bug(message, duration) {
notifyInstanse.showNotify(message, 'bug', duration);
},
happy(message, duration) {
notifyInstanse.showNotify(message, 'happy', duration);
},
free(message, duration) {
notifyInstanse.showNotify(message, 'free', duration);
},
close() {
notifyInstanse.closeNotify();
}
}
}
}
export default NotifyPlugin
4.8 实现两个方法 🥛
下面就是在 Notify.vue
组件实现打开和关闭通知的逻辑,使用 setTimeout
定时器,需要注意几个细节:
1.在使用时,如果多次点击是会重置延时的,所以在代码中,我们应该首先判断 timer
变量是不是有定时器,如果有先把原来的定时器清除了,然后再根据具体的延时,重新设置定时器。
2.通知显示与隐藏,借助了translate和transition过度动画,性能更好,也很简单。
<script>
export default {
name: "notify",
data() {
return {
// 通知内容
message: '',
// 通知类型
type: '',
// 延时
duration: 3000,
// 计时器
timer: null,
// 图标映射
iconObj: {
"success": "z-chenggong",
"warning": "z-jinggao",
"info": "z-tongzhi",
"error": "z-icon-test1",
"bug": "z-bug",
"loading": "z-jiazai3",
"happy": "z-kaixin",
"free": "z-yinliao2"
},
iconStyle: '',
}
},
methods: {
showNotify(message, type, duration) {
this.type = type;
this.message = message;
this.duration = duration || 3000;
if (arguments[3]) {
this.iconStyle = arguments[3];
}
// 通过translate上下移动,结合transition过度动画
this.$refs.znotify.style.transform = "translate(-50%,55px)";
if (this.timer) {
this.timer = clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
this.closeNotify();
}, this.duration);
},
closeNotify() {
if (this.timer) {
this.timer = clearTimeout(this.timer);
}
this.$refs.znotify.style.transform = "translate(-50%,0)";
}
},
computed: {
iconClass() {
if (this.type == 'loading') {
if (this.iconStyle == 'icon1') {
return `z-jiazai_shuang rotate-icon`;
} else if (this.iconStyle == 'icon2') {
return `z-jiazai2 rotate-icon`;
} else if (this.iconStyle == 'icon3') {
return `z-jiazai1 rotate-icon`;
}
return `${this.iconObj[this.type]} rotate-icon`;
}
return this.iconObj[this.type];
}
},
}
</script>
4.9 修改固定模板 🏜️
我们在js代码中,已经将消息和图标类名抽取出来了,现在需要把html替换为动态变量,代码如下:
<template>
<div class="z-notify" ref="znotify">
<i class="zfont" :class="iconClass"></i>
<span>{{message}}</span>
</div>
</template>
组件 Notify.vue
全部代码如下:
<template>
<div class="z-notify" ref="znotify">
<i class="zfont" :class="iconClass"></i>
<span>{{message}}</span>
</div>
</template>
<script>
export default {
name: "notify",
data() {
return {
// 通知内容
message: '',
// 通知类型
type: '',
// 延时
duration: 3000,
// 计时器
timer: null,
// 图标映射
iconObj: {
"success": "z-chenggong",
"warning": "z-jinggao",
"info": "z-tongzhi",
"error": "z-icon-test1",
"bug": "z-bug",
"loading": "z-jiazai3",
"happy": "z-kaixin",
"free": "z-yinliao2"
},
iconStyle: '',
}
},
methods: {
showNotify(message, type, duration) {
this.type = type;
this.message = message;
this.duration = duration || 3000;
if (arguments[3]) {
this.iconStyle = arguments[3];
}
// 通过translate上下移动,结合transition过度动画
this.$refs.znotify.style.transform = "translate(-50%,55px)";
if (this.timer) {
this.timer = clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
this.closeNotify();
}, this.duration);
},
closeNotify() {
if (this.timer) {
this.timer = clearTimeout(this.timer);
}
this.$refs.znotify.style.transform = "translate(-50%,0)";
}
},
computed: {
iconClass() {
if (this.type == 'loading') {
if (this.iconStyle == 'icon1') {
return `z-jiazai_shuang rotate-icon`;
} else if (this.iconStyle == 'icon2') {
return `z-jiazai2 rotate-icon`;
} else if (this.iconStyle == 'icon3') {
return `z-jiazai1 rotate-icon`;
}
return `${this.iconObj[this.type]} rotate-icon`;
}
return this.iconObj[this.type];
}
},
}
</script>
<style lang="less">
// 引入字体图标
@import url("../fonts/iconfont.css");
.z-notify {
// 固定定位并居中
position: fixed;
left: 50%;
transform: translateX(-50%);
// 默认隐藏
top: -32px;
height: 30px;
max-width: 70%;
line-height: 30px;
text-align: center;
background: #e8e8e8;
padding: 0px 16px;
border-radius: 30px;
transition: transform 0.55s;
display: flex;
align-items: center;
i {
display: block;
display: flex;
align-items: center;
justify-content: center;
width: 15px;
height: 15px;
margin-left: -5px;
&::before {
display: flex;
align-items: center;
justify-content: center;
width: 15px;
height: 15px;
}
}
span {
color: #213547;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
user-select: none;
padding-left: 4px;
font-size: 13px;
vertical-align: middle;
}
// 字体图标大小表现不一,微调
.z-jiazai_shuang {
font-size: 17px;
}
.z-chenggong {
font-size: 13px;
}
.z-icon-test1 {
font-size: 15px;
}
.z-jinggao {
font-size: 15px;
}
.z-tongzhi {
font-size: 13px;
}
.z-bug {
font-size: 14px;
}
.z-kaixin {
font-size: 14px;
}
.z-yinliao2 {
font-size: 15.5px;
}
.z-jiazai1 {
font-size: 20px;
}
.z-jiazai3 {
font-size: 15px;
}
.z-jiazai2 {
font-size: 13px;
}
}
// 加载时的字体图标动画
@keyframes rotate-icon {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(1turn);
}
}
// 加载时的字体图标类名
.rotate-icon {
animation: rotate-icon 0.85s linear infinite;
}
</style>
至此呢,通知组件就封装完成啦,是不是很有意思呢?
5. 测试组件 🛠️
组件封装完成了,现在来测试一下吧!在 App.vue
中写两个按钮,一个打开,一个取消,然后调用封装的方法,如下:
<template>
<div id="app">
<button @click="$notify.loading('长时间加载')">打开</button>
<button @click="$notify.close()">关闭</button>
</div>
</template>
效果如下:
6. 打包组件 📦
好嘞,下面呢,来说说怎么打包我们的组件。
6.1 编写打包命令 ⌨️
来到 package.json
文件,添加一个脚本。
"package": "vue-cli-service build --target lib ./src/components/notify/index.js --name zhouql-notify --dest zhouql-notify",
- ./src/components/notify/index.js 指的是打包的入口文件,这里就是通知组件的入口文件。
- –name 参数配置打包后的文件名。
- –dest 参数配置打包完成后生成的文件夹名。
理解了上面的几个参数之后呢,就可以编写自己的打包命令,然后运行就可以了,很快就打包好了。
6.2 整理打包文件 📄
这里看一下我打包后的目录结构:
其实我们只需要一个css和一个js文件就可以了,可以看到有3个js文件,其中 zhouql-notify.common.js
只能用 commonjs
的语法,zhouql-notify.umd.js
兼容AMD和commonJS规范的同时,还兼容全局引用的方式,所以我们用 zhouql-notify.umd.js
就可以了,还有一个js文件 zhouql-notify.umd.min.js
,这其实就是压缩版本的,看自己需要,可以看到压缩版的比没压缩的大小小了一大半,我选择压缩版的,哈哈。
所以这里的文件只保留 zhouql-notify.umd.min.js
和 zhouql-notify.css
即可,其他的我就先删除了。
6.3 生成 package.json 📁
为了发包,我们需要一个package.json记录包信息。
在当前目录进入命令行,执行命令
npm init -y
即可生成默认的 package.json 文件。
6.4 修改 package.json 📁
main:这是主入口,也就是我们刚刚保留的 zhouql-notify.umd.min.js
,当然也可以修改文件名为 index.js
,然后配置为 index.js
这都没有要求的,看自己需要,随便造。
name:这就是使用命令安装时的包名,不允许重复。
viersion: 版本
description:描述
scripts:脚本,咱这个不需要,删除即可
keywords:关键字,有利于别人发现你的包
author:作者
license:开源协议
homepage: 主页面,就是自定义的一个介绍页面,非必要
下面是我已经发布的包的配置,大家可以参考一下:
{
"name": "zhouql-notify",
"version": "1.0.6",
"description": "vue2全局函数式提示组件,一行代码一条通知",
"main": "index.js",
"homepage": "http://zhouql.vip/notify/",
"keywords": [
"vue",
"vue2",
"js",
"html",
"css",
"全局",
"提示",
"消息",
"函数式组件"
],
"author": "prince-zhouql",
"license": "ISC"
}
6.5 README.md文件 📃
添加 README.md
文件,注意必须是大小,之前用小写没有起作用。
7. 发布组件 🎉
7.1 账号注册 📱
发布前,首先要在 npm 注册一个账号
打开 www.npmjs.com
, 选择 sign up
进入账户注册页面
根据提示填写注册信息,点击注册后,填写的邮箱会收到一个验证码,输入验证码后即可完成注册。
完成注册后就可以看到 npm 个人主页了,到这里注册账户已经完成了。
7.2 检查源 ⛏️
使用命令检查npm下载源是不是官方的,有小伙伴可能用的淘宝镜像或者其他源,发包前需要切换到官方源,官方源是 https://registry.npmjs.org
npm config get registry
如果不是,使用下面命令切换到官方源
npm config set registry https://registry.npmjs.org
7.3 添加用户 🧑🎄
第一次发布用以下 (填写你注册的npm账号的username,password,email) 。
否则用 npm login
即可。
npm adduser
使用以下命令查看当前登录的npm用户。
npm whoami
7.4 推送 ⏏️
一定要把邮箱给认证了,否则publish会报错,切换到要发布包的入口,然后执行命令:
本文就是外层文件夹下执行。
npm publish
稍等片刻就可在 package里面看到自己的的npm包了。
7.5 更新版本 ✅
如果代码有更新,publish的时候需要更改版本号的,否则也提交不上。
方式两种如下:
-
直接改package.json
-
npm指令操作package.json
npm version patch //补丁版本,最后一位数加1
npm version minor //增加了新功能 中间的数字加1
npm version major //大改动,不向下兼容 第一位数字加1
7.6 删除包 ❗
执行下面命令删除自己发布的包,也可以在npm官网页面删除。
npm unpublish [packagename]--force
8. 安装自己编写的组件 😀
好了,到这里了,假设你已经上传了自己的包到npm,打开网站开到包的主页,这里以我的为例子。
可以看到右侧的安装命令,以及readme使用说明。
然后我们可以新建一个空的vue项目,然后使用以下指令安装库。
npm i zhouql-notify
安装成功后,打开 package.json
可以看到已经安装成功了。
然后根据 readme文件,引入组件,css文件,使用Vue.use()注册即可。
import Vue from 'vue'
import App from './App.vue'
// 引入组件
import Notify from "zhouql-notify"
// 引入样式文件
import "zhouql-notify/notify.css"
// 使用组件
Vue.use(Notify)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
然后在App.vue组件的 mounted钩子函数中调用测试如下:
<template>
<div id="app">
</div>
</template>
<script>
export default {
name: 'App',
mounted() {
// 测试
this.$notify.loading("加载中,成功了!", "icon3");
}
}
</script>
启动项目并在浏览器打开,可以发现成功啦!是不是很开心呢。
9. 总结 📄
好玩,哈哈,对于像通知这样常用的组件,如果没有合适的,自己封装发布才是正确的打开方式。
文章来源:https://www.toymoban.com/news/detail-493293.html
恭喜你,读完了本篇文章,能力 + 100 ,颜值 + 10,欢迎下次再来。👋 先别走,如果觉得文章写的不错,还请点赞,收藏,关注帅气的博主啊,手动狗头,我会继续努力,原创不易,你的支持就是小周的动力 ☃️文章来源地址https://www.toymoban.com/news/detail-493293.html
到了这里,关于Vue2封装一个全局通知组件并发布到NPM的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!