Sass准备推出更人性化的模块系统

简介: Sass just launched a major new feature you might recognize from other languages: a module system. This is a big step forward for @import. one of the most-used Sass-features. While the current @import rule allows you to pull in third-party packages, and split your Sass into manageable "partia

Sass just launched a major new feature you might recognize from other languages: a module system. This is a big step forward for @import. one of the most-used Sass-features. While the current @import rule         allows you to pull in third-party packages, and split your Sass into manageable "partials," it has a few limitations:


  • @import is also a CSS feature, and the differences can be confusing
  • If you @import the same file multiple times, it can slow down compilation, cause override conflicts, and generate duplicate output.
  • Everything is in the global namespace, including third-party packages – so my color() function might override your existing color() function, or vice versa.
  • When you use a function like color(). it’s impossible to know exactly where it was defined. Which @import does it come from?


Sass package authors (like me) have tried to work around the namespace issues by manually prefixing our variables and functions — but Sass modules are a much more powerful solution. In brief, @import is being replaced with more explicit         @use and @forward rules. Over the next few years Sass @import will be deprecated, and then removed. You can still use CSS imports, but         they won’t be compiled by Sass. Don’t worry, there’s a migration tool to help you upgrade!

Import files with @use


@use 'buttons';


The new @use is similar to @import. but has some notable differences:


  • The file is only imported once, no matter how many times you @use it in a project.
  • Variables, mixins, and functions (what Sass calls "members") that start with an underscore (_) or hyphen (-) are considered private, and not imported.
  • Members from the used file (buttons.scss in this case) are only made available locally, but not passed along to future imports.
  • Similarly, @extends will only apply up the chain; extending selectors in imported files, but not extending files that import this one.
  • All imported members are namespaced by default.


When we @use a file, Sass automatically generates a namespace based on the file name:


@use 'buttons'; // creates a `buttons` namespace
@use 'forms'; // creates a `forms` namespace


We now have access to members from both buttons.scss and forms.scss — but that access is not transferred between the imports: forms.scss still has no access to the variables defined in buttons.scss.         Because the imported features are namespaced, we have to use a new period-divided syntax to access them:


// variables: <namespace>.$variable
$btn-color: buttons.$color;
$form-border: forms.$input-border;
// functions: <namespace>.function()
$btn-background: buttons.background();
$form-border: forms.border();
// mixins: @include <namespace>.mixin()
@include buttons.submit();
@include forms.input();


We can change or remove the default namespace by adding as <name> to the import:


@use 'buttons' as *; // the star removes any namespace
@use 'forms' as 'f';
$btn-color: $color; // buttons.$color without a namespace
$form-border: f.$input-border; // forms.$input-border with a custom namespace复制代码


Using as * adds a module to the root namespace, so no prefix is required, but those members are still locally scoped to the current document.


Import built-in Sass modules


Internal Sass features have also moved into the module system, so we have complete control over the global namespace. There are several built-in modules — math, color, string, list, map,         selector, and meta — which have to be imported explicitly in a file before they are used:


@use 'sass:math';
$half: math.percentage(1/2);


Sass modules can also be imported to the global namespace:


@use 'sass:math' as *;
$half: percentage(1/2);


Internal functions that already had prefixed names, like map-get or str-index. can be used without duplicating that prefix:


@use 'sass:map';
@use 'sass:string';
$map-get: map.get(('key': 'value'), 'key');
$str-index: string.index('string', 'i');


You can find a full list of built-in modules, functions, and name changes in the Sass module specification.


New and changed core features


As a side benefit, this means that Sass can safely add new internal mixins and functions without causing name conflicts. The most exciting example in this release is a sass:meta mixin called load-css(). This works similar         to @use but it only returns generated CSS output, and it can be used dynamically anywhere in our code:


@use 'sass:meta';
$theme-name: 'dark';
[data-theme='#{$theme-name}'] {
  @include meta.load-css($theme-name);
}


The first argument is a module URL (like @use) but it can be dynamically changed by variables, and even include interpolation, like theme-#{$name}. The second (optional) argument accepts a map of configuration values:


// Configure the $base-color variable in 'theme/dark' before loading
@include meta.load-css(
  'theme/dark', 
  $with: ('base-color': rebeccapurple)
);


The $with argument accepts configuration keys and values for any variable in the loaded module, if it is both:


  • A global variable that doesn’t start with _ or - (now used to signify privacy)
  • Marked as a !default value, to be configured


// theme/_dark.scss
$base-color: black !default; // available for configuration
$_private: true !default; // not available because private
$config: false; // not available because not marked as a !default


Note that the 'base-color' key will set the $base-color variable.


