Decorative image frame

【基礎教學】 TailwindCSS 逐步上手

正好想學,在研究之餘,順便寫個教學留個紀錄。

Tailwind 安裝流程 (使用 npm)

依照 Tailwind 的官網,安裝有兩種方式:

  • 使用 CDN
  • 使用 npm

而今天主要是以 npm 的方式來展示。

因為 Tailwind 若是靠 CDN 的方式,會導致很多強大的功能沒有全開,我們這邊就簡單舉例幾個:

  • 基本的,就是無法 新增自訂顏色支援深色模式
  • 再進階一點,就是無法使用 `@apply` 指令將語法整理起來。
  • 最嚴重的,就是 無法壓縮檔案,CDN 版大小約 2.79MB。 (完整版約 3.7MB~4MB)

所以如果只是為了 玩玩、體驗 Tailwind 的特性及帶來的方便性,完全可以使用 CDN 的方式就好,但如果是想要做公司產品/成品上線,這個沒壓縮的大小很有可能讓你被火了XDDD

前置作業 - 安裝 node.js

首先,如果你會用 linux 類系統,這個你肯定用過。
不過新手們的話就別跳過,還是都跑一遍吧,以免你有指令沒跑到或東西沒裝到。

1
sudo apt update

接著,這個很重要,要安裝 curl。 因為等一下就要靠他了。

1
sudo apt-get install curl

再來,使用到剛剛安裝的 curl,把 node.js 12版的安裝腳本下載下來。請注意,這邊一定要抓12版或更新的!

1
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -

這下,就可以放心的把 node.js 安裝下去了~

1
sudo apt-get install nodejs

安裝 Tailwind

注意:Tailwind CSS 需要 Node.js 版本在 12.13.0 以上

  1. 首先,因為 Thailwind 有 node.js 的版本限制,可以先執行以下的指令檢查 node.js 的版本。
1
node --version
  1. 如果檢查完,確定 node 是 12 版以上,那就可以放心地執行下面這行指令了。
1
npm init

在打入 NPM init 後,會被要求輸入幾個欄位

package name: 你這個 Project 要叫什麼名字
version: 你決定這個 Project 現在該是第幾版
description: Project 基本介紹
entry point: 進入點,如果要跑你的 Project 應該要執行哪個檔案
author: 作者(自己)
license: 你這個 Project 是採用什麼授權的
test command: 這個不太重要,待會會說明

基本上結束後,你可以看到這個資料夾底下,新增了一個 Package.json

1
2
3
4
5
6
7
8
9
10
11
{
"name": "my-project",
"version": "1.0.0",
"description": "this is my project",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Kimi",
"license": "ISC"
}

基本上使用 NPM 來創建的 Project,他會連一些專案的資訊都幫你做管理。

Optional Info
如果你覺得上面要一直輸入很冗,可以使用 npm init -y
他跟 npm init 幾乎一樣,只是它會幫你把預設選項全部跳過,產生一個空白的 package.json (懶人專用)

  1. 接著, 依照官方教學,需安裝 Tailwind CSS、PostCSS,還有 autoprefixer 這三個檔案;因為 Tailwind 不會自動加上瀏覽器前缀詞到產生的 CSS 中,所以瀏覽器無法讀取 CSS,要加上 autoprefixer 這個套件去處理此問題。
    我們使用一行指令就三個安裝好了,輕鬆愉快。
1
npm install tailwindcss@latest postcss@latest autoprefixer@latest
  1. 再來,為了以防萬一,我們執行一次 `npm install` 來更新和安裝其他依賴套件的狀態。
1
npm install

那這樣就算安裝完了,只是距離使用,中間還有一些東西需要設定。
如果你還會透過 postCSS 安裝其他插件、或是像官方的推薦使用 autoprefixer的話,可以看一下下方的 **安裝 PostCSS 套件。

Optional Info

如果在安裝過程中,有遇到以下錯誤,那可能是 將 Tailwind 與舊版本的 PostCSS 混合使用 所導致的結果, v2.0 版本的 Tailwind 依賴於 PostCSS 8。

1
Error: PostCSS plugin tailwindcss requires PostCSS 8.

這時請安裝 PostCSS 7 版本,才能兼容。


安裝 PostCSS 7

1
2
npm uninstall tailwindcss
npm install tailwindcss@npm:@tailwindcss/postcss7-compat

安裝 PostCSS 套件

1
npm install tailwindcss autoprefixer postcss postcss-cli

