webpack2歸納總結

来源:http://www.cnblogs.com/rynxiao/archive/2017/07/14/7171583.html
-Advertisement-
Play Games

本文github倉庫: "https://github.com/Rynxiao/webpack2 learn" 從v1遷移到v2 1. 配置類型 在webpack1的時候,主要是通過導出單個 來進行配置。例如下麵的配置: 而在webpack2中,則有三種方式來靈活配置,可以針對不同的場景。 1.1 ...


本文github倉庫:https://github.com/Rynxiao/webpack2-learn

從v1遷移到v2

1. 配置類型

在webpack1的時候,主要是通過導出單個object來進行配置。例如下麵的配置:

// webpack1 導出方式
module.export = {
    entry : 'app.js',
    output : { */... */},
    /* ... */
};

而在webpack2中,則有三種方式來靈活配置,可以針對不同的場景。

1.1 通過不同環境變數導出不同的配置文件

// 可以有兩種方式傳遞當前值,一種是簡單傳遞字元串,另外一種則是傳遞一個對象
// 例如: webpack --env production 控制台列印的就是 'production',是一個字元串
// 而當這樣調用時:webpack --env.production --env.size 60,控制台列印的就是 { production : true, size : 60 }

var path = require('path'),
    webpack = require('webpack'),
    UglifyJsPlugin = new webpack.optimize.UglifyJsPlugin(),
    plugins = [];

module.exports = function(env) {

    console.log(env);
  
    if (env === 'production') {
        plugins.push(UglifyJsPlugin);
    }

    return {
        entry : path.resolve(__dirname, 'js/app.js'),
        output : {
            path : path.resolve(__dirname, 'build'),
            filename : '[name].bundle.js'
        },
        module : {
            rules : [
                { 
                    test : /\.js|\.jsx$/, 
                    loader : 'babel-loader', 
                    options : {
                        presets : ["es2015", "react"]
                    } 
                },
                { 
                    test : /\.css$/,
                    use : ['style-loader', 'css-loader']
                },
                {
                    test : /\.less$/,
                    use : ['style-loader', 'css-loader', 'less-loader']
                }
            ]
        },
        plugins : plugins
    };
}

// 在package.json中配置兩個命令
{
    "dev" : "webpack",
    "build" : "webpack --env production"
}

具體的生產環境構建方式可以參看官網production

1.2 通過promise方式導出配置文件

這種方式的應用場景是在某些情況下,我們暫時拿不到配置文件所需要的配置參數,比如需要配置的文件名等等,或許這是一個非同步的操作,通過promise方式可以使我們在非同步操作之後得到配置變數,然後再執行配置文件。

// 在這種情況下,1秒之後會返回配置文件並且執行

var path = require('path');

module.exports = () => {
    return new Promise((resolve, reject) => {
        console.log('loading configuration ...');
        setTimeout(() => {
            console.log('loading completed!');
            resolve({
                entry : path.resolve(__dirname, 'js/app.js'),
                output : {
                    path : path.resolve(__dirname, 'build'),
                    filename : '[name].bundle.js'
                },
                module : {
                    rules : [
                        { 
                            test : /\.js|\.jsx$/, 
                            loader : 'babel-loader', 
                            options : {
                                presets : ["es2015", "react"]
                            } 
                        },
                        { 
                            test : /\.css$/,
                            use : ['style-loader', 'css-loader']
                        },
                        {
                            test : /\.less$/,
                            use : ['style-loader', 'css-loader', 'less-loader']
                        }
                    ]
                },
            });
        }, 1000);
    });
}

1.3 同時打包多份配置文件

webpack1時只能導出單份配置文件,在webpack2中可以同時打包多份配置文件,意味著可以為多個入口文件打包,在多頁面打包的時候,就再也不需要為在每一個單獨的頁面執行打包命令了。

// config-amd.js
var path = require('path');

