webpack打包avalon+oniui+jquery
隨着avalon的發展壯大,我根據CSDN的統計數字,中國前端大概有1%的人在使用avalon了。
avalon的最大優勢是能兼容IE6,並且其API是非常穩定,只是在1.3.7 對ms-duplex的攔截器做了一次改動(但這次改動也向下兼容),1.5中去除avalon.define的舊風格支持,廢掉ms-widget指令改成更強大的自定義標簽指令。相對於其他MVVM框架來說,是非常的良心。此外,配套是非常完善,尤其是oniui,也支持到IE6。至於對移動端啊,微信啊,支持也非常好,難怪每天加群的人這么多。
不過,一個問題是,avalon沒有提供打包機制,雖然官網有教如何用requirejs打包avalon(比如滴滴出行,他們則是用fis3打包avalon ),但也有一些小公司,因為前端團隊實力不濟,無法實現打包。因此就有了這篇文章了。
本文是使用當今最強大的構建工具webpack實現的,各種看官首先得裝上npm。
建立一個新工程(我是將此工程起名為oni), 然后用npm初始化它,目的是建立一個package.json文件:

然后全局安裝以下東西
$ npm install webpack -g |
$ npm install style-loader css-loader url-loader text-loader -g |
然后再到oni目錄下執行npm link命令
$ npm link webpack style-loader css-loader text-loader |

雖然看似報了一大堆錯,但好歹也裝上了:

然后 我們安裝avalon依賴,由於要用oniui,只能使用1.4.* 版本,並且只能沒有加載器的版本(帶shim字樣的),請到這里下載https://github.com/RubyLouvre/avalon/tree/master/dist ,將里面的avalon.shim.js下載回來,放到dev/avalon目錄下。
然后 我們開始裝oniui。oniui是一個龐大的UI庫,為了滿足去哪兒各條業務線千奇百計的需求,組件非常豐富,功能強大無比! 我們沒有必要將它們全部裝上。我們可以在這里(https://github.com/RubyLouvre/avalon.oniui)一覽其全貌,挑選自己需要的組件(readme里有中文名)
比如,我們用手風琴(accordion)組件,那么打開里面的avalon.accordion.js的源碼,看其依賴情況:

會發現它依賴於avalon.getModel.js,這是在它的上級目錄;還有它的模板文件,與它同目錄; 還有一些樣式。accordion的目錄下有許多東西,為了節省時間,我們可以全部拷下來放到dev目錄下。

然后 我們在oni的根目錄下建立webpack的配置文件webpack.package.js,內容如下:
var webpack = require( "webpack" ); |
var path = require( "path" ); |
module.exports = { |
entry: "./dev/index" , //我們開發時的入口文件 |
output: {path: path.join(__dirname, "dist" ), filename: "bundle.js" }, //頁面引用的文件 |
module: { |
loaders: [ |
{test: /\.css$/, loader: 'style-loader!css-loader' } |
] |
}, |
resolve: { |
extensions: [ '.js' , "" , ".css" ], |
alias: { |
avalon: './avalon/avalon.shim' , //在正常情況下我們以CommonJS風格引用avalon,以require('avalon') |
"../avalon" : './avalon/avalon.shim' //由於oniui都以是../avalon來引用avalon的,需要在這里進行別名 |
} |
} |
} |
dev下的index.js是這樣的:
var avalon = require( "avalon" ) |
require( "./accordion/avalon.accordion" ) |
avalon.define({ |
$id: "test" , |
aaa: "Hello Avalon!" |
}) |
// 具體參考這里 https://github.com/RubyLouvre/avalon.oniui/blob/master/accordion/avalon.accordion.ex1.html |
avalon.define({ |
$id: "test" , |
aaa: "Hello Avalon!" , |
$opts:{ |
data: [{ |
'title' : '標題1' , |
'content' : '正文1<p>fasdfsdaf</p>' |
}, { |
'title' : '標題2' , |
'content' : '正文2' |
}], |
accordionClass: "oni-accordion-customClass" , |
initIndex: 1, |
width: "500" , |
onBeforeSwitch: function () { |
avalon.log( this ); |
avalon.log(arguments); |
avalon.log( "onBeforeSwitch callback" ); |
}, |
onSwitch: function () { |
avalon.log( "onSwitch callback" ); |
}, |
multiple: true |
} |
}) |
然后我們在控制台下,定位到oni目錄下,輸入webpack開始打包,報了一堆錯誤

其實我對webpack也不怎么熟,主要是參考如下中文教程開始玩的
我思度一下,估計沒有像我這樣混用CommonJS與AMD兩種風格,問題是出在加載CSS上,難道正則有問題嗎?試了好久,沒有辦法,自己寫預加載器,從avalon.accordion的源碼中干掉css!字樣。
具體過程如下,在oni/node_modules目錄下建立一個amdcss-loader目錄,結構如下:

package.json內容如下(這個不能少)
{ |
"author" : { |
"name" : "RubyLouvre" |
}, |
"dependencies" : {}, |
"description" : "Webpack的預處理器,處理AMD風格的模塊依賴列表中的css!字符串" , |
"license" : "MIT" , |
"main" : "index.js" , |
"name" : "amdcss-loader" , |
"scripts" : { |
"test" : "echo \"Error: no test specified\" && exit 1" |
}, |
"version" : "0.0.1" |
} |
index.js內容如下:
module.exports = function (source) { |
this .cacheable && this .cacheable(); |
source = source.replace(/css\!/g, "" ) |
this .callback( null , source); |
}; |
將webpack.config.js修改如下:
var webpack = require( "webpack" ); |
var path = require( "path" ); |
module.exports = { |
entry: './dev/index' , //我們開發時的入口文件 |
output: {path: path.join(__dirname, "dist" ), filename: "bundle.js" }, //頁面引用的文件 |
module: { |
loaders: [ |
{test: /\.css$/, loader: 'style-loader!css-loader' } |
], |
preLoaders: [ |
{test: /\.js$/, loader: "amdcss-loader" } |
] |
}, |
resolve: { |
extensions: [ '.js' , "" , ".css" ], |
alias: { |
avalon: './avalon/avalon.shim' , //在正常情況下我們以CommonJS風格引用avalon,以require('avalon') |
"../avalon" : './avalon/avalon.shim' //由於oniui都以是../avalon來引用avalon的,需要在這里進行別名 |
} |
} |
} |
再打包就成功了!

好了,我們需要一個頁面看一下效果。oniui目錄下,建立一個index.html
<!DOCTYPE html> |
< html > |
< head > |
< title >TODO supply a title</ title > |
< meta charset = "UTF-8" > |
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" > |
< script src = "dist/bundle.js" ></ script > |
< style > |
body{ |
padding:2em; |
} |
</ style > |
</ head > |
< body ms-controller = "test" > |
< h1 >{{aaa}}</ h1 > |
< div ms-widget = "accordion,$,$opts" ></ div > |
</ body > |
</ html > |
然后打開你的頁面就行看到效果了(我是使用netBeans直接運行,大家也可以試一下webpack-dev-server)

我們再看一下如何結合jquery一起使用,jquery我們還是使用兼容IE6的版本,可以到這里下回來,放到dev/jquery目錄下!
我們繼續修改webpack.config.js
var webpack = require( "webpack" ); |
var path = require( "path" ); |
module.exports = { |
entry: './dev/index' , //我們開發時的入口文件 |
output: {path: path.join(__dirname, "dist" ), filename: "bundle.js" }, //頁面引用的文件 |
module: { |
loaders: [ |
{test: /\.css$/, loader: 'style-loader!css-loader' } |
], |
preLoaders: [ |
{test: /\.js$/, loader: "amdcss-loader" } |
] |
}, |
resolve: { |
extensions: [ '.js' , "" , ".css" ], |
alias: { |
jquery: "./jquery/jquery.js" , |
avalon: './avalon/avalon.shim' , //在正常情況下我們以CommonJS風格引用avalon,以require('avalon') |
"../avalon" : './avalon/avalon.shim' //由於oniui都以是../avalon來引用avalon的,需要在這里進行別名 |
} |
} |
} |
然后修改dev/index.js
var avalon = require( "avalon" ) |
require( "./accordion/avalon.accordion" ) |
var $ = require( "jquery" ) |
avalon.define({ |
$id: "test" , |
aaa: "Hello Avalon!" , |
$opts:{ |
data: [{ |
'title' : '標題1' , |
'content' : '正文1<p>fasdfsdaf</p>' |
}, { |
'title' : '標題2' , |
'content' : '正文2' |
}], |
accordionClass: "oni-accordion-customClass" , |
initIndex: 1, |
width: "500" , |
onBeforeSwitch: function () { |
avalon.log( this ); |
avalon.log(arguments); |
avalon.log( "onBeforeSwitch callback" ); |
}, |
onSwitch: function () { |
avalon.log( "onSwitch callback" ); |
}, |
multiple: true |
} |
}) |
$( function (){ |
$( "<div>這是jQuery生成的</div>" ).appendTo( "body" ) |
}) |
重新運行webpack命令,jquery就打包進去!

不過,我其實不希望大家將jquery與avalon都打包進去的,因為這兩個庫比較常用,幾乎每個頁面都有,建立放到CDN中,用script獨立引入。詳見 《Webpack 性能優化 (一)(使用別名做重定向)》一文。
至此,本文完畢。我是希望avalon社區能使用更強大的工具進行打包,而不是用requriejs之流了