国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

vue:服務(wù)端渲染技術(shù)

gnehc / 1468人閱讀

摘要:在實(shí)際應(yīng)用中,我們可以將回調(diào)函數(shù)拿到的拼接到模板中,下面看下的實(shí)現(xiàn)把轉(zhuǎn)換模板字符串方法不斷拼接模板字符串,用做存儲,然后調(diào)用當(dāng)通過完畢,執(zhí)行傳入方法支持傳入實(shí)例和渲染完成后的回調(diào)函數(shù)。

服務(wù)端渲染:

簡單說:比如說一個(gè)模板,數(shù)據(jù)是從后臺獲取的,如果用客戶端渲染那么瀏覽器會先渲染htmlcss,然后再通過jsajax去向后臺請求數(shù)據(jù)再更改渲染。就是在前端再用Node建個(gè)后臺,把首屏數(shù)據(jù)加載成一個(gè)完整的頁面在node建的后臺渲染好,瀏覽器拿到的就是一個(gè)完整的dom樹。根據(jù)項(xiàng)目打開地址,路由指到哪個(gè)頁面就跳到哪。

服務(wù)端比起客戶端渲染頁面的優(yōu)點(diǎn):

首屏渲染速度更快

客戶端渲染的一個(gè)缺點(diǎn)是,用戶第一次訪問頁面,此時(shí)瀏覽器沒有緩存,需要先從服務(wù)端下載js,然后再通過js操作動態(tài)添加dom并渲染頁面,時(shí)間較長。而服務(wù)端渲染的規(guī)則是,用戶第一次訪問瀏覽器可以直接解析html文檔并渲染頁面,并屏渲染速度比客戶端渲染更快。

SEO

服務(wù)端渲染可以讓搜索引擎更容易讀取頁面的meta信息,以及其它SEO相關(guān)信息,大大增加了網(wǎng)站在搜索引擎中的速度。

減少HTTP請求

服務(wù)端渲染可以把一些動態(tài)數(shù)據(jù)在首次渲染時(shí)同步輸出到頁面,而客戶端渲染需要通過AJAX等手段異步獲取這些數(shù)據(jù),這樣就相當(dāng)于多了一次HTTP請求。

普通服務(wù)端渲染

vue提供了renderToString接口,可以在服務(wù)端把vue組件渲染成模板字符串,我們先看下用法:

benchmarks/ssr/renderToString.js

const Vue = require("../../dist/vue.runtime.common.js")
const createRenderer = require("../../packages/vue-server-renderer").createRenderer
const renderToString = createRenderer().renderToString
const gridComponent = require("./common.js") // vue支行時(shí)的代碼,不包括編譯部分

console.log("--- renderToString --- ")
const self = (global || root)
self.s = self.performance.now()

renderToString(new Vue(gridComponent), (err, res) => {
  if (err) throw err
  // console.log(res)
  console.log("Complete time: " + (self.performance.now() - self.s).toFixed(2) + "ms")
  console.log()
})

這段代碼是支行在node.js環(huán)境中的,主要依賴vue.common.js,vue-server-render.其中vue.common.jsvue運(yùn)行時(shí)代碼,不包括編譯部分:vue-server-render對外提供createRenderer方法,renderToStringcreateRenderer方法返回值的一個(gè)屬性,它支持傳入vue實(shí)例和渲染完成后的回調(diào)函數(shù),這里要注意,由于引用的是只包括運(yùn)行時(shí)的vue代碼,不包括編譯部分,所以其中err表示是否出錯(cuò),result表示dom字符串。在實(shí)際應(yīng)用中,我們可以將回調(diào)函數(shù)拿到的result拼接到模板中,下面看下renderToString的實(shí)現(xiàn):

src/server/create-renderer.js

