上一篇終於在 Views 裡找到了一堆 .cshtml。
雖然裡面混著 Razor 和 C#,但至少還看得到熟悉的 HTML。
所以我接下來最想找的當然就是:
CSS在 哪 裡?
我在 Views 裡翻來翻去,就是沒有看到熟悉的 .css 檔案。
後來才發現,CSS 根本不住在 Views 裡面。
它藏在一個叫做:
wwwroot
的資料夾裡。

而且旁邊還有一個:
js的資料夾!
這個我也算認識!
還看到一個:
favicon.ico
這個我也知道!
就是瀏覽器分頁上,網站名稱旁邊的那個小圖示。
以前做網站時,我大概只會想:
換 Logo 的小圖示嘛!
現在才知道,原來這些前端會用到的檔案,都被集中放在 wwwroot 裡面。
wwwroot 是什麼?第一次看到 wwwroot 這個名字,當然又跑去問 AI:
wwwroot是什麼?為什麼 CSS 要放在這裡?
查了之後,我先用菜鳥版理解:
wwwroot是放網頁靜態檔案的地方,也是前端資源的大本營!
裡面常見的內容包含:
css:放 CSS 樣式js:放 JavaScriptimages:放圖片、Logolib:放前端套件favicon.ico:瀏覽器分頁上的網站小圖示陌生名詞又出現了。
我看到「靜態檔案」時,第一個問題就是:
靜態是什麼?所以它完全不能變嗎?
後來才知道,這裡的「靜態」不是指內容永遠不能修改。
而是指這類檔案通常不需要先經過 Razor 或 C# 運算,就可以直接提供給瀏覽器使用。
例如:
CSS
JavaScript
圖片
字型
這些檔案通常已經先放在專案裡。
瀏覽器需要時,就可以直接向網站要求這些資源。
例如畫面載入時,瀏覽器可能會要求:
/css/styles.css
/js/site.js
/images/logo.png
/favicon.ico
流程就是:
瀏覽器打開網頁
↓
HTML 裡面有 CSS、JavaScript 或圖片路徑
↓
瀏覽器再向伺服器索取這些檔案
↓
伺服器從 wwwroot 提供檔案
↓
畫面套用樣式與功能
所以 wwwroot 不只是「放 CSS 的資料夾」。
它比較像是:
瀏覽器可以公開取得的前端資源根目錄。
Views 和 wwwroot 差在哪裡?wwwroot 裡面放的靜態檔案,比較像是先準備好,等網頁需要時直接載入。
不像 Views 裡面的 .cshtml,可能會根據後端資料產生不同的 HTML 畫面。
簡單來說:
| 位置 | 主要放什麼 |
|---|---|
Views |
Razor View,負責產生畫面內容與結構 |
wwwroot |
CSS、JavaScript、圖片和字型等靜態資源 |
菜鳥版理解:
Views負責畫面要出現什麼。
wwwroot負責畫面要穿什麼、用什麼圖片,以及執行哪些前端程式。
例如:
Views/Home/Index.cshtml
負責首頁的 HTML 結構
wwwroot/css/styles.css
負責首頁使用的樣式
wwwroot/js/site.js
負責首頁使用的 JavaScript
wwwroot/images/logo.png
負責首頁顯示的圖片
這樣一對照,我就比較有感覺了。
wwwroot?我看到 CSS 的寫法是:
<link rel="stylesheet" href="~/css/styles.css" />
但檔案明明放在:
wwwroot/css/styles.css
那為什麼路徑不是寫成:
~/wwwroot/css/styles.css
這裡我又問了一輪。
原來在 ASP.NET Core 裡,wwwroot 通常被設定成網站的靜態資源根目錄。
所以瀏覽器看到的網址路徑,會從 wwwroot 裡面開始計算。
也就是:
專案裡的實際位置:
wwwroot/css/styles.css
瀏覽器使用的路徑:
/css/styles.css
所以不需要把 wwwroot 寫進網址裡。
菜鳥版理解就是:
wwwroot是專案裡的資料夾名稱,但對瀏覽器來說,它已經是公開資源的起點。
~ 又是什麼?CSS 路徑前面還有一個:
~
例如:
<link rel="stylesheet" href="~/css/styles.css" />
我又開始問:
這個波浪符號是什麼?為什麼不是直接寫
/css/styles.css?
在 Razor View 裡,~ 通常代表網站的根路徑。
菜鳥版理解:
~/css/styles.css
就是:
從這個網站的根目錄開始,找到
css/styles.css。
不過這個 ~ 是 Razor 和 ASP.NET Core 幫忙處理的。
它不是瀏覽器原生 HTML 路徑的一部分。
最後瀏覽器實際收到的 HTML,可能會是:
<link rel="stylesheet" href="/css/styles.css" />
我看到 CSS,當然開心地先寫了一個按鈕來測試。
先在共用版面載入 CSS:
<link rel="stylesheet" href="~/css/styles.css" />
再到 .cshtml 裡面套用 class:
<button class="test-button">測試按鈕</button>
CSS 簡單寫成:
.test-button {
background-color: blue;
color: white;
padding: 8px 16px;
}

