欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解Webpack中Tapable事件機制

 更新時間:2023年11月13日 08:27:55   作者:古茗前端團隊  
Webpack?是前端工程化常用的靜態(tài)模塊打包工具,在合適的時機通過?Webpack?提供的?API?改變輸出結(jié)果,使?Webpack?可以執(zhí)行更廣泛的任務,擁有更強的構(gòu)建能力,本文將介紹?Tapable?的基本使用以及底層實現(xiàn),需要的朋友可以參考下

引言

Webpack 是前端工程化常用的靜態(tài)模塊打包工具。在合適的時機通過 Webpack 提供的 API 改變輸出結(jié)果,使 Webpack 可以執(zhí)行更廣泛的任務,擁有更強的構(gòu)建能力。

Webpack 的插件機制本質(zhì)上是一種事件流的機制,它的工作流程就是將各個插件串聯(lián)起來,而實現(xiàn)這一切的核心就是Tapable,Webpack中最核心的負責編譯的Compiler和負責創(chuàng)建bundles的Compilation都是Tapable的實例。

本文將介紹 Tapable 的基本使用以及底層實現(xiàn)。

Tapable

Tapable 是一個類似于 Node.js 中的 EventEmitter 的庫,但它更專注于自定義事件的觸發(fā)和處理。通過 Tapable 我們可以注冊自定義事件,然后在適當?shù)臅r機去執(zhí)行自定義事件。這個和我們所熟知的生命周期函數(shù)類似,在特定的時機去觸發(fā)。

我們先看一個 簡單 Tapable 的 例子:

const { SyncHook } = require("tapable");

// 實例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端");

通過上面的例子,我們大致可以將 Tapable 的使用分為以下三步:

  • 實例化鉤子函數(shù)
  • 事件注冊
  • 事件觸發(fā)

事件注冊

  • 同步的鉤子要用 tap 方法來注冊事件
  • 異步的鉤子可以像同步方式一樣用 tap 方法來注冊,也可以用 tapAsynctapPromise 異步方法來注冊。
    • tapAsync: 使用用 tapAsync 方法來注冊 hook 時,必須調(diào)用callback 回調(diào)函數(shù)。
    • tapPromise:使用 tapPromise 方法來注冊 hook 時,必須返回一個 pormise ,異步任務完成后 resolve

事件觸發(fā)

  • 同步的鉤子要用 call 方法來觸發(fā)
  • 異步的鉤子需要用 callAsyncpromise 異步方法來觸發(fā)。
    • callAsync:當我們用 callAsync 方法來調(diào)用 hook 時,第二個參數(shù)是一個回調(diào)函數(shù),回調(diào)函數(shù)的參數(shù)是執(zhí)行任務的最后一個返回值
    • promise:當我們用 promise 方法來調(diào)用 hook 時,需要使用 then 來處理執(zhí)行結(jié)果,參數(shù)是執(zhí)行任務的最后一個返回值。

Tapable Hook 鉤子

tapable 內(nèi)置了 9 種 hook 。分為 同步異步 兩種執(zhí)行方式。異步執(zhí)行 Hook 又可以分為 串行 執(zhí)行和 并行 執(zhí)行。除此之外,hook 可以根據(jù)執(zhí)行機制分為 常規(guī) 瀑布模式 熔斷模式 循環(huán)模式 四種執(zhí)行機制。

同步鉤子

同步鉤子顧名思義:同步執(zhí)行,上一個鉤子執(zhí)行完才會執(zhí)行下一個鉤子。

示例代碼如下:

const { SyncHook } = require("tapable");

// 實例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//該監(jiān)聽函數(shù)有返回值
syncHook.tap("同步鉤子2", (name) => {
  console.log("同步鉤子2", name);
});


//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端");

執(zhí)行結(jié)果如下所示:

異步鉤子

異步鉤子分為: 串行執(zhí)行和并行執(zhí)行。在串行執(zhí)行中,如果上一個鉤子沒有調(diào)用callback 回調(diào)函數(shù),下一個鉤子將不會觸發(fā)對應的事件監(jiān)聽。

示例代碼如下:

const { AsyncParallelHook, AsyncSeriesHook } = require("tapable");


const asyncParallelHook = new AsyncParallelHook(["name"]);

const asyncSeriesHook = new AsyncSeriesHook(["name"]);


