當ArcGIS API for JavaScript遇見Webpack(二)

weixin_34124651發表於2017-08-23

作者:liuyl 郵箱:liuyl.gisuni@gmail.com
關於作者:GIS從業者,主要在ArcGIS平臺下做WebGIS開發


廣告移到前面以免你看不到


公眾號開通了,歡迎掃描下面二維碼進行關注,我爭取每週都寫一兩篇文章,記錄和分享工作和學習中的收穫。內容將主要是WebGIS開發方面的,歡迎在公眾號中留言和我進行交流。


7292919-2dc380de01ff943b.jpg
請掃碼關注我的微信公眾號 幻想GIS

寫在最前面


如果你還沒有看過並理解了上一篇 當ArcGIS API for JavaScript遇見Webpack(一) 的內容的話,請移步第一篇。

為什麼寫這篇文章


在上一篇中,我們成功的讓ArcGIS API for JavaScript和Webpack和諧地相處了。

如果是個Demo的話,這就算是成功了。不過我們是要做專案的,第三方庫比如jquery、lodash甚至是react之類的,總是要用到一些。甚至更進一步,我們要開發一個基於ArcGIS API for JavaScript的元件庫,並希望像其他第三方庫一樣用import語句引用。

這時你會發現,ArcGIS API for JavaScript和Webpack的相處並不如表面上那麼和諧,問題一個接一個出現……現在,我們來一個個地解決這些問題。

下面是正文


正常情況下引入第三方庫


關於正常情況下,也就是不引入ArcGIS API for JavaScript,或是dojo,又或是AMD時,第三方庫的引用還是比較簡單的,有大量教程可供參考,這裡簡單列一下。
(以下均以從CDN引入jquery、lodash和react.js為例、且不考慮把第三方庫的程式碼直接一同打包的情況)

  1. 引入js檔案
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdn.bootcss.com/react/15.6.1/react.js"></script>
  1. 修改webpack配置externals
    將webpack的externals項配置成如下
externals: [
    {
        jquery: 'jQuery',
        lodash: '_',
        react: 'React',
    },
]

這一步的配置可以讓我們在程式碼中使用import語句引入第三方庫,比如:

    import $ from 'jquery'
    import _ from 'lodash'
    import React from 'react'
  1. 修改webpack配置plugins項(可選)
    在webpack的plugins項配置中新增如下程式碼
plugins: [
    new webpack.ProvidePlugin({
        $: 'jquery',
        _: 'lodash',
    }),
]

這一步的配置可以讓我們不需要再通過import語句引入jquery和lodash,而是可以直接在全域性使用$和_符號

在引入ArcGIS API for JavaScript的情況下引入第三方庫


假設按照第一篇中我們引入了ArcGIS API for JavaScript,並按照上面所述引入第三方庫、修改webpack配置。然後寫一個例子,比如這樣:

import Map from 'esri/Map'
import MapView from 'esri/views/MapView'
const $mapDom=$('<div style="width:400px;height:300px"></div>').appendTo($('body'))
var map = new Map({
    basemap: 'streets'
})

var view = new MapView({
    container: yourDom,
    map: map
})

直接訪問應用,喜聞樂見地在控制檯看到了錯誤資訊。

這裡我們同時引用了jquery和api,這個錯誤資訊取決於jquery和api在html頁中的引入次序,主要是兩類錯誤。

  1. multipleDefine錯誤


    7292919-faa8ddaea40e71cf.png

這主要是由於在html頁中api的init.js在第三方庫(如jquery.js)之前被引入。
具體成因我沒有搞清楚,歡迎補充~

  1. 找不到庫檔案錯誤


    7292919-977fb2d09edb4c83.png

當在html頁中api的init.js在第三方庫(如jquery.js)之後被引入時,就會導致這個錯誤。

第一篇時我們討論過webpack的externals項配置,它是用來標明外部引用的。

externals: [
    {
        jquery: 'jQuery',
    },
]

這個配置會讓打包生成的js檔案的最前面生成類似這樣的程式碼