上面這個指令,就是幫你的專案透過 npm 安裝 tailwindcss、postcss、autoprefixer,及 postcss-cli 這四個東西。
那如果有看過官方教學,對前三個東西應該不陌生,只是最後一個也是必須的,算是幫官方安裝過程的補充。

在前面的步驟產生完 Tailwind 設定檔之後,我們要來產生 PostCSS 的設定檔,不然 PostCSS 不會幫我們載入 tailwind 和 autoprefixer。

  • Windows用戶
1
在專案目錄下新增檔案 -> 檔名為 "postcss.config.js"
  • Linux、Ubuntu、MacOS 類用戶,直接下指令即可( 在專案目錄下)
1
touch postcss.config.js

新增完 `postcss.config.js`,我們要在檔案中加入下面的內容:

1
2
3
4
5
6
7
// postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
]
}

或是用官方給的格式也可以~

1
2
3
4
5
6
7
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}

最後的最後,只要把前面設定執行的地方,改成

1
2
3
4
scripts:[
/* ... 前面是你其它的設定值,略 .... */
"{ 你的執行名稱 }": "postcss {你 css 檔擺放的路徑}/{要寫樣式的css檔名稱} -o {你 css 檔擺放的路徑}/{頁面要掛載的css檔名稱}"
],

剩下的步驟,都跟上面 tailwind 的教學一樣囉~

設定 Tailwind

  1. 依照上方的流程,安裝完 Tailwind 應該會看到 package.json 中有新增後來安裝的 Tailwind CSS、PostCSS 和 autoprefixer。
1
2
3
4
5
6
7
8
// package.json
{
"dependencies": {
"autoprefixer": "^10.2.6",
"postcss": "^8.3.0",
"tailwindcss": "^2.1.4"
}
}
  1. 在使用 Tailwind CSS 之前,我們得產生 tailwind 必要的設定檔,這也是要達到 tailwind 客製化前最重要的一步。
1
npx tailwindcss init

執行上方的指令後,在專案目錄下應該會多出一個叫做 `tailwind.config.js` 的檔案。

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};

這麼一來,就可以自訂專屬於你的 Tailwind 設定了!

新增 Tailwind 到 CSS 中

現在需要在你存放 CSS 樣式檔的資料夾裡,建立兩個 CSS 檔:

  • 一個是你頁面要掛載的 style.css (名稱可自訂)
  • 一個是用來寫 CSS 樣式的 tailwind.css (名稱也可自訂)

這樣講可能有點抽象,拿我的專案目錄來舉例好了。

在我的專案下,擺放 CSS 檔的路徑是 /public/css ,那就在這個資料夾底下產生兩個 CSS檔,名稱自訂,我這邊是把要掛載的叫做 app.css,要寫樣式內容的叫做 tailwind.css

建立完之後,我們在要用來寫樣式的 CSS 檔 (我這邊叫做 tailwind.css )裡面貼上下面的 CSS 語法。

1
2
3
4
5
/* 貼在用來寫樣式的css檔,範例這邊是 tailwind.css */

@tailwind base;
@tailwind components;
@tailwind utilities;

我們要掛載的 CSS 檔 (這邊叫做 app.css) 保持空白就好,以後所有要補充的樣式都寫在另一個 CSS 檔(tailwind.css)裡。
因為如果接下來 進行編譯, 要掛載的 CSS 檔 (app.css) 的 內容會被覆蓋掉

編譯

終於進入最後一步了。
開啟剛剛 `npm init` 執行後產生的 package.json,在 scripts 裡面加一行:

1
2
3
4
scripts:[
/* ... 前面是你其它的設定值,略 .... */
"{ 你的執行名稱 }": "tailwind build {你 css 檔擺放的路徑}/{要寫樣式的css檔名稱} -o {你 css 檔擺放的路徑}/{頁面要掛載的css檔名稱}"
],

如果把上述的名稱都取代完之後,在我這邊的範例,會長的像下面這樣:

1
2
3
4
scripts:[
/* ... 前面是你其它的設定值,略 .... */
"build-css": "tailwind build public/css/tailwind.css -o public/css/app.css"
],

最後,我們只要執行 npm run {你的執行名稱},那他就會開始進行編譯了!

1
npm run build-css

編譯完成後,應該會在命令列 (終端機) 看到下面的訊息:

這樣就算編譯完成了,現在只要你在頁面掛上要掛載的 CSS 檔,並用上 Tailwind 語法,就會有效果囉!

使用 Tailwind CSS

在你網頁中插入的 CSS 檔路徑要改成剛剛上面說過的,用來掛載在頁面上的 CSS 檔。

