Webpack打包文件分類與url修正(原理講解)

在原來的文章中我們把打包的東西都存放到了dist目錄下,并沒有進行分類存儲,亂成一團,現(xiàn)在我們需要處理一下打包的路徑,讓打包后的目錄看起來更加優(yōu)雅。

代碼準備

我們先建立起這樣一個目錄結(jié)構(gòu)

├── 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

我們在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中去修改一下入口文件位置
接下來,我們?nèi)y試一下打包效果

npm run dev

所有文件都打包到dist目錄下,接下來,我們需要去優(yōu)化webpack配置,讓打包出來的文件不那么混亂

webpack配置

把js文件分類

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

output: {
    path: path.resolve(__dirname, 'dist/assets'),
        //上面這個path代表webpack默認會把所有的文件都打包進assets文件夾中
        //上面這個是一個所有打包文件輸出位置的公用路徑
    filename: 'js/my-first-webpack.bundle.js'
},

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

npm install clean-webpack-plugin --save

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

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
                }
            }]
        }
    ]
}
}

這個時候你會發(fā)現(xiàn),每次運行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'])
],

把字體文件分類

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

// 處理文字 
{
    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字符串的位數(shù),ext表示原來文件的擴展名(文件后綴)

把圖片文件分類

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

{
    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中表示的意思是一樣的

把css文件分類

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

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

這個插件的使用規(guī)則和其他plugin差不多,首先需要引入插件
webpack.config.js

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

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

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

最后,需要修改css輸出規(guī)則

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

修正url地址

先分析一下webpack.config.js中的devServer

devServer:{
    //當服務器啟動的時候,自動幫助我們打開瀏覽器
    open: true,
        //服務器啟動的端口地址
    port: 8090,
        //訪問的本地目錄是哪個
    contentBase: './dist'
}

上面所有的webpack配置好了之后,我們在終端用npm start啟動服務時,發(fā)現(xiàn)根本訪問不了dist文件,因為
1.當服務啟動的時候,CleanWebpackPlugin這個插件的配置,就會把dist目錄刪除
2.而npm start并不會再次生成dist,但是內(nèi)存中是有dist的,為什么訪問不了?
解決辦法
當服務啟動的時候,wenbpack-dev-server會把資源打包到內(nèi)存
npm start會優(yōu)先訪問內(nèi)存中output.path:path.resolve(__dirname,'dist/assets')設置的輸出路徑,而為什么訪問不了內(nèi)存中的資源?
舉例:當我們在瀏覽器中輸入http://localhost:8090/ ,訪問的是內(nèi)存中的dist/assets
當我們在瀏覽器中輸入http://localhost:8090/js/my-first-webpack.bundle.js,訪問的是內(nèi)存中的dist/assets/js/my-first-webpack.bundle.js.js
這樣的話,我們發(fā)現(xiàn),我們只可以訪問到assets目錄下的內(nèi)容,卻訪問不到和assets同級的內(nèi)容
當我們把各種類型的文件分類好以后,運行打包出來的index.html,發(fā)現(xiàn)圖片、字體、css等的路徑是不正確的,沒辦法正常工作,這個時候,我們需要用output配置項下的 publicPath來修正一下

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

其中,publicPath設置的是所有靜態(tài)資源的基礎目錄,要快速理解它,可以記住下面的公式:
靜態(tài)資源最終訪問路徑 = 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'

設置的output.publicPath為 "assets/", 那么最終的訪問路徑就是output.publicPath+loader設置的路徑,例如圖片路徑就會是 assets/img/[name]_[hash:4].[ext]
devServer.publicPath這個選項是webpack-dev-server服務器的訪問路徑,我們知道當webpack-dev-server啟動后,會把資源打包到內(nèi)存,你不需要關注它在內(nèi)存中的什么地方,你可以理解為打包后的資源也被輸出了,只是輸出到內(nèi)存中,你無法在硬盤上看到而已,但是它提供了一個地址來供我們訪問,devServer.publicPath就是用來設置訪問內(nèi)存中被打包出來的資源地址的,舉例說明:

output: {
    path: path.resolve(__dirname, 'dist/assets'),
    filename: 'js/my-first-webpack.bundle.js',
    },
devServer: {
    open: true,
    publicPath: '/aa'
},
其他配置省略...

像上面這種配置,當服務器啟動的時候,如果想訪問到內(nèi)存中的my-first-webpack.bundle.js,那么需要通過http://localhost:8090/aa/js/my-first-webpack.bundle.js
當前的完整的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/my-first-webpack.bundle.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.output.path: path.resolve(__dirname, 'dist/assets'), 這條配置是打包后資源輸出的目錄,
同樣的當webpack-dev-server啟動后,資源會打包到內(nèi)存,用不太嚴謹?shù)ㄋ椎脑拋碚f你可以認為所有文件被打包到這個目錄,只是這個目錄放在內(nèi)存而不是輸出到硬盤,你訪問這個目錄里面的文件的時候,路徑和打包到硬盤是看到的路徑一樣,例如:在硬盤上 dist/assets/js/app.js, 在內(nèi)存中也通過這種目錄結(jié)構(gòu)訪問就是了
2.output.publicPath: 'assets/',是靜態(tài)資源的基礎路徑
3.devServer.publicPath: '/aa', 這個配置是服務器訪問內(nèi)存的虛擬路徑, http://localhost:8080/aa 這個地址,你可以認為是映射到了output.path設置的目錄,不太嚴謹?shù)ㄋ椎恼f,你可以認為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.devServer.contentBase設置的是本地目錄(在內(nèi)存中找不到的時候去硬盤上找)
5.filename: '../index.html',這個在HtmlWepackPlugin中的配置,設置了被打包出來的html位置,../表示往上一層返回,那就意味著,最終打包出來的結(jié)構(gòu)是這樣的:

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

我們通過http://localhost:8080/aa (devServer.publicPath屬性定義的映射)能訪問到dist/assets目錄,也可以訪問該目錄下層級更深的文件,只需要在http://localhost:8080/aa 后面加文件路徑就可以了,但是卻永遠無法返回到上層目錄去訪問到index.html
當訪問不到內(nèi)存中的index.html?件,webpack-dev-server就會去訪問本地的路徑,這個本地的路徑就是devServer.containtBase: 'src/'這個選項設置的,所以npm start的時候,直接打開的是項目下的src目錄
好了,問題原因已經(jīng)找出來了,接下來我們需要修改以下webpack配置,具體的思路就是

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

這樣做,原來打包輸出的目錄變成了dist目錄,而不是原來的dist/assets目錄, 那么http://localhost:8090/aa 這個地址指向的內(nèi)存虛擬目錄就是 dist目錄,那么通過http://localhost:8090/aa/index.html 就可以訪問到內(nèi)容了
具體完整配置如下:

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/my-first-webpack.bundle.js',
        publicPath: '/'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: './index.html'
        }),
        new CleanWebpackPlugin(['dist'])
    ],
    devServer: {
        open: true,
        publicPath: '/'
    },
    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:8090/ 來訪問打包出來的index.html文件了。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 寫在開頭 先說說為什么要寫這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,452評論 4 31
  • 你真的知道publicPath的用處嗎?無論是官方文檔,還是水友的解釋都很難讓豁然開朗,比如: 對于按需加載(on...
    九丘建木閱讀 15,392評論 4 16
  • 原文首發(fā)于:Webpack 3,從入門到放棄 Update (2017.8.27) : 關于 output.pub...
    昵稱都被用完了衰閱讀 2,003評論 4 19
  • 記得2004年的時候,互聯(lián)網(wǎng)開發(fā)就是做網(wǎng)頁,那時也沒有前端和后端的區(qū)分,有時一個網(wǎng)站就是一些純靜態(tài)的html,通過...
    陽陽陽一堆陽閱讀 3,470評論 0 5
  • 最近在用樹莓派的時候,發(fā)現(xiàn)接入小的低音炮音響時候有嘶嘶的電流聲音。并且在樹莓派有數(shù)據(jù)交換的時候尤其的響。以下...
    braveheart1991閱讀 3,190評論 0 0

友情鏈接更多精彩內(nèi)容