前言
sass是开发中常用的预处理器,帮助开发者可以简单的在css中实现逻辑操作,提高代码的可读性和复用性。
业务中经常看到@import/@use/@forward
混用,尽管官方明确表示逐步废弃@import
,但是业务中任然大量出现,我觉得有必要将 @import
out了。
sass的基本语法都比较常见,本文主要对sass中经典的三种导入方法进行区分。
@import
我们知道@import
是一个逐渐被官方弃用的语法,主要有如下原因:
- 定义的内容全局可见
- 不好追溯来源
- 重复输出
这三点缺点用sass的应该都听过一些(懂了又似乎没完全懂)。逐步解释下:
什么是全局可见??
我们三个样式表 a
,b
,c
,在a
中声明了一个变量和一个实体样式,然后我们需要编写b
和c
样式表,需要用到a
中的样式和变量;
最后我们在入口处index
中引入module b
和c
,此时会发现:为什么在index
中能使用$red
??
事实上我们的初衷是在b
和c
中导入使用,而不是暴露给上游。
这就是全局可见,因为我们不可避免的会多个模块之间有依赖关系,那么每次@import
一次,都会暴露给下游所有的内容-即全局。
不好追溯来源
其实问题一就是问题二的主要原因:当前能使用的内容大于我们显示导入的内容。我们只显示导入了b
,c
,但是能使用的内容却是a
,b
,c
;
重复输出
@import
实际上是每次导入一个模块,就会向当前文件编译输出一次。
可以看到上图输出结果中包含了两次.text-red
的样式,这是没有必要的,不仅会增加输出的包体积,还会影响编译速度增加编译时间
@use
@use
解决了以上问题
- 为什么说解决了全局可见的问题?
从定义上来看,这是sass的模块化实现方法,可以类比esm
中的import
;如果你在b
和c
中需要使用a
,那就import a from 'a'
,注意,此处只是使用,却没有向外暴露,所以依赖b
和c
的样式表是不能访问a的内容的。这也就是说为什么@use
可以解决全局可见的问题,准确来说是模块化的正确实现。
- 由于要使用就得显示
@use
,也就来源可溯了 @use
导入的文件,无论导入多少次,结果中只会输出一次
@use
主要是针对性的解决了@import
的问题
@forward
@forwrad
主要是和@use
一起使用,作用是模块转发
模块转发??
其实意思就是向外暴露模块,类比esm
中的export
,举个例子:
css
/* a.scss */
$red: red;
.text-red {
color: $red;
}
/*b.scss */
@forward './a.scss';//类比js export * from './a'
$b: green;
/* c.scss */
@forward './a.scss';//类比js export * from './a'
$c: yellow;
/*index.scss */
@use './c.scss' as *;//类比js import * from 'c'
@use './b.scss' as *;//类比js import * from 'b'
.idx {
color: $b;
color: $c;
color: $red;//这里就可以使用a的变量啦
}
我们之前说过@use
就是使用,是不对外暴露模块的,也就是说,在index
中不能访问a
的内容,如果需要访问,那就通过@forward
暴露
值得一提的是,@forward
也不会产生重复导入的问题
项目中的使用(以vite为例)
既然@import
不好,那我们就直接弃用吧(就好像esm
逐步淘汰cjs
一样)
先下个结论:@use
和@forward
完全可以替换掉@import
!
项目中一般有一个静态变量的定义文件,比如variables/mixin/function.scss
这些是需要全局都能访问的
typescript
//variables.scss
$jm-text-color-primary: #303133 !default;
$jm-text-color-regular: #606266 !default;
$jm-text-color-secondary: #909399 !default;
$jm-text-color-placeholder: #c0c4cc !default;
// vite.config
css: {
preprocessorOptions: {
//全局css
scss: {
additionalData: `
@use "@/styles/theme/${env.VITE_APP_BASE_THEME}.scss" as *;
@use "@/styles/variables.scss" as *;
`
}
}
},
vite config中有一个配置项:additionalData
,在这可以注入全局的变量
注意:这里是'注入',additionalData 的本质是在每一个样式文件首行都注入我们的内容,类似于
sassFileSource = additionalData+ sassFileSource
这也就是为什么可以全局访问变量的原因 所以不要在这里加实体样式!,否则会在所有的样式文件中都会多出注入的实体样式,这是没有意义的
定义一个分离的样式:
typescript
@forward './c.scss';
@forward './b.scss';
@use './c.scss' as *;
@use './b.scss' as *;
.idx {
color: $b;
color: $c;
color: $red;
}
在vue模版中导入样式:
vue
<style lang="scss" scoped>
@use './index.scss';
</style>
总结
@import
因为重复输出,全局污染等问题,可以直接弃用。
@use
和@forward
配合使用,@use
负责导入使用,@forward
负责向外暴露转发模块