module.exports = {
    entry : path.resolve(__dirname, 'js/app.js'),
    output : {
        path : path.resolve(__dirname, 'build'),
        filename : '[name].amd.js',
        libraryTarget : 'amd'
    },
    module : {
        rules : [
            { 
                test : /\.js|\.jsx$/, 
                loader : 'babel-loader', 
                options : {
                    presets : ["es2015", "react"]
                } 
            },
            { 
                test : /\.css$/,
                use : ['style-loader', 'css-loader']
            },
            {
                test : /\.less$/,
                use : ['style-loader', 'css-loader', 'less-loader']
            }
        ]
    }
};

// config-commonjs.js
var path = require('path');

module.exports = {
    entry : path.resolve(__dirname, 'js/app.js'),
    output : {
        path : path.resolve(__dirname, 'build'),
        filename : '[name].commonjs.js',
        libraryTarget : 'commonjs'
    },
    module : {
        rules : [
            { 
                test : /\.js|\.jsx$/, 
                loader : 'babel-loader', 
                options : {
                    presets : ["es2015", "react"]
                } 
            },
            { 
                test : /\.css$/,
                use : ['style-loader', 'css-loader']
            },
            {
                test : /\.less$/,
                use : ['style-loader', 'css-loader', 'less-loader']
            }
        ]
    }
};

// webpack.config.js
var configAmd = require('./config-amd.js'),
    configCommonjs = require('./config-commonjs.js');

module.exports = [
    configAmd,
    configCommonjs
]

2. resolve相關

2.1 extensions 尾碼擴展

在webpack2中,不需要預設寫一個空字元串,如果沒有配置這個選項,則預設的尾碼名是['.js', '.json'],這樣可以在需要用到import 'some.js'的時候直接寫import 'some'就好。

如果不想開啟自動尾碼,則需要在resolve中配置enforceExtension : true,例如:

var path = require('path');

module.exports = {
    entry : // ....,
    // ...
    resolve : {
        enforceExtension : true
    }
};

此時,如果在js/app.js中引用js/text.js,就會報錯

// Error
import './text';

// Right
import './text.js';

2.2 root/fallback/modulesDirectories 文件定位

webapck1 resolve中配置這三個屬性,是告訴webpack在引入模塊的時候必須要尋找的文件夾,webpack2中則直接更換成了一個單獨的屬性modules,預設優先搜索node_modules(註意,這是一個相對位置)

// config
resolve: {
    // root : path.join(__dirname, "src")  webpack1方式
    modules : [
        path.join(__dirname, "src"),    // 優先於node_modules/搜索
        "node_modules"
    ]
}

// 修改 js/app.js
// 在js文件夾中,增加一個lodash.js,如果按照上面的配置了modules,則會優先載入我們自己的lodash庫
import '../css/style.less';
import _ from 'lodash';

console.log(_.isObject([1, 2, 3]));
document.getElementById('container').textContent = 'APP';

// js/lodash.js
export default {
    isObject(a) {
        console.log('this is my lodash library!');
        return a && typeof a === 'object';
    }
}

得到的結果如下圖:

resolve-modules

3. module相關

3.1 module.rules替換module.loaders

The old loader configuration was superseded by a more powerful rules system, which allows configuration of loaders and more. For compatibility reasons, the old module.loaders syntax is still valid and the old names are parsed. The new naming conventions are easier to understand and are a good reason to upgrade the configuration to using module.rules.

大意就是新的命名更容易理解(反正對於我來說就是換了個英文單詞:-D),同時還會相容老的方式,也就是說,你照樣寫module.loaders還是可以的。

module : {
    // webpack1 way
    // loaders : [...]
    
    // now
    rules : [
        ...
    ]
}

3.2 module[*].loader寫法

如果需要載入的模塊只需要一個loader,那麼你還是可以直接用loader這個關鍵詞;如果要載入的模塊需要多個loader,那麼你需要使用use這個關鍵詞,在每個loader中都可以配置參數。代碼如下:

module : {
    rules : [
        { test : /\.js|\.jsx$/, loader : 'babel-loader' },
      
        /* 如果後面有參數需要傳遞到當前的loader,則在後面繼續加上options關鍵詞,例如:
          { 
            test : /\.js|\.jsx$/, 
            loader : 'babel-loader', 
            options : { presets : [ 'es2015', 'react' ] } 
          }
        */
      
        {
            test : /\.css$/,
            // webpack1 way
            // loader : 'style!css'
          
            use : [ 'style-loader', 'css-loader' ]
        },
        {
            test : /\.less$/,
            use : [
                'style-loader',     // 預設相當於 { loader : 'style-loader' }
                {
                    loader : 'css-loader',
                    options : {
                        modules : true
                    }
                },
                'less-loader'
            ]
        }
    ]
}

3.2 取消自動添加-loader尾碼

之前寫loader通常是這樣的:

loader : 'style!css!less'
// equals to
loader : 'style-loader!css-loader!less-loader'

都自動添加了-loader尾碼,在webpack2中不再自動添加,如果需要保持和webpack1相同的方式,可以在配置中添加一個屬性,如下:

module.exports = {
    ...
    resolveLoader : {
        moduleExtensions : ["-loader"]
    }
}

// 然後就可以繼續這樣寫,但是官方並推薦這樣寫
// 不推薦的原因主要就是為了照顧新手,直接寫會讓剛接觸的童鞋感到困惑
// github.com/webpack/webpack/issues/2986
use : [ 'style', 'css', 'less' ]

3.3 json-loader內置啦

如果要載入json文件的童鞋再也不需要配置json-loader了,因為webpack2已經內置了。

4. plugins相關

4.1 UglifyJsPlugin 代碼壓縮插件

壓縮插件中的warningssourceMap不再預設為true,如果要開啟,可以這樣配置

plugins : [
    new UglifyJsPlugin({
        souceMap : true,
        warnings : true
    })
]

4.2 ExtractTextWebapckPlugin 文本提取插件

主要是寫法上的變動,要和webpack2配合使用的話,需要使用version 2版本

// webpack1 way
modules : {
    loaders : [
        { 
            test : /\.css$/, 
            loader : ExtractTextPlugin.extract('style-loader', 'css-loader', { publicPath : '/dist' })
        }   
    ]
},
plugins : [
    new ExtractTextPlugin('bunlde.css', { allChunks : true, disable : false })
]

// webapck2 way
modules : {
    rules : [
        { 
            test : /\.css$/, 
            use : ExtractTextPlugin.extract({
                fallback : 'style-loader',
                use : 'css-loader',
                publicPath : '/dist'
            })
        }
    ]
},
plugins : [
    new ExtractTextPlugin({
        filename : 'bundle.css',
        disable : false,
        allChunks : true
    })
]

5. loaders的debug模式

在webpack1中要開啟loaders的調試模式,需要載入debug選項,在webpack2中不再使用,在webpack3或者之後會被刪除。如果你想繼續使用,那麼請使用以下寫法:

// webpack1 way
debug : true

// webapck2 way 
// webapck2將loader調試移到了一個插件中
plugins : [
    new webpack.LoaderOptionsPlugin({
        debug : true
    })
]

6. 按需載入方式更改

6.1 import()方式

在webpack1中,如果要按需載入一個模塊,可以使用require.ensure([], callback)方式,在webpack2中,ES2015 loader定義了一個import()方法來代替之前的寫法,這個方法會返回一個promise.

// 在js目錄中新增一個main.js
// js/main.js
console.log('main.js');

// webpack1 way
require.ensure([], function(require) {
    var _ = require('./lodash').default;
    console.log(_);
    console.log('require ensure');
    console.log(_.isObject(1));
});

// webpack2 way
// 採用這種方式,需要promise 的 polyfill
// 兩種方式:
// 1. npm install es6-promise --save-dev
//    require('es6-promise').polyfill();
//
// 2. babel方式,在webpack中配置babel插件
//    npm install babel-syntax-dynamic-import --save-dev
//    options : {
//        presets : ['es2015'],
//        plugins : ['syntax-dynamic-import']
//    }
import('./lodash').then(module => {
    let _ = module.default;
    console.log(_);
    console.log('require ensure');
    console.log(_.isObject(1));
});

會得到的chunk文件,如下圖:

code-splitting

code-splitting-console

6.2 動態表達式

