一次webpack體驗

目錄結構

project
        - css
            - bootstrap.min.css
            - jb.css
        - fonts
            - 一些bootstrap的字體
        - images
            - 一些項目用到的圖片
        - js
            - bootstrap.min.js
            - jquery.min.js
            - jb.js
        - index.html
        - favicon.ico複製代碼

項目背景

這個是公司要作的一個官方網站。因爲項目比較簡單,要求是單頁的,沒有頁面跳轉,因此只有一個 .html 文件。項目用了比較常規的 bootstrap + jquery 的開發,這個也沒啥好說的。考慮到 CDN 的可控性,因此把全部 bootstrap 的資源都下載到了本地進行引用。項目開始時是用了常規的 js 和 css 引用(css 放前面,js 放後面),在開發完成後,發現有時間多餘,就考慮用 webpack 對他進行處理一下,以鞏固和學習一下 webpack 所用到的知識css

初始化工做

1. 初始化

npm init

先初始化一個 package.json 文件來管理咱們 webpack 所依賴的文件包。一路無腦回車便可。複製代碼

2. 安裝 webpack

想要用 webpack ,那麼你首先確定要安裝 webpack 才能夠啊。用如下命令:

    npm install webpack --save複製代碼

3. webpack.config.js

在根目錄下新建一個 webpack.config.js 文件,用來對 webpack 進行配置。
當有這個文件後,咱們系能夠在命令行中用如下命令來啓動配置好的 webpack 了。

    webpack --config webpack.config.js複製代碼

4. 修改 webpack 打包命令

以上雖然也能夠啓動配置好的 webpack。可是每次要輸這麼一串命令好像有點太長了(懶啊)。因此在 package.json 中修改這樣一項:

    "script": {

      + "start": "webpack --config webpack.config.js",

        "test": "echo \"Error: no test specified\" && exit 1"
    }複製代碼

這樣的話咱們就能夠在命令行中少敲幾個鍵盤了。直接用如下命令,就等同於上面的命令了:html

npm start複製代碼

好了,到這裏,初始化工做就作完了,那麼開始咱們的 webpack 配置吧node

webpack.config.js

1. 哪裏來的入口文件?

什麼事入口文件

webpack 建立應用程序全部依賴的關係圖(dependency graph)。圖的起點被稱之爲入口起點(entry point)。入口起點告訴 webpack 從哪裏開始,並根據依賴關係圖肯定須要打包的內容。能夠將應用程序的入口起點認爲是根上下文(contextual root) 或 app 第一個啓動文件jquery

跟其餘的 spa 應用不同,這樣的普通應用,其全部依賴都來自於 index.html 文件。若是說要有入口文件的話,怎麼也應該是 index.html 他自己吧。可是 webpack
基本都是用 .js 文件做爲入口文件,用 .html 做爲入口文件的...(反正我是沒有見到過)。至於這裏有啥緣由的話,你們就參考一下這篇文章吧。(其實我也不懂)webpack

因此說,無論怎麼樣,咱們都須要有個入口文件。git

那就無論怎麼樣,咱們常常看到的 webpack 的配置都是這樣的github

module.exports = {
        entry: './index.js'
    }複製代碼

那麼咱們無論三七二十一,先在根目錄下新建一個 index.js,而後讓他做爲咱們的入口文件。web

index.js

要知道,對於咱們原來的項目而言,咱們根本就是不須要這麼一個 index.js 文件的(沒有他,咱們能夠活得更好)。可是咱們又不得不建立了這樣一個文件。那麼問題來了,這麼建立出來的文件,裏面又該放什麼內容呢?咱們總不應救這麼建立一個空文件就算了吧。npm

在考慮這個問題的時候,咱們能夠先去看下 webpack 官方的那個很經典的圖(我很懶,就不放圖了,你們本身去網上找吧)。webpack 把左邊亂七八糟的 .js .css .png .jpg .sass。。。等等文件所有打包成了靜態資源。也就是說,webpack 打包的是除 html 外的全部資源,那麼咱們是否是隻要把這些資源都放到入口文件中那麼就可讓 webpack 幫咱們打包了呢?json

可是等等。其餘的都沒有問題,什麼 css 啊,什麼 js 啊,都好說,由於用來也不過這麼幾個,可是圖片呢,字體呢?我在項目中用了那麼多圖片,要所有再在 index.js 裏面再寫一遍!天吶!這是要命的啊!那麼我能不能偷懶下,就只寫 css 和 js 呢,其餘亂七八糟的我先無論?那就先這麼來吧。修改咱們的 index.js 文件,添加如下內容。

