我是如何用GPT优化vue组件库的(上)

160 阅读3分钟

当前工作中的组件库使用方法难用,还容易掉坑。以下先完成一个组件库,然后描述当前的组件库的问题,再尝试做优化。

Vue2组件库的正确姿势

问通义千问:我想做一个vue2组件库,使用webpack5构建,并有examples目录用于测试组件。请给出代码和思路。

给出的答案完成度挺高的,缺失babel.config.js配置,webpack配置需要微调,参考意义非常大。

  1. 组件库项目目录结构
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配置
  1. 消息message对象

类似于element-ui的message, 使用方法

this.$message.success('搞错了,再来')

src/components/message/index.js 导出一个对象,包含success方法

export default {
  name: 'Message',
  success (content) {
    alert(content)
  }
}
  1. 按钮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
  1. src/index.js导出所有组件

    1. 导出对象包括install方法
    2. 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
}
  1. 构建库

在package.json中添加命令

"build-lib": "webpack --config webpack.config.js",

webpack.config.js配置

  1. output.filename是导出js的文件名。这里会输入出"dist/jview.js"
  2. libraryTarget值为umd,导出的库支持 CommonJS、AMD 或全局变量的方式
  3. library: 'jview',也就是全局变量为window.jview
  4. 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'
  }
}
  1. 使用组件

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

  1. 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
  }
}
  1. 运行

    1. 执行命令npm run build-lib,生成dist/jview.js文件。检查源码不包括vue.js
    2. 执行命令npm run start-example 点击按钮,弹框显示正常。

总结

  1. 为什么需要button/index.js? 因为组件复杂时,可能有组件内的样式, vue文件等,统一在index.js导出,更易维护。

    1. .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'
        ```
    
    
  2. 'jview': path.resolve(__dirname, '../dist/jview.js') 配置了jview组件库的别名,实际开发中会发布到jview.js到公司内部的npm仓库,其他项目npm install使用。

为什么用通义千问?国内的GPT们,轻度对比了一下,通义千问给出技术方案还是稍强点,很难理解kimi.ai是怎么火的。大家有更好的建议,欢迎私聊

项目地址:github.com/jovenwang12…