Slide 1

Slide 1 text

How We Build ng-my.org

Slide 2

Slide 2 text

Don’t fall asleep yet!

Slide 3

Slide 3 text

_Jenning Ho_ _Jecelyn Yeen_ _Adrian Yeong_

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

➔ Background ➔ Search Engine Optimization (SEO) ➔ Collaboration ➔ Performance ➔ Techniques

Slide 6

Slide 6 text

6 Background

Slide 7

Slide 7 text

Free stuff!! I love that!

Slide 8

Slide 8 text

Target: Cost $0

Slide 9

Slide 9 text

Single Page App (SPA) Firebase Hosting (Free) NoDB (JSON) Start with Blank (Angular CLI)

Slide 10

Slide 10 text

ng new --createApplication false ng generate application site2019 Create blank workspace Generate an application under the project

Slide 11

Slide 11 text

ng add @angular/fire ng deploy site2019 Add deploy feature Build & deploy

Slide 12

Slide 12 text

Rapid Prototyping with (free)

Slide 13

Slide 13 text

13 Search Engine Optimization

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

July 06-07 | NG-MY 2019 Title tag & Meta tags are essential for SEO

Slide 17

Slide 17 text

export class PageService { } Utilize the built-in Title & Meta Service Set page title Update meta tag Inject title & meta services 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 }); ... }

Slide 18

Slide 18 text

Dev (No crawling) Prod (Allow crawling) Robots.txt - To crawl or not to crawl? robots.txt robots.prod.txt

Slide 19

Slide 19 text

... "architect": { "build": { "assets": [ ..., "projects/site2019/src/robots.txt" ] ... } } angular.json Include robots.txt as output assets

Slide 20

Slide 20 text

"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

Slide 21

Slide 21 text

SPA & SEO Can we have both?

Slide 22

Slide 22 text

Why is it not working!!!

Slide 23

Slide 23 text

SEO Problem in SPA </head> ... </html> Server sends HTML to Client (No meta tags) <html> <head> <title>home</title> <meta ...> <script src="bundle.js"> </head> ... </html> Client executes JS and add title & meta tags (too late!) Most crawlers only understand this. :(

Slide 24

Slide 24 text

Solution Prerendering during build time

Slide 25

Slide 25 text

Brief: Angular CLI Build Process ng build --prod Generate & Optimize files - js, css, etc Generate index.html Webpack Index transform We can extend & customize these!

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Puppeteer - Programmable Chrome Browser const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); })(); 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: 'networkidle2'}); const html = await page.content();

Slide 29

Slide 29 text

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!

Slide 30

Slide 30 text

Extend Angular CLI Webpack 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

Slide 31

Slide 31 text

const Prerenderer = require('prerender-xs'); const routes = ['/food', '/team', '/speakers', ...]; module.exports = async (targetOptions, indexHtml) => { const data = await Prerenderer.render({ staticDir: 'dist/site2019', routes, indexHtml }); return data.find(x => x.route === '/').html; } index-html-transform.js Update angular.json to use this custom file

Slide 32

Slide 32 text

Sitemap is still necessary https://support.google.com/webmasters/answer/156184?hl=en

Slide 33

Slide 33 text

Solution: 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

Slide 34

Slide 34 text

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)

Slide 35

Slide 35 text

const CreateFileWebpack = require('create-file-webpack'); const routes = ['/food', '/team', '/speakers', ...] module.exports = { plugins: [ new CreateFileWebpack({ path: 'dist/site2019', fileName: 'sitemap.xml', content: generateSitemap(routes) })] } extra-webpack.config.js Our custom method to generate sitemap https://github.com/chybie/ng-my/blob/master/ projects/site2019/extra-webpack.config.js

Slide 36

Slide 36 text

Monitor Site SEO

Slide 37

Slide 37 text

37 Collaboration

Slide 38

Slide 38 text

➔ Work together & independently ➔ Mandatory Code Linting ➔ Continuous Integration & Deployment (CI/CD)

Slide 39

Slide 39 text

Flow Feature Branch Local Commit Trigger Linting During development... Automate with Husky

Slide 40

Slide 40 text

Husky - Git Hook Made Easy npm install -D husky // package.json { "husky": { "hooks": { "pre-commit": "ng lint" } } }

Slide 41

Slide 41 text

Flow Create PR PR Approved Merge to Master Branch Trigger Build Trigger Deployment Complete feature development Automate with Github actions (beta)

Slide 42

Slide 42 text

.github/workflows/ main.yml name: CI on: push: branches: - master jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: 12.8 - run: npm install - run: npm run lint - run: npm run deploy env: FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} Only run in master Lint, Build & Deploy to Firebase

