Skip to main content

· 3 min read

近期因為公司專案部份功能進行重構,再加上之前公司的專案內並沒有定義 Desgin Guide,前端在實做各元件時都是直接照設計稿上的樣式去填值,也因此常常遇到要改一個顏色需要全盤修改與盤點,非常耗時。因此在這次要進行重構時,與設計討論要將 Design Token 概念導入。

由於目前公司設計稿是使用 Figma 來設計,因此我們是用 Tokens Studio for Figma 這個在 Figma 上的 Plugin。

Figma

在 Figma 上使用 Tokens Studio for Figma,整體上要設定並不困難,同時又可以與如 Github 之類的 remote repository 連動,對於要讓設計給完後同步給前端來說很方便。較須注意注意的我覺得是命名方式。

To RD

在如何將 Token 轉換成 RD 程式上的邏輯,我覺得是較麻煩的部份。我們是使用 Style Dictinoary 和 sd-transforms 這兩個套件來完成。由於我們的專案是使用目前最新的 Tailwind 4.0,目前在相關的 formatter 上面是沒有找到,因此選擇自己完成 formatter。

在 formatter 轉換邏輯上,就需要了解 Token Studio 上的命名模式了,除非要自己做大量的 mapping,不然好的命名方式對於在將 token 轉成 RD 程式上的邏輯會是比較輕鬆的。

結論

目前自己公司在 token 的使用上事先針對 web 端來使用,但在 android 和 iOS 同樣可以使用此方法,這也是後續可以在嘗試的地方。

· 2 min read

package.json 內定義了我們專案中會使用到的套件,需使用的語法,或是專案中的一些詳細設定。

package.json 內容的套件版本

通常我們在安裝專案所需要用到的套件時,套件的前面都會出現^的符號。

  "dependencies": {
"urijs": "^1.19.7"
}

這個符號一般是用來讓我們安裝套裝套件時,讓npm知道我們在套裝這個套件時,可以安裝大版號相同的最新版本套件。

舉例來說,我們現在安裝一個urijs套件,目前最新版本是1.19.7,假設今天出了新的版本叫做1.19.8,那我們在重新安裝這個專案的套件時就會安裝1.19.8。

但這樣,似乎代表不能指定要安裝的版本號。

指定版本安裝方式

在npm 5後,新增了一個叫做package-lock.json的檔案,簡單來說,這個檔案會嚴格定義各個套件所使用的版本。

· 6 min read

Prisma

Prisma 是一個 Node.js 的 ORM 框架,基本上他可以用在任何的 Node.js 框架上。而自己之所以會接觸到也是因為公司的小專案上(使用 Next.js)需要使用到 DB,但又懶得自己去處理一些 SQL injection 的問題,因此就找到了此 ORM 框架。

那 Prisma 基本上是由三個工具組成

  • Prisma Client: Auto-generated and type-safe query builder for Node.js & TypeScript
  • Prisma Migrate: Declarative data modeling & migration system
  • Prisma Studio: GUI to view and edit data in your database

那基本上我們的重點會是在Prisma ClientPrisma Migrate上,Prisma Studio可以想像成我們平常在用來看 DB 的 GUI 工具,所以要裝不裝都沒差。

note

官方的詳細介紹:連結

Prisma Migrate

在講 Migrate 之前要先說到Prisma Schema,簡單來說他就是定義 Prisma 的設定檔,裡面大致上分為三塊。

  • Data sources: DB 的來源與類型。(目前 Prisma 主要可以使用關連式資料庫,至於像 MongoDB 這種 NoSQL 資料庫,官方有說到資源並不全面)
  • Generators: 生成哪些客戶端可以使用。我自己只有使用prisma-client,其他假如你是使用 nestjs 這種框架,可以參考看看。
  • Data model definition: 定義 DB Table 的一些欄位資料和表之間的關聯。
note

Generators:這部分我自己只有使用Prisma Client,如果是使用 nestjs 或需要用到 GraphQL 的人,可以參考連結其他的 generators。

Data sources

datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public"
}

基本上 provider 就是定義是用哪個 DB,URL 則是 DB 的位置。

在官方的文件中,可能會看到 url 後面是寫env("DATABASE_URL"),基本上你要把 DB 位置寫在環境變數的檔案中或直接寫死在設定檔都可以。

Generators

generator client {
provider = "prisma-client-js"
}

一般來說,如果你沒有要特別使用其他的 generator,就都是使用prisma-client-js

