前後端中階作業:預處理器、資料結構、Cache
hw1:CSS 預處理器
自從學會 CSS 預處理器之後,寫 CSS 的複雜度一下子降低了很多,一方面是巢狀 CSS 可以很簡單的實作出來,另一方面是多了變數這個好用的東西。
現在請你把以前寫的 CSS 用你自己挑的 CSS preprocessor(LESS, SASS, Stylus)來改寫(要選哪一個作業都行,隨便選一個就好,只是想讓大家熟悉一下預處理器的用法) 。
—
這次選定之前留言板作業來改。但發現還頗有難度的XD。因為我用了大量的逗號,以及為了權重使用了 nav.navbar
之類的方式。
不過因為留言板已經做了這麼多,所以還是就用這個例子來修改好了,才可以最大化的學習到如何使用預處理。
構想
未來的變動、重複的內容、巢狀結構、結構規劃、BEM 巢狀結構
就之前上課的印象,可以把一些可能會變動的使用變數替代,這樣在調整的時候就可以使用變數
逗號可以使用 @extend
就可以繼承,不過我並沒有要繼承,所以要思考看要保持原狀,還是找找看有沒有更適合的寫法。
突然想到我有一些是寫在好幾個位置,所以可以針對那個 CSS 使用 @extend
繼承那些屬性,所以有另外設置的就不用逗號了,但沒有設置的就要多一點逗號了,而繼承的逗號可不可以更多
當作品集的 notion 規劃完成之後就開始寫作業了。寫一寫做了一些背景色的變數之後,覺得說應該一邊弄一邊測試看看留言板的板型有沒有跑掉才對,所以就把鏡頭 作業轉到留言板的資料夾繼續改造 SCSS 下去。
然後只有修改一些過度強調權重的部份,然後也發現到一些之前沒寫好的部份,所以一併的作修改。
最主要覺得有點卡住是因為我寫了顏色的部份,但是光用變數名稱,可能還有點難寫,因為 xxxxxBackgroundColor
的 BackgroundColor
就佔掉兩個單字。
因為我之前找駝峰命名法的時候,有看到資料有找到說,變數名稱也不宜太長,大概是以三個單字為主就好了,類似於 BEM 的感覺XD。然後是變數命名也不宜縮寫,因為共同作業的時候,很容易導致別人看不懂,所以縮寫成 xxxxxBgc
也一定是不適合的。
所以光是規劃怎麼寫也是需要思考的點。
花了很久時間規劃,因為一邊規劃一邊寫不方便,所以就直接在這邊寫總結。
剛好又發現一個優質網站,可以直接在上面測試 SCSS 轉 CSS,VERY GOOD
改這種東西最怕的就是改壞了,所以有一個地方可以改之前先行測試,會非常方便多的。整體改完之後跟佈署的比起來無違和,VERY GOOD。
總結就是大致上整理了所有的複雜的部份,慢慢觀察之後,把一些可以重複的部份,整理在一起,因為他們差異只有一點點,然後當初分開的原因是因為需要另外的顏色,後來想想用了一些覆蓋的方式就可以了。
所以等於是改變整個結構,讓他可以更短。
不過在使用 CSS preprocessor 的部份,SCSS 的東西做一做就是非常的長,導致轉化出來之後反而比較短。
我在想這應該是犧牲了一些資料來換取更容易讀吧
另一點是為了方便之後改造,所以直接把所有的顏色都另外拉出來用變數來替代,這樣之後變動的時候才會很方便。
然後是突然想到之前找資料,意外看到預處理器相關的,那個人說的順序是先寫預處理器,才寫 CSS 。所以我們反過來寫會這麼難想應該也是有原因的,也許可以透過這樣子去摸更熟該怎麼改造?
成品:
https://gist.github.com/u88803494/4c367b184c40d95b086c425ada85b12b
hw2:實作出 Stack 與 Queue
請你實作出Stack
跟Queue
兩個 Function(或是 Class),讓以下程式碼可以順利執行: (禁止使用內建函式push
與pop
)
var stack = new Stack()
stack.push(10)
stack.push(5)
console.log(stack.pop()) // 5
console.log(stack.pop()) // 10var queue = new Queue()
queue.push(1)
queue.push(2)
console.log(queue.pop()) // 1
console.log(queue.pop()) // 2
—
由於沒什麼想法,所以就直接構想 .push()
怎麼實作。
目前的想法是,.push()
先偵測本來的 array,根據原本的長度+1,創造一個新的 array,然後把原本的放入前幾位,然後把新的資料放進最後一個,接著覆蓋原本的 array 就完成 .push()
的功能。Queue 的部份則是相反。
然後是回憶一下物件導向怎麼寫。
就是 const stack = new Stack();
。
然後是 class 的架構:
class Stack {
constructor(){
建構子
} xxx(){
function xxx 的內容
} ooo(){
function ooo 的內容
}
}const stack = new Stack();stack.xxx(); // 使用 function xxx
stacl.ooo(); // 使用 function ooo
有些注意事項:像是類別的名稱要大寫,特別用粗體標示,可能前面都只是沾沾類別的醬油而已,所以很多細節都忘了。
關於實作的部份,這部份其實有些混亂。也可以發現到自己最近真的很少在那邊寫純 JavaScript 了XD。在寫的時候一直滿腦子 PHP 的語法,尤其是回頭去看自己物件導向的筆記,又 JavaScript 又 PHP,還真的讓人覺得很混亂。
而關於 .push()
的部份,我一直覺得自己好像有寫過,但又沒印象阿?
最後回去看之前的作業才發現,真的沒做過,只是之前有寫過 .join()
、.reverse()
、.splice()
等等。所以這次這樣寫就是回到那時候的感覺。有種懷舊感。
我想會有那種感覺應該是因為我之前在應用 .push()
的時候,就有在思考我是不是應該自己實作一下才可以更熟悉,結果我忘了這回事情,然後在這麼後面的作業就碰到了。莫非有神秘力量就是要讓我學會。
其他的部份就是測試一下 array 要怎麼添加資料?要怎麼刪除資料?等等一邊寫一邊 console.log()
測試,最後寫出結果。就成功了:
class Stack {
constructor() {
this.arr = [];
} push(item) {
this.arr[this.arr.length] = item;
} pop() {
const { length } = this.arr; // 取得長度
const pop = this.arr[length - 1]; // 取出資料
this.arr.length = this.arr.length - 1; // 把最後一個砍掉
return pop; // 回傳取得的最後比資料
// 取得回應相對資料,然後刪除,之後 return
} now() { // 測試用的,之後會刪除
console.log(this.arr);
}
}const stack = new Stack();
stack.push(10);
stack.push(5);
console.log(stack.pop()); // 5
console.log(stack.pop()); // 10
額外新增一個 .now()
的 function,用意只是在測試看看刪除之後的 this.arr
變成什麼樣子。
然後 Queue 的部份應該就是照這樣子改變一下即可。
結果實際做出來跟我想像的差很多,不是只有改變一下,是整個都不一樣了,長度還增加非常多。難怪放在第二個,真的是難了許多。
因為要往第一個添加的話,就必須要另外開一個 arr 了吧。思考了一下要如何添加之後,才確定真的得開另外的新 arr。所以就利用 new Array(this.arr.length + 1)
來達成另外開新 array 的效果。並且把資料存進 index 0 之後在放入迴圈添加。最後直接取代原本的 array,就可以達到我們想要的效果。
.pop()
的部份則是一模一樣,所以就不需要做變更了。
整體如下:
class Queue {
constructor() {
this.arr = [];
} push(item) {
// 添加到第一個,所以要創造新的 arr 長度+1 的 arr 並把資料填入
const newArr = new Array(this.arr.length + 1);
// 創造一個多一長度的 array newArr[0] = item; // 把新增值填入之後,把舊值加入 for (let i = 1; i <= newArr.length - 1; i += 1) {
newArr[i] = this.arr[i - 1]; // 加入舊值
} this.arr = newArr; // 覆蓋原值
} pop() {
const { length } = this.arr; // 取得長度
const pop = this.arr[length - 1]; // 取出資料
this.arr.length = this.arr.length - 1; // 把最後一個砍掉
return pop; // 回傳取得的最後比資料
// 取得回應相對資料,然後刪除,之後 return
}
}const queue = new Queue();
queue.push(1);
queue.push(2);
console.log(queue.pop()); // 1
console.log(queue.pop()); // 2
經過測試之後確實可以按照 Queue 的模式運作。這樣就完成了這個作業。
hw3:HTTP Cache
請閱讀這篇文章:循序漸進理解 HTTP Cache 機制來理解 HTTP Cache 機制。
看了作業裡面好像不需要寫,但我覺得還是寫一下比較好。在這裡學到可以利用過期時間跟過期時間長度來指定 cache 的時間。利用這一點可以確認到什麼時候該重新抓取資料。
也發現到這個世界真的很廣闊,因為除此之外還多了很多種的應用。每一種都看起來相似,卻又有一些些的區別。因為除了過期時間之外,還可以偵測到資料有沒有變動來決定需不需要新的 request,而這些都可以省下許許多多的成本。
到最後是 SPA 的部份。通過這個才知道,原來可以直接使用 .js
檔案附帶 etag 的功能來判斷要不要重新載入頁面。而這樣也可以省下許多的資源,因為頂多只需要額外下載新的 js 檔案。
這讓我不禁佩服起想到這種應用方式的前輩們的厲害之處。
hw4:簡答題
- CSS 預處理器是什麼?我們可以不用它嗎?
- 請舉出任何一個跟 HTTP Cache 有關的 Header 並說明其作用。
- Stack 跟 Queue 的差別是什麼?
- 請去查詢資料並解釋 CSS Selector 的權重是如何計算的(不要複製貼上,請自己思考過一遍再自己寫出來,沒有很完整也行)
—
CSS 預處理器是什麼?我們可以不用它嗎?
CSS 預處理器,就是一種協助我們撰寫 CSS 的工具,他可以像是寫程式一樣的方式,協助我們做一些變數管理,運算,以及繼承等功能。就是一個方便我們撰寫 CSS 的工具,也因為如此,所以沒有 CSS 預處理器也是可以的。我們可以手動直接寫 CSS,只不過就是原生的 CSS,之後在做改動的時候可能會比較麻煩一些而已。
請舉出任何一個跟 HTTP Cache 有關的 Header 並說明其作用。
Cache-Control: max-age=31536000
。Cache-Control
中間有許多種的方法,可以控制是私人的還是公開的。可以控制有效性,以及驗證確認等。
而 max-age
是其中一個方法,可以用來設定要保留多久的秒數。
Stack 跟 Queue 的差別是什麼?
最大的差別就是使用的順序,放入的時後的順序都一樣,差異在取出的順序。Stack 是先進後出,Queue 是先進先出。
請去查詢資料並解釋 CSS Selector 的權重是如何計算的(不要複製貼上,請自己思考過一遍再自己寫出來)
權重有三種層級,除此之外還有兩個權重更高的方式,不過跟原本的方式不太一樣,屬於例外規則。
關於權重的三個層級可以利用 0, 0, 0 來標示。
學習 CSS 的時候,肯定會認識到 HTML 的元素、class、id 等。然後還有直接寫在 html 上面的 inline 寫法。這之中還有一個重點就是權重。
而權重就是指 CSS 的優先權。有幾個特性:
- 權重後寫的可以覆蓋前寫的 CSS
- 當兩個選擇器同時作用在一個元素,權重高的優先生效
- 當重複選擇的時候,選擇數字會被重複計算。
但是這些權重是怎麼計算的呢?
基本的權重大小是:inline style > ID > Class > Element > *
*
是屬於沒有權重的,這在這之間是沒有任何權重的。屬於 0, 0, 0
,任何一個權重都可以蓋過他。
element
元素則是最小的部分。div
、p
、h1
、li
、ol
、footer
、a
… 等。屬於 ### 0, 0, 1
。
class
次小的就是 class 了,屬於 0, 1, 0
。
還有一些部分是屬於 0, 1, 0
偽類、屬性選擇器。偽類 :nth-child()
、 :link
、 :hover
,屬性選擇器 [type:checkbox]
、[attr]
。
id
三者中最大的就是 id 了,屬於 1, 0, 0
。
超越這三者的有 inline style
。 inline style
就是 <div style="xxx">
直接寫在 HTML 裡面的元素上面的。也可以說是 1, 0, 0, 0
。
然後還有一個強制性最高的 !important
直接寫在 CSS 屬性裡面。也可以說是 1, 0, 0, 0, 0
。
參考資料:
Day20:小事之 CSS 權重 (css specificity)
前端雜談: CSS 權重 (Specificity)