Angular Universal预生成页面:选择器" app-root"与任何元素都不匹配

时间:2018-03-27 20:29:32

标签: angular angular-universal

我一直试图拼凑如何预先生成现有Angular Universal应用程序的页面。我坚持这个例外:

  

选择器" app-root"与任何元素都不匹配。

执行node dist/pre-render.js /时,我得到了异常。

请注意,此应用程序适用于ng servenpm run build:universal; npm run serve:universal。所以这并不像忘记在app.module declarations中放置一样简单。

重现的步骤:

  1. 运行npm run build:universal
  2. 运行node dist/pre-render.js /
  3. 观察错误The selector "app-root" did not match any elements
  4. 以下是设置:

    package.json脚本部分

    我认为这与the Angular guide相同。

    "build:universal": "npm run build:client-and-server-bundles && npm run webpack:server",
    "serve:universal": "node dist/server.js",
    "build:client-and-server-bundles": "ng build --prod && ng build --prod --app 1 --output-hashing=false",
    "webpack:server": "webpack --config webpack.server.config.js --progress --colors"
    

    webpack.server.config.js

    这也是straight out of the guide,除非您注意我还有一个entry来自我pre-render.js的{​​{1}}脚本,我将在下面展示。

    pre-render.ts

    const path = require('path'); const webpack = require('webpack'); module.exports = { entry: { server: './server.ts', 'pre-render': './pre-render.ts' }, resolve: { extensions: ['.js', '.ts'] }, target: 'node', // This makes sure we include node_modules and other third-party libraries. externals: [/(node_modules|main\..*\.js)/], output: { path: path.join(__dirname, 'dist'), filename: '[name].js' }, module: { rules: [{ test: /\.ts$/, loader: 'ts-loader' }] }, plugins: [ // Temporary Fix for issue: https://github.com/angular/angular/issues/11580 // for 'WARNING Critical dependency: the request of a dependency is an expression' new webpack.ContextReplacementPlugin( /(.+)?angular(\\|\/)core(.+)?/, path.join(__dirname, 'src'), // location of your src {} // a map of your routes ), new webpack.ContextReplacementPlugin( /(.+)?express(\\|\/)(.+)?/, path.join(__dirname, 'src'), {} ) ] };

    我从the guide's server.ts filea book by Philippe Martin获取了提示。该指南还没有解释如何做这些事情,菲利普的书中的例子并不适合我。

    pre-render.ts

    说实话,我不知道import 'zone.js/dist/zone-node'; import { renderModuleFactory } from '@angular/platform-server'; import { enableProdMode } from '@angular/core'; import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; const args = process.argv.slice(2); if (args.length !== 1) { process.stdout.write('Usage: node dist/pre-render.js <url>'); process.exit(); } enableProdMode(); const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle'); renderModuleFactory( AppServerModuleNgFactory, { url: args[0], extraProviders: [ provideModuleMap(LAZY_MODULE_MAP) ] } ).then(rendered => { process.stdout.write(rendered); }); 属性的用途,或者extraProviders是否适合放在那里。但如果我不给它任何provideModuleMap(...),那么我会收到这个错误:

      

    NullInjectorError:没有InjectionToken MODULE_MAP的提供程序

    很明显,财产必须对某些事情很重要。

    此外,我注意到,extraProviders属性与documenturl并列无效,即使extraProviders属性是不带{{1}的字符串元素。

2 个答案:

答案 0 :(得分:0)

所有遗漏的是claim.Children.AddRange (newChildren)属性,其中包含字符串中的document元素。

当我将此对象作为第二个参数传递给<app-root></app-root>时,它可以正常工作。请注意添加renderModuleFactory属性:

document

现在,虽然这会导致呈现某些内容,但这并不完全是正确的,因为我们的开头的document: `<!DOCTYPE html> <html> <body> <app-root></app-root> </body> </html>`, url: args[0], extraProviders: [ provideModuleMap(LAZY_MODULE_MAP) ] 字符串与document的内容不同。< / p>

要解决这个问题,只需执行Angular Universal Starter在its prerender.ts file中所做的事情:

index.html

注意他们如何读取已编译的const index = readFileSync(join('browser', 'index.html'), 'utf8'); renderModuleFactory(AppServerModuleNgFactory, { document: index, url: route, extraProviders: [ provideModuleMap(LAZY_MODULE_MAP) ] }) 文件的内容,并使用 字符串作为browser/index.html属性。

答案 1 :(得分:-1)

在server.ts文件中添加以下行。

import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
 }));

这将有助于在服务器端映射模块。