There are two more sass:meta functions that are new: module-variables() and module-functions(). Each returns a map of member names and values from an already-imported module. These accept a single argument matching         the module namespace:


@use 'forms';
$form-vars: module-variables('forms');
// (
//   button-color: blue,
//   input-border: thin,
// )
$form-functions: module-functions('forms');
// (
//   background: get-function('background'),
//   border: get-function('border'),
// )


Several other sass:meta functions — global-variable-exists(), function-exists(), mixin-exists(), and get-function() — will get additional $module arguments, allowing us         to inspect each namespace explicitly.


Adjusting and scaling colors


The sass:color module also has some interesting caveats, as we try to move away from some legacy issues. Many of the legacy shortcuts like lighten(). or adjust-hue() are deprecated for now in favor of explicit         color.adjust() and color.scale() functions:


// previously lighten(red, 20%)
$light-red: color.adjust(red, $lightness: 20%);
// previously adjust-hue(red, 180deg)
$complement: color.adjust(red, $hue: 180deg);


Some of those old functions (like adjust-hue) are redundant and unnecessary. Others — like lighten. darken. saturate. and so on — need to be re-built with better internal logic. The original functions         were based on adjust(). which uses linear math: adding 20% to the current lightness of red in our example above. In most cases, we actually want to scale() the lightness by a percentage, relative         to the current value:


// 20% of the distance to white, rather than current-lightness + 20
$light-red: color.scale(red, $lightness: 20%);


Once fully deprecated and removed, these shortcut functions will eventually re-appear in sass:color with new behavior based on color.scale() rather than color.adjust(). This is happening in stages to avoid sudden         backwards-breaking changes. In the meantime, I recommend manually checking your code to see where color.scale() might work better for you.


Configure imported libraries


Third-party or re-usable libraries will often come with default global configuration variables for you to override. We used to do that with variables before an import:


// _buttons.scss
$color: blue !default;
// old.scss
$color: red;
@import 'buttons';


Since used modules no longer have access to local variables, we need a new way to set those defaults. We can do that by adding a configuration map to @use:


@use 'buttons' with (
  $color: red,
  $style: 'flat',
);


This is similar to the $with argument in load-css(). but rather than using variable-names as keys, we use the variable itself, starting with $.


I love how explicit this makes configuration, but there’s one rule that has tripped me up several times: a module can only be configured once, the first time it is used. Import order has always been important for Sass, even with @import.         but those issues always failed silently. Now we get an explicit error, which is both good and sometimes surprising. Make sure to @use and configure libraries first thing in any "entrypoint" file (the central document that imports         all partials), so that those configurations compile before other @use of the libraries.


It’s (currently) impossible to "chain" configurations together while keeping them editable, but you can wrap a configured module along with extensions, and pass that along as a new module.


Pass along files with @forward


We don’t always need to use a file, and access its members. Sometimes we just want to pass it along to future imports. Let’s say we have multiple form-related partials, and we want to import all of them together as one namespace. We can do that with         @forward:


// forms/_index.scss
@forward 'input';
@forward 'textarea';
@forward 'select';
@forward 'buttons';


Members of the forwarded files are not available in the current document and no namespace is created, but those variables, functions, and mixins will be available when another file wants to @use or @forward the entire collection.         If the forwarded partials contain actual CSS, that will also be passed along without generating output until the package is used. At that point it will all be treated as a single module with a single namespace:


// styles.scss
@use 'forms'; // imports all of the forwarded members in the `forms` namespace复制代码


Note: if you ask Sass to import a directory, it will look for a file named index or _index)


By default, all public members will forward with a module. But we can be more selective by adding show or hide clauses, and naming specific members to include or exclude:


// forward only the 'input' border() mixin, and $border-color variable
@forward 'input' show border, $border-color;
// forward all 'buttons' members *except* the gradient() function
@forward 'buttons' hide gradient;


Note: when functions and mixins share a name, they are shown and hidden together.


In order to clarify source, or avoid naming conflicts between forwarded modules, we can use as to prefix members of a partial as we forward:


// forms/_index.scss
// @forward "<url>" as <prefix>-*;
// assume both modules include a background() mixin
@forward 'input' as input-*;
@forward 'buttons' as btn-*;
// style.scss
@use 'forms';
@include forms.input-background();
@include forms.btn-background();


And, if we need, we can always @use and @forward the same module by adding both rules:


@forward 'forms';
@use 'forms';


That’s particularly useful if you want to wrap a library with configuration or any additional tools, before passing it along to your other files. It can even help simplify import paths:


// _tools.scss
// only use the library once, with configuration
@use 'accoutrement/sass/tools' with (
  $font-path: '../fonts/',
);
// forward the configured library with this partial
@forward 'accoutrement/sass/tools';
// add any extensions here...
// _anywhere-else.scss
// import the wrapped-and-extended library, already configured
@use 'tools';


