首页 前端知识 lightningcss介绍及使用

lightningcss介绍及使用

2024-08-21 10:08:15 前端知识 前端哥 405 208 我要收藏

lightningcss介绍及使用

一款使用 rust 编写的 css 解析器,转换器、及压缩器。

特性

  • 特别快:可以在毫秒级别解析、压缩大量的 css 文件,而且比其他工具的打包结果更小
  • 给值添加类型:许多其他css解析器会将值解析成一个无类型的 tokens,这也就意味着每个转换器如果想要操作这些值就需要手动侵入他们,造成重复工作及不统一性。而 lightningcss 遵循 css 的语法规范,给每个值提供了一个规范的类型。
  • 基于浏览器的解析器:lightningcss 是在 cssParser 和 selectors 基础上创建的(这俩工具都是由 Mozilla 创建,被 Firefox 和 Servo 使用),基于它们提供的有效、通用的 css 解析,lightningcss 实现了全部的 css 规格及属性。
  • 压缩:lightningcss 其中一个主要功能就是压缩 css,使其变得更小,包括以下几点优化:
    • 尽可能将常规写法的属性编写简写
    • 在安全情况下将相同选择器或者声明中的规则进行合并
    • 根据提供的浏览器类型删除多余的前缀
    • 尽可能减少 calc 表达式
    • 尽可能将 colors 值转换成十六进制格式
    • 压缩 gradient 属性
    • 压缩 css grid
    • 值排序
    • 拥有许多细节优化,比如:转换成更短的单位,移除非必要的引号等
  • 浏览器前缀:跟其他 css 编译器一样支持自动添加浏览器前缀
  • 浏览器配置:支持浏览器列表配置
  • 语法降级:只能最新的css语法,并能够根据浏览器目标进行适配
    • css 嵌套
    • 常规的媒体查询
    • 逻辑值
    • Color level 5
      • color-mix() 函数
      • 相对颜色语法:lab(from purple calc(l * .8) a b)
    • Color lever 4
      • lab,lch、oklab、oklch 这些颜色格式
    • 选择器
      • :not、:lang、:dir、:is
  • css 模块:支持 css modules 的特性
  • 自定义转换器:支持通过插件的方法自定义转换器插件

安装

npm i lightningcss -D

api

不同 api 打包的结果不同。

示例结构:

  • index.css(导入 body.css)
  • body.css
/* index.css */
@import url('./body.css');

a,
.link {
  color: red;
}
a:hover {
  color: blue;
}

/* body.css */
body {
  background: rgb(157, 177, 88);
}

transform

编译一个 css 文件,可以配置压缩、语法降级、sourcemap 生成(默认不生成)等。

import * as lightningcss from "lightningcss";
import fs from "node:fs/promises";

async function getCssContent() {
  const content = await fs.readFile("./index.css", "utf-8");
  return content;
}

async function transform() {
  const file = await getCssContent();
  let result = await lightningcss.transform({
    code: Buffer.from(file),
  });
  return result;
}

transform().then(res => {
  console.log(res)
})
// {
//  code: <Buffer 40 69 6d 70 6f 72 74 20 22 2e 2f 62 6f 64 79 2e 63 73 73 22 3b 0a 0a 61 2c 20 2e 6c 69 6e 6b 20 7b 0a 20 20 63 6f 6c 6f 72 3a 20 72 65 64 3b 0a 7d 0a ... 28 more bytes>,
//  map: null,
//  exports: null,
//  references: null,
//  dependencies: null,
//  warnings: []
// }

可以看到返回的结果中 code 也是 Buffer,需要调用 toString() 方法转换成字符串。

res.code.toString();
// @import "./body.css";

// a, .link {
//   color: red;
// }

// a:hover {
//   color: #00f;
// }

该方法并不会直接解析出 @import 导入的 css 文件内容,也没有任何关于导入文件信息,如果我们要分析导入文件,需要传递 analyzeDependencies: true

此时在 dependencies 字段中就会有相关的信息:

// {
//  code: <Buffer 40 69 6d 70 6f 72 74 20 22 2e 2f 62 6f 64 79 2e 63 73 73 22 3b 0a 0a 61 2c 20 2e 6c 69 6e 6b 20 7b 0a 20 20 63 6f 6c 6f 72 3a 20 72 65 64 3b 0a 7d 0a ... 28 more bytes>,
//  map: null,
//  exports: null,
//  references: null,
//  dependencies: [
//    {
//      type: 'import',
//      url: './body.css',
//      placeholder: '2PNO-q',
//      supports: null,
//      media: null,
//      loc: {
//        filePath: ''
//        start: { line: 1, column: 9 },
//        end: { line: 1, column: 20 }
//      }
//    }
//  ],
//  warnings: []
// }

需要注意以下上面 dependencies 中的 placeholder 字段,它是一个占位符,可以通过这个占位符将生成的文件名进行更改:

transformStyleAttribute

编译单个文件,比如说 html 中的内联 css。使用这个 api 时能不能携带 @import

