为开发设置 Angular 通用应用程序

Posted

技术标签:

【中文标题】为开发设置 Angular 通用应用程序【英文标题】:Setting up Angular Universal App for development 【发布时间】:2018-09-23 04:29:11 【问题描述】:

我用 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 包版本是最新的:

@angular/* - @5.2.* @angular/cli @1.7.3

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

ts-loader @3.5.0

因此,如果有人有任何有关如何使这项工作的信息,将不胜感激!或者,也许您知道一些项目模板,其中配置了为 --dev--prod 构建以及 --watch 的能力配置的 Angular Universal App?

【问题讨论】:

这意味着您需要使用 watch 构建 3 件事,对:客户端包、服务器包以及重新构建 server.js/ts? 看来是这样。但是我很难理解为什么服务器包和 server.ts 代码被分开处理。 真正帮助我理解 Angular Universal 和所有构建步骤的是观看此视频:youtube.com/watch?v=QDasXnb6EFI。我强烈建议您观看并遵循所有步骤。 【参考方案1】:

“npm run start”和使用“http://localhost:4200”对我有用。即使使用 Angular 10

【讨论】:

【参考方案2】:

毫无疑问,这有点混乱,因为我们希望一个命令来统治所有这些。 我想出了一个不错的解决方案,我的输出将是: 分配/浏览器 dist/ng-服务器

使用可执行的 npm-run-all 包(我发现它在 Windows 机器上比同时运行要好得多)我运行三个监视任务:浏览器、ng-server 和 nodeJS。监视节点定义了一个预任务,它只运行一个小型实用程序/帮助程序/文件,监视 dist/ng-server 文件夹的存在并在找到后自行终止。

为了使所有这些工作(基于截至 2018 年 11 月的通用启动器存储库),需要对 package.json 进行一些修改。首先,为了支持 ng run 命令上的 --watch 标志,我们需要更新编译器-cli(如果有记忆的话), ng update --all 应该负责这一点,在这个过程中为您提供最新的 angular/cli 版本(假设您在全球范围内安装了最新的 cli 版本)。

package.json

    ng 更新 --all 角度6+ 角度/cli 7+ yarn add/npm install 如下 乔基达 npm 运行所有 (与 -p 标志并行运行我们的任务。-p 杀死所有进程,-l 在控制台中为每个正在运行的任务提供特定的颜色和名称) ts-node(以 ts 格式运行 nodejs) nodemon // 用于重启 ts-node 添加类似于我的 util/await-file.js 的内容(经过一番考虑,我在下面添加了我自己的文件监视程序代码,尽管它的编写并不是为了展示...) 修改您的 package.json 脚本,如下所示 修改您的 angular.json 以匹配您的文件夹名称,按照我的示例,主要是“服务器”的 outputPath 应该从 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`));

【讨论】:

【参考方案3】:

正如贡献者所指出的,这可能不是最有效和最快的开发方式,但是我不想接受变通方法。此外,将前端和后端托管在单独的服务器上会带来 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 的说明仅包括为生产构建配置快速服务器的说明。而且由于服务器不需要能够动态呈现 html 模板,因此工作的 --dev 构建命令将是:

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

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

现在来看整个混乱:

在单独的命令窗口中运行它们:

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

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

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

其他一些重要的东西:

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

ng generate universal

它会生成一个固定版本的main.ts,避免client angular bootstrap issue: document.addEventListener('DOMContentLoaded', () => platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.log(err)); );

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

【讨论】:

我相信你在写这篇文章时它已经奏效了。但就目前而言,尽管提供的监视命令似乎可以正确执行所有操作,但在我明确运行 build:s-s-rserve:s-s-r 之前,任何更改都不会反映在任何地方。 ng serve 和其他命令也不做任何事情,实际上会产生空白页。【参考方案4】:

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

简而言之,对于开发,运行npm run start 并查看http://localhost:4200

对于生产,运行 npm run build:s-s-rnpm run serve:s-s-r 并查看 http://localhost:4000

【讨论】:

我真的希望这行得通 :( 但是通用的依赖项之一,domino,与ng serve 出错,因为到处都是'use strict' 指令不喜欢使用该模块@987654332 @ :|【参考方案5】:

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

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

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

所以拥有手表系统意味着

观察/重新编译客户端包 观察/重新编译服务器包 创建服务器包后重新编译server.ts

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

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

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

【讨论】:

结果证明,当我在--dev 模式下构建时,server/main.bundle 中完全缺少AppServerModuleNgFactory。这可能就是我得到这个TypeError: Cannot read property 'moduleType' of undefined error 的原因。知道为什么它在非--prod 构建中丢失了吗?【参考方案6】:

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

我进行 prod 构建和运行服务器仅用于最终测试,以查看一切是否按预期工作(由于任何 3PP 库 DOM 操作或 AOT 相关问题等没有错误)

Here,我创建了一个 Angular Universal 项目的骨架结构。由于我在项目中广泛使用 Vagrant 和 Docker,因此我在 Vagrant 来宾系统内的 Docker 容器中运行服务器。对于 UI 的开发,我不运行服务器。简单来说就是ng serve

如果您在上面的 Github 链接中查看我的结构,您会在自述文件中找到有关如何运行它以进行开发和生产的详细信息。

【讨论】:

如果您进行了更改,您需要添加标志 --live-reload 以自动重新加载网络。

以上是关于为开发设置 Angular 通用应用程序的主要内容,如果未能解决你的问题,请参考以下文章

使用 Firebase 云功能为带有 i18n 的 Angular 10 s-s-r 通用应用程序提供服务

所有页面通用的 Angular ngOnInit

如何调试 Angular 通用?

使用 angular-cli 在通用渲染中使用 cookie

路由时超时。 Angular 9 通用 + Firebase

将 Angular 2/3/4/5/6 项目转换为 Angular Universal