设置 Angular 通用应用程序以进行开发

Setting up Angular Universal App for development

我用 Angular-CLI 创建了一个项目。 (使用命令:ng new my-angular-universal)。 然后我仔细按照 https://github.com/angular/angular-cli/wiki/stories-universal-rendering

的所有说明进行操作

它为 --prod 构建并且运行良好。但是没有关于如何设置 --dev 构建并使用 --watch 标志服务的说明。

我尝试从 npm "scripts" 中删除 --prod 标志,但在开发模式下甚至 运行 都没有。它构建良好,但当我在浏览器中打开它时,这是我看到的(直接打印到响应):

 TypeError: Cannot read property 'moduleType' of undefined
    at C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:7069:134
    at ZoneDelegate.invoke (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:105076:26)
    at Object.onInvoke (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:6328:33)
    at ZoneDelegate.invoke (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:105075:32)
    at Zone.run (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:104826:43)
    at NgZone.run (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:6145:69)
    at PlatformRef.bootstrapModuleFactory (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:7068:23)
    at Object.renderModuleFactory (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:52132:39)
    at View.engine (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:104656:23)
    at View.render (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:130741:8)

目前我使用的npm包版本是最新的:

除了 ts-loader,不得不降级,因为它不工作:

因此,如果有人知道如何进行这项工作,我们将不胜感激!或者,也许您知道一些项目模板具有 Angular 通用应用程序,为 --dev--prod 构建配置并能够 --watch?

基本上有两部分 - 服务器和 UI。在开发 UI 时,我只是使用 ng serve。这意味着当我在 IDE 中更改我的代码时,浏览器会自动刷新。而且,这里没有使用服务器部分。

我做产品构建和 运行 服务器只是为了最终测试,看看是否一切都按预期工作(没有由于任何 3PP 库 DOM 操作或 AOT 相关问题等引起的错误)

Here,我创建了一个 Angular 通用项目的骨架结构。当我在我的项目中广泛使用 Vagrant 和 Docker 时,我 运行 Vagrant 来宾系统中 Docker 容器中的服务器。而为了UI的开发,我没有运行服务器。简单地说,使用ng serve

如果您在上面 Github link 中查看我的结构,您将在自述文件中找到有关如何 运行 将其用于开发和生产的详细信息。

Web 服务器处理程序 server.ts 使用服务器包

const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');

这就是为什么在编译 server.ts 文件之前需要先编译服务器包的原因。

所以拥有手表系统就意味着

  • watching/recompiling 客户端包
  • watching/recompiling 服务器包
  • 创建服务器包后重新编译 server.ts

所有这些都需要一些时间(尤其是如果您使用 aot 来完成)

我建议,就像 Saptarshi Basu 提到的那样,使用 ng serve 尽可能地开发并经常检查 angular universal。

否则,应该可以通过某种任务 (grunt/gulp/...) 实现您想要的,这些任务会依次触发 ng build ... 并重新编译 server.ts 文件。

对于开发,运行 npm run start 触发 ng serve。当前设置具有热模块重新加载,因此它会监视您的更改并更新您的开发视图。我使用了相同的说明并在此处运行 https://github.com/ariellephan/angular5-universal-template

简而言之,为了发展,运行 npm run start 并查看 http://localhost:4200

对于生产,运行 npm run build:ssrnpm run serve:ssr 并查看 http://localhost:4000

正如贡献者所指出的,这可能不是最有效和最快的开发方式,但我不想接受变通方法。此外,在不同的服务器上托管前端和后端会带来 CORS 问题,而且我从未计划将我的应用程序 运行 放在不同的主机上,我希望它与 API 方法一起位于同一主机上。

--dev 构建的问题是这样的:

使用以下命令构建时:

ng build --app 1 --output-hashing=false(注意没有--prod标志)

AppServerModuleNgFactory竟然不见了./dist-server/main.bundle

我想这与提前 (--aot) 编译有关,这是为 --prod 构建时的默认行为。因此 https://github.com/angular/angular-cli/wiki/stories-universal-rendering 中的说明包括为生产构建配置 express 服务器的说明。并且由于服务器不需要能够动态呈现 html 模板,因此工作 --dev 构建命令将是:

ng build --app 1 --output-hashing=false --aot

这摆脱了 TypeError: Cannot read property 'moduleType' of undefined

现在来看这整个烂摊子:

运行 这些在单独的命令中 windows:

  1. ng build --watch
  2. ng build --app 1 --output-hashing=false --aot --watch
  3. webpack --config webpack.server.config.js --progress --colors --watch

要让服务器在更改时重新启动,您必须像这样安装 nodemon 软件包和 运行:

  1. nodemon --inspect dist/server--inspect 如果你想用 chrome 调试服务器)

一些其他重要的东西:

Angular/CLI 有一条命令可以为通用应用程序生成必要的脚手架:

ng generate universal

并且它生成一个固定版本的 main.ts 来避免 client angular bootstrap issuedocument.addEventListener('DOMContentLoaded', () => { platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.log(err)); });

实施后偶然发现的一个问题TransferState

毫无疑问,这有点混乱,因为我们更希望一个命令来统治它们。 我想出了一个还算不错的解决方案,我的输出将是: dist/browser dist/ng-server

使用可执行文件 npm-运行-all 包(我发现它在 windows 机器上比同时运行好很多)我 运行 三个监视任务:浏览器, ng-server 和 nodeJS。监视节点定义了一个预任务,它只是 运行 一个小的 utility/helper/file,监视 dist/ng-server 文件夹的存在并在找到后自行终止。

为了使所有这些都起作用(基于截至 2018 年 11 月的 universal-starter 存储库),需要对 package.json 进行一些修改。首先,为了在 ng 运行 命令上支持 --watch 标志,我们需要更新 compiler-cli(如果有记忆的话),ng update --all 应该处理这个问题,为您提供最新的 angular/cli 版本正在处理中(假设您在全局安装了最新的 cli 版本)。

package.json

  1. ng 更新 --all
    • angular6+
    • angular/cli 7+
  2. yarn add/npm 安装以下
    • chokidar
    • npm-运行-全部 (运行我们的任务与 -p 标志并行。-p 杀死所有进程,-l 在控制台中为每个 运行ning 任务赋予特定的颜色和名称)
    • ts-node(运行s nodejs 的 ts 格式)
    • nodemon // 用于重启 ts-node
  3. 添加类似于我的 util/await-file.js 的内容(经过一番考虑,我在下面添加了自己的文件观察器代码,尽管它并不是为了展示而编写的…… )
  4. 修改您的 package.json 脚本,如下所示
  5. 修改你的 angular.json 以匹配你的文件夹名称,按照我的示例,主要是 "server" 的输出路径应该从 dist/server 更改为 dist/ng-server。

package.json 脚本

"dev": "npm-run-all -p -r -l watch:ng-server watch:browser watch:node",
"watch:browser": "ng build --prod --progress --watch --delete-output-path",
"watch:ng-server": "ng run ng-universal-demo:server --watch --delete-output-path",
"watch:node": "yarn run watch:file-exist && yarn run ts-node",
"ts-node": "nodemon --exec ts-node server.ts -e ts,js",
"watch:file-exist": "node utils/await-file.js",

util/await-file.js

const chokidar = require('chokidar');
const fs = require('fs');
const path = require('path');
const DIR_NAME = 'ng-server';

const DIST_PATH = './dist';

// creates dist folder if it doesn't exist - prior to adding it to the watcher.
if (!fs.existsSync(DIST_PATH)) {
  fs.mkdirSync(DIST_PATH);
}

const watcher = chokidar.watch('file, dir', {
  ignored: '*.map',
  persistent: true,
  awaitWriteFinish: {
    stabilityThreshold: 5000,
    pollInterval: 100
  }
});

const FOLDER_PATH = path.join(process.cwd(), 'dist');
watcher.add(FOLDER_PATH);

console.log(`file-watcher running, waiting for ${DIST_PATH}/${DIR_NAME}`);

function fileFound() {
  console.log(`${DIR_NAME} folder found - closing`);
  watcher.close();
  process.exit();
}

watcher
  .on('add', function (filePath) {
    const matchWith = path.join('dist', DIR_NAME);
    const paths = filePath.split(path.sep);
    const fileName = paths[paths.length - 1];
    if ((filePath.indexOf(matchWith) >= 0)
      && fileName.indexOf('.js') > fileName.length - 4) {
      fileFound();
    }
  })
  .on('error', error => console.log(`Watcher error: ${error}`));

"npm 运行 start" 和使用 "http://localhost:4200" 对我有用。即使 Angular 10