1
2
3
4
5
6
7
8
9
10
<!-- 你專案中要使用 Tailwind CSS 的頁面 -->
<html>
<head>
<!-- 前略 -->
<link rel="stylesheet" href="./{你CSS檔的路徑}/{要掛載在頁面的CSS檔名稱}">
</head>
<body>
<!-- 略 -->
</body>
</html>

底下這邊是我依照我專案所改寫後的。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 這範例是我專案中的 index.html -->
<html>
<head>
<!-- 前略 -->
<link rel="stylesheet" href="./css/app.css">
</head>
<body>
<!-- 馬上用 Tailwind 寫一個熱騰騰的黃綠藍三色漸層 Hello World 方塊 -->
<div class="m-3 p-10 bg-gradient-to-r from-yellow-400 via-green-400 to-blue-400 text-white">
Hello World ! I'm using Tailwind CSS Now ~
</div>
</body>
</html>

這樣就把 Tailwind CSS 安裝完了,這樣你就可以盡情地使用 Tailwind 寫出一堆美美的介面了啦!

Optional:幫 CSS 瘦身

Tailwind 因為是 Utility 的關係,會把整包檔案匯入,如果整包檔案要選染於網頁,效能就會低落。

Tailwind 官方文件有提供可以瘦身的方法:

  1. 選到 tailwind.config.js 檔案,將我可能會使用到的內容寫在 purge 屬性內
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//tailwind.config.js

module.exports = {
purge: {
enabled: true,
content: ["./**/*.html"],
},
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};

  1. 這樣程式執行時,就會去監聽我哪支檔案有使用到,只會編譯使用的檔案,真的很方便!

JS 宣告變數: var/ let / const 差異

如果你像我一樣開始學 JS,多少會有個疑惑:學習時是用 let / const 宣告變數,怎麼網路上查詢的 JS 程式碼,有些是用 var 宣告變數的呢?它們之間有什麼差異?
因好奇驅使,於是誕生這篇談 「 var 」 與 「 let / const 」 幾項差異的文,大綱如下:

  • ES6 後,從 var 到 let / const
  • var 是函式作用域,let / const 是區塊作用域
  • var 與 let ,for 迴圈的綁定(bind)差異
  • var 的提升與 let / const 不同
  • var 允許重複宣告,let / const 會出錯

p.s. 第三部分 for loop 是我覺得最有趣且難解釋的,推薦閱讀&討論 !



ES6 後,從 var 到 let / const

為何現在 (2019) 學習的是 let / const ,卻在網上看到 var 宣告變數 ?

因為目前 JavaScript 主流是 ES6 版本,在這個版本中,是推薦使用 let 和 const 宣告變數,而在網上查詢的資料,很可能是 ES6 版本前撰寫的程式碼,那時候宣告變數的方式還是用 var 語法。

總言之,在 ES6 版本後,會推薦用 let 與 const ,而非 var 宣告變數(原因本文會有說明)。

而 var 與 let / const ,主要有幾項差異:

  • 作用域 (scope) 不同
  • for 迴圈的綁定 (bind) 差異
  • 變量提升 (hoisting) 不同
  • 重複宣告的差異

這些改變的主要好處是: 讓 JS 變數的操作更加嚴謹,減少不直覺、出錯或重複宣告的可能性,藉此讓多人或個人大型開發更順利。接下來將一一討論及舉例,文中是我理解後的解釋脈絡,如有誤,歡迎不吝指正。


var 是函式作用域,let / const 是區塊作用域

在 ES6 前,並沒有區塊作用域(block scope)的概念,僅有全域(global scope)與函式作用域(function scope),所以 var 宣告的變數,具有函式作用域(function scope)的性質,代表切分最小單位的有效範圍是 function。

直到 ES6 後,新增區塊切分的概念, let / const 宣告的變數,才具有區塊作用域(block scope)的性質,切分最小單位的有效範圍是 blcok。

而所謂的作用域就是 「變數有效的作用範圍」,最大為全域作用範圍,意指變數有效範圍是全部範圍。而上文所稱的區塊是指 { }大括號範圍 。

綜合整理:

  • var 具有「函式作用域」,亦即在函式中宣告的變數,有效作用範圍會被限制在該函式中。但不具有區塊作用域,所以在區塊中宣告的變數,依然會作用到區塊之外,並不會被區塊限制住。
  • let / const 具有「區塊作用域」,亦即在區塊中宣告的變數,有效作用範圍會被限制在該區塊中。

看例子更好懂,在區塊 { } 中,用 var / let,宣告柯基犬的名字為吐司來看看結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// 「var」 不受區塊限制,區塊外變數存取成功。///

