1. 程式人生 > >顯微鏡下的webpack4:路徑操作

顯微鏡下的webpack4:路徑操作

對於打包工具來說,最簡單也是最複雜的操作莫過於路徑的安排了,原本都在src下的資源,想要打包到dist目錄下,但是打包出來的檔案路徑甚不如人意。明明想要分門別類地放置檔案檔案,然後卻像大雜燴一樣js,htmlcss甚至圖片都混在了一起。雖然打包之後雲心沒什麼問題,但是這是要逼死強迫症患者啊。

所以這篇文章就是講解如何明明白白安排各資源的路徑,無關webpack效能,無關各類騷操作,只是基礎的路徑操作。

傳統的網站一般會將資料夾分為三類,styles,scripts,images,看到這個三個資料夾就會倍感親切,有種老朋友的感覺。如果這個時候css檔案出現在了scripts中,或者更images資料夾出現在scripts中,估計寫這個網站的前端會被大家吐槽了。但是如果是webpack打包之後發生這種事,毫不驚奇,習以為常,佛系打包。每次打包都是一個驚喜,如果配置不對,打包出來的檔案可能會和你玩捉迷藏。為此我整理了下webpack打包中可能會出現的路徑問題,如下方大綱所示,如果有夥伴們遇到過此類問題可以按需查詢。

大綱:

  • js路徑問題
  • css路徑問題
  • html路徑問題
  • 圖片路徑問題(important)
    • js中的圖片引用路徑
    • css中的圖片引用路徑
    • html中的圖片引用路徑

webpack打包流程

webpack打包流程簡單來說就是把所有的資源都變成js的chunk模組,然後再對chunk們進行操作,最後再根據配置分門別類輸出。

為了能夠深入瞭解我們的檔案打包去哪兒了,我建了一個比較變態的檔案目錄。

JS生成路徑

JS在這個過程中是最好控制的,根據配置的entry和output既可以輕鬆控制來龍去脈。而這兩個配置在官方文件中也解釋地相當詳細。

像下方這樣的配置,應該比較常用,就是一個html,一個場景一個entry,然後output的時候按照entry名字生成相對應的檔案。

entry:{
    "index":path.resolve(__dirname,"src/scripts/index/index.js"),
    "list":path.resolve(__dirname,"src/scripts/list/list.js"),
},
output:{
    path:path.resolve(__dirname,"dist"),
    filename:"[name].js"
},

這裡需要注意:

  • output.path是專案的路徑,也就是之後的css,圖片等打包會按著這個path為相對路徑來生成檔案。
  • output.filename不僅僅可以命名,如果你想要js放在特定目錄下可以在這裡配置,就像這樣filename:"scripts/[name].js"
    ,這樣就會在dist目錄下生成一個scripts的目錄,JS打包之後都會生成在這個資料夾之中。

CSS生成路徑

CSS的引用,就比CSS複雜一些,不是直接HTML引用,而是import "../../styles/index/index.css"像這樣匯入,或者require到JS之中,這樣webpack才會去打包CSS到新目錄下。不過呢原生的CSS,JS是不認識的,所以這個時候需要loader幫我們編譯CSS才能匯入JS之中。

CSS的loader們

css-loader很顯然就是將CSS編譯成JS認識的語法。

style-loader最主要乾的事情就是將JS中編譯的CSS程式碼插入DOM之中,使之生效。

mini-css-extract-plugin,這個外掛最主要的目的就是將每個JS(chunk)中的CSS程式碼剝離出來,分別打包到各自命名的CSS檔案之中。(注:ExtractTextPlugin只適用於webpack3及以下。),有關CSS檔案生成路徑的問題,我們主要就是用這個外掛來實現,而這個外掛不僅要在loader的時候參與編譯CSS,還需要在打包的時候發揮作用,將CSS打包到相應的資料夾之中。

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports={
    ...
    //loader
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        publicPath: '../'
                    }
                    },
                    "css-loader"
                ]
            }
        ]
        ,
    },
    plugins: [
        //css打包
        new MiniCssExtractPlugin({
            filename: "styles/[name].css"
        })
    ]
}

