设置 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包版本是最新的:
- @angular/* - @5.2.*
- @angular/cli@1.7.3
除了 ts-loader,不得不降级,因为它不工作:
- ts-loader @3.5.0
因此,如果有人知道如何进行这项工作,我们将不胜感激!或者,也许您知道一些项目模板具有 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:ssr
和 npm 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:
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
毫无疑问,这有点混乱,因为我们更希望一个命令来统治它们。
我想出了一个还算不错的解决方案,我的输出将是:
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
- ng 更新 --all
- angular6+
- angular/cli 7+
- yarn add/npm 安装以下
- chokidar
- npm-运行-全部
(运行我们的任务与 -p 标志并行。-p 杀死所有进程,-l 在控制台中为每个 运行ning 任务赋予特定的颜色和名称)
- ts-node(运行s nodejs 的 ts 格式)
- nodemon // 用于重启 ts-node
- 添加类似于我的 util/await-file.js 的内容(经过一番考虑,我在下面添加了自己的文件观察器代码,尽管它并不是为了展示而编写的…… )
- 修改您的 package.json 脚本,如下所示
- 修改你的 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
我用 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
因此,如果有人知道如何进行这项工作,我们将不胜感激!或者,也许您知道一些项目模板具有 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:ssr
和 npm 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:
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
毫无疑问,这有点混乱,因为我们更希望一个命令来统治它们。 我想出了一个还算不错的解决方案,我的输出将是: 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
- ng 更新 --all
- angular6+
- angular/cli 7+
- yarn add/npm 安装以下
- chokidar
- npm-运行-全部 (运行我们的任务与 -p 标志并行。-p 杀死所有进程,-l 在控制台中为每个 运行ning 任务赋予特定的颜色和名称)
- ts-node(运行s nodejs 的 ts 格式)
- nodemon // 用于重启 ts-node
- 添加类似于我的 util/await-file.js 的内容(经过一番考虑,我在下面添加了自己的文件观察器代码,尽管它并不是为了展示而编写的…… )
- 修改您的 package.json 脚本,如下所示
- 修改你的 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