傻瓜式跟着就能写Vue组件库的项目

379 阅读6分钟

实现

大概讲一下要实现的功能,就两点:

  1. 组件库能打包并发布npm(作者比较懒,只教打包,npm发布难不倒大家)
  2. 编写md文档,能在vue项目中显示出来

思路

  1. 打包vue组件用到rollup构建工具(是一款ESModule打包器,可以理解为简易版webpack,作者懒,可以自行百度了解)
  2. 想要解析md文档需要自己写loader实现,因为webpack提供的loader不合适做组件库,作者比较懒,不写,拷贝了element-ui的md-loader源码,有兴趣的可以自己手写

开始

搭建项目

搭建项目作者是直接用vue脚手架,vue-cli用的是vue2的版本,脚手架怎么安装自己百度,作者比较懒,这里不教,安装完脚手架vue-cli之后我们就开始创建项目,先执行下面的指令就会出现以下图片的界面

vue ui

image.png

点击左上角,选择项目管理器,来到创建项目这个页面

image.png 这里我没有选择ts,需要的可以自己选择,一顿操作下来,项目就创建好了,先跑起来看看

image.png 熟悉的界面,接下来开始将md文件解析并放在vue项目中显示出来

image.png

md-loader实现渲染出页面

首先在src同级目录下新建一个build文件夹(作者也不知道取什么名字),再新建一个md-loader文件夹

image.png 随便在index.js里面写点东西

image.png

  module.exports = function(source) {
    return `
      <template>
        <div>渲染的md文档</div>
      </template>
    `;
  };

然后来到vue.config.js里面添加一点代码,主要是匹配以.md结尾的文件通过刚刚写的loader处理

image.png

const { defineConfig } = require("@vue/cli-service");
const path = require("path");

module.exports = defineConfig({
  transpileDependencies: true,
  chainWebpack: (config) => {
    config.module
      .rule("md")
      .test(/\.md/)
      .use("vue-loader")
      .loader("vue-loader")
      .end()
      .use(path.resolve("./build/md-loader/index.js"))
      .loader(path.resolve("./build/md-loader/index.js"))
      .end()
  },
});

然后我们来到src这个目录,新建一个文件夹docs,里面存放的都是md文档,我们继续随便写点东西,

image.png

image.png 然后去app.vue引入md文件,注释或删除多余的,将它当做组件去使用

image.png 这个时候看看页面,你会发现他显示出来了md-loader返回的

image.png 接下来我们去复制element-ui/build文件下的md-loder源码里面的md-loader覆盖我们原来的(他的有代码演示和隐藏展开代码)

image.png 接下来我们去看看他们代码里面有没有用到其他包,我们需要安装使用,如下图是我看到的几个包

image.png

image.png

image.png 查看之后发现除了vue-template-compiler其他包都没有,执行以下指令安装

yarn add @vue/component-compiler-utils markdown-it-chain markdown-it-anchor transliteration markdown-it-container -D

安装完成之后重新跑一次项目,发现报错了,检查之后发现还有个包没装

image.png 再执行一次安装指令

yarn add markdown-it -D

查看以下项目执行效果,发现我们写在md文档上的文字都能显示出来了,左边的图标是怎么回事呢,看了一下element组件库的文档,发现是他也有的,那我们不管

image.png

image.png

我们去参考一下element是怎么写演示代码的,路径:examples\docs\zh-CN里面很多md文件,随便打开一个

image.png 我们只需要选框中的那一块,复制到我们的md文档,因为我们还没组件,所以改造一下

image.png

##  测试测试测试