Slide 43

Slide 43 text

43 Performance

Slide 44

Slide 44 text

Feeling of browsing slow website...

Slide 45

Slide 45 text

Pleasant Browsing Experience

Slide 46

Slide 46 text

Page Speed matters to SEO Page Ranking

Slide 47

Slide 47 text

http://bit.ly/mobile-page-weight Page Weight (Mobile)

Slide 48

Slide 48 text

Measure Page Performance Lighthouse | PageSpeed Insights | Web Page Test

Slide 49

Slide 49 text

➔ Images ➔ Fonts ➔ Angular & JavaScript ➔ CSS ➔ Effortless

Slide 50

Slide 50 text

Images

Slide 51

Slide 51 text

5MB Images a page Data Plan

Slide 52

Slide 52 text

SVG

Slide 53

Slide 53 text

32kb

Slide 54

Slide 54 text

NPM: https://www.npmjs.com/package/svgo UI: https://jakearchibald.github.io/svgomg/ Optimize SVG with SVGOMG 32kb -> 2kb (867B gzip)

Slide 55

Slide 55 text

Like PNG but smaller

Slide 56

Slide 56 text

WebP Images are 25-35% smaller than equivalent JPEG 0r PNG.

Slide 57

Slide 57 text

78% Global User WebP Has arrived Based on data from caniuse Source: bit.ly/webp-support Supported Supported Supported

Slide 58

Slide 58 text

.png - 119 kb .webp - 28 kb .png - 278 kb .webp - 30 kb

Slide 59

Slide 59 text

Serve WebP and support browsers Image container If browser supports WebP Else PNG it is

Slide 60

Slide 60 text

Image Compression

Slide 61

Slide 61 text

Lossy Image For most images, 80-85% quality will reduce file size by 30-40% with minimal effect on image quality

Slide 62

Slide 62 text

495 kb 180 kb (80% quality) Lossy Image

Slide 63

Slide 63 text

Responsive Image

Slide 64

Slide 64 text

Image in desktop & tablet can be ~2-4x larger than mobile 28 kb 12 kb

Slide 65

Slide 65 text

Serve different image sizes Small screen and if browser supports WebP https://developer.mozilla.org/en-US/docs/Learn/HTML/ Multimedia_and_embedding/Responsive_images

Slide 66

Slide 66 text

squoosh.app resize, compress, format Or automate with these npm packages: imagemin, sharp, jimp

Slide 67

Slide 67 text

Native lazy loading Defer fetching offscreen images / iframes until a user scrolls near them. Load when scrolling down

Slide 68

Slide 68 text

Performant Images ➔ Is Lazy ➔ Appropriate format ➔ Appropriate compression ➔ Appropriate display size

Slide 69

Slide 69 text

Fonts

Slide 70

Slide 70 text

Display Font Immediately By default, if a font is not loaded, The browser will hide text for up to:

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

fonts.googleapis.com/css?family=Source+Sans+Pro &display=swap

Slide 73

Slide 73 text

fonts.googleapis.com/css?family=Source+Sans+Pro :400,600,900 Limit font weights https://www.smashingmagazine.com/2019/06/o ptimizing-google-fonts-performance/

Slide 74

Slide 74 text

Font import Eliminate render blocking above the fold ... @import "//fonts.googleapis...";

Slide 75

Slide 75 text

Icons

Slide 76

Slide 76 text

If we are using only 10 icons, why should we load 100 other icons?

Slide 77

Slide 77 text

icomoon.io 13 icons - 3.7kb

Slide 78

Slide 78 text

Angular & JavaScript

Slide 79

Slide 79 text

If only we could snap and destroy half the files...

Slide 80

Slide 80 text

Lazy Loading Traditional SPA - big-bundle.js (60kb) Split & Lazy Load - route-speakers.js (20kb) - route-food.js (20kb) - route-schedule.js (20kb) - ….

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

OnPush improves performance by minimizing change detection

Slide 83

Slide 83 text

Change detection @Component({ selector: 'my-root', ... changeDetection: ChangeDetectionStrategy.OnPush }) export class AppComponent { } Change detection perform only when the component have received different inputs

Slide 84

Slide 84 text

Defer JavaScript <script defer> HTML parsing HTML parsing paused Script download Script execution

Slide 85

Slide 85 text

Defer Google Tag Manager ... Defer it Reduction of domComplete time

Slide 86

Slide 86 text

CSS

Slide 87

Slide 87 text

Minimize global css Move to component css

Slide 88

Slide 88 text

Effortless

Slide 89

Slide 89 text

Cache Static Assets

Slide 90

Slide 90 text