define(["jQuery", ……],
    function (__WEBPACK_EXTERNAL_MODULE_3__,……){
    ...
    })

對dojo比較熟的同學會看出來,當AMD不認識jQuery時,就會在其工作路徑下尋找jQuery.js檔案,找不到的話就會報上面的錯誤。

如何避免錯誤


針對上述第1個錯誤,我們需要在html檔案中將api的init.js檔案放在其他第三方庫的後面引入。

針對第2個錯誤,我們可以在api的init.js檔案引入後,人為地將第三方庫的全域性名稱define到AMD中,html檔案中大致這樣:

<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdn.bootcss.com/react/15.6.1/react.js"></script>
<script src="https://js.arcgis.com/4.4/init.js"></script>
<script>
    define("jQuery",[],function () {
        return jQuery
    })
    define("lodash",[],function () {
        return lodash
    })
    define("React",[],function () {
        return React
    })
</script>
<script>
    require(['app.js'],function () {
    })
</script>

webpack的配置按上面說的正常情況進行修改即可。

寫一個基於ArcGIS API for JavaScript的元件庫


正常情況下寫一個類庫時,webpack的output配置項,應該配置成如下的樣子

output: {
    libraryTarget: 'umd',
    library: 'YourLib',
    ……
}

libraryTarget: 'umd'意味著我們的庫可以通過輸出全域性變數引入,也支援commonJs或AMD方式引入,大部分我們常使用的第三方庫都是這樣的。但正如前所說,這樣的庫在ArcGIS API for JavaScript的init.js檔案之後引入時會導致multipleDefine錯誤。

我們的庫是基於ArcGIS API for JavaScript的,也就是不得不在api後面引入,如果我們設定了libraryTarget: 'umd',那麼就同樣會遇到multipleDefine錯誤。

解決這個問題,就把webpack的output項設定成如下所示

output: {
    libraryTarget: 'var',
    library: 'MyLib',
    ……
}

libraryTarget: 'var'其實是預設值,就是向全域性輸出一個MyLib變數,在應用中引入這個庫時,同樣需要手動define一下,整個引入流程大致如下:

webpack配置

externals: [
    {
        mylib: 'MyLib',
    },
]

html檔案

<script src="https://js.arcgis.com/4.4/init.js"></script>
<script src="/mylib.js"></script>
<script>
    define("MyLib",[],function () {
        return MyLib
    })
</script>
<script>
    require(['app.js'],function () {
    })
</script>

應用呼叫檔案

import myLib from 'mylib'
myLib.testFun()
最後,這個庫如何封裝和呼叫ArcGIS API for JavaScript
function createMap(dom){
    window.require(['esri/Map', 'esri/views/MapView'], (Map, MapView) => {
        var map = new Map({
            ……
        })
        this.view = new MapView({
            container: dom,
            map: map,
        })
    })
}
export default createMap

上面只是個示例,這裡就不能用import語句呼叫api了,而是使用傳統的require語句。但是要注意的是不能直接寫require,因為webpack會對require語句進行編譯,用window.requrire不會被編譯。
當然,我感覺通過webpack的適當配置,還是有可能實現使用import語句呼叫api的,歡迎有興趣的同學繼續探索。

寫在最後


關於ArcGIS API for JavaScript和Webpack之間的糾葛就寫到這了。

這兩篇裡面的內容實際上折騰了我挺長時間,當然也是在這個過程中才逐漸理解了webpack的核心邏輯,還是挺值得的。

ArcGIS API for JavaScript和Webpack之間的整合,其實還有一些其他的解決方案,比如esri-loader。我沒有嘗試去用過,據說有坑,有興趣的同學倒也不妨一試。對於我這個ArcGIS API for JavaScript的深度使用者,還是希望保持對其的絕對控制的,這兩篇文章中的解決方案基本上滿足了我工作中的需求,我覺得還是比較完善的。

如果你覺得這篇文章對你有幫助,請移動到文章頂部,關注我的微信公眾號。
如果你有任何疑問和建議,歡迎留言和我交流。

相關文章