草庐IT

javascript - 动态加载外部 webpack 捆绑的 ngModule 作为路由处理程序

coder 2024-05-06 原文

我们想将我们的大型前端项目分成多个单独部署的项目,这样更容易使用。我正在尝试包含一个捆绑的 ngModule 来处理来自另一个应用程序的路由。这些应用程序必须不知道彼此的配置。这些包将通过全局变量共享一些大型依赖项(如 Angular)。我们不需要跨包摇晃,我们可能只需要接受一些重复的依赖项。

根路由器提示

Error: No NgModule metadata found for 'TestsetModule'.

这让我相信子模块没有在加载时进行 Angular 编译,或者由于某种原因没有注册它的模块。我认为可能需要手动编译模块,但我不确定如何使用这个 https://angular.io/api/core/Compiler#compileModuleAndAllComponentsAsync

根应用程序通过路由加载子应用程序:

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const load = require("little-loader");


const routes: Routes = [
  { path: ``, loadChildren: () => new Promise(function (resolve) {
      load('http://localhost:3100/testset-module-bundle.js',(err: any) => {
        console.log('global loaded bundle is: ', (<any>global).TestsetModule )
        resolve((<any>global).TestsetModule)
      }
    )
  })}
];

export const HostRouting: ModuleWithProviders = RouterModule.forRoot(routes);

我也尝试过使用 Angular 路由器的字符串解析语法而不是你看到的这种奇怪的全局事物,但我遇到了类似的问题。

这是正在加载的模块,除了全局导出外非常标准:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';
//import { MaterialModule } from '@angular/material';
import { FlexLayoutModule } from '@angular/flex-layout';
import { FormsModule }   from '@angular/forms';
import { LoggerModule, Level } from '@churro/ngx-log';

import { FeatureLoggerConfig } from './features/logger/services/feature-logger-config';


import { TestsetComponent } from './features/testset/testset.component';
import { TestsetRouting } from './testset.routing';

@NgModule({
    imports: [
        CommonModule,
        //MaterialModule,
        FlexLayoutModule,
        HttpModule,
        FormsModule,
        LoggerModule.forChild({
          moduleName: 'Testset',
          minLevel: Level.INFO
        }),
        TestsetRouting,
    ],
    declarations: [TestsetComponent],
    providers: [
      /* TODO: Providers go here */
    ]
})
class TestsetModule { }
(<any>global).TestsetModule = TestsetModule

export {TestsetModule as default, TestsetModule};

这是根包的 webpack 配置。请注意通过命名不当的“ProvidePlugin”进行的全局导出。

const webpack = require('webpack');
const AotPlugin = require('@ngtools/webpack').AotPlugin;
const path = require('path');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const IgnorePlugin = require('webpack/lib/IgnorePlugin');
const PolyfillsPlugin = require('webpack-polyfills-plugin');
const WebpackSystemRegister = require('webpack-system-register');


module.exports = (envOptions) => {
    envOptions = envOptions || {};
    const config = {

        entry: {
          'bundle': './root.ts'
        },
        output: {

          libraryTarget: 'umd',
          filename: '[name].js',//"bundle.[hash].js",
          chunkFilename: '[name]-chunk.js',
          path: __dirname
          },
          externals: {

          },
        resolve: {
            extensions: ['.ts', '.js', '.html'],
        },
        module: {
            rules: [
                { test: /\.html$/, loader: 'raw-loader' },
                { test: /\.css$/, loader: 'raw-loader' },

            ]
        },
        devtool: '#source-map',
        plugins: [
          new webpack.ProvidePlugin({
            'angular': '@angular/core',
            'ngrouter': '@angular/router',
            'ngxlog':'@churro/ngx-log'
          })

        ]
    };
    config.module.rules.push(
      { test: /\.ts$/, loaders: [
        'awesome-typescript-loader', 
        'angular-router-loader',
        'angular2-template-loader', 
        'source-map-loader'
      ] } 
    );
  }



    return config;
};

这是子包的 webpack 配置。请注意将 Angular 视为全局的“外部”。

