Vue 3.0.3 : 新增CSS變數傳遞以及最新的Ref提案

語言: CN / TW / HK

前言

在 Vue 3.0.3 版本中,Ref 語法糖已經實現,即使反對的人再多,尤雨溪也堅信這一語法糖會像 TypeScript 一樣,用之前都覺得不好,用過之後說真香

所以不顧大家的反對,毅然決然的實現了這一需求,首先我們先來簡單的回顧一下這個語法糖是幹什麼的:

Ref 語法糖

在之前,我們想要定義一個基本資料型別的響應式變數需要寫成這樣:

import { ref } from 'vue

const a = ref(0)
複製程式碼

當使用時要寫成這樣:

a.value = a.value + 10
// 或 a.value += 10
複製程式碼

這個value屬性就很煩人了,因為咱們定義了一個數字0,給它取了個名字叫a,雖然咱們並不是寫成了這樣:

const a = 0
複製程式碼

但是在好多人的心目中,這個a就像上面這行程式碼一樣,覺得它應該是基礎型別,一方面不符合直覺,經常會忘記寫.value。另一方面寫起來也很麻煩,而且有時候需要寫.value,有時候又不需要,比如用在<template>標籤中時就不需要寫.value,或者把ref賦值給reactive物件時:

import { ref, reactive } from 'vue'

const obj = reactive({
	a: ref(0)
})

// 這裡不需要寫成obj.a.value
console.log(obj.a)
複製程式碼

這無疑為我們增加了許多的心智負擔,尤雨溪希望通過編譯來實現像使用基礎型別一樣使用ref,寫法如下:

// 宣告一個變數(這個變數將會被編譯成一個ref)
ref: count = 1

function inc() {
  // 該變數可以像普通變數那樣使用
  count++
}

// 想要獲取到原本的變數的話需要在變數前面加一個💲符號
console.log($count.value)
複製程式碼

Vue 3.0.3 CHANGELOG

通過 GitHub 中尤雨溪的 CHANGELOG 我們可以得知:

前半段寫的修復的 Bug🐞 先暫且不提,咱們只看新的實驗性特性有哪些:

  • compileScript inline render function mode
  • new script setup implementation
  • new SFC css varaible injection implementation
  • support kebab-case components in <script setup> sfc template
  • explicit expose API

這裡面重點是第二條、第三條以及最後一條:

  • 實現了新的 <script setup>
  • 實現了新的單檔案元件注入 CSS 變數
  • 控制匯出的 expose API

那麼新的<script setup>就包含了這個 Ref 語法糖

CSS 變數注入

那麼這個CSS變數注入又是個什麼鬼呢?

其實有篇文章已經把這個特性說的相當詳細了:

《Vue超好玩的新特性:在CSS中引入JS變數》

總結起來就是:

  1. 以前在 JS 中的變數不能直接和 CSS 變數產生聯絡
  2. 現在可以在 <style> 標籤中將 JS 變數與 CSS 變數進行關聯
  3. 同時還具有響應性,比如改變了 JS 中 this.xxx 的值,同名的 CSS 變數也會隨之改變,檢視隨之進行更新:
<template>
  <h1>Vue</h1>
</template>

<script>
export default {
  data () {
    return {
      opacity: 0
    }
  },
  mounted () {
    setInterval(_ => {
      this.opacity >= 1 && (this.opacity = 0)
      this.opacity += 0.2
    }, 300)
  }
}
</script>

<style vars="{ opacity }">
h1 {
  color: rgb(65, 184, 131);
  opacity: var(--opacity);
}
</style>
複製程式碼

執行結果: 可以看到有了這一特性,我們只需要改變同名的 JS 變數即可,這會讓我們的專案更加的靈活,比如根據使用者輸入的合法顏色值來進行動態換膚等功能。

但這次更新改變了寫法,在<style>標籤中不需要再寫vars="{ xxx }"了,在 CSS 程式碼中也不需要再寫原生的 CSS 變數引用了(var(--xxx)),取而代之的是v-bind()這個函式,注意 ⚠️ 這個函式雖然跟 JS 裡的v-bind很像,但只能用在 CSS 中,寫法如下:

<template>
  <h1> shit! </h1>
</template>

<script>
export default {
  data () {
    return {
      color: 'yellow'
    }
  }
}
</script>

<style>
h1 {
  color: v-bind(color)
}
</style>
複製程式碼

如果是 JS 變數是xxx.xxx這種形式的話,比如這樣:

export default {
  data () {
    return {
      font: {
      	weight: 100
      }
    }
  }
}
複製程式碼

那麼 CSS 不能直接寫成這樣:

h1 {
	font-weight: v-bind(font.weight)
}
複製程式碼

而是需要用一對引號擴起來,類似於字串的那種形式:

h1 {
	font-weight: v-bind('font.weight')
}
複製程式碼

expose API

那麼這又是個什麼鬼呢?

在 Vue 2 時期我們是通過 Options API 來寫程式碼的,也就是說需要匯出一個物件,物件的鍵無非就是那些耳熟能詳的:

export default {
    data () {},
    mounted () {},
    computed: {},
    methods: {},
    // 省略若干 Options ...
}
複製程式碼

而現在新加了一個 expose:

export default {
    data () {},
    mounted () {},
    computed: {},
    methods: {},
    // 省略若干 Options ...
    expose: []
}
複製程式碼

這玩意是幹嘛的呢?首先它的用法和 props 的陣列形式差不多:

export default {
    data () {
    	return {
            x: 1,
            y: 2
        }
    },
    expose: [ 'x' ]
}
複製程式碼

這樣的話在父元件中用 ref 獲取子元件例項時,子元件身上只有x這個變數,y是獲取不到的!

而在 Composition API 中的用法是這樣的:

export default defineComponent((_, { expose }) => {
    expose({
      x: ref(1)
    })
    
    return {
      x: ref(2),
      y: ref(3)
    }
  }
})
複製程式碼

看!咱們這裡有兩個x,你們猜哪個x會被匯出?

答案是如果有兩個匯出的變數的話,以 expose 匯出的為準。

那麼 Composition API 和 Options API 混合用法會是什麼樣的呢:

expotr default defineComponent({
  expose: ['x'],
  data () {
    return {
      x: 1
    }
  },
  setup (_, { expose }) {
    expose({
      y: ref(2)
    })
    
    return {
      y: ref(3),
      z: ref(4)
    }
  }
})
複製程式碼

正確答案是:

  • x = 1
  • y = 2
  • z = undefined

當然這些只在元件外起作用,元件內部還是能獲取到z的,而且y也還是等於 3 的。

結語

Vue 現在已經不是我以前認識的那個 Vue 了,有些功能挺好挺實用,但有些……我都不知該說啥好了,大家是怎麼看待現在已經改的面目全非了的 Vue 呢?

本文首發於公眾號:《前端學不動》

往期精彩文章