Webpack從入門到上線

webpack是目前一個很熱門的前端打包工具,官網說得很清楚,webpack的出現就是要把requirejs幹掉。同時它還提供了十分便利的本地開發的環境。網上並不容易找到一個講解得比較詳細完整的教程,本文結合實踐經驗,總結一套可用的開發和上線的配置和流程。javascript

首先,Require JS有什麼問題php

RequireJs存在的問題

博主先是使用了RequireJs,後來又轉了webpack,綜合比較,requirejs確實存在一些缺點:css

1.寫法比較笨拙

 須要把全部的依賴模塊寫在require函數裏面,當模塊不少的時候,看起來逼格就不高了,感覺以下: html

而webpack既兼容requirejs的寫法,也兼容commonjs的寫法,也就是說,使用webpack你既能夠繼續像上面那樣寫,也能夠像node那樣寫,感覺以下:前端

複製代碼
var modules = {
        signHandler: require("module/sign-log"),
        chatHandler: require("module/chat-win"),
        mapHandler: require("lib/map"),
        util: require("lib/util")
    };
複製代碼

能夠在須要的時候再去require,而不是搞個大括號把所有的模塊一會兒寫到一塊兒。(模塊的導出用module.exports = ....)java

固然這兩種寫法不只是感光上的區別,邏輯上也有區別。用中括號加載的模塊一般webpack是動態去加載,而沒有中括號是和主文件打包在一塊兒的。node

2. 沒有通用模塊的概念

例若有一個彈框模塊,用在登錄註冊,而且全部頁面都有登錄註冊,因此這是一個全部頁面的通用模塊。若是頁面的其它模塊都沒調到通用模塊裏面的東西的話,用RequireJs沒什麼問題。可是實際狀況上不是這樣的,例如util模塊既會被登錄註冊的模塊調用,也會被不少其它模塊調用。這個時候合併壓縮就有問題了:合併後的通用模塊如common-app.js會帶上util的代碼、另一個頁面的例如detail.js也會帶上util的代碼,之後一改util.js裏面的東西,就會一併改動其它全部用到util的頁面js,就得從新打全部js的版本號。這樣不管對佈署上線,仍是對於用戶的緩存來講都是不利的。
react

 webpack能夠把幾個文件的通用模塊抽出來單獨做爲一個模塊common-chunk.js,引用的時候每一個頁面先引一個common-chunk.js,再引一個該頁面本身的js文件如detail.js,原detail.js裏面和其它js文件共用的模塊已經被提取到common-chunk.js裏面。webpack

3. 沒辦法直接動態合併壓縮一個須要異步加載的模塊

這個問題是這樣的,假設個人聊天模塊文件有500Kb這麼大,並不但願一刷頁面就加載,而是用戶點了聊天再去加載。這個聊天模塊有一個入口文件和其它幾個模塊文件,我合併壓縮了入口文件,須要有一個輸出文件,而入口文件define的模塊名和壓縮優化後的輸出文件的路徑確定是要不同的,可是壓縮以後他並不會自動去改變輸出文件的模塊名。這樣就致使你要手動去改一下壓縮文件的模塊名,否則會require不到。我以前找了一下,沒有找到解決方案,因此採起了一個壓縮兩次的比較笨拙的方法。nginx

而webpack有一個文件束chunkFile的概念,它會自動去把須要異步加載的文件變成一個chunkFile,而後觸發加載的時候再去加載chunkFile。

4. 須要藉助gulp等管理工具進行開發

webpack自己有一些插件和第三方的插件,能夠在本地開一個webpack-dev-server,文件一保存的時候就會自動打包編譯js/css/less/sass等。

 

