使用Browserify的JS库与Angular 2一起使用

17
我希望能在我的Angular 2项目中使用libquassel(https://github.com/magne4000/node-libquassel)。该库已经被browserify过,理论上应该可以工作,但我不太确定该如何将其导入到我的项目中。
我尝试将其添加到我的typings.d.ts文件中。
declare module 'libquassel';

然后使用以下命令导入该库:

import * as Quassel from 'libquassel';

但是我得到了
EXCEPTION: net.Socket is not a function

当我尝试运行我的代码时,我相信这是browserify嵌入在client/libquassel.js文件中的另一个库。

我该如何使用这个库?

编辑:我将在此回答所有问题:

  • My setup is a plain angular-cli project. No fancy stuff, just ng new proj1 and then npm install libquassel --save.
  • My index.html doesn't have anything else that ng new hasn't placed in there.
  • I tried importing the library with import * as Quassel from 'libquassel' and var Quassel = require('quassel') (and permutations of those), without any luck (errors varying from unknown function 'require' to can't find module lib|quassel).
  • Steps to repro my project:

    ng new test
    cd test
    npm install libquassel --save
    ng g s quassel
    
然后在app.module.ts中的providers数组中添加QuasselService。这是我的问题的一个很好的演示,即如何在QuasselService中导入libquassel

你能提供更多有关你的设置的详细信息吗?你的index.html是什么样子的?你在使用angular-cli吗?你如何导入这个模块? - eko
@echonax 请检查我的编辑。你也可以复制我的项目,因为这只是一个普通的angular-cli项目。 - alexandernst
@Aravind 哪个配置文件? - alexandernst
systemjs 还是 webpack? - Aravind
@Aravind,请在提问时更加详细。正如我之前所说,这是一个angular-cli项目(使用ng new生成),所以我不太明白你的问题。 - alexandernst
显示剩余3条评论
4个回答

5

更新(修正,这次是真的)

为什么你的代码不起作用有一个非常好的原因:因为我对你撒了谎。所以,如果你还没有自己“diffed”它,这里是我的真正作弊方法。

我仍然需要两次require libquassel,但第一次不是通过调用无用的require来完成的,而是将其添加到angular-cli.jsonscripts部分:

  "scripts": [
    "../node_modules/libquassel/client/libquassel.js"
  ],

这很重要,因为“client/libquassel.js”声明了自己的require,但没有显式导出它,所以如果你这样做。
require('libquassel/client/libquassel.js');

libquassel的自定义require被留在匿名命名空间中,您无法访问它。另一方面,将其添加到scripts部分可以让libquassel.js污染全局命名空间(即window),并使其工作。

因此,使其正常工作的实际步骤是:

  1. 将client/libquassel.js添加到angular-cli.jsonscripts部分中
  2. 使用额外的require来加载Quassel模块
let Quassel = window['require']('quassel'); // note that usingg require('quassel') leads to compilation error
let quasselObj = new Quassel(...);

这里是我的尝试。看起来你需要两次require "quassel": 第一次从"../node_modules/libquassel/client/libquassel.js"加载JS,第二次让覆盖的require从该JS实际解析"quassel"。这里有一个技巧,在'main.ts'中似乎对我有效:
require('../node_modules/libquassel/client/libquassel.js');
let Quassel = window['require']('quassel'); // note that using require('quassel') leads to compilation error
let quasselObj = new Quassel(...);

我需要一个技巧来避免编译错误:特别是在第二次调用时,我必须使用window ['require']而不是只使用require,因为这是对client/libquassel.js内定义的内部require的调用,Node / Angular-cli无法处理它。
请注意,在设置后,我的应用程序仍然在某些AJAX中出现运行时错误,因为浏览libquassel.js似乎需要在服务器端上设置代理,例如/api/vm/net/connect的URL,这可能是net-browsify的服务器端应该执行的操作,但我没有尝试设置它。
回答评论:
看起来您正在使用旧版本的Angular-CLI,如果您升级,一切都应该正常工作。
以下是我的原始机器上ng --version告诉我的内容。
angular-cli: 1.0.0-beta.28.3
node: 6.10.0
os: win32 x64
@angular/common: 2.4.10
@angular/compiler: 2.4.10
@angular/core: 2.4.10
@angular/forms: 2.4.10
@angular/http: 2.4.10
@angular/platform-browser: 2.4.10
@angular/platform-browser-dynamic: 2.4.10
@angular/router: 3.4.10
@angular/compiler-cli: 2.4.10

当我试图将原始项目复制到另一台机器上时,我也遇到了一个有关require的编译错误,但当我更新Angular-CLI后,问题得到了解决。
@angular/cli: 1.0.0
node: 6.10.0
os: darwin x64
@angular/common: 2.4.10
@angular/compiler: 2.4.10
@angular/core: 2.4.10
@angular/forms: 2.4.10
@angular/http: 2.4.10
@angular/platform-browser: 2.4.10
@angular/platform-browser-dynamic: 2.4.10
@angular/router: 3.4.10
@angular/cli: 1.0.0
@angular/compiler-cli: 2.4.10

它编译并且工作一样。我所做的就是遵循说明。

"angular-cli" 软件包已被弃用并重命名为 "@angular/cli"。

请执行以下步骤以避免问题:
"npm uninstall --save-dev angular-cli"
"npm install --save-dev @angular/cli@latest"

然后根据 ng serve的说明更新 angular-cli.json 文件。


这看起来不错!不用担心你遇到的AJAX错误,那是因为你需要一个特别定制的expressjs服务器在监听。 - alexandernst
我终于回家了,正在测试你的代码,但出现了这个错误:src/app/quassel-client.service.ts (6,1): Cannot find name 'require' - alexandernst
@alexandernst,我现在不在我的代码中,但你从哪里得到它?这是针对行require('../node_modules/libquassel/client/libquassel.js');的编译错误还是针对let Quassel = window['require']('quassel')的运行时错误? - SergGr
它能在您的项目中工作,所以肯定是我的有问题。我将通过比较您的代码进行调试。因为这是最清晰的解决方案(在我看来), 我接受它。感谢您的帮助和出色回答! - alexandernst
1
@alexandernst,你的代码为什么不起作用有一个非常好的原因:因为我骗了你。我想我找到了真正让我的项目工作的东西。请查看问题中的更新,如果出于某种原因这仍然对你不起作用,请告诉我。 - SergGr
显示剩余5条评论

1
工作得很好:
declare var require;
const Quassel = require( 'libquassel/lib/libquassel.js' );
console.log( 'Quassel', Quassel );

看了一下node_module中的libquassel文件夹,发现根目录下没有index.js文件。
如果没有index.js文件,则无法执行以下操作:
 require( 'libquassel' );

仅仅因为node不知道要为你拉取什么,所以你需要指定确切的路径,在这种情况下并不糟糕。

另外,请注意最好将declare var require;移动到位于src文件夹下的您的typings文件中,因为您可能需要再次声明它,所以最好在那里。

编辑: 以下是我尝试实例化Quassel的结果:

const Quassel = require( 'libquassel/lib/libquassel.js' );
console.log( 'Quassel', Quassel );
var quassel = new Quassel( "quassel.domain.tld",
    4242,
    { backloglimit : 10 },
    function ( next ) {
        next( "user", "password" );
    } );
console.log( 'quassel', quassel );

以下是我的控制台日志:

在此输入图像描述 但是,我发现libquassel.js内部存在问题,如下所示:

在第10行,他们这样做:

   var net = require('net');

查看他们的package.json文件,没有像net这样的东西,但有net-browserify-alt;

因此,如果他们将导入更改为:

 var net = require('net-browserify-alt'),

一切都会正常工作。

话虽如此,显然你不想编辑你的node_module,但我真的很惊讶这个在angular和webpack之外也能正常工作,因为他们明显提到了一个错误的node_module,如果你搜索一下,只有一个名为net的包,我看了一下,它是空的和虚假的!!!

** ********** 更新:********** **

需要做什么:

1- 运行 ng eject

这将在你的根目录中生成一个webpack.conf.js

2- 在其中找到resolve属性并添加:

 "alias":{
     'net':'net-browserify-alt'
  },

所以你的解决方案可能看起来像:
"resolve": {
    "extensions": [
      ".ts",
      ".js"
    ],
      "alias":{
         'net':'net-browserify-alt'
      },
    "modules": [
      "./node_modules"
    ]
  },

3- 导入并使用它:

declare var require;
const Quassel = require( 'libquassel/lib/libquassel.js' );
console.log( 'Quassel', Quassel );
var quassel = new Quassel( "quassel.domain.tld",
    4242,
    { backloglimit : 10 },
    function ( next ) {
        next( "user", "password" );
    } );
console.log( 'quassel', quassel );

注意:

看一下webpack配置,似乎webpack喜欢覆盖一些模块:

"node": {
    "fs": "empty",
    "global": true,
    "crypto": "empty",
    "tls": "empty",
    "net": "empty",
    "process": true,
    "module": false,
    "clearImmediate": false,
    "setImmediate": false
  }

我不太清楚为什么,但这个列表在webpack配置中,似乎导致net变成未定义(空),这就是我们不得不创建别名的原因。


3
net 是一个 Node.js 内置模块(https://nodejs.org/api/net.html)。 - Alexus
@alexandernst,试试我的更新,它在我的本地完全可用。 - Milad
1
一些标准的 Node 模块不能在 Web 浏览器中运行,只能在 Node 中运行。这就是为什么有 netnet-browserify-alt 的映射 - 这样它就可以在浏览器中运行。 OP 需要包含所有这些映射。 - K Scandrett
1
@Milad,你所说的“工作”是什么意思?你能成功执行quassel.connect吗?还是只是创建了一个新对象(这远远不够)?你是否获得了真正的连接?或者至少你是否收到了向服务器发送的AJAX请求?从我在代码中看到的内容来看,似乎你需要至少其中相当一部分的依赖项才能使它真正地工作。 - SergGr
1
@SergGr 在这里说得有道理,映射可能是个坏主意,因为这就是 Browserify 所做的,而且它可能比我们做得更好。 - alexandernst
显示剩余9条评论

0

关键问题是browserified的libquassel库内部包含全局的require函数。这与标准的require函数冲突。

有很多处理方法,但其中一种解决方案是将/libquassel/client/libquassel.js(和minified版本)复制到你的/src/app/目录中。

然后在这些文件中运行搜索和替换以将所有出现的require改为_quasselRequire_

你需要对libquassel.jslibquassel.min.js都进行此操作。

要使用它,你可以像这样在quassel.service.ts中执行:

import { Injectable } from '@angular/core';
import * as dummyQuassel1 from './libquassel.js'

var dummyQuassel2 = dummyQuassel1; // something just to force the import

declare var _quasselRequire_ :any;

@Injectable()
export class QuasselService {

  constructor() { 

    // set up in the constructor as a demo

    let Quassel = _quasselRequire_( 'quassel' );
    console.log('Quassel', Quassel);

    var quassel = new Quassel( 
        "quassel.domain.tld",
        4242,
        { backloglimit : 10 },
        function ( next ) {
            next( "user", "password" );
        } 
    );

    console.log('quassel', quassel);

    quassel.on('network.init', function(networkId) {
        console.log("before net");
        var network = quassel.getNetworks().get(networkId);
        console.log("after net");
        // ...
    });

    console.log("before connect");
    quassel.connect();  
    console.log("after connect");
  }

  getQuassel() {
      return 'My Quassel service';
  }
}

这本身就导致了以下错误:

POST http://localhost:4200/api/vm/net/connect 404 (未找到)

错误:未捕获,未指定的“error”事件。 在新的 Error (native) 上 在 Quassel.n.emit (http://localhost:4200/main.bundle.js:570:4210) [angular] 在 Socket.(http://localhost:4200/main.bundle.js:810:19931) [angular] 在 Socket.EventEmitter.emit (http://localhost:4200/main.bundle.js:573:1079) [angular] ...

但据我了解,这在我没有设置 Express 等时是相当正常的。

注意:名称 _quasselRequire_ 可以是适合您的任何内容。


如你所知,你还需要在 tsconfig.app.jsoncompilerOptions 中添加属性 "allowJs": true - K Scandrett
如果你要这样做,你可能需要编写一个脚本来完成,因为大多数情况下,安装依赖项后就不会再去碰它们了。从升级的角度来看,这也是不好的,如果你想升级软件包,那么你就会遇到麻烦。 - Milad
你为什么会被搞糊涂了? - K Scandrett
@alexandernst 我同意,但我从未建议替换node_module中的任何内容。我上面的建议是先将最终的browserified脚本(libquassel/client/libquassel.[min].js)复制到您项目的/src/app/中,然后在那里进行编辑。当然,这一切都可以很容易地自动化。 - K Scandrett
1
可以使用https://www.npmjs.com/package/string-replace-loader来实现,这样Webpack就能从`node_modules`中获取最新版本的browserified库,并在打包过程中搜索并替换`require`为`_quasselRequire_`。 - K Scandrett
显示剩余3条评论

0

Angular-cli使用webpack

您需要将JS文件添加到angular-cli.json文件中的apps [0] .scripts中,然后Webpack会将其捆绑在一起,就像使用标签加载一样。

apps:[
{
  ...
  "scripts": [
    "../node_modules/libquassel/client/libquassel.js"
  ],

如果您这样做,可以通过在src/typings.d.ts或组件中添加declare var Quassel: any;来访问它。
let quassel = new Quassel("localhost", 4242, {}, function(next) {
  next("user", "password");
});
quassel.connect();

1
我收到了EXCEPTION: Quassel is not defined的异常。并让我解释一下(在这篇文章中已经是第5次了),这不是一个正常的JS库,它导出的东西是一个Browserify库,应该被require,这就是你的解决方法不起作用的原因(实际上这也是我为什么给这个问题分配500个点数的原因,因为它非常棘手)。如果我在一个脚本标签中加载这个库,唯一能够访问它的方法是通过var Quassel = require("quassel") - alexandernst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接