若有其他特殊需求,可參考上方備註。

Data model definition

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
}

model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int
}

model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
title String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
categories Category[] @relation(references: [id])
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] @relation(references: [id])
}

enum Role {
USER
ADMIN
}

講白了,基本上就只是你 Table Colume 的定義內容和 Table 之間的關聯。哪個欄位是 PK、哪個欄位有 Default 的資料或哪個欄位是 NOT NULL,在model <Table name>裡面都會定義出來。但相對的,要怎麼去寫這些定義,就需要去查官方文件了。

所以,基本上我都是先在 db 建好 table,定義好 colume 的格式,再透過

npx prisma db pull

這個指令,讓 prisma 幫我產好 model。

假設在 db 設計 table 時,table 沒有 PK 等之類的問題,再透過上方的 shell 去執行後,prisma 都會有 warning 或 error 的提示,可以讓我們順便知道在設計 table 時有沒有犯了哪些根本問題。

note

有關 Prisma 的 model 要怎麼寫,可以參考官方的文件連結

我自己是習慣使用npx prisma db pull幫我產好 model XDD

Prisma Client

白話的說法就是你可以透過上方定義的 generator,透過 JavaScript 之類的方式,開始去跟 DB 進行互動。

使用的方式首先是將 generator 給引入,要使用 CJS 還是 ESM 的方式引入都可以。

const { PrismaClient } = require("@prisma/client");

const prisma = new PrismaClient();

CRUD

const user = await prisma.user.create({
data: {
email: "elsa@prisma.io",
name: "Elsa Prisma",
},
});

其寫法簡單來說就是

<引入的prisma client變數>.<prisma model name>.<行為(CRUD)>({
<where 條件>,
<寫入的資料>.....
})

行為有像是單筆新增的create,多筆新增的createMany,查詢的findUniquefindFirstfindMany......。

note

這邊不多做使用說明,因為小弟覺得 ORM 的東西看文件才能比較系統性的了解其語法。 參考:

結語

上方簡單說明了 Prisma 這個 ORM 框架。像 Prisma Client 我們也只有簡略的介紹了 CRUD 的語法,但他其實還有像 Middleware 和 log 等較進階的東西。若對 Prisma 有興趣,可以在去閱讀其官方文件。

· 2 min read

Git config 是定義了 Git 環境的設定檔,這些檔案可以被存放在三個地方。

Git Config 環境層級

  1. System 層級的:/etc/gitconfig/

    用於針對所有用戶的設定

    可以透過以下指令來針對 system 層級做設定。

    $ git config --system
  2. 用戶層級的:~/.gitconfig

    用於針對個別使用者的設定

    可以透過以下指令針對用戶層級做設定。

    $ git config --global

    個人是習慣 git 的相關設定會設置於用戶層級的,因此大多設定內容都在~/.gitconfig內。

  3. 專案層級的:/.git/config

    個別專案設定的

    可以透過以下指令針對專案層級做設定。

    $ git config

    大多如 remote 倉庫的位置等資訊都是設置在這個層級的。

在這三個層級裡,專案層級的東西會去覆蓋用戶層級的,而用戶層級的內容會去覆蓋 System 層級的。

Git 規格覆蓋的準則:專案層級 > 使用者層級 > 系統層級

· 3 min read

curl 是一個同 wget 為 Linux 上方便的指令,可把網頁抓下來進行分析。一般來說當工程師撰寫完 API 後,都需要進行 HTTP Request 來測試,目前有像 postman 方便的 GUI tool,但如果懶惰或不想啟動較吃資源的 GRU tool,此時我們就可以透過 curl 指令幫助我們測試。

以下我紀錄個人覺得較常用之指令。

curl GET

crul 預設為使用 GET 請求,一般來說指令組成結構為curl [option] [URL]

下方我們會以 httpbin 作為 HTTP Request 的 URL 並說明常用 option 選項。

補充:httpbin 回傳會以 JSON 格式為內容格式。

curl https://httpbin.org/get
//取得對httpbin進行get的回傳內容

curl -I https://httpbin.org/get
//只需要顯示response header
//其實就等於 curl --head hhttps://httpbin.org/get
//-I為簡寫,可透過curl -help查詢

curl -o abc.txt https://httpbin.org/get
//將httpbin進行get的回傳內容儲存下來,並存在一為abc.txt的檔案內
//小寫"o"為下載請求資源到新的檔案
//檔案之名稱與副檔名可自行設定