{ "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

Slide 91

Slide 91 text

Performance Optimization we get for FREE! ➔ Minification & Uglify (CLI) ➔ Ahead of Time Compilation (CLI) ➔ GZIP (Firebase) ➔ Differential Loading (CLI) ng build --prod

Slide 92

Slide 92 text

Smaller apps with Differential Loading Evergreen Legacy Minimal polyfill Modern syntax Full polyfill ES5 syntax

Slide 93

Slide 93 text

-Guess the result!-

Slide 94

Slide 94 text

651 kb 469 kb Result

Slide 95

Slide 95 text

95 Techniques

Slide 96

Slide 96 text

➔ CMS Markdown ➔ Utilize ng-template ➔ Embed Google Forms ➔ CSS Variables ➔ CSS Grid

Slide 97

Slide 97 text

CMS Markdown

Slide 98

Slide 98 text

No content

Slide 99

Slide 99 text

npm install marked Markdown Parser

Slide 100

Slide 100 text

this.selectedPost.content = marked.parse(result); const result = await fetch(`post-01.txt`) .then(res => res.text()); ![picture](banner.jpg) Let’s make it happen LAH. Are you coming? 5 (+1) reasons why you should! ### 1. Enjoy incredible talks NG-MY provides you... post-page.component.ts post-01.txt

Slide 101

Slide 101 text

Utilize ng-template

Slide 102

Slide 102 text

No content

Slide 103

Slide 103 text

Repeated Day Schedule

Slide 104

Slide 104 text

Should we extract it to a new child component?

Slide 105

Slide 105 text

Not really, because it is only used within this component. We can use ng-template.

Slide 106

Slide 106 text

{{ title }}

...
Input variables Template reference Create ng-template (schedule-page)

Slide 107

Slide 107 text

Template reference Assign variable values Use ng-template (schedule-page) … Reuse, pass in day 2 schedule

Slide 108

Slide 108 text

Embed Google Forms

Slide 109

Slide 109 text

iframe (responsive!)

Slide 110

Slide 110 text

const form = ...; // from json const urlPattern = 'https://docs.google.com/forms/d/e/{id} /viewform?embedded=true'; form.url = this.sanitizer.bypassSecurityTrustResou rceUrl(urlPattern.replace('{id}', form.id)); { "call-for-sponsors": { "name": "Call for Sponsors", "desc": "Interested to...", "id": "google-form-id", "height": 1633 }, ... } form-page.component.ts forms.json Assign to iframe

Slide 111

Slide 111 text

CSS Variables

Slide 112

Slide 112 text

Color fades in Color fades out Shadow increases

Slide 113

Slide 113 text

You don’t even notice, DON’T YOU??!

Slide 114

Slide 114 text

:root { --hero-scroll: 0; } global.css .menu { opacity: var(--hero-scroll); } menu.component.css .bg:before { opacity: calc(1 - 1 * var(--hero-scroll)); } .bg { opacity: calc(0.3 + 0.4 * var(--hero-scroll)); } home.component.css

Slide 115

Slide 115 text

@HostListener('window:scroll', ['$event']) onScroll(e) { const opacityValue = ...; // calculation document.documentElement.style.setProperty( '--hero-scroll', `${opacityValue}` ); } menu.component.ts Update CSS Variables Listen to scroll

Slide 116

Slide 116 text

Can I Use 91.11% IE Edge Firefox Chrome Safari 11 18 67 75 12.1

Slide 117

Slide 117 text

CSS Grid

Slide 118

Slide 118 text

CSS Grid Collage

Slide 119

Slide 119 text

Define grid template 5 columns, 12 rows size = 1 col size = 2 cols size = 2 rows, 2 cols

Slide 120

Slide 120 text

.container { display: grid; grid-template-columns: repeat(5,200px); grid-template-rows: repeat(12,200px); grid-auto-flow: row dense; } 5 columns 12 rows Auto placement algorithm No. of rows and columns different based on screen size (media queries)

Slide 121

Slide 121 text

How auto placement algorithm works? dense = fill the holes https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow

Slide 122

Slide 122 text

.photo:nth-child(1) { grid-row: span 2; grid-column: span 2; } Define how many rows & columns each photo take/

Slide 123

Slide 123 text

These are CSS Grid as well!

Slide 124

Slide 124 text

Can I Use 92.03% IE Edge Firefox Chrome Safari 11 18 67 75 12.1

Slide 125

Slide 125 text

No content

Slide 126

Slide 126 text

No content

Slide 127

Slide 127 text

Thank you! Follow @ngmykia (Instagram, Facebook) github: https://github.com/chybie/ng-my slides: https://bit.ly/ng-my-site video: https://youtu.be/6l779_V4LV8