从JavaScript文件加载自定义组件ViewModel

从JavaScript文件加载自定义组件ViewModel,javascript,knockout.js,Javascript,Knockout.js,我试图在knockout中创建一个自定义组件加载器,但我在视图模型方面遇到了困难。基本上,我想远程获取HTML模板和JavaScript视图模型,但在本例中,我不想使用传统的AMD模块加载器 我已经设法实现了一些功能,特别是加载HTML模板,但我不知道如何加载视图模型。在开始之前,这里是我的目录结构: --index.html --customerLoader.js --comps    -- myCustom.html    -- myCustom.js 所以我创建了我的组件加载器ge

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

我已经设法实现了一些功能,特别是加载HTML模板,但我不知道如何加载视图模型。在开始之前,这里是我的目录结构:

--index.html
--customerLoader.js
--comps
   -- 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无效:

未捕获的SyntaxError:非法返回语句

这是我得到的导致此错误的当前内容:

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);

};
因此,最终我不确定如何实现这一点,但我想有三个基本问题可以解释我理解上的差距:

  • 我的“视图模型”JavaScript文件应该确切包含什么?函数?一个物体?等等
  • 我是否需要调用
    ko.components.defaultLoader.loadViewModel
  • 在我的
    customLoader
    中,
    loadViewModel()
    应该如何处理jQuery回调的结果?我不确定我是得到一个JavaScript对象,还是一个字符串
    如果需要的话,我愿意以不同的方式实现这一点(例如,不使用jQuery,而是以不同的方式获取文件),但我不想使用模块加载器(例如,在本例中使用require.js/curl.js)。

    首先让我们看看发生了什么

    从:

    这(
    $.getScript()
    )是一个速记Ajax函数,相当于:

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


    数据类型:
    “script”
    :将响应评估为JavaScript并以纯文本形式返回

    因此,您的代码会被提取、求值,然后会作为文本返回,但求值首先会失败,因为如果您不在函数中,则无法返回

    那么我们能做些什么呢?有几种选择:

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

    function myCreateViewModel(param) {
      // whatever
    }
    
    其用法是:

    $.getScript(templateConfig, function() {
        callback(myCreateViewModel);
    });
    
    这里的缺点是,如果在同一页面中两次遍历该代码路径,脚本将覆盖旧的声明。这可能永远不会是个问题,但感觉很脏

  • 不要使用
    $.getScript()
    ,使用
    $.ajax()
    (或
    $.get()
    )和
    数据类型:“文本”
    并评估自己。
    从代码中删除
    return
    ,并用
    eval()将其包装起来。它将作为函数表达式进行计算,eval的返回值将是您的函数,您可以将其直接传递给回调:

    $.get({
      url: templateConfig,
      dataType: 'text',
      success: function(text) {
        callback(eval(text));
      }
    });
    
    这将起作用,但它将使用皱眉的
    eval()
    ,这将使您面临各种风险


  • 您的脚本不应以
    返回开头
    。它应该是可以独立执行的代码。感谢您的回答@Amit,这很有意义。我假设典型的模块加载器在加载脚本时必须使用
    eval()
    ,如果这是获取后执行某些内容的唯一方法?可能实际上使用
    新函数(…)
    ,但差别不大。他们会尽可能安全地使用经过测试的代码。是的,我知道eval的问题,所以在决定时会考虑到这一点。一旦我证明其中一种方法有效,我就会接受你的答案。非常感谢。