【實(shí)戰(zhàn)】動(dòng)態(tài)表單之表單組件的插件式加載方案
前言
組件插件式加載方案的現(xiàn)狀
一、Webpack 懶加載
import('./moduleA').then((moduleA) => {
moduleA.add(1,2); // 3
})
import('http://static.cai-inc.com/moduleA.js').then((moduleA) => {
// ERROR,打包過(guò)程會(huì)出現(xiàn)報(bào)錯(cuò)
moduleA.add(1,2);
})

二、現(xiàn)有瀏覽器支持的 Dynamic Import
import('http://static.cai-inc.com/moduleA.js').then((moduleA) => {
moduleA.add(1,2); // 3
})
三、require.js AMD 規(guī)范
// 需要被動(dòng)態(tài)加載的 moduleA.js
define('moduleA', [], function () {
var add = function (x, y) {
return x + y;
};
return {
add: add
};
});
// 加載和使用
require.config({
paths: {
"moduleA": "lib/moduleA"
}
});
require(['moduleA'], function (moduleA){
// 代碼
moduleA.add(1,2); // 使用被動(dòng)態(tài)引入的插件的方法
});
data-main="scripts/main" 作為入口文件。但是我們的 React 項(xiàng)目也有一個(gè)入口,這會(huì)導(dǎo)致出現(xiàn)兩個(gè)入口。兩者用法并不能很好的并存。需求拆解
一、加載資源
因?yàn)椴寮为?dú)發(fā)布之后要放在 CDN 上,所以加載靜態(tài)資源的方案需要滿(mǎn)足沒(méi)有跨域限制的條件。
二、插件模塊打包
插件模塊最好能使用現(xiàn)有模塊標(biāo)準(zhǔn)例如 CMD、AMD 模塊標(biāo)準(zhǔn),這樣我們就可以使用更多的社區(qū)開(kāi)源方案,降低方案的風(fēng)險(xiǎn)性。同時(shí)降低團(tuán)隊(duì)成員學(xué)習(xí)使用成本。 插件需要能夠被注入依賴(lài),例如項(xiàng)目中已經(jīng)包含有 Lodash 或者 AntD 組件庫(kù)的包,這時(shí)候插件模塊中使用 Lodash 或者 AntD 組件庫(kù)的話(huà)我們當(dāng)然希望能夠直接引用項(xiàng)目中已有的,而不是插件模塊中重新引入一個(gè)。
需求分析
一、靜態(tài)資源加載
export default function (url) {
return new Promise(function (resolve, reject) {
const el = document.createElement('script'); // 創(chuàng)建 script 元素
el.src = url; // url 賦值
el.async = false; // 保持時(shí)序
const loadCallback = function () { // 加載成功回調(diào)
el.removeEventListener('load', loadCallback);
resolve(result);
};
const errorCallback = function (evt) { // 加載失敗回調(diào)
el.removeEventListener('error', errorCallback);
var error = evt.error || new Error("Load javascript failed. src=" + url);
reject(error);
};
el.addEventListener('load', loadCallback);
el.addEventListener('error', errorCallback);
document.body.appendChild(el); // 節(jié)點(diǎn)插入
});
}
二、為加載模塊注入依賴(lài)
// require.js
const modules = {};
const define = function(moduleName, depends, callback){
modules[moduleName] = { // 將模塊存起來(lái),等待后續(xù)調(diào)用
depends,
callback,
};
}
// moduleA.js
define('moduleA', [], ()=>{
// code
})
備注: 這里是對(duì) AMD 進(jìn)行了粗略的原理解釋?zhuān)唧w實(shí)現(xiàn)還有很多細(xì)節(jié),想要了解的話(huà),可以在網(wǎng)上找到很多源碼解析,這里就不再細(xì)講。 Webpack 打包之后的代碼的模塊管理方式是 Webpack 自己實(shí)現(xiàn)的一套類(lèi)似 CommonJS 規(guī)范的東西。去看看打包生成的代碼就可以發(fā)現(xiàn)里面都是一些 webpack_modules__,webpack_require,webpack_exports 這樣的關(guān)鍵詞,和 CommonJS 規(guī)范的 modules,require,exports 相對(duì)應(yīng)。
三、模塊打包標(biāo)準(zhǔn)
變量:作為一個(gè)全局變量,通過(guò) script標(biāo)簽來(lái)訪(fǎng)問(wèn)(libraryTarget:'var')。this:通過(guò) this對(duì)象訪(fǎng)問(wèn)(libraryTarget:'this')。window:通過(guò) window對(duì)象訪(fǎng)問(wèn),在瀏覽器中(libraryTarget:'window')。UMD:在 AMD 或 CommonJS 的 require之后可訪(fǎng)問(wèn)(libraryTarget:'umd')。AMD:基于 AMD 規(guī)范的打包方式( libraryTarget:'amd')。
export default {
test: ()=>{
console.log('測(cè)試模塊打包!');
}
};
define(["lodash"], (__WEBPACK_EXTERNAL_MODULE__92__) => (() => {
// code ...
// return funciton
})());
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("lodash")); // cmd
else if(typeof define === 'function' && define.amd)
define(["lodash"], factory); // amd
else { //
var a = typeof exports === 'object' ? factory(require("lodash")) : factory(root["_"]);
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(self, function(__WEBPACK_EXTERNAL_MODULE__92__) {
// code
});
方案選取
一、加載資源的方案
采用動(dòng)態(tài)插入 Script 方式實(shí)現(xiàn) JS 資源加載。
二、模塊打包方案
UMD 規(guī)范的打包方式。
// importScript.js
export default function (url, _) {
const defineTemp = window.define; // 將 window 下的 define 方法暫存起來(lái)。
let result; // 結(jié)果
window.define = (depends, func) => { // 自定義 define 方法,
result = func(_); // 包依賴(lài)注入
}
window.define.amd = true; // 偽裝成 amd 的 define。
return new Promise(function (resolve, reject) {
const el = document.createElement('script'); // 創(chuàng)建 script 元素
el.src = url;
el.async = false; // 保持時(shí)序
const loadCallback = function () { // 加載完成之后處理
el.removeEventListener('load', loadCallback);
window.define = defineTemp;
resolve(result);
};
const errorCallback = function (evt) { // 加載失敗之后處理
el.removeEventListener('error', errorCallback);
window.define = defineTemp;
var error = evt.error || new Error("Load javascript failed. src=" + url);
reject(error);
};
el.addEventListener('load', loadCallback); // 綁定事件
el.addEventListener('error', errorCallback); // 綁定事件
document.body.appendChild(el); // 插入元素
});
}
import importScript from './importScript.js';
import _ from 'lodash';
importScript('http://static.cai-inc.com/app.bundle.js', _).then((mod)=>{
// code mod.xxx
})
三、與自定義表單結(jié)合
總結(jié)
歡迎關(guān)注「前端雜貨鋪」,一個(gè)有溫度且致力于前端分享的雜貨鋪
關(guān)注回復(fù)「加群」,可加入雜貨鋪一起交流學(xué)習(xí)成長(zhǎng)
評(píng)論
圖片
表情
