antd-mobile是螞蟻金服出的移動端設計指南和前端框架,它提供了一套基於React的移動端組件庫,能夠很方便地用來開發體驗良好的移動應用。css
在閱讀了antd-mobile的介紹說明後,使用這一組件庫彷佛很簡單,要作的只是安裝和引入組件而已:前端
安裝react
$ npm install antd-mobile --save
引入組件git
import { Button } from 'antd-mobile/lib/button'; ReactDOM.render(<Button>按鈕</Button>, mountNode);
antd-mobile的介紹說明中推薦使用babel-plugin-import插件來按需加載類庫,但爲了減小初期使用antd-mobile所面臨的複雜度,以上代碼採用了最簡單的組件引入寫法(顯式指定組件的路徑antd-mobile/lib/button
,並在HTML文件中單獨引入CSS樣式文件antd-mobile/dist/antd-mobile.min.css
)。github
在安裝完antd-mobile模塊並引入須要的組件後,接下來的一步即是構建整個移動應用。此時,若是項目不是React Native應用,而是Web應用的話,構建過程會報錯,顯示react-native模塊找不到(Error: Cannot resolve module 'react-native'...
)。這個錯誤無疑是很是使人困惑的:當前所開發的是一個普通的移動端Web項目,與react-native沒有任何關係,爲何須要react-native模塊?事實上,即便根據報錯提示安裝react-native模塊,在後續的構建過程當中也會報一些別的錯誤,致使構建失敗。web
進一步的調查發現,問題出在antd-mobile的組件模塊設計上。因爲antd-mobile被設計爲同時支持React Native應用開發和Web應用開發,所以全部的組件都暴露爲2個模塊文件:index.js
和index.web.js
。其中,index.js
是給React Native開發使用的,而index.web.js
則是給Web開發使用的。因爲Browserify和Webpack等打包工具在解析JavaScript模塊引入操做時(require
或import
語句),會優先查找.js
後綴名的文件(當不指定模塊文件名時,默認文件名即爲index.js
),所以即便當前項目與React Native無關,組件模塊的引入操做也會致使對react-native的依賴。npm
找到問題的緣由後,解決方案初步考慮有2種:json
引入模塊時,顯式指定模塊文件的文件名(import { Button } from 'antd-mobile/lib/button/index.web';
)。react-native
對Browserify或Webpack等打包工具進行配置,更改其模塊引入操做時的後綴名優先級,使得.web.js
文件得以優先使用。數組
第一種方案比較簡單,對代碼的改動量也很小。但事實證實,這一方案是行不通的:antd-mobile的組件代碼中存在內部組件依賴(如List組件依賴ListItem組件,在List組件的index.web.js
文件中,會出現require('./ListItem')
這樣的代碼),而這些引入內部組件的操做並未指定具體的模塊文件名,所以仍是會產生require('./ListItem/index.js')
這樣的效果,並最終致使對react-native的依賴。
對於第二種方案,若是是用Webpack打包,則antd-mobile社區有現成的解決方法 — 設定extensions
選項的值,並將.web.js
放在.js
以前便可。但在Browserify中,這一問題該如何解決呢?
和Webpack同樣,Browserify也提供了一個叫extensions
的配置選項,用於設定模塊文件的後綴名及其優先級。但和Webpack不一樣的是,Browserify中默認的2個模塊文件後綴名(.js
和.json
)永遠具備最高優先級,即便在extensions
配置選項中設定.web.js
比.js
具備更高的優先級(extensions: ['.web.js', '.js', ...]
)也無濟於事。緣由在於Browserify源代碼中的如下這一行:
mopts.extensions = [ '.js', '.json' ].concat(mopts.extensions || []);
能夠看到,不管設定的extensions
值爲什麼,.js
和.json
永遠具備最高優先級。那麼,在這種狀況下如何設定比.js
優先級還要高的模塊文件後綴名呢?
在通過一些思索後,發現這個問題只能用比較hack的方式來解決:對於上述計算最終extensions值的操做,修改JavaScript中數組的concat
行爲,讓mopts.extensions
在[ '.js', '.json' ]
數組以前插入,而不是在其後添加。具體代碼爲:
var origin_concat = Array.prototype.concat; Array.prototype.concat = function() { if (this.length === 2 && this[0] === '.js' && this[1] === '.json') { return origin_concat.apply(arguments[0], this); } return origin_concat.apply(this, arguments); };
運行以上代碼後,就能夠經過配置extensions: ['.web.js', ...]
來用Browserify打包antd-mobile開發的Web應用了。
爲了方便使用,上述hack Browserify的代碼被抽象爲一個模塊:browserify-high-priority-extensions
,其意爲」讓Browserify的extensions選項值具備比默認的後綴名更高的優先級「。使用該模塊很是簡單:
安裝
$ npm install browserify-high-priority-extensions --save-dev
啓用extensions高優先級設定
var hpe = require('browserify-high-priority-extensions'); hpe.enable();
啓用後,便可經過配置extensions: ['.web.js', ...]
來用Browserify打包antd-mobile開發的Web應用。
取消extensions高優先級設定
當不須要配置extensions
選項高優先級時,能夠用如下語句恢復到默認狀態:
hpe.disable();