Slide 1

Slide 1 text

Vueと比べて理解するNuxtの機能 auto-import編 1

Slide 2

Slide 2 text

目次 1. 自己紹介 2. きっかけ 3. vueで確認する 4. Nuxtで確認する 5. ソースコードで確認する Engineer LT Night #1 @渋谷 2

Slide 3

Slide 3 text

自己紹介

Slide 4

Slide 4 text

飯原帆隆 株式会社メタップスホールディングス エンジニア Vue/Nuxt Rails Litを主に書きます Github: gityosan Engineer LT Night #1 @渋谷 4

Slide 5

Slide 5 text

きっかけ

Slide 6

Slide 6 text

きっかけ vue3で開発していて、Nuxt3と比べてルーティングや各種関数の初期化や読み込 み周りで辛さを感じることがあったため、しっかりと調査して比較することにしま した。 Engineer LT Night #1 @渋谷 6

Slide 7

Slide 7 text

まず、vueで確認してみる

Slide 8

Slide 8 text

ログを埋め込む // main.ts const resolveFunc = () => { return new Promise((resolve) => { setTimeout(() => { resolve('main.ts:resolveFunc:resolved') }, 2000) }) } const asyncCall = async () => { console.debug('main.ts:asyncCall:calling') const result = await resolveFunc() console.debug(result) } new Promise((resolve) => { setTimeout(() => { resolve('main.ts:Promise:resolved') }, 2000) }).then(() => { console.debug('main.ts:Promise:then') }) asyncCall().then(() => { console.debug('main.ts:asyncCall:then') }) // router/middleware.ts const resolveFunc = () => { return new Promise((resolve) => { setTimeout(() => { resolve('beforeEach:resolveFunc:resolved') }, 2000) }) } const asyncCall = async () => { console.debug('beforeEach:asyncCall:calling') const result = await resolveFunc() console.debug(result) } new Promise((resolve) => { setTimeout(() => { resolve('beforeEach:Promise:resolved') }, 2000) }).then(() => { console.debug('beforeEach:Promise:then') }) asyncCall().then(() => { console.debug('beforeEach:asyncCall:then') }) Engineer LT Night #1 @渋谷 8

Slide 9

Slide 9 text

トップレベルに一切awaitを付けなかった場合 Engineer LT Night #1 @渋谷 9

Slide 10

Slide 10 text

トップレベルにawaitを付けた場合 Engineer LT Night #1 @渋谷 10

Slide 11

Slide 11 text

下記のように関連する処理全てにawaitを付けた としても、、、 // main.ts const app = await createApp(AppEmployee) await app.use(router) await app.component() await app.provide(storeKey, createGlobalStore()) await app.mount('#app') // router.ts - router.beforeEach(authCheckOnRouteChanged) + router.beforeEach(async (to, from) => { + await authCheckOnRouteChanged(to, from) + }) main.tsとrouter.tsの処理が非同期的に進みなが らapp.vueのmountへとつながっている Engineer LT Night #1 @渋谷 11

Slide 12

Slide 12 text

一方、Nuxt

Slide 13

Slide 13 text

ログを埋め込む // plugin/index.ts const resolveFunc = () => { return new Promise((resolve) => { setTimeout(() => { resolve('plugin:resolveFunc:resolved') }, 2000) }) } const asyncCall = async () => { console.debug('plugin:asyncCall:calling') const result = await resolveFunc() console.debug(result) } export default defineNuxtPlugin(async (app) => { await new Promise((resolve) => { setTimeout(() => { resolve('plugin:Promise:resolved') }, 2000) }).then(() => { console.debug('plugin:Promise:then') }) await asyncCall().then(() => { console.debug('plugin:asyncCall:then') }) }) // middleware/index.ts const resolveFunc = () => { return new Promise((resolve) => { setTimeout(() => { resolve('middleware:resolveFunc:resolved') }, 2000) }) } const asyncCall = async () => { console.debug('middleware:asyncCall:calling') const result = await resolveFunc() console.debug(result) } export default defineNuxtRouteMiddleware(async (to) => { await new Promise((resolve) => { setTimeout(() => { resolve('middleware:Promise:resolved') }, 2000) }).then(() => { console.debug('middleware:Promise:then') }) await asyncCall().then(() => { console.debug('middleware:asyncCall:then') }) }) Engineer LT Night #1 @渋谷 13

Slide 14

Slide 14 text

トップレベルに一切awaitを付けなかった場合 Engineer LT Night #1 @渋谷 14

Slide 15

Slide 15 text

トップレベルにawaitを付けた場合 Engineer LT Night #1 @渋谷 15

Slide 16

Slide 16 text

Nuxtではasync awaitを適切に設定した場合、pluginとmiddlewareの読み込みが同 期的に進みます これが地味に嬉しい。 しかし、なぜこんな挙動をするのか Engineer LT Night #1 @渋谷 16

Slide 17

Slide 17 text

ソースコードで確認してみる

Slide 18

Slide 18 text

// https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/plugins/router.ts export default defineNuxtPlugin<{ route: Route, router: Router }>({ name: 'nuxt:router', enforce: 'pre', setup (nuxtApp) { // < 中略> async function handleNavigation (url: string | Partial, replace?: boolean): Promise { try { // Resolve route const to = getRouteFromPath(url) // Run beforeEach hooks for (const middleware of hooks['navigate:before']) { const result = await middleware(to, route) // Cancel navigation if (result === false || result instanceof Error) { return } // Redirect if (typeof result === 'string' && result.length) { return handleNavigation(result, true) } } for (const handler of hooks['resolve:before']) { await handler(to, route) } // Perform navigation Object.assign(route, to) if (import.meta.client) { window.history[replace ? 'replaceState' : 'pushState']({}, '', joinURL(baseURL, to.fullPath)) if (!nuxtApp.isHydrating) { // Clear any existing errors await nuxtApp.runWithContext(clearError) } } // Run afterEach hooks for (const middleware of hooks['navigate:after']) { await middleware(to, route) } } catch (err) { if (import.meta.dev && !hooks.error.length) { console.warn('No error handlers registered to handle middleware errors. You can register an error handler with `router.onError()`', err) } for (const handler of hooks.error) { await handler(err) } } } // < 中略> } }) Engineer LT Night #1 @渋谷 18

Slide 19

Slide 19 text

// https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/entry.ts ... entry = async function initApp () { if (vueAppPromise) { return vueAppPromise } const isSSR = Boolean( window.__NUXT__?.serverRendered || document.getElementById('__NUXT_DATA__')?.dataset.ssr === 'true' ) const vueApp = isSSR ? createSSRApp(RootComponent) : createApp(RootComponent) const nuxt = createNuxtApp({ vueApp }) try { await applyPlugins(nuxt, plugins) } catch (err) { await nuxt.callHook('app:error', err) nuxt.payload.error = (nuxt.payload.error || err) as any } try { await nuxt.hooks.callHook('app:created', vueApp) await nuxt.hooks.callHook('app:beforeMount', vueApp) vueApp.mount(vueAppRootContainer) await nuxt.hooks.callHook('app:mounted', vueApp) await nextTick() } catch (err) { await nuxt.callHook('app:error', err) nuxt.payload.error = (nuxt.payload.error || err) as any } return vueApp } ... Engineer LT Night #1 @渋谷 19

Slide 20

Slide 20 text

恐らく、、、 pluginはもとよりmiddlewareもrouterのpluginとして定義して、その上で初期化時にpluginを順次読み 込んでいるため先述のような挙動になるのだろうと考えられる Engineer LT Night #1 @渋谷 20

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Thank you for listening!!