【问题标题】:Loading Custom Component ViewModel from JavaScript file从 JavaScript 文件加载自定义组件 ViewModel
【发布时间】:2015-09-29 07:18:32
【问题描述】:

我正在尝试在 knockout 中创建自定义组件加载器,但我在视图模型方面遇到了困难。本质上,我想远程获取 HTML 模板和 JavaScript 视图模型,但在这种情况下,我不想使用传统的 AMD 模块加载器。

我已经设法完成了其中的一些工作,特别是加载 HTML 模板,但我不知道如何加载视图模型。在我开始之前,这是我的目录结构:

-- index.html
-- customerLoader.js
-- 组合
-- myCustom.html
-- myCustom.js

所以我已经像这样创建了我的组件加载器。 getConfig 基本上采用组件的名称并将其转换为 viewModel 和 html 模板的路径。

var customLoader = {
    getConfig: function(name, callback) {
        callback({ template: "comps/" + name + ".html", viewModel: "comps/" + name + ".js" });
    },
    loadTemplate: function(name, templateConfig, callback) {
        console.log("loadTemplate", name, templateConfig);
        $.get(templateConfig, function(data) {
            callback(data);
        });
    },
    loadViewModel: function(name, templateConfig, callback) {
        console.log("loadViewModel", name, templateConfig);
        $.getScript(templateConfig, function(data) {
            callback(data);
        });
    }
};

ko.components.loaders.unshift(customLoader);

这成功地发出了加载模板的请求,这会带回一些基本内容。我正在努力解决的是视图模型。我不确定我的 JavaScript 文件的目标应该是什么?

我假设我想返回一个带有一些参数的函数,很可能是一个params 对象。但是,如果我尝试这样做,我会收到一个错误,告诉我 JavaScript 无效:

Uncaught SyntaxError: Illegal return statement

这是产生此错误的当前内容:

return function(params) {

    console.log("myCustom.js", name, viewModelConfig);

    // Add a computed value on
    params.bookNum = ko.computed(function() {
        switch(this.title()) {
            case "A": return 1;
            case "B": return 2;
            case "C": return 3;
            default: return -1;
        }
    });

    //ko.components.defaultLoader.loadViewModel(name, viewModelConstructor, callback);

};

所以最终我不确定如何实现这一点,但我想有 3 个基本问题可以解释我的理解差距:

  1. 我的“视图模型”JavaScript 文件应该包含什么?一个函数?一个东西?等等……
  2. 我需要拨打ko.components.defaultLoader.loadViewModel吗?
  3. 在我的customLoader 中,loadViewModel() 应该如何处理 jQuery 回调的结果?我不确定我是返回一个 JavaScript 对象,还是只是一个字符串?

如果需要,我愿意以不同的方式实现这一点(例如,不使用 jQuery 而是以不同的方式获取文件),但我不想使用模块加载器(例如 require.js/curl.js在这种情况下)。

【问题讨论】:

  • 您的脚本不应以return 开头。应该是可以独立执行的代码。

标签: javascript knockout.js


【解决方案1】:

首先让我们弄清楚发生了什么......

来自docs

这个($.getScript())是一个简写的Ajax函数,相当于:

$.ajax({
  url: url,
  dataType: "script",
  success: success
});

来自jQuery.ajax()

...
数据类型: ...
"script":将响应评估为 JavaScript 并将其作为纯文本返回。

因此,您的代码被提取、评估,然后将作为文本返回,但评估首先失败,因为如果您不在函数内,则无法 return

那么可以做些什么呢?有几种选择:

  1. 使用模块加载器。
    jQuery 不是模块加载器,因此它无法解析获取的代码并从该代码创建值/对象。专门为此任务设计了一个模块加载器。它将采用以特定模式编写的脚本并将其“评估”为一个值(通常是具有 1 个或多个属性的对象)。
  2. 将您的脚本更改为合法脚本
    因为在全局代码中有 return 语句是非法的,所以您当前的代码会失败。但是,您可以创建一个命名函数(或具有函数表达式的变量),然后使用该名称来引用该函数。它可能看起来像这样:

    function myCreateViewModel(param) {
      // whatever
    }
    

    而用法是:

    $.getScript(templateConfig, function() {
        callback(myCreateViewModel);
    });
    

    这里的缺点是,如果您在同一页面中通过该代码路径两次,您的脚本将覆盖旧的声明。这可能永远不是问题,但感觉很脏。

  3. 不使用 $.getScript(),使用 $.ajax() (或 $.get())与 @987654333 @并评估自己。
    从您的代码中删除return,并用eval() 包装它。它将被评估为函数表达式,eval 的返回值将是您的函数,您可以将其直接传递给回调:

    $.get({
      url: templateConfig,
      dataType: 'text',
      success: function(text) {
        callback(eval(text));
      }
    });
    

    这会起作用,但它会使用皱眉的eval(),这会让您面临各种风险。

【讨论】:

  • 感谢您的回答@Amit,这是有道理的。我假设典型的模块加载器在加载脚本时使用 must eval(),如果这是获取后执行某些内容的唯一方法?
  • 实际上可能使用new Function(...),但差别不大。他们会尽可能安全地使用经过试验和测试的代码。
  • 是的,我知道 eval 的问题,所以在决定时会考虑到这一点。一旦我证明其中一种技术有效,我就会接受你的回答。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-22
  • 1970-01-01
  • 2019-10-20
相关资源
最近更新 更多