const render = createRenderFunction(modules, directives, isUnaryTag, cache)
return {
    renderToString (
      component: Component,
      context: any,
      cb: any
    ): ?Promise {
      if (typeof context === "function") {
        cb = context
        context = {}
      }
      if (context) {
        templateRenderer.bindRenderFns(context)
      }

      // no callback, return Promise
      let promise
      if (!cb) {
        ({ promise, cb } = createPromiseCallback())
      }

      let result = ""
      const write = createWriteFunction(text => {
        result += text
        return false
      }, cb)
      try {
        //  render:把component轉(zhuǎn)換模板字符串str ,write方法不斷拼接模板字符串,用result做存儲,然后調(diào)用next,當(dāng)component通過render完畢,執(zhí)行done傳入resut,
        render(component, write, context, () => {
          if (template) {
            result = templateRenderer.renderSync(result, context)
          }
          cb(null, result)
        })
      } catch (e) {
        cb(e)
      }

      return promise
    }
}

renderToString方法支持傳入vue實(shí)例component和渲染完成后的回調(diào)函數(shù)done。它定義了result變量,同時(shí)定義了write方法,最后執(zhí)行render方法。整個(gè)過程比較核心的就是render方法:

src/server/render.js

return function render (
    component: Component,
    write: (text: string, next: Function) => void,
    userContext: ?Object,
    done: Function
  ) {
    warned = Object.create(null)
    const context = new RenderContext({
      activeInstance: component,
      userContext,
      write, done, renderNode,
      isUnaryTag, modules, directives,
      cache
    })
    installSSRHelpers(component)
    normalizeRender(component)
    renderNode(component._render(), true, context)
  }
/**
 * // render實(shí)際上是執(zhí)行了renderNode方法,并把component._render()方法生成的vnode對象作為參數(shù)傳入。
 * @param node 先判斷node類型,如果是component Vnode,則根據(jù)這個(gè)Node創(chuàng)建一個(gè)組件的實(shí)例并調(diào)用_render方法作為當(dāng)前node的childVnode,然后遞歸調(diào)用renderNode
 * @param isRoot 如果是一個(gè)普通dom Vnode對象,則調(diào)用renderElement渲染元素,否則就是一個(gè)文本節(jié)點(diǎn),直接用write方法。
 * @param context
 */
function renderNode (node, isRoot, context) {
  if (node.isString) {
    renderStringNode(node, context)
  } else if (isDef(node.componentOptions)) {
    renderComponent(node, isRoot, context)
  } else if (isDef(node.tag)) {
    renderElement(node, isRoot, context)
  } else if (isTrue(node.isComment)) {
    if (isDef(node.asyncFactory)) {
      // async component
      renderAsyncComponent(node, isRoot, context)
    } else {
      context.write(``, context.next)
    }
  } else {
    context.write(
      node.raw ? node.text : escape(String(node.text)),
      context.next
    )
  }
}
/**主要功能是把VNode對象渲染成dom元素。
 * 先判斷是不是根元素,然后渲染開始開始標(biāo)簽,如果是自閉合標(biāo)簽直接寫入write,再執(zhí)行next方法 
 * 如果沒有子元素,又不是閉合標(biāo)簽,通過write寫入開始-閉合標(biāo)簽。再執(zhí)行next.dom渲染完畢
 * 否則就通過write寫入開始標(biāo)簽,接著渲染所有的子節(jié)點(diǎn),再通過write寫入閉合標(biāo)簽,最后執(zhí)行next
 * @param context
 */
function renderElement (el, isRoot, context) {
  const { write, next } = context

  if (isTrue(isRoot)) {
    if (!el.data) el.data = {}
    if (!el.data.attrs) el.data.attrs = {}
    el.data.attrs[SSR_ATTR] = "true"
  }

  if (el.functionalOptions) {
    registerComponentForCache(el.functionalOptions, write)
  }

  const startTag = renderStartingTag(el, context)
  const endTag = ``
  if (context.isUnaryTag(el.tag)) {
    write(startTag, next)
  } else if (isUndef(el.children) || el.children.length === 0) {
    write(startTag + endTag, next)
  } else {
    const children: Array = el.children
    context.renderStates.push({
      type: "Element",
      rendered: 0,
      total: children.length,
      endTag, children
    })
    write(startTag, next)
  }
}
流式服務(wù)端渲染