使用RequireJs雖然看起來缺點比較多,可是使用RequireJs也有webpack不具有的優勢,那就是RequireJs開發的時候在瀏覽器裏面,每一個模塊都是單獨一個文件,跟本地文件保持一致,而webpack是把主文件和該文件都用到的模塊都打包成了一個文件,這樣在調試的時候就須要你去搜索找到要調試的位置,而使用requireJs直接根據第幾行就能夠了。不過,考慮到使用webpack能夠搭建一個很方便的本地開發環境,因此這個缺點也不是很明顯。

使用webpack

用一句歸納就是:寫一個配置文件,而後執行下webpack,就能夠把生成的文件輸出,可壓縮帶版本號,同時生成一個source-map文件,這個文件包含了每一個模塊的js和css的實際(帶版本號)路徑,根據這個路徑就能夠把html裏面的js/css等換成真實的路徑。

webpack是一個打包的工具,它有一個重要的概念,就是把js/css/image/coffee都當成地位相等的資源,你能夠在js裏面require一個css,也能夠require一個image。可是這種模式比較適用於React等框架,都是用js控制。

 webpack的其它幾個重要概念:

1. loader加載器

上面說到,各類各樣的資源均可以在webpack裏面加載,而這些資源都須要相應的加載器,webpack才能識別,而後解析成正常的瀏覽器認識的資源。

換句話說,你能夠給webpack加載各類各樣的資源:css/less/sass/png/babel等,而後在代碼裏面進行管理。

例如要加一個sass的loader,須要先安裝:

npm install sass-loader node-sass

而後在配置文件添加一個loader:

複製代碼
{
    test: /\.sass$/,
    loaders: ["style", "css", "sass"]
},
複製代碼

這樣當你require(「hello.sass」)的時候,webpack就能處理這種.sass結尾的文件。這樣子有兩個好處,一個是webpack可以自動編譯sass爲css,另外一個是require進來的style,webpack會把它解析成一個object,這個object的key就是類名,就能夠在js使用樣式的類名,這種比較適合相似於react的開發模式。

2. 文件束chunk

上面提到的,會把動態加載的文件生成一個個的chunk,在配置文件的output裏面加一行:

chunkFilename: "bundle-[id].js"

就會根據id區分不一樣動態加載的chunk文件,而這些chunk文件名對於咱們來講是可有可無,由於這個是webpack管理的,開發者無需關心叫什麼又是怎麼加載的。

3. webpack-dev-server

  這是webpack的一個插件,能夠在本地開一個靜態服務,用來做爲本地開發的重要工具。具體步驟就是html裏面引用的資源用一個假的域名,如develop.com:

<script src="//develop.com/site/app-init.js"></script>

而後再把develop.com綁到本地迴路:

     127.0.0.1 fedren.com

這樣請求就打到了本地的80端口。同時在本地開一個nginx監聽在80端口,nginx收到80端口的請求後,再把請求轉發到webpack的服務(默認是8080端口)。這樣就可以實現本地開發,下文會具體介紹。

 

下面一步步介紹怎麼配置和使用webpack

webpack的基本配置

首先,npm init建立一個node的配置文件package.json,而後安裝webpack:

npm install webpack
sudo npm install webpack -g //安裝一個全局的命令

再建立一個webpack.config.js文件,加入最基本的配置:

複製代碼
module.exports = {
    // The standard entry point and output config
    //每一個頁面的js文件
    entry: {
        home: "js/home",
        detail: "js/detail"
    },
    output: {
        path: "assets",                   //打包輸出目錄
        publicPath: "/static/build/",     //webpack-dev-server訪問的路徑
        filename: "[name].js",            //輸出文件名
        chunkFilename: "bundle-[id].js"   //輸出chunk文件名
    }
};
複製代碼

工程的js都放到js目錄下,一個叫home.js,另外一個叫detail.js,輸出到assets目錄,publicPath是爲webpack-dev-server所使用

而後在當前目錄執行webpack,發現webpack報錯了:

ERROR in Entry module not found: Error: Cannot resolve module 'js/home' in /Users/yincheng/code/blog-webpack

找不到js/home的模塊,只要在配置裏面加一句resolve:

    resolve: {
        modulesDirectories: ['.']
    }

