当前工作中的组件库使用方法难用,还容易掉坑。以下先完成一个组件库,然后描述当前的组件库的问题,再尝试做优化。
Vue2组件库的正确姿势
问通义千问:我想做一个vue2组件库,使用webpack5构建,并有examples目录用于测试组件。请给出代码和思路。
给出的答案完成度挺高的,缺失babel.config.js配置,webpack配置需要微调,参考意义非常大。
- 组件库项目目录结构
jview/
├── src/
│ ├── components/ # 主要的组件源码
│ │ ├── button/ # 按钮组件目录
│ │ │ ├── button.vue # 按钮组件
│ │ │ └── index.js # 导出按钮组件
│ │ ├── input/
│ │ │ ├── input.vue
│ │ │ └── index.js
│ │ ├── message/ # 消息弹框
│ │ │ ├── index.js
│ │ └── ...
│ ├── index.js # 导出所有组件
├── examples/ # 示例项目,用来展示和测试组件
│ ├── App.vue # 主组件
│ ├── index.html # 页面模板
│ ├── main.js # 入口js
│ └── webpack.config.js # 示例项目的webpack配置
├── dist/ # 打包后的组件库文件夹
├── babel.config.js # Babel配置
├── package.json
├── README.md
└── webpack.config.js # 组件库的webpack配置
- 消息message对象
类似于element-ui的message, 使用方法
this.$message.success('搞错了,再来')
src/components/message/index.js 导出一个对象,包含success方法
export default {
name: 'Message',
success (content) {
alert(content)
}
}
- 按钮button组件
组件使用方法:
<j-button type="success" @click="handleClick">点我呀</j-button>
src/components/button/button.vue 内容稍复杂,基本上完成让通义千问完成的
<!-- MyButton.vue -->
<template>
<button :class="buttonClasses" @click="onClick">
<!-- 插槽用于显示按钮内容 -->
<slot></slot>
</button>
</template>
<script>
export default {
name: 'JButton',
props: {
// 定义 type 属性,限定可接受的值
type: {
type: String,
required: true,
validator(value) {
return ['success', 'primary'].includes(value);
}
}
},
methods: {
onClick(event) {
// 触发点击事件,可以在此处添加自定义逻辑
this.$emit('click', event);
}
},
computed: {
// 根据 type 属性计算按钮的类名
buttonClasses() {
return [
'my-button', // 基本样式类名
`my-button--${this.type}` // 动态添加样式类名
];
}
}
};
</script>
<style scoped>
/* 样式部分,这里只是示例,请根据实际需求定义样式 */
.my-button {
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.my-button--success {
background-color: #52c41a;
color: white;
}
.my-button--primary {
background-color: #1890ff;
color: white;
}
/* 添加鼠标悬停、点击等状态的样式 */
.my-button:hover {
opacity: 0.8;
}
</style>
src/components/button/index.js 导出button.vue
import Button from './button.vue'
export default Button
-
src/index.js导出所有组件
- 导出对象包括install方法
- install方法全局注册组件、message对象挂载到Vue原型上
import Button from './components/button'
import Input from './components/input'
import Message from './components/message'
// ... 更多组件的导入
// 假设有这样一个包含所有组件对象的集合
const components = [
Button,
Input,
// ... 其他组件
]
// 定义一个 Vue 插件
const install = function (Vue) {
// 遍历组件集合,将每个组件全局注册
components.forEach(component => {
Vue.component(component.name, component)
})
Vue.prototype.$message = Message
// 可能还有其他的全局注册操作,例如指令、过滤器等
}
// 导出插件
export default {
version: '1.0.0',
install
}
- 构建库
在package.json中添加命令
"build-lib": "webpack --config webpack.config.js",
webpack.config.js配置
- output.filename是导出js的文件名。这里会输入出"dist/jview.js"
- libraryTarget值为umd,导出的库支持 CommonJS、AMD 或全局变量的方式
- library: 'jview',也就是全局变量为window.jview
- externals.vue表示vue.js不会被打包进jview.js
// webpack.config.js
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
entry: './src/index.js',
mode: 'production',
output: {
filename: 'jview.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'umd',
libraryExport: 'default',
library: 'jview'
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
},
{
test: /.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
// 配置其他loader,如css-loader、file-loader等
]
},
plugins: [new VueLoaderPlugin()],
externals: {
vue: 'Vue'
}
}
- 使用组件
examples/main.js注册组件
Vue.use(jview),会调用jview.install,也就是第4步骤,全局注册组件、把$message挂载到Vue原型
// examples/main.js
import Vue from 'vue'
import App from './App.vue' // 你的示例App组件
import jview from 'jview' // 引入组件库入口文件
// 全局注册组件库中的所有组件
Vue.use(jview)
new Vue({
render: h => h(App)
}).$mount('#app')
examples/App.vue使用jbutton组件、调用$message方法
<template>
<div>
<h1>这里是示例</h1>
<j-button type="success" @click="handleClick">点我呀</j-button>
</div>
</template>
<script>
export default {
methods: {
handleClick () {
this.$message.success('搞错了,再来')
}
}
}
</script>
package.json添加examples本地服务命令
"start-example": "webpack serve --config examples/webpack.config.js"
example/webpack.congfig.js
- App.vue的
import jview from 'jview'中"jview"是别名,别名声明是resolve.alias, 路径为"dist/jview.js",
// examples/webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
function resolve (dir) {
return path.resolve(__dirname, '', dir)
}
module.exports = {
mode: 'development',
entry: resolve('main.js'),
output: {
filename: 'bundle.js',
path: resolve('dist')
},
resolve: {
alias: {
'jview': path.resolve(__dirname, '../dist/jview.js'),
}
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
}
// 配置其他loader,如babel-loader等
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: resolve('index.html')
})
],
devServer: {
port: 8080
}
}
-
运行
- 执行命令
npm run build-lib,生成dist/jview.js文件。检查源码不包括vue.js - 执行命令
npm run start-example点击按钮,弹框显示正常。
- 执行命令
总结
-
为什么需要button/index.js? 因为组件复杂时,可能有组件内的样式, vue文件等,统一在index.js导出,更易维护。
- .vue文件经过vue-loader解析,它实际上相当于以下代码中的"MyComponentOptions"对象,需要注册为组件才能在template中使用。
// 假设这是你的组件选项对象 var MyComponentOptions = { template: `<div>这是一个自定义组件!{{ message }}</div>`, props: { // 定义接收父组件传递的属性 message: String }, data() { return { localMessage: '这是组件内部的数据' } }, methods: { greet() { console.log('你好,我是 MyComponent!'); } }, mounted() { this.greet(); // 组件挂载时调用此方法 } }; // 使用 Vue.component 全局注册组件 Vue.component('my-component', MyComponentOptions); // 现在这个组件可以在任何 Vue 实例的模板中使用 new Vue({ el: '#app', data: { parentMessage: '从父组件传递的消息' } }); // 在 HTML 模板中使用该组件 <!-- 父组件模板 --> <div id="app"> <my-component :message="parentMessage"></my-component> </div> ``` 1. 实际上button.vue也可以直接引入的,message.js引入。 1. ``` import Button from './components/button.vue' import Message from './components/message.js' ``` -
'jview': path.resolve(__dirname, '../dist/jview.js')配置了jview组件库的别名,实际开发中会发布到jview.js到公司内部的npm仓库,其他项目npm install使用。
为什么用通义千问?国内的GPT们,轻度对比了一下,通义千问给出技术方案还是稍强点,很难理解kimi.ai是怎么火的。大家有更好的建议,欢迎私聊