webpack多頁應用架構系列(四):老式jQuery插件還不能丟,怎麼兼容?

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: https://segmentfault.com/a/1190000006887523
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang

前言

目前前端雖處於百花齊放階段,angular/react/vue競相角逐,但畢竟還沒有徹底成熟,有些需求仍是得依靠咱們的老大哥jQuery的。javascript

我我的對jQuery並不反感,但我對jQuery生態的停滯不前至關無奈,好比說赫赫有名的bootstrap(特指3代),在webpack上打包還得靠個loader的,太跟不上時勢了。何況,bootstrap還算好的,有些jquery插件都有一兩年沒更新了,連NPM都沒上架呢,可恰恰就是找不到它們的替代品,項目又急着要上,這可咋辦吶?css

別急,今天就教你適配兼容老式jQuery插件。前端

老式jQuery插件爲和不能直接用webpack打包?

若是你把jQuery看作是一個普通的js模塊來加載(要用到jQuery的模塊通通先require後再使用),那麼,當你加載老式jQuery插件時,每每會提示找不到jQuery實例(有時候是提示找不到$),這是爲啥呢?vue

要解釋這個問題,就必須先稍微解釋一下jQuery插件的機制:jQuery插件是經過jQuery提供的jQuery.fn.extend(object)jQuery.extend(object)這倆方法,來把插件自己實現的方法掛載到jQuery(也即$)這個對象上的。傳統引用jQuery及其插件的方式是先用<script>加載jQuery自己,而後再用一樣的方法來加載其插件;jQuery會把jQuery對象設置爲全局變量(固然也包括了$),既然是全局變量,那麼插件們很容易就能找到jQuery對象並掛載自身的方法了。java

而webpack做爲一個聽從模塊化原則的構建工具,天然是要把各模塊的上下文環境給分隔開以減小相互間的影響;而jQuery也早已適配了AMD/CMD等加載方式,換句話說,咱們在require jQuery的時候,實際上並不會把jQuery對象設置爲全局變量。說到這裏,問題也很明顯了,jQuery插件們找不到jQuery對象了,由於在它們各自的上下文環境裏,既沒有局部變量jQuery(由於沒有適配AMD/CMD,因此就沒有相應的require語句了),也沒有全局變量jQueryreact

怎麼來兼容老式jQuery插件呢?

方法有很多,下面一個一個來看。jquery

ProvidePlugin + expose-loader

首先來介紹我最爲推薦的方法:ProvidePlugin + expose-loader,在我公司的項目,以及我我的的腳手架開源項目webpack-seed裏使用的都是這一種方法。webpack

ProvidePlugin的配置是這樣的:git

var providePlugin = new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    'window.jQuery': 'jquery',
    'window.$': 'jquery',
  });

ProvidePlugin的機制是:當webpack加載到某個js模塊裏,出現了未定義且名稱符合(字符串徹底匹配)配置中key的變量時,會自動require配置中value所指定的js模塊。github

如上述例子,當某個老式插件使用了jQuery.fn.extend(object),那麼webpack就會自動引入jquery(此處我是用NPM的版本,我也推薦使用NPM的版本)。

另外,使用ProvidePlugin還有個好處,就是,你本身寫的代碼裏,再!也!不!用!require!jQuery!啦!畢竟少寫一句是一句嘛哈哈哈。

接下來介紹expose-loader,這個loader的做用是,將指定js模塊export的變量聲明爲全局變量。下面來看下expose-loader的配置:

/*
    很明顯這是一個loader的配置項,篇幅有限也只能截取相關部分了
    看不明白的麻煩去看本系列的另外一篇文章《webpack多頁應用架構系列(二):webpack配置經常使用部分有哪些?》:https://segmentfault.com/a/1190000006863968
 */
{
  test: require.resolve('jquery'),  // 此loader配置項的目標是NPM中的jquery
  loader: 'expose?$!expose?jQuery', // 先把jQuery對象聲明成爲全局變量`jQuery`,再經過管道進一步又聲明成爲全局變量`$`
},

你或許會問,有了ProvidePlugin爲嘛還須要expose-loader?問得好,若是你全部的jQuery插件都是用webpack來加載的話,的確用ProvidePlugin就足夠了;但理想是豐滿的,現實倒是骨感的,總有那麼些需求是隻能用<script>來加載的。

externals

externals是webpack配置中的一項,用來將某個全局變量「假裝」成某個js模塊的exports,以下面這個配置:

externals: {
      'jquery': 'window.jQuery',
    },

那麼,當某個js模塊顯式地調用var $ = require('jquery')的時候,就會把window,jQuery返回給它。

與上述ProvidePlugin + expose-loader的方案相反,此方案是先用<script>加載的jQuery知足老式jQuery插件的須要,再經過externals將其轉換成符合模塊化要求的exports。

我我的並不太看好這種作法,畢竟這就意味着jQuery脫離NPM的管理了,不過某些童鞋有其它的考慮,例如爲了加快每次打包的時間而把jQuery這些比較大的第三方庫給分離出去(直接調用公共CDN的第三方庫?),也算是有必定的價值。

imports-loader

這個方案就至關於手動版的ProvidePlugin,之前我用requireJS的時候也是用的相似的手段,因此我一開始從requireJS遷移到webpack的時候用的也是這種方法,後來知道有ProvidePlugin就立刻換了哈。

這裏就不詳細說明了,放個例子你們看看就懂:

// ./webpack.config.js

module.exports = {
    ...
    module: {
        loaders: [
            {
                test: require.resolve("some-module"),
                loader: "imports?$=jquery&jQuery=jquery", // 至關於`var $ = require("jquery");var jQuery = require("jquery");`
            }
        ]
    }
};

總結

以上的方案其實都屬於shimming,並不特別針對jQuery,請觸類旁通使用。另外,上述方案並不只用於shimming,好比用上ProvidePlugin來寫少幾個require,本身多多挖掘,頗有樂趣的哈~~

補充

誤用externals(2016-10-17更新)

有童鞋私信我,說用了我文章的方案依然提示$ is not a function,在我仔細分析後,發現:

  1. 他用的是我推薦的ProvidePlugin + expose-loader方案,也就是說,他已經把jquery打包進來了。
  2. 可是他又不明就裏得配了externals:
externals: {
    jquery: 'window.jQuery',
  },
  1. 然而實際上他並無直接用<script>來引用jQuery,所以window.jQuery是個null。
  2. 結果,他的jquery插件得到的$就是個null了。

這裏面咱們能夠看出,externals是會覆蓋掉ProvidePlugin的。

但這裏有個問題,expose-loader的做用就是設置好window.jQuery和window.$,那window.jQuery怎麼會是null呢?個人猜測是:externals在expose-loader設置好window.jQuery前就已經取了window.jQuery的值(null)了。

說了這麼多,其實關鍵意思就是,不要手賤不要手賤不要手賤(重要的事情說三遍)!

示例代碼

諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seed(https://github.com/Array-Huang/webpack-seed)

附系列文章目錄(同步更新)

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: https://segmentfault.com/a/1190000006887523
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang
相關文章
相關標籤/搜索