//通過tap函數(shù)注冊事件
asyncParallelHook.tapAsync("異步并行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("異步并行鉤子1", name);
  }, 3000);
});

//該監(jiān)聽函數(shù)有返回值
asyncParallelHook.tapAsync("異步并行鉤子2", (name, callback) => {
  setTimeout(() => {
    console.log("異步并行鉤子2", name);
  }, 1500);
});

//通過tap函數(shù)注冊事件
asyncSeriesHook.tapAsync("異步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("異步串行鉤子1", name);
  }, 3000);
});

//該監(jiān)聽函數(shù)有返回值
asyncSeriesHook.tapAsync("異步串行鉤子2", (name, callback) => {
  setTimeout(() => {
    console.log("異步串行鉤子2", name);
  }, 1500);
});


// 異步并行鉤子 通過 callAsync 發(fā)布事件
asyncParallelHook.callAsync("古茗前端", () => {
  console.log("1111");
  return "1122";
});

// 異步串行鉤子 通過 callAsync 發(fā)布事件
asyncSeriesHook.callAsync("古茗前端", () => {
  console.log("1111");
  return "1122";
});

控制臺輸出結(jié)果如下圖所示:

串行鉤子1沒有調(diào)用callback, 所以串行鉤子2沒有觸發(fā)。添加callback后,控制臺輸出結(jié)果:

熔斷類

AsyncSeriesBailHook 是一個異步串行、熔斷類型的 Hook。在串行的執(zhí)行過程中,只要其中一個有返回值,后面的就不會執(zhí)行了。

示例代碼如下:

const { SyncBailHook, AsyncSeriesBailHook } = require("tapable");

const syncBailHook = new SyncBailHook(["name"]);
const asyncSeriesBailHook = new AsyncSeriesBailHook(["name"]);

syncBailHook.tap("保險類同步鉤子1", (name) => {
  console.log("保險類同步鉤子1", name);
});

syncBailHook.tap("保險類同步鉤子2", (name) => {
  console.log("保險類同步鉤子2", name);
  return "有返回值";
});

syncBailHook.tap("保險類同步鉤子3", (name) => {
  console.log("保險類同步鉤子3", name);
});

asyncSeriesBailHook.tapAsync("保險類異步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("保險類異步串行鉤子1", name);
    callback();
  }, 3000);
});

asyncSeriesBailHook.tapAsync("保險類2步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("保險類異步串行鉤子2", name);
    callback("有返回值");
  }, 2000);
});

asyncSeriesBailHook.tapAsync("保險類異步串行鉤子3", (name) => {
  setTimeout(() => {
    console.log("保險類異步串行鉤子3", name);
  }, 1000);
});

syncBailHook.call("古茗前端");
asyncSeriesBailHook.callAsync("古茗前端", (result) => {
  console.log("result", result);
});

控制臺輸出結(jié)果如下圖所示:

循環(huán)類

SyncLoopHook 是一個同步、循環(huán)類型的 Hook。循環(huán)類型的含義是不停的循環(huán)執(zhí)行事件函數(shù),直到所有函數(shù)結(jié)果 result === undefined,不符合條件就調(diào)頭重新開始執(zhí)行。

示例代碼:

const { SyncLoopHook } = require("tapable");

const syncLoopHook = new SyncLoopHook(["name"]);

let count = 4;

syncLoopHook.tap("循環(huán)鉤子1", (name) => {
  console.log("循環(huán)鉤子1", count);
  return count <= 3 ? undefined : count--;
});

syncLoopHook.tap("循環(huán)鉤子2", (name) => {
  console.log("循環(huán)鉤子2", count);
  return count <= 2 ? undefined : count--;
});

syncLoopHook.tap("循環(huán)鉤子3", (name) => {
  console.log("循環(huán)鉤子3", count);
  return count <= 1 ? undefined : count--;
});


syncLoopHook.call();

控制臺輸出結(jié)果:

瀑布類

AsyncSeriesWaterfallHook 是一個異步串行、瀑布類型的 Hook。如果前一個事件函數(shù)的結(jié)果 result !== undefined,則 result 會作為后一個事件函數(shù)的第一個參數(shù)(也就是上一個函數(shù)的執(zhí)行結(jié)果會成為下一個函數(shù)的參數(shù))。

示例代碼:

const { AsyncSeriesWaterfallHook, SyncWaterfallHook } = require("tapable");

const syncWaterfallHook = new SyncWaterfallHook(["name"]);

const asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook(["name"]);

syncWaterfallHook.tap("瀑布式同步鉤子1", (name) => {
  console.log("瀑布式同步鉤子1", name);

  return "古茗前端1";
});

syncWaterfallHook.tap("瀑布式同步鉤子2", (name) => {
  console.log("瀑布式同步鉤子2", name);
});

syncWaterfallHook.tap("瀑布式同步鉤子3", (name) => {
  console.log("瀑布式同步鉤子3", name);

  return "古茗前端3";
});

asyncSeriesWaterfallHook.tapAsync("瀑布式異步串行鉤子1", (name, callback) => {
  setTimeout(() => {
    console.log("瀑布式異步串行鉤子1", name);

    callback();
  }, 1000);
});

asyncSeriesWaterfallHook.tapAsync("瀑布式異步串行鉤子2", (name, callback) => {
  console.log("瀑布式異步串行鉤子2", name);
  setTimeout(() => {
    callback();
  }, 2000);
});

asyncSeriesWaterfallHook.tapAsync("瀑布式異步串行鉤子3", (name, callback) => {
  console.log("瀑布式異步串行鉤子3", name);
  setTimeout(() => {
    callback("古茗前端3");
  }, 3000);
});

syncWaterfallHook.call("古茗前端");

asyncSeriesWaterfallHook.callAsync("古茗前端", (result) => {
  console.log("result", result);
});


控制臺輸出結(jié)果:

Tapable 高級特性

Intercept

除了通常的 tap/call 之外,所有 hook 鉤子都提供額外的攔截API。— intercept 接口。

intercept 支持的中間件如下圖所示:

intercept類型描述
call(...args) => void 當鉤子被觸發(fā)時,向攔截器添加調(diào)用將被觸發(fā)。您可以訪問hooks參數(shù)
tap(tap: Tap) 將tap添加到攔截器將在插件點擊鉤子時觸發(fā)。提供的是Tap對象。無法更改Tap對象
loop(...args) => void 向攔截器添加循環(huán)將觸發(fā)循環(huán)鉤子的每個循環(huán)。
register(tap: Tap) => Tap 或 undefined將注冊添加到攔截器將觸發(fā)每個添加的Tap,并允許對其進行修改。

context

插件和攔截器可以選擇訪問可選的上下文對象,該對象可用于向后續(xù)插件和攔截器傳遞任意值。

我們通過下面的小案例,來幫助我們理解。示例代碼如下:

const { SyncHook } = require("tapable");

// 實例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);

syncHook.intercept({
  context: true,
  register(context, name) {
    console.log("every time tap", context, name);
  },
  call(context, name) {
    console.log("before call", context, name);
  },
  loop(context, name) {
    console.log("before loop", context, name);
  },
  tap(context, name) {
    console.log("before tap", context, name);
  },
});

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子2", (name) => {
  console.log("同步鉤子2", name);
});

//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端");
syncHook.call("古茗前端 call2");

控制臺輸入結(jié)果如圖所示:

由上面的案例結(jié)果,我們可以知道。intercept 中的 register 會在每一次的 tap 觸發(fā)。 有幾個 tap 就會觸發(fā)幾次 register。然后依次執(zhí)行鉤子里面的 call、tap.

intercept 特性在 webpack 內(nèi)主要被用作進度提示,如 webpack/lib/ProgressPlugin 插件中,分別對 compiler.hooks.emit 、compiler.hooks.afterEmit 鉤子應用了記錄進度的中間件函數(shù)。

HookMap

HookMap HookMap是具有Hooks的Map的輔助類.提供了一種集合操作能力,能夠降低創(chuàng)建與使用的復雜度,用法比較簡單:

const { SyncHook, HookMap } = require("tapable"); 
const syncMap = new HookMap(() => new SyncHook()); 

// 通過 for 函數(shù)過濾集合中的特定鉤子 
syncMap.for("some-key").tap("MyPlugin", (arg) => { /* ... */ });
syncMap.for("some-key").tapAsync("MyPlugin", (arg, callback) => { /* ... */ });
syncMap.for("some-key").tapPromise("MyPlugin", (arg) => { /* ... */ });