告訴webpack全部模塊的啓始目錄由當前目錄開始,再執行下webpack就能夠正常輸出了:

到目前爲此,當前工程的目錄結構就是這樣的了:

接下來,建立html:home.html,裏面引入js文件,"static/build"即爲上面定義的publicPath:

複製代碼
<body>
    <p>home.html</p>
    <script src="//develop.com/static/build/home.js"></script>
</body>
複製代碼

注意咱們用了一個develop.com的域名,把這個域名綁到本地迴路:

127.0.0.1 develop.com

而後配置nginx,打開nginx.conf,加多一個server:

複製代碼
    server {
        listen       80;
        server_name  payment-admin.com;
        charset utf-8;
        #工程路徑
        root    /Users/yincheng/code/demo;
        autoindex       on;
        autoindex_exact_size    on;

        location ~* /.+\.[a-z]+$ {
            proxy_set_header x-request-filename $request_filename;
            # webpack的服務
            proxy_pass http://127.0.0.1:8080;
        }
     }
複製代碼

啓動nginx或者重啓下nginx

而後再裝一個webpack-dev-server:

npm install webpack-dev-server --save-dev
sudo npm install webpack-dev-server -g

而後啓動webpack-dev-server,執行:

webpack-dev-sever --port=8080 //不加port參數,默認就爲8080端口

而後就能夠訪問:http://develop.com/html/home.html

這個時候,只要一改變home.js的內容,webpack-dev-server就會自動打包新的文件 ,一刷新頁面,就是最新的修改了。這樣就實現了最基本的本地開發,無論你用的jsp/php,都不須要把js/css往服務器上傳。 注意webpack-dev-server是在內存生成的文件,你在本地是找不到static/build目錄的,只有執行了webpack打包纔會輸出文件到assets目錄。一個爲上面配置裏的publicPath,另外一個爲path。

 

引入樣式文件——首先建立css/home.css:

body{
    color: #f00;
}

而後在js裏面引入這個css文件:

require("css/home.css");

一保存以後,會發現webpack-dev-server報錯了:

複製代碼
ERROR in ./css/home.css
Module parse failed: /Users/yincheng/code/blog-webpack/css/home.css Unexpected token (1:4)
You may need an appropriate loader to handle this file type.
複製代碼

根據提示,咱們須要加裝一個css loader,讓webpack可以處理css文件,更改webpack.config.js,加入一個loader:

複製代碼
module.exports = {
    entry: ...,
    output: ...,
    resolve: ...,
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: "style-loader!css-loader"
            },
        ]
    }
};
複製代碼

固然要先安裝一下:npm install style-loader css-loader --save-dev,而後再重啓下webpack-dev-server,就能夠加載樣式了,咱們發現webpack是把樣式動態插到了head標籤的style裏面,可是通常並不但願直接寫到head裏面,而是獨立的一個css文件,這個時候藉助一個分離css的插件就能夠了:

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

同時把配置文件的loader改一下:

複製代碼
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
    module: {
        loaders: [
            // Extract css files
            {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract("style-loader", "css-loader")
            },
        ]
    },

    plugins: [
        new ExtractTextPlugin("[name].css")
    ]
};
複製代碼

就會生成和js相同路徑和名字的css文件,在home.html裏面引入css文件:

<link rel="stylesheet" href="//develop.com/static/build/home.css"></link> 

你也能夠加載各類各樣的loader,如加載一個sass/less loader,require一個sass/less文件後就能夠寫sass/less了,webpack會把它編譯成和上面同樣普通的css文件,讀者能夠本身試試,還能夠再裝一個png/jpg的loader,指定一個小於多少個k的圖片的參數,webpack就會把小於指定尺寸的圖片轉成base64的格式。各類loader的安裝查一查就有了。

 到這裏一個最基本的本地開發環境就已經搭起來了。接下來討論自動刷新

