用Browserify構建antd-mobile應用

antd-mobile是螞蟻金服出的移動端設計指南和前端框架,它提供了一套基於React的移動端組件庫,能夠很方便地用來開發體驗良好的移動應用。css

使用antd-mobile遇到的問題:react-native模塊找不到

在閱讀了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.jsindex.web.js。其中,index.js是給React Native開發使用的,而index.web.js則是給Web開發使用的。因爲Browserify和Webpack等打包工具在解析JavaScript模塊引入操做時(requireimport語句),會優先查找.js後綴名的文件(當不指定模塊文件名時,默認文件名即爲index.js),所以即便當前項目與React Native無關,組件模塊的引入操做也會致使對react-native的依賴。npm

找到問題的緣由後,解決方案初步考慮有2種:json

  1. 引入模塊時,顯式指定模塊文件的文件名(import { Button } from 'antd-mobile/lib/button/index.web'; )。react-native

  2. 對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中,這一問題該如何解決呢?

使用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應用了。

模塊抽象:browserify-high-priority-extensions

爲了方便使用,上述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();
相關文章
相關標籤/搜索