本文主要内容为,介绍前端开发中使用 father 进行打包组件库、工具库时,如何预处理编译 less 文件
在前端开发中,我们经常会遇到需要对一些组件库进行二次封装的需求,特别是当我们使用阿里系的组件库,如 antd 或 antd-mobile 时。在这种情况下,我们通常会使用 father 和 dumi 这一套官方推荐的方案。然而,father 官方并没有提供处理 scss 或 less 的示例,因此我们需要自行实现 father 的样式预处理。本文将介绍如何实现这一目标。
在深入讨论如何实现样式预处理之前,我们首先需要了解一下 father 在前端打包中的工作原理。
father 不仅可以用于打包前端代码,它也可以用于打包通用的 ts/js 代码库。实际上,father 是一个集成了多个打包工具的打包工具,例如,它集成了 babel 和 esbuild 两种编译器。
当我们需要打包的代码为前端代码(如前端组件库等)时,father 会使用 babel 作为 js/ts 的编译器。当我们需要打包的代码为 node 库时,father 则会使用速度更快的 esbuild。
此外,father 还提供了一些插件功能,例如 extraBabelPlugins
和 addLoader
。extraBabelPlugins
可以让我们使用 babel 插件,而 addLoader
功能则类似于 webpack 的 loaders
功能,可以用于处理非 js/ts 文件。
了解了 father 的工作原理后,我们就可以开始实现样式预处理了。我们的任务可以分为两部分:
addLoader
功能,实现一个 less 转 css 的插件。extraBabelPlugins
,实现一个插件,将 js/ts 中的 .less
文件内容转为 .css
。接下来,我们将详细介绍如何实现这两个任务。
这一步主要是为了把 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');
}
},
},
};
};
这一步要实现一个把 less 文件转换为 css 文件的插件,其中:
这两步本身其实已经和 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 文件了。
事实上 father 支持 babel、esbuild 和 SWC 三种构建方式,而本文使用的是 babel 模式。
father 的配置文档 中提到,platform
为 broswer
的时候,transformer
默认使用 babel
进行 js 的编译器,这意味着,father 官方也是推荐使用 babel 来编译前端的代码的。
即使使用 esbuild 有更快的打包速度,但是 esbuild 处理的是文件的二进制格式,很多现存的前端编译插件无法直接应用到其中;而 babel 则是生成 AST 语法树,其兼容性和拓展性也是 esbuild 和 SWC 无法比拟的。
不过最关键的还是:father 还没有实现自定义 esbuild 插件!它只提供了 babel 的自定义插件能力,这样一来其实我们别无选择。
点击这里前往 Github 查看原文,交流意见~
文档信息
版权声明:自由转载 - 非商用 - 非衍生 - 保持署名(创意共享3.0许可证)