iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0

上一篇終於在 Views 裡找到了一堆 .cshtml

雖然裡面混著 Razor 和 C#,但至少還看得到熟悉的 HTML。

所以我接下來最想找的當然就是:

CSS 在 哪 裡?

我在 Views 裡翻來翻去,就是沒有看到熟悉的 .css 檔案。

後來才發現,CSS 根本不住在 Views 裡面。

它藏在一個叫做:

wwwroot

的資料夾裡。

wwwroot 資料夾

而且旁邊還有一個:

js的資料夾!

這個我也算認識!

還看到一個:

favicon.ico

這個我也知道!

就是瀏覽器分頁上,網站名稱旁邊的那個小圖示。

以前做網站時,我大概只會想:

換 Logo 的小圖示嘛!

現在才知道,原來這些前端會用到的檔案,都被集中放在 wwwroot 裡面。

wwwroot 是什麼?

第一次看到 wwwroot 這個名字,當然又跑去問 AI:

wwwroot 是什麼?為什麼 CSS 要放在這裡?

查了之後,我先用菜鳥版理解:

wwwroot 是放網頁靜態檔案的地方,也是前端資源的大本營!

裡面常見的內容包含:

  • css:放 CSS 樣式
  • js:放 JavaScript
  • images:放圖片、Logo
  • 網站使用的字型
  • lib:放前端套件
  • favicon.ico:瀏覽器分頁上的網站小圖示

什麼是靜態檔案?

陌生名詞又出現了。

我看到「靜態檔案」時,第一個問題就是:

靜態是什麼?所以它完全不能變嗎?

後來才知道,這裡的「靜態」不是指內容永遠不能修改。

而是指這類檔案通常不需要先經過 Razor 或 C# 運算,就可以直接提供給瀏覽器使用。

例如:

CSS
JavaScript
圖片
字型

這些檔案通常已經先放在專案裡。

瀏覽器需要時,就可以直接向網站要求這些資源。

例如畫面載入時,瀏覽器可能會要求:

/css/styles.css
/js/site.js
/images/logo.png
/favicon.ico

流程就是:

瀏覽器打開網頁
↓
HTML 裡面有 CSS、JavaScript 或圖片路徑
↓
瀏覽器再向伺服器索取這些檔案
↓
伺服器從 wwwroot 提供檔案
↓
畫面套用樣式與功能

所以 wwwroot 不只是「放 CSS 的資料夾」。

它比較像是:

瀏覽器可以公開取得的前端資源根目錄。

Viewswwwroot 差在哪裡?

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,當然開心地先寫了一個按鈕來測試。

先在共用版面載入 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?

我的 CSS 是先放在共用版面裡:

<link rel="stylesheet" href="~/css/styles.css" />

ASP.NET Core MVC 專案常見的共用版面檔案是:

Views/Shared/_Layout.cshtml

第一次看到 _Layout.cshtml 時,我又出現一個問號:

共用版面是什麼?

菜鳥版理解就是:

_Layout.cshtml 很像整個網站共用的外框。

例如:

  • 頁首 Header
  • 導覽列 Navbar
  • 頁尾 Footer
  • 共用 CSS
  • 共用 JavaScript

都可能放在這裡。

如果很多頁面都會使用同一份 CSS,就不用每一個 .cshtml 都重複寫一次:

<link rel="stylesheet" href="~/css/styles.css" />

只要在共用版面載入一次,其他使用這個 Layout 的 View,也能一起使用這份 CSS。

流程可以先理解成:

_Layout.cshtml 載入共用 CSS
↓
不同 View 套用 _Layout.cshtml
↓
每個畫面都能使用相同的 CSS

這個概念在 React 也有嗎?

看到「共用版面」後,我突然覺得這個概念好像有一點熟悉。

因為以前學 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 真的有成功載入?

這個藍色按鈕雖然很醜,但其實不只是隨便測試。

它可以幫我確認整條 CSS 載入流程有沒有接成功。

流程是:

_Layout.cshtml 載入 styles.css
↓
.cshtml 的按鈕使用 test-button
↓
styles.css 裡有 .test-button
↓
瀏覽器取得 styles.css
↓
按鈕變成藍底白字

如果按鈕沒有變化,我現在會依序檢查:

  1. CSS 檔案是否真的放在 wwwroot/css
  2. <link> 的路徑是否正確
  3. CSS 檔名是否拼錯
  4. .cshtmlclass 是否和 CSS 選擇器一致
  5. 檔案是否有儲存
  6. 瀏覽器是否還在使用快取
  7. 自己修改的是不是正確的 .cshtml
  8. 這個 View 是否有使用目前的 _Layout.cshtml
  9. 是否有其他 CSS 規則把樣式蓋掉

以前畫面沒變時,我可能只會一直重新整理。

現在至少會開始把問題拆開:

是 CSS 根本沒載入?
還是 View 沒有使用這個 Layout?
還是 CSS 有載入,但選擇器沒對到?
還是樣式被其他規則蓋掉?

用瀏覽器開發者工具確認 CSS

除了看畫面有沒有變,也可以用瀏覽器的開發者工具確認。

我可以在按鈕上按右鍵,選擇「檢查」,然後查看:

  • 按鈕有沒有 test-button 這個 class
  • .test-button 規則有沒有出現在 Styles
  • background-color: blue 是否被劃掉
  • CSS 檔案有沒有載入失敗
  • Network 裡的 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

  • 資料庫連線密碼
  • API 金鑰
  • 內部帳號密碼
  • 不應公開的設定檔
  • 個人或病患資料
  • 只有後端可以使用的機密檔案

因為只要網站有公開提供這些檔案,就可能被瀏覽器直接存取。

這是我以前只把 wwwroot 當成「放 CSS 的地方」時,完全沒有想到的事情。

比較完整的理解應該是:

wwwroot 是公開靜態資源的根目錄,所以只能放適合提供給前端使用的檔案。

AI 在這一篇幫了我什麼?

我原本只問:

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# 專案卻沒反應?


上一篇
Day 6|先從最熟悉的畫面開始:HTML 和 `.cshtml` 差在哪裡?
下一篇
Day 8|React 專案為什麼可以直接寫 SCSS?C# 專案卻沒反應?
系列文
學過一點 React,卻被撈進 C#:新手用 AI 硬啃 MVC 企業專案的 3 個月實錄18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言