module.exports = (envOptions) => {
    envOptions = envOptions || {};
    const config = {
        entry: {
          'testset-module-bundle': './src/index.ts'
        },
        output: {
          //library: 'TestsetModule',
          libraryTarget: 'umd',
          filename: '[name].js',//"bundle.[hash].js",
          chunkFilename: '[name]-chunk.js',
          path: path.resolve(__dirname, "dist")
          },
          externals: {
            //expect these to come from the app that imported us
            // name to be required : name from global
             'angular': '@angular/core',
             'ngrouter': '@angular/router',
             'ngxlog': '@churro/ngx-log'       
          },
        resolve: {
            extensions: ['.ts', '.js', '.html'],
        },
        module: {
            rules: [
                { test: /\.html$/, loader: 'raw-loader' },
                { test: /\.css$/, loader: 'raw-loader' },

            ]
        },
        devtool: '#source-map',
        plugins: [

        ]
    };

    config.module.rules.push(
      { test: /\.ts$/, loaders: [
        'awesome-typescript-loader', 
        'angular-router-loader',
        'angular2-template-loader', 
        'source-map-loader'
      ] }
    );
  }



    return config;
};

这里还有我的 tsconfig 文件,“awesome-typescript-loader”读取它。

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true,
    "baseUrl": ".",
    "rootDir": "src",
    "outDir": "app",
    "paths": {
      "@capone/*": [
        "*"
      ],
      "@angular/*": [
        "node_modules/@angular/*"
      ],
      "rxjs/*": [
        "node_modules/rxjs/*"
      ]
    }
  },

  "exclude": ["node_modules", "src/node_modules", "compiled", "src/dev_wrapper_app"],
  "angularCompilerOptions": {
    "genDir": "./compiled",
    "skipMetadataEmit": true
  }
}

如果您还在阅读,那太棒了。当两个包都是同一个 webpack 配置的一部分并且子模块只是一个 block 时,我能够让它工作。 Angular 就是为此而设计的。但是我们的用例是让 child 和 parent 在运行时之前彼此不知道。

最佳答案

正如你所说

The apps must be ignorant of each other's configuration.

我在 Angular2 中遇到了类似的问题。我通过创建一个子应用程序解决了它。一个单独的 sub-main.browser.ts 和 index.html 文件。它有自己的依赖关系,共享相同的节点模块。两个主要模块都引导不同的应用程序组件。我们在没有 angular-cli 的情况下使用 Angular。

在webpack配置中,我添加了

entry: {

  'polyfills': './src/polyfills.browser.ts',
  'main' .   :     './src/main.browser.aot.ts',
  'sub-main' : '/src/sub-main.browser.ts'

},

和更详细的 HtmlWebpackPlugin。在 block 中,我们只加载将在两个应用程序中使用的模块。如果我们看到 polyfill 很常见。

   new HtmlWebpackPlugin({
    template: 'src/index.html',
    title: METADATA.title,
    chunksSortMode: 'dependency',
    metadata: METADATA,
    inject: 'head',
    chunks: ['polyfills','main']
  }),

  new HtmlWebpackPlugin({
    template: 'src/index2.html',
    title: 'Sub app',
    chunksSortMode: 'dependency',
    metadata: METADATA,
     inject: 'head',
    filename: './sub-app.html',
    chunks: ['polyfills','sub-main']
  }),

下一个任务是为开发环境的两个子应用程序创建单独的端点。

devServer: {
      port: METADATA.port,
      host: METADATA.host,
      historyApiFallback: true,
      watchOptions: {
        aggregateTimeout: 300,
        poll: 1000
      },
      proxy: {
   "/sub-app": {
    target: "http://localhost:3009",
    bypass: function(req, res, proxyOptions) {
        return "/index2.html";
    }
  }
}
    },

现在,当我构建项目时,会生成两个不同的 HTML 文件。每个都有自己的 javascript 包依赖项和公共(public) Assets 。它们也可以部署在不同的服务器上。

我通过大量的反复试验完成了我的 POC。我的建议是看起来比 Angular 高出一步。查看 webpack 如何部署您当前的项目。如果您可以配置它以满足您的目的。

关于javascript - 动态加载外部 webpack 捆绑的 ngModule 作为路由处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47018621/

有关javascript - 动态加载外部 webpack 捆绑的 ngModule 作为路由处理程序的更多相关文章

  1. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  5. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  6. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  7. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  8. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  9. ruby-on-rails - Rails 3,嵌套资源,没有路由匹配 [PUT] - 2

    我真的为这个而疯狂。我一直在搜索答案并尝试我找到的所有内容,包括相关问题和stackoverflow上的答案,但仍然无法正常工作。我正在使用嵌套资源,但无法使表单正常工作。我总是遇到错误,例如没有路线匹配[PUT]"/galleries/1/photos"表格在这里:/galleries/1/photos/1/edit路线.rbresources:galleriesdoresources:photosendresources:galleriesresources:photos照片Controller.rbdefnew@gallery=Gallery.find(params[:galle

  10. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

随机推荐