1. 程式人生 > >webpack最佳入門實踐系列(09)

webpack最佳入門實踐系列(09)

ase 公式 svg 規則 exclude module 容易 light url

9.路徑相關

原來我們打包的東西都存放到了dist目錄下,並沒有進行分類存儲,亂成一團,這一節我們就要處理一下打包的路徑,讓打包後的目錄看起來更加優雅

9.1.代碼準備

我們先建立起這樣一個目錄結構

.
├── node_modules
├── src
|   ├── assets
|       └── css
|           └── index.css
|       └── img
|            └── noding.jpg
|       └── js
|            └── index.js
|   └── index.html
├── .babelrc
├── package-lock.json
├── package.json
├── webpack.config.js
└── dist

  

你只需要把前面我們寫的代碼copy一份,然後刪除掉dist裏面的東西並且在src下新建assets文件夾來裝css和img就可以了

我們在src/index.html中輸入一段代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="box"></div>
    <i class="fa fa-bath" aria-hidden="true"></i>
    <i class="fa fa-envelope-open" aria-hidden="true"></i>
    <i class="fa fa-microchip" aria-hidden="true"></i>
    <i class="fa fa-user-circle-o" aria-hidden="true"></i>
</body>
</html>

  

接下來在assets/js/index.js中輸入下面代碼

// 引入icon圖標字體
import "font-awesome/css/font-awesome.css"

// 引入圖片

import imgSrc from ‘../img/nodeing.jpg‘

// 把圖片插入到html網頁中

document.getElementById("box").innerHTML = ‘<img src="‘+imgSrc+‘" />‘

  

然後去webpack.config.js中去修改一下入口文件位置

接下來,我們去測試一下打包效果

npm run dev

  

所有文件都打包到dist目錄下,到此,我們這小節準備工作已經完成,接下來,我們需要去優化webpack配置,讓打包出來的文件不那麽混亂

9.2.webpack配置

9.2.1.把js文件分類

我們希望打包出來的文件也像src目錄一樣能夠分門別類的存放,首先,我們把js單獨放進assets/js文件夾下,這時我們需要修改webpack配置文件,把輸出目錄改掉

output: {
    path: path.resolve(__dirname, ‘dist/assets‘),
    filename: ‘js/app.js‘
},

  

運行npm run dev 查看效果,發現dist目錄下多出了一個assets文件,並且已經建立好了js文件夾,app.js也被放進來了,但是,原來打包出來的那些文件還在,這時候,dist目錄就更亂了,我們希望每次打包生成的文件都是最新的,得手動去刪除上次打包出來的文件,像這種手動刪除dist目錄的操作,我們可以交給webpack相關插件來完成,這個插件叫clean-webpack-plugin

安裝clean-webpack-plugin插件

npm install clean-webpack-plugin --save

  

修改webpack配置文件,引入clean-webpack-plugin插件

const path = require("path")


const HtmlWebpackPlugin = require("html-webpack-plugin")
// 引入clean-webpack-plugin插件
const CleanWebpackPlugin = require("clean-webpack-plugin")

module.exports = {
    entry: "./src/assets/js/index.js",
    output: {
        path: path.resolve(__dirname, ‘dist/assets‘),
        filename: ‘js/app.js‘
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: ‘./src/index.html‘,
            filename: ‘index.html‘
        }),
        // 使用插件,設置需要清除的目錄
        new CleanWebpackPlugin([‘dist‘])
    ],
    devServer: {
        open: true
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use:[‘babel-loader‘],
                exclude: path.resolve(__dirname, ‘node_modules‘)
            },
            {
                test: /\.css$/,
                use: [‘style-loader‘, ‘css-loader‘]
            },
            // 處理文字
            {
                test: /\.(eot|svg|ttf|woff|woff2)$/,
                use: ‘file-loader‘
            },
            {
                test: /\.(jpg|png|gif|webp|bmp)$/,
                use: [{
                    loader: ‘url-loader‘,
                    options: {
                        limit: 10240
                    }
                }]
            }
        ]
    }

}

  