curl -O https://httpbin.org/get
//同為將httpbin進行get的回傳內容
//大寫"O"為使用指定網址伺服器的檔名作為下載之檔名

curl -L google.com
//檔網頁進行redirect時,連到redirect網址
//可以試看看沒有加上"-L"的狀況

curl https://httpbin.org/get -H "accept: application/json"
//設定request所要挾帶的header
//這邊設定告知伺服器用戶端可解讀JSON內容

curl POST/PUT/......

除了最基本的 get,可以使用"-X"決定要進行"GET|POST|PUT|DELETE|PATCH"哪個 http method。

在我們使用-X 得時候,我們常常會使用到下列的指令。

-H 夾帶的header
-d 夾帶post data內容
-u 夾帶使用者帳號密碼
-b 攜帶cookie
curl -X POST "https://httpbin.org/post" -d "email=abc@gmail.com" -H "accept: application/json"
//進行post時,夾帶email內容
//要使用其他的http method 更改-X後面的名稱

curl -X POST "https://httpbin.org/post" -b "num=20"
//設定cookie num=20

curl -u "abc:200" https://httpbin.org/get
//若網頁有使用basic auth,可以使用-u夾帶帳號密碼過驗證

· 3 min read

透過 Hostname 來決定使用哪一組帳號的 SSH Key 進行溝通。

SSH 生成

首先我們先使用ssh-keygen指令生成金鑰

圖中我們的指令為ssh-keygen -t rsa -C "email@gamil.com"

  • -t 為加密方法的選擇,我們選擇使用 RSA 加密
  • -C 為註解,會加入 SSH 的金鑰。可做為金鑰持有者的辨識。

輸入上述指令後,會分別詢問

  • 金鑰存放位置和檔案名稱
  • 是否設置 Passphrase(如有輸入,會須重複輸入一次)

上述均輸入完畢後,會產生 id_rsa 和 id_rsa.pub(這邊以預設檔名說明)

  • id_rsa 此為私鑰,也就是要自己保管好的密碼。
  • id_rsa.pub 此為公鑰,也就是對外公開的鑰匙,此會作為與本地端私鑰溝通使用。

將生成的金鑰複製到 Github

如果使用 linux,可以直接使用 ssh-copy-id 複製。

mac 如要使用 ssh-copy-id,需使用 Homebrew 安裝。

  • mac 還可以使用 pbcopy 來複製,指令pbcopy < ~/.ssh/id_rsa.pub

或是去打開檔案複製都可以。


設定 ssh config 方便選擇對應的 git 倉庫

在金鑰該層目錄,新增一 config 檔案。

可以使用nanovim之類編輯都可以,看個人習慣。

nano ~/.ssh/config

新增下列資訊

Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa

Host github-another
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_new
  • Host 後面的 github.com 或 github-another 就是你要連接倉庫的簡稱。
  • HostName 填 HostNmae 的 domain 或 IP,因為是連 github,所以是 github.com。
  • User 登入 SSH 的 username,個人習慣統一為 git。
  • IdentityFile key 的路徑。 其他還有 Port 或 ForwardX11 等指令。不過我們是連 github,所以不需要。

設定完成後 可以下ssh -T <Host>檢查。 ex: ssh -T github-another


Clone 與 push

  • Clone: git clone <host-in-ssh-config>:<username>/<repo>
  • Push: git remote set-url origin <host-in-ssh-config>:<username>/<repo>

補充

以往只有單一一組的 github,我們會直接在 git 的 global 設定好 username 和 email。

但在有多組的 github 帳號後,如不想均使用相通的名稱。 需先使用git config — global — unset user.name git config — global — unset user.email取消 global 設定。

再根據 Repo 來決定 User 資料

  • git config user.name "userName"
  • git config user.email "eamil"

· 3 min read

Google Apps Script(GAS)是什麼,可以參考wiki的介紹。但我一般會把它解釋成一個後端,類似 nodejs 之類的。

在 GAS 裡面,你可以透過 JavaScript 去連接 Google 的各類服務,或是去連接 Google 的 Firebase 資料庫也是可以的。這邊我們會使用 GAS 來串接 Google Sheets。

GAS 連結 Google 表單

要開啟 GAS 的編輯器,可以從 Google 表單上方的工具列 工具>指令碼編輯器 或是在 Google 雲端硬碟右鍵>更多>Google Apps Script(要先連結 GAS 應用程式),開啟後副檔名應該會是 gs。

