webpack多頁應(yīng)用架構(gòu)系列(四):老式j(luò)Query插件還不能丟,怎么兼容?

本文首發(fā)于Array_Huang的技術(shù)博客——實用至上,非經(jīng)作者同意,請勿轉(zhuǎn)載。
原文地址:https://segmentfault.com/a/1190000006887523
如果您對本系列文章感興趣,歡迎關(guān)注訂閱這里:https://segmentfault.com/blog/array_huang

前言

目前前端雖處于百花齊放階段,angular/react/vue競相角逐,但畢竟尚未完全成熟,有些需求還是得依靠我們的老大哥jQuery的。

我個人對jQuery并不反感,但我對jQuery生態(tài)的停滯不前相當(dāng)無奈,比如說赫赫有名的bootstrap(特指3代),在webpack上打包還得靠個loader的,太跟不上時勢了。況且,bootstrap還算好的,有些jquery插件都有一兩年沒更新了,連NPM都沒上架呢,可偏偏就是找不到它們的替代品,項目又急著要上,這可咋辦吶?

別急,今天就教你適配兼容老式j(luò)Query插件。

老式j(luò)Query插件為和不能直接用webpack打包?

如果你把jQuery看做是一個普通的js模塊來加載(要用到j(luò)Query的模塊統(tǒng)統(tǒng)先require后再使用),那么,當(dāng)你加載老式j(luò)Query插件時,往往會提示找不到j(luò)Query實例(有時候是提示找不到$),這是為啥呢?

要解釋這個問題,就必須先稍微解釋一下jQuery插件的機(jī)制:jQuery插件是通過jQuery提供的jQuery.fn.extend(object)jQuery.extend(object)這倆方法,來把插件本身實現(xiàn)的方法掛載到jQuery(也即$)這個對象上的。傳統(tǒng)引用jQuery及其插件的方式是先用<script>加載jQuery本身,然后再用同樣的方法來加載其插件;jQuery會把jQuery對象設(shè)置為全局變量(當(dāng)然也包括了$),既然是全局變量,那么插件們很容易就能找到jQuery對象并掛載自身的方法了。

而webpack作為一個遵從模塊化原則的構(gòu)建工具,自然是要把各模塊的上下文環(huán)境給分隔開以減少相互間的影響;而jQuery也早已適配了AMD/CMD等加載方式,換句話說,我們在require jQuery的時候,實際上并不會把jQuery對象設(shè)置為全局變量。說到這里,問題也很明顯了,jQuery插件們找不到jQuery對象了,因為在它們各自的上下文環(huán)境里,既沒有局部變量jQuery(因為沒有適配AMD/CMD,所以就沒有相應(yīng)的require語句了),也沒有全局變量jQuery

怎么來兼容老式j(luò)Query插件呢?

方法有不少,下面一個一個來看。

ProvidePlugin + expose-loader

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

ProvidePlugin的配置是這樣的:

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

ProvidePlugin的機(jī)制是:當(dāng)webpack加載到某個js模塊里,出現(xiàn)了未定義且名稱符合(字符串完全匹配)配置中key的變量時,會自動require配置中value所指定的js模塊。

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

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

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

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

你或許會問,有了ProvidePlugin為嘛還需要expose-loader?問得好,如果你所有的jQuery插件都是用webpack來加載的話,的確用ProvidePlugin就足夠了;但理想是豐滿的,現(xiàn)實卻是骨感的,總有那么些需求是只能用<script>來加載的。

externals

externals是webpack配置中的一項,用來將某個全局變量“偽裝”成某個js模塊的exports,如下面這個配置:

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

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

與上述ProvidePlugin + expose-loader的方案相反,此方案是先用<script>加載的jQuery滿足老式j(luò)Query插件的需要,再通過externals將其轉(zhuǎn)換成符合模塊化要求的exports。

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

imports-loader

這個方案就相當(dāng)于手動版的ProvidePlugin,以前我用requireJS的時候也是用的類似的手段,所以我一開始從requireJS遷移到webpack的時候用的也是這種方法,后來知道有ProvidePlugin就馬上換了哈。

這里就不詳細(xì)說明了,放個例子大家看看就懂:

// ./webpack.config.js

module.exports = {
    ...
    module: {
        loaders: [
            {
                test: require.resolve("some-module"),
                loader: "imports?$=jquery&jQuery=jquery", // 相當(dāng)于`var $ = require("jquery");var jQuery = require("jquery");`
            }
        ]
    }
};

總結(jié)

以上的方案其實都屬于shimming,并不特別針對jQuery,請舉一反三使用。另外,上述方案并不僅用于shimming,比如用上ProvidePlugin來寫少幾個require,自己多多挖掘,很有樂趣的哈~~

示例代碼

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

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

本文首發(fā)于Array_Huang的技術(shù)博客——實用至上,非經(jīng)作者同意,請勿轉(zhuǎn)載。
原文地址:https://segmentfault.com/a/1190000006887523
如果您對本系列文章感興趣,歡迎關(guān)注訂閱這里:https://segmentfault.com/blog/array_huang

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容