快速打造簡易高效的webpack配置

webpack給前端開發帶來了毋庸置疑的改變,它把JS,圖片,css都做爲模塊處理,同時具備開發便捷,自動化,兼容AMD寫法等等諸多無須贅述的優勢,更使人稱道的是其插件社區很是強大,對於不一樣的業務需求和技術需求社區都有大量插件可供使用。css

凡事都具備兩面性,許多人說:前端開發不再能只需新建HTML文件和JS文件就能夠開始寫代碼了。webpack帶來了更高級更規範的前端開發模式,因爲其自己也在不斷完善中,從1到2再到發佈不久的webpack3,頻繁的修改給新手帶來了許多困惑。並且網絡上各類教程魚目混雜,常常出現別人的教程代碼copy下來在本身的環境卻跑不通的蛋疼問題。就拿devtool配置項來講,官方文檔提供了多達7種的配置方法,連react核心團隊成員Pete Hunt都在twitter上調侃:我分不清webpack的許多配置之間的區別。因此今天咱們拋開那些琳琅滿目的插件和使人煩躁的配置項,筆者和你們一塊兒5分鐘從零搭建一個簡易高效的webpack開發環境。html

首先咱們明確一下需求:前端

  • 打包調試
  • 提取公共代碼
  • 壓縮
  • 熱替換

1.打包調試

第一步,咱們在目標文件夾下安裝webpack(假設已有package.json)
npm i webpack@ -g
cnpm i webpack@ --save-dev
(這裏推薦你們安裝穩定的2.x版本)node

項目結構如圖:react


咱們將編寫的js代碼和樣式文件放置在 app文件夾內(正常項目開發須要 js文件和 less文件更規範的組織文件結構,此處僅爲演示方便)。

第二步,咱們在目標文件夾下新建webpack.config.jswebpack

module.exports = {
    entry:{
        main:__dirname + '/app/main.js',
    },
    output:{
        path:__dirname + '/public',
        filename:'[name].[id].js',//此格式寫法後續會提到爲何
        publicPath:'/public/'
    }
}複製代碼

咱們已經完成了webpack最基礎的部分:添加了文件的輸入和輸出。入口是app文件夾內的main.js文件,出口爲public文件夾。接下來咱們來處理各類文件的解析,就是大名鼎鼎的loader的舞臺了。假設咱們使用es6less開發,那麼咱們須要:
npm i babel-loader babel-core babel-preset-es2015 babel-preset-stage-0 --save-deves6

npm i less less-loader css-loader style-loader --save-devweb

接下來咱們只須要在modules字段下把這些loader加進去:npm

module.exports = {
    devtool:'cheap-module-eval-source-map',//多種選擇,選擇最適合本身的
    entry:{
        main:__dirname + '/app/main.js',
    },
    output:{
        path:__dirname + '/public',
        filename:'[name].[id].js',
        publicPath:'/public/'
    },
    module:{
        loaders:[
            {
                test:/\.js$/,  //解析文件類型
                exclude:/node_modules/,  //排除node_modules文件
                loader:'babel-loader', //使用哪一種loader解析
                query:{
                    presets:['es2015','stage-0']//loader的配置項,解析es6
                }
            },
            {
                test:/\.less$/,
                exclude:/node_modules/,
                loader:'style-loader!css-loader!less-loader'//順序爲從右向左
            }
        ]
    },
}複製代碼

大功告成!json

若是你在全局安裝有webpack的話,能夠在終端敲入webpack並回車,幾秒鐘後,main.js文件已經在public打包出來了!

以後咱們在index.html中引入main.0.js文件,再打開index.html就能夠看到效果了。

以上步驟,咱們已經實現了文件的打包調試,可是如今有個問題擺在咱們面前:第三方庫代碼和業務代碼打包到了同一個文件main.0.js內,每次更新代碼都要更新整個文件。那麼接下來咱們對代碼進行拆分。

2.提取公共代碼

引入CommonsChunkPlugin插件,在webpack.config.js添加以下內容:

module.exports = {
    devtool:'cheap-module-eval-source-map',
    entry:{
        main:__dirname + '/app/main.js',
        vendor:'moment'
    },
    output:{
        path:__dirname + '/public',
        filename:'[name].[id].js',
        publicPath:'/public/'
    },
    module:{
        loaders:[
            {
                test:/\.js$/,
                exclude:/node_modules/,
                loader:'babel-loader',
                query:{
                    presets:['es2015','stage-0']
                }
            },
            {
                test:/\.less$/,
                exclude:/node_modules/,
                loader:'style-loader!css-loader!less-loader'
            }
        ]
    },
    plugins:[
            new webpack.optimize.CommonsChunkPlugin({
                names:['vendor','manifest']
         })
     ]
}複製代碼

咱們看到向插件的構造函數傳入了兩個參數vendormanifest,以及咱們在entry也加入了新的入口momentmoment是經常使用的時間處理的第三方庫,咱們能夠經過npm i moment --save-dev進行安裝。而entry處的vendor將成爲output字段filename[name]的值,也就是說將打包出main.x.jsvendor.x.js兩個文件,main.x.js文件將保存咱們的業務代碼,vendor.x.js將保存moment的代碼,這樣咱們將公共代碼和業務代碼進行了初步分離。

在新添加的CommonmChunkPlugin插件中,咱們添加了manifest值,這是爲何呢?若是你不添加這個值,你在打包時會發現,main.x.js有更新,vendor.x.js仍是有更新,並未真正實現"分離"。官方文檔對此的解釋是:

The issue here is that on every build, webpack generates some webpack runtime code, which helps webpack do it’s job. When there is a single bundle, the runtime code resides in it. But when multiple bundles are generated, the runtime code is extracted into the common module, here the vendor file.

大體的意思就是說,webpack每次編譯時運行的代碼會影響到hash值的變化,當只有一個打包文件時這部分代碼會塞進去,當有多個打包文件時,這部分代碼會進入公共的vendor。因此解決辦法是使用manifest字段把這部分代碼從vendor中做爲一個公共模塊抽出來,從而不會影響vendor

將以上的配置寫入webpack.config.js,運行webpack命令,咱們發現業務代碼和公共庫代碼成功分離,改寫main.1.js文件的內容,再次打包,發現vendor文件並無變化,成功!

當咱們再進行打包時,發現又會多出了新的main.x.js等文件,打包三次就會出現三個main.x.js文件,此時該怎麼辦呢?咱們可使用clean-webpack-plugin插件:

npm i clean-webpack-plugin --save-dev

而後在webpack.config.js中引入:

var CleanWebpackPlugin = require('clean-webpack-plugin');
    new CleanWebpackPlugin(
            ['public/main.*.js','public/manifest.*.js'],//要刪除的文件目錄匹配
            {
                root:__dirname,
                verbose:true,
                dry:false
            }
        ),複製代碼

這樣咱們每次在打包新的代碼時,舊文件就會刪除,不會再出現同一份文件存在多份的狀況。

3.壓縮

在webpack中,圖片,css,js等等其餘資源皆可壓縮,本文僅以壓縮js爲例。
安裝插件:
npm i uglifyjs-webpack-plugin --save-dev

webpack.config.js中引入:

var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
new UglifyJsPlugin({
    beautify:true,
    exclude:['/node_modules/'],
    compress:{
        warnings:false
    },
    output:{
        comments:false
    }
})複製代碼

咱們指定了壓縮的方法,排除了不須要壓縮的node_modules部分,同時咱們去除了comments部分(comments爲@license等註釋,是可觀的壓縮空間)。再次在終端輸入打包命令,可見js打包後的體積有使人滿意的減少。

4.熱替換

webpack老是繞不開熱替換的話題。熱替換的功能配置和原理是一大話題,三天三夜也說不完,也並不是本文重點,本文只提供簡易高效的配置方法。

熱替換存在兩種使用方式,clinodecli方式無需添加新的熱替換插件,且無需在入口處添加webpack-dev-server等入口,故本文采用cli使用方式。

webpack.config.js中添加devServer字段,加入以下代碼:

devServer:{
    inline:true,
    hot:true
},複製代碼

保存後運行webpack-dev-server --inline --hot --progress,再修改下main.less文件的樣式,會發現瀏覽器並無刷新,但頁面已經發生了變化,咱們的熱替換功能也成功加入了!

