vue组件库合并样式打包成less文件

前言

基于公司需求,会搭建一个常规的组件 ui 库,简单的组件库还是非常好搭建的,我也搭建了很多次了,美滋滋的去搭建了,局部组件、公共组件,巴拉巴拉一顿输出。

在于 antdesign 经常会自己设置主题,我们的组件库需要支持 antdesign 中的变量继承,看到 antdesignmain.js 中可以 直接 import '*****.less', 我是比较好奇的,但是还是真没在组件库做过。开始想到的是用 webpack 或者 gulp 进行 打包成 less ,在于本人技术实在尴尬,网上也搜不到对应的东西。 后面想想,干脆自己用 fs 的方式进行操作文件抽离出来,时间又紧迫,只好想了后就开始行动,经过几个小时的奋战,把文件的样式 class 都设置成了比较语义化,以及成功在其他项目引用修改变量能顺利修改。

开始

vue-template-compiler

我们需要分离 vue 文件中的 style 中的样式,那么vue-template-compiler必不可少!

vue-template-compiler编译 vue 模板的包,传入模板返回AST 抽象语法树

例子

下面是一个简单的 tooltip 小组件。

<template>
  <tooltip placement="top">
    <template slot="title" v-if="isAllShow || content.length > len">
      {{ tipContent || content }}
    </template>
    {{ content.length > len ? content.slice(0, len) + "..." : content }}
  </tooltip>
</template>
<script>
  import { Tooltip } from "ant-design-vue";
  export default {
    name: "CTooltip",
    components: { Tooltip },
    props: {
      tipContent: {
        type: String,
        default: "",
      },
      content: {
        type: String,
        default: "",
      },
      len: {
        type: Number,
        default: 12,
      },
      isAllShow: {
        type: Boolean,
        default: false,
      },
    },
  };
</script>

我们现在来获取它的结构

// 引入 vue-template-compiler
const compiler = require("vue-template-compiler");
// 处理文件与目录路径
const path = require("path");
// 处理文件的读写、复制、s删除、重命名等操作
const fs = require("fs");
// 拼接路径
const resolve = (dir) => path.join(__dirname, dir);
// 读取文件
const content = fs.readFileSync(
  resolve("./packages/components/CTooltip/index.vue"),
  "utf-8"
);
// 编译模板字符串并返回已编译的JavaScript代码及其AST树。
const ret = compiler.parseComponent(content);
console.log(ret);

运行

node demo.js

可以看到常规分为了 3 部分, templatescriptstyles

我们只需要 styles 数组的东西。

遍历文件夹

合并成 less 文件,需要遍历 packages 文件夹中所有的文件,抽取出 vue 文件、less 文件 、 css 文件。

function ergodicDirectory(dirPath) {
  // 读取文件
  const files = fs.readdirSync(dirPath);
  files.forEach((file) => {
    // 当前遍历的 文件
    const filePath = path.resolve(dirPath, file);
    if (isVue(file)) {
      // 是vue文件的处理方式
      copyVue && copyVue(filePath);
    } else if (isLessOrCss(file)) {
      // 是否是 less 或 css 文件
      copyFile && copyFile(filePath);
    } else if (isDirectory(filePath)) {
      // 是否是 目录 文件夹
      ergodicDirectory(filePath);
    }
  });
}

是否是 VUE 文件

function isVue(filePath) {
  return filePath && filePath.toLowerCase().endsWith(".vue");
}

是否是 Less 或 Css 文件

function isLessOrCss(filePath) {
  return (
    filePath &&
    (filePath.toLowerCase().endsWith(".less") ||
      filePath.toLowerCase().endsWith(".css"))
  );
}

是否是文件夹

function isDirectory(filePath) {
  const stat = fs.statSync(filePath);
  return stat.isDirectory();
}

抽离 vue 中 style

function copyVue(filePath) {
  const content = fs.readFileSync(filePath, "utf-8");
  const ret = compiler.parseComponent(content);
  // 循环styles
  ret.styles &&
    ret.styles.forEach((style) => {
      // 追加内容到 规定文本
      pushStyle(style.content);
    });
}

Less、Css 文件复制

function copyFile(filePath) {
  const content = fs.readFileSync(filePath, "utf-8");
  const ret = compiler.parseComponent(content);
  pushStyle(ret.source);
}

追加到文本

arr 是存文本的全局变量

function pushStyle(data) {
  // 去掉 deep
  data = data.replace("/deep/", "");
  // 去掉 @import 引入的 文本
  const last = data.lastIndexOf('";');
  const last2 = data.lastIndexOf('");');
  if (last !== -1) arr += data.substring(last + 2);
  else if (last2 !== -1) arr += data.substring(last2 + 3);
  else arr += data;
}

文本写入

function save () {
  // 写入 的 路径地方以及名称
  fs.writeFileSync(resolve('./lib/demo.less'), arr)
}

运行的代码

ergodicDirectory(resolve("./packages"));
save();

package.json

"build": "vue-cli-service build --target lib --name custom-ant-base-ui --dest lib packages/index.js && node demo.js"

运行

npm run build

完整代码

// 引入 vue-template-compiler
const compiler = require("vue-template-compiler");
// 处理文件与目录路径
const path = require("path");
// 处理文件的读写、复制、s删除、重命名等操作
const fs = require("fs");
// 拼接路径
const resolve = (dir) => path.join(__dirname, dir);
let arr = "";

//  遍历目录
function ergodicDirectory(dirPath) {
  // 读取文件
  const files = fs.readdirSync(dirPath);
  files.forEach((file) => {
    // 当前遍历的 文件
    const filePath = path.resolve(dirPath, file);
    if (isVue(file)) {
      // 是vue文件的处理方式
      copyVue && copyVue(filePath);
    } else if (isLessOrCss(file)) {
      // 是否是 less 或 css 文件
      copyFile && copyFile(filePath);
    } else if (isDirectory(filePath)) {
      // 是否是 目录 文件夹
      ergodicDirectory(filePath);
    }
  });
}

// 是否是VUE文件
function isVue(filePath) {
  return filePath && filePath.toLowerCase().endsWith(".vue");
}

// 是否是Less或者Css文件
function isLessOrCss(filePath) {
  return (
    filePath &&
    (filePath.toLowerCase().endsWith(".less") ||
      filePath.toLowerCase().endsWith(".css"))
  );
}

// 是否是目录
function isDirectory(filePath) {
  const stat = fs.statSync(filePath);
  return stat.isDirectory();
}

function copyVue(filePath) {
  const content = fs.readFileSync(filePath, "utf-8");
  const ret = compiler.parseComponent(content);
  // 循环styles
  ret.styles &&
    ret.styles.forEach((style) => {
      // 追加内容到 规定文本
      pushStyle(style.content);
    });
}

function copyFile(filePath) {
  const content = fs.readFileSync(filePath, "utf-8");
  const ret = compiler.parseComponent(content);
  pushStyle(ret.source);
}

function pushStyle(data) {
  // 去掉 deep
  data = data.replace("/deep/", "");
  // 去掉 @import 引入的 文本
  const last = data.lastIndexOf('";');
  const last2 = data.lastIndexOf('");');
  if (last !== -1) arr += data.substring(last + 2);
  else if (last2 !== -1) arr += data.substring(last2 + 3);
  else arr += data;
}

function save() {
  // 写入 的 路径地方以及名称
  fs.writeFileSync(resolve("./lib/demo.less"), arr);
}

ergodicDirectory(resolve("./packages"));
save();

总结

对于项目紧急的情况,现在是最好的处理方式了,欢迎各位小伙伴们告诉我其他方式,后面再优化,修改。

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值