国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - 編程技術 - 聊聊 Nuxt 開箱即用的特性

聊聊 Nuxt 開箱即用的特性

2021-08-31 22:55大轉轉FE方勇 編程技術

最近公司項目中使用了 Nuxt 框架,進行首屏的服務端渲染,加快了內容的到達時間 (time-to-content),于是筆者開始了對 Nuxt 的學習和使用。以下是從源碼角度對 Nuxt 的一些特性的介紹和分析。

聊聊 Nuxt 開箱即用的特性

引文

最近公司項目中使用了 Nuxt 框架,進行首屏的服務端渲染,加快了內容的到達時間 (time-to-content),于是筆者開始了對 Nuxt 的學習和使用。以下是從源碼角度對 Nuxt 的一些特性的介紹和分析。

FEATURES

服務端渲染(SSR)

Vue.js 是構建客戶端應用程序的框架。默認情況下,可以在瀏覽器中輸出 Vue 組件,進行生成 DOM 和操作 DOM。然而,也可以將同一個組件渲染為服務器端的 HTML 字符串,將它們直接發送到瀏覽器,最后將這些靜態標記"激活"為客戶端上完全可交互的應用程序。 ------Vue SSR 指南

官方Vue SSR指南的基本用法章節,給出了 demo 級別的服務端渲染實現,Nuxt 也是基于該章節實現的,大體流程幾乎一致。建議先食用官方指南,再看本文定大有裨益。

Nuxt 作為一個服務端渲染框架,了解其服務端渲染的實現原理必然是重中之重,就讓我們通過相關源碼,看看其具體實現吧!