重新整理後:
讚讚讚!很醜!但是有出現!!
終於有一種回到自己地盤的感覺!
.cshtml 最後還是會產生 HTML,所以一樣可以透過 class 套用 CSS。
雖然現在是在 C# MVC 專案裡,但 HTML 和 CSS 的基本用法還是一樣的!
我的 CSS 是先放在共用版面裡:
<link rel="stylesheet" href="~/css/styles.css" />
ASP.NET Core MVC 專案常見的共用版面檔案是:
Views/Shared/_Layout.cshtml
第一次看到 _Layout.cshtml 時,我又出現一個問號:
共用版面是什麼?
菜鳥版理解就是:
_Layout.cshtml很像整個網站共用的外框。
例如:
都可能放在這裡。
如果很多頁面都會使用同一份 CSS,就不用每一個 .cshtml 都重複寫一次:
<link rel="stylesheet" href="~/css/styles.css" />
只要在共用版面載入一次,其他使用這個 Layout 的 View,也能一起使用這份 CSS。
流程可以先理解成:
_Layout.cshtml 載入共用 CSS
↓
不同 View 套用 _Layout.cshtml
↓
每個畫面都能使用相同的 CSS
看到「共用版面」後,我突然覺得這個概念好像有一點熟悉。
因為以前學 React 時,也會把每個頁面都會重複出現的內容,整理成共用元件。
例如:
function Layout({ children }) {
return (
<>
<header>網站標題</header>
<nav>導覽列</nav>
<main>
{children}
</main>
<footer>頁尾</footer>
</>
);
}
其他頁面可以放進這個 Layout:
function HomePage() {
return (
<Layout>
<h1>首頁</h1>
<p>這是首頁內容</p>
</Layout>
);
}
這裡的:
{children}
就是每一頁不同內容會被放進去的位置。
如果使用 React Router,也可能會使用:
import { Outlet } from "react-router-dom";
function Layout() {
return (
<>
<header>網站標題</header>
<nav>導覽列</nav>
<main>
<Outlet />
</main>
<footer>頁尾</footer>
</>
);
}
這裡的:
<Outlet />
就是子路由頁面顯示的位置。
_Layout.cshtml 和 React Layout 對照兩邊的概念其實有一點像:
| ASP.NET Core MVC | React |
|---|---|
_Layout.cshtml |
Layout.jsx |
@RenderBody() |
{children} 或 <Outlet /> |
| 放共用 Header、Navbar、Footer | 放共用 Header、Navbar、Footer |
| 可以載入共用 CSS、JavaScript | 可以引入共用 CSS 和元件 |
| 不同 View 填入中間內容 | 不同頁面元件填入中間內容 |
| 通常由伺服器處理 Razor 後產生 HTML | 通常由前端 React 組合畫面 |
ASP.NET Core MVC 的 _Layout.cshtml 裡,常會看到:
<body>
<header>
網站標題
</header>
<main>
@RenderBody()
</main>
<footer>
頁尾
</footer>
</body>
其中:
@RenderBody()
就是每一個 View 自己內容會被放進來的位置。
所以菜鳥版可以先這樣對照:
MVC 的 @RenderBody()
有點像 React 的 children 或 Outlet
流程則是:
ASP.NET Core MVC
_Layout.cshtml 提供共用外框
↓
View 的內容放進 @RenderBody()
↓
伺服器產生完整 HTML
↓
傳給瀏覽器
React 則比較像:
React
Layout 元件提供共用外框
↓
頁面元件放進 children 或 Outlet
↓
React 在前端組合畫面
↓
瀏覽器顯示內容
所以它們概念很像,但背後執行方式不完全一樣。
MVC 的 Layout 通常由伺服器處理。
React 的 Layout 通常由前端 JavaScript 組合。
這也讓我第一次覺得:
原來以前學過的 React 並不是完全派不上用場!
雖然語法和執行方式不同,但「把共用外框抽出來,不要每一頁重複寫」這個想法是相通的。
這個藍色按鈕雖然很醜,但其實不只是隨便測試。
它可以幫我確認整條 CSS 載入流程有沒有接成功。
流程是:
_Layout.cshtml 載入 styles.css
↓
.cshtml 的按鈕使用 test-button
↓
styles.css 裡有 .test-button
↓
瀏覽器取得 styles.css
↓
按鈕變成藍底白字
如果按鈕沒有變化,我現在會依序檢查:
wwwroot/css
<link> 的路徑是否正確.cshtml 的 class 是否和 CSS 選擇器一致.cshtml
_Layout.cshtml
以前畫面沒變時,我可能只會一直重新整理。
現在至少會開始把問題拆開:
是 CSS 根本沒載入?
還是 View 沒有使用這個 Layout?
還是 CSS 有載入,但選擇器沒對到?
還是樣式被其他規則蓋掉?
除了看畫面有沒有變,也可以用瀏覽器的開發者工具確認。
我可以在按鈕上按右鍵,選擇「檢查」,然後查看:
test-button 這個 class.test-button 規則有沒有出現在 Stylesbackground-color: blue 是否被劃掉styles.css 是否回傳成功如果 .test-button 規則完全沒有出現,可能代表:
CSS 沒有載入
或
選擇器沒有對到這個元素
如果規則有出現,但是被劃掉,可能代表:
有其他優先權更高的 CSS 把它蓋掉
所以畫面沒變,不一定代表 CSS 完全沒作用。
有時候是 CSS 有載入,只是輸給了其他樣式規則。
favicon.ico 也在 wwwroot看到 favicon.ico 後,我原本只覺得:
喔,就是換 Logo 的小圖示嘛!
不過更精準地說,它是網站的 favicon。
通常會出現在:
HTML 裡可能會用類似這樣的方式載入:
<link rel="icon" href="~/favicon.ico" />
如果直接把檔案換掉,但畫面沒有立即更新,也可能是瀏覽器快取。
因為 favicon 特別容易被瀏覽器記住。
所以有時候需要:
這也讓我發現:
前端檔案明明換了,畫面沒變,不一定是程式沒改成功,也可能只是瀏覽器還記得舊檔案。
wwwroot 裡的東西都可以公開嗎?因為 wwwroot 是瀏覽器可以取得靜態資源的地方,所以也代表:
不適合把敏感資料放在這裡。
例如不應該把這些東西放進 wwwroot:
因為只要網站有公開提供這些檔案,就可能被瀏覽器直接存取。
這是我以前只把 wwwroot 當成「放 CSS 的地方」時,完全沒有想到的事情。
比較完整的理解應該是:
wwwroot是公開靜態資源的根目錄,所以只能放適合提供給前端使用的檔案。
我原本只問:
CSS 在哪裡?
結果一路問下去,又冒出一堆問題:
wwwroot是什麼?
靜態檔案是什麼?
為什麼路徑裡不用寫wwwroot?
~又代表什麼?
CSS 為什麼通常放在_Layout.cshtml?
_Layout.cshtml和 React 的 Layout 是不是一樣的概念?
@RenderBody()是不是有點像 React 的children?
畫面沒變要怎麼知道是路徑錯,還是樣式被蓋掉?
wwwroot裡可以放密碼嗎?
AI 先幫我建立方向。
但我還是回到專案裡實際確認:
找到 wwwroot/css/styles.css
↓
在 _Layout.cshtml 載入 CSS
↓
確認 View 有使用這個 Layout
↓
在 View 加上 class
↓
重新啟動網站
↓
使用開發者工具查看 CSS 是否生效
我才慢慢理解,CSS 能顯示不是因為:
我把檔案丟進 css 資料夾,Visual Studio 就自動知道了。
而是因為整條流程有接起來:
CSS 檔案放在 wwwroot
↓
_Layout.cshtml 產生正確的 link
↓
View 套用 Layout
↓
瀏覽器向伺服器取得 CSS
↓
CSS 選擇器對到 HTML 元素
↓
畫面才會出現樣式
目前我先記得:
Views
放 Razor View,負責產生 HTML 畫面
wwwroot
放瀏覽器可以直接取得的靜態資源
_Layout.cshtml
放網站共用的版面外框與共用資源
常見結構可能是:
Views
├── Home
│ └── Index.cshtml
└── Shared
└── _Layout.cshtml
wwwroot
├── css
├── js
├── images
├── lib
└── favicon.ico
CSS 載入流程是:
wwwroot/css/styles.css
↓
_Layout.cshtml 使用 <link> 載入
↓
View 使用這個 Layout
↓
.cshtml 元素套用 class
↓
瀏覽器取得 CSS
↓
畫面出現樣式
React 和 MVC 的共用版面概念則可以先這樣對照:
MVC
_Layout.cshtml
+
@RenderBody()
React
Layout Component
+
children 或 Outlet
兩邊都是把重複的網站外框抽出來,只是執行的位置和語法不同。
以及一個很重要的觀念:
wwwroot裡的內容可能被前端直接存取,所以不能放敏感資料。
好,找到 CSS 後,我終於有一種回到自己地盤的感覺。
而且以前學過的 React,居然也可以拿來幫助理解 C# MVC 的共用版面。
但當初我學習 React 時,使用的是 SCSS。
於是我很自然地建立了一個 scss 資料夾,寫完樣式後重新整理……
結果:
畫面完全沒有變?
我又開始滿頭問號地求助我的程式助教。
下一篇:
React 專案為什麼可以直接寫 SCSS?C# 專案卻沒反應?