Both @use and @forward must be declared at the root of the document (not nested), and at the start of the file. Only @charset and simple variable definitions can appear before the import commands.


Moving to modules


In order to test the new syntax, I built a new open source Sass library (Cascading Color Systems) and a new website for my band — both         still under construction. I wanted to understand modules as both a library and website author. Let’s start with the "end user" experience of writing site styles with the module syntax…


Maintaining and writing styles


Using modules on the website was a pleasure. The new syntax encourages a code architecture that I already use. All my global configuration and tool imports live in a single directory (I call it config), with an index file that forwards         everything I need:


// config/_index.scss
@forward 'tools';
@forward 'fonts';
@forward 'scale';
@forward 'colors';


As I build out other aspects of the site, I can import those tools and configurations wherever I need them:


// layout/_banner.scss
@use '../config';
.page-title {
  @include config.font-family('header');
}


This even works with my existing Sass libraries, like Accoutrement and Herman, that still use the old @import syntax. Since the @import        rule will not be replaced everywhere overnight, Sass has built in a transition period. Modules are available now, but @import will not be deprecated for another year or two — and only removed from the language a year after that. In         the meantime, the two systems will work together in either direction:


  • If we @import a file that contains the new @use/@forward syntax, only the public members are imported, without namespace.
  • If we @use or @forward a file that contains legacy @import syntax, we get access to all the nested imports as a single namespace.


That means you can start using the new module syntax right away, without waiting for a new release of your favorite libraries: and I can take some time to update all my libraries!


Migration tool


Upgrading shouldn’t take long if we use the Migration Tool built by Jennifer Thakar. It can be installed with Node, Chocolatey, or Homebrew:


npm install -g sass-migrator
choco install sass-migrator
brew install sass/sass/migrator


This is not a single-use tool for migrating to modules. Now that Sass is back in active development (see below), the migration tool will also get regular updates to help migrate each new feature. It’s a good idea to install this globally, and keep         it around for future use.


The migrator can be run from the command line, and will hopefully be added to third-party applications like CodeKit and Scout as well. Point it at a single Sass file, like style.scss.         and tell it what migration(s) to apply. At this point there’s only one migration called module:


# sass-migrator <migration> <entrypoint.scss...>
sass-migrator module style.scss


By default, the migrator will only update a single file, but in most cases we’ll want to update the main file and all its dependencies: any partials that are imported, forwarded, or used. We can do that by mentioning each file individually,         or by adding the --migrate-deps flag:


sass-migrator --migrate-deps module style.scss


For a test-run, we can add --dry-run --verbose (or -nv for short), and see the results without changing any files. There are a number of other options that we can use to customize the migration — even one specifically for         helping library authors remove old manual namespaces — but I won’t cover all of them here. The migration tool is fully documented on the Sass website.


Updating published libraries


I ran into a few issues on the library side, specifically trying to make user-configurations available across multiple files, and working around the missing chained-configurations. The ordering errors can be difficult to debug, but the results are         worth the effort, and I think we’ll see some additional patches coming soon. I still have to experiment with the migration tool on complex packages, and possibly write a follow-up post for library authors.


The important thing to know right now is that Sass has us covered during the transition period. Not only can imports and modules work together, but we can create "import-only"         files to provide a better experience for legacy users still @importing our libraries. In most cases, this will be an alternative version of the main package file, and you’ll want them side-by-side: <name>.scss for         module users, and <name>.import.scss for legacy users. Any time a user calls @import <name>, it will load the .import version of the file:


// load _forms.scss
@use 'forms';
// load _forms.input.scss
@import 'forms';


This is particularly useful for adding prefixes for non-module users:


// _forms.import.scss
// Forward the main module, while adding a prefix
@forward "forms" as forms-*;


Upgrading Sass


You may remember that Sass had a feature-freeze a few years back, to get various implementations (LibSass, Node Sass, Dart Sass) all caught up, and eventually retired the original Ruby implementation.         That freeze ended last year, with several new features and active discussions and development on GitHub – but not much fanfare. If you missed those releases, you can get caught up on the Sass Blog:



Dart Sass is now the canonical implementation, and will generally be the first to implement new features. If you want the latest, I recommend making the switch. You can install Dart Sass with Node, Chocolatey,         or Homebrew. It also works great with existing gulp-sass build steps.


Much like CSS (since CSS3), there is no longer a single unified version-number for new releases. All Sass implementations are working from the same specification, but each one has a unique release schedule and numbering, reflected with support information         in the beautiful new documentation designed by Jina.


Sass Modules are available as of October 1st, 2019 in Dart Sass 1.23.0.



