[Tailwind] 環境建立
TL;DR
之前參加六角學院的切版直播班,有提供Vite基本設定的模板。
本篇文章只是 將內容給拆分出來做個紀錄,實際上應該有更好的專案建立方式。
檔案名稱以及指令會用區塊
的方式呈現
設定 .gitignore
,git init
,npm init
首先在資料夾下將git初始化git init
、npm初始化npm init
並且新增.gitignore
- 程式碼
- 資料夾結構
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
./
├── .git/
└── .gitignore
這邊記得先製作一個commit
git init
&npm init
的部分則不在這個部分贅述
使用NPM安裝Tailwind
- 指令
- 資料夾結構
npm install -D tailwindcss postcss autoprefixer
./
├── .git/
└── .gitignore
- autoprefixer是為了加上各個瀏覽器的前綴,例如
-webkit-min-device-pixel-ratio: 2
- 因為Tailwind本身很大一包,需要使用postcss,可以讓撰寫的CSS精簡化,讓沒有使用到的CSS可以被刪減而不會被編譯出來。
- tailwindcss就不用說明了… css本人!
其中的-D
是npm install中的一個選項,是—-save-dev
的縮寫,如果有啟用這個設定的話則會安裝在devDependencies
中,代表這個是開發中會使用到的套件而不是正式上線的時候使用的套件。
使用NPM安裝Vite等等其他相依套件
- 指令
- package.json內容
npm install -D ejs vite vite-plugin-ejs
npm install vite-plugin-live-reload glob gh-pages sass sass-loader
{
"devDependencies": {
"autoprefixer": "^10.4.16",
"ejs": "^3.1.9",
"postcss": "^8.4.30",
"tailwindcss": "^3.3.3",
"vite": "^4.4.9",
"vite-plugin-ejs": "^1.6.4"
},
"dependencies": {
"gh-pages": "^6.0.0",
"glob": "^10.3.7",
"sass": "^1.68.0",
"sass-loader": "^13.3.2",
"vite-plugin-live-reload": "^3.0.2"
}
}
以上套件的說明如下
- autoprefixer: 用於自動為 CSS 添加瀏覽器前綴,以確保在不同瀏覽器中具有相同的樣式效果。通常在開發期間使用。
- ejs: Embedded JavaScript (EJS) 是一個模板引擎,用於生成動態 HTML 內容。它可以幫助你在後端生成 HTML,或在前端透過 JavaScript 嵌入動態內容。
- postcss: PostCSS 是一個用於處理 CSS 的工具,它允許你使用插件來轉換、優化和擴展 CSS。常用於前端開發中的 CSS 後處理。
- tailwindcss: 一個高度可自訂的 CSS 框架,用於快速開發現代網頁界面。它基於一組預定義的 CSS 類別,可用於快速構建 UI 元素。
- vite: Vite 是一個快速的前端開發工具,用於建構現代的 Web 應用程式。它具有快速的開發伺服器、即時重載等功能。
- vite-plugin-ejs: 這是一個 Vite 插件,用於支援 EJS 模板。它讓你可以在 Vite 專案中使用 EJS 進行動態內容生成。
在 "dependencies" 中的套件:
- @tailwindcss/forms: 這是 Tailwind CSS 的一個官方插件,用於樣式化 HTML 表單元素,使它們與 Tailwind CSS 的風格一致。
- gh-pages: 一個用於將靜態網站部署到 GitHub Pages 的工具。通常用於將網站部署到 GitHub。
- glob: 一個用於匹配文件路徑的工具,通常用於在 Node.js 中進行文件操作,如檔案搜索。
- sass 和 sass-loader: 用於處理 SASS(Syntactically Awesome Style Sheets)或 SCSS(Sassy CSS)的套件。它們允許你在專案中使用 SASS 或 SCSS 語法來編寫 CSS。
- vite-plugin-live-reload: 這是一個 Vite 插件,用於實現網頁的即時重載功能,使你在開發期間可以看到即時的變更效果。
設定package.json
的scripts
回到package.json中,補上scripts的內容如下
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"deploy": "vite build && gh-pages -d dist"
}
所以目前的package.json完整內容應該要如下
{
"name": "專案名稱",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"deploy": "vite build && gh-pages -d dist"
},
"devDependencies": {
"autoprefixer": "^10.4.15",
"ejs": "^3.1.9",
"postcss": "^8.4.28",
"tailwindcss": "^3.3.3",
"vite": "^4.2.0",
"vite-plugin-ejs": "^1.6.4"
},
"dependencies": {
"@tailwindcss/forms": "^0.5.4",//這個我在上面並沒有特別安裝,如果有需要再去tailwind官方查詢,有介紹到表單的套件
"gh-pages": "^5.0.0",
"glob": "^10.2.2",
"sass": "^1.61.0",
"sass-loader": "^13.2.2",
"vite-plugin-live-reload": "^3.0.2"
}
}
因為後續會使用到export default的es module語法,所以這邊一定要在package.json中設定"type": "module"
否則會跳以下的警告:To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
設定vite.config.js
- vite.config.js
- 資料夾結構
這邊需要在根目錄下新增一隻新的檔案如下
import { defineConfig } from 'vite';
import { ViteEjsPlugin } from 'vite-plugin-ejs';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { glob } from 'glob';
import liveReload from 'vite-plugin-live-reload';
function moveOutputPlugin() {
return {
name: 'move-output',
enforce: 'post',
apply: 'build',
async generateBundle(options, bundle) {
for (const fileName in bundle) {
if (fileName.startsWith('pages/')) {
const newFileName = fileName.slice('pages/'.length);
bundle[fileName].fileName = newFileName;
}
}
},
};
}
export default defineConfig({
// base 的寫法:
// base: '/Repository 的名稱/'
base: '/web-layout-training-vite/',
plugins: [
liveReload(['./layout/**/*.ejs', './pages/**/*.ejs', './pages/**/*.html']),
ViteEjsPlugin(),
moveOutputPlugin(),
],
server: {
// 啟動 server 時預設開啟的頁面
open: 'pages/index.html',
},
build: {
rollupOptions: {
input: Object.fromEntries(
glob
.sync('pages/**/*.html')
.map((file) => [
path.relative('pages', file.slice(0, file.length - path.extname(file).length)),
fileURLToPath(new URL(file, import.meta.url)),
])
),
},
outDir: 'dist',
},
});
上方highlight的部分需要修正為repo名稱,否則在部署的時候可能會有問題
./
├── .git/
├── .gitignore
├── node_modules/
├── package-lock.json
├── package.json
├── readme.md
└── vite.config.js
設定postcss.config.js
官方文件有提到,不管有沒有要使用postcss,都需要加上這支postcss.config.js
檔案
並且將autoprefixer
還有tailwindcss
加入postcss.config.js
這支檔案
所以我們需要創建一個postcss.config.js
檔案,並且寫入以下資料
前面有提到,會採用ES6的寫法,所以我們也特別在package.json
內加上了"type":"module"
我們這邊不採用官方的module.exports
的寫法,而是使用ES6的export default
- ES6設定
- 官方提供寫法
- 資料夾結構
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
我們在這邊採用ES6的寫法
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
./
├── .git/
├── .gitignore
├── node_modules/
├── package-lock.json
├── package.json
├── postcss.config.js
├── readme.md
└── vite.config.js
設定tailwind.config.js
新增一個tailwind.config.js,並且設定如下(這邊同樣不採用官方提供的module.exports的寫法,改用export default)
- ES6寫法
- 官方提供寫法
- 資料夾結構
我們同樣採用ES6的export default寫法
/** @type {import('tailwindcss').Config} */
export default {
content: ['./pages/**/*.html', './layout/**/*.ejs', './main.js'],
theme: {
extend: {},
},
plugins: [],
}
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
./
├── .git/
├── .gitignore
├── node_modules/
├── package-lock.json
├── package.json
├── postcss.config.js
├── readme.md
├── tailwind.config.js
└── vite.config.js
邊需要注意content
內要撰寫tailwind要追蹤的檔案有哪些,是用陣列包字串
的方式去撰寫。
例如./pages/\*\*/\*.html
代表會追蹤pages資料夾下,不管多少層級(**)下的副檔名是html的檔案
追蹤的用途是為了確認有使用到哪些的tailwind css,會透過postcss去精簡部署時的css檔案。
如果追蹤的檔案沒有使用到的tailwind css,就不會編譯出來,縮減css檔案的大小。
算是補償了tailwind css肥大的問題~
如果發現編譯出來的結果沒有正確顯示,就需要來調整這個區塊的content內容。
設定main.css
並加上index.html
文件中有寫到,需要在main.css
中加上以下的內容
又因為我們有使用vite,所以我們需要先創建一個assets資料夾,才在裡面加上一個main.css檔案,寫入官方文件中所指定的資訊
- 資料夾結構
- main.css
- main.js
- index.html
./
├── assets/
│ └── css/
│ └── main.css
├── main.js
├── package-lock.json
├── package.json
├── pages/
│ └── index.html
├── postcss.config.js
├── readme.md
├── tailwind.config.js
└── vite.config.js
@tailwind base;
@tailwind components;
@tailwind utilities;
使用vite需要給他一個進入點,在根目錄下創建main.js,並且將剛剛已經引入tailwind css的main.css給加入其中(使用import)
import './assets/css/main.css';
console.log('hello world'); //加上這行在瀏覽器中console會顯示! 確認是否有成功
在根目錄下創建一個index.html檔案,並且需要在</body>
結尾之前的位置加上main.js的路徑
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
...
<script type="module" src="../main.js"></script>
</body>
</html>
測試功能性
可以使用npm run dev
,會開啟一個伺服器,並且會隨著每次儲存資料去更新渲染的畫面
例如在index.html中輸入如下
另外,使用npm run build
會建立一個dist資料夾
(如下圖),並且在assets內會有一個編譯過後的css檔案
可以觀察到這隻被編譯出來的css檔案中,其實內容並沒有很多。
因為是已經被壓縮過後的所以可讀性不高,可是還 是可以看到在最後有一個.text-blue-500的class
是因為我們在index.html中有使用到這個class,所以在編譯的時候經過postcss才會被保留。其他則都是一些base等等的設定。
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}