require('./css/bootstrap.min.css')
    require('./css/jubang.css')
    require('./js/jquery.min.js')
    require('./js/bootstrap.min.js')
    require('./js/jb.js')複製代碼

先這樣把他看成咱們的入口文件吧

2. 配置出口文件

出口文件就很好配置了,將他打包到根目錄下的 dist 目錄中。嗯 ~ 這很常見!

var path = require('path');
    module.exports = {
        entry: './index.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        }
    }複製代碼

3. 配置 loader

這個不知道怎麼配置,就先看這篇文章吧。

因此說,到這裏咱們的 webpack.config.js 就是這樣的:

var path = require('path');

    module.exports = {
        entry: './index.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        module: {
            rules: [{
                test: /\.css$/,
                use: [
                    'style.loader',
                    'css-loader'
                ]
            }, {
                test: /\.(png|jpg|svg|git)$/,
                use: [
                    'file-loader'
                ]
            }, {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    'file-loader'
                ]
            }]
        }
    }複製代碼

這裏咱們用到了三個 loader,須要先安裝下

npm install css-loader style-loader file-loader --save複製代碼

這三個 loader 的做用你們仍是本身去網上查找吧

4. 第一次打包

配置到這裏,咱們能夠先來打包一下,有問題再改嘛!

命令行切換到項目目錄下,執行如下命令:

    npm start


打包結束後,項目的目錄結構

    project
        - css
        - dist
        - fonts
        - images
        - js
        - node-modules
        - favicon.ico
        - index.html
        - index.js
        - package.json
        - webpack.config.js

咱們能夠看到,項目根目錄下面多出來一個 dist 的目錄。沒錯,這個就是咱們 webpack 打包後文件生成的目錄,至於爲何會是 dist 目錄,那是由於你在 webpack.config.js 的 output 中設置的 path。


如今咱們來查看下 webpack 打包出了什麼東西

        - dist
            - xxxx.jpg
            - xxxx.woff2
            - xxxx.jpg
            - xxxx.svg
            - bundle.js
            - xxxx.ttf
            - xxxx.eot
            - xxxx.woff複製代碼

注:xxxx表明一串數字和字母的組合,爲了表示方便就這麼寫了

打包文件中生成了一個 .js 文件,兩個 .jpg 文件,四個字體文件(.woff二、.ttf、.eot、.woff),和一個 .svg 文件

咱們來看下這是個類型的文件都來自哪裏吧複製代碼

1. bundle.js

bundle.js 是咱們根據咱們入口文件,將咱們在入口文件中全部的依賴資源都打包進去生成的。這個也是咱們最主要要關注的文件。

2. .jpg

兩個 .jpg 文件是從哪裏來的呢?查看了兩張圖片以後,其實咱們會知道,這兩個圖片都是在咱們本身寫的 jb.css 中用來做爲 background-image 引入的。咱們說了,webpack 會根據入口文做爲起點,並根據依賴關係圖來進行打包的。換句話說,咱們在入口文件中依賴了 jb.css,而 jb.css 依賴了兩張 .jpg 圖片,因此 webpack 根據依賴分析,將這兩張圖片也都一塊兒打包進來了。

3. 字體文件 + svg

四個字體文件的來源就須要咱們對 bootstrap 有必定了解了。若是熟悉 bootstrap 的同窗確定會知道,在 bootstrap 的依賴裏面,他正是依賴了這些字體,也就是說這些字體文件是從 bootstrap.min.css 文件中被打包進來的。其實我在作這個項目的時候,把 bootstrap 的源碼弄到本地的時候,會發現裏面有一個 fonts 的文件夾,也就是咱們項目根目錄下的 fonts 文件夾,這裏會有四個一樣文件結尾的字體文件和一個 .svg 結尾的 svg 文件。而這五個文件不就是咱們這裏多出來的五個文件嘛

若是還不放心的話,咱們能夠再作個驗證。修改 webpack.config.js 文件

{
        test: /\.(png|jpg|svg|git)$/,
        use: [
        -    'file-loader'
        +    'file-loader?name=[hash:8].[name].[ext]'
        ]
    }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
        -    'file-loader'
        +    'file-loader?name=[hash:8].[name].[ext]'
        ]
    }複製代碼

咱們把圖片和字體文件,在 file-loader 處理後讓他的名字就變成 ’8位hash值-原文件名-後綴名‘的格式,那麼咱們就能夠比對這幾個文件的來源了。
從新打包後(你得先刪除原來的 dist 文件夾),咱們就能夠發現咱們的猜想是正確的!

4. 遺留問題