// 觸發(fā) guming-test 類型的鉤子 
syncMap.get("guming-test").call();

Tapable 底層原理

我們先將 Tapable 工程源碼克隆到本地, 執(zhí)行如下指令:

$ git clone https://github.com/webpack/tapable.git

Tapable 源碼的 lib 目錄結(jié)構(gòu), 如下所示:

lib
├─ AsyncParallelBailHook.js
├─ AsyncParallelHook.js
├─ AsyncSeriesBailHook.js
├─ AsyncSeriesHook.js
├─ AsyncSeriesLoopHook.js
├─ AsyncSeriesWaterfallHook.js
├─ Hook.js
├─ HookCodeFactory.js
├─ HookMap.js
├─ MultiHook.js
├─ SyncBailHook.js
├─ SyncHook.js
├─ SyncLoopHook.js
├─ SyncWaterfallHook.js
├─ index.js
└─ util-browser.js

除了上面我們所提及的基本 hooks 函數(shù)、HookMap高級特性,還會有一些 HookCodeFactory、Hook 這些文件。我們簡單過一下 hooks 函數(shù)內(nèi)的內(nèi)容, 會發(fā)現(xiàn)所有的 hooks 函數(shù)都會引用 HookCodeFactoryHook 這兩個文件所導出的對象實例。

我們以 syncHook 鉤子函數(shù)為例, 如下圖所示:

我們大致能夠知道 一個 hooks 函數(shù) 會由一個 CodeFactory 代碼工廠 以及 Hook 實例組成。 Hook 實例會針對不同場景的 hooks 函數(shù), 更改其對應的 注冊鉤子(tapAsync ,tap, tapPromise ),事件觸發(fā)鉤子( call , callAsync ), 編譯函數(shù)(complier)Complier 函數(shù)會由我們 HookCodeFactory 實現(xiàn)。

接下來,我們將通過分析 HookCodeFactoryHook 的內(nèi)部實現(xiàn)來了解 Tapable 的內(nèi)部實現(xiàn)機制。

Hook 實例

Hook 實例會生成我們 hooks 鉤子函數(shù)通用的 事件注冊,事件觸發(fā)。核心邏輯,我們大致可以分為以下三個部分:

  • 實例初始化構(gòu)造函數(shù)
  • 事件注冊 的實現(xiàn)
  • 事件觸發(fā)的實現(xiàn)

構(gòu)造函數(shù)

構(gòu)造函數(shù)會對實例屬性初始化賦值。代碼如下圖所示:

注冊事件

注冊事件主要分為兩塊,一塊是 適配器注冊調(diào)用, 第二塊是 觸發(fā)事件注冊。核心邏輯在 _tap 函數(shù)內(nèi)部實現(xiàn),代碼如下圖所示:

適配器調(diào)用

在這里會對攜帶 register 函數(shù)的適配器進行調(diào)用,更改 options 配置,返回新的 options 配置。代碼如下圖所示:

觸發(fā)事件注冊

Hook 實例的 taps 會存儲我們的注冊事件, 同時會根據(jù),注冊事件配置的執(zhí)行順序去存儲對應的注冊事件。

觸發(fā)事件

觸發(fā)事件會通過調(diào)用內(nèi)部 的 _createCall 函數(shù),函數(shù)內(nèi)部會調(diào)用實例的 compile 函數(shù)。我們會發(fā)現(xiàn):Hook 實例內(nèi)部不會去實現(xiàn) complier的邏輯, 不同鉤子的 complier 函數(shù)會通過通過對應的 繼承 HookCodeFactory 的實例去實現(xiàn)。代碼如下圖所示:

接下來,我們繼續(xù) 探究 HookCodeFactory 實例,了解 Tapable 事件觸發(fā)的邏輯。

HookCodeFactory 實例

HookCodeFactory 實例會根據(jù)我們傳入的事件觸發(fā)類型 (sync, async, promise)以及我們的觸發(fā)機制類型 (常規(guī) 瀑布模式 保險模式 循環(huán)模式), 生成事件觸發(fā)函數(shù)的函數(shù)頭, 函數(shù)體。通過 new Function 構(gòu)造出事件觸發(fā)函數(shù)。