目前的 GAS 是可以使用 es6 語法的,但因為要做一些對應的設定,這邊我們會使用較舊的 JavaScript 語法撰寫。

function doPost(e) {
//取得參數
var params = JSON.parse(e.postData.contents);
var num = params.num;
var one = params.one;
var one_other = params.one_other;
var boss_one = params.boss_one;
var to = params.name;
var date = params.date;

//sheet資訊
var SpreadSheet = SpreadsheetApp.openById("");
var Sheet = SpreadSheet.getSheets()[0];


//setValue...
...

return ContentService.createTextOutput(params);
}

上述我們撰寫了一個 doPost 的 function。 doPost 其實就是我們在 Call 這隻 gs 檔的 API,進行 post 時會觸發的 function。

我們可以先透過 e 這個參數取得 post 的資料。 接下來透過SpreadsheetApp.openById("")選擇要開啟哪個 Google 表單的檔案,再透過SpreadSheet.getSheets()[0]綁定好選擇的檔案裡面的哪張表(0 表示第一張表)。

選擇好表後,就可以透過 getRange()取得表的指定格子位置,並透過 setValue()或 setFormula()方法來將值存入。

最後的 return 則是要回傳什麼內容。

GAS 部署

在寫完 GAS 的 code 後,我們要部署並產生 API。 選擇發佈>部署爲網路應用程式,將具有應用程式存取權的使用者改爲 “Anyone, even anonymous“ ,並點選部署。

接下來第一次部署會出現權限核對的一些設定。 基本上就是核對權限>選擇自己的帳戶>進階>前往>允許。 點選完畢後會出現下圖

那串 URL 就是你的 API 路徑。


補充

  • 在 GAS 內沒有 console.log(),要使用 Logger.log()

  • GAS 的 goGet()和 doPost()方法,不能直接 return 一個 object。但可以轉成 JSON 回傳,詳細可參考

· 2 min read

要製作 Discord Bot 目前主要有兩個 API 可以使用。

  • discord.io (官方維護)
  • discord.js (民間版本)

但從 npm 上可以發現目前 discord.io 已經約兩年沒有更新了,如果遇到任何 Bug,要等到官方修復可能需要一定的時間。也因此較推薦使用 discord.js 這個非官方的套件。


製作方法

  • 先去 discord 官方建立一個 bot(可參考 discord.js 上的教學)
  • 把 bot 加入你所要放置的頻道
  • 開始撰寫你的 bot
// 讀取discord.js套件
const Discord = require('discord.js');

// 建立一個Discord client
const bot = new Discord.Client();

// 當你要啟動這個bot時,會執行的事情
bot.on('ready', () => {
console.log('Ready!');
});

// 在建立一個bot時會取得token,這邊要輸入token
bot.login('your-token');

上述輸入完後,就會在你的 Discord 頻道看到 Discord 機器人。 {% asset_img bot.png %} 我這邊因為已經啟動上述的 code,所以 bot 會是線上的狀況,如果沒有執行的話,會顯示離線。

在建立完 bot 之後,接下來就是要開始監聽輸入的內容。

// message裡面會有使用者輸入的內容和使用者資料等等
bot.on('message', message => {
console.log(message.content);
});

我們只需要去監聽使用者輸入的內容做相對應得事情。 例如使用!作為 bot 要回覆得行為偵測, 接下來就可以使用 switch...case 的寫法,來判定!後面的內容 如果!後面的內容並不是定義好的 可以再使用 default 來回覆錯誤訊息。

· 5 min read

App.js 文件配置

//引入第三方middleware package
//引入http-errors套件
var createError = require('http-errors');

//引入express套件
var express = require('express');

//引入path套件
var path = require('path');

//引入cookie-parser套件
//接收到cookie資料做解析
var cookieParser = require('cookie-parser');

//引入morgan套件
//可以記錄各種事件資料
//例如進行get 或 post等行為
var logger = require('morgan');

//連接透過express.Router()產生實例的router
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

//模板引擎 這邊使用pug 要使用ejs將pug換成ejs
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev'));

//可以透過json或一般的字串取得get post等資料
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
//靜態資料 抓到根目錄/public 資料夾
app.use(express.static(path.join(__dirname, 'public')));

//使用上方引入的./routes/index 來管理根目錄router
app.use('/', indexRouter);
//使用上方引入的./routes/user 來管理user router
app.use('/users', usersRouter);

