卡住将 ngResource angular 服务转换为 Vanilla JS

Stuck converting ngResource angular service to Vanilla JS

我们正在将我们的网站从旧 angularjs 迁移到使用 Vuejs。 第一步是修改我们整个站点使用的服务,这些服务都严重依赖 ngResource,并将它们转换为可以被 Vue 调用的 vanilla js 代码。

挑战在于,除了使用 ngResource 进行 API 调用外,他们还通过原型扩展了 returning 对象。 在常规 javascript 中使用模块模式时,我可以模仿 ngResource 服务的 API 行为。但我不清楚如何设置它以便它也可以支持应用于 returning 对象(无论是单个对象还是数组)的原型扩展。

例如,我们当前的一项服务可能如下所示

        "use strict";
    
    angular.module("myApp")
        .factory("PortfolioService",
            [
                "$resource", "$rootScope", "$http",
                function($resource,
                    $rootScope,
                    $http) {
                    var Portfolio = $resource("services/portfolios/:Uid",
                        {
                            '_': function() { return Date.now() }
                        }, {
                   'query': {
                        method: "GET",
                        url: "services/portfolios/",
                        transformResponse: $http.defaults.transformResponse.concat([
                            function (data) { return data.Data; }
                        ])
                    }
                });
                      
                    Portfolio.prototype.getPicUrl= function() {
                        return this.ImgBasePath + this.ImgUrl;
                    };
                  return Portfolio;
            }
        ]);

注意:它会调用一个名为 query 的服务,但也会使用一个名为 getPicUrl 的新函数扩展 returning 对象。

我创建了一个类似这样的 JS 等价物

const vPortfolioService = (() => {
    var baseapipath = "http://localhost:8080/services/";

    var Portfolio = {
        query: function() {
            return axios.get(baseapipath + "portfolios/");
        }
    };

    Portfolio.prototype.getPicUrl= function () {
         return this.ImgBasePath + this.ImgUrl;
    }

    return Portfolio;
})();

服务部分工作正常,但我不知道如何做 ngResource 似乎做的事情,即 return 来自 API 的资源,其中包括原型扩展。

如有任何建议,我们将不胜感激。 谢谢

对象实例不公开 prototype 属性。相反,您可以使用以下方式访问它:

  object.__proto__ // not recommended, or better
  Object.getPrototypeOf(object)

Object.getPrototypeOf() return 对象的原型对象,可用于分配新属性。

Object.getPrototypeOf(Portfolio).getPicUrl= function () {
     return this.ImgBasePath + this.ImgUrl;
}

注意:不过,您仍然可以通过执行 Function.prototype.

访问 Function() 的原型

更新:您的投资组合应该是一个新对象,从全局 javascript 对象创建,以避免 @user3781737 提到的问题。

 var Portfolio = Object.create({
      query: function() {
        return axios.get(baseapipath + "portfolios/");
      }
    });

正如我在对@Igor Moraru 的回复中提到的,这取决于您要替换的代码库有多少,以及现有代码库中有多少使用了 ngResource 的全部功能,这不是琐碎的事情。但是只关注你问题中的具体示例,你需要先了解一些普通的 JS。

为什么 Portfolio 对象在从 $resource() 返回时有 prototype 属性,但在通过对象字面量创建时却没有?简单:$resource() 返回的对象是一个函数,这反过来也意味着它是一个 class,并且那些自动具有 prototype 属性。

在JavaScript中,正则函数和classes是一回事。唯一的区别是意图。在这种情况下,$resource() 返回的函数旨在用作 class,并且很容易复制 class 的某些方面,例如静态 query 方法和非静态(即在原型上)getPicUrl 方法:

const vPortfolioService = (() => {
    var baseapipath = "http://localhost:8080/services/";

    class Portfolio {
        constructor(params) {
            Object.assign(this, params);
        }
        
        static query() {
            return axios.get(baseapipath + "portfolios/").then(res => {
                // this convert the objects into an array of Portfolio instances
                // you probably want to check if the response is valid before doing this...
                return res.data.map(e => new this(e));
            });
        }
        
        getPicUrl() {
            return this.ImgBasePath + this.ImgUrl;   
        }
    }

    return Portfolio;
})();

但问题是,这可能还不够。如果你是 migrating/refactoring 一个完整的应用程序,那么你必须确定你的应用程序使用 ngResource 的每个实例,并且根据你的问题,我相当确定你使用它的次数比这个多 class 将允许。

例如$resource创建的每个class也有getpost等静态方法,以及对应的实例方法如$get$post 等。此外,我为 class 提供的 constructor 只是一个非常懒惰的权宜之计,允许您创建具有任意属性的实例,例如 [=19= 引用的属性] 方法。

所以我认为你有三个选择:

  • 继续使用上面的 class 以适应更接近您需要的东西,然后编辑应用程序中您的代码依赖组合服务的每个地方,以便它现在反映这个新的、更有限的class。这可能是您的最佳选择,特别是如果您的应用程序不是那么大并且您不必担心其他人的代码无法正常工作
  • 分析 ngResource 的源代码,将其删除并修改,使其不需要 AngularJS 即可工作。也许有人已经这样做并将其作为图书馆提供?我想有点远景,但它可能会奏效。
  • 在您的应用程序中保留 AngularJS,与 Vue 一起使用,但仅使用它来获取 $http$resource 等必需品。下面是一个示例实现,但这可能是最糟糕的选择。 bootstrapping angular 的片段会产生额外的开销,而且它可能需要 bootstrap 其他我没有想到的片段......但它很整洁,我想大声笑:
const vPortfolioService = (() => {
    var inj = angular.injector(["ng", "ngResource"]);
    var $http = inj.get("$http"), $resource = inj.get("$resource");

    var Portfolio = $resource("services/portfolios/:Uid",
        {
            '_': function () { return Date.now() }
        }, {
        'query': {
            method: "GET",
            url: "services/portfolios/",
            transformResponse: $http.defaults.transformResponse.concat([
                function (data) { return data.Data; }
            ])
        }
    });

    Portfolio.prototype.getPicUrl = function () {
        return this.ImgBasePath + this.ImgUrl;
    };
    
    return Portfolio;
})();