Tapable 事件觸發(fā)的執(zhí)行,是動態(tài)生成執(zhí)行代碼, 包含我們的參數(shù),函數(shù)頭,函數(shù)體,然后通過 new Function 來執(zhí)行。相較于我們通常的遍歷/遞歸調(diào)用事件,這無疑讓 webpack 的整個事件機制的執(zhí)行有了一個更高的性能優(yōu)勢。

由上面我們可知, Hook 實例 的 complier 函數(shù)是 HookCodeFactory 實例 create 函數(shù) 的返回。

接下來,我們就從 create 函數(shù) 一步步揭秘 Tapable 的動態(tài)生成執(zhí)行函數(shù)的核心實現(xiàn)。

create

create 函數(shù)通過對應的 函數(shù)參數(shù), 函數(shù) header, 函數(shù) content方法構(gòu)造出我們事件觸發(fā)的函數(shù)的內(nèi)容, 通過 new Function 創(chuàng)建出我們的觸發(fā)函數(shù)。

create 函數(shù)會根據(jù)事件的觸發(fā)類型 ( sync、async、promise),進行不同的邏輯處理。代碼如下圖所示:

每一種觸發(fā)機制,都會由 this.argsthis.headerthis.contentWithInterceptors 三個函數(shù)去實現(xiàn) 動態(tài)函數(shù)的 code。代碼如下圖所示:

接下來我們看一看 this.contentWithInterceptors 函數(shù)如何生成我們事件觸發(fā)函數(shù)的函數(shù)體。

contentWithInterceptors

contentWithInterceptors 函數(shù)里包含兩個模塊, 一個是適配器 (interceptor), 一個 content 生成函數(shù)。 同時,HookCodeFactory 實例本身不會去實現(xiàn) content 函數(shù)的邏輯,會由繼承的實例去實現(xiàn)。整體結(jié)構(gòu)代碼如下圖所示:

每個 hooks 鉤子函數(shù)的 CodeFactory 實例會去實現(xiàn) content 函數(shù)。 content 函數(shù)會調(diào)用 HookCodeFactory 實現(xiàn)的不同運行機制的方法( callTap、callTapsSeries、callTapsLooping、callTapsParallel), 構(gòu)造出最終的函數(shù)體。實現(xiàn)代碼如下圖所示:

接下來,就是不同運行機制,根據(jù)不同的調(diào)用方式 ( syncasyncpromise ) 生成對應的執(zhí)行代碼。

動態(tài)函數(shù)

我們通過下面一個簡單案例,看 New Function 輸入的內(nèi)容是什么?

實例代碼如下:

const { SyncHook, AsyncSeriesHook } = require("tapable");

// 實例化 鉤子函數(shù) 定義形參
const syncHook = new SyncHook(["name"]);
const asyncSeriesHook = new AsyncSeriesHook(["name"])

//通過tap函數(shù)注冊事件
syncHook.tap("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//通過tap函數(shù)注冊事件
asyncSeriesHook.tapAsync("同步鉤子1", (name) => {
  console.log("同步鉤子1", name);
});

//同步鉤子 通過call 發(fā)布事件
syncHook.call("古茗前端sync");
asyncSeriesHook.callAsync("古茗前端async");
asyncSeriesHook.promise("古茗前端promise")

在 HookCodeFactory 的 create 打印 fn, 實例代碼如圖所示:

sync 同步調(diào)用的輸出結(jié)果如下:

 function anonymous(name
) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(name);

}

async 異步調(diào)用的輸出結(jié)果如下:

function anonymous(name, _callback
) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(name, (function(_err0) {
if(_err0) {
_callback(_err0);
} else {
_callback();
}
}));

}

promise 調(diào)用的輸出結(jié)果如下

 function anonymous(name
) {
"use strict";
var _context;
var _x = this._x;
return new Promise((function(_resolve, _reject) {
var _sync = true;
function _error(_err) {
if(_sync)
_resolve(Promise.resolve().then((function() { throw _err; })));
else
_reject(_err);
};
var _fn0 = _x[0];
_fn0(name, (function(_err0) {
if(_err0) {
_error(_err0);
} else {
_resolve();
}
}));
_sync = false;
}));

}

三種不同方式調(diào)用,內(nèi)部代碼實現(xiàn)差異還是比較清晰的。async 調(diào)用相較于 sync 多了回調(diào)函數(shù)。 asyncpromise 的區(qū)別再去返回 promise 還是回調(diào)函數(shù)。