{
var corgiDogName = '吐司';
}

console.log(corgiDogName);
// 吐司

///「let」會受區塊限制,區塊外變數存取失敗。///
{
let corgiDogName = '吐司';
}

console.log(corgiDogName);
// ReferenceError: corgiName is not defined

雖然 var 不受區塊限制,但會受到函式範圍限制,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// 「var」 受函式限制,函式外變數存取失敗。///

function callCorgi() {
var corgiDogName = '吐司';
}

console.log(corgiDogName);
// ReferenceError: corgiDogName is not defined

/// 即使用 var 宣告相同的變數名稱 dogName,但因為「 函式作用域 」,所以不會讓同名變數衝突。 ///

// 將柯基犬命名為吐司,之後用 callCargi() 呼叫
function callCargi() {
var dogName = '吐司';
console.log(dogName);
}

// 將米克斯犬命名為厚片,之後用 callMix() 呼叫
function callMix() {
var dogName = '厚片';
console.log(dogName);
}

callCargi(); // 吐司
callMix(); // 厚片

由上述範例,可以看出限定作用域範圍有兩個好處:

  • 避免同名變數的衝突。
  • 能維持最小權限的原則,以避免變數資料被不當存取。

而過往用 var 宣告,僅有 function 函式範圍有這樣的好處,而如今用 let / const 宣告,就能讓上述好處發生在 if、for 等「 區塊作用域 」當中,藉此降低多人協作或大型專案的衝突情況。



var 與 let ,for 迴圈的綁定(bind)差異

讓我們用一個經典案例作為第二部分的開場。

1
2
3
4
5
6
7
/// 用 for 迴圈執行五次,每隔 0.1 秒會印出 i ///

for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}

你覺得上述的程式執行的結果為何?

(A) 0 、 1 、 2 (B) 3 、 3 、 3

是不是 A 啊 ~ No ~ No ~ No ~ 其實是 B 哦。


我想很多沒學過 var 的人,包括我,都會直覺性的選擇 A ,因為如果我們學的是 let ,而將 var 替換成 let ,答案就會是 A ,這不是更符合 for 的效用嗎!!! 可惜事與願違,真正執行的結果會是 B。

上述預期結果應該是 0 1 2 ,卻莫名變成 3 3 3,正與宣告變數語法 var / let 有關。究竟怎麼了?

要解釋這個問題,要分成兩點討論:

  1. setTimeout() 的時間延遲,與 function 中 console.log(i) 的「 執行時間點 」。
  2. function 中 console.log(i) 的「 值怎麼來 」的。

首先來看第一點,當進入 for 迴圈時,宣告變數 var i = 0 ,並開始條件判斷,當 i < 3 時, i + 1,執行完後,接著需要等待 0.1 秒的時間,才會進行 setTimeout() 內的 function() { console.log( i ) ; }

而 JavaScript 是「異步/非同步」語言,因此在等待執行 function() { console.log( i ) ; } 前的這 0.1 秒內,會先執行完已經能執行的 for 迴圈。

所以現在能理解第一點的結論: function 中,console.log( i ) 的執行時間點會在 for 迴圈執行完畢之後。

1
2
3
4
5
6
7
8
9
10
///因為非同步與延遲時間,所以會先執行完 for 迴圈後,才執行 function。///

for (var i = 0; i < 3; i++) {
// for 迴圈正在 murmur : 還要等你 setTimeout 0.1 秒,不如我先做一做,不要浪費時間。
setTimeout(function () {
console.log(i);
}, 100);
}
// 3 3 3

澄清一下,目前為止所提的第一點,其實與 var 和 let 的差異是沒關係的,只是說明非同步和時間點的狀況。接著進入到第二點,console.log(i) 的值是怎麼來的,這就與 var 和 let 的差異有關了。

var 是函式作用域,這段 for 迴圈程式碼外沒有任何包覆,所以 var 所宣告的 i 會存在於全域 window (瀏覽器) 當中,且只會被綁定(bind)一次,也可以說是共用一個 instance。

加上 for 迴圈會先跑完,才 console.log( i ),所以最後 console.log( i ) 的值會才會是 3。

至於 let 則是「 區塊作用域 」每次 i 都會被紀錄在創造出來的區域中,更精確地說,是每次迭代都會建立一個新的環境(context),而這個環境會紀錄當下的變數 i 值,不會覆蓋掉上一個環境裡面的變數值,因此可以產生多個 i 值。。

我有做張示意圖,便於理解「整體概念」,但細節上可能有出入。