這個時候你會發現,每次運行npm run dev,這個插件就會先把上次的dist目錄刪除,然後再新建一個dist目錄,把新打包的文件放裏面

以上步驟,我們把js文件放到了assets/js目錄下,但隨之而來的就是,所有的文件都被打包到assets這個目錄下了,我們不希望index.html這個文件也被打包進assets目錄下,而是像在src目錄下一樣,它們應該是同級的,所以我們需要修改一下 index.html被輸出的路徑

plugins: [
    new HtmlWebpackPlugin({
        template: ‘./src/index.html‘,
        // 這裏表示往外回退一層
        filename: ‘../index.html‘
    }),
    new CleanWebpackPlugin([‘dist‘])
],

  

9.2.2.把字體文件分類

修改webpack配置文件,給字體文件增加輸出規則

 // 處理文字
{
    test: /\.(eot|svg|ttf|woff|woff2)$/,
    use: [{
        loader: "file-loader",
        options: {
            name: ‘fonts/[name]_[hash:4].[ext]‘
        }
    }]
},

  

其中,[name]、[hash]、[ext]都是可變的,name表示原來的文件名字,hash表示生成的hash值,hash:4表示可以限定hash字符串的位數,ext表示原來文件的擴展名(文件後綴)

9.2.3.把圖片文件分類

修改webpack配置文件,給圖片文件增加輸出規則

 {
    test: /\.(jpg|png|gif|webp|bmp)$/,
    use: [{
        loader: ‘url-loader‘,
        options: {
            limit: 10240,
            name: ‘img/[name]_[hash:4].[ext]‘
        }
    }]
}

  

因為,ulr-loader是對file-loader的封裝,其中的[name]、[hash]、[ext]表示的意思和file-loader中表示的意思是一樣的

9.2.4.把css文件分類

css文件是打包進js文件的,如果需要單獨打包出來,需要安裝另一個插件,extract-text-webpack-plugin

npm install extract-text-webpack-plugin --save-dev

  

這個插件的使用規則和其他plugin差不多,首先需要引入插件

const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin")

  

其次,是需要實例化對象,並且傳入配置項,然後將實例化出來的這個對象加入到plugins選項中

const extractcss = new ExtractTextWebpackPlugin({
    //打包輸出的路徑
    filename:  ‘assets/css/index.css‘
})
plugins: [
        new HtmlWebpackPlugin({
            template: ‘./src/index.html‘,
            filename: ‘./index.html‘
        }),
        new CleanWebpackPlugin([‘dist‘]),
        // 這裏是前面實例化得到的對象
        extractcss
    ],

  

最後,需要修改css輸出規則

{
    test: /\.css$/,
    use: extractcss.extract({
        fallback:‘style-loader‘,
        use:[‘css-loader‘]
    })
},

  

9.2.5.修正url地址

當我們把各種類型的文件分類好以後,運行打包出來的index.html,發現圖片、字體、css等的路徑是不正確的,沒辦法正常工作,這個時候,我們需要用output配置項下的 publicPath來修正一下

 output: {
        path: path.resolve(__dirname, ‘dist/assets‘),
        filename: ‘js/app.js‘,
        publicPath: ‘assets/‘
    },

  

其中,publicPath設置的是所有靜態資源的基礎目錄,要快速理解它,可以記住下面的公式

靜態資源最終訪問路徑 = output.publicPath + 資源loader或插件等配置路徑

舉例說明,我們在各種loader中設置的路徑如下:

// css輸出路徑
name: ‘css/[name]_[hash:4].[ext]‘
// 圖片輸出路徑
name: ‘img/[name]_[hash:4].[ext]‘
// 字體文件輸出路徑
name: ‘fonts/[name]_[hash:4].[ext]‘
// js文件輸出路徑
filename: ‘js/app.js‘

  

設置的publicPath為 "assets/", 那麽最終的訪問路徑就是 publicPath/loader設置的路徑,例如圖片路徑就會是 assets/img/[name]_[hash:4].[ext]

總結來說就是最終的url訪問路徑是拿publicPath+loader路徑相拼接的

