爲何要使用webpack對組件或者模塊進行打包?由於可複用庫的模塊化,須要適合在任何場景中進行引用,好比AMD/CMD、CommonJs、ES六、ES5等環境。從webpack打包以後的頭文件來看:javascript
(function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === 'object' && typeof module === 'object') module.exports = factory(); // node else if (typeof define === 'function' && define.amd) define([], factory); // AMD/CMD else if (typeof exports === 'object') exports["Url"] = factory(); else root["Url"] = factory(); })(this, function () { // somecode }
從代碼能夠看出,webpack打包出來的文件是支持多場景的引用方式的。html
筆者使用了一個測試包來分析。先上代碼:vue
這是package文件java
// package.json { "name": "Url", "version": "1.0.0", "description": "測試模塊", "main": "./build/Url.min.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": {}, "keywords": [], "author": "nardo.li", "license": "ISC", "devDependencies": { "babel-core": "^6.25.0", "babel-loader": "^7.1.1", "babel-preset-es2015": "^6.24.1", "webpack": "^3.1.0" } }
這是
webpack
配置文件(babel
和copy
的插件其實沒用到)node
其中output的配置項裏須要寫入libraryTarget: 'umd'
webpack
const webpack = require('webpack') const path = require('path') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { context: path.resolve(__dirname, './lib'), entry: { Url: './Url.js' }, output: { path: path.resolve(__dirname, './build'), filename: '[name].min.js', libraryTarget: 'umd' }, module: { loaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015'] } }] }, plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, './lib'), to: path.resolve(__dirname, './build'), force: true, toType: 'dir', ignore: ['.*'] } ]) ] }
這是模塊文件(隨便寫了一個)es6
// Url.js function Url () {} Url.prototype.getParams = function (URL, key) { if (key) { let reg = new RegExp(key + '=([^&]+)') let val = (URL.match(reg) || ['', undefined])[1] return val } else { let _options = {}, urlStr = '' if (URL.indexOf('?') > 0) { urlStr = /\?([\S]*)/.exec(URL)[1] urlStr.split('&').forEach(function (item, i) { _options[item.split('=')[0]] = item.split('=')[1] }) } return _options } } module.exports = new Url()
webpack
內部模塊的引用和輸出使用的是CommonJs規範(也就是node的模塊),因此模塊輸出方式遵守默認規範module.exports = new Url()
web
接下來,打包編譯。執行$ webpack
命令,打包成功。
爲了方便測試,直接將編譯好的文件複製一份放到筆者選擇最近手上在作的項目中,項目正好使用的是vue
框架。 npm
啓動服務,項目中引用方式以下:json
import Url from './modules/Url'
控制檯打印:
Url undefined
筆者當時的疑問是,難道打包好的文件沒有成功輸出嗎?因而換用了node
模塊引入的方式:
控制檯打印:
Url Object{Url: Url, __esModule: true}
能夠看到,這種方式輸出有值,但彷佛是掛載到輸出對象的Url
屬性上,這種輸出形式與es6
的export Url
有點相似。因而乎,筆者猜測,是否像es6
未指定default
對象時,須要採用{ Url }
解構賦值的方式從輸出對象的引用中獲取到須要的值。
接下來再換了兩種方式:
不出所料,控制檯打印:
Url Url{}
看到這,有點搞清楚了,再回到打包好後的文件頭部:
添加輸出日誌,發現控制檯打印:
彷佛明白了,最終輸出方式的不一樣,應該是webpack切割文件時致使的。在這個項目中,打包好的Url.js文件裏的自調用函數傳入的this
指向了webpack
定義的一個全局對象,用來掛載輸出。而引入未編譯好的模塊包,則不會有這種問題。
爲何會出現這個問題?筆者決定先將引入的Url模塊包換成未編譯時的文件Url2.js
,而後將整個項目打包:
// Url2.js function Url() { } Url.prototype.getParams = function (URL, key) { if (key) { let reg = new RegExp(key + '=([^&]+)') let val = (URL.match(reg) || ['', undefined])[1] return val } else { let _options = {}, urlStr = '' if (URL.indexOf('?') > 0) { urlStr = /\?([\S]*)/.exec(URL)[1] urlStr.split('&').forEach(function (item, i) { _options[item.split('=')[0]] = item.split('=')[1] }) } return _options } } module.exports = new Url()
而後看打包後的app.js
:
能夠看到webpack切割以後的對應的Url輸出方式仍然爲module.exports
,與其餘模塊包無異。
而後換成編譯後的Url.js
,再次打包:
是否是一目瞭然了,最終輸出該文件時沒有傳入exports
對象,因此採用了掛載全局的形式。
簡單來看,是由於webpack對已編譯過的模塊和未編譯的模塊的切割方式不一樣。而根本緣由是,webpack在打包時能識別依賴模塊是否符合UMD規範,若是不是UMD規範模塊(好比註明的jQuery),或編譯過的模塊,都會被判斷爲非規範模塊,以全局掛載的方式輸出。
若是這個編譯後的Url.js
直接在一個靜態html
文件中採用以下方式引用:
<script src="../src/modules/Url.js"></script> <script> console.log('Url', Url) </script>
控制檯打印:
能夠看到,this
指向window
,因此輸出值掛載在了window
對象。
結論
綜上所述,通過webpack打包後的模塊,能夠經過多種方式引用,這也是由於webpack
的編譯過程跟編譯後如何被其餘文件引用沒有關係,只是跟js執行時的環境有關。通常來講Server端node場景不須要引入編譯好的模塊,而在使用vue
等框架開發業務時,通也會結合webpack,因此引入的模塊其實也不須要提早編譯好。
之因此須要對這些業務組件進行打包編譯,首先是由於在小組提供的公用組件中有很多是依賴於第三方庫,其次是由於有很多項目應用場景仍是沿用的以script
標籤加載js資源的方式。