讓們適時地整理一下,在 for 迴圈中,

  • var 的情況只會綁定一次,而且不具區塊作用域,最終會只有一個值存在於全域中(以此例而言),也可以說只有一個 instance。
  • let 的情況會發生重複綁定,而且具有區塊作用域,或者說多個紀錄變數的環境,最終會有多的值存在於 for 迴圈區塊中,也可以說會有多個 instance。

就實作而言,雖然僅有 var 的情況下也可以用「立即呼叫執行函式 IIFE」處理這樣的狀況,但較為複雜且不直覺,改用 let 後,就能簡單處理,且能更直覺地知道 for 迴圈結果啦!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
///將 var i 改為 let i 後,輕易解決問題。///

for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}
// 0 1 2

///不改 var i 的情況下,以 IIFE 相對複雜且不直覺。///

for (var i = 0; i < 3; i++) {
(function (x) {
setTimeout(function () {
console.log(x);
}, 100 * x);
})(i);
}
// 0 1 2

呼,接下來進入倒數第二 part,剩下的 part 字數較少 , 你就快看完了!



var 的提升與 let / const 不同

讓我們再次以一個問題開始 :

1
2
console.log(i);
var i = 5;

你覺得上述的程式執行的結果為何?

  • (A) 5
  • (B) ReferenceError
  • (C) undefined

可以想一下 XD

如果你跟我一樣比較少使用 var ,常使用 let 的話,應該會認為是(B) ReferenceError。當然,不會那麼容易,印出來的結果其實是 (C) undefined。

非常不直覺啊…,因為 undefined 代表找不到值,已經被宣告,但尚未賦值,而 ReferenceError 則表示根本找不到 i ,尚未宣告。所以一般而言,會預期上述程式碼結果應該為「尚未宣告變數的ReferenceError」。

但以結果來說,卻是印出 undefined ,即是代表:雖然我們看不見,但其實在 console.log(i) 之前,i 就已經被宣告了,只是尚未賦值。

是不是頗違反直覺,因為 console.log(i) 前,根本沒有程式碼…。上述狀況,我們可以想像成下面這段程式碼來表示:

1
2
3
4
5
6
7
8
9
10
///由於 var 直接變量提升,所以上面程式碼等同下面程式碼///

console.log(i);
var i = 5;
//undefined

var i;
console.log(i);
i = 5;
//undefined

這其中是因為 var 有著「提升(hoisting)」的特性,其實不僅是 var ,function 也有這個特性。以 var 宣告變數而言,「 變數提升 」簡而言之是: 在執行任何程式碼前,會把變數放進記憶體中,這樣的特點是,可以在程式碼宣告該變數之前使用它。

1
2
3
4
5
6
7
8
/// 因為 hoisting,所以可以在宣告變數前,就預先使用變數,所以可以寫完後一起宣告///

i = 2;
n = 3;
console.log(i + n);
var i;
var n;
// 5

在這樣的情況下,只需要有賦值就不會出錯,即使尚未宣告變數也不會出錯。

這樣會造成什麼問題?當養成了「 後宣告 」的習慣,如果最後忘了用 var 宣告呢?並不會出錯,只是變數會跑到全域中,變成全域變數,可能造成些 bug,像是:

1
2
3
4
5
6
7
8
9
10
11
12
/// 函式中沒有用 var 宣告,導致污染到全域 ///

var x = 1;

function addFunc(y) {
x = 100;
x = x + y;
}

addFunc(50);
console.log(x);
// 150,預期應該要是 1,但函式中的 x 跑出來了

而在 let 中呢,相對的 hoisting 是較安全的( let 還是有 hoisting,只是情況不同,可以搜尋關鍵字 TDZ ),所以 習慣用 let 的開發者,通常會先宣告變數,而不會習慣先運算變數後宣告的情況,藉此降低開發出錯的機率 。

1
2
3
4
5
6
7
8
9
10
/// 先使用變數,後宣告會直接「出錯」,更嚴謹的養成先宣告,後使用///

console.log(i);
let i = 5;
//ReferenceError

i = 5;
console.log(i);
let i;
//ReferenceError

這樣的使用方式除了降低錯誤外,也相對直覺。



var 允許重複宣告,let / const 會出錯

最後再提一個能幫助防止開發錯誤或衝突的小地方,就是用 var 可以重複宣告同樣名稱的變數,而 let / const 如果重複宣告同名變數會出錯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var myDogName = '吐司';
var myDogName = '厚片';
console.log(myDogName);
// 厚片

