Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

father4 预处理编译 less 文件 #30

Open
hexh250786313 opened this issue Nov 18, 2023 · 4 comments
Open

father4 预处理编译 less 文件 #30

hexh250786313 opened this issue Nov 18, 2023 · 4 comments
Labels
post blog post type: POST

Comments

@hexh250786313
Copy link
Owner

hexh250786313 commented Nov 18, 2023

不要点开, 博客网站用的
博文标题图片

pic

博文置顶说明
本文主要内容为,介绍前端开发中使用 father 进行打包组件库、工具库时,如何预处理编译 less 文件

一、引言

在前端开发中,我们经常会遇到需要对一些组件库进行二次封装的需求,特别是当我们使用阿里系的组件库,如 antd 或 antd-mobile 时。在这种情况下,我们通常会使用 fatherdumi 这一套官方推荐的方案。然而,father 官方并没有提供处理 scss 或 less 的示例,因此我们需要自行实现 father 的样式预处理。本文将介绍如何实现这一目标。

二、father 的工作原理

在深入讨论如何实现样式预处理之前,我们首先需要了解一下 father 在前端打包中的工作原理。

father 不仅可以用于打包前端代码,它也可以用于打包通用的 ts/js 代码库。实际上,father 是一个集成了多个打包工具的打包工具,例如,它集成了 babel 和 esbuild 两种编译器。

当我们需要打包的代码为前端代码(如前端组件库等)时,father 会使用 babel 作为 js/ts 的编译器。当我们需要打包的代码为 node 库时,father 则会使用速度更快的 esbuild。

此外,father 还提供了一些插件功能,例如 extraBabelPluginsaddLoaderextraBabelPlugins 可以让我们使用 babel 插件,而 addLoader 功能则类似于 webpack 的 loaders 功能,可以用于处理非 js/ts 文件。

三、实现样式预处理

了解了 father 的工作原理后,我们就可以开始实现样式预处理了。我们的任务可以分为两部分:

  1. addLoader 功能,实现一个 less 转 css 的插件。
  2. 使用 extraBabelPlugins,实现一个插件,将 js/ts 中的 .less 文件内容转为 .css

接下来,我们将详细介绍如何实现这两个任务。

3.1. 第一步、实现 babel 插件

这一步主要是为了把 js/ts 文件中的与 less 相关的文本替换为 css,例如:import "./index.less" 替换为 import "./index.css"

这一步比较简单,直接实现一个 babel 插件用于进行 .less 替换为 .css 的文本操作即可,代码如下:

// .fatherrc.ts
import { defineConfig } from 'father';

export default defineConfig({
    
    ...

    esm: { output: 'dist', transformer: 'babel' }, // 必须要使用 babel 模式
    extraBabelPlugins: [
        [
            './babel-less-to-css.js', // 把 js/ts 文件中的 '.less' 字符转为 '.css'
            {
                test: '\\.less',
            },
        ],
    ],

    ...

});
// babel-less-to-css.js
module.exports = function () {
    return {
        visitor: {
            ImportDeclaration(path) {
                if (/\.less$/.test(path.node.source.value)) {
                    path.node.source.value = path.node.source.value.replace(/\.less/, '.css');
                }
            },
        },
    };
};

3.2. 第二步、实现 loader 插件

这一步要实现一个把 less 文件转换为 css 文件的插件,其中:

  1. 使用 less.js 来进行对 less 到 css 的转义。
  2. 使用 postcss 来处理 less 中的相对路径和别名路径的处理以及处理浏览器兼容问题。

这两步本身其实已经和 father 没什么太大的关系,都是 webpack 中常用的对 less 文件的处理方法,所以这里直接给出示例代码,看代码就可以知道如何在 father 中实现这些功能:

// .fatherrc.ts
import { defineConfig } from 'father';

export default defineConfig({

    ...

    plugins: [
        './loader.ts', // 实现 loader 功能
    ],

    ...

});
// loader.ts
import type { IApi } from 'father';
import { addLoader, ILoaderItem } from 'father/dist/builder/bundless/loaders';

