Upgrade to Pro — share decks privately, control downloads, hide ads and more …

如何打造高性能且SEO友好的单页应用(SPA)

Jecelyn Yeen
November 24, 2019

 如何打造高性能且SEO友好的单页应用(SPA)

Jecelyn Yeen

November 24, 2019
Tweet

More Decks by Jecelyn Yeen

Other Decks in Programming

Transcript

  1. <title>July 06-07 | NG-MY 2019</title> <meta name="description" content="NG-MY 2019..."> <meta

    property="og:url" content="https://2019.ng-my.org/" /> <meta property="og:description" content="NG-MY 2019..."> <meta property="og:type" content="website" /> <meta property="og:title" content="..." /> <meta property="og:image" content="img.png" /> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:image" content="img.png"> Title tag & Meta tags are essential for SEO
  2. constructor(private title: Title, private meta: Meta) {} setPageMeta(title: string, metaDesc:

    string, metaImg: string) { this.title.setTitle(title); this.meta.updateTag({ property: 'og:title', content: title }); this.meta.updateTag({ name: 'description', content: metaDesc }); this.meta.updateTag({ name: 'twitter:image', content: metaImg }); ... } export class PageService { } Utilize the built-in Title & Meta Service Set page title Update meta tag Inject title & meta services
  3. ... "architect": { "build": { "assets": [ ..., "projects/site2019/src/robots.txt" ]

    ... } } angular.json Include robots.txt as output assets
  4. "configurations": { "production": { "fileReplacements": [ ... { "replace": "projects/site2019/src/robots.txt",

    "with": "projects/site2019/src/robots.prod.txt" } ] } } Replace file in angular.json Environment name Replace when env = production ng build -c production, or ng build --prod
  5. SEO Problem in SPA <html> <head> <script src="bundle.js"> </head> ...

    </html> 伺服器器 sends HTML to 浏览器器 (No meta tags) <html> <head> <title>home</title> <meta ...> <script src="bundle.js"> </head> ... </html> 浏览器器 executes JS and add title & meta tags (too late!) Most crawlers only understand this. :(
  6. Brief: Angular Application Build Process (CLI) ng build --prod Generate

    & Optimize files - js, css, etc Generate index.html Webpack Index transform We can extend & customize these!
  7. Solution: Prerendering during build time ng build --prod Generate &

    Optimize files - js, css, etc Open browser Browse & Save each route as .html Deploy all files to server Generate static html pages = Prerendering Generate index.html
  8. Solution: Prerendering during build time ng build --prod Generate &

    Optimize files - js, css, etc Deploy all files to server Automate Prerendering using Puppeteer Generate index.html Program Puppeteer Browse & Save each route as .html
  9. Puppeteer 前端神器器 - Programmable Chrome Browser const puppeteer = require('puppeteer');

    (async () => { })(); const fs = require('fs').promises; await fs.writeFile('food.html', html, 'utf-8'); await browser.close(); await page.goto('https://ng-my.org/food', { waitUntil: ‘networkidle0'}); const html = await page.content(); const browser = await puppeteer.launch(); const page = await browser.newPage();
  10. Solution: Prerendering during build time ng build --prod Generate &

    Optimize files - js, css, etc Program Puppeteer Browse & Save each route as .html Deploy all files to server Generate index.html Extend Angular CLI to customize this step Find an even easier library to help us!
  11. Extend Angular CLI to handle Prerendering npm install -D @angular-builders/custom-webpack

    npm install -D prerender-xs Extend Angular CLI Library to prerender with Puppeteer https://github.com/just-jeb/angular-builders https://github.com/chybie/prerender-xs
  12. const Prerenderer = require('prerender-xs'); const routes = ['/food', '/team', '/speakers',

    ...]; index-html-transform.js Update angular.json to use this custom file module.exports = async (targetOptions, indexHtml) => { } const data = await Prerenderer.render({ staticDir: 'dist/site2019', routes, indexHtml }); return data.find(x => x.route === '/').html;
  13. Generate Sitemap Automatically ng build --prod Generate & Optimize files

    - js, css, etc Deploy all files to server Generate sitemap.xml Extend Angular CLI Webpack to do so Generate index.html
  14. Extend Angular CLI for Sitemap Generation npm install -D create-file-webpack

    https://github.com/Appius/create-file-webpack Webpack plugin to create file (any format)
  15. module.exports = { plugins: [ new CreateFileWebpack({ path: 'dist/site2019', fileName:

    'sitemap.xml', content: generateSitemap(routes) })] } const CreateFileWebpack = require('create-file-webpack'); const routes = ['/food', '/team', '/speakers', ...] extra-webpack.config.js Our custom method to generate sitemap
  16. @JecelynYeen ➔ 压缩丑化 Uglify (CLI) ➔ 事前编译 AOT (CLI) ➔

    GZIP (Firebase) ➔ 差异化加载Differential Loading (CLI) Performance Optimization we get for FREE! ng build --prod
  17. { "hosting": { "headers": [{ "source": "**/*.@(js|css|jpg|jpeg|gif|png|webp|svg)", "headers": [{ "key":

    "Cache-Control", "value": "max-age=31536000" }] }], ... }, } Efficient cache (firebase.json) js, css, images longer cache
  18. SVG

  19. 78% Global User WebP has Arrived Based on data from

    caniuse Source: bit.ly/webp-support Supported Supported Supported
  20. .png - 119 kb .webp - 28 kb .png -

    278 kb .webp - 30 kb
  21. Serve WebP and support browsers <picture> <source srcset="拉茶.webp" type="image/webp"> <img

    src=“拉茶.png"> </picture> Image container If browser supports WebP Else PNG it is
  22. 图⽚片压缩 Lossy Image For most images, 80-85% quality will reduce

    file size by 30-40% with minimal effect on image quality.
  23. Image in desktop & tablet can be ~2-4x larger than

    mobile 28 kb 12 kb 响应式图⽚片 Responsive Images
  24. Serve different image sizes <picture> <source media="(max-width: 800px)" srcset=“拉茶⼩小.webp" type="image/webp">

    <source srcset="拉茶.webp" type="image/webp"> <img src="拉茶.png" > </picture> Small screen and if browser supports WebP https://developer.mozilla.org/en-US/docs/Learn/ HTML/Multimedia_and_embedding/ Responsive_images
  25. @JecelynYeen Native lazy loading Defer fetching offscreen images / iframes

    until a user scrolls near them. Load when scrolling down
  26. image / iframe lazy load <picture> ... <img src="teh-tarik.png" loading="lazy">

    </picture> <iframe src="https://www.youtube.com/embed/RYUPiv_lRFI" loading="lazy"> </iframe> https://developer.mozilla.org/en-US/docs/Learn/ HTML/Multimedia_and_embedding/ Add lazy loading attribute
  27. 延迟加载 Lazy Loading 旧式 SPA (Eager Load) - big-bundle.js (60kb)

    现代 SPA (Split & Lazy Load) - route-speakers.js (20kb) - route-food.js (20kb) - route-schedule.js (20kb) - ….
  28. Lazy Loading 1-2 pages per module const routes: Routes =

    [ { path: 'speakers', loadChildren: () => import('./speakers/speakers.module') .then(m => m.SpeakersModule) }, ... ]; Latest Angular version 8 syntax
  29. Display Font Immediately By default, if a font is not

    loaded, The browser will hide text for up to:
  30. Flash of Unstyled Text (FOUT) @font-face { font-family: Source Sans

    Pro', sans-serif; src: url('...') format('woff'); font-display: swap; } Display unstyled text until font loaded .5s improvement in “Visual Complete” on 3G