可以動態的傳遞參數來載入你需要的模塊,例如:

function route(path, query) {
    return import(`./routes/${ path }/route`)
        .then(route => { ... })
}

7. 熱替換更加簡單

webpack2中提供了一種更簡單的使用熱替換功能的方法。當然如果要用node啟動熱替換功能,依然可以按照webpack1中的方式。

npm install webpack-dev-server --save-dev

// webpack.config.js
module.exports = {
    // ...,
    devServer : {
        contentBase : path.join(__dirname, 'build'),
        hot : true,
        compress : true,
        port : 8080,
        publicPath : '/build/'
    },
    plugins : [
        new webpack.HotModuleReplacementPlugin()
    ]
}

談談V2版本

主要是介紹之前在webpack1中忽略的以及v2版本中新加的一些東西。

1. caching(緩存)

瀏覽器為了不重覆載入相同的資源,因此加入了緩存功能。通常如果請求的文件名沒有變的話,瀏覽器就認為你請求了相同的資源,因此載入的文件就是從緩存裡面拿取的,這樣就會造成一個問題,實際上確實你的文件內容變了,但是文件名沒有變化,這樣還是從緩存中載入文件的話,就出事了。

那麼,之前傳統的做法就是給每個文件打上加上版本號,例如這樣:

app.js?version=1
app.css?version=1

每次變動的時候就給當前的版本號加1,但是如果每次只有一個文件內容變化就要更新所有的版本號,那麼沒有改變的文件對於瀏覽器來說,緩存就失效了,需要重新載入,這樣就很浪費了。那麼,結合數據摘要演算法,版本號根據文件內容生成,那麼現在的版本可能是這樣的。

// before
app.js?version=0add34
app.css?version=1ef4a2

// after
// change app.js content
app.js?versoin=2eda1c
app.css?version=1ef4a2

關於怎麼部署前端代碼,可以查看大公司怎樣開發和部署前端代碼

webpack為我們提供了更簡單的方式,為每個文件生成唯一的哈希值。為了找到對應的入口文件對應的版本號,我們需要獲取統計信息,例如這樣的:

{
  "main.js": "main.facdf96690cca2fec8d1.js",
  "vendor.js": "vendor.f4ba2179a28531d3cec5.js"
}

同時,我們結合html-webpack-plugin使用的話,就不需要這麼麻煩,他會自動給文件帶上對應的版本。具體看法參看之前寫的webpack1知識梳理,那麼我們現在的配置變成了這個樣子:

npm install webpack-manifest-plugin --save-dev

// webpack.config.js
module.exports = {
    entry : { /* ... */ },
    output : {
        path : path.resolve(__dirname, 'build-init'),
        filename : '[name].[chunkhash].js',
        chunkFilename : '[name].[chunkhash].js'
    },
    module : {
        // ...
    },
    plugins : [
        new htmlWebpackPlugin({
            title : 'webpack caching'
        }),
        new WebpackManifestPlugin()
    ]
}

html引入情況

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>webpack caching</title>
  </head>
  <body>
  <div id="container"></div>
  <script type="text/javascript" src="main.facdf96690cca2fec8d1.js"></script><script type="text/javascript" src="vendor.f4ba2179a28531d3cec5.js"></script></body>
</html>

WARNNING:

不要在開發環境下使用[chunkhash],因為這會增加編譯時間。將開發和生產模式的配置分開,併在開發模式中使用[name].js的文件名,在生產模式中使用[name].[chunkhash].js文件名。

為了使文件更小化,webpack使用標識符而不是模塊名稱,在編譯的時候會生成一個名字為manifest的chunk塊,並且會被放入到entry中。那麼當我們更新了部分內容的時候,由於hash值得變化,會引起manifest塊文件重新生成,這樣就達不到長期緩存的目的了。webpack提供了一個插件ChunkManifestWebpackPlugin,它會將manifest映射提取到一個單獨的json文件中,這樣在manifest塊中只需要引用而不需要重新生成,所以最終的配置是這樣的:

