问题描述
这与javascript代码有关。我的应用程序有许多子应用程序,有时在一个页面上使用多次。一个示例是允许用户搜索某些数据并显示搜索结果的应用程序。此应用程序可用于页面上的多个位置以搜索不同类型的数据。 每个子应用程序通常在一个单独的文件中包含许多 javascript 模块。我经历过许多不同的模块模式来尝试创建多个模块/文件的单独实例,但没有成功。网上有很多关于如何创建对象的多个实例、使用工厂模式等的建议,但我无法使其与我的应用程序的名称空间结构和模块模式一起使用。请参阅下面的示例。
问题是如何创建 SubAppA 的多个独立实例,包括其所有子模块。
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.Config = (function () {
function A () { ... };
function B () { ... };
return {
A : A,B : B
}
})();
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.GetData = (function () {
function A () { ... };
function B () { ... };
return {
A : A,B : B
}
})();
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.displayData = (function () {
etc.....
非常感谢
--- MikeM 提出的解决方案后的附加信息 -----
谢谢 MikeM,你的回答让我有了更好的理解,但是当我尝试使用我现有的命名空间结构来实现它时,我无法让模块相互通信。我尝试了以下方法:
//Solution $S.AS - New file
var $S = $S || {};
$S.AS = $S.AS || {};
$S.AS.DataStore = function () {
var _SomeVar = "Default";
function Setvar (Data) {
_SomeVar = Data;
};
function Getvar () {
return _SomeVar;
};
return {
Setvar : Setvar,Getvar : Getvar
}
};
//Solution $S.AS - New file
var $S = $S || {};
$S.AS = $S.AS || {};
$S.AS.ManageData = function () {
function StoreData (Data) {
console.log($S.AS.DataStore); //outputs f ()
//Does Now work since DataStore is Now a function
//$S.AS.DataStore.Setvar(Data);
$S.AS.DataStore().Setvar(Data);
};
function displayData () {
//Does Now work since DataStore is Now a function
//var SomeVar = $S.AS.DataStore.Getvar();
//Does not work,still outputs "Default"
var SomeVar = $S.AS.DataStore().Getvar();
console.log(SomeVar);
};
return {
StoreData : StoreData,displayData : displayData
}
};
//Solution $S.AS - New file - The contructor function for AS
var MainApp = MainApp || {};
MainApp.S = MainApp.S || {};
MainApp.S.AS = MainApp.S.AS || {};
MainApp.S.AS = function () {
this.DataStore = $S.AS.DataStore();
this.ManageData = $S.AS.ManageData();
//additional modules
};
//Main/Page specific code - creating the different instances
MainApp.S.AS_1 = new MainApp.S.AS();
MainApp.S.AS_2 = new MainApp.S.AS();
//Attemps to store and retrieve data
//Stores AAA in the DataStore module
MainApp.S.AS_1.ManageData.StoreData("AAA");
//Stores BBB in the DataStore module
MainApp.S.AS_2.ManageData.StoreData("BBB");
//Not working,ouputs: "Default" (Desired result is "AAA")
MainApp.S.AS_1.ManageData.displayData();
//Not working,ouputs: "Default" (Desired result is "BBB");
MainApp.S.AS_2.ManageData.displayData();
我想我明白为什么输出“默认”(调用页面加载时存储的原始变量)但不知道如何修复它。
对于上下文,我有一个自定义 PHP 脚本,它连接页面所需的所有 JS 文件,然后将它们作为单个标签添加到页面中。我认为这会加速脚本加载,特别是因为我的大多数页面都有 50 多个 JS 文件/模块。页面的典型名称空间结构如下所示(但包含更多模块):
MainApp = {
//Page specific or utility modules
ModuleA : [ func / module ],ModuleB : [ func / module ],ModuleC : [ func / module ],ModuleD : [ func / module ],//Resulable applications consisting of multiple modules
SubAppA : {
ModuleA : [ func / module ],},SubAppB : {
ModuleA : [ func / module ],}
}
我希望我能以某种方式保留这个结构以避免模块名称冲突的风险。我很高兴改变模块本身的结构(例如从 IIFE 到其他东西)以获得问题的解决方案。 谢谢
解决方法
谢谢 MikeM,它现在可以工作了,非常有帮助!是的,我知道我需要更仔细地研究 ES 模块。在一个完全不同的领域工作了 17 年之后,我最近学会了编码以测试一个想法,因此必须做出一些捷径......
这里是我一步一步的实现,以防它对不太熟悉 Javascript 模块的其他人有所帮助。
问题的目的是:
- 在每个应用程序由多个模块组成的页面上启用应用程序的多个实例
- 允许模块调用其他模块中的方法
- 使应用程序的实例化(创建新版本)变得容易
与以下解决方案相关的一些注意事项:
- 而不是静态调用其他模块中的方法(例如 ReuseApp.App1.Display.DisplayData(Data) 每个模块存储新创建的应用程序实例的顶级对象的内部引用(例如 _App.Display.DisplayData(数据)。
- 创建的模块没有立即引起的特征(即没有 IIFE 模式)。
- 需要引用所有必需模块的构造函数。这个函数会将新创建的对象(this)发送到每个模块中,例如this.Config = ReuseApps.App1.Config(this);
- 每个模块将此引用作为参数(App)并将其存储在模块(_App)中。 _App 将在调用其他模块方法时使用。
分步指南:
步骤 A:使用以下模式创建模块(如果不需要,则忽略多级命名空间):
//Separate file e.g. Config.js
const ReuseApps = ReuseApps || {};
ReuseApps.App1 = ReuseApps.App1 || {};
ReuseApps.App1.Config = function (App) {
let _App; //Used to call other module methods
let _Settings = {};
function Init (Settings) {
_Settings = Settings;
//Configure app e.g. store element refs,add event handlers etc
var Data = GetDataFromSomeWhere();
//Call another module using the _App reference
_App.Display.DisplayData(Data);
}
_App = App;
Return {
Init : Init
}
}
//Separate file e.g. Display.js
const ReuseApps = ReuseApps || {};
ReuseApps.App1 = ReuseApps.App1 || {};
ReuseApps.App1.Display = function (App) {
let _App; //Used to call other module methods
function DisplayData (Data) {
//Display Data in DOM
}
_App = App;
return {
DisplayData : DisplayData
}
}
步骤 B:创建创建应用程序新实例所需的构造函数
//can be in separate file e.g. app1_create.js
function App1Create () {
this.Config = ReuseApps.App1.Config(this);
this.Display = ReuseApps.App1.Display(this);
//etc more modules …
}
步骤 C:在主代码中创建上述应用程序的单独实例
//Create new instance using constructur function
//(Assumes the MainApp namespace exists already)
MainApp.ViewDataList = new App1Create();
//If application needs to be initiated
var Settings = { some settings };
MainApp.ViewDataList.Config.Init(Settings);
,
创建“SubAppA 的多个独立实例,包括其所有子模块”的一种简单示例,其中模块定义在多个文件中:
// test.js
import { MainApp } from './mainApp.js';
import { SubApp } from './subApp.js';
MainApp.SubAppA = new SubApp();
console.log(MainApp.SubAppA.Config.A()); // 1
MainApp.SubAppB = new SubApp();
console.log(MainApp.SubAppB.Config.B()); // -1
// subApp.js
import { config } from './config.js';
import { getData } from './getData.js';
export function SubApp() {
this.Config = config();
this.GetData = getData();
}
// config.js
export function config() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,B: B
}
}
作为单个文件:
const MainApp = {};
function SubApp() {
this.Config = config();
this.GetData = getData();
}
function config() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,B: B
}
}
function getData() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,B: B
}
}
MainApp.SubAppA = new SubApp();
console.log(MainApp.SubAppA.Config.A()); // 1
console.log(MainApp.SubAppA.GetData.A()); // 1
console.log(MainApp.SubAppA.Config.B()); // 0
MainApp.SubAppB = new SubApp();
console.log(MainApp.SubAppB.Config.B()); // -1
console.log(MainApp.SubAppB.GetData.B()); // -1
console.log(MainApp.SubAppB.Config.A()); // 0
与您自己的代码的重要区别在于,我将立即调用函数表达式 (IIFE) 替换为每次创建子应用程序时都会创建闭包的普通函数。
为响应您的修改而添加:
为了使您在编辑中添加的代码起作用,您需要确保在创建新的 ManageData
对象时传递对父对象的引用:
$S.AS.ManageData = function (owner) {
function StoreData (Data) {
owner.DataStore.SetVar(Data);
};
function DisplayData () {
var SomeVar = owner.DataStore.GetVar();
console.log(SomeVar);
};
return {
StoreData : StoreData,DisplayData : DisplayData
}
};
// ...
MainApp.S.AS = function () {
this.DataStore = $S.AS.DataStore();
this.ManageData = $S.AS.ManageData(this);
};
我鼓励您使用 ES 模块来构建代码并避免命名冲突。可以从模块捆绑器(例如 roll-up.js)创建单个文件。