關於CSS權重的筆記

關於權重的計算,在W3C的網頁上的介紹是:

A selector’s specificity is calculated as follows

  • count the number of ID selectors in the selector (= a)
  • count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)
  • count the number of type selectors and pseudo-elements in the selector (= c)
  • ignore the universal selector

翻譯一下

  • ID selector的數量為a
  • class, attr, 偽類別為b
  • tag跟偽元素為c
  • 星號直接無視

範例

*               /* a=0 b=0 c=0 -> specificity = 0-0-0 */
LI              /* a=0 b=0 c=1 -> specificity = 0-0-1 */
UL LI           /* a=0 b=0 c=2 -> specificity = 0-0-2 */
UL OL+LI        /* a=0 b=0 c=3 -> specificity = 0-0-3 */
H1 + *[REL=up]  /* a=0 b=1 c=1 -> specificity = 0-1-1 */
UL OL LI.red    /* a=0 b=1 c=3 -> specificity = 0-1-3 */
LI.red.level    /* a=0 b=2 c=1 -> specificity = 0-2-1 */
#x34y           /* a=1 b=0 c=0 -> specificity = 1-0-0 */
#s12:not(FOO)   /* a=1 b=0 c=1 -> specificity = 1-0-1 */

計算結果的比較方式

從a比較到c,且abc各自獨立,無法進位。inline style 跟 !important 則不在計算範圍中。

inline style 跟 !important 的權重

inline style 的權重大於所有樣式表內容。!important 則是在權重計算時給予最優先權,權重越大的 !important 也會覆寫權重小的 !important。也因此,在inline style中加上 !important,也會覆寫在樣式表中加的 !important,例如:

css

.class1 {
  color: #f00 !important;
}

html

<div class="class1" style="color: #0f0 !important>
  我會是綠色
</div>

CSS :is, :where Selector

:is() 跟 :where() 是什麼?

常常在一些情況之下,css selector有可能會需要寫很長,尤其是剛開始在寫sass, scss時,巢狀寫的很開心,結果compile出來的東西有可能會像下面這樣:

.container .class1 article,
.container .class2 article,
.container .class3 article,
.container .class4 article {
  color: #f00;
}

而 :is() 跟 :where() 就是用來解決這個問題。在is:() or :where() 中可以帶入逗點分格的selector,讓原本多行且重複的結構簡化為一行:

.container :is(.class1, .class2, .class3, .class4) article {
  color: #f00;
}

/* or */

.container :where(.class1, .class2, .class3, .class4) article {
  color: #f00;
}

:is() 跟 :where() 的不同

基本上是完全一樣,唯一的差異是 :where() 的權重是0,因此可以被輕易複寫。

Nuxt asyncData與fetch的不同

首先是this,fetch 的 this 指的就是component本身,asyncData 的this是undefined…

fetch可以被component本身用 this.$fetch() 再次呼叫,而asyncData不行

顯示的效果也有差。server-side render感覺不出來,不過在client side render時,就有明顯差異。使用asyncData的話,會先取資料,取完資料才換頁。使用fetch的話,取資料前就會先換頁,等到資料取完後,才會填入資料。所以使用fetch的話,體驗會比較像Facebook App在loadiing那樣,勢必要處理當頁面沒有任何東西時,要顯示的替代內容。例如替代標題或內文的灰色色條,或是loading中的icon。

簡單來說,我覺得要做得比較像app的話,應該是用fetch。如果要像一般傳統頁面的感覺,也不需要重複fetch資料的情況,用asyncData應該就可以了。

Nuxt Server-Middleware

前陣子需要利用server middleware做fb oauth功能,於是我就在middleware資料夾新增了fbauth.js,內容:

export default async function (req, res, next) {
  var querystring = require('querystring')
  var axios = require('axios')

  var query = querystring.parse(req._parsedOriginalUrl.query)
  var appId = query.appId
  var redirectUri = query.redirectUri.replace('#_=_', '')
  var code = query.code

  if (!appId || !redirectUri || !code) {
    // response fail
    res.writeHead(404, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify({ msg: 'Login Failed' }))
    return false
  }

  try {
    // get access token
    var clientSecret = 'xxxxx'
    var url = `https://graph.facebook.com/v6.0/oauth/access_token?client_id=${appId}&redirect_uri=${redirectUri}&client_secret=${clientSecret}&code=${code}`
    var result = await axios.get(url)
    var token = result.data.access_token

    // get userid
    var useridUrl = `https://graph.facebook.com/me?&access_token=${token}`
    var uinfo = await axios.get(useridUrl)
    var uid = uinfo.data.id

    // response success
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify({ token: token, uid: uid }))
  } catch (e) {
    // response fail
    res.writeHead(404, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify({ msg: 'Auth Failed' }))
  }
}

結果build完之後,發現clientSecret竟然也一起打包進client端的js裡!!

於是我查了關於如何不將code打包進client side的資料後,就在外面包了一層 if (process.env.VUE_ENV === ‘server’) 如下:

export default async function (req, res, next) {
  if (process.env.VUE_ENV === 'server') {
    oauth的code...
  }
}

問題就解決了!但總覺得不是很合理,明明叫server-middleware但卻會被build進client-side用的js。

直到最近重翻doc才發現,當初secret key會被打包進去的原因是,因為當初我覺得反正都是middleware,就一起放middleware資料夾。但當我將server middleware分別放入新建的server-middleware資料夾後,發現原本發生的問題都不存在了…恩…總之就是我沒仔細看文件啦…orz

因此可以推定,middleware資料夾內的所有檔案,跟mixin資料夾內的所有檔案一樣,都會被打包進app.xxxx.js內,而不是有需要的時候才load。雖然這樣看起來是很合理,但隨著網站越長越大,很多共用的東西要拉出來的時候,就會累積越來越多非頁面關鍵需求的東西,這點似乎有點麻煩…