var path = require('path'),
    webpack = require('webpack'),
    htmlWebpackPlugin = require('html-webpack-plugin'),
    ChunkManifestWebpackPlugin = require('chunk-manifest-webpack-plugin'),
    WebpackChunkHash = require('webpack-chunk-hash');

module.exports = {
    entry : {
        main : path.resolve(__dirname, 'js/app.js'),
        vendor : path.resolve(__dirname, 'js/vendor.js')
    },
    output : {
        path : path.resolve(__dirname, 'build'),
        filename : '[name].[chunkhash].js',
        chunkFilename : '[name].[chunkhash].js'
    },
    module : {
        // ...
    },
    plugins : [
        new webpack.optimize.CommonsChunkPlugin({
            name : ['vendor', 'manifest'],
            minChunks : Infinity
        }),
        new webpack.HashedModuleIdsPlugin(),
        new WebpackChunkHash(),
        new htmlWebpackPlugin({
            title : 'webpack caching'
        }),
        new ChunkManifestWebpackPlugin({
            filename : 'chunk-mainfest.json',
            manifestVariable : 'webpackManifest',
            inlineManifest : true
        })
    ]
}

tips:如果還不是很明白,去對比一下加了ChunkManifestWebpackPlugin和沒加的區別就可以清楚的感受到了。在本文的代碼文件夾caching中可以看到這一差別


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 什麼是LESSCSS LESSCSS是一種動態樣式語言,屬於CSS預處理語言的一種,它使用類似CSS的語法,為CSS的賦予了動態語言的特性,如變數、繼承、運算、函數等,更方便CSS的編寫和維護。 LESSCSS可以在多種語言、環境中使用,包括瀏覽器端、桌面客戶端、服務端。 語言特性快速預覽: 變數: ...
  • 做了一個網站,放到線上,用微信打開,點擊分享,可是分享後發給朋友的鏈接卡片是微信預設自帶的,如下: 這標題,描述以及圖片是預設自帶的,醜不說,分享給別人還以為是盜號網站呢,而接入微信的JSSDK後,分享可以自定義內容,如下: 我承認,雖然這分享的標題和內容也並不正經,但這不妨礙我表達 我們可以通過微 ...
  • 我們把這些用戶信息保存到list的數組中,然後增刪改查就在這個數組上進行: 這裡面的表單有:文本輸入框,單選按鈕,select選擇框,覆選框等。 1. 展示數據 我們的數據都放在數組list中,但是這裡並不直接對list對迴圈輸出,而是先把list中的數據給一個數組slist,對slist進行迴圈輸 ...
  • web前端開發的工作流程的第一步就是根據ui給的psd來還原設計圖樣貌。 可是一打開滿屏的參考線。這時我們可以alt+v+d清空參考線 這時可以按alt+滑鼠拖放圖片。同時也可以按F進入半屏和勻速連按兩次F進入全屏。F7為調出圖層面板。 下一步,是找到我們需要的圖層圖片。 (前提是設置了) 接著 總 ...
  • 一.概述 JavaScript一種直譯式腳本語言,是一種動態類型、弱類型、基於原型的語言,內置支持類型。它的解釋器被稱為JavaScript引擎,為瀏覽器的一部分,廣泛用於客戶端的腳本語言,最早是在HTML(標準通用標記語言下的一個應用)網頁上使用,用來給HTML網頁增加動態功能。 JavaScri ...
  • CSS3-loading載入動畫 線上示例demo:http://liyunpei.xyz/loading.html 之前發了四篇,二十二個效果,今天再分享六個效果,總計二十八個效果。 二十三、效果二十三 兩個正方形,初始均定位至左上(top:0;left:0;); 一次完整運動分為四個階段:第一個 ...
  • 在開發中,頁面 js 經常會遇到需要 當前登錄用戶信息(菜單許可權,用戶基本信息,配置信息) 的地方,一般情況我們可能對這些信息獲取方式不是太在意,但是現在的前端通過webpack打包,即使做了代碼分割,js文件,css文件還是很大。 在首次載入的情況下麵,存在一定的優化空間。下麵主要介紹一些信息獲... ...
  • <!DOCTYPE html><html><head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> <link rel="stylesheet" href=" ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...