为开发设置 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-r
和 serve: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-r
和 npm 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-cli 在通用渲染中使用 cookie