async function transform() {
  const file = await getCssContent();
  let result = await lightningcss.transformStyleAttribute({
    code: Buffer.from(file),
  });
  return result;
}

transform().then((res) => {
  console.log(res.code.toString());
});
// SyntaxError: Unknown at rule: @import

由于 index.css 中携带了 @import url(‘./body.css’),会导致报错,把这行注释掉就可以运行了。

bundle、bundleAsync

bundle 同步打包 css 及对应的依赖(@import 导入的文件);bundleAsync 异步打包 css 及对应的依赖(@import 导入的文件)。

async function transform() {
  let result = await lightningcss.bundleAsync({
    filename: "./index.css",
    // analyzeDependencies: true,
  });
  return result;
}

transform().then((res) => {
  console.log(res);
});
// {
//  code: <Buffer 40 69 6d 70 6f 72 74 20 22 2e 2f 62 6f 64 79 2e 63 73 73 22 3b 0a 0a 61 2c 20 2e 6c 69 6e 6b 20 7b 0a 20 20 63 6f 6c 6f 72 3a 20 72 65 64 3b 0a 7d 0a ... 28 more bytes>,
//  map: null,
//  exports: null,
//  references: null,
//  dependencies: null,
//  warnings: []
// }

与 transform 方法返回的结果类似,不同的是 bundle、bundleAsync 会把 @import 导入的文件也输出在 code 字段中:

res.code.toString();
// body {
//   background: #9db158;
// }

// a, .link {
//   color: red;
// }

// a:hover {
//   color: #00f;
// }

此时即使添加了 analyzeDependencies: true 配置,也不会打印出任何依赖相关信息。

composeVisitors

将多个 visitor 对象合并成一个。

visitor 对象

自定义转换是通过将 visitor 对象传递给 lightningcss 来实现的。visitor 对象包含一个或多个函数,这些函数是针对特定值类型(如规则、属性或长度)调用的。一般来说,我们应该尽可能具体地说明要处理的值的类型。这样,lightningcss 需要尽可能少地调用JS,使用尽可能小的对象,从而提高性能。

async function transform() {
  let result = await lightningcss.transform({
    code: Buffer.from(`
      .foo {
        width: 12px;
      }
    `),
    visitor: {
      Length(length) {
        return {
          unit: length.unit,
          value: length.value * 2,
        };
      },
    },
  });
  return result;
}

transform().then((res) => {
  console.log(res.code.toString());
});
// .foo {
//    width: 24px;
// }
let environmentVisitor = {
  EnvironmentVariable: {
    "--branding-padding": () => ({
      type: "length",
      value: {
        unit: "px",
        value: 20,
      },
    }),
  },
};

let doubleFunctionVisitor = {
  FunctionExit: {
    double(f) {
      if (f.arguments[0].type === "length") {
        return {
          type: "length",
          value: {
            unit: f.arguments[0].value.unit,
            value: f.arguments[0].value.value * 2,
          },
        };
      }
    },
  },
};

async function transform() {
  let result = await lightningcss.transform({
    code: Buffer.from(`
    .foo {
      padding: double(env(--branding-padding));
    }
  `),
    visitor: lightningcss.composeVisitors([
      environmentVisitor,
      doubleFunctionVisitor,
    ]),
  });
  return result;
}

transform().then((res) => {
  console.log(res.code.toString());
});
// .foo {
//    padding: 40px;
// }

browserslistToTargets

将一个浏览器列表转换成可以被 lightningcss 识别的目标。

通过使用 browserslist 结合一起使用:

lightningcss.browserslistToTargets(browserslist(">= 0.25%"))

在 target 字段中进行配置:

{
  filename: "./index.css",
  targets: lightningcss.browserslistToTargets(browserslist("IE 8")),
}
browserslist

browserslist 是一个轻量级但功能强大的工具,它为前端开发者提供了一种标准化的方法来选择需要支持的浏览器列表。这个项目旨在解决一个问题:在多如牛毛的浏览器版本中,如何决定我们的代码应该兼容哪些?它的存在使得我们可以通过简洁的语句定义我们的目标浏览器,从而自动化处理兼容性问题。

Browserslist 的核心是一个解析和处理查询字符串的引擎。开发者可以以 YAML、JSON 或直接在代码中定义支持的浏览器范围,例如 “last 2 versions” 或 “> 5%, not IE 11”。这些查询基于caniuse的数据,实时更新,确保了我们的项目始终与市场现状相符。

配置方式:

在工程中使用 Browserslist 有两种常见方式:在 package.json 相应字段中增加;独立的 browserslistrc 文件

在 package.json 中声明

"browserslist": [
 "> 1%",
 "last 2 versions",
 "not ie <= 8"
]

通过 browserslistrc 配置

# Browsers that we support
> 1%
last 2 versions
not ie <= 8

两种方式没有差异,大家根据自己习惯或者各自团队规范进行管理即可。

如何配置?

