Slide 1

Slide 1 text

Vue.js Performance Tips v-kansai Vue.js/Nuxt.js meetup #11 Masashi Hirano / @shisama

Slide 2

Slide 2 text

ฏ໺ ণ࢜ / Masashi Hirano αΠϘ΢ζגࣜձࣾ ɹɹϑϩϯτΤϯυΤΩεύʔτνʔϜ @shisama_ @shisama Node.js Core Collaborator ؔ੢NodeֶԂOrganizer

Slide 3

Slide 3 text

Agenda • Webϖʔδͷ଎౓ʹ͍ͭͯ • ύϑΥʔϚϯεͷܭଌ • ϖʔδϩʔυͷ଎౓վળ • ϥϯλΠϜͷ଎౓վળ

Slide 4

Slide 4 text

Webϖʔδͷ଎౓ʹΑΔӨڹ • αΠτͷىಈ͕࣌ؒ3ඵҎ্
 ɹ὎53%ͷϢʔβ͕அ೦ͯ͠͠·͏ • ىಈ͕࣌ؒ1ඵ஗͘ͳΔ
 ɹ὎ίϯόʔδϣϯ཰͕ 7% ‑Down

Slide 5

Slide 5 text

Webϖʔδ͕දࣔ͞ΕΔ·Ͱ Loading Scripting Rendering Painting

Slide 6

Slide 6 text

Webϖʔδ͕දࣔ͞ΕΔ·Ͱ Loading Scripting Rendering Painting Runtime

Slide 7

Slide 7 text

ύϑΥʔϚϯεࢦඪʹͳΔRAILϞσϧ https://developers.google.com/web/fundamentals/performance/rail 100ms 10ms 50ms 1000ms Ϣʔβʔೖྗ εΫϩʔϧ
 Ξχϝʔγϣϯ ΞΠυϧதͷॲཧ ϖʔδϩʔυ

Slide 8

Slide 8 text

https://developers.google.com/web/fundamentals/performance/rail 100ms 10ms 50ms 1000ms Ϣʔβʔೖྗ εΫϩʔϧ
 Ξχϝʔγϣϯ ΞΠυϧதͷॲཧ ϖʔδϩʔυ 3VOUJNF ύϑΥʔϚϯεࢦඪʹͳΔRAILϞσϧ

Slide 9

Slide 9 text

Webϖʔδͷ଎౓ • ϖʔδϩʔυͷ଎౓ • Loadingͷ࣌ؒΛ͍͔ʹ୹͘Ͱ͖Δ͔ • ϥϯλΠϜͷ଎౓ • දࣔ·Ͱͷ଎౓Λ͍͔ʹ୹͘Ͱ͖Δ͔ • ϢʔβʔΠϯλϥΫγϣϯʹ͍͔ʹ଎͘Ԡ͑Δ͔

Slide 10

Slide 10 text

ύϑΥʔϚϯεΛܭଌ͢Δ

Slide 11

Slide 11 text

ύϑΥʔϚϯεͷվળʹ͸ܭଌ͕ॏཁ • Measure, Don’t Guess (ਪଌ͢ΔͳɺܭଌͤΑ) • ݱঢ়ΛՄࢹԽͯ͠ϘτϧωοΫΛݟ͚ͭΔ • Chrome DevToolsɺLighthouseɺWebPageTestͳͲศརͳ πʔϧ͕͋Δ • User Timing APIͳͲͰݸผͷॲཧΛܭଌ

Slide 12

Slide 12 text

User Timing API // ܭଌ։࢝ͷϚʔΫ performance.mark(“A"); heavyTask(); // ܭଌऴྃͷϚʔΫ performance.mark(“B"); // “heavy task”ͱ͍͏໊લͰ”A”ͱ”B”ͷؒͷ࣌ؒΛܭଌ performance.measure("heavy task", "A", "B");

Slide 13

Slide 13 text

User Timing API

Slide 14

Slide 14 text

Vue.config.performance • ίϯϙʔωϯτ͝ͱͷॲཧ࣌ؒΛܭଌ • தͰUser Timing API͕࢖ΘΕ͍ͯΔ const isDev = process.env.NODE_ENV !== “production"; Vue.config.performance = isDev; // trueͰܭଌ͞ΕΔ new Vue({ router, store, render: h => h(App) }).$mount("#app");