我們通過 nuxt 啟動 Nuxt 項目,其首先會執行 startDev 方法,然后調用_listenDev 方法,獲取 Nuxt 配置,調用getNuxt方法實例化 Nuxt。然后執行 nuxt.ready() 方法,生成渲染器。

  1. // @nuxt/server/src/server.js 
  2. async ready () { 
  3.  // Initialize vue-renderer 
  4.  this.serverContext = new ServerContext(this) 
  5.  this.renderer = new VueRenderer(this.serverContext) 
  6.  await this.renderer.ready() 
  7.  
  8.  // Setup nuxt middleware 
  9.  await this.setupMiddleware() 
  10.  
  11.  return this 

在 ready 中會執行 this.setupMiddleware() ,其中會調用nuxtMiddleware 中間件(這里是響應的關鍵)。

  1. // @nuxt/server/src/middleware/nuxt.js 
  2. export default ({ options, nuxt, renderRoute, resources }) => async function nuxtMiddleware (req, res, next) { 
  3.  const context = getContext(req, res) 
  4.  try { 
  5.    const url = normalizeURL(req.url) 
  6.    res.statusCode = 200 
  7.    const result = await renderRoute(url, context) // 渲染相應路由,后文會展開 
  8.     
  9.    const { 
  10.      html, 
  11.      redirected, 
  12.      preloadFiles 
  13.   } = result // 得到html 
  14.  
  15.    // 設置頭部字段 
  16.    res.setHeader('Content-Type''text/html; charset=utf-8'
  17.    res.setHeader('Accept-Ranges''none'
  18.    res.setHeader('Content-Length', Buffer.byteLength(html)) 
  19.    res.end(html, 'utf8') // 做出響應 
  20.    return html 
  21. } catch (err) { 
  22.    if (context && context.redirected) { 
  23.      consola.error(err) 
  24.      return err 
  25.   } 
  26.    next(err) 

nuxtMiddleware 中間件中首先標準化請求的url,設置請求狀態碼,通過url匹配到相應的路由,渲染出對應的路由組件,設置頭部信息,最后做出響應。

  1. renderSSR (renderContext) { 
  2.  // Call renderToString from the bundleRenderer and generate the HTML (will update the renderContext as well) 
  3.  // renderSSR 只是 universal app的渲染方法,Nuxt 也可以進行開發普通的 SPA 項目 
  4.  const renderer = renderContext.modern ? this.renderer.modern : this.renderer.SSR 
  5.  return renderer.render(renderContext) 

其中 renderRoute 方法會調用 @nuxt/vue-render 的renderSSR 進行服務端渲染操作。

  1. // @nuxt/vue-renderer/src/renderers/SSR.js 
  2. async render (renderContext) { 
  3.  // Call Vue renderer renderToString 
  4.  let APP = await this.vueRenderer.renderToString(renderContext) 
  5.  
  6.  let HEAD = '' 
  7.  // ... 此處省略n行HEAD拼接代碼,后續 HEAD 管理部分會提及 
  8.     
  9.  // Render with SSR template 
  10.  const html = this.renderTemplate(this.serverContext.resources.SSRTemplate, templateParams) 
  11.  
  12.  return { 
  13.    html, 
  14.    preloadFiles 

而 renderSSR 又會調用 renderer.render 方法,將 url 匹配的路由渲染成字符串,將字符串與模版相結合,得到最終返回給瀏覽器的html,至此 Nuxt 服務端渲染完成。

最后貼一張盜來的 Nuxt 執行流程圖,圖畫的很棒,流程也很清晰,感謝。

聊聊 Nuxt 開箱即用的特性

數據拉取(Data Fetching)

在客戶端程序(CSR)可以通過在 mounted 鉤子中獲取數據,但在通用程序(Universal)中則需要使用特定的鉤子才能在服務端獲取數據。

Nuxt 中主要提供了兩種鉤子獲取數據

  • asyncData
    • 只可以在頁面級組件中獲取,不可以訪問 this
    • 通過返回對象保存數據狀態或與Vuex配合進行狀態保存
  • fetch
    • 所有組件中都可以獲取,可以訪問 this
    • 無需傳入 context,傳入 context 會 fallback 到老版的 fetch,功能類似于 asyncData
  1. // .nuxt/server.js 
  2. // Components are already resolved by setContext -> getRouteData (app/utils.js) 
  3. const Components = getMatchedComponents(app.context.route) 
  4.   
  5. // 在匹配的路由中,調用 asyncData 和 legacy 版本的 fetch方法 
  6. const asyncDatas = await Promise.all(Components.map((Component) => { 
  7.  const promises = [] 
  8.  
  9.  // 調用 asyncData(context) 
  10.  if (Component.options.asyncData && typeof Component.options.asyncData === 'function') { 
  11.    const promise = promisify(Component.options.asyncData, app.context) 
  12.    promise.then((asyncDataResult) => { 
  13.      SSRContext.asyncData[Component.cid] = asyncDataResult 
  14.      applyAsyncData(Component) 
  15.      return asyncDataResult 
  16.   }) 
  17.    promises.push(promise) 
  18.   } else { 
  19.      promises.push(null
  20.   } 
  21.  
  22.    // 調用 legacy 版本的fetch(context) 兼容老版本的 fetch 
  23.    if (Component.options.fetch && Component.options.fetch.length) { 
  24.      promises.push(Component.options.fetch(app.context)) 
  25.   } else { 
  26.    promises.push(null
  27.  
  28.  return Promise.all(promises) 
  29. })) 

在生成的 .nuxt/server.js 中,會遍歷匹配的組件,查看組件中是否定義了 asyncData 選項以及 legacy 版 fetch ,存在就依次調用,獲得 asyncDatas。

  1. // .nuxt/mixins/fetch.server.js  
  2. // nuxt v2.14及之后 
  3. async function serverPrefetch() { 
  4.  // Call and await on $fetch 
  5.  // v2.14 之后推薦的 fetch 
  6.  try { 
  7.    await this.$options.fetch.call(this) 
  8. } catch (err) { 
  9.    if (process.dev) { 
  10.      console.error('Error in fetch():', err) 
  11.   }  
  12.  this.$fetchState.pending = false // 設置fetchState 為 false 

在服務端實例化 vue 實例之后,執行 serverPrefetch,觸發 fetch 選項方法,獲取數據,數據會作用于生成 html的過程。

HEAD 管理(Meta Tags and SEO)

截至目前,Google 和 Bing 可以很好對同步 JavaScript 應用程序進行索引。但是對于異步獲取數據的網站來說,主流的搜索引擎暫時還無法支持,于是造成網站搜索排名靠后,于是希望獲得更好的SEO成為眾多網站考慮使用SSR框架的原因。

為了獲得良好的SEO,那么就需要對HEAD進行精細化的配置和管理。讓我們看看其是如何實現的吧~

Nuxt框架借助 vue-meta 庫實現全局、單個頁面的 meta 標簽的自定義。Nuxt 內部的實現也幾乎遵循 vue-meta 官方的 SSR meta 管理的流程。具體詳情請查看。

  1. // @nuxt/vue-app/template/index.js 
  2. // step1 
  3. Vue.use(Meta, JSON.stringify(vueMetaOptions)) 
  4.  
  5. // @nuxt/vue-app/template/template.js 
  6. // step2 
  7. export default async (SSRContext) => { 
  8.  const _app = new Vue(app) 
  9.  // Add meta infos (used in renderer.js) 
  10.  SSRContext.meta = _app.$meta() 
  11.  return _app 

首先通過Vue插件的形式,注冊vue-meta,內部會在Vue的原型上掛載$meta屬性。然后將meta添加到服務端渲染上下文中。

  1. async render (renderContext) { 
  2.    // Call Vue renderer renderToString 
  3.    let APP = await this.vueRenderer.renderToString(renderContext) 
  4.    // step3 
  5.    let HEAD = '' 
  6.  
  7.    // Inject head meta 
  8.    // (this is unset when features.meta is false in server template) 
  9.    // 以下就是上文省略的 n 行 HEAD 拼接代碼,可以適當忽略 
  10.    // 了解主要過程即可,具體細節按需查看 
  11.    const meta = renderContext.meta && renderContext.meta.inject({ 
  12.      isSSR: renderContext.nuxt.serverRendered, 
  13.      ln: this.options.dev 
  14.   }) 
  15.  
  16.    if (meta) { 
  17.      HEAD += meta.title.text() + meta.meta.text() 
  18.   } 
  19.  
  20.    if (meta) { 
  21.      HEAD += meta.link.text() + 
  22.        meta.style.text() + 
  23.        meta.script.text() + 
  24.        meta.noscript.text() 
  25.   } 
  26.  
  27.    // Check if we need to inject scripts and state 
  28.    const shouldInjectScripts = this.options.render.injectScripts !== false 
  29.  
  30.    // Inject resource hints 
  31.    if (this.options.render.resourceHints && shouldInjectScripts) { 
  32.      HEAD += this.renderResourceHints(renderContext) 
  33.   } 
  34.  
  35.    // Inject styles 
  36.    HEAD += this.renderStyles(renderContext) 
  37.  
  38.  
  39.    // Prepend scripts 
  40.    if (shouldInjectScripts) { 
  41.      APP += this.renderScripts(renderContext) 
  42.   } 
  43.  
  44.    if (meta) { 
  45.      const appendInjectorOptions = { body: true } 
  46.      // Append body scripts 
  47.      APP += meta.meta.text(appendInjectorOptions) 
  48.      APP += meta.link.text(appendInjectorOptions) 
  49.      APP += meta.style.text(appendInjectorOptions) 
  50.      APP += meta.script.text(appendInjectorOptions) 
  51.      APP += meta.noscript.text(appendInjectorOptions) 
  52.   } 
  53.  
  54.    // Template params 
  55.    const templateParams = { 
  56.      HTML_ATTRS: meta ? meta.htmlAttrs.text(renderContext.nuxt.serverRendered /* addSrrAttribute */) : ''
  57.      HEAD_ATTRS: meta ? meta.headAttrs.text() : ''
  58.      BODY_ATTRS: meta ? meta.bodyAttrs.text() : ''
  59.      HEAD, 
  60.      APP, 
  61.      ENV: this.options.env 
  62.   } 
  63.  
  64.    // Render with SSR template 
  65.    // 通過模版和參數 生成html 
  66.    const html = this.renderTemplate(this.serverContext.resources.SSRTemplate, templateParams) 
  67.  
  68.    let preloadFiles 
  69.    if (this.options.render.http2.push) { 
  70.      // 獲取需要預加載的文件 
  71.      preloadFiles = this.getPreloadFiles(renderContext) 
  72.   } 
  73.  
  74.    return { 
  75.      html, 
  76.      preloadFiles, 
  77.   } 

最后在響應的 html 中注入 metadata 即可。

文件系統路由(File System Routing)

想必使用過 Nuxt 的同學應該都對其基于文件生成路由的特性,印象深刻。讓我從源碼角度看看 Nuxt 是如何實現基于 pages 目錄(可配置),自動生成路由的。

首先在啟動 Nuxt 項目或者修改文件時,會自動調 generateRoutesAndFiles 方法,生成路由 以及 .nuxt 目錄下的文件。

  1. // @nuxt/builder/src/builder.js 
  2. async generateRoutesAndFiles() { 
  3.   ... 
  4.   await Promise.all([ 
  5.     this.resolveLayouts(templateContext), 
  6.     this.resolveRoutes(templateContext), //解析生成路由,需要關注的重點 
  7.     this.resolveStore(templateContext), 
  8.     this.resolveMiddleware(templateContext) 
  9.   ]) 
  10.   ... 

解析路由會存在三種情況:一是修改了默認的 pages 目錄名稱,且未在 nuxt.config.js 中配置相關目錄,二是使用 nuxt 默認的 pages 目錄,三是使用調用用戶自定義的路由生成方法生成路由。

  1. // @nuxt/builder/src/builder.js 
  2. async resolveRoutes({ templateVars }) { 
  3.   consola.debug('Generating routes...'
  4.   if (this._defaultPage) { 
  5.     // 在srcDir下未找到pages目錄 
  6.   } else if (this._nuxtPage) { 
  7.     // 使用nuxt動態生成路由 
  8.   } else { 
  9.     // 用戶提供了自定義方法去生成路由,提供用戶自定義路由的能力 
  10.   } 
  11.   // router.extendRoutes method 
  12.   if (typeof this.options.router.extendRoutes === 'function') { 
  13.     const extendedRoutes = await this.options.router.extendRoutes( 
  14.       templateVars.router.routes, 
  15.       resolve 
  16.     ) 
  17.     if (extendedRoutes !== undefined) { 
  18.       templateVars.router.routes = extendedRoutes 
  19.     } 
  20.   } 

除此之外,還可以提供相應的 extendRoutes 方法,在 nuxt 生成路由的基礎上添加自定義路由。

  1. export default { 
  2.   router: { 
  3.     extendRoutes(routes, resolve) { 
  4.       // 例如添加 404 頁面 
  5.       routes.push({ 
  6.         name'custom'
  7.         path: '*'
  8.         component: resolve(__dirname, 'pages/404.vue'
  9.       }) 
  10.     } 
  11.   } 

其中當修改了默認的 pages 目錄,導致找不到相關的目錄,會使用 @nuxt/vue-app/template/pages/index.vue 文件生成路由。

  1. async resolveRoutes({ templateVars }) { 
  2.   if (this._defaultPage) { 
  3.     // createRoutes 方法根據傳參,生成路由。具體算法,不再展開 
  4.     templateVars.router.routes = createRoutes({ 
  5.       files: ['index.vue'], 
  6.       srcDir: this.template.dir + '/pages', // 指向@nuxt/vue-app/template/pages/index.vue 
  7.       routeNameSplitter, // 路由名稱分隔符,默認`-` 
  8.       trailingSlash // 尾斜杠 / 
  9.     }) 
  10.   } else if (this._nuxtPage) { 
  11.     const files = {} 
  12.     const ext = new RegExp(`\\.(${this.supportedExtensions.join('|')})$`) 
  13.     for (const page of await this.resolveFiles(this.options.dir.pages)) { 
  14.       const key = page.replace(ext, ''
  15.       // .vue file takes precedence over other extensions 
  16.       if (/\.vue$/.test(page) || !files[key]) { 
  17.         files[key] = page.replace(/(['"])/g, '\\$1') 
  18.       } 
  19.     } 
  20.     templateVars.router.routes = createRoutes({ 
  21.       files: Object.values(files), 
  22.       srcDir: this.options.srcDir, 
  23.       pagesDir: this.options.dir.pages, 
  24.       routeNameSplitter, 
  25.       supportedExtensions: this.supportedExtensions, 
  26.       trailingSlash 
  27.     }) 
  28.     } else { 
  29.       templateVars.router.routes = await this.options.build.createRoutes(this.options.srcDir) 
  30.     } 
  31.     // router.extendRoutes method 
  32.     if (typeof this.options.router.extendRoutes === 'function') { 
  33.       const extendedRoutes = await this.options.router.extendRoutes( 
  34.         templateVars.router.routes, 
  35.         resolve 
  36.       ) 
  37.       if (extendedRoutes !== undefined) { 
  38.         templateVars.router.routes = extendedRoutes 
  39.       } 
  40.   } 

然后就是調用 createRoutes 方法,生成路由。生成的路由大致長這樣,和手動書寫的路由文件幾乎一致(后續還會進行打包??,懶加載引入路由組件)。

  1.   { 
  2.     name'index'
  3.     path: '/'
  4.     chunkName: 'pages/index'
  5.     component: 'Users/username/projectName/pages/index.vue' 
  6.   }, 
  7.   { 
  8.     name'about'
  9.     path: '/about'
  10.     chunkName: 'pages/about/index'
  11.     component: 'Users/username/projectName/pages/about/index.vue' 
  12.   } 

智能預取(Smart Prefetching)

從 Nuxt v2.4.0 開始,當 出現在可視區域后,Nuxt將會預取經過code-splitted的 page 頁面的腳本,使得在用戶點擊之前,該路由指向的地址,就處于 ready 狀態,這將極大的提升用戶的體驗。

相關實現邏輯集中于 .nuxt/components/nuxt-link.client.js 中。

首先 Smart Prefetching 特性的實現依賴于window.IntersectionObserver 這個實驗性的 API,如果瀏覽器不支持該 API,就不會進行組件預取操作。

  1. mounted () { 
  2.   if (this.prefetch && !this.noPrefetch) { 
  3.     this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 }) 
  4.   } 

然后在需要預取的 組件掛載階段,會調用 requestIdleCallback 方法在瀏覽器的空閑時段內調用 observe 方法。

  1. const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => { 
  2.   entries.forEach(({ intersectionRatio, target: link }) => { 
  3.     // 如果intersectionRatio 小于等于0,表示目標不在viewport內 
  4.     if (intersectionRatio <= 0 || !link.__prefetch) { 
  5.       return 
  6.     } 
  7.     // 進行預取數據(其實就是加載組件) 
  8.     link.__prefetch() 
  9.   }) 
  10. }) 

當被監聽的元素的可視情況發生改變的時候(且出現在視圖內時),會觸發 new window.IntersectionObserver(callback) 的回調,執行真正的預取操作prefetchLink。

  1. prefetchLink () { 
  2.   // 判斷網絡環境,離線或者2G環境下,不進行預取操作 
  3.   if (!this.canPrefetch()) { 
  4.     return 
  5.   } 
  6.   // 停止監聽該元素,提高性能 
  7.   observer.unobserve(this.$el) 
  8.   const Components = this.getPrefetchComponents() 
  9.  
  10.   for (const Component of Components) { 
  11.     // 及時加載組件,使得用戶點擊時,該組件是一個就緒的狀態 
  12.     const componentOrPromise = Component() 
  13.     if (componentOrPromise instanceof Promise) { 
  14.       componentOrPromise.catch(() => {}) 
  15.     Component.__prefetched = true // 已經預取的標志位 
  16.   } 

總結

上文從源碼角度介紹了 Nuxt 服務端渲染的實現、服務端數據的獲取以及 Nuxt 開箱即用的幾個特性:HEAD 管理、基于文件系統的路由和智能預取 code-splitted 的路由。如果希望對 SSR 進行更深入研究,還可以橫向學習 React 的 SSR 實現 Next 框架。

希望對您有所幫助,如有紕漏,望請輔正。

參考

為什么使用服務器端渲染 (SSR)?

Nuxt源碼精讀

Vue Meta

Introducing Smart prefetching

服務端渲染

原文鏈接:https://mp.weixin.qq.com/s/fvv12ZPxiEpCzER3Q-X5pQ

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91麻豆精品国产91久久久更新资源速度超快 | 亚洲高清一区二区三区 | 亚洲视频二区 | 亚洲国产精品成人va在线观看 | 日韩成人精品在线观看 | 欧美三级网站 | 在线国产视频 | 国产日韩中文字幕 | 久久精品无码一区二区三区 | 北条麻妃一区二区三区在线观看 | 欧美a网| 欧洲毛片 | 伊人中文 | 激情久久久久 | 午夜精品视频在线观看 | 欧美精品99 | 91九色在线 | 久久综合久久88 | 九九久久精品 | 7799精品视频 | 久久国内精品 | 精品久久ai | 国产高清在线精品一区二区三区 | 九九九久久久久久 | 成人午夜视频在线观看 | 国产综合久久 | 丝袜+亚洲+另类+欧美+变态 | 成人免费毛片aaaaaa片 | 欧美日韩三区 | 日本手机在线视频 | 国产美女久久久 | 日韩欧美视频 | 激情亚洲 | 欧洲精品码一区二区三区免费看 | 欧美国产精品一区 | 高清一区二区 | 日韩精品中文字幕在线 | 亚洲专区中文字幕 | caoporn免费| 欧美一区二区三 | 亚洲一区精品在线 |