自動刷新

上面一保存js/css的時候,webpack server就會自動打包,刷新頁面的時候就是最新的修改。這個刷新只要使用webpack的hot模式就能夠自動實現,即一保存就自動打包刷新。將上面運行webpack-dev-server的命令再加多兩個參數,按照官方文檔的方式:

webpack-dev-server --port=8383 --hot --inline

若是沒有意外,在你的電腦上將會報錯:

複製代碼
ERROR in multi home
Module not found: Error: Cannot resolve module 'webpack/hot/dev-server' in /Users/yincheng/code/blog-webpack
 @ multi home
複製代碼

這個問題困惑了筆者很久,由於在node_modules裏面是有這個"webpack/hot/dev-server"的,其實只要認真看下上面的提示,就會發現它並非說在node_modules裏面,而是在當前工程目錄裏,因此把node_modules裏的webpack文件夾拷一份到外面就能夠正常運行了。(若是你又配了個context的參數的話,那就根據提示拷到context指定的目錄)

使用hot模式,只要一保存js/css就能夠自動刷新了,這個功能確實很方便。若是不寫參數,也能夠把它寫在配置文件裏面:

複製代碼
var hotModuleReplacementPlugin = require("webpack/lib/HotModuleReplacementPlugin");
module.exports = {
    plugins: [
        new ExtractTextPlugin("[name].css"),
        new hotModuleReplacementPlugin()
    ],
    devServer: {
        historyApiFallback: true,
        hot: true,
        inline: true,
        progress: true
    }
};
複製代碼

而後運行server就不用帶上後面那兩個參數了。

Common chunk

如上文提到,webpack能夠將幾個js的公共模塊提取成一個chunk,須要藉助一個commonChunkPlugin,在上面的plugins再添加一個:

複製代碼
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
plugins: [
        new CommonsChunkPlugin({
            //minChunks: 3,
            name: "common-app.chunk",
            chunks: ["home", "detail", "list"]
        })
    ]
複製代碼

這樣就能夠把home、detail、list三個js和css用到的公共模塊提取到common-app.chunk.js和common-app.chunk.css這兩個文件了。注意頁面要先引入這兩個文件,而後再引入具體頁面的js,webpack在common chunk裏面定義了它的require函數。如上面的home.html:

    <script src="//develop.com/static/build/common-app.chunk.js"></script>
    <script src="//develop.com/static/build/home.js"></script>

能夠指定一個minChunk的參數,指定模塊至少被require幾回才能提取出來,默認是3

還能夠定義兩個commonChunk,例如在詳情頁、列表頁和首頁都有搜索的模塊,而其它頁面沒有搜索的模塊,也就是說除了全部頁面都有的公共模塊如登錄註冊外,還有一個搜索的公共模塊有三個頁面要用到。若是都用一個common chunk,會把搜索的也放進來,但其它不少頁面並不須要用到。這個時候須要加多一個common chunk:

複製代碼
    plugins: [
        new CommonsChunkPlugin({
            name: "search-app.chunk",
            chunks: ["search-app-init", "home", "detail", "list"]
        }),
        new CommonsChunkPlugin({
            name: "common-app.chunk",
            chunks: ["home", "detail", "search-map", "search-app.chunk", "sell", "about", "blog"]
        })
    ]
複製代碼

注意要把search-app.chunk也寫到下面那個全部頁面的chunk裏面,不然webpack會定義兩個同樣的require函數,頁面的模塊也會跟着混亂,一刷頁面就報錯。頁面引用js的順序就變成了:

    <script src="//develop.com/static/build/common-app.chunk.js"></script>
    <script src="//develop.com/static/build/search-app.chunk.js"></script>
    <script src="//develop.com/static/build/home.js"></script>

壓縮和版本號

壓縮只須要要在plugins裏面再添加一個用來壓縮的插件:

複製代碼
var webpack = require("webpack");
plugins: [
    new webpack.optimize.UglifyJsPlugin()
]
複製代碼