普通服務(wù)器有一個(gè)痛點(diǎn)——由于渲染是同步過程,所以如果這個(gè)app很復(fù)雜的話,可能會阻塞服務(wù)器的event loop,同步服務(wù)器在優(yōu)化不當(dāng)時(shí)甚至?xí)o客戶端獲得內(nèi)容的速度帶來負(fù)面影響。vue提供了renderToStream接口,在渲染組件時(shí)返回一個(gè)可讀的stream,可以直接pipeHTTP Response中,流式渲染能確保在服務(wù)端響應(yīng)度,也能讓用戶更快地獲得渲染內(nèi)容。renderToStream源碼:

benchmarks/ssr/renderToStream.js

const Vue = require("../../dist/vue.runtime.common.js")
const createRenderer = require("../../packages/vue-server-renderer").createRenderer
const renderToStream = createRenderer().renderToStream
const gridComponent = require("./common.js")

console.log("--- renderToStream --- ")
const self = (global || root)
const s = self.performance.now()

const stream = renderToStream(new Vue(gridComponent))
let str = ""
let first
let complete
stream.once("data", () => {
  first = self.performance.now() - s
})
stream.on("data", chunk => {
  str += chunk
})
stream.on("end", () => {
  complete = self.performance.now() - s
  console.log(`first chunk: ${first.toFixed(2)}ms`)
  console.log(`complete: ${complete.toFixed(2)}ms`)
  console.log()
})

這段代碼也是同樣運(yùn)行在node環(huán)境中的,與rendetToString不同,它會把vue實(shí)例渲染成一個(gè)可讀的stream。源碼演示的是監(jiān)聽數(shù)據(jù)的讀取,并記錄讀取數(shù)據(jù)的時(shí)間
,而在實(shí)際應(yīng)用中,我們也可以這樣寫:

const Vue = require("../../dist/vue.runtime.common.js")
const createRenderer = require("../../packages/vue-server-renderer").createRenderer
const renderToStream = createRenderer().renderToStream
const gridComponent = require("./common.js")

const stream = renderToStream(new Vue(gridComponent))
app.use((req,res)=>{
    stream.pipe(res)
})

如果代碼運(yùn)行在Express框架中,則可以通過app.use方法創(chuàng)建middleware,然后直接把stream piperes中,這樣客戶端就能很快地獲得渲染內(nèi)容了,下面看下renderToStream的實(shí)現(xiàn):