//如果上方的router都沒進入,抓取錯誤
//透過http-errors套件顯示404錯誤
//也可以自定義404錯誤要顯示的title,這邊title定為"This item is not exist!"
//要顯示其他訊息 將404改成其他http狀態碼
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404, 'This item is not exist!'));
});

//錯誤的處理
//預設的處理方式是僅在開發的過程中提供錯誤訊息
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

// render the error page
res.status(err.status || 500);
res.render('error');
});

module.exports = app;

上述為 express App.js 的默認設定。

但在實際上,express-generator 產生的專案,package.json 內 npm start 實際上是去執行"node ./bin/www",也就是執行 bin 資料夾裡 www 的檔案。下面簡單紀錄 www 文件配置。


bin/www 文件配置

#!/usr/bin/env node

/**
* Module dependencies.
*/
//有載入上述說明的app.js檔設定
var app = require('../app');
var debug = require('debug')('expr:server');
var http = require('http');

/**
* Get port from environment and store in Express.
*/
//設定port 如果環境有預設使用環境預設的,沒有就使用3000 port
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}

return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}

var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
debug('Listening on ' + bind);
}

express router 上個人常用的指令

req -> request

res -> response

  • req.params
    • 取得路徑參數值
    • ex
    app.get('user/:number', function (req, res) {
    var num = req.params.number;
    res.json({ number: num });
    });
  • res.json()
    • 輸出 json 資料
  • res.render()
    • 渲染指定畫面
  • res.redirect()
    • 網址重新導向

其餘大致上是邏輯的處理。


補充

  • req.params. ...
    • 撈出路由設定的資料
  • req.query. ...
    • 取得網址的參數
  • app.use()
    • 使用 middleware
    • 類似一層一層的過濾,中間可以處理 http 的 request、response 或一些檢查動作等等。
    • 參考

· 4 min read

ESLint 是一個 code 的檢查工具,他可以幫我們檢查我們 code 是否符合我們所設定的規範,並指出在哪裡。而 code 有著一定的規範,可以讓我們在團隊開發中,有著統一的標準,讓整個團隊開發更一致;對於個人則是可以讓我們學習許多資深開發者的想法,有助於個人程式碼攥寫的提升。


ESLint 安裝

自己較常是全域安裝

npm install eslint -g

要注意 ESLint 對於 node 和 npm 版本有要求,如果不符合 ESLint 所要求的版本,請將 node 和 npm 安裝到目前的穩定版本。 {% asset_img 2.png %} 一般我會使用

npm install npm -g

將 npm 重新安裝 再透過

npm install n -g

安裝 n 模組 這個 n 模組其實就是 node 的版本管理工具

可參考官方介紹&n 模組

在中國的 SegmentFault 也看到一篇不錯的說明,有興趣的可以參考

請注意上述 node 版本更新不適用 windows 上,windows 上請直接下載最新版的 node 並進行覆蓋,或參考官方說明文件,如何在 windows 上安裝多版本 node

ESLint 建置

在專案中,使用

eslint --init

建置.eslintrc.js 文件 {% asset_img 1.png %} 建置過程中會問一些設定的問題,根據選項去選擇即可。至於 style guide 部分,有三種規範可以使用。分別是

  • Google
  • Airbnb
  • Standard

其中 Airbnb 規範最嚴謹,Google 次之,個人是選擇 Airbnb。關於 Airbnb 的規範,可參考 此連結。目前已經有繁體中文版,上方連結可找到。

在選擇完規範後,會選擇要以 yaml、js 還是 json 儲存規範,這邊選擇 js。

重開安裝 eslint 的專案,在問題部分就會提醒你 code 哪邊不符合規範。 {% asset_img 3.png %} 比如說括號前後要空格,或是 props 參數沒用到等等。

在.eslintrc.js 的文件中,會有許多的設定。

  • parser 是解析器,可以根據專案設定不同。
  • extends 可以設定一些設定好的規範,如前面提到的 Airbnb 規範,就是寫在 extends 裡。
  • rules 是指額外的規則,像上方圖片裡面有一條提到 js 文件裡面不能寫 JSX,這個問題我們即可在 reles 加入"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],來解決。
  • ecmaFeatures 是設定可以使用哪些額外功能,像可以使用 JSX 等等。
  • plugins 使用一些額外的第三方套件。
  • env 環境,個人主要在 browser 和 node 上。 關於 eslint 文件設定方法和參數設定,可參考連結