iT邦幫忙

2022 iThome 鐵人賽

DAY 15
1
Modern Web

真的好想離開 Vue 3 新手村 feat. CompositionAPI系列 第 15

Day 15: 在 Vue 專案使用 Sass/SCSS +共用變數 (feat. Vite)

  • 分享至 

  • xImage
  •  

Outline

主要分成四個部份

  1. 安裝 Sass 預處理器,在 SFC 檔使用 SCSS 撰寫 style
  2. 區域引入 SCSS 樣式或變數
  3. 全域引入 SCSS 樣式
  4. 全域共用 SCSS 變數
    • @use & @import 踩坑

前言

先說說新手可能會搞不懂的地方(還是只有我自己XD),Vue 提供開發者透過 lang 屬性宣告語言塊 (如:<style>) 所使用的語言,但實際上,專案內所有樣式的預處理跟打包,是由建構工具來執行的,所以,「要怎麼在 Vue 專案引入 .sass/.scss 檔案」這件事,要看建構工具的官方文件。

這也是 Vue 官方文件在 Pre-Processors 提到的:

Note that integration with various pre-processors may differ by toolchain. Check out the respective documentation for examples:

注意对不同预处理器的集成会根据你所使用的工具链而有所不同,具体细节请查看相应的工具链文档来确认:

  • Vite
  • Vue CLI
  • webpack + vue-loader

我目前專案上使用的建構工具是 Vite,以下說明內容都是以 Vite 為主。

使用 Sass 或其他預處理器

vite 內建支持 .scss.sass.less.styl.stylus 等檔案,沒有 vite 特殊的插件,只要安裝一般的預處理器就可以了。

  1. 透過套件管理器(以npm為例)安裝預處理器 Sass
npm add -D sass
  1. 在 Vue <style> 註明使用語言

Vue SFC 檔的 language blocks 可以透過 lang 屬性宣告所使用的語言,所以想在 <style> block 使用 SCSS 風格撰寫,必須這樣寫:

<style lang="scss">
$primary-color: #333;
body {
    color: $primary-color;
    }
</style>

好了,就這樣。

(???)
你可能有的疑惑:「我不用自己手動將 .scss 預處理成 .css 嗎?」

對的,不用,交給 vite 處理!

區域引入 .scss 樣式或變數

區域引入指的是:只要將樣式或變數引入到單一元件檔的 <style> 中使用。

我們可以透過 @use@import,將整份 .scss 檔引入到元件檔中,就可以在 <style> 內使用 Sass 變數、@mixin@extend 等。

<style lang="scss" scoped>
@use "@/assets/scss/_font.scss";
@import "@/assets/scss/_colors.scss";

body {
    color: $primary-color;
    font-size: font.$super-big;
    }
</style>