接下來,我們npm start啟動我們的項目,發現最終訪問的是項目的根目錄,而並沒有去訪問打包出來的那個index.html文件,在找出沒有訪問到index.html文件的原因之前,我們先來講清楚webpack中幾個非常容易混淆的路徑問題

1.output.path

2.output.publicPath

3.devServer.publicPath

4.devServer.contentBase

output.path 是靜態資源的輸出目錄,打包後的資源都放到這裏

output.publicPath是靜態資源的基礎目錄,設置這個目錄後,最終的靜態資源訪問路徑前面都會拼接上這個選項設置的值,這也方便我們單獨設置靜態資源服務器地址,例如:靜態資源服務器地址是http://static.nodeing.com, 設置了這個值,最終訪問圖片的地址就會是 http://static.nodeing.com/assets/img/nodeing.com 這種形式,因此,這也是output.publicPath這個選項和後面devServer.publicPath比較大的區別,前者是可以設置http網址的

devServer.publicPath這個選項是webpack-dev-server服務器的訪問路徑,我們知道當webpack-dev-server啟動後,會把資源打包到內存,你不需要關註它在內存中的什麽地方,你可以理解為打包後的資源也被輸出了,只是輸出到內存中,你無法在硬盤上看見而已,但是它提供了一個地址來供我們訪問,devServer.publicPath就是用來設置訪問內存中被打包出來的資源地址的,舉例說明:

output: {
        path: path.resolve(__dirname, ‘dist/assets‘),
        filename: ‘js/app.js‘,
    },
devServer: {
        open: true, 
        publicPath: ‘/aa‘
    },
    其他配置省略...

  

像上面這種配置,當服務器啟動的時候,如果想訪問到內存中的app.js,那麽需要通過http://localhost:8080/aa/js/app.js

回到開始的問題,npm start啟動服務器後,無法訪問到打包出來的index.html? 我們來找找具體的原因,說明一下幾種路徑的問題

當前的完整的webpack配置是這樣的:

const path = require("path")


const HtmlWebpackPlugin = require("html-webpack-plugin")

const CleanWebpackPlugin = require("clean-webpack-plugin")

module.exports = {
    entry: "./src/assets/js/index.js",
    output: {
        path: path.resolve(__dirname, ‘dist/assets‘),
        filename: ‘js/app.js‘,
        publicPath: ‘assets/‘
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: ‘./src/index.html‘,
            filename: ‘../index.html‘
        }),
        new CleanWebpackPlugin([‘dist‘])
    ],
    devServer: {
        open: true, 
        containtBase: ‘src/‘
        publicPath: ‘/aa‘
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use:[‘babel-loader‘],
                exclude: path.resolve(__dirname, ‘node_modules‘)
            },
            {
                test: /\.css$/,
                use: [
                    ‘style-loader‘,
                    {
                        loader: ‘css-loader‘,
                        options: {
                            name: ‘css/[name]_[hash:4].[ext]‘
                        }
                    }
                ]
            },
            // 處理文字
            {
                test: /\.(eot|svg|ttf|woff|woff2)$/,
                use: [{
                    loader: "file-loader",
                    options: {
                        name: ‘fonts/[name]_[hash:4].[ext]‘
                    }
                }]
            },
            {
                test: /\.(jpg|png|gif|webp|bmp)$/,
                use: [{
                    loader: ‘url-loader‘,
                    options: {
                        limit: 10240,
                        name: ‘img/[name].[ext]‘
                    }
                }]
            }
        ]
    }

}

  

我們來解讀幾條關鍵的配置:

1.path: path.resolve(__dirname, ‘dist/assets‘), 這條配置是打包後資源輸出的目錄,同樣的當webpack-dev-server啟動後,資源會打包到內存,用不太嚴謹但通俗的話來說你可以認為所有文件被打包到這個目錄,只是這個目錄放在內存而不是輸出到硬盤,你訪問這個目錄裏面的文件的時候,路徑和打包到硬盤是看到的路徑一樣,例如:在硬盤上 dist/assets/js/app.js, 在內存中也通過這種目錄結構訪問就是了