Slide 15

Slide 15 text

Vue.config.performance

Slide 16

Slide 16 text

ϖʔδϩʔυͷ଎౓վળ

Slide 17

Slide 17 text

ϖʔδϩʔυͷ଎౓վળ • JavaScriptͷ෼ׂɾ஗ԆಡΈࠐΈ • ը૾ͷ஗ԆಡΈࠐΈ • ը૾ͷ࠷దԽ • ServiceWorkerʹΑΔΩϟογϡ • …etc

Slide 18

Slide 18 text

ϖʔδϩʔυͷ଎౓վળ • JavaScriptͷ෼ׂɾ஗ԆಡΈࠐΈ • ը૾ͷ஗ԆಡΈࠐΈ • ը૾ͷ࠷దԽ • ServiceWorkerʹΑΔΩϟογϡ • …etc

Slide 19

Slide 19 text

JavaScriptͷ෼ׂɾ஗ԆಡΈࠐΈ import Home from ‘./Home.vue’ import About from ‘./About.vue’ const router = new VueRouter({ routes: [ { path: '/', component: Home }, { path: ‘/about', component: About }, ] }) ੩తͳimport

Slide 20

Slide 20 text

JavaScriptͷ෼ׂɾ஗ԆಡΈࠐΈ "QQWVF )PNFWVF "CPVUWVF FOUSZKT 7VF WVF SPVUFS SPVUFSKT BQJKT BYJPT BQQKT ϒϥ΢βͰ࣮ߦ͞ΕΔίʔυ

Slide 21

Slide 21 text

JavaScriptͷ෼ׂɾ஗ԆಡΈࠐΈ const router = new VueRouter({ routes: [ {path: '/',component: () => import('./Home.vue')}, {path: '/about',component: () => import('./About.vue')}, ] }) ಈతͳimportʹ͢Δ͜ͱͰ ෼ׂ͢Δ͜ͱ͕Ͱ͖Δ

Slide 22

Slide 22 text

JavaScriptͷ෼ׂɾ஗ԆಡΈࠐΈ "QQWVF )PNFWVF "CPVUWVF FOUSZKT 7VF WVF SPVUFS SPVUFSKT BQJKT BYJPT BQQKT IPNFKT BCPVUKT

Slide 23

Slide 23 text

JavaScriptͷ෼ׂɾ஗ԆಡΈࠐΈ "QQWVF )PNFWVF "CPVUWVF FOUSZKT 7VF WVF SPVUFS SPVUFSKT BQJKT BYJPT BQQKT IPNFKT BCPVUKT ࠷ॳ͸app.js͚ͩಡΈࠐΈɺ࣮ߦ࣌ʹ ෼ׂͨ͠ίʔυΛϩʔυ͢Δ

Slide 24

Slide 24 text

ը૾ͷ஗ԆಡΈࠐΈ

Slide 25

Slide 25 text

ϖʔδશମͷ͏ͪɺ࠷ॳʹදࣔ ͞ΕΔͷ͸͚ͩ͜͜

Slide 26

Slide 26 text

஗ԆಡΈࠐΈՄೳ

Slide 27

Slide 27 text

εΫϦʔϯʹ ͍ࣸͬͯΔൣғ (viewport) ը૾ͷ஗ԆಡΈࠐΈ

Slide 28

Slide 28 text

ը૾ͷཁૉ͕viewport ʹೖΔͱը૾Λϩʔυ ը૾ͷ஗ԆಡΈࠐΈ

Slide 29

Slide 29 text

v-lazy-image https://github.com/alexjoverm/v-lazy-image

Slide 30

Slide 30 text

v-lazy-image import Vue from "vue"; import { VLazyImagePlugin } from "v-lazy-image"; import App from "./src/App"; Vue.use(VLazyImagePlugin); new Vue({ el: "#app", render: h => h(App) }); Vue.useʹ౉͢͜ͱͰͲͷίϯϙʔω ϯτͰ΋࢖͑ΔΑ͏ʹͳΔ

Slide 31

Slide 31 text

v-lazy-image
.v-lazy-image { width: 100%; } ίϯϙʔωϯτ಺Ͱ v-lazy-imageΛ࢖͏

Slide 32

Slide 32 text

ϥϯλΠϜͷ଎౓վળ

Slide 33

Slide 33 text

ϥϯλΠϜͷ଎౓վળ • FCP΍TTI·Ͱͷ࣌ؒΛ୹͘͢Δ • UIϒϩοΫ͢Δॏ͍ॲཧΛආ͚Δ • ϒϥ΢βͷΞΠυϧ࣌ؒΛ࠷େݶ׆༻͢Δ • Web WorkerΛ༻͍ͨϝΠϯεϨουͷॲཧͷ࡟ݮ • …etc

Slide 34

Slide 34 text

ϥϯλΠϜͷ଎౓վળ • FCP΍TTI·Ͱͷ࣌ؒΛ୹͘͢Δ • UIϒϩοΫ͢Δॏ͍ॲཧΛආ͚Δ • ϒϥ΢βͷΞΠυϧ࣌ؒΛ࠷େݶ׆༻͢Δ • Web WorkerΛ༻͍ͨϝΠϯεϨουͷॲཧͷ࡟ݮ • …etc

Slide 35

Slide 35 text

https://developers.google.com/web/fundamentals/performance/rail

Slide 36

Slide 36 text

https://developers.google.com/web/fundamentals/performance/rail Կ͔͕දࣔ͞Εͨͱ͖ Կ͔ίϯςϯπ͕දࣔ͞Εͨͱ͖ ҙຯͷ͋Δද͕ࣔ͞Εͨͱ͖ ૢ࡞Մೳͱͳͬͨͱ͖

Slide 37

Slide 37 text

Loading HTMLɾCSS JavaScript '$1 55* Client Side Rendering

Slide 38

Slide 38 text

Loading JavaScript '$1 55* Server Side Rendering HTMLɾCSS

Slide 39

Slide 39 text

Loading JavaScript '$1 55* Server Side Rendering αʔόʔαΠυͰϨϯμϦϯάͨ݁͠ՌΛฦ͍ͯ͠Δ HTMLɾCSS

Slide 40

Slide 40 text

• VueͷίʔυΛNode.jsͰ࣮ߦ͠αʔόʔαΠυͰHTMLΛ૊ ΈཱͯΔ • ΫϥΠΞϯτ༻ͷJSϑΝΠϧ΋഑৴͢Δ • Nuxt.js͸σϑΥϧτͰSSR Server Side Rendering

Slide 41

Slide 41 text

https://ssr.vuejs.org/guide/hydration.html

Slide 42

Slide 42 text

• αʔόʔαΠυͰ૊ΈཱͯͨDOMΛ࠶ར༻͢Δ • ੩తͳHTMLʹΫϥΠΞϯταΠυͷVueΛΞλον • ΫϥΠΞϯταΠυͰσʔλมߋʹରԠͰ͖ΔಈతDOM ʹม׵ Client Side Hydration

Slide 43

Slide 43 text

Loading JavaScript '$1 55* HTMLɾCSS Hydration )ZESBUJPO͍ͯ͠Δؒ͸ ૢ࡞Ͱ͖ͳ͍

Slide 44

Slide 44 text

Loading JavaScript '$1 55* HTMLɾCSS Hydration ஈ֊తʹ)ZESBUJPO͢Δ

Slide 45

Slide 45 text

Progressive Hydration • ඞཁͳ෼Λஈ֊తʹHydration͢Δ • Hydrationͷ࣌ؒΛ୹͘͢Δ͜ͱͰTTI·Ͱͷ࣌ؒΛ୹͘͢Δ • Google I/O 2019Ͱ΋঺հ͞Ε͍ͯΔ
 https://youtu.be/k-A2VfuUROg

Slide 46

Slide 46 text

Progressive Hydration ·ͩHydration ͞Ε͍ͯͳ͍

Slide 47

Slide 47 text

viewportʹೖ͖ͬͯͨͱ͖ʹ hydrationͯ͠ૢ࡞Մೳʹ͢Δ Progressive Hydration

Slide 48

Slide 48 text

https://github.com/maoberlehner/vue-lazy-hydration

Slide 49

Slide 49 text

vue-lazy-hydration
import LazyHydrate from 'vue-lazy- hydration'; export default { components: { LazyHydrate, SomeComponent: () => import('./SomeComponent.vue'), }, // ... };

Slide 50

Slide 50 text

ϒϥ΢β͕ΞΠυϧঢ়ଶʹͳͬ ͨͱ͖ʹhydration

Slide 51

Slide 51 text

SSRͰͷΈॲཧ͢Δ

Slide 52

Slide 52 text

viewportʹೖ͖ͬͯͨΒhydration

Slide 53

Slide 53 text

ίϯϙʔωϯτʹରͯ͠ ૢ࡞ͨ͠ͱ͖hydration e.g. focusɺhover

Slide 54

Slide 54 text

when-idle https://github.com/maoberlehner/vue-lazy-hydration/blob/master/src/LazyHydrate.js

Slide 55

Slide 55 text

requestIdleCallback • ϒϥ΢β͕ΞΠυϧঢ়ଶͷͱ͖ʹίʔϧόοΫؔ਺Λ࣮ߦ͢ Δ • ϝΠϯεϨουઐ༗ʹΑΔϨϯμϦϯά΍Ϣʔβʔૢ࡞ͷ๦ ֐Λճආ͢Δ

Slide 56

Slide 56 text

requestIdleCallback // 1ඵܦͬͯίʔϧόοΫ͕࣮ߦ͞Εͳ͚Ε͹λΠϜΞ΢τ const requestId = requestIdleCallback(() => { // ΞΠυϧঢ়ଶʹͳΕ͹࣮ߦ heavyTask(); }, {timeout: 1000}); // Ωϟϯηϧ͢Δ͜ͱ΋Մೳ cancelIdleCallback(requestId);

Slide 57

Slide 57 text

when-visible https://github.com/maoberlehner/vue-lazy-hydration/blob/master/src/LazyHydrate.js

Slide 58

Slide 58 text

IntersectionObserver • λʔήοτཁૉ͕viewportͱަࠩ͢Δͱ͖(ը໘಺ʹೖͬͯ ͖ͨͱ͖)ʹίʔϧόοΫؔ਺Λ࣮ߦ͢Δ • ࢖༻ྫɿը૾஗ԆಡΈࠐΈɺແݶεΫϩʔϧ etc

Slide 59

Slide 59 text

IntersectionObserver const observer = new IntersectionObserver(entries => { const entry = entries[0]; if (entry.intersectionRatio > 0) { enterTask(); // ཁૉ͕ը໘಺ʹೖͬͨͱ͖ } else { exitTask(); // ཁૉ͕ը໘಺ʹग़ͨͱ͖ } }); observer.observe(document.querySelector("#target"));

Slide 60

Slide 60 text

https://github.com/vuejs/vue-next

Slide 61

Slide 61 text

https://github.com/vuejs/vue-next 4JHOJpDBOUMZGBTUFS ʹޤ͏͝ظ଴

Slide 62

Slide 62 text

·ͱΊ • ύϑΥʔϚϯεܭଌΛ͢Δ΂͠ • ଎౓վળʹ͸ϖʔδϩʔυͱϥϯλΠϜ͕͋Δ • ଎౓վળͷͨΊͷVue༻ͷOSSϥΠϒϥϦ͕͋Δ

Slide 63

Slide 63 text

ࢀߟ • ௒଎! Webϖʔδ଎౓վળΨΠυ(WEB+DB PRESS plus) • Web Fundamentals | Google Developers • Vue.js App Performance Optimization – A Tutorial Series • How to Drastically Reduce Estimated Input Latency and Time to Interactive of SSR Vue.js Applications • Progressive Hydration #react_fukuoka - Speaker Deck

Slide 64

Slide 64 text

એ఻ 10/25(Fri) 11/30(Sat), 12/1(Sun)

Slide 65

Slide 65 text

Thanks. @shisama_ @shisama