通过上述方式,我们可以圈定我们也支持哪些浏览器及版本,接下来就是如果通过 browserslist 进行配置?

可以通过 https://browsersl.ist/ 这个网站来查看,我们配置的内容具体支持的浏览器情况
可以选择在全球、某个地区或某个国家/地区拥有超过或低于一定规模观众的版本

  • 5% 全世界使用率大于5%
  • = 5% in US 美国使用率大于等于5%

选择最近的浏览器版本

  • last 2 versions 所有浏览器最新的2个版本
  • last 2 Chrome versions chrome 浏览器最新2个版本

特定浏览器版本

  • Chrome > 100 chrome 浏览器版本大于100
  • not Firefox ESR 排除 Firefox ESR

选择支持特定功能的浏览器版本

  • supports es6-module 支持 es6-module 的浏览器
  • supports css-grid 支持 css-grid 的浏览器

以上条件可以组合

  • 0.5%, last 2 versions 使用率大于0.5% 或者 所有浏览器最新2个版本(等价于 > 0.5% or last 2 versions)
  • 0.5% and last 2 versions 使用率大于0.5% 的浏览器最新2个版本
  • defaults 等价于 > 0.5%, last 2 versions, Firefox ESR, not dead

了解了上述配置语法,配置完成后,我们可以上述提到的 https://browsersl.ist/ 进行实时查看。除此,你也可以通过下述工具,来检测配置是否正确和支撑的具体浏览器版本。

配置

  • filename:在 transform、transformStyleAttribute 这两个 api 中是可选的,仅用于在错误或者 sourcemap 中提示文件名,而在 bundle、bundleAsync 中是必传的,以这个文件名作为入口文件进行处理。
  • code:在 transform、transformStyleAttribute 这两个 api 中是必传的,要进行处理的 css 内容,与其他css处理器不同的是,它只支持 Uint8Array 格式,传入 string 格式会报错,在 node 中可以使用 buffer.from(string) 进行转换后传递,在浏览器中使用 new TextEncoder().encode(string) 进行转换后传递,在 bundle、bundleAsync 中这个配置无效。
  • minify:可选,是否要压缩
  • target:可选,浏览器目标
  • analyzeDependencies:可选,是否要分析 url() 导入的依赖,默认 false,设置成true 的话返回的结果会携带相关依赖信息。在 bundle、bundleAsync 中无效,因为会被打包在一个文件中,不会有任何的 dependence。
  • errorRecovery:可选,默认 false,是否要忽略无效的规则或者声明而不是直接报错。设置成 true 的话不会无效的规则、声明,照样打包,但是会提供警告。
  • visitor:可选,可以用于自定义转换器或者分析 lightningcss 的转换结果。

与工具集成

在 deno 或者浏览器中直接使用

需要导入 lightningcss-wasm 才能使用,在浏览器中使用的话需要支持 WebAssembly 特性,同时因为 lightningcss 不支持使用 string 的格式传递要转换成 css 代码,需要使用 TextEncoder 进行转换后传递,最后使用 TextDecoder 对结果进行处理:

import init, { transform } from 'https://esm.run/lightningcss-wasm';

await init();

let {code, map} = transform({
  filename: 'style.css',
  code: new TextEncoder().encode('.foo { color: red }'),
  minify: true,
});

console.log(new TextDecoder().decode(code));

bundle 及 composeVisitors 暂时不支持在浏览器中使用。

在 webpack 中使用

需要安装 lightningcss、css-minimizer-webpack-plugin、browserslist 这三个依赖,之后在 webpack.config.js 文件中进行配置:

// webpack.config.js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const lightningcss = require('lightningcss');
const browserslist = require('browserslist');

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new CssMinimizerPlugin({
        minify: CssMinimizerPlugin.lightningCssMinify,
        minimizerOptions: {
          targets: lightningcss.browserslistToTargets(browserslist('>= 0.25%'))
        },
      }),
    ],
  },
};

在 vite 中使用

vite 是默认支持 lightningcss 的,但是也要先安装 lightningcss:

import {browserslistToTargets} from 'lightningcss';

export default {
  css: {
    transformer: 'lightningcss',
    lightningcss: {
      targets: browserslistToTargets(browserslist('>= 0.25%'))
    }
  },
  build: {
    cssMinify: 'lightningcss'
  }
};

可配置字段可以查阅Lightning CSS 仓库

cli

lightningcss 提供了一个独立的 cli 工具,可以用于编译、压缩、打包 css 文件。它只针对于css的编译,如果我们需要代码分割或者支持其他类型的css文件这些更强大的功能,就不能使用这个 cli 工具了。

安装 cli:

npm install --save-dev lightningcss-cli
{
  "scripts": {
    "build": "lightningcss --minify --bundle --targets '>= 0.25%' input.css -o output.css"
  }
}
转载请注明出处或者链接地址:https://www.qianduange.cn//article/16291.html
标签
评论
发布的文章

安装Nodejs后,npm无法使用

2024-11-30 11:11:38

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!