記得在語言塊加上 scoped 屬性,才能將樣式鎖定在區域元件內。(否則就變成全域共享 CSS 樣式啦,還不清楚 scoped 作用的人,建議先去看昨天的文章-Day 14: style scoped 原理

全域引入 .scss 樣式

想要全域引入 scss 樣式,主要有兩個引入點:

  1. App.vue
  2. main.js

在這裡想要先說明,全域「引入 .scss 樣式」這個說法並不精準,就轉換的執行結果,準確來說是「全域共享這份 .scss 檔所轉換出來的 CSS 樣式」,這也是為什麼要特別區分「樣式」與「Sass 變數」。


  1. App.vue

其實概念和引用到單一元件檔是一樣的,只要不加上 scoped,就可以影響全域的樣式,但因為 App.vue 通常是最整個專案的「根元件」,這裡引入全域樣式是最合理,也更容易管理的。

注意:轉換前的 Sass 變數、@mixin@extend 等等是不能共享的;轉換後的 CSS 樣式,因為沒有 scoped 限制,所以可以影響全域的元件。

<style lang="scss">
@use "@/assets/scss/_font.scss";
@import "@/assets/scss/_colors.scss";

body {
    color: $primary-color;
    font-size: font.$super-big;
    }
</style>
  • 想要全域引用不能加上 scoped
  • 共用的是 css 樣式而不是 scss所以變數@mixin ** 和** @extend 還是不能全域共享

也就是說,實際上共用的是下面這組樣式:

body {
    color: cyan;
    font-size: 48px;
}
  1. main.js

這個方法一樣是利用轉換後的 CSS 樣式,套用到全域,所以轉換前的 Sass 變數等等還是不能共享。

也就是說,只有 Sass 變數的檔案在這裡引入是沒有意義的,元件檔內也無法使用這些變數。

import { createApp } from "vue";
import App from "./App.vue";

import "./assets/main.css

//只有 Sass 變數的檔案在這裡引用沒有意義
import "@/assets/scss/_colors.scss";

//適合 reset.scss 這類型要套用全局的樣式
import "@/assets/scss/_reset.scss";

const app = createApp(App);
app.mount("#app");
  • 共用的是 css 樣式而不是 scss所以變數@mixin ** 和** @extend 還是不能全域共享

全域共用 scss 變數

透過 vite 設定來處理,在 vite.config.js 加入下列這段設定:

css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@import "@/assets/global.scss";`
    }
  }
},

@import "@/assets/global.scss"; 這行會被加到每一份 style 中,等同於在每個 SFC <style> 引入 "@/assets/global.scss",如此一來元件就都能取用 global.scss 裡面 scss 的變數!

但在這裡要注意選擇引用的方式(@use v.s @import)。

引用方式踩坑

稍微說明一下 @use@import 的差異:

  • @use
    • 會做 name-spacing,每次取用變數都需要加上 fileName.variableName ,避免變數衝突的問題
    • 引入需寫在檔案最前面,須先於 @import
  • @import
    • 沒有 name-spacing,兩個檔案如果定義了相同名稱的變數,後面引入的變數值,會直接覆蓋前者
    • 引入需寫在檔案前面,但不得先於 @use

最關鍵的是引入順序,@use 在前,@import 在後,否則會報錯。

css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@use "@/assets/globalColor.scss";`
    }
  }
},

試想,additionalData@import "@/assets/globalColor.scss";這一行字串會寫在檔案的哪裡?

這行字串會被加到每份 <style> 的最前面所以要注意 @use @import 的引用順序衝突

如果選擇在 additionalData 使用:

  • @import 引入:

    SFC 內就不能使用 @use 引入 .scss 檔案
    只要在任何 SFC 內使用 @use 引入 .scss 檔案,就會直接報錯 (錯誤訊息:@use rules must be written before any other rules.),因為 Sass 的規則就是 @use 在前,@import 在後。

  • @use 引入

    注意 name spacing 或改用 * alias

    1. @use 'file':要注意的是,在個別的 SFC 中,雖然看不到引用的檔案名稱,但每次取用變數還是要幫他加上 name spacing!
    2. @use 'file' as * 效果如同 @import,可以巧妙避開引用順序報錯,但缺點如 @import,要注意變數衝突的問題。

不建議用這個方式引入太多 scss 檔案,因為每份 .scss 檔案都會被所有元件樣式重覆引入

  • 適合用來引入會被超多檔案用到的 Sass 變數,否則還是建議在 SFC 個別引入 .scss
  • 全域樣式則建議透過 main.jsApp.vue<style> 引入。

註:vite 官方文件其實沒有特別提到 additionalData 引入的內容會寫在哪裡,我主要是根據測試報錯內容、vitejs github 討論和 webpack sass loader 說明來推測的。


參考資料


上一篇
真的好想離開 Vue 3 新手村 - Day 14: style scoped 原理與特殊選擇器 :deep()&:slotted()
下一篇
Day 16: 從 vuejs 原始碼看 v-on 修飾符串接
系列文
真的好想離開 Vue 3 新手村 feat. CompositionAPI31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言