大概算是一份教程吧,只不過效果肯定不如視頻演示之類的好..
Webpack最近在英文社區上經常看到,留了心,但進一步了解是通過下邊的視頻: 視頻: How Instagram.com Works , Peter Hunt Peter Hunt也是React的傳教士,我由於對React的關注因此細看了視頻 再後來是出現React Hot Loader這樣的開發神器,我認為Webpack應該很棒http://gaearon.github.io/ react-hot-loader/ 為了解決簡聊當中一些問題,我消耗了很多時間了解Webpack,整理在這裡
Webpack最近在英文社區上經常看到,留了心,但進一步了解是通過下邊的視頻: 視頻: How Instagram.com Works , Peter Hunt Peter Hunt也是React的傳教士,我由於對React的關注因此細看了視頻 再後來是出現React Hot Loader這樣的開發神器,我認為Webpack應該很棒http://gaearon.github.io/ react-hot-loader/ 為了解決簡聊當中一些問題,我消耗了很多時間了解Webpack,整理在這裡
Webpack 是什麼
https://github.com/webpack
Webpack是德國開發者Tobias Koppers開發的模塊加載器
Instagram工程師認為這個方案很棒,似乎還把作者招過去了
在Webpack當中,所有的資源都被當作是模塊, js, css,圖片等等..
因此, Webpack當中js可以引用css, css中可以嵌入圖片dataUrl
Webpack是德國開發者Tobias Koppers開發的模塊加載器
Instagram工程師認為這個方案很棒,似乎還把作者招過去了
在Webpack當中,所有的資源都被當作是模塊, js, css,圖片等等..
因此, Webpack當中js可以引用css, css中可以嵌入圖片dataUrl
對應各種不同文件類型的資源, Webpack有對應的模塊loader
比如CoffeeScript用的是
比如CoffeeScript用的是
coffee-loader
,其他還有很多: http://webpack.github.io/docs/list-of-loaders.html 大致的寫法也就這樣子: module : {
loaders: [
{ test: /\.coffee$/ , loader: 'coffee-loader' },
{ test: /\.js$/ , loader: 'jsx-loader?harmony' } // loaders can take parameters as a querystring
]
},
CommonJS 與AMD 支持
Webpack對CommonJS的AMD的語法做了兼容,方便遷移代碼
不過實際上,引用模塊的規則是依據CommonJS來的
不過實際上,引用模塊的規則是依據CommonJS來的
require ( 'lodash' ) //從模塊目錄查找
require ( './file' ) //按相對路徑查找
AMD 語法中, 也要注意, 是按CommonJS 的方案查找的
define ( require , exports. module ) ->
require ( 'lodash' ) # commonjs當中這樣是查找模塊的
require ( './file' )
特殊模塊的Shim
比如某個模塊依賴
Webpack有不少的Shim的模塊,比如
window.jQuery
,需要從npm模塊中將jquery
掛載到全局Webpack有不少的Shim的模塊,比如
expose-loader
用於解決這個問題https://github.com/webpack/docs/ wiki/shimming-modules 其他比如從模塊中導出變量...具體說明有點晦澀..手頭的兩個例子,比如我們用到Pen這個模塊,
這個模塊對依賴一個
而
最終的寫法就是做Shim處理直接提供支持:
這個模塊對依賴一個
window.jQuery
,可我手頭的jQuery是CommonJS語法的而
Pen
對象又是生成好了綁在全局的,可是我又需要通過require('pen')
獲取變量最終的寫法就是做Shim處理直接提供支持:
{test: require .resolve( 'jquery' ), loader: 'expose?jQuery' },
{test: require .resolve( 'pen' ), loader: 'exports?window.Pen' },
基本的使用
安裝
可以使用參數,也可以配置
建議按照Peter Hunt給的教程走一遍,基本的功能都會用到了https://github .com/petehunt/webpack-howto
webpack
模塊之後,可是使用webpack
這個命令行工具可以使用參數,也可以配置
webpack.config.js
文件直接運行webpack
調用建議按照Peter Hunt給的教程走一遍,基本的功能都會用到了https://github .com/petehunt/webpack-howto
簡單的例子就是這樣一個文件,可以把
./main.js
作為入口打包bundle.js
:// webpack.config.js
module .exports = {
entry: './main.js' ,
output: {
filename: 'bundle.js'
}
};
查找依賴
Webpack是類似Browserify那樣在本地按目錄對依賴進行查找的
可以構造一個例子,用
例子當中
注意一下,
可以構造一個例子,用
--display-error-details
查看查找過程, 例子當中
resolve.extensions
用於指明程序自動補全識別哪些後綴, 注意一下,
extensions
第一個是空字符串 !對應不需要後綴的情況.// webpack.config.js
module .exports = {
entry: './a.js' ,
output: {
filename: 'b.js'
},
resolve: {
extensions: [ '' , '.coffee' , '.js' ]
}
}
// a.js
require ( './c' )
➤➤ webpack --display-error-details
Hash : e38f7089c39a1cf34032
Version : webpack 1.5 .3
Time : 54 ms
Asset Size Chunks Chunk Names
b.js 1646 0 [emitted] main
[ 0 ] ./a.js 15 { 0 } [built] [ 1 error]
ERROR in ./a.js
Module not found : Error : Cannot resolve 'file' or 'directory' ./c in /Users/chen/Drafts/webpack/details
resolve file
/Users/chen/Drafts/webpack/details/c doesn 't exist
/Users/chen/Drafts/webpack/details/c.coffee doesn' t exist
/Users/chen/Drafts/webpack/details/c.js doesn 't exist
resolve directory
/Users/chen/Drafts/webpack/details/c doesn' t exist (directory default file)
/Users/chen/Drafts/webpack/details/c/package.json doesn 't exist (directory description file)
[/Users/chen/Drafts/webpack/details/c]
[/Users/chen/Drafts/webpack/details/c.coffee]
[/Users/chen/Drafts/webpack/details/c.js]
@ ./a.js 2:0-14
./c
是不存在,從這個錯誤信息當中我們大致能了解Webpack是怎樣查找的大概就是會嘗試各種文件名,會嘗試作為模塊,等等
一般模塊就是查找
node_modules
,但這個也是能被配置的: http://webpack.github.io/docs/configuration.html#resolve-modulesdirectoriesCSS 及圖片的引用
require ( './bootstrap.css' );
require ( './myapp.less' );
var img = document .createElement( 'img' );
img.src = require ( './glyph.png' );
上邊的是JavaScript代碼, CSS跟LESS,還有圖片,被直接引用了
實際上CSS被轉化為
但是要主要在
實際上CSS被轉化為
<style>
標籤,而圖片可能被轉化成base64格式的dataUrl 但是要主要在
webpack.config.js
文件寫好對應的loader
:// webpack.config.js
module .exports = {
entry: './main.js' ,
output: {
path: './build' , // This is where images AND js will go
publicPath: 'http://mycdn.com/' , // This is used to generate URLs to eg images
filename: 'bundle.js'
},
module : {
loaders: [
{ test: /\.less$/ , loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
{ test: /\.css$/ , loader: 'style-loader !css-loader' },
{test: /\.(png|jpg)$/ , loader: 'url-loader?limit=8192' } // inline base64 URLs for <=8k images, direct URLs for the rest
]
}
};
url-loader
.demo {
background-image : url ( 'a.png' ) ;
}
那麼對應這樣的loader配置就能把
並且按照文件大小,或者轉化為base64,或者單獨作為文件:
a.png
抓出來, 並且按照文件大小,或者轉化為base64,或者單獨作為文件:
module : {
loaders: [
{test: /\.(png|jpg)$/ , loader: 'url-loader?limit=8192' } // inline base64 URLs for <=8k images, direct URLs for the rest
]
}
file-loader
由於
url-loader
是對file-loader
的一個封裝,以因此帶有後者一些功能: https://github.com/webpack/file-loader 比如說, file-loader
有不弱的定義文件名 的功能
require ( "file?name=[path][name].[ext]?[hash]!./dir/file.png" )
對應
url-loader
當中如果文件超出體積,就給一個這樣的文件名..打成多個包
有時考慮類庫代碼的緩存,我們會考慮打成多個包,這樣不難
比如下邊的配置,首先
然後
比如下邊的配置,首先
entry
有多個屬性,對應多個JavaScript包, 然後
commonsPlugin
可以用於分析模塊的共用代碼,單獨打一個包出來: https://github.com/petehunt/webpack-howto#8-optimizing-common-code https://github.com/webpack/docs/wiki/optimization#multi-page-app// webpack.config.js
var webpack = require ( 'webpack' );
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin( 'common.js' );
module .exports = {
entry: {
Profile: './profile.js' ,
Feed: './feed.js'
},
output: {
path: 'build' ,
filename: '[name].js' // Template based on keys in entry above
},
plugins: [commonsPlugin]
};
對文件做revision
output: { chunkFilename: "[chunkhash].bundle.js" }
plugins: [
function () {
this .plugin( "done" , function (stats) {
require ( "fs" ).writeFileSync(
path.join(__dirname, "..." , "stats.json" ),
JSON .stringify(stats.toJson()));
});
}
]
同時,可以註冊事件,拿到生成的帶Hash的文件的一個表
但是拿到那個表之後,就需要自己寫代碼進行替換了..這有點麻煩
官網的Issue裡提到個辦法是生成HTML時引用
我此前的方案是生成HTML之後再進行替換,相對賴上生成時寫入更好一些
但是拿到那個表之後,就需要自己寫代碼進行替換了..這有點麻煩
官網的Issue裡提到個辦法是生成HTML時引用
stats.json
的數據, 我此前的方案是生成HTML之後再進行替換,相對賴上生成時寫入更好一些
上線
- 另一份配置文件
用
這個文件當中可以寫不一樣配置,專門用於代碼上線時的操作
webpack --config webpack.min.js
指定另一個名字的配置文件這個文件當中可以寫不一樣配置,專門用於代碼上線時的操作
- 壓縮JavaScript
因為代碼都是JavaScript,所以壓縮就很簡單了,加上一行plugin就好了http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
plugins: [
new webpack.optimize.MinChunkSizePlugin(minSize)
]
- 壓縮React
React官方提供的代碼是已經合併的,這個是Webpack不推薦的用法,
在合併話的代碼上進行定制有點麻煩, Webpack提供了設置環境變量來優化代碼的方案:
在合併話的代碼上進行定制有點麻煩, Webpack提供了設置環境變量來優化代碼的方案:
new webpack.DefinePlugin({
"process.env" : {
NODE_ENV: JSON .stringify( "production" )
}
})
- CDN
替換CDN這個工作, Webpack也內置了,設置
output.publicPath
即可http://webpack.github.io/docs/configuration.html#output-publicpath代碼熱替換
雖然文檔上寫得挺複雜的, 但如果只是簡單的功能還是很容易的
- 第一步,把
'webpack/hot/dev-server'
加入到打包的代碼當中,
這個是對應node_modules/webpack/
目錄當中的文件的:
entry: {
main: [ 'webpack/hot/dev-server' , './main' ],
vendor: [ 'lodash' , './styles' ]
},
- 啟動服務器, 比如我是這樣子的
webpack-dev-server --hot --quiet
React Hot Replace
entry: [
'webpack-dev-server/client?http://0.0.0.0:8080' , // WebpackDevServer host and port
'webpack/hot/only-dev-server' ,
'./scripts/index' // Your appʼs entry point
]
我特意問了下作者為什麼上邊配置看起來不一樣.. https://github.com/gaearon/react-hot-loader/issues/73#issuecomment-73679446 回復大致說是為了避免自動的強制刷新他用了特別的寫法.. 關於這項功能具體如何實現,我沒有深入了解過...
hot replace 非靜態的網頁
上邊
於是文檔上給出了一套稍微複雜一些的方案,用來配合其他的服務器調試
大致的思路是這樣的:
localhost:8080
的方案並不適合複雜的頁面, 於是文檔上給出了一套稍微複雜一些的方案,用來配合其他的服務器調試
大致的思路是這樣的:
- Webpack打包生成的那些靜態資源用服務器A進行serve
這裡說的A就是上邊說的這個:
webpack-dev-server --hot --quiet
- 我們的HTML由B渲染, B會引用A serve的靜態資源
B生成的頁面當中加上類似這樣的代碼:
< script src = "http://<A的地址>/assets/bundle.js" >
還可能要設置一下
3.文件修改時,
output.publicPath
,把所有靜態資源指向A 3.文件修改時,
webpack-dev-server
通過socket.io
通知客戶端更新單獨打包CSS
因為公司裡有這個需要求,強制把CSS從js文件當中獨立出來.
官方文檔是以插件的形式做的: http://webpack.github.io/docs/stylesheets.html#separate-css-bundle 參考文檔但是注意一下函數參數,第一第二個參數是有區別的,比如這樣用:
官方文檔是以插件的形式做的: http://webpack.github.io/docs/stylesheets.html#separate-css-bundle 參考文檔但是注意一下函數參數,第一第二個參數是有區別的,比如這樣用:
ExtractTextPlugin.extract( 'style-loader' , 'css!less' )
第一個參數是編譯好以後的代碼用的, 第二個參數是編譯到源代碼用的.. 有點難懂..
感想
Webpack的報錯挺不友好的,最初的時候我看著模塊找不到沒法搞明白
這種時候把中間過程打印出來看是不錯的選擇:
這種時候把中間過程打印出來看是不錯的選擇:
webpack --display- error -details
另一個報錯是沒有對應loader的提示.. log可能很長找不到重點
我建議是先自己去想想什麼地方需要考慮loader吧...可能就知道了
我還遇到就是源碼裡有使用dataUrl導致報錯...確實奇怪了
我建議是先自己去想想什麼地方需要考慮loader吧...可能就知道了
我還遇到就是源碼裡有使用dataUrl導致報錯...確實奇怪了
不說這些坑的話, Webpack我認為是我目前接觸到最好的前端開發方案
很多功能之前FIS文檔上看到過,但FIS相對重一些我始終沒上手
而Webpack一上來就繞過了此前公司用RequireJS打包時遇到的各種問題
很多功能之前FIS文檔上看到過,但FIS相對重一些我始終沒上手
而Webpack一上來就繞過了此前公司用RequireJS打包時遇到的各種問題
如果去掃Webpack的文檔的話,還有很多功能我完全沒涉及到.. http://webpack.github.io/docs/
0 意見:
張貼留言