tips:

在實際項目打包時,能夠將filename字段的值換爲[name].[chunkhash].js,其中[chunkhash]爲webpack每次打包後給每一個模塊的標識值,這個值每次打包後都會更換。爲何在此處咱們使用[id]呢,由於chunkhash與熱替換存在衝突,終端會有報錯,那麼使用id能夠算做一個解決方案。這就引伸出另外一話題,咱們可使用兩套webpack配置分別用於生產環境和開發環境,經過webpack指定config來進行打包。例如咱們在開發環境使用id,在生產環境去掉熱替換並使用hash的方式。並且,一些壓縮插件也不必在開發環境過分使用,兩套配置能讓webpack發揮最大的威力。

另外,chunkhashhash有區別,chunkhash顧名思義是模塊的標識,而hash是webpack每次編譯的標識值,不一樣的資源如js和css存在chunkhash解耦的問題,此處不進行過多討論。

關於熱替換的更多細節和原理,參考文章:www.cnblogs.com/wonyun/p/70…

5.運行

咱們知道,每次打包後,都會有新的main.x.js文件生成,其hash值每次打包後都會發生變化,難道咱們的index.html文件須要每次打包後都手動修改main.x.js的路徑嗎?還好社區提供了html-webpack-plugin插件,能夠在已有html模板的條件下自動爲咱們生成帶有最新代碼的html文件:

npm i html-webpack-plugin --save-dev

webpack.config.js中引入:

var HtmlWebpackPlugin = require('html-webpack-plugin');
new HtmlWebpackPlugin({
    title:'demo',
    template:'index.html'
}),複製代碼

在終端運行打包命令,咱們看到public文件夾下生成了新的index.html文件:

之後咱們再進行調試時,以本文爲例,則須要打開localhost:8080/public/index.html,由於每次webpack的HtmlWebpackPlugin都會把新的js文件加入到這個html文件內。在開發所有完成後,咱們能夠將js路徑寫死,添加到原有的index.html文件中。

如下是咱們webpack.config.js所有的配置;

var webpack = require('webpack');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
    devtool:'cheap-module-eval-source-map',
    entry:{
        main:__dirname + '/app/main.js',
        vendor:'moment'
    },
    output:{
        path:__dirname + '/public',
        filename:'[name].[id].js',
        publicPath:'/public/'
    },
    devServer:{
        inline:true,
        hot:true
    },
    module:{
        loaders:[
            {
                test:/\.js$/,
                exclude:/node_modules/,
                loader:'babel-loader',
                query:{
                    presets:['es2015','stage-0']
                }
            },
            {
                test:/\.less$/,
                exclude:/node_modules/,
                loader:'style-loader!css-loader!less-loader'
            }
        ]
    },
    plugins:[
        new CleanWebpackPlugin(
            ['public/main.*.js','public/manifest.*.js'],
            {
                root:__dirname,
                verbose:true,
                dry:false
            }
        ),
        new webpack.optimize.CommonsChunkPlugin({
            names:['vendor','manifest']
        }),
        new HtmlWebpackPlugin({
            title:'demo',
            template:'index.html'
        }),
        new UglifyJsPlugin({
            beautify:true,
            exclude:['/node_modules/'],
            compress:{
                warnings:false
            },
            output:{
                comments:false
            }
        })
    ]
}複製代碼

整個項目,咱們在app文件下的main.js內寫業務代碼,main.less寫樣式,在public/index.html下使用熱替換進行調試,打包後的壓縮文件在public文件夾下,而且對業務代碼,第三方代碼進行了清晰地區分。

使用這份webpack配置,咱們實現了:

  • 工程的打包調試
  • 公共代碼提取,提升開發效率
  • 資源壓縮
  • 熱替換

這份配置麻雀雖小,五臟俱全。本文還有許多不完善之處,好比一些插件的使用方法,原理沒有與你們講清楚,但webpack實在太龐大了,一個插件的使用方法和原理均可以寫上千字的文章了,學習不可淺嘗輒止,但也不能太鑽牛角尖,與你們共勉~

相關文章
相關標籤/搜索