export default async (api: IApi) => {
    const loaders: ILoaderItem[] = await api.applyPlugins({
        key: 'addPostcssLoader',
        initialValue: [
            {
                key: 'less-to-css',
                test: /\.less$/,
                loader: require.resolve('./loader-less-to-css'), // less 文件转 css 文件
            },
        ],
    });

    loaders.forEach((loader) => addLoader(loader));
};
// loader-less-to-css.js
const path = require('path');
const less = require('less');
const postcss = require('postcss');
const syntax = require('postcss-less');
const atImport = require('postcss-import');
const autoprefixer = require('autoprefixer');

const loader = function (lessContent) {
    const cb = this.async();
    this.setOutputOptions({
        ext: '.css',
    });
    postcss([
        autoprefixer({
            // 提升兼容性
            overrideBrowserslist: ['last 10 versions'],
        }),
        atImport({
            resolve: (id) => {
                const currentPath = this.resource;
                if (id.startsWith('@')) {
                    // 处理别名路径,把 @ 替换成 src
                    const srcPath = path.join(__filename, './src');
                    const targetPath = id.replace(/^@/, srcPath);
                    return targetPath;
                } else {
                    // 处理相对路径
                    const relativePath = id;
                    const targetPath = path.resolve(currentPath, '..', relativePath);
                    return targetPath;
                }
            },
        }),
    ])
        .process(lessContent, { syntax })
        .then((result) => {
            // less 转 css
            less.render(result.content, (err, css) => {
                if (err) {
                    console.error(err);
                    return;
                }
                cb(null, css.css);
            });
        })
        .catch((err) => {
            console.error(err);
        });
};

module.exports = loader;

经过上述处理,就可以实现 less 的预处理,编译为 css 文件了。

四、补充

4.1 使用 babel 的原因

事实上 father 支持 babel、esbuild 和 SWC 三种构建方式,而本文使用的是 babel 模式。

father 的配置文档 中提到,platformbroswer 的时候,transformer 默认使用 babel 进行 js 的编译器,这意味着,father 官方也是推荐使用 babel 来编译前端的代码的。

即使使用 esbuild 有更快的打包速度,但是 esbuild 处理的是文件的二进制格式,很多现存的前端编译插件无法直接应用到其中;而 babel 则是生成 AST 语法树,其兼容性和拓展性也是 esbuild 和 SWC 无法比拟的。

不过最关键的还是:father 还没有实现自定义 esbuild 插件!它只提供了 babel 的自定义插件能力,这样一来其实我们别无选择。

@hexh250786313 hexh250786313 added the post blog post type: POST label Nov 18, 2023
@Lands-1203
Copy link

请问怎么修改

extraBabelPlugins: [
    [
      './babel-less-to-css.js', // 把 js/ts 文件中的 '.less' 字符转为 '.css'
      {
        test: '\\.less',
      },
    ],
  ],
  plugins: [
    'less-to-css', // 将 less 文件转换为 css 文件
  ],

让他直接转换呢?
不太想自己写loader

@hexh250786313
Copy link
Owner Author

@Lands-1203 extraBabelPlugins 可以直接使用通用的 babel 插件,但是我估计没有相关的这种把 ".css" 变为 ".less" 的插件,而且 babel 只能处理 js/ts 代码,处理 less 只能利用 loader,而 loader 就只能通过我上面的方式来写,我还没找到其他什么好方法

@ngwszsd
Copy link

ngwszsd commented May 14, 2024

你好 我在做一个基于antd的组件库,我现在需要在father.ts配置一个打包后把antd的类名前缀ant-设置成其他比如xlb-,由于我现在不能全局在Provide配置,因为要打不同的配置包,知道如何解决吗大佬

@hexh250786313
Copy link
Owner Author

@ngwszsd

由于我现在不能全局在Provide配置,因为要打不同的配置包

没看懂啥意思

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
post blog post type: POST
Projects
None yet
Development

No branches or pull requests

3 participants