如何使用代码拆分来构建一个UI组件库,用于CSS样式。

huangapple go评论66阅读模式
英文:

How to build a UI Component Library with code-splitting for CSS styles

问题

我想创建一个UI组件库,让消费者只能捆绑他们实际使用的组件的样式。

假设这个问题与框架无关,因为我更感兴趣评估现有的方法。

理念是用户可以这样做

import { Button } from '@mylib/ui`

而不需要去他们的 main.ts 添加

import '@mylib/ui/styles/main.min.css`

现在,显然,第一个解决方案是避免将CSS捆绑在一起,因此用户可以导入每个组件的单独样式表

import '@mylib/ui/styles/button.min.css';
import '@mylib/ui/styles/accordion.min.css';

现在,如果你使用少量组件并希望控制捆绑大小,这种方法效果很好,但如果消费者开始使用数十个组件,它就不适用了。

我如何克服这个挑战?

我能想到的唯一方法是内联样式,但由于缺少选择器、媒体查询等,这种方法不起作用。

我希望实现的是,让消费者声明他们需要什么,然后获取该组件的样式,而无需进行任何其他配置/导入,同时保留仅捆绑属于导入的组件的CSS的能力

import { Button } from '@mylib/ui`

我正在思考如何使这个想法变成现实...

英文:

I want to create a UI Component Library where consumers can bundle only the styles for the components they are actually using.

Let's say this question is framework-agnostic since I am more interested to evaluate existing approaches.

The idea would be for a user to be able to

import { Button } from '@mylib/ui`

and not being required to go to their main.ts and add

import '@mylib/ui/styles/main.min.css`

Now, obviously, the first solution is to avoid bundling the CSS together, so a user would be able to import separate stylesheet for each component

import '@mylib/ui/styles/button.min.css';
import '@mylib/ui/styles/accordion.min.css';

Now, this approach works well if you use a handful of components and want to keep the bundle size in check, but it doesn't scale if a consumer starts using dozen of components.

How can I overcome that challenge?

The only approach I can think of is inline styling, which won't work because of missing selectors, media queries, etc.

What I would love to achieve, is a way for consumer to declare what they need, and get the styles for that component without any additional configuration/import, while preserving the ability of only bundle the CSS that belongs to the components that are imported

import { Button } from '@mylib/ui`

I am scratching my head to come up with an idea of how this would work...

答案1

得分: 1

我认为在考虑所使用的框架之前很难回答这个问题。如果我们不知道所使用的框架,为每个组件导入CSS样式表可能是这个问题的最直接答案。

了解库设计的框架是至关重要的,因为这样你就知道了可用的捆绑工具和技术。最近我不得不解决Vue 3 SFC库的一个类似问题。我找到的解决方案是提供未捆绑的库代码(仅转译的 .vue 文件)(你可以查看 unbuild工具)。

我相信其他框架可能也提供类似的解决方案。
当然,这样一个库也有其缺点(例如你不能在没有捆绑器的情况下使用它)。

然而,这个解决方案直接回答了你的问题,因为使用这样构建的库,你可以写出这样的代码:

import { Button } from '@mylib/ui`

捆绑器将仅从此组件中捆绑样式(因为在SFC文件中,样式位于文件内部)。

附注:如果你要尝试这个解决方案,请记得在库的package.json中设置 sideEffects: false,这样使用你的库的项目中的捆绑器就会知道它可以对你的库进行树摇晃。

英文:

I think it's hard to answer this question without considering the framework you are using. If we don't know the framework, importing CSS sheets for each component is probably the most straightforward answer to this question.

Knowing the framework for which the library is designed is essential because then you know what bundling tools & technologies are available. Recently I had to resolve such a problem with Vue 3 SFC library. The solution I found was to deliver library code unbundled, (transpiled only .vue files) (you can check unbuild tool).

I believe other frameworks may offer similar solutions.
Of course, there are downsides to such a library (for example you can't use it without a bundler).

However, this solution directly answers your question, as with such constructed library you are able to write code like this:

import { Button } from '@mylib/ui`

and bundler will bundle styles only from this component (as in SFC files styles are inside the file).

PS, if you will play with this solution remember to set sideEffects: false in library package.json, so the bundler in a project which uses your library will know it can treeshake your library.

答案2

得分: 0

感谢 @Maksymilian Tomczyk 提出使用 `unbuild` 的建议,我提出了一个满足我的需求的配置:

```ts
// vite.config.ts
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  /**
   * Build is handled by Unbuild
   */
});
// build.config.ts
import { defineBuildConfig } from "unbuild";

export default defineBuildConfig({
  entries: [
    { builder: "mkdist", input: "./src/" },
    { builder: "mkdist", input: "./src/", format: "cjs", ext: "js" },
  ],
  declaration: true,
  clean: true,
});
{
  "name": "my-lib",
  "version": "0.0.4",
  "private": false,
  "files": [
    "dist"
  ],
  "module": "./dist/index.mjs",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    }
  },
  "sideEffects": false,
  "scripts": {
    "build:": "unbuild"
  }
}

这是项目的结构:

如何使用代码拆分来构建一个UI组件库,用于CSS样式。

这是最终的输出:

如何使用代码拆分来构建一个UI组件库,用于CSS样式。

这会创建一个未经触碰的输出,消费者可以导入并使用他们的捆绑工具来优化树摇动/编译。

只有 TypeScript 和 SASS 被 unbundled 预编译。


<details>
<summary>英文:</summary>

Thanks to @Maksymilian Tomczyk and his suggestion of using `unbuild` I&#39;ve come up with a configuration that serves my need:

```ts
// vite.config.ts
import { fileURLToPath, URL } from &quot;node:url&quot;;
import { defineConfig } from &quot;vite&quot;;
import vue from &quot;@vitejs/plugin-vue&quot;;
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      &quot;@&quot;: fileURLToPath(new URL(&quot;./src&quot;, import.meta.url)),
    },
  },
  /**
   * Build is handled by Unbuild
   */
});

// build.config.ts
import { defineBuildConfig } from &quot;unbuild&quot;;

export default defineBuildConfig({
  entries: [
    { builder: &quot;mkdist&quot;, input: &quot;./src/&quot; },
    { builder: &quot;mkdist&quot;, input: &quot;./src/&quot;, format: &quot;cjs&quot;, ext: &quot;js&quot; },
  ],
  declaration: true,
  clean: true,
});
{
&quot;name&quot;: &quot;my-lib&quot;,
  &quot;version&quot;: &quot;0.0.4&quot;,
  &quot;private&quot;: false,
  &quot;files&quot;: [
    &quot;dist&quot;
  ],
  &quot;module&quot;: &quot;./dist/index.mjs&quot;,
  &quot;main&quot;: &quot;./dist/index.js&quot;,
  &quot;types&quot;: &quot;./dist/index.d.ts&quot;,
  &quot;exports&quot;: {
    &quot;.&quot;: {
      &quot;import&quot;: &quot;./dist/index.mjs&quot;,
      &quot;require&quot;: &quot;./dist/index.js&quot;
    }
  },
  &quot;sideEffects&quot;:false,
  &quot;scripts&quot;: {
    &quot;build: &quot;unbuild&quot;
  }
}

This is the structure of the project:

如何使用代码拆分来构建一个UI组件库,用于CSS样式。

This is the final output:

如何使用代码拆分来构建一个UI组件库,用于CSS样式。

This creates an untouched output that consumers can import and use their bundler to optimise tree shaking/compilation.

Only TypeScript and SASS are pre-compiled by unbundled

huangapple
  • 本文由 发表于 2023年2月18日 18:43:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/75492775.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定