2.publicPath: ‘assets/‘,這個配置,前面我們也講到了,是靜態資源的基礎路徑,可以返回去再閱讀一下

3.publicPath: ‘/aa‘, 這個配置是服務器訪問內存的虛擬路徑, http://localhost:8080/aa 這個地址,你可以認為是映射到了output.path設置的目錄,不太嚴謹但通俗的說,你可以認為http://localhost:8080/aa 這個地址指向了這個目錄(dist/assets/),那麽你在http://localhost:8080/aa後面加 /js/app.js 就相當於訪問 dist/assets/js/app.js,所以啟動以後通過http://localhost:8080/aa/js/app.js是可以訪問到這個js文件的

4.filename: ‘../index.html‘,這個在HtmlWepackPlugin中的配置,設置了被打包出來的html位置,../表示往上一層返回,那就意味著,最終打包出來的結構是這樣的:

├── dist
|   ├── assets
|       ├── css
|       ├── img
|       ├── js
|   ├── index.html
|

  

我們通過http://localhost:8080/aa 能訪問到dist/assets目錄,也可以訪問該目錄下層級更深的文件,只需要在http://localhost:8080/aa 後面加文件路徑就可以了,但是卻永遠無法返回到上層目錄去訪問到 index.html

當訪問不到內存中的index.html文件,webpack-dev-server就會去訪問本地的路徑,這個本地的路徑就是containtBase: ‘src/‘這個選項設置的,所以npm start的時候,直接打開的是項目下的src目錄

好了,問題原因已經找出來了,接下來我們需要修改以下webpack配置,具體的思路就是

1. 輸出目錄往上挪動一層    output.path 設置成 path.resolve(__dirname, ‘dist‘),
2. 在每個loader上加深一層  js加深一層是這樣的 assets/js/app.js  圖片加深一層是這樣的  assets/img/...,其他,同理
3. ../index.html 改為當前目錄  ./index.html
4. output.publicPath的值設置來和devServer.publickPath的值一樣,不然靜態資源的訪問地址會出錯,返回404

這樣做,原來打包輸出的目錄變成了dist目錄,而不是原來的dist/assets目錄, 那麽http://localhost:8080/aa 這個地址指向的內存虛擬目錄就是 dist目錄,那麽通過http://localhost:8080/aa/index.html 就可以訪問到內容了

具體完整配置如下:

const path = require("path")


const HtmlWebpackPlugin = require("html-webpack-plugin")

const CleanWebpackPlugin = require("clean-webpack-plugin")

module.exports = {
    entry: "./src/assets/js/index.js",
    output: {
        path: path.resolve(__dirname, ‘dist‘),
        filename: ‘assets/js/app.js‘,
        publicPath: ‘/aa/‘
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: ‘./src/index.html‘,
            filename: ‘./index.html‘
        }),
        new CleanWebpackPlugin([‘dist‘])
    ],
    devServer: {
        open: true, 
        publicPath: ‘/aa‘
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use:[‘babel-loader‘],
                exclude: path.resolve(__dirname, ‘node_modules‘)
            },
            {
                test: /\.css$/,
                use: [
                    ‘style-loader‘,
                    {
                        loader: ‘css-loader‘,
                        options: {
                            name: ‘assets/css/[name]_[hash:4].[ext]‘
                        }
                    }
                ]
            },
            // 處理文字
            {
                test: /\.(eot|svg|ttf|woff|woff2)$/,
                use: [{
                    loader: "file-loader",
                    options: {
                        name: ‘assets/fonts/[name]_[hash:4].[ext]‘
                    }
                }]
            },
            {
                test: /\.(jpg|png|gif|webp|bmp)$/,
                use: [{
                    loader: ‘url-loader‘,
                    options: {
                        limit: 10240,
                        name: ‘assets/img/[name].[ext]‘
                    }
                }]
            }
        ]
    }

}

  

通常output.publicPath和devServer.publicPath設置成 "/",這樣我們就可以通過 http://localhost:8080/ 來訪問打包出來的index文件了,自己動手改改,試試效果吧!!

webpack最佳入門實踐系列(09)