注:本文假设对Bazel有一定的了解。本文基于Bazel 4.2.2 版本
在web前端领域,前端样式,web浏览器只认CSS样式语言。而CSS样式语言又过于低级。于是有人发明了更高级的语言:Sass[1],用于生成CSS代码。
这样的方案,称为:“CSS预处理器”。除了使用Sass实现,还可以使用LESS实现等。
本文介绍是如何使用Bazel将Sass文件编译成CSS文件。
编译Sass的方法已经很多了,为什么我们还要使用Bazel呢?虽然本质上,Bazel编译Sass源代码时,使用的是sass原生的编译工具[2] 。
使用Bazel的原因如下:
1. 它支持增量构建:以避免将来的不必要的构建速度成本;
2. 对单仓库友好:因为它支持多语言,所以,我们可以放心地把前后端代码、基础设施代码放在同一个仓库中;
3. 统一构建工具:使整个工程的构建逻辑达到高度一致,以避免将来构建逻辑不一致的成本。
[workspace]/
WORKSPACE
hello_world/
BUILD
main.scss
shared/
BUILD
_fonts.scss
_colors.scss
在WORKSPACE中引入Rule:
git_repository(
name = "io_bazel_rules_sass",
commit = "354793d0603dbe26232f4a5fb25e67e0e9e4c909",
# 我这里指定最新的是commit
remote = "https://github.com/bazelbuild/rules_sass.git",
)
# Setup Bazel NodeJS rules.
# See: https://bazelbuild.github.io/rules_nodejs/install.html.
# Setup repositories which are needed for the Sass rules.
load("@io_bazel_rules_sass//:defs.bzl", "sass_repositories")
sass_repositories()
我们采用git_repository
的方式引入rules_sass
。
当你执行构建时,会遇到错误:
ERROR: Failed to load Starlark extension '@build_bazel_rules_nodejs//:index.bzl'.
Cycle in the workspace file detected. This indicates that a repository is used prior to being defined.
The following chain of repository dependencies lead to the missing definition.
- @build_bazel_rules_nodejs
This could either mean you have to add the '@build_bazel_rules_nodejs' repository with a statement like `http_archive` in your WORKSPACE file (note that transitive dependencies are not added automatically), or move an existing definition earlier in your WORKSPACE file.
ERROR: cycles detected during target parsing
这是因为rules_sass
依赖于rules_nodejs
。解决办法是在rules_sass
之前声明rules_nodejs
:
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "c077680a307eb88f3e62b0b662c2e9c6315319385bc8c637a861ffdbed8ca247",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/5.1.0/rules_nodejs-5.1.0.tar.gz"],
)
load("@build_bazel_rules_nodejs//:repositories.bzl", "build_bazel_rules_nodejs_dependencies")
build_bazel_rules_nodejs_dependencies()
load("@rules_nodejs//nodejs:repositories.bzl", "nodejs_register_toolchains")
load("@rules_nodejs//nodejs:yarn_repositories.bzl", "yarn_repositories")
nodejs_register_toolchains(
name = "nodejs",
node_version = "16.13.2",
)
yarn_repositories(
name = "yarn",
yarn_version = "1.22.17",
)
以下是rules_sass的声明
注:将rules_nodejs
显示声明在WORKSPACE中,是一个避免与其它Rules发生冲突的好习惯。
注:执行构建的过程需要连接外网,请留意你的网络。
样例工程中,shared/BUILD
定义的是sass库:
package(default_visibility = ["//visibility:public"])
load("@io_bazel_rules_sass//:defs.bzl", "sass_library")
sass_library(
name = "colors",
srcs = ["_colors.scss"],
)
sass_library(
name = "fonts",
srcs = ["_fonts.scss"],
)
hello_world/BUILD
声明的是最终的sass文件的构建逻辑,注意它使用的是sass_binary
:
package(default_visibility = ["//visibility:public"])
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
sass_binary(
name = "hello_world",
src = "main.scss",
deps = [
"//shared:colors",
"//shared:fonts",
],
)
sass_binary
用于输出一个css文件。它还有很多参数,比如:
• output_dir: css输出文件的文件夹
• output_name:css输出的文件名称
更多参数,请前往Github仓库。
在main.scss文件中,我们需要注意的是:在@import
时,import的是sass_library的名称,而不是带有下划线的真实文件名:
@import "shared/fonts"; // 这里需要注意
@import "shared/colors";
html {
body {
font-family: $default-font-stack;
h1 {
font-family: $modern-font-stack;
color: $example-red;
}
}
}
$ bazel build //hello_world
INFO: Found 1 target...
Target //hello_world:hello_world up-to-date:
bazel-bin/hello_world/hello_world.css
bazel-bin/hello_world/hello_world.css.map
INFO: Elapsed time: 1.911s, Critical Path: 0.01s
采用Bazel构建Sass不是没有成本的。比如旧工程的迁移成本、前端开发人员的学习成本等。你需要根据你的项目的实际情况做权衡。
[1]
Sass: https://sass-lang.com/[2]
编译工具: https://github.com/bazelbuild/rules_sass/blob/main/sass/sass_wrapper.js
往期文章推荐:
比构建速度,Bazel是Gradle的10倍,不服不行!!!