目录
相关文章
|
8月前
|
资源调度 前端开发 JavaScript
构建高效前端项目:现代包管理器与模块化的深度解析
【2月更文挑战第21天】 在当今快速演变的前端开发领域,高效的项目管理和代码组织已成为成功交付复杂Web应用的关键。本文将深入探讨现代前端包管理器如npm, yarn和pnpm的工作原理,以及它们如何与模块化编程实践(例如CommonJS、ES6模块)协同工作以优化开发流程。我们将剖析这些工具的内部机制,了解它们如何解决依赖冲突,提高安装速度,并保证项目的健壮性。同时,本文还将介绍模块化编程的最佳实践,包括代码拆分、重用和版本控制,帮助开发者构建可维护且性能卓越的前端项目。
|
4月前
|
Linux C# 开发者
Uno Platform 驱动的跨平台应用开发:从零开始的全方位资源指南与定制化学习路径规划,助您轻松上手并精通 C# 与 XAML 编程技巧,打造高效多端一致用户体验的移动与桌面应用程序
【9月更文挑战第8天】Uno Platform 的社区资源与学习路径推荐旨在为初学者和开发者提供全面指南,涵盖官方文档、GitHub 仓库及社区支持,助您掌握使用 C# 和 XAML 创建跨平台原生 UI 的技能。从官网入门教程到进阶技巧,再到活跃社区如 Discord,本指南带领您逐步深入了解 Uno Platform,并提供实用示例代码,帮助您在 Windows、iOS、Android、macOS、Linux 和 WebAssembly 等平台上高效开发。建议先熟悉 C# 和 XAML 基础,然后实践官方教程,研究 GitHub 示例项目,并积极参与社区讨论,不断提升技能。
145 2
|
4月前
|
C# Android开发 开发者
Uno Platform 高级定制秘籍:深度解析与实践样式和模板应用,助你打造统一且高效的跨平台UI设计
【9月更文挑战第7天】Uno Platform 是一个强大的框架,支持使用 C# 和 XAML 创建跨平台 UI 应用,覆盖 Windows、iOS、Android、macOS 和 WebAssembly。本文介绍 Uno Platform 中样式和模板的应用,助力开发者提升界面一致性与开发效率。样式定义控件外观,如颜色和字体;模板则详细定制控件布局。通过 XAML 定义样式和模板,并可在资源字典中全局应用或嵌套扩展。合理利用样式和模板能简化代码、保持设计一致性和提高维护性,帮助开发者构建美观高效的跨平台应用。
90 1
|
7月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的高校实验室信息化综合管理平台附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的高校实验室信息化综合管理平台附带文章和源代码部署视频讲解等
172 6
|
7月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的企业人才引进服务平台附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的企业人才引进服务平台附带文章和源代码部署视频讲解等
34 1
|
6月前
|
数据安全/隐私保护 Sentinel Windows
5款简洁干净,功能强悍,专注实用的软件
电脑上的各类软件有很多,除了那些常见的大众化软件,还有很多不为人知的小众软件,专注于实用功能,简洁干净、功能强悍。
64 0
|
7月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的博物馆展览与服务一体化平台附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的博物馆展览与服务一体化平台附带文章和源代码部署视频讲解等
72 0
|
8月前
|
JavaScript 前端开发 编译器
TypeScript的编译器、编辑器支持与工具链:构建高效开发环境的秘密武器
【4月更文挑战第23天】TypeScript的强大力量源于其编译器、编辑器支持和工具链,它们打造了高效的开发环境。编译器`tsc`进行类型检查、语法分析和代码转换;编辑器如VS Code提供智能提示、错误检查和格式化;工具链包括Webpack、Rollup等构建工具,Jest、Mocha等测试框架,以及代码质量和性能分析工具。这些组合使用能提升开发效率、保证代码质量和优化项目性能。
|
8月前
|
JavaScript 前端开发 API
使用Python和Vue构建多用户协作平台的终极指南
【4月更文挑战第11天】本指南介绍了如何使用Python和Vue.js构建多用户协作平台。首先确保安装Node.js、Python 3.x、pip和git。使用Flask搭建后端,设计RESTful API实现用户注册、登录等功能。前端利用Vue.js创建组件,结合Vuex和Vue Router处理状态管理和页面路由。通过Axios与后端通信,实现用户交互和数据同步。完成后进行测试,用Docker容器化应用并选择云服务部署。随着需求和技术发展,持续迭代和完善平台。
211 0
|
8月前
|
缓存 编译器 测试技术
简化 CMake 多平台兼容性处理:高效开发的秘诀
简化 CMake 多平台兼容性处理:高效开发的秘诀
279 0