最后,我們來用一些結(jié)構(gòu)圖來總結(jié)一下 Tapable 中事件觸發(fā)的邏輯。

HookCodeFactory 會根據(jù)我們觸發(fā)的方式,生成我們對應 new Function 里面的 content, args, header

content 最終會由 callTapsSeries、callTapsLooping、callTapsParallel 生成。每種生成方式都會包含 Done 處理、Error 處理以及 Result 處理。

總結(jié)

其他機制的 Hooks 鉤子實現(xiàn)原理大致是相同的, 這里就不一一贅述了。Tapable 是一個非常優(yōu)秀的庫,靈活擴展性高,許多優(yōu)秀的開源項目的插件化設計都借鑒或采納了 Tapable 的設計思想,是一個非常值得推薦學習的一個開源工具庫。

以上就是一文詳解Webpack中Tapable事件機制的詳細內(nèi)容,更多關于Webpack Tapable事件機制的資料請關注腳本之家其它相關文章!

相關文章

  • 利用百度地圖API獲取當前位置信息的實例

    利用百度地圖API獲取當前位置信息的實例

    下面小編就為大家?guī)硪黄冒俣鹊貓DAPI獲取當前位置信息的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望對大家有所幫助
    2017-11-11
  • Cocos2d實現(xiàn)刮刮卡效果

    Cocos2d實現(xiàn)刮刮卡效果

    這篇文章主要為大家詳細介紹了Cocos2d實現(xiàn)刮刮卡效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • JavaScript中的this關鍵字用法詳解

    JavaScript中的this關鍵字用法詳解

    在編寫JavaScript應用的時候,我們經(jīng)常會使用this關鍵字。那么this關鍵字究竟是怎樣工作的,它的設計有哪些好的地方,有哪些不好的地方,本文帶大家全面系統(tǒng)地認識這個老朋友,感興趣的小伙伴可以借鑒閱讀
    2023-05-05
  • TypeScript中的interface與type實戰(zhàn)

    TypeScript中的interface與type實戰(zhàn)

    這篇文章主要介紹了TypeScript中的interface與type詳解,它們都是用來定義類型的強大工具,在實際開發(fā)中,你可以根據(jù)具體情況選擇使用 interface 或 type,或者甚至將它們結(jié)合起來使用,需要的朋友可以參考下
    2023-06-06
  • PhotoSwipe異步動態(tài)加載圖片方法

    PhotoSwipe異步動態(tài)加載圖片方法

    這篇文章主要為大家詳細介紹了PhotoSwipe異步動態(tài)加載圖片方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • echarts地圖設置背景圖片及海岸線實例代碼

    echarts地圖設置背景圖片及海岸線實例代碼

    公司的業(yè)務涉及到統(tǒng)計圖的有很多,最近一直echarts里面踩各種坑,下面這篇文章主要給大家介紹了關于echarts地圖設置背景圖片及海岸線的相關資料,需要的朋友可以參考下
    2022-12-12
  • 前端技巧之HTTP中POST提交數(shù)據(jù)四種方式

    前端技巧之HTTP中POST提交數(shù)據(jù)四種方式

    這篇文章主要為大家介紹了前端技巧之HTTP中POST提交數(shù)據(jù)四種方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • 過期軟件破解辦法實例詳解

    過期軟件破解辦法實例詳解

    這篇文章主要介紹了過期軟件破解辦法實例詳解的相關資料,5行腳本代碼即可改變軟件期限,具有參考價值,其他軟件可參考此方法,需要的朋友可以參考下
    2017-01-01
  • 異步javascript的原理和實現(xiàn)技巧介紹

    異步javascript的原理和實現(xiàn)技巧介紹

    因為工作的需要,我要在網(wǎng)頁端編寫一段腳本,把數(shù)據(jù)通過網(wǎng)頁批量提交到系統(tǒng)中去。所以我就想到了Greasemonkey插件,于是就開始動手寫,發(fā)現(xiàn)問題解決得很順利
    2012-11-11
  • 再談JavaScript線程

    再談JavaScript線程

    繼上篇討論了一些關于JavaScript線程的知識,我們不妨回過頭再看看,是不是JavaScript就不能多線程呢?看下面一段很簡單的代碼(演示用,沒考慮兼容問題):
    2015-07-07

最新評論