let myDogName = '吐司';
let myDogName = '厚片';
console.log(myDogName);
// SyntaxError: Identifier 'myDogName' has already been declared

const myDogName = '吐司';
const myDogName = '厚片';
console.log(myDogName);
// SyntaxError: Identifier 'myDogName' has already been declared

這點對初學者或多人學做而言更能防止錯誤、容易尋找到錯誤所在。



var 與 let / const 差異總結

啊啊不知不覺寫了頗長,但整理、學習的過程又讓再次了解、複習迴圈的細部運作與變量提升等等,算是蠻好玩的 !

內容比較多,在此三點總結:

  • var 屬於函式作用域,let/const 屬於區域作用域,後者能避免更多情況下的同名變數與提取變數衝突、區塊內變數污染到全域的情況,且讓 for loop 使用更直覺方便。
  • var 會自動提升變數,let/const 則較為嚴謹,後者能避免忘記宣告變數或因無宣告讓變數污染到全域的情況。
  • var 能重複宣告同名變數,let/const 不能重複宣告同名變數,後者能避免些開發上的錯誤情況。

綜合來說就是 let/const 將宣告變數變得更加嚴謹,藉此增加易讀性、防止出錯,而最重要的 CTA ,我想就是:
在 JS 中,不要再用 var 請都使用 let / const 宣告變數吧!

【心得分享】寫在活動之後

身為活動的 PO,很高興能夠邀請到硬體業 PM Eva 和軟體業 PM Rafeni 來和大家分享他們的經歷。

活動 :PM 相談室《新手篇》

前情提要

筆者隸屬於 PM361 社群,近期於 2021/09 月時舉辦了「PM 相談室《新手篇》」活動,主要的 TA 為那些想要轉職 PM 或剛入行的新手 PM 們。
我們希望能夠透過有經驗者的分享,來幫助他們解決一些常見的疑惑、瞭解 PM 的工作職務,因而對 PM 的認知有個基礎的架構。


因為在活動中的身份不太一樣,此篇文章主要會以「籌劃者」的角度來回顧活動,而非分享活動心得體驗,大綱可分為以下幾個部分:

  • 活動的初衷
  • 主題挑選與聚焦
  • 活動檢討與回饋

活動的初衷

說到 PM 361 社群 ,社團成立的衷旨為「期許 PM 們擁有 360 ° 的全方位視野,加上 1° 自己的觀點」,讓同為 PM 的這群同溫層有個平台可以互相交流、成長。而在我加入社群後,也一直為著這個目標努力著,和成員們除了透過每月舉辦的活動,分享 PM 相關知識與議題,也不斷在關注著社團內的需求與動向。

在分析社團成員的組成與面向後,我們發現主要的 TA 大多為:

  • 對 PM 有興趣、欲轉職為 PM 者(本身非 PM 或為新鮮人)
  • 0 - 3 年的新手 PM

其分別的痛點大致如下:

對 PM 有興趣、欲轉職為 PM 者

  • 不清楚 PM 的工作職能
  • 找不到 PM 前輩可以請教
  • 不知道自己是否適合、能否勝任這個職位
  • 不知道特定產業的 PM 差別
  • 有薪資上的需求(高薪)

0 - 3 年的新手 PM

  • 對產品策略與規劃不熟悉,沒有好的思考框架
  • 溝通問題(向上管理、跨部門協作)
  • 無系統化學習(學習管道、專業技能)
  • 不知道如何提升工作效率(工具、方法模型的使用)
  • 職涯規劃(履歷、面試技巧)

為了滿足上述的需求,才規劃了此次的活動。


主題挑選與聚焦

在確定了活動的 TA 與大方向後,我首先針對「產業別」來挑選適合的講師,最終鎖定「硬體業」和「軟體業」。
除了希望能對比做出差異化之外,這兩個也算是市場普遍的兩大產業。(當然未來有機會的話,也可以瞄準電商、醫療等領域)

起初設定的分享內容會相對深一點,適合有一些基礎的對象參與,不過在瀏覽報名者的回饋與疑惑後,發現普遍都對 PM 沒概念,才調整以「知識普及」、「觀念建立」、「框架分享」為導向,列出幾個關鍵標籤:

  • 轉職/求職
  • PM 工作職能介紹
  • 溝通與協作

也就是大家現在看到的版本,而過程中也和講師們協調過多次,最終才定稿。
P.S. 真的特別感謝講師們的配合,花了很多時間討論與修改內容,真的是人美又心善,辛苦了!!


活動檢討與回饋

接著來談談活動後,回顧整場活動:

1. 參與度

