Webpack是一個現代js應用的模塊打包機。若是一個文件依賴另外一個文件,webpack認爲這就存在一個依賴關係。無論另外一個文件是什麼內容,image,css或js都被看成一個模塊。Webpack從entry points開始構建依賴關係圖,將應用所須要的全部模塊處理成瀏覽器可識別的格式,再打包成一批(個)文件,未來發送給瀏覽器。
chunk:多個模塊打包以後的代碼集合。
css
1.全局安裝webpackhtml
npm install -g webpackvue
若是沒有package.json,使用CLI建立:npm initnode
2.項目根目錄安裝webpackreact
npm install --save-dev webpackjquery
3.配置webpack.config.js文件,運行webpack命令時不用再輸入參數webpack
4.運行webpackgit
在項目根目錄經過CLI調用:$webpack #必須全局安裝,不然須要指定webpack.bat的文件路徑
webpack默認只會在當前shell工做目錄找webpack.config配置文件;若是配置文件不在shell當前工做目錄,能夠經過--config ./file/pathOfConfig 進行傳參數。若是沒有配置文件,則須要輸入webpack <entry> <output>.
github
webpack內部自定義了一些參數的映射,運行webpack時能夠傳入,如--debug映射爲debug:true;-p映射爲--optimize-minimize
--optimize-occurrence-order.
單雙連字符的使用沒有特殊規定:Two hyphen-minus characters ( -- ) are used on some programs to specify "long options" where more descriptive option names are used.單連字符取代駝峯命名,用於鏈接單詞或指定很短的參數選項(一般只有一個字母)。
或在Node中調用web
var webpack = require('webpack'); //只須要本地安裝便可 var config2 = require('./webpack.config'); var compiler = webpack(config2);
webpack的配置文件webpack.config.js
若是該文件存在,只須要執行CLI命令:$webpack,webpack就會自動讀取配置,並輸出打包的文件。配置文件是以js文件的形式代替命令行形式參數,是符合commonjs規範的node模塊,裏面能夠有function和require(),只要最後以對象形式輸出配置便可。
具體配置信息以下:
devtool:source-map.生成何種類型的source map,方便打包後的調試。
entry: webpack從哪裏開始構建整個依賴關係。動態的模塊不能是entry point。通常一個HTML頁面一個entry point。若是有多個entry point,能夠採用對象形式定義。不支持通配符配置。若是確實有須要,用以下sinppet,而後將entries傳入webpack.config中,或利用node-glob模塊讀取文件名。
var glob = require("glob"); // Match files using the patterns the shell uses
entry: glob.sync("./src/scripts/*.js")
output:打包後輸出的路徑(path)和輸出的文件名(filename)。能夠有多個entry point,但只能有一個output。
若是是多entry,filename須要使用替換保證每一個輸出文件的名字惟一:
[name] is replaced by the name of the chunk.
[hash] is replaced by the hash of the compilation.(compilation對象的hash值,和webpack的compiler環境相關,同一次編譯時的值同樣)
[chunkhash] is replaced by the hash of the chunk.(具體模塊的hash值,和整個文件內容相關。內容不變,chunkhash值不變)
loaders:webpack將每一個資源文件看成一個模塊,可是webpack只能處理js。經過loaders將其餘格式的資源文件轉換成模塊後(好比將JSX語言轉換成js、將coffeescript轉換成js)加入依賴關係中,最後打包輸出。須要單獨安裝包而且在webpack.config.js下的modules關鍵字下進行配置loaders後才能使用。 loders下的字段:
test:肯定哪些文件將被加載器處理;
user:使用哪一個加載器,」-loader」後綴不能省略;
include/exclude:必須處理或屏蔽不須要處理的文件(文件夾)(可選);
options:當前loader須要的特殊配置(可選),如babel-loader的.babelrc配置文件裏的信息。webpack2.5以前爲query
plugins:擴展Webpack功能,會在整個構建過程當中生效,執行相關的任務。須要經過require()引入並將插件實例添加到plugins數組中。
resovle:經過別名、擴展名、根路徑或備用目錄等屬性決定webpack如何更快找到import/require()的模塊。
resolve.alias:用別的路徑或模塊替代。把requirejs項目改成webpack項目時能夠利用此屬性。
resolve.extensions:經過擴展名組成的數組解析require()的模塊文件。好比加載一個coffeeScript文件,須要增長’.coffee’擴展名。若修改後必須增長空字符串爲第一個元素。
經常使用命令:webpack –watch改動代碼後自動打包
自動刷新頁面顯示修改後的效果
使用webpack-dev-server模塊構建本地服務器:
npm install --save-dev webpack-dev-server
安裝完畢以後運行$webpack-dev-server --open
若是報命令不識別的錯誤,可在package.json的script字段添加("start": "webpack-dev-server --progress --hot --inline --content-base ./dist"),而後運行$npm start命令;
或再全局安裝一次。
壓縮bundle.js文件的大小
//會加載整個lodash,因此體積更大 import { concat, sortBy, map, sample } from 'lodash'; //Use relative file paths,只加載須要的函數模塊,體積更小 //例如import { Button } from 'antd';使用babel-plugin-import插件==》var _button = require('antd/lib/button'); import concat from 'lodash/concat'; import sortBy from 'lodash/sortBy'; import map from 'lodash/map'; import sample from 'lodash/sample';
生產環境中要刪除sourcemap
使用 webpack –p
命令構建
bundle.js
經過命令webpack --profile --json >> stats.json生成構建過程的文件,在 https://webpack.github.io/analyse上分析哪一個模塊佔用的空間大。或者使用webpack-bundle-analyzer插件,用視圖的形式分析bundle.js中的每一個模塊。
var debug = process.env.NODE_ENV !== 'production'; //是不是開發模式,生產模式時須要去除sourcemap const webpack = require('webpack'); //訪問內置插件 const path = require('path'); var htmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm const config = { devtool: debug ? 'cheap-eval-source-map' : undefined, //開發模式時生成何種類型的源代碼映射文件,方便調試打包後的代碼 entry: { //webpack打包的切入點,通常一個頁面的業務代碼定義一個entry point,key爲輸出chunk的name home: ['./footer.js', './home.js'], //若爲[string],則將多個相互獨立的文件及其依賴打包成一個chunk about: './about.js', contact: './contact.js', vendor: ['jquery', 'lodash', 'vue'] // 使用commonschunkplugin抽離的公共模塊 }, output: { //輸出配置項 path: path.resolve(__dirname, 'dist'), //打包後輸出的路徑 filename: '[name].[chunkhash:8].js', //打包後輸出的文件名,多個entry須要替換 chunkFilename: '[id].js', //name of non-entry chunk files。瀏覽器按需加載的chunk publicPath: 'http://cdnOfCompany.com/' // 供webpack及插件在生產模式下更新內嵌到css、html文件裏的cdn路徑 }, resolve: { extensions: ['', '.js', '.vue'], fallback: [path.join(__dirname, '../node_modules')], alias: { '@': resolve('src'), //別名,js中import時使用,在src和@import中須要加 ~ 'moment': path.join(__dirname, '../node_modules/moment/min/moment-with-locales.min.js'), // 爲模塊配置別名 } }, externals: { 'jquery': 'jQuery' // 但願經過script標籤引進的依賴,能夠require/import,但不打包進bundle。必須先加載,webpack會以module的形式對其引用。 }, module: { // webpack2.0的字段名作了些修改 rules: [//每條rule定義對應的module如何生成,是loaders的alias { test: /\.(js|jsx)$/, //一個匹配loaders所處理的文件的拓展名的正則表達式 exclude: /(node_modules|bower_components)/, use: [ //使用到的loader及其配置 { loader: 'babel-loader', //使用的加載器名稱,-loader後綴不能省略;也可經過?query設置參數 options: { //爲當前loader設置的參數,對於babel能夠提取出單獨放在.babelrc文件中; presets: ['react', 'es2015'], plugins: [require('react-html-attrs')] } } ] }, { test: /\.css$/, exclude: /(node_modules)/, //手動添加必須處理(include)或屏蔽不須要處理的文件(文件夾) use: [//使用多個loader處理同一源文件,從右往左順序處理 //css-loader將css文件看成一個模塊引入當前模塊中,類名相一樣式也不會衝突;style-loader將當前模塊中的樣式引入頁面的style元素中 { loader: 'style-loader' }, { loader: 'css-loader', options: { importLoaders: 1 } }, { loader: 'less-loader', options: { noIeCompat: true } } ] }, { // 解決圖片的路徑在發佈先後不一致的問題(資源打包前在/src/assert和/static中,打包後所有歸併到/dist/static目錄下) //vue-cli中:assert中的資源要通過webpack處理,只能經過相對路徑(視爲模塊依賴)訪問;/static中的資源不用通過webpack處理,經過絕對路徑訪問 test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, // 小於8kb的直接轉爲base64 loader: 'url-loader', options: { limit: 8192, name: utils.assetsPath('img/[name].[hash:7].[ext]') // 被加載器處理後重寫的url路徑,打包以後圖片也會被複制到dist的該路徑中 } } ] }, plugins: debug ? [] : [//添加插件實例 //依據模板,生成最終的html文件。該文件中會生成標籤自動引用了打包後的js/css文件,不用手動一個一個修改url。 new htmlWebpackPlugin({ // 一個單頁應用配置一個該實例,多個頁面配置多個 filename: 'index.html', //輸出文件的名字 template: './src/index.tpl', //使用的模板文件 title: 'this is home page', //傳入模版的參數,在模版中能夠經過<%=htmlWebpackPlugin.options.title%>獲取該值;也能夠傳遞其餘自定義的屬性供模板使用,如css等靜態資源,不會被當作依賴被打包。 favicon: 'path/to/yourfile.ico', //生成的 html 標籤中會包含這樣一個 link 標籤指向favicon inject: 'body', //插入script的位置 chunks: ['vendor', entry], //不設置時默認包含entry中全部的chunks(包括CommonsChunkPlugin插件生成的chunk) excludeChunks: ['contact', 'other chunk']//排除引入的chunk }), new webpack.optimize.DedupePlugin(), new webpack.NoErrorsPlugin(), //構建過程當中發生error時跳過錯誤繼續生成 new webpack.optimize.OccurenceOrderPlugin(), new webpack.DefinePlugin({ // webpack定義的全局常量,編譯後會替換成value對應的文本,減小手動一個修改 去除對warning和一些提示信息的代碼 'process.env': { 'NODE_ENV': JSON.stringify('production') } }), // https://github.com/LiPinghai/UglifyJSDocCN/blob/master/README.md new webpack.optimize.UglifyJsPlugin({ //簡化壓縮代碼,去除console,warning等語句 sourcemap: false, compress: { screw_ie8: true, warnings: false, drop_debugger: true, // 去除debugger語句 pure_funcs: ['console.log'], // 發佈時不被打包的函數 dead_code: true // 去除不被執行的代碼 }, mangle: { screw_ie8: true }, output: { comments: false, screw_ie8: true } }), // http://webpack.github.io/docs/list-of-plugins.html // https://stackoverflow.com/questions/39548175/can-someone-explain-webpacks-commonschunkplugin new webpack.optimize.CommonsChunkPlugin({ //把全部入口節點的公共代碼提取出來,生成一個chunk(name爲common.js)進行復用 names: ['vendor', 'manifest'], // 須要抽離的公共模塊的名字。配置manifest,其餘代碼改變但vendor不變化時common.js不變,但manifest的hash值改變(由於含有webpack runtime代碼) filename: 'commons.js', // 最後生成公共chunk的文件名,優先於output裏的filename配置。不建議固定文件名,若是更新了不方便用戶從新下載。 minChunks: Infinity // with more entries, this ensures that no other module goes into the vendor chunk }), new webpack.DllReferencePlugin({ // 引用dll包中的模塊,在dll包中沒法找到時纔打包進當前bundle context: __dirname, manifest: require('./manifest.json') }), // 複製/static目錄中的資源到 dist/static下對應的目錄中 new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, // dist/static下 ignore: [] } ]) ], watch: true, //監聽源文件的修改,以後recompile,但不刷新頁面。爲了提升性能,須要將safe write關閉,用save file觸發。 watchOptions: { aggregateTimeout: 300, //從修改文件開始到rebuilding的延遲時間,將多個改變積累到一塊兒 poll: 1000, //Check for changes every second ignored: /node_modules///排除監聽的目錄 }, devServer: { //構建本地服務器實時提供服務並刷新,編譯後的文件保存在內存中,因此比較快。 contentBase: './', //本地服務器所提供服務的內容來源 port: 8080, //監聽端口,和編輯器的端口不同 colors: true, //終端中輸出結果爲彩色 historyApiFallback: true, //全部的跳轉將指向index.html inline: true //會輸出錯誤 } }; module.exports = config;
code-spitting
/**每一個打包後的bundle都有一個webpack啓動函數,負責在客戶端啓動js;使用commonchunkplugin後runtime函數在最後一個chunk中**/ (function(modules) { // webpackBootstrap
緣由:webpack所有打包進一個文件,只要一修改代碼,hash就會改變,客戶端只能從新下載沒法利用緩存。另外有些模塊特定的用戶不必定用到,首次訪問時所有加載會影響首屏速度。
方法:
1. 分離業務邏輯,使用CommonsChunkPlugin打包第三方類庫,注意須要在業務邏輯模塊以前下載公共模塊,由於webpackBootstrap函數會在公共模塊中。
<script src="common.js" charset="utf-8"></script> // webpackBootstrap <script src="app.js" charset="utf-8"></script> // webpackJsonp
2. 動態加載指定模塊
import()語法:
// other logic import('./sideAd').then(_ => doSomething()).catch(error => 'An error occurred while loading the component'); require.ensure([dependency],function(require){var footAd = require('./footAd') }) // webpack在靜態打包時,會將sideAd模塊當作單獨的chunk打包,將footAd及其依賴單獨打包(被webpackJsonp函數包裹),這樣當前模塊文件體積就會減少
webpack將import('...')轉變成``__webpack_require__.e/* import() */(n).then()``*或者``__webpack_require__.e/* require.ensure */(0).then((function (require) {__webpack_require__(n)}))``的形式,在瀏覽器中webpackbootstrap會*經過__webpack_require__.e(JSONP方法)動態加載chunkId爲n的模塊。*
require():在webpack項目中只能靜態打包到bundle中。依賴都被加載且都被執行後才執行回調函數。
require.ensure():單獨打包,動態加載。依賴被下載後不會被執行,只有在回調函數**再次**使用require(module)後,這個模塊纔會被執行。
3. 使用DllPlugin和DllReferencePlugin
將常用的庫文件打包到dll包中,它自己不能運行,是用來給業務代碼引用的。分離了第三方庫,打包速度更快,並且不受業務模塊的chunkhash影響。使用步驟:
a. 使用webpack.DllPlugin插件打包第三方庫
b. 在主配置文件中使用webpack.DllReferencePlugin插件引用dll包
4. 對於單頁應用,可使用bundle-loader異步加載各路由對應的組件(懶加載),儘快顯示首屏。
5. 在vue-router中,使用``const Foo = () => import('./Foo.vue')``懶加載。
routes: [ { path: '/', name: 'Hello', component: () => import('../module/HelloWorld') }, { path: '/check', name: 'check', component: r => require(['@/pages/spa/module/CheckBox'], r) } ]