上述程式碼給出了MiniCssExtractPlugin這個外掛的用法。它主要的生成配置是在filename:"styles/[name].css",這否覺得似成相識,和webpack的output.filename的配置一樣,可以將css打包至styles資料夾之下。

HTML生成路徑

HTML的打包編譯就比較特殊,一般使用html-webpack-plugin外掛,通過編寫模版來配置生成html檔案。這個外掛的功能很強大,不過這裡只提及生成路徑的配置。

const HtmlWebpackPlugin = require('html-webpack-plugin');

我是這樣配置html,匯入模版,然後生成檔案,我給filename一個絕對路徑,這樣就不用擔心檔案會生成到什麼奇怪的地方了。當然直接filename: 'index.html'給一個檔名也是可以的,這樣就會按照webpack中output配置的path,即專案目錄為物件的相對路徑。這裡的chunks: ["index"]使之html中包含的獨立,否則會將所有的資源全部潛入當前的html中。

new HtmlWebpackPlugin({
    filename: path.join(__dirname,'/dist/index.html'),
    template: path.join(__dirname,'/src/templates/index.html'),
    chunks: ["index"]
})

圖片生成路徑

這一塊應該是最相對最複雜的一塊了,不過分別分析之後也不會太負責。

images in JS

這一部分是最簡單的了,因為從JS中獲取資源最直接,不用編譯多道工序。

說到匯入檔案的地址,我們最常用的是file-loader這個loader。在outputPath之中配置檔案的生成地址。這裡我們配置了images/ ,也就是dist/images之下。useRelativePath這個選項看似人畜無害,但是配置的時候需謹慎不然多次呼叫這個loader的話,就會發生dist/images/images的情況。

{
    test: /\.(png|jpg|gif)$/,
    use: [
      {
        loader: 'file-loader',
        options: {
            useRelativePath:false,
            outputPath: 'images/',
            name: '[name].[ext]'
        }
      }
    ]
},

JS想要匯入圖片,也很簡單直接import或者require+圖片地址,即可,與CSS的呼叫一致。

images in CSS

在CSS中我們經常會用到圖片,比如background這個屬性,那麼在CSS中我們怎麼打包圖片,並且改變CSS中圖片的地址呢?因為我們CSS是被import到JS之中的,所以和JS一樣的處理方式,file-loader會幫助我們處理好圖片的問題的。不過CSS中圖片的引用地址是個問題啊。

{
    loader: MiniCssExtractPlugin.loader,
    options: {
        publicPath: '../'
    }
}

這個時候就可以利用loader中publicPath這個屬性,這個屬性不參與編譯,只在最後打包的時候替換路徑。這裡CSS中圖片的路徑是image/xxx.jpg,如果我們加上這個../,那麼就變成../image/xxx.jpg。通過這樣來控制CSS中圖片的問題,如果想改成CDN地址也是可以。

images in HTML

這一部分是最令人頭疼的file-loader不處理html中的img標籤,這裡我們會利用html-loader來處理圖片的問題,如下方配置:

{
    test: /\.html$/,
    use: [ {
    loader: 'html-loader',
    options: {
        publicPath:"./",
        attrs: ['img:src']
    }
    }],
}

這樣我們就會將html中的圖片檔案完美的打包出來啦。不過這個外掛沒有publicPath的配置,這裡的地址需要依靠output.publicPath。如果output.publicPath為空,那麼打包出來的檔案地址就是images/xxx.jpg。如果配置了output.publicPath="./",那麼打包出來的就是./images/xxx.jpg

總結

打包路徑說複雜各種相對路徑確實容易搞暈,所以這個時候需要保持清醒的頭腦。該用相對路徑的用相對。不需要的就用絕對路徑。有些外掛還是很人性化地有publicPath這個選項,還可以手動控制下。

本文所用到的loader,plugin一覽

plugin/loader usage
用於管理JS中匯入的資源或者CSS中引用的資源路徑。
用於管理HTML,提取管理引用資源,如img中的src,還可以對html進行優化,如去除所有的註釋壓縮等。
用於提取CSS,並進行分別打包,雖然有mini,但是意思不是壓縮CSS,如需壓縮還需要其他的外掛配置。
一個強大的html管理外掛,可以用於生成html,可以配置模板,靈活的配置chunk。