:::demo 测试`demo````html
<template>
  <div>折叠的代码</div>
</template>
\```
:::

啊不会写,上面的代码被砍掉了,加了个\不然他结束
保存看一下效果,发现不是我们想要的效果,看一下控制台发现报错了,原来我们少了一个组件demo-block,这个组件我们去element源码里面复制,路径:\examples\components,把里面的所有文件都复制过来(比较懒,懒得挑,怕少了文件)

image.png 新建一个mdConfig文件夹,统一管理md文档的东西 image.png 去看一下demo-block文件看到里面还引入其他文件

image.png 直接去element拷贝过来 至于Element这个模块,我们要用到我们自己的组件库,在路径:src/components下新建一个Button的文件夹和一个index.js(统一管理我们的组件),Button里面新建一个index.vueindex.js 三个文件的代码分别是:

image.png

Button/index.vue

<template>
    <div class="btn">按钮</div>
  </template>
  
  <script>
  export default {
      name:"MyButton"
  }
  </script>
  
  <style>
  
  </style>

Button/index.js

import Button from "./index.vue"

Button.install = function(Vue) {
  Vue.component(Button.name, Button)
}

export default Button

index.js

import Button from "./components/Button/index";

const components = [Button];

const install = function (Vue) {
  components.forEach((component) => {
    Vue.component(component.name, component);
  });
};

if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}

export default { install, Button, version: "1.0.0" };

修改demo-block指向我们新建的组件库

image.png

 import Element from '../../components/index';

再到main.js里面全局注册我们的demo-block组件

image.png


import DemoBlock from './mdConfig/components/demo-block.vue'
...

Vue.component("demo-block", DemoBlock)

保存执行之后报错了,我们修改一下demo-block.vue组件里面的langConfig,将他里面的this.lang写死成zh-CN

image.png

langConfig() {
    return compoLang.filter(config => config.lang === "zh-CN")[0]['demo-block'];
  },

再去看一下效果发现代码正常能折叠了,还能在线运行,看控制台还有报错,原来是里面还用了element的组件,我这里不管他

image.png

rollup打包代码

在根目录新建一个rollup.config.js文件,并下载相关依赖

yarn add rollup rollup-plugin-vue rollup-plugin-terser glob@7.1.4

在package.json增加命令:

"scripts": {
    "serve": "vue-cli-service serve",
    "build:docs": "vue-cli-service build",
    "build": "rollup -c"
  },

rollup.config.js

const vue = require("rollup-plugin-vue");
const { terser } = require("rollup-plugin-terser");


module.exports = [
  {
    input: "./src/components/Button/index.js",
    output: [
      {
        file: "es/button.js",
        format: "es",
      },
    ],
    plugins: [
      vue({
        css: true,
        compileTemplate: true,
      }),
      terser(),
    ],
  },
];

先打包一下Button这个组件看一下效果,执行yarn build; 打包完成会生成一个es/button.js,我们去main.js打印看一下生成的组件是否可用

image.png

image.png

控制台打印看到我们的对象正常,证明打包成功,但只能打包一个组件,如果需要打包多个,就在数组里面添加多个 利用global可获取所有的组件,通过遍历去改造成数组,以下是改造后的js,想要将所有的组件打包到一个index下要单独打包index.js文件

const vue = require("rollup-plugin-vue");
const { terser } = require("rollup-plugin-terser");
const glob = require("glob");
const path = require("path");

const files = glob.sync(path.join(__dirname, "./src/components/**/*.js"));

const components = files.map((item) => {
  let nameArr = item.split("/");
  let name = nameArr[nameArr.length - 2];
  return {
    input: item,
    output: [
      {
        file: `es/${name.toLowerCase()}.js`,
        format: "es",
      },
    ],
    plugins: [
      vue({
        css: true,
        compileTemplate: true,
      }),
      terser(),
    ],
  }
});

module.exports = components

修改一下demo-block.vue下的goCodepen,让他的script指向打包后的文件index,

image.png

const resourcesTpl = '<scr' + 'ipt src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.js"></scr' + 'ipt>' +
        '\n<scr' + `ipt src="http://127.0.0.1:5500/es/index.js"></scr` + 'ipt>';

去mian.js将我们的组件安装上

import MyComponents from "../es/index"
Vue.use(MyComponents)

然后去md文档使用,发现成功了

image.png

结尾

后期的样式界面可以自己调整,作者比较懒,就这样吧,功能实现了
至于npm发布需要的话留言,或者百度了解

补充一下package.json代码,避免安装版本问题导致错误

{
  "name": "vue-components-demo2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build:docs": "vue-cli-service build",
    "build": "rollup -c"
  },
  "dependencies": {
    "core-js": "^3.8.3",
    "glob": "7.1.4",
    "rollup": "^3.2.3",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-vue": "5.1.9",
    "vue": "^2.6.14",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~5.0.0",
    "@vue/cli-plugin-router": "~5.0.0",
    "@vue/cli-plugin-vuex": "~5.0.0",
    "@vue/cli-service": "~5.0.0",
    "@vue/component-compiler-utils": "^3.3.0",
    "markdown-it": "^13.0.1",
    "markdown-it-anchor": "^8.6.5",
    "markdown-it-chain": "^1.3.0",
    "markdown-it-container": "^3.0.0",
    "sass": "^1.32.7",
    "sass-loader": "^12.0.0",
    "transliteration": "^2.3.5",
    "vue-template-compiler": "^2.6.14"
  }
}