雖然咱們的第一次打包成功了,可是仍是留下了幾個問題沒有解決:第一,個人 js、css、字體、圖片等資源都被打包進了 dist 目錄,可是做爲咱們的項目最主要的 index.html 文件呢?沒有這個文件,咱們打包出來的東西還有什麼意義呢!。第二,個人 images 文件夾裏有那麼多圖片,你這個 webpack 打包後爲何就只有兩張圖片了,其餘的呢?

那麼接下來讓咱們急需解決。

5. html-webpack-plugin

要解決第一個問題,咱們須要用到 html-webpack-plugin 插件。這個插件的具體說明能夠查看這裏。這個插件的做用是能夠將 html 文件打包,並自動添加對打包後的 output 文件的引用。具體如何使用,請先安裝:

npm install html-webpack-plugin --save複製代碼

修改配置文件:

var path = require('path');
    +   var HtmlWebpackPlugin = require('html-webpack-plugin');

在 module 後面加

    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html'
        })
    ]複製代碼

咱們在配置文件中引入了一個插件,並向 HtmlWebpackPlugin 構造函數傳遞了一個對象參數,在這個對象參數中,咱們指明瞭一個 template 字段,表面咱們要打包的 html 的文件源。而後咱們從新打包,再查看咱們的目錄就能夠發現 dist 目錄下多出了一個 index.html。因爲這個項目自己就是一個簡單的不依賴任何環境的項目,因此若是正常的話咱們直接打開 index.html 頁面就能在瀏覽器里正常顯示了。雖然不知道會怎麼樣,可是咱們仍是打開來試試吧。

當咱們打開 index.html 在瀏覽器中顯示的時候,咱們發現瀏覽器中好多圖片都不見了。細想咱們的項目代碼,發現除了在 jb.css 中的背景圖被正確顯示之外,其餘的定義在 img 標籤中的圖片沒有一張是顯示出來的。

因此雖然這個文件配置還有點問題沒解決,那麼咱們先來解決 html 中的 img 問題吧,也就是咱們上面提到的第二個問題。

6. html-withimg-loader

要解決第二個問題(也就是 html 中的 img 問題),咱們須要用到 html-whithimg-loader,具體關於這個怎麼用能夠查看這裏

修改配置文件,直接在 rules 中再添加一條配置規則

{
        test: /\.(html|htm)$/,
        use: [
            'html-withimg-loader'
        ]
    }複製代碼

這條配置規則代表,全部要處理的 html 文件首先會通過 html-withimg-loader 這個 loader 處理。就是這麼簡單能將咱們第二個問題解決嗎?試試看吧,事件是檢驗真理的惟一標準。

修改資源目錄

從新打包,再查看 dist 目錄,這一查看沒關係,發現 dist 目錄下多了好多圖片文件,密密麻麻,亂七八糟的,這些該不會就是咱們 html 中的圖片吧。按住本身的強迫症,先找到 index.html (咱們最關心的仍是他嘛),打開後再瀏覽器查看效果。發現果真,咱們的圖片都已經在了,並且樣式也差很少對了。可是這麼亂七八糟的 dist 目錄,怎麼會是咱們這種強迫症患者所想要的結果呢!咱們想要的是圖片都放在圖片文件夾下,字體都放在字體文件夾下,其餘的比較少的就先讓他在外面呆着吧。說幹就幹,咱們來調整一下咱們的配置文件

{
            test: /\.(png|jpg|svg|git)$/,
            use: [
            -    'file-loader?name=[hash:8].[name].[ext]'
            +    'file-loader?name=images/[hash:8].[name].[ext]'
            ]
        }, 
        {
            test: /\.(woff|woff2|eot|ttf|otf)$/,
            use: [
            -    'file-loader?name=[hash:8].[name].[ext]'
            +    'file-loader?name=fonts/[hash:8].[name].[ext]'
            ]
        } 複製代碼

自動刪除 dist

還有一個問題我已經忍了好久了,每次打包前,咱們都須要手動先去刪除上次打包留下來的 dist 目錄,這個就煩了,雖然只是一個 delete 的事情,可是作多了也煩啊!咱們想能不能讓 webpack 自動幫咱們作了這件事,讓咱們不須要手動去刪除。還好 webpack 夠智能,總能知足你提出來的各類無理取鬧。不過咱們先要裝一個插件:

npm install clean-webpack-plugin --save複製代碼

而後再配置文件中添加對這插件的引用

var HtmlWebpackPlugin = require('html-webpack-plugin');
    +   var CleanWebpackPlugin = require('clean-webpack-plugin');複製代碼

