diff --git a/.travis.yml b/.travis.yml index add582f4be..92fc263cb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ addons: - ubuntu-toolchain-r-test packages: - g++-4.8 + chrome: stable branches: except: - g3 @@ -21,5 +22,6 @@ cache: before_install: - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.10.1 - export PATH="$HOME/.yarn/bin:$PATH" +- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost & script: - "./aio/deploy-cn.sh" diff --git a/aio/deploy-cn.sh b/aio/deploy-cn.sh index a6e7ca7053..dbe117e358 100755 --- a/aio/deploy-cn.sh +++ b/aio/deploy-cn.sh @@ -9,7 +9,14 @@ commitMessage=$(git log --oneline -n 1) cd `dirname $0` yarn build -ts-node ./tools/translator/bin/ssr.ts + +yarn start & + +sleep 10 + +yarn update-webdriver + +yarn prerender if [[ ! -d "./ng-docs.github.io" ]] then diff --git a/aio/package.json b/aio/package.json index 8289932391..3ecc291bb2 100644 --- a/aio/package.json +++ b/aio/package.json @@ -68,7 +68,8 @@ "post~~build": "yarn build-404-page", "~~build-ie-polyfills": "webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production", "~~http-server": "http-server", - "~~minify-lunr": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/generated/lunr.min.js --source-map" + "~~minify-lunr": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/generated/lunr.min.js --source-map", + "prerender": "protractor tests/e2e/protractor.conf.js --disableChecks --specs tests/e2e/prerender.e2e-spec.ts \"--grep=^Prerender \"" }, "engines": { "node": ">=10.9.0 <11.0.0", diff --git a/aio/src/app/app.module.ts b/aio/src/app/app.module.ts index b5b57dd261..8f597a46c0 100644 --- a/aio/src/app/app.module.ts +++ b/aio/src/app/app.module.ts @@ -132,7 +132,7 @@ export const svgIconProviders = [ @NgModule({ imports: [ - BrowserModule, + BrowserModule.withServerTransition({appId: 'ng-docs'}), BrowserAnimationsModule, CustomElementsModule, HttpClientModule, diff --git a/aio/src/main.ts b/aio/src/main.ts index dc96f6ad56..00ca40d010 100644 --- a/aio/src/main.ts +++ b/aio/src/main.ts @@ -8,5 +8,7 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); - +document.addEventListener('DOMContentLoaded', () => { + platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); +}); diff --git a/aio/tests/e2e/prerender.e2e-spec.ts b/aio/tests/e2e/prerender.e2e-spec.ts new file mode 100644 index 0000000000..7f953cd99e --- /dev/null +++ b/aio/tests/e2e/prerender.e2e-spec.ts @@ -0,0 +1,88 @@ +import { browser, promise } from 'protractor'; +import { readFileSync, writeFileSync } from 'fs'; +import { concat, defer, Observable } from 'rxjs'; +import { map, switchMap, tap } from 'rxjs/operators'; +import * as path from 'path'; +import * as mkdirp from 'mkdirp'; +import { minify } from 'html-minifier'; +import * as globby from 'globby'; + +const minifyOptions = { + collapseWhitespace: true, + ignoreCustomFragments: [/[\s\S]*?<\/code>/], + minifyCSS: true, + minifyJS: true, + removeComments: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, +}; + +function fromPromise(input: promise.Promise): Observable { + return Observable.create(observer => { + input.then((value) => { + observer.next(value); + observer.complete(); + }); + }); +} + +function prerender(url: string): Observable { + return defer(() => fromPromise(browser.get(url))).pipe( + tap(() => browser.executeScript('document.body.classList.add(\'no-animations\')')), + tap(() => browser.waitForAngular()), + switchMap(() => fromPromise(browser.executeScript('return document.documentElement.outerHTML'))), + map(content => minify(content, minifyOptions)), + map((content) => `${content}`), + tap((html) => { + const filename = path.join('dist', `${url}.html`); + mkdirp.sync(path.dirname(filename)); + writeFileSync(filename, html, 'utf-8'); + }), + tap(() => console.log('Rendered ', url)), + ); +} + +function getGuideUrls(): string[] { + const navigation = readFileSync('./content/navigation.json', 'utf-8'); + return (navigation.match(/"url": "(.*?)"/g) || []) + .map((entry) => entry.replace(/^"url": "(.*?)".*$/, '$1')) + .filter(url => url.slice(0, 4) !== 'http'); +} + +function getApiUrls(): string[] { + return globby.sync('./src/generated/docs/api/**/*.json') + .map(file => file.replace(/^.*generated\/docs\/(.*).json$/, '$1')); +} + + +function renderUrlsByTemplate(urls: string[], templateUrl: string): void { + const template = readFileSync(path.join('dist', `${templateUrl}.html`), 'utf-8'); + urls.filter(url => url !== templateUrl).forEach((url) => { + console.log('Render by template: ', url); + const article = JSON.parse(readFileSync(`./src/generated/docs/${url}.json`, 'utf-8')); + const content = template + .replace(/
.*<\/article>/, article.contents) + .replace(/.*<\/title>/, `${article.title} - Angular 官方文档`); + const filename = path.join('dist', `${url}.html`); + mkdirp.sync(path.dirname(filename)); + writeFileSync(filename, content, 'utf-8'); + }); +} + +describe('Prerender', function () { + it('prerender', (done) => { + const apiUrls = getApiUrls(); + const guideUrls = getGuideUrls().filter(url => url.match(/^(guide|tutorial|cli)/)); + const otherUrls = getGuideUrls().filter(url => !url.match(/^(guide|tutorial|cli)/)); + const urls = [guideUrls[0], apiUrls[0], otherUrls[0], 'index.html']; + const tasks = urls.map(url => prerender(url)); + concat(...tasks).subscribe(undefined, undefined, () => { + console.log('Start render by template'); + renderUrlsByTemplate(apiUrls, apiUrls[0]); + renderUrlsByTemplate(guideUrls, guideUrls[0]); + renderUrlsByTemplate(otherUrls, otherUrls[0]); + console.log('All rendered'); + done(); + }); + }); +}); diff --git a/aio/tests/e2e/protractor.conf.js b/aio/tests/e2e/protractor.conf.js index 2fbf107c8f..39b7413c6a 100644 --- a/aio/tests/e2e/protractor.conf.js +++ b/aio/tests/e2e/protractor.conf.js @@ -3,8 +3,9 @@ const { SpecReporter } = require('jasmine-spec-reporter'); +const MaxInt32 = Math.pow(2, 31) - 1; exports.config = { - allScriptsTimeout: 11000, + allScriptsTimeout: MaxInt32, specs: [ './*.e2e-spec.ts' ], @@ -12,7 +13,6 @@ exports.config = { browserName: 'chrome', // For Travis chromeOptions: { - binary: process.env.CHROME_BIN, args: ['--no-sandbox'] } }, @@ -21,7 +21,7 @@ exports.config = { framework: 'jasmine', jasmineNodeOpts: { showColors: true, - defaultTimeoutInterval: 30000, + defaultTimeoutInterval: MaxInt32, print: function() {} }, beforeLaunch: function() { diff --git a/aio/tools/translator/assets/aio-shell-index.html b/aio/tools/translator/assets/aio-shell-index.html deleted file mode 100644 index 59e9737114..0000000000 --- a/aio/tools/translator/assets/aio-shell-index.html +++ /dev/null @@ -1,12 +0,0 @@ -<aio-shell ng-version="6.0.0" - class="mode-stable sidenav-open page-guide-template-syntax folder-guide view-SideNav aio-notification-hide "> - <ul role="navigation"><!----> - <li class="ng-star-inserted"><a class="nav-link" href="/features" title="特性">特性</a></li> - <li class="ng-star-inserted"><a class="nav-link" href="/docs" title="文档">文档</a></li> - <li class="ng-star-inserted"><a class="nav-link" href="/resources" title="资源">资源</a></li> - <li class="ng-star-inserted"><a class="nav-link" href="/events" title="会议">会议</a></li> - <li class="ng-star-inserted"><a class="nav-link" href="https://blog.angular.io/" title="博客">博客</a></li> - <li class="ng-star-inserted"><a class="nav-link" href="/translations/cn/home" title="关于中文版">关于中文版</a></li> - <li class="ng-star-inserted"><a class="nav-link" href="https://ng-china.org" title="****2018 ngChina @ 杭州!****">****2018 ngChina @ 杭州!****</a></li> - </ul> -</aio-shell> diff --git a/aio/tools/translator/assets/aio-shell-template.html b/aio/tools/translator/assets/aio-shell-template.html deleted file mode 100644 index d3950e4ce9..0000000000 --- a/aio/tools/translator/assets/aio-shell-template.html +++ /dev/null @@ -1,3 +0,0 @@ -<aio-shell ng-version="7.0.0-rc.1" class="mode-stable sidenav-open page-docs folder-docs view-TopBar view-SideNav aio-notification-hide"><div id="top-of-page"></div><!----><mat-toolbar class="app-toolbar no-print mat-toolbar mat-primary mat-toolbar-multiple-rows" color="primary"><mat-toolbar-row class="notification-container mat-toolbar-row"><aio-notification expirationdate="2018-07-01" notificationid="angular-v6-announcement" class="ng-tns-c2-0 ng-trigger ng-trigger-hideAnimation" style="height: 0px;"><span class="content"><a href="https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4"><mat-icon aria-label="Announcement" class="icon mat-icon" role="img" svgicon="insert_comment" aria-hidden="true"><svg fill="#ffffff" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></mat-icon><span class="message">Version 6 of Angular Now Available!</span><span class="action-button">Learn More</span></a></span><button class="close-button mat-icon-button" aria-label="Close" mat-icon-button=""><span class="mat-button-wrapper"><mat-icon aria-label="Dismiss notification" class="mat-icon ng-tns-c2-0" role="img" svgicon="close" aria-hidden="true"><svg fill="#ffffff" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></mat-icon></span><div class="mat-button-ripple mat-ripple mat-button-ripple-round" matripple=""></div><div class="mat-button-focus-overlay"></div></button></aio-notification></mat-toolbar-row><mat-toolbar-row class="mat-toolbar-row"><button class="hamburger mat-button" mat-button="" title="Docs menu"><span class="mat-button-wrapper"><mat-icon class="mat-icon" role="img" svgicon="menu" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></svg></mat-icon></span><div class="mat-button-ripple mat-ripple" matripple=""></div><div class="mat-button-focus-overlay"></div></button><a class="nav-link home" href="/"><!----><img alt="Home" height="40" src="assets/images/logos/angular/logo-nav@2x.png" title="Home" width="150" class="ng-star-inserted"><!----></a><!----><aio-top-menu class="ng-star-inserted"><ul role="navigation"><!----><li class="ng-star-inserted"><a class="nav-link" href="features" title="特性">特性</a></li><li class="ng-star-inserted"><a class="nav-link" href="docs" title="文档">文档</a></li><li class="ng-star-inserted"><a class="nav-link" href="resources" title="资源">资源</a></li><li class="ng-star-inserted"><a class="nav-link" href="events" title="会议">会议</a></li><li class="ng-star-inserted"><a class="nav-link" href="https://blog.angular.io/" title="博客">博客</a></li><li class="ng-star-inserted"><a class="nav-link" href="translations/cn/home" title="关于中文版">关于中文版</a></li></ul></aio-top-menu><aio-search-box class="search-container"><input aria-label="search" placeholder="搜索" type="search"></aio-search-box><div class="toolbar-external-icons-container"><a href="https://twitter.com/angular" title="Twitter"><img src="assets/images/logos/twitter-icon.svg"></a><a href="https://github.com/angular/angular" title="GitHub"><img src="assets/images/logos/github-icon.svg"></a></div></mat-toolbar-row></mat-toolbar><!----><mat-sidenav-container class="sidenav-container mat-drawer-container mat-sidenav-container mat-drawer-transition" role="main"><!----><div class="mat-drawer-backdrop ng-star-inserted"></div><div class="cdk-visually-hidden cdk-focus-trap-anchor"></div><mat-sidenav class="sidenav mat-drawer mat-sidenav ng-tns-c6-1 ng-trigger ng-trigger-transform mat-drawer-side ng-star-inserted" tabindex="-1" style="transform: none; visibility: visible;"><div class="mat-drawer-inner-container"><!----><aio-nav-menu><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position: relative" href="guide/quickstart" title="对 Angular 和 Angular CLI 基础知识的简短介绍" target="_self"> 快速上手 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="此《英雄指南》教程会带你用 TypeScript 一步步创建一个 Angular 应用。" aria-pressed="false"> 教程 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial" title="《英雄指南》教程简介" target="_self"> 简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial/toh-pt0" title="创建应用的外壳" target="_self"> 应用的“外壳” <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial/toh-pt1" title="第一部分:构建一个简单的英雄编辑器" target="_self"> 1. 英雄编辑器 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial/toh-pt2" title="第二部分:构建一个主从结构的页面,用于展现英雄列表。" target="_self"> 2. 显示英雄列表 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial/toh-pt3" title="第三部分:把主从结构的页面重构成多个组件。" target="_self"> 3. 主从组件 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial/toh-pt4" title="第四部分:创建一个可复用的服务来管理英雄数据。" target="_self"> 4. 服务 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial/toh-pt5" title="第五部分:添加 Angular 路由器,并且学习在视图之间导航。" target="_self"> 5. 路由 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="tutorial/toh-pt6" title="第六部分:通过 HTTP 来获取并保存英雄数据。" target="_self"> 6. HTTP <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="学习 Angular 的核心知识" aria-pressed="false"> 核心知识 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 应用的基本构造块。" aria-pressed="false"> 架构 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/architecture" title="Angular 应用的基本构造块" target="_self"> 架构概览 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/architecture-modules" title="关于模块。" target="_self"> 模块(NgModule)简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/architecture-components" title="关于组件、模板和视图。" target="_self"> 组件简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/architecture-services" title="关于服务与依赖注入。" target="_self"> 服务与 DI 简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/architecture-next-steps" title="学完基础知识之后……" target="_self"> 后续步骤 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="使用数据绑定构建动态视图" aria-pressed="false"> 组件与模板 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/displaying-data" title="属性绑定可以帮助应用把数据显示在界面上" target="_self"> 显示数据 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/template-syntax" title="学习如何写模板,以便借助数据绑定机制显示数据并响应事件。" target="_self"> 模板语法 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/user-input" title="用户输入会触发 DOM 事件。Angular 会通过事件绑定来监听那些事件,并把修改后的值传回应用的组件和模型中。" target="_self"> 用户输入 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/lifecycle-hooks" title="Angular 调用指令和组件的生命周期钩子函数,包括它的创建、变更和销毁时。" target="_self"> 生命周期钩子 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/component-interaction" title="在不同的指令和组件之间共享信息" target="_self"> 组件交互 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/component-styles" title="添加专属于某个组件的样式" target="_self"> 组件样式 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/elements" title="把组件转换成自定义元素。" target="_self"> Angular 自定义元素 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/dynamic-component-loader" title="动态加载组件" target="_self"> 动态组件 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/attribute-directives" title="属性型指令把行为添加到现有元素上。" target="_self"> 属性型指令 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/structural-directives" title="结构型指令可以操纵页面的布局" target="_self"> 结构型指令 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/pipes" title="管道可以在模板中转换显示的内容。" target="_self"> 管道 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 的表单" aria-pressed="false"> 表单 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/forms-overview" title="表单可以创建集中、高效、引人注目的输入体验。Angular 表单可以协调一组数据绑定控件,跟踪变更,验证输入,并表达错误信息。" target="_self"> 简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/reactive-forms" title="使用 FormBuilder、表单组和表单数组创建响应式表单。" target="_self"> 响应式表单 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/forms" title="使用指令和 Angular 模板语法创建模板驱动表单。" target="_self"> 模板驱动表单 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/form-validation" title="验证用户的表单输入" target="_self"> 表单验证 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/dynamic-form" title="使用 FormGroup 渲染动态表单。" target="_self"> 动态表单 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Observable 与 RxJS" aria-pressed="false"> Observable 与 RxJS <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/observables" title="" target="_self"> 可观察对象(Observable) <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/rx-library" title="" target="_self"> RxJS 库 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/observables-in-angular" title="" target="_self"> Angular 中的可观察对象 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/practical-observable-usage" title="" target="_self"> 用法实战 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/comparing-observables" title="" target="_self"> 与其它技术的比较 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/bootstrapping" title="在应用的根模块(AppModule)中告诉 Angular 如何构造并引导引用。" target="_self"> 引导启动 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 中的模块" aria-pressed="false"> Angular 模块 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/ngmodules" title="使用 NgModule 让你的应用更高效" target="_self"> NgModule 简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/ngmodule-vs-jsmodule" title="JavaScript 模块和 NgModule 之间的差异" target="_self"> JS 模块 vs NgModule <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/frequent-ngmodules" title="介绍最常用的 Angular 模块" target="_self"> 常用模块 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/module-types" title="介绍特性模块的几种类型" target="_self"> 特性模块的分类 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/entry-components" title="关于 Angular 中入口组件的一切" target="_self"> 入口组件 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/feature-modules" title="创建特性模块,以组织你的代码" target="_self"> 特性模块 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/providers" title="服务提供商与 Angular 模块" target="_self"> 服务提供商 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/singleton-services" title="创建单例应用" target="_self"> 单例应用 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/lazy-loading-ngmodules" title="惰性加载模块,以提高应用的性能" target="_self"> 惰性加载的特性模块 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/sharing-ngmodules" title="共享 Angular 模块让你的应用现代化。" target="_self"> 共享 Angular 模块 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/ngmodule-api" title="理解 NgModule 的那些细节。" target="_self"> NgModule API <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/ngmodule-faq" title="回答关于 NgModules 的常见问题。" target="_self"> NgModule 常见问题 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="依赖注入:创建并注入各种服务。" aria-pressed="false"> 依赖注入 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/dependency-injection" title="Angular 的依赖注入系统能够为 Angular 创建的类创建并交付它们所依赖的服务。" target="_self"> Angular 依赖注入 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/hierarchical-dependency-injection" title="与组件树平行的注入器树,并支持嵌套的依赖。" target="_self"> 多级注入器 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/dependency-injection-providers" title="各种提供商类型的更多知识。" target="_self"> DI 提供商 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/dependency-injection-in-action" title="依赖注入的使用技巧" target="_self"> DI 实战 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/dependency-injection-navtree" title="使用注入器树来查找父组件。" target="_self"> 在组件树中导航 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/http" title="通过 HTTP 协议与远程服务器对话。" target="_self"> HttpClient <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/router" title="揭示如何通过 Angular 路由进行基本的屏幕导航。" target="_self"> 路由与导航 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 动画系统指南" aria-pressed="false"> 动画 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/animations" title="Angular 动画的基础技术。" target="_self"> 简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/transition-and-triggers" title="转场与触发器的高级技术。" target="_self"> 转场与触发器 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/complex-animation-sequences" title="复杂的 Angular 动画序列。" target="_self"> 复杂序列 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/reusable-animations" title="创建可复用的动画。" target="_self"> 可复用动画 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/route-animations" title="为路由提供转场动画。" target="_self"> 路由转场动画 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="把 Angular 用到你的实际工作中的一些技巧" aria-pressed="false"> 其它技术 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/security" title="Angular 应用开发中的安全技术。" target="_self"> 安全 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/i18n" title="把应用模板中的文本翻译成多种语言。" target="_self"> 国际化 (i18n) <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular Service Worker: 控制应用资源的缓存。" aria-pressed="false"> Service Worker 与 PWA <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/service-worker-intro" title="Angular 对 Service Worker 的实现提升了慢速或不稳定的网络连接下的用户体验。" target="_self"> 简介 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/service-worker-getting-started" title="在 CLI 项目中启用 Service Worker,并在浏览器中查看效果。" target="_self"> 快速起步 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/service-worker-communications" title="那些能让你和 Angular 的 Service Worker 通讯的服务类。" target="_self"> 与 Service Worker 通讯 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/service-worker-devops" title="使用 Service Worker 运行应用、管理应用更新、调试以及杀掉正在运行的应用。" target="_self"> 生产环境下的 Service Worker <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/service-worker-config" title="配置 Service Worker 的缓存行为。" target="_self"> Service Worker 配置 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/universal" title="使用 Angular Universal 在服务端渲染 HTML。" target="_self"> 服务端渲染 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="把 AngularJS 应用增量式的升级到 Angular。" aria-pressed="false"> 从 AngularJS 升级 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/upgrade" title="把 AngularJS 应用增量式的升级到 Angular。" target="_self"> 升级步骤 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/upgrade-performance" title="用更灵活的方式把 AngularJS 升级到 Angular" target="_self"> 提高升级效率 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/ajs-quick-reference" title="学习如何把 AngularJS 的概念映射到 Angular 中。" target="_self"> AngularJS 与 Angular 的概念对照 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="关于环境搭建、构建、测试、部署环境与工具的信息。" aria-pressed="false"> 环境搭建与部署 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/file-structure" title="Angular 工作空间在文件系统中是怎样的。" target="_self"> 项目文件结构 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/npm-packages" title="会默认安装到项目中的 npm 包的说明。" target="_self"> npm 包 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/typescript-configuration" title="给 Angular 开发者的 TypeScript 配置。" target="_self"> TypeScript 配置 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/aot-compiler" title="了解为何以及如何使用预先(AOT)编译器。" target="_self"> 预先(AOT)编译 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/build" title="构建应用及为应用启动开发服务器。" target="_self"> 构建与启动开发服务器 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/testing" title="测试 Angular 应用的技巧与实践。" target="_self"> 测试 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/deployment" title="了解如何部署 Angular 应用。" target="_self"> 发布 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/browser-support" title="浏览器支持与腻子脚本指南。" target="_self"> 浏览器支持 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="整合开发环境和工具。" aria-pressed="false"> 开发工具集成 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/language-service" title="使用 Angular 语言服务加速开发。" target="_self"> 语言服务 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="Angular 的版本发布实践、更新与升级。" aria-pressed="false"> 发布信息 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/releases" title="Angular 的版本、发布、支持、弃用策略与实践。" target="_self"> Angular 发布策略与实践 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/updating" title="如何把 Angular 应用和库升级到最新版本。" target="_self"> 保持最新 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="逐渐把 AngularJS 应用升级成 Angular。" aria-pressed="false"> 从 AngularJS 升级 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/upgrade" title="逐渐把 AngularJS 应用升级成 Angular。" target="_self"> 升级指南 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position: relative" href="guide/ajs-quick-reference" title="学习如何把 AngularJS 的概念映射到 Angular 中。" target="_self"> AngularJS 与 Angular 的概念对照 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><!----><div class="ng-star-inserted"><!----><!----><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="Angular 语法、编码、术语汇总。" aria-pressed="false"> 快捷手册 <mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><!----><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/cheatsheet" title="关于 Angular 常用编码技术的快速指南。" target="_self"> 速查表 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/styleguide" title="写出 Angular 风格的程序" target="_self"> 风格指南 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position: relative" href="guide/glossary" title="Angular 中最重要的词汇的简要定义。" target="_self"> 词汇表 <!----></a><!----></div><!----></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position: relative" href="api" title="关于 Angular 中类和值的详细信息。" target="_self"> API 参考手册 <!----></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><!----><div class="mat-divider ng-star-inserted" style="margin: 4px 20px; border-top: 1px solid lightgray"></div></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position: relative" href="https://github.com/ng-docs/ng-docs.github.io/issues" title="github 上的中文互助问答区" target="_blank"> 互助问答 <!----><mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position: relative" href="https://material.angular.cn" title="Angular Material 组件库的中文文档" target="_blank"> 官方 Material 组件库 <!----><mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a><!----></div><!----></aio-nav-item><aio-nav-item class="ng-star-inserted"><!----><div class="ng-star-inserted"><!----><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position: relative" href="https://ng.ant.design/" title="Ant Design 的 Angular 实现,服务于企业级后台产品。" target="_blank"> ng-zorro 组件库 <!----><mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a><!----></div><!----></aio-nav-item></aio-nav-menu><div class="doc-version"><aio-select><div class="form-select-menu"><button class="form-select-button"><strong></strong><!---->stable (v7.0.0) </button><!----></div></aio-select></div></div></mat-sidenav><div class="cdk-visually-hidden cdk-focus-trap-anchor"></div><!----><mat-sidenav-content cdkscrollable="" class="mat-drawer-content mat-sidenav-content ng-star-inserted" style="margin-left: 260px;"><section class="sidenav-content" role="content" id="docs"></section></mat-sidenav-content></mat-sidenav-container><!----><footer class="no-print"><aio-footer><div class="grid-fluid"><!----><div class="footer-block ng-star-inserted"><h3>资源</h3><ul><!----><li class="ng-star-inserted"><a class="link" href="about" title="Angular 贡献者。">关于</a></li><li class="ng-star-inserted"><a class="link" href="resources" title="网络上的 Angular 工具、培训、博客等">资源列表</a></li><li class="ng-star-inserted"><a class="link" href="presskit" title="我们的联系方式、LOGO 和品牌">宣传资料</a></li><li class="ng-star-inserted"><a class="link" href="https://blog.angular.io/" title="Angular 官方博客">博客</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>帮助</h3><ul><!----><li class="ng-star-inserted"><a class="link" href="https://stackoverflow.com/questions/tagged/angular" title="Stack Overflow: 这里的社区会回答你关于 Angular 的技术问题">Stack Overflow</a></li><li class="ng-star-inserted"><a class="link" href="https://gitter.im/angular/angular" title="和老鸟聊 Angular">Gitter</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/angular/issues" title="在 github 上报告问题和建议。">报告问题</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md" title="让我们彼此尊重">行为规范</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>社区</h3><ul><!----><li class="ng-star-inserted"><a class="link" href="events" title="Post issues and suggestions on github.">活动</a></li><li class="ng-star-inserted"><a class="link" href="http://www.meetup.com/topics/angularjs/" title="参加聚会,向别的开发人员学习">聚会</a></li><li class="ng-star-inserted"><a class="link" href="https://twitter.com/angular" title="Twitter">Twitter</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/angular" title="GitHub">GitHub</a></li><li class="ng-star-inserted"><a class="link" href="contribute" title="向 Angular 做贡献">做贡献</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>多语言</h3><ul><!----><li class="ng-star-inserted"><a class="link" href="https://angular.io/" title="English Version.">English Version</a></li><li class="ng-star-inserted"><a class="link" href="https://angular.jp/" title="日本語版">日本語版</a></li></ul></div></div><p> Powered by Google ©2010-2018. 代码授权方式:<a href="license" title="License text">MIT-style License</a>. 文档授权方式:<a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>. -</p><p> 当前版本:7.0.1-local+sha.3ff4bedd0c. -</p></aio-footer></footer></aio-shell> diff --git a/aio/tools/translator/bin/ssr.ts b/aio/tools/translator/bin/ssr.ts deleted file mode 100644 index c3e32f0926..0000000000 --- a/aio/tools/translator/bin/ssr.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as fs from 'fs'; -import * as mkdirp from 'mkdirp'; -import * as path from 'path'; -import * as klawSync from 'klaw-sync'; -import { minify } from 'html-minifier'; - -const rootElementPattern = /<aio-shell\b[\s\S]*<\/aio-shell>/; - -const indexTemplate = fs.readFileSync('./dist/index.html', 'utf-8'); -const aioShellTemplate = fs.readFileSync(__dirname + '/../assets/aio-shell-template.html'); -const pageTemplate = indexTemplate.replace(rootElementPattern, `${aioShellTemplate}`); - -const minifyOptions = { - collapseWhitespace: true, - ignoreCustomFragments: [/<code>[\s\S]*?<\/code>/], - minifyCSS: true, - minifyJS: true, - removeComments: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true, -}; - -function composePage(url) { - console.log(`pre-rendering ${url}...`); - const { title, contents } = JSON.parse(fs.readFileSync(`./dist/generated/docs/${url}.json`, 'utf-8')); - - if (!contents) { - return; - } - const ssrContent = contents.replace(/href="(?!http)(.{2,}?)"/gi, 'href="/$1"'); - const pageContent = pageTemplate - .replace('<title>Angular Docs', `${title} - Angular 官方文档`) - .replace('
', - `
${ssrContent}
`); - mkdirp.sync(path.dirname(`./dist/${url}`)); - fs.writeFileSync(`./dist/${url}.html`, minify(pageContent, minifyOptions), 'utf-8'); -} - -function buildGuidePages(): void { - const navigation = fs.readFileSync('./content/navigation.json', 'utf-8'); - (navigation.match(/"url": "(.*?)"/g) || []) - .map((entry) => entry.replace(/^"url": "(.*?)".*$/, '$1')) - .filter(url => url.slice(0, 4) !== 'http') - .forEach(url => composePage(url)); -} - -function buildApiPages(): void { - const apiList = klawSync('./dist/generated/docs/api', { nodir: true }) - .map(file => file.path.replace(/^.*generated\/docs(\/.*).json$/, '$1')); - apiList.forEach(url => composePage(url)); - - const links = apiList.map(url => `${url}`).join('\n'); - const apiListContent = fs.readFileSync('./dist/api.html', 'utf-8') - .replace(rootElementPattern, `

API List

${links}
`); - - fs.writeFileSync(`./dist/api.html`, minify(apiListContent, minifyOptions), 'utf-8'); -} - -function buildIndexPage(): void { - const indexTemplate = fs.readFileSync('./dist/index.html', 'utf-8'); - const aioShellIndex = fs.readFileSync(__dirname + '/../assets/aio-shell-index.html'); - const content = indexTemplate.replace(rootElementPattern, `${aioShellIndex}`); - fs.writeFileSync(`./dist/index.html`, minify(content, minifyOptions), 'utf-8'); -} - -buildGuidePages(); -buildApiPages(); -buildIndexPage(); diff --git a/aio/tools/translator/tsconfig.json b/aio/tools/translator/tsconfig.json new file mode 100644 index 0000000000..f5b6194e92 --- /dev/null +++ b/aio/tools/translator/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "strict": true, + "noImplicitAny": false, + "outDir": "dist", + "baseUrl": "src", + "module": "commonjs", + "sourceMap": true, + "declaration": true, + "moduleResolution": "node", + "noUnusedLocals": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "types": [ + "node" + ], + "lib": [ + "es2017", + "dom" + ] + }, + "exclude": [ + "**/*.spec.ts" + ] +}