src/server/create-renderer.js

  const render = createRenderFunction(modules, directives, isUnaryTag, cache)
  
  return {
    ...

    renderToStream (component: Component,context?: Object): stream$Readable {
        if (context) {
            templateRenderer.bindRenderFns(context)
        }
        const renderStream = new RenderStream((write, done) => {
            render(component, write, context, done)
        })
        if (!template) {
            return renderStream
        } else {
            const templateStream = templateRenderer.createStream(context)
            renderStream.on("error", err => {
                templateStream.emit("error", err)
            })
            renderStream.pipe(templateStream)
            return templateStream
        }
   }

renderToStream傳入一個(gè)Vue對象實(shí)例,返回的是一個(gè)RenderStream對象的實(shí)例,我們來看下RenderStream對象的實(shí)現(xiàn):

src/server/create-stream.js

// 繼承了node的可讀流stream.Readable;必須提供一個(gè)_read方法從底層資源抓取數(shù)據(jù)。通過Push(chunk)調(diào)用_read。向隊(duì)列插入數(shù)據(jù),push(null)結(jié)束
export default class RenderStream extends stream.Readable {
  buffer: string; // 緩沖區(qū)字符串
  render: (write: Function, done: Function) => void; // 保存?zhèn)魅氲膔ender方法,最后分別定義了write和end方法
  expectedSize: number;  // 讀取隊(duì)列中插入內(nèi)容的大小
  write: Function;
  next: Function;
  end: Function;
  done: boolean;

  constructor (render: Function) {
    super() // super調(diào)用父類的構(gòu)造函數(shù)
    this.buffer = ""
    this.render = render
    this.expectedSize = 0
    
    // 首先把text拼接到buffer緩沖區(qū),然后判斷buffer.length,如果大于expecteSize,用this.text保存                
    //text,同時(shí)調(diào)用this.pushBySize把緩沖區(qū)內(nèi)容推入讀取隊(duì)列中。
    this.write = createWriteFunction((text, next) => {
      const n = this.expectedSize
      this.buffer += text
      if (this.buffer.length >= n) {
        this.next = next
        this.pushBySize(n)
        return true // we will decide when to call next
      }
      return false
    }, err => {
      this.emit("error", err)
    })

     // 渲染完成后;我們應(yīng)該把最后一個(gè)緩沖區(qū)推掉.
    this.end = () => {
      this.done = true // 標(biāo)志組件的渲染已經(jīng)完畢,然后調(diào)用push將緩沖區(qū)剩余內(nèi)容推入讀取隊(duì)列中
      this.push(this.buffer) //把緩沖區(qū)剩余內(nèi)容推入讀取隊(duì)列中
    }
  }

  //截取buffer緩沖區(qū)前n個(gè)長度的數(shù)據(jù),推入到讀取隊(duì)列中,同時(shí)更新buffer緩沖區(qū),刪除前n條數(shù)據(jù)
  pushBySize (n: number) {
    const bufferToPush = this.buffer.substring(0, n)
    this.buffer = this.buffer.substring(n)
    this.push(bufferToPush)
  }

  tryRender () {
    try {
      this.render(this.write, this.end) // 開始渲染組件,在初始化RenderStream方法時(shí)傳入。
    } catch (e) {
      this.emit("error", e)
    }
  }

  tryNext () {
    try {
      this.next() // 繼續(xù)渲染組件
    } catch (e) {
      this.emit("error", e)
    }
  }

  _read (n: number) {
    this.expectedSize = n
    // 可能最后一個(gè)塊增加了緩沖區(qū)到大于2 n,這意味著我們需要通過多次讀取調(diào)用來消耗它
    // down to < n.
    if (isTrue(this.done)) { // 如果為true,則表示渲染完畢;
      this.push(null) //觸發(fā)結(jié)束信號
      return
    }
    if (this.buffer.length >= n) { // 緩沖區(qū)字符串長度足夠,把緩沖區(qū)內(nèi)容推入讀取隊(duì)列。
      this.pushBySize(n)
      return
    }
    if (isUndef(this.next)) {
         this.tryRender() //false,開始渲染組件
    } else {
         this.tryNext() //繼續(xù)渲染組件
    }
  }
}

回顧一下,首先調(diào)用renderToStream(new Vue(option))創(chuàng)建好stream對象后,通過stream.pipe()方法把數(shù)據(jù)發(fā)送到一個(gè)WritableStream中,會觸發(fā)RenderToStream內(nèi)部_read方法的調(diào)用,不斷把渲染的組件推入讀取隊(duì)列中,這個(gè)WritableStream就可以不斷地讀取到組件的數(shù)據(jù),然后輸出,這樣就實(shí)現(xiàn)了流式服務(wù)端渲染技術(shù)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/93079.html

相關(guān)文章

  • 珠峰前架構(gòu)師培養(yǎng)計(jì)劃

    摘要:公司的招聘要求都提到了至少熟悉其中一種前端框架,有前端工程化與模塊化開發(fā)實(shí)踐經(jīng)驗(yàn)相關(guān)字眼。我們主要從端公眾號移動端小程序三大平臺進(jìn)行前端的技術(shù)選型,并來說說選其技術(shù)的幾大優(yōu)勢。技術(shù)的優(yōu)勢互聯(lián)網(wǎng)前端大潮后,前端出現(xiàn)了大框架,分別是與。 1、技術(shù)選型的背景前端技術(shù)發(fā)展日新月異,互聯(lián)網(wǎng)上出現(xiàn)的新型框架也比較多,如何讓新招聘的人員...

    ccj659 評論0 收藏0
  • 細(xì)說 Vue 組件的服務(wù)渲染

    摘要:所以,這次就來聊聊組件的服務(wù)器端渲染。這種模式下,后端只提供接口,傳統(tǒng)的服務(wù)器端路由模板渲染則都有層接管。這樣,前端開發(fā)人員可以自由的決定哪些組件需要在服務(wù)器端渲染,哪些組件可以放在客戶端渲染,前后端完全解耦,但又保留了服務(wù)器端渲染的功能。 細(xì)說 Vue 組件的服務(wù)器端渲染 聲明:需要讀者對 NodeJs、Vue 服務(wù)器端渲染有一定的了解 現(xiàn)在,前后端分離與客戶端渲染已經(jīng)成為前端開發(fā)的...

    reclay 評論0 收藏0
  • vue項(xiàng)目技術(shù)選型、開發(fā)工具、周邊生態(tài)

    摘要:有目錄結(jié)構(gòu)書寫方式組件集成項(xiàng)目構(gòu)建等的約束,整個(gè)應(yīng)用中是沒有文件的,所有的響應(yīng)都是動態(tài)渲染的,包括里面的元信息路徑等。更多參考細(xì)說后端模板渲染客戶端渲染中間層服務(wù)器端渲染開發(fā)工具開發(fā)時(shí)主要會用到的工具。 vue 前端項(xiàng)目技術(shù)選型、開發(fā)工具、周邊生態(tài) 聲明:這不是一篇介紹 Vue 基礎(chǔ)知識的文章,需要熟悉 Vue 相關(guān)知識 主架構(gòu):vue, vue-router, vuex UI 框...

    Awbeci 評論0 收藏0
  • vue項(xiàng)目技術(shù)選型、開發(fā)工具、周邊生態(tài)

    摘要:有目錄結(jié)構(gòu)書寫方式組件集成項(xiàng)目構(gòu)建等的約束,整個(gè)應(yīng)用中是沒有文件的,所有的響應(yīng)都是動態(tài)渲染的,包括里面的元信息路徑等。更多參考細(xì)說后端模板渲染客戶端渲染中間層服務(wù)器端渲染開發(fā)工具開發(fā)時(shí)主要會用到的工具。 vue 前端項(xiàng)目技術(shù)選型、開發(fā)工具、周邊生態(tài) 聲明:這不是一篇介紹 Vue 基礎(chǔ)知識的文章,需要熟悉 Vue 相關(guān)知識 主架構(gòu):vue, vue-router, vuex UI 框...

    enali 評論0 收藏0
  • 2017前技術(shù)總結(jié):收獲非淺,但仍需進(jìn)步

    摘要:平臺主要功能如下支持客戶端渲染和服務(wù)端渲染微信登錄鑒權(quán)頁面組件增刪改查,復(fù)制移動等圖片上傳微信文章一鍵復(fù)制等等動態(tài)組件的配置原理之后專門用一篇文章詳細(xì)寫吧持續(xù)集成這個(gè)其實(shí)也不算是項(xiàng)目,算是前端的工具。 2017年算是踏入真正的前端的一年,從實(shí)習(xí)到去年,說是前端的崗位,但卻因?yàn)閷?shí)習(xí)生的身份、公司技術(shù)不夠等原因,一直停留在傳統(tǒng)的html+css+jq,那時(shí)候感覺前端的世界在翻天覆地地變化,...

    txgcwm 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<