從出席率和活動過程來看,與會者的參與度還算高,都蠻認真的在聽講師分享,不過因爲此場活動沒有開放「現場互動」,而是採用「單方面文字回饋」,無法反應「活躍度」與「互動率」,這點確實蠻可惜的。

2. 控場與節奏

身為 PO 兼主持人,我必須先承認在節奏與氛圍上,有很多地方需要再改進,流程、平台工具、主持切換、背景音樂等部分都有優化的空間,之後會再重點留意。

3. 參與者回饋

根據參與者填答的回饋內容,針對「框架」、「心法」、「案例分享」等內容頗受好評,也有不少人反映後續可以延續此次活動主題,多邀請不同產業的 PM 來做經驗分享,這方面我們會納入考慮,作為下次活動的備選清單。



綜上所述,為我個人對此次活動的淺見,希望有參與活動的大家都有所收穫!
如果有任何建議與指正,也歡迎留言告訴我!

【基礎教學】利用 Hexo 與 GitPage 建置個人 Blog

前言

本文主要介紹如何使用 Hexo 來製作個人 Blog,並利用 GitPage 進行自動更新部署,讓沒接觸過新手小白也能輕鬆上手。

環境建置

在開始之前,須先:

  • 安裝 Node.js
  • 安裝 Git
  • 註冊 GitHub 帳號
  • 瞭解 cmd 或 bash 指令(下文中所有以 ‘$’ 為開頭的,皆為 bash 指令)

建置 Hexo

安裝 Hexo

使用 npm 來安裝 hexo (須先安裝 Node.js)

1
$ npm install -g hexo-cli

安裝成功後,輸入以下指令可查看版本。

1
$ hexo version

安裝 Hexo Git

1
$ npm install hexo-deployer-git --save

接下來即可初始化我們的部落格了。

執行下方指令,Hexo 就會在指定資料夾 新增所需的檔案。

1
2
3
hexo init <folder>   # 初始化 blog 資料夾(自己命名)
cd <folder> # 移動到剛創建的 blog 資料夾裡
npm install # 安裝相關套件

安裝完成之後,docs 的目錄結構如下:

  • _config.yml 網站 全域配置 檔案,可以在此設置大部分的設定,其設定是用 yaml 格式編寫。
  • package.json Node.js 的套件版本控制。
  • scaffolds Hexo 釋出文章的時候使用,建立新文章時,會根據 scaffold 來建立檔案。
  • source MarkDown 和各種原始檔,放置內容的地方。
  • themes Hexo 的主題資料夾,會根據主題來產生靜態檔案。

本地端啟動

1
$ hexo generate

亦可用 hexo g,產生靜態檔案,會在目錄下產生 public 資料夾。

1
$ hexo server

亦可用 hexo s,啟動伺服器,預設是 http://localhost:4000/

1
$ hexo deploy

亦可用 hexo d,部署網站。( 如:github、heroku 等 )

完成上述步驟後,在瀏覽器中即可看到本地端啟動的個人網頁了。



部署到 GitHub

新建 GitHub Repository

接著,我們可以在 Github 的主分支上來建立我們的文件了。有兩種方式可以達成:

簡單起見,假設我們新建立了一個名為 yourname.github.io 的倉庫(repository),當然你也可以用一個已經存在的專案繼續下面的操作。

請將上面的 yourname 替換成你 GitHub 的帳號名稱

創建完新 repo 的畫面

再來,到已創立的指定資料夾 中找一個 _config.yml 的檔案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Site
title: /標題(會顯示在網頁標題與頁首)/
subtitle: /子標題(顯示在頁首)/
description: /內容描述(給搜尋引擎看的)/
author: /作者(顯示在頁尾)/
language: /網站預設語言(台灣:zh-tw)/
timezone: /時區(預設使用你電腦的時區)/

# URL
url: /網站的網域位址/
root: /網站根目錄/
permalink: /文章目錄(預設使用 YYYY\MM\DD\文章名稱)/

# Extensions
theme: /網站的佈景主題/# Deployment
deploy:
type: /發佈型態/
repository: /部署位置/
branch: /分支/
message: /部署訊息/

開啟之後,拉到檔案最底部,可以看到

1
2
deploy:
type:

將以下資訊對應輸入

1
2
3
4
deploy:
type: git
repository: http://github.com/yourname/yourname.github.io.git
branch: master

注意:
設定中的 : 後一定要有一個空格
將上面的 yourname 改成你的 GitHub 帳號名稱


連線上傳 GitHub

過程中,可能會需要輸入GitHub的帳號、密碼做授權。

使用下方的命令在本地 clone 專案:

1
$ git clone http://github.com/yourname/yourname.github.io.git

USERNAME 替換為你的使用者名稱,REPOSITORY 替換為你的專案名稱。

1
$ git add .

為 commit 留註解。

1
$ git commit -m “first commit

手動新增 GitHub 遠端 branch 名稱、連結

1
$ git remote origin  http://github.com/yourname/yourname.github.io.git

推到你的 GitHub master。

1
2
$ git push origin master
# 只有第一次 push 需要全打,第二次以後執行 git push 即可

這樣就把本地的檔案上傳到 GitHub 囉!

設定 GitPage

打開 GitHub 專案的 Setting,下面有個 GitHub Page 的地方,將其勾選設定,接著就可以在你個人的 https://yourname.github.io/ 看到畫面了。


設定專屬個人 Domain

有了個人的 GitHub Page 後,若想把網址換成專屬網域名稱,提供以下兩個網站可供參考:

  1. Gandi.net
  2. Godaddy

進去網站後,挑選並確定了自己想購買的網址後,在購物車結帳頁面可以選擇自己想要購買的年限,至少要買一年,若一年後還要再使用可以自行續約。

以 Gandi.net 為例,購買後,到 域名-區域檔紀錄-新增

新增紅框中的四筆 DNS

新增 DNS 紀錄 (可參考 GitHub Page 官方說明文件

1
2
3
4
5
6
7
類型: A
TTL: 1800
IPv4:
185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153

再來,到 GitHub 的 Setting,Custom Domain 欄位輸入購買的網址,按儲存就設定完成了。

小建議,下方的 HTTPS 也一同勾選。

稍待一陣子,就可以用新購買的網址進入個人網站囉。



更換主題

Hexo 預設的主題是 landscape,而我們可以到 Hexo主題庫 來挑選我們欲安裝的主題,以下以 Ocean 為例。

安裝 Ocean

安裝 Ocean

1
$ git clone https://github.com/zhwangart/hexo-theme-ocean.git themes/ocean

修改網站設定 <folder>/ _config.yml

1
theme: ocean

<folder>/theme/ocean/ 裡面的 _config.yml 也可以自己設定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Menu
menu:
Home: /
Archives: /archives
Gallery: /gallery
About: /about
Links: /links
rss: /atom.xml

# Miscellaneous
favicon: /favicon.ico
brand: /images/hexo.svg

# Ocean Video
# Because I put videos in multiple formats on the same path, I just labeled the path here.
ocean:
overlay: true
path: images/ocean/ # Video storage path, formats: mp4/ogg/webm
brand: /images/hexo-inverted.svg # Optional, a small logo

# Content
excerpt_link: Read More...

# fancybox
fancybox: true

# Local search
search_text: Search

# Gitalk
gitalk:
enable: true
clientID: # GitHub Application Client ID
clientSecret: # Client Secret
repo: # Repository name
owner: # GitHub ID
admin: # GitHub ID

# Valine
valine:
enable: false # Default: false.
el: 'vcomments' # The DOM element to be mounted on initialization.
appId: # Application appId from Leancloud.
appKey: # Application appKey from Leancloud.
notify: false # Mail notifier, Default: false.
verify: true # Validation code, Default: true.
avatar: 'mp' # Gravatar type.
pageSize: '10' # Number of pages per page.
placeholder: '請輸入...' #

重新啟動 server

1
$ hexo server

這樣就更改成功了!

修改後的 Ocean 主題



發佈文章

1
$ hexo new [layout] <title>

你可以在指令中指定文章的佈局(layout),預設為 post ,也可以透過修改 _config.yml 中的 default_layout 設定來指定預設佈局。

佈局(Layout)

Hexo 有三種預設佈局:post、page 和 draft,它們分別對應不同的路徑,而你所自定的其他佈局和 post 相同,都儲存至 source/_posts 資料夾。

1
2
3
$ hexo new “postName”         # 新建文章
$ hexo new page “pageName” # 新建頁面
$ hexo publish “draftName” # 新建草稿

刪除文章

首先進入到 source /_post 資料夾中,找到 helloworld.md,在本地直接執行刪除。

執行以下操作,一段時間後就會發現文章已刪除。

1
2
$ hexo clean       # 清除快取檔案和已產生的靜態檔案。
$ hexo d -g # d = deploy, g = generate

其他指令

1
2
3
$ hexo server -p 5000
# 如果 port:4000 被佔用,可以用這個$ hexo render <file1> [file2]
# 將文件渲染成靜態頁面

附錄:Hexo 官方參考文件