在 plugins 中添加對這個插件的使用

new CleanWebpackPlugin(['dist'])複製代碼

這樣咱們就能不用手動刪除 dist 文件夾了。

從新打包下試試吧!

打包完後咱們在查看 dist 目錄就瞬間感受清爽多了有木有!

- dist
        - fonts
            - 一些字體文件
        - images
            - 一些圖片文件

        - bundle.js
        - index.html複製代碼

修改 index.html

將打包後的項目再次在瀏覽器中查看,並查看控制檯會發現,控制檯報了一堆錯誤。其中有幾個是對一些 css、js 文件引用的錯誤。由於咱們把須要用的 css、js 都打包進了 bundle.js 中了。而咱們原來的項目是經過靜態資源引用的方式一個個導入 html 文件中的。因此,當咱們 webpack 打包成功後,就不須要對這些資源進行引用了,咱們只須要對 bundle.js(咱們打包後的文件)進行引用就能夠了,所幸的是,打包後的文件 webpack 已經自動幫咱們引用了。因此直接在原來項目中的 index.html 中幹掉那些 css、js 就能夠了。而後從新打包後就沒有這些資源找不到的亂七八糟的錯誤了。

可是,有一個小問題就是關於咱們的 .ico 文件。這是咱們網站的圖標文件。他的引用錯誤該如何解決呢?咱們能夠在生成 html 的時候,將這個問題先給解決了。修改配置文件

new HtmlWebpackPlugin({
        template: './index.html',
    +    favicon: path.resolve(__dirname, './favicon.ico')
    })複製代碼

這樣的話咱們的圖標文件也就有了。

7. 關於 jquery

雖然一些亂七八糟的引用錯誤解決了,可是控制檯留下了一個讓咱們很是頭疼的問題:jquery 的引用問題:

Uncaught Error: Bootstrap's JavaScript requires jQuery複製代碼

這裏咱們須要用到 expose-loader 這個東西,關於他,能夠查看這裏,廢話很少說:

npm install expose-loader --save複製代碼

在 module 的 rules 中再添加一個 loader

{
        test: require.resolve('./js/jquery.min.js'), // 引入 jquery
        use: [{
            loader: 'expose-loader',
            options: '$'
        }, {
            loader: 'expose-loader',
            options: 'jQuery'
        }]
    }複製代碼

固然,網上或許有其餘方法關於引入 jquery 的,這裏只是說一種。而後再打包咱們的頁面就沒有問題了:各類資源都有了,js 寫的效果也出現了。

8. 提取 css

雖然網站看上去沒啥問題了,可是細心的同窗確定會發現:當咱們打開網站的時候,他會先出現一個沒有樣式的頁面,而後一閃而逝,最後纔出現咱們預期的樣子。這是爲何呢?

緣由很好理解,由於咱們把 css 和 js 都打包進了同一個 bundle.js 裏面了。可是,這個 bundle.js 是在頁面最後面才加載進來的。也就是說,咱們的樣式被放在了頁面的底部被加載。這徹底不符合咱們的預期啊。咱們但願的是樣式在 head 中加載,而 js 腳本才放在頁面底部加載。因此咱們就不能把 css 和 js 一塊兒打包進 bundle.js 中了。

extract-text-webpack-plugin

詳細資料看這裏

npm install extract-text-webpack-plugin --save複製代碼

增長 require

var ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');複製代碼

修改 css rules

{
        test: /\.css$/,
        use: ExtractTextWebpackPlugin.extract[{
            fallback: 'style-loader',
            use: 'css-loader'
        }]
    }複製代碼

增長 plugin

new ExtractTextWebpackPlugin('style.css')複製代碼

打包,而後咱們會發現 dist 中多了一個 style.css,而後再 index.html 的 head 中的多了對這個 css 的引用

9. 其餘

1. 壓縮 js

增長 plugin

new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        }
    })複製代碼

2. 壓縮 html

new HtmlWebpackPlugin({
    template: './index.html',
    favicon: path.resolve(__dirname, './favicon.ico'),
    minify: {
        removeAttributeQuotes: true,
        removeComments: true,
        removeEmptyAttribute: true,
        collapseWhitespace: true
    }
}),複製代碼

3. 優化圖片

{
        test: /\.(jpg|png|gif|svg)$/,
        - use: 'file-loader?name=images/[hash:8].[name].[ext]'
        + use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
    }複製代碼

總結

暫時就先那麼多吧,沒時間寫了,之後再說。第一次發文,求輕虐。

參考文檔

webpack(v3.5.5)中文文檔

相關文章
相關標籤/搜索