添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

前言

上一次在介紹 mini-css-extract-plugin 時,有提到關於使用 background-image:url() 以相對路徑參考本地圖片時所發生的錯誤,最後是使用 file-loader 解決此問題;簡單來說,file-loader 就是用來處理一般開發網頁時所使用的靜態資源,例如:字形、圖片等等,將所有資源載入到 Webpack 內,並且解析資源的相互依賴,最後以配置的選項生成對應的結果;而 url-loader 則類似於 file-loader,可依資源的大小做對應的處理。此篇將介紹 file-loader 與 url-loader 的使用方法,以及兩者在應用時最大的差別為何。

  • url-loader 與 file-loader 安裝
  • url-loader 與 file-loader 基本使用
  • url-loader 與 file-loader 可傳遞選項
  • 補充:url-loader 與 file-loader 實際應用
  • 補充:file-loader 載入本地字體
  • url-loader 與 file-loader 安裝

    套件連結: url-loader file-loader

    主要的套件:

    1
    npm install url-loader file-loader -D

    package.json:

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "devDependencies": {
    "file-loader": "^6.0.0",
    "url-loader": "^4.0.0",
    "webpack": "^4.42.1",
    "webpack-cli": "^3.3.11"
    }
    }

    在前面章節,我們會先以 file-loader 做示範,直到關於 base64 一詞的出現,才會使用到 url-loader,請先將兩個 loader 進行安裝。

    file-loader 與 url-loader 基本使用

    初始專案結構:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    webpack-demo/

    ├─── node_modules/
    ├─── src/
    │ │
    │ └─── img/
    │ │
    │ └─── test.png # 測試用圖片
    │ │
    │ └─── main.js # entry 入口檔案

    ├─── webpack.config.js # Webpack 配置檔案
    ├─── package-lock.json
    └─── package.json

    配置 webpack.config.js 檔案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const path = require('path');

    module.exports = {
    entry: './src/main.js',
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    },
    module: {
    rules: [
    {
    test: /\.(png|jpe?g|gif)$/i,
    use: [
    // 配置 loader (第一步)
    {
    loader: 'file-loader',
    },
    ],
    },
    ],
    },
    };

    配置 file-loader 相對簡單,這邊要注意的是,目前我們的 Regex 只有單純篩選圖片的相關檔案,並未包含其他靜態資源。

    entry 入口處 ( src/main.js ) 引入圖片檔案:

    1
    import './img/test.png';

    執行編譯指令:

    1
    npm run build

    此時會生成名稱為 hash 值的圖片檔案:

    1
    2
    3
    4
    5
    6
     webpack-demo/

    ├─── dist/
    │ │
    +│ ├─── 4664caca877b29c20cf1cc536e41911e.png
    │ └─── bundle.js

    相信有閱讀過以前 Webpack 文章的朋友已經發現其中的問題,那就是我們還沒有配置 filename ,導致預設名稱為 hash 值,這邊要注意,file-loader 並沒有 filename 這個屬性,取而代之的是 name 屬性,讓我們趕緊來配置它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(png|jpe?g|gif)$/i,
    use: [
    // 配置 loader (第一步)
    {
    loader: 'file-loader',
    options: {
    // 配置 name 屬性 (第二步)
    name: '[name].[ext]',
    },
    },
    ],
    },
    ],
    },
    };

    在 file-loader 中的 name 屬性就類似於其他 loader 或 plugin 的 filename 屬性,不同的地方在於, name 屬性得依照官方文件中的 Placeholders 配置才行,上面這個配置就是最基本的依照 entry 檔案的名稱以及附檔名進行 output。

    切記先將 dist 資料夾完全刪除,以保證最新的編譯結果如同預期,之後也會介紹使用 clean-webpack-plugin 解決此問題

    再次執行編譯指令:

    1
    npm run build

    此時會生成與 entry 檔案名稱相同的圖片檔案:

    1
    2
    3
    4
    5
    6
     webpack-demo/

    ├─── dist/
    │ │
    +│ ├─── test.png
    │ └─── bundle.js

    以上就是 file-loader 的基本使用,可能有人會問,那 url-loader 呢?神奇的事情要發生了!讓我們直接將上面的 webpack.config.js 中的 file-loader 更改為 url-loader,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(png|jpe?g|gif)$/i,
    use: [
    {
    loader: 'url-loader', // 將 file-loader 更改為 url-loader
    options: {
    name: '[name].[ext]',
    },
    },
    ],
    },
    ],
    },
    };

    此時如果你執行 npm run build 進行編譯,結果會與 file-loader 一模一樣,有沒有很神奇?事實上,url-loader 預設提供了一個名為 fallback 的選項,用以調用超過文件大小的程序,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(png|jpg|gif)$/i,
    use: [
    {
    loader: 'url-loader',
    options: {
    // 超過大小時調用 file-loader 處理該檔案
    fallback: require.resolve('file-loader'),
    },
    },
    ],
    },
    ],
    },
    };

    url-loader 中的 options 選項是與 file-loader 共用的 ,差別在於 url-loader 新增了 limit 選項,用以設置可轉為 base64 的文件大小上限,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(png|jpe?g|gif)$/i,
    use: [
    {
    loader: 'url-loader',
    options: {
    name: '[name].[ext]',
    limit: 8192 , // 用以限制須轉為 base64 的文件大小 (單位:byte)
    },
    },
    ],
    },
    ],
    },
    };

    url-loader 唯一的功能就在於將資源轉換為 base64 的格式,主要依靠 limit 控制需轉換的文件,轉為 base64 的好處就在於,往後網頁在渲染圖片時,不需要以 request 的方式加載圖片,直接向 JavaScript 檔案拿取即可,這樣子以效能來說,會提高許多,但你也不能把大小的上限設定太高,由於 base64 是存在於 bundle.js 內,這樣子的做法會導致 JavaScript 異常的肥大,對於效能來說反而會下降,衡量並設置適當的大小才是正確的作法。

    再次執行編譯指令:

    1
    npm run build

    通過 url-loader 的文件將轉成 base64 存在於 bundle.js 內:

    1
    2
    3
    4
    5
    webpack-demo/

    ├─── dist/
    │ │
    │ └─── bundle.js # 圖片轉為 base64 存在於 JavaScript 檔案內

    關於 base64 的實際應用將在下面補充,讓我們來做個總結:

    file-loader 用以將靜態資源載入到 Webpack 內,並且解析資源的相互依賴關係,最後 output 到指定的位置,而 url-loader 用以將指定大小上限內的圖片資源轉換為 base64 格式,如遇到超過上限的資源,將 fallback 給 file-loader 做處理,兩者功能並沒有衝突,由於處理對象相同,導致很多人會搞混,通常兩個 loader 都是一起使用居多,並且直接設置 url-loader 即可

    url-loader 與 file-loader 可傳遞選項

    可參考 url-loader Options 可傳遞參數列表,以下為常用的參數配置:

  • limit: Number | Boolean | String
    限制可轉為 base64 的檔案大小上限,單位為 byte,默認為 underfined

  • fallback: String
    指定當文件大小超過 limit 限制時,需轉向的加載程序,默認為 fiel-loader

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(png|jpg|gif)$/i,
    use: [
    {
    loader: 'url-loader',
    options: {
    // 用以限制須轉為 base64 的文件大小 (單位:byte)
    limit: 8192,
    // 超過大小及調用 file-loader
    fallback: require.resolve('file-loader'),
    },
    },
    ],
    },
    ],
    },
    };

    可參考 file-loader Options 可傳遞參數列表,以下為常用的參數配置:

  • name: String | Function
    設置 output 時的文件名稱,相關參數可參考 Placeholders ,默認為 [contenthash].[ext]

  • outputPath: String | Function
    指定目標文件的公共路徑,在 mini-css-extract-plugin 文章有介紹到,默認為 underfined

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(png|jpg|gif)$/i,
    use: [
    {
    loader: 'file-loader',
    options: {
    // 設置 output 時的檔案名稱
    name: 'img/[name].[ext]',
    // 修改公共路徑
    publicPath: '../',
    },
    },
    ],
    },
    ],
    },
    };

    補充:url-loader 與 file-loader 實際應用

    前面講解到了 url-loader 與 file-loader 的基本使用方式,這次讓我們帶入到實際應用內,加深各位對這兩個 loader 的印象。

    套件連結: url-loader file-loader css-loader mini-css-extract-plugin

    主要的套件:

    1
    npm install url-loader file-loader -D

    過程會使用的套件:

    1
    npm install css-loader mini-css-extract-plugin -D

    初始專案結構:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    webpack-demo/

    ├─── node_modules/
    ├─── src/
    │ │
    │ └─── img/
    │ │
    │ ├─── banner.jpg # Size >= 10 KB
    │ └─── logo.jpg # Size < 10 KB
    │ │
    │ └─── css/
    │ │
    │ └─── all.css # CSS 主檔案
    │ │
    │ └─── main.js # entry 入口檔案

    ├─── index.html # 引入 bundle.js 與 main.css 測試用檔案
    ├─── webpack.config.js # Webpack 配置檔案
    ├─── package-lock.json
    └─── package.json

    撰寫 CSS 範例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    .w-100-h-100 {
    width: 100px;
    height: 100px;
    }

    .bg-cover {
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
    }

    .banner {
    background-image: url('../img/banner.jpg');
    }

    .logo {
    background-image: url('../img/logo.png');
    }

    配置 webpack.config.js 檔案:

    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
    const path = require('path');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');

    module.exports = {
    entry: './src/main.js',
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/bundle.js',
    },
    module: {
    rules: [
    {
    test: /\.css$/i,
    use: [
    {
    loader: MiniCssExtractPlugin.loader,
    // 由於 CSS 增加了一層的結構,相對的 publicPath 也需增加一層
    options: {
    publicPath: '../',
    },
    },
    'css-loader',
    ],
    },
    {
    test: /\.(png|jpe?g|gif)$/i,
    use: [
    {
    // 直接配置 url-loader 就好,超過上限的資源會自動 fallback 給 file-loader
    loader: 'url-loader',
    options: {
    name: 'img/[name].[ext]',
    limit: 10000,
    },
    },
    ],
    },
    ],
    },
    plugins: [
    new MiniCssExtractPlugin({
    filename: 'css/[name].css',
    }),
    ],
    };

    這邊要特別注意!並不是 file-loader 與 url-loader 都要進行配置,直接配置 url-loader 就等同於配置了 file-loader, limit 內的會交由 url-loader 處理,超過 limit 的資源則會 fallback 給 file-loader 進行處理。

    Webpack 會自動解析 CSS 內的參考圖檔,將它抓出來以 require 的方式處理,除非有特定資源需要透過 file-loader 處理,不然不需要另外的 import 這些 CSS 所用的圖檔

    entry 入口處 ( src/main.js ) 引入 CSS 檔案:

    1
    import './css/all.css';

    package.json 新增編譯指令:

    1
    2
    3
    4
    5
    {
    "scripts": {
    "build": "webpack --mode development"
    }
    }

    執行編譯指令:

    1
    npm run build

    ./index.html 引入打包而成的 bundle.js main.css 檔案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!-- 其他省略 -->
    <head>
    <!-- 引入打包生成的 CSS -->
    <link rel="stylesheet" href="dist/css/main.css" />
    </head>
    <body>
    <div class="banner w-100-h-100 bg-cover"></div>
    <div class="logo w-100-h-100 bg-cover"></div>
    <!-- 引入打包生成的 JavaScript -->
    <script src="dist/js/bundle.js"></script>
    </body>

    查看結果:

    從上面結果可以得知,logo.png 圖檔已被轉換成 base64 格式,而 banner.png 這張較大的圖檔,被 url-loader fallback 給 file-loader 處理,最後就只是在配置的指定位置生成而已。

    補充:file-loader 載入本地字體

    有時我們在開發網頁時,會需要使用一些特殊字體,像我本身就很常到 Google Fonts 拉一些字體出來用,不僅可以增加網頁整體的質感,還可以擺脫傳統字體的呆板樣式。

    而外部字體的載入方式有很多種,包含一般最為常見的 CSS link,或是使用 @import 方式載入字體,我個人是偏好使用 @font-face 來載入字體,將字體給下載下來,提供較為穩定的載入字體方法。

    先前介紹了以 file-loader 或 url-loader 來處理圖片等靜態資源,此章節將介紹如何以 file-loader 處理 .ttf .otf 等字體資源,讓我們直接開始吧!

    請先至 Google Fonts 隨意下載字體,並放置在 src/font

    1
    2
    3
    4
    5
    6
    7
     webpack-demo/

    ├─── src/
    │ │
    +│ └─── font/
    +│ │
    +│ └─── NotoSansTC-Regular.otf

    @font-face 載入本地字體:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @font-face {
    font-family: 'NotoSansTC';
    src: url('../font/NotoSansTC-Regular.otf') format('opentype');
    }

    p {
    font-family: 'NotoSansTC';
    font-size: 40px;
    }

    配置 webpack.config.js 檔案:

    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
    50
    51
    52
    53
    54
    55
    56
    const path = require('path');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');

    module.exports = {
    entry: './src/main.js',
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/bundle.js',
    },
    module: {
    rules: [
    {
    test: /\.css$/i,
    use: [
    {
    loader: MiniCssExtractPlugin.loader,
    options: {
    publicPath: '../',
    },
    },
    'css-loader',
    ],
    },
    // 處理 require("font")
    {
    test: /\.(woff|woff2|eot|ttf|otf|)$/,
    use: [
    {
    loader: 'file-loader',
    options: {
    name: 'font/[name].[ext]',
    },
    },
    ],
    },
    // 處理 require("image")
    {
    test: /\.(png|jpe?g|gif)$/i,
    use: [
    {
    loader: 'url-loader',
    options: {
    name: 'img/[name].[ext]',
    limit: 10000,
    },
    },
    ],
    },
    ],
    },
    plugins: [
    new MiniCssExtractPlugin({
    filename: 'css/[name].css',
    }),
    ],
    };

    這邊要注意,如果你打算將圖片、文字打包後放置在同一個路徑下,可以不必另外寫一個 Regex 去處理, 上面這種寫法,主要是將圖片與文字放置在不同的資料夾 ,千萬要記得,Webpack 會將 CSS 內的相關路徑參考語法轉換為 require 的方式進行處理,並不是說 file-loader 的配置只能在 url-loader 區塊內配置,Webpack 是以 Regex 配對相關的 use,千萬不要搞混了!

    執行編譯:

    1
    npm run build

    此時 src 資料夾內的 font 也通通打包進來了,以下為打包後的 dist 資料夾專案結構:

    1
    2
    3
    4
    5
    6
    7
    8
    webpack-demo/

    ├─── dist/
    │ │
    │ └─── font/
    │ │
    │ └─── NotoSansTC-Regular.otf
    // 其他省略

    觀察打包生成的 CSS 檔案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @font-face {
    font-family: 'NotoSansTC';
    src: url(../font/NotoSansTC-Regular.otf) format('opentype');
    }

    p {
    font-family: 'NotoSansTC';
    font-size: 40px;
    }

    從上面結果可以得知,CSS 內的 @font-face 連結也是正確的,直接打開網頁即可看到字體已被更改,此時我們打包字體的目的也就成功了。

    有人可能會問,font 字體可以使用 url-loader 處理嗎?答案是可以的,但非常不建議這樣做,英文字體少說 150 KB 起跳,而中文字體則是 5MB 起跳,對於網頁的效能來說,會有非常大的影響,這也是之前提到的 url-loader 中 limit 選項需要自己去衡量的原因,一般來說 8KB 左右就是極限了,超過的檔案就都建議以 file-loader 進行處理,各位可以自己試試看。

  •