這樣執行webpack輸出的js/css就是壓縮的

版本號就是在輸出帶上hash的替換符,以下:

複製代碼
module.exports = {
    output: {
        path: "assets",
        publicPath: "/static/build/",
        filename: "[name]-[chunkhash].js",
        chunkFilename: "bundle-[chunkhash].js"
    },

    plugins: [
        new ExtractTextPlugin("[name]-[contenthash].css")
    ],
}
複製代碼

其中js用的是webpack的chunkhash,而css用的是contenthash,contenthash是根據內容生成的hash。若是不用contenthash,那麼一改js,css的版本號也會跟着改變,這個就有問題了。webpack還有另一個自帶的叫作"[hash]",這個hash是全部文件都用的同一個哈希,也就是說某個文件改了,全部文件的版本號都會跟着改,因此通常不用這個。

運行webpack,若是報了下面這個錯誤:

複製代碼
ERROR in chunk detail [entry]
[name]-[chunkhash].js
Cannot use [chunkhash] for chunk in '[name]-[chunkhash].js' (use [hash] instead)
複製代碼

那你就把plugins裏面的熱替換插件註釋掉就行了,上線的config不須要熱替換:

    plugins: [
        //new hotModuleReplacementPlugin(),
    ],

成功執行後,就會在設定的output目錄下面輸出加上版本號的文件:

複製代碼
.
├── detail-d19e4614a1c4f3c1581b.js
├── home-11198f8526424e8c58ce10a2799793e3.css
└── home-5ec13a52eea2a6faf96a.js
複製代碼

有了版本號以後,下一步是要把html裏面的js/css換成帶版本號的路徑

替換Html裏js/css路徑

 以前在html裏的路徑是test.com,如今要把它換成cdn且帶版本號的路徑,也就是說,目標是要把下面的引入:

<script src="//develop.com/static/build/home.js"></script>

替換成下面的引入,並把新生成的html輸出到built目錄

<script src="//cdn.mycdn.com/test/home-5ec13a52eea2a6faf96a.js"></script>

目測沒有現成符合格式的插件能夠用,能夠自已用node寫一個,不費事。

首先要知道全部文件的對應的版本號,能夠用AssetsPlugin,生成source-map:

複製代碼
var AssetsPlugin = require('assets-webpack-plugin');
    output: {
        publicPath: "//cdn.mycdn.com/static/build/"
    },
    plugins: [
        new AssetsPlugin({filename: './source-map.json', prettyPrint: true}),
    ]
複製代碼

執行webpack以後,就會生成source-map.json,打開這個文件:

複製代碼
{
  "detail": {
    "js": "//cdn.mycdn.com/static/build/detail-c8a2c82ebe2e48e06564.js"
  },
  "home": {
    "js": "//cdn.mycdn.com/static/build/home-380af86bfeb6fcb477a4.js",
    "css": "//cdn.mycdn.com/static/build/home-11198f8526424e8c58ce10a2799793e3.css"
  }
}
複製代碼

根據develop.com開頭的以及最後面的home.js/home.css,就能夠在上面找到對應的路徑名。筆者寫了個腳本,能夠實現這個功能,詳見:version-control-replace-html

到這裏,整個流程就基本完成了。還有一些優化的步驟

優化

1. 優化模塊id

 webpack對於每一個模塊都是用id標誌,而不是用模塊的名字,只是爲了節省空間。還能夠再節省,就是用它自帶的occurrence-order插件將最經常使用的模塊靠前,這樣能夠再節省一點點空間,由於id是從0開始排的,從一位數到n位數。

new webpack.optimize.OccurenceOrderPlugin()

2. 移出版本號

在上面用了common-chunk的插件,抽離公共模塊,在這個common-chunk.js裏,webpack會定義每一個模塊加載的src,以便於加載那些須要動態加載的chunk,以下:

script.src = __webpack_require__.p + "" + chunkId + "-" + {"0":"0cb48ff1ab1d1156015d","5":"e9e7f761f306c648ccef","6":"cbbdf8e3ad1aba34ced0"}[chunkId] + ".js";

從上面能夠看出它會把版本號也寫在裏面,這樣就致使一個問題,每改一個js文件,它的版本號就會變化,就會致使common chunk裏面的內容發生變化,因此它的版本號也得跟着變,也就是說改了一個文件,影響了兩個文件。因此須要把它抽出來,有個插件已經作了這樣的事情,叫作ChunkManifestPlugin

複製代碼
var ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
plugins: [
        new ChunkManifestPlugin({
            filename: "chunk-manifest.json",
            manifestVariable: "webpackManifest"
        })
]
複製代碼

傳兩個參數,一個是輸出文件名,另外一個是變量名,用於上面的script.src,執行webpack後,它會把上面script.src的那一坨東西放到chunk-manifest.json,而後在頁面寫一個內聯的script,定義一個全局變量window.webpackManifest,值爲manifest.json裏面的內容。筆者已在上面的替換版本號的腳本作處理,只需在頁面合適的地方寫上一行:

<!--%webpack manifest%-->

就會把這行替換成一個script標籤。

3. 多個common-chunk的優化

 在上面寫了兩個common chunk,在生成的兩個chunk文件裏面,你會發現大量的的重複代碼,已經失去了公共模塊的做用,這個問題能夠用一個MoveToParentMergingPlugin解決,它會把search-app用到的common-app的模塊所有移到了common-app,search-app就不會重複common-app的內容了。

html保存自動刷新

 上面提到,只要一保存css/js,webpack-dev-server就會自動保存和刷新,可是html/jsp沒辦法(若是你用react開發,能夠用react-hot-loader),其實能夠手動解決這個問題。打開node_modules/webpack-dev-server/client/index.js這個文件,能夠發現webpack是用的sockjs實現自動刷新的。瀏覽器使用sockjs建立socket客戶端,鏈接到webpack的服務,保存更改的時候,服務就向瀏覽器的socket發送消息,接收到這個消息後客戶端就調window.location.reload刷新頁面。因此能夠模仿這個過程,在本地另開一個服務,監聽html的修改,而後向瀏覽器端發送刷新頁面的消息。

具體來講,首先在上面的node_modules/webpack-dev-server/client/index.js這個文件最後面再添加一個socket鏈接:

複製代碼
/*自定義reload window*/
var reload = new SockJS("http://localhost:9999/reload");
reload.onopen = function(){
    console.log("customer reload start.......");
}

reload.onclose = function(){
    console.log("customer reload close.......");
}

reload.onmessage = function(_msg){
    var msg = JSON.parse(_msg.data);
    if(msg.type === "reload"){
        console.log("customer reload window now");
        window.location.reload();
    }
}
複製代碼

這個9999端口的server就是下面要在本地監聽的一個socket服務。在開這個socket服務以前,須要先在本地開一個監聽文件修改的服務,而後再向這個socket服務發送消息。監聽的服務比較好寫,有現成的node包能夠用:chokidar,使用也很是簡單。監聽到修改以後就能夠執行上傳服務器的命令,而後(使用進程間的通訊)再向socket服務發送一個須要刷新的消息,再傳遞給瀏覽器的scoket,如上面的代碼,一收到消息就刷新頁面。具體代碼查看github 

 

 除了優化,在使用中會遇到的一些問題:

解決問題

1. umd的require模式

 有時候會引入外部的庫,這些庫可能會用umd的require模式,判斷是要用requirejs仍是commonjs或是寫個全局的函數:

複製代碼
    /* CommonJS */ if (typeof require === 'function' && typeof module === 'object' && module && typeof exports === 'object' && exports)
        module['exports'] = init(require("ByteBuffer"));
    /* AMD */ else if (typeof define === 'function' && define["amd"])
        define("lib/chat/ProtoBuf", ["./ByteBuffer"], init);
    /* Global */ else(global["dcodeIO"] = global["dcodeIO"] || {})["ProtoBuf"] = init(global["dcodeIO"]["ByteBuffer"]);
複製代碼

這個的問題就在於,只要頁面上有require出現,webpack就會去打包,無論你是寫if裏面還click事件裏面。由於像上面說的,webpack會把異步加載的文件打包成一個boundle文件,同時也會把非異步的打包到一塊兒。像上面那樣寫,它會重複打包,生成好多個bundle。只要加多一個umdREquirePlugin,webpack就能正常打包了。

2. 如何加載外部資源

 webpack是一個打包的工具,它並非像requireJs那樣能夠支持直接require一個外部資源。

例如我要require谷歌地圖:https://maps.googleapis.com/maps/api/js,打包的時候webpack會給出一個warning,說加載不到這個外部資源,運行代碼的時候會報錯,提示沒有這個模塊。

另一個問題是,我須要if else判斷,若是是中國的環境就加載中國域名的谷歌地圖:http://ditu.google.cn/maps/api/js 不然就加載上面的,使用webpack是沒辦法作到的, 使用requireJs就能夠很簡單地直接require一下就行。

但其實這個問題很好解決只要本身寫一個動態加載script的函數就行了,一個兼容性很好的版本:

複製代碼
function loadScript(url, callback){
            var script = document.createElement("script")
            script.type = "text/javascript";
            if (script.readyState){  //IE
                script.onreadystatechange = function(){
                    if (script.readyState == "loaded" || script.readyState == "complete"){
                        script.onreadystatechange = null;
                        callback();
                    }
                };
            } else {  //Others
                script.onload = function(){ callback(); };
            }
            script.src = url;
            document.getElementsByTagName("head")[0].appendChild(script);
        }
複製代碼

詳見:The best way to load external JavaScript

 

webpack雖然是一個利器,可是坑也很多,目前遇到過的不太好解決的問題:

遇到的困難

1. chunkhash

 使用chunkhash有兩個問題,一個是css改變以後,js的版本號也會跟着改變,即便js沒有修改,可是比較這兩個js文件的時候,你會發現這兩個版本號不同的文件內容是徹底如出一轍的。由於chunkhash不是根據文件內容算的hash值。第二個問題是,相同的代碼在不一樣人的機器上打的包的版本號不同。若是使用一些根據文件內容打版本號的插件,如webpack-md5-hash,這個插件是用文件內容做一個md5的計算得出一個版本號,這樣能夠解決上面的兩個問題,可是又引起了新的問題,這個md5的時不時就會出現打的版本號不惟一的狀況,文件內容不一樣、版本號相同,並且這個機率還不小。因此最後仍是放棄了使用這個插件,而後又嘗試了另一個使用sha算法計算,可是這個改了一個文件會使幾個文件的版本號也發生變化。如今仍是使用chunkhash

2. 模塊id發生變化

上文提到,webpack的模塊是用id標誌的,每一個模塊對就一個id,例如util對應2,可是這個id不是固定不變的,在n次修改和打包以後,util的id可能會變成了3,這個就比較坑了,給增量上線形成了阻力,即單獨上一個html有風險。由於在common-chunk裏面,util的id是上次打包的時候定的,可是你此次打包util的id變了,而你只想上home.html,在home.html裏面引的home.js裏面使用到的util的id對不上common-chunk裏面的,致使不能在home裏面正常地加載util這個模塊。一個臨時的解決辦法是,home.js不要使用common-chunk,全部的模塊都打包到home.js裏面就不會有這個問題。

 

綜上對於webpack的介紹基本說完了,後續會繼續研究webpack的打包方式和怎麼樣寫一個webpack的插件。若是上面有什麼不合理或能夠優化的地方還請指出。

相關文章
相關標籤/搜索