亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Vue渲染函數詳解

2019-11-19 15:26:16
字體:
來源:轉載
供稿:網友

前面的話

Vue 推薦在絕大多數情況下使用 template 來創建HTML。然而在一些場景中,真的需要 JavaScript 的完全編程的能力,這就是 render 函數,它比 template 更接近編譯器。本文將詳細介紹Vue渲染函數

引入

下面是一個例子,如果要實現類似下面的效果。其中,H標簽可替換

<h1> <a name="hello-world" href="#hello-world" rel="external nofollow" rel="external nofollow" > Hello world! </a></h1>

在 HTML 層,像下面這樣定義來組件接口:

<anchored-heading :level="1">Hello world!</anchored-heading>

當開始寫一個通過 level prop 動態生成 heading 標簽的組件,可能很快想到這樣實現:

<script type="text/x-template" id="anchored-heading-template"> <h1 v-if="level === 1"> <slot></slot> </h1> <h2 v-else-if="level === 2"> <slot></slot> </h2> <h3 v-else-if="level === 3"> <slot></slot> </h3> <h4 v-else-if="level === 4"> <slot></slot> </h4> <h5 v-else-if="level === 5"> <slot></slot> </h5> <h6 v-else-if="level === 6"> <slot></slot> </h6></script>

JS代碼如下

Vue.component('anchored-heading', { template: '#anchored-heading-template', props: { level: {  type: Number,  required: true } }})

在這種場景中使用 template 并不是最好的選擇:首先代碼冗長,為了在不同級別的標題中插入錨點元素,需要重復地使用 <slot></slot>

雖然模板在大多數組件中都非常好用,但是在這里它就不是很簡潔的了。那么,來嘗試使用 render 函數重寫上面的例子:

<div id="example"> <anchored-heading :level="2"><a name="hello-world" href="#hello-world" rel="external nofollow" rel="external nofollow" >Hello world!</a></anchored-heading></div><script src="vue.js"></script><script>Vue.component('anchored-heading', { render: function (createElement) { return createElement(  'h' + this.level, // tag name 標簽名稱  this.$slots.default // 子組件中的陣列 ) }, props: { level: {  type: Number,  required: true } }}) new Vue({ el: '#example'})</script>

這樣的代碼精簡很多,但是需要非常熟悉 Vue 的實例屬性。在這個例子中,需要知道當不使用 slot 屬性向組件中傳遞內容時,比如 anchored-heading 中的 Hello world!,這些子元素被存儲在組件實例中的 $slots.default中

虛擬DOM

在深入渲染函數之前,了解一些瀏覽器的工作原理是很重要的。以下面這段 HTML 為例:

<div> <h1>My title</h1> Some text content <!-- TODO: Add tagline --></div>

當瀏覽器讀到這些代碼時,它會建立一個“DOM 節點”樹來保持追蹤,如同會畫一張家譜樹來追蹤家庭成員的發展一樣。HTML 的 DOM 節點樹如下圖所示:

DOM Tree Visualization

每個元素都是一個節點。每段文字也是一個節點。甚至注釋也都是節點。一個節點就是頁面的一個部分。就像家譜樹一樣,每個節點都可以有子節點 (也就是說每個部分可以包含其它的一些部分)

高效的更新所有這些節點會是比較困難的,不過所幸不必再手動完成這個工作了。只需要告訴 Vue 希望頁面上的 HTML 是什么,這可以是在一個模板里:

<h1>{{ blogTitle }}</h1>

或者一個渲染函數里:

render: function (createElement) { return createElement('h1', this.blogTitle)}

在這兩種情況下,Vue 都會自動保持頁面的更新,即便 blogTitle 發生了改變。

【虛擬DOM】

Vue 通過建立一個虛擬 DOM 對真實 DOM 發生的變化保持追蹤

return createElement('h1', this.blogTitle)
createElement 到底會返回什么呢?其實不是一個實際的 DOM 元素。它更準確的名字可能是 createNodeDescription,因為它所包含的信息會告訴 Vue 頁面上需要渲染什么樣的節點,及其子節點。我們把這樣的節點描述為“虛擬節點 (Virtual DOM)”,也常簡寫它為“VNode”?!疤摂M DOM”是我們對由 Vue 組件樹建立起來的整個 VNode 樹的稱呼

createElement

接下來需要熟悉的是如何在 createElement 函數中生成模板。這里是 createElement 接受的參數:

// @returns {VNode}createElement( // {String | Object | Function} // 一個 HTML 標簽字符串,組件選項對象,或者一個返回值類型為 String/Object 的函數,必要參數 'div', // {Object} // 一個包含模板相關屬性的數據對象 // 這樣,可以在 template 中使用這些屬性??蛇x參數。 { }, // {String | Array} // 子節點 (VNodes),由 `createElement()` 構建而成, // 或簡單的使用字符串來生成“文本節點”??蛇x參數。 [ '先寫一些文字', createElement('h1', '一則頭條'), createElement(MyComponent, {  props: {  someProp: 'foobar'  } }) ])

【深入data對象】

正如在模板語法中,v-bind:class 和 v-bind:style ,會被特別對待一樣,在 VNode 數據對象中,下列屬性名是級別最高的字段。該對象也允許綁定普通的 HTML 特性,就像 DOM 屬性一樣,比如 innerHTML (這會取代 v-html 指令)

{ // 和`v-bind:class`一樣的 API 'class': { foo: true, bar: false }, // 和`v-bind:style`一樣的 API style: { color: 'red', fontSize: '14px' }, // 正常的 HTML 特性 attrs: { id: 'foo' }, // 組件 props props: { myProp: 'bar' }, // DOM 屬性 domProps: { innerHTML: 'baz' }, // 事件監聽器基于 `on` // 所以不再支持如 `v-on:keyup.enter` 修飾器 // 需要手動匹配 keyCode。 on: { click: this.clickHandler }, // 僅對于組件,用于監聽原生事件,而不是組件內部使用 `vm.$emit` 觸發的事件。 nativeOn: { click: this.nativeClickHandler }, // 自定義指令。注意事項:不能對綁定的舊值設值 // Vue 會持續追蹤 directives: [ {  name: 'my-custom-directive',  value: '2',  expression: '1 + 1',  arg: 'foo',  modifiers: {  bar: true  } } ], // Scoped slots in the form of // { name: props => VNode | Array<VNode> } scopedSlots: { default: props => createElement('span', props.text) }, // 如果組件是其他組件的子組件,需為插槽指定名稱 slot: 'name-of-slot', // 其他特殊頂層屬性 key: 'myKey', ref: 'myRef'}

【完整示例】

有了這些知識,現在可以完成最開始想實現的組件:

var getChildrenTextContent = function (children) { return children.map(function (node) { return node.children  ? getChildrenTextContent(node.children)  : node.text }).join('')}Vue.component('anchored-heading', { render: function (createElement) { // create kebabCase id var headingId = getChildrenTextContent(this.$slots.default)  .toLowerCase()  .replace(//W+/g, '-')  .replace(/(^/-|/-$)/g, '') return createElement(  'h' + this.level,  [  createElement('a', {   attrs: {   name: headingId,   href: '#' + headingId   }  }, this.$slots.default)  ] ) }, props: { level: {  type: Number,  required: true } }})

【約束】

組件樹中的所有 VNodes 必須是唯一的。這意味著,下面的 render function 是無效的:

render: function (createElement) { var myParagraphVNode = createElement('p', 'hi') return createElement('div', [ // 錯誤-重復的 VNodes myParagraphVNode, myParagraphVNode ])}

如果真的需要重復很多次的元素/組件,可以使用工廠函數來實現。例如,下面這個例子 render 函數完美有效地渲染了 20 個重復的段落:

render: function (createElement) { return createElement('div', Array.apply(null, { length: 20 }).map(function () {  return createElement('p', 'hi') }) )}

JS代替模板

【v-if和v-for】

由于使用原生的 JavaScript 來實現某些東西很簡單,Vue 的 render 函數沒有提供專用的 API。比如,template 中的 v-if 和 v-for:

<ul v-if="items.length"> <li v-for="item in items">{{ item.name }}</li></ul><p v-else>No items found.</p>

這些都會在 render 函數中被 JavaScript 的 if/else 和 map 重寫:

render: function (createElement) { if (this.items.length) { return createElement('ul', this.items.map(function (item) {  return createElement('li', item.name) })) } else { return createElement('p', 'No items found.') }}

【v-model】

ender 函數中沒有與 v-model 相應的 api,必須自己來實現相應的邏輯:

render: function (createElement) { var self = this return createElement('input', { domProps: {  value: self.value }, on: {  input: function (event) {  self.value = event.target.value  self.$emit('input', event.target.value)  } } })}

這就是深入底層要付出的,盡管麻煩了一些,但相對于 v-model 來說,可以更靈活地控制

【事件&按鍵修飾符】

對于 .passive、.capture 和 .once事件修飾符,Vue 提供了相應的前綴可以用于 on:

Modifier(s)  Prefix.passive    &.capture    !.once      ~.capture.once   or.once.capture ~!

下面是一個例子

on: { '!click': this.doThisInCapturingMode, '~keyup': this.doThisOnce, `~!mouseover`: this.doThisOnceInCapturingMode}

對于其他的修飾符,前綴不是很重要,因為可以直接在事件處理函數中使用事件方法:

Modifier(s)  Equivalent in Handler.stop  event.stopPropagation().prevent  event.preventDefault().self  if (event.target !== event.currentTarget) returnKeys:.enter, .13  if (event.keyCode !== 13) return (...)Modifiers Keys:.ctrl, .alt, .shift, .meta  if (!event.ctrlKey) return (...)

下面是一個使用所有修飾符的例子:

on: { keyup: function (event) { // 如果觸發事件的元素不是事件綁定的元素 // 則返回 if (event.target !== event.currentTarget) return // 如果按下去的不是 enter 鍵或者 // 沒有同時按下 shift 鍵 // 則返回 if (!event.shiftKey || event.keyCode !== 13) return // 阻止 事件冒泡 event.stopPropagation() // 阻止該元素默認的 keyup 事件 event.preventDefault() // ... }}

【插槽】

可以從 this.$slots 獲取 VNodes 列表中的靜態內容:

render: function (createElement) { // `<div><slot></slot></div>` return createElement('div', this.$slots.default)}

還可以從 this.$scopedSlots 中獲得能用作函數的作用域插槽,這個函數返回 VNodes:

render: function (createElement) { // `<div><slot :text="msg"></slot></div>` return createElement('div', [ this.$scopedSlots.default({  text: this.msg }) ])}

如果要用渲染函數向子組件中傳遞作用域插槽,可以利用 VNode 數據中的 scopedSlots 域:

render (createElement) { return createElement('div', [ createElement('child', {  // pass `scopedSlots` in the data object  // in the form of { name: props => VNode | Array<VNode> }  scopedSlots: {  default: function (props) {   return createElement('span', props.text)  }  } }) ])}

JSX

如果寫了很多 render 函數,可能會覺得痛苦

createElement( 'anchored-heading', { props: {  level: 1 } }, [ createElement('span', 'Hello'), ' world!' ])

特別是模板如此簡單的情況下:

<anchored-heading :level="1"> <span>Hello</span> world!</anchored-heading>

這就是為什么會有一個 Babel 插件,用于在 Vue 中使用 JSX 語法的原因,它可以讓我們回到更接近于模板的語法上

import AnchoredHeading from './AnchoredHeading.vue'new Vue({ el: '#demo', render (h) { return (  <AnchoredHeading level={1}>  <span>Hello</span> world!  </AnchoredHeading> ) }})

[注意]將 h 作為 createElement 的別名是 Vue 生態系統中的一個通用慣例,實際上也是 JSX 所要求的,如果在作用域中 h 失去作用,在應用中會觸發報錯

函數式組件

之前創建的錨點標題組件是比較簡單,沒有管理或者監聽任何傳遞給它的狀態,也沒有生命周期方法。它只是一個接收參數的函數。

在這個例子中,我們標記組件為 functional,這意味它是無狀態 (沒有 data),無實例 (沒有 this 上下文)

一個 函數式組件 就像這樣:

Vue.component('my-component', { functional: true, // 為了彌補缺少的實例 // 提供第二個參數作為上下文 render: function (createElement, context) { // ... }, // Props 可選 props: { // ... }})

[注意]在 2.3.0 之前的版本中,如果一個函數式組件想要接受 props,則 props 選項是必須的。在 2.3.0 或以上的版本中,你可以省略 props 選項,所有組件上的屬性都會被自動解析為 props

組件需要的一切都是通過上下文傳遞,包括:

props:提供 props 的對象children: VNode 子節點的數組slots: slots 對象data:傳遞給組件的 data 對象parent:對父組件的引用listeners: (2.3.0+) 一個包含了組件上所注冊的 v-on 偵聽器的對象。這只是一個指向 data.on 的別名。injections: (2.3.0+) 如果使用了 inject 選項,則該對象包含了應當被注入的屬性。

在添加 functional: true 之后,錨點標題組件的 render 函數之間簡單更新增加 context 參數,this.$slots.default 更新為 context.children,之后this.level 更新為 context.props.level。

因為函數式組件只是一個函數,所以渲染開銷也低很多。然而,對持久化實例的缺乏也意味著函數式組件不會出現在 Vue devtools 的組件樹里。

在作為包裝組件時它們也同樣非常有用,比如,當需要做這些時:

1、程序化地在多個組件中選擇一個

2、在將 children, props, data 傳遞給子組件之前操作它們

下面是一個依賴傳入 props 的值的 smart-list 組件例子,它能代表更多具體的組件:

var EmptyList = { /* ... */ }var TableList = { /* ... */ }var OrderedList = { /* ... */ }var UnorderedList = { /* ... */ }Vue.component('smart-list', { functional: true, render: function (createElement, context) { function appropriateListComponent () {  var items = context.props.items  if (items.length === 0)   return EmptyList  if (typeof items[0] === 'object') return TableList  if (context.props.isOrdered)  return OrderedList  return UnorderedList } return createElement(  appropriateListComponent(),  context.data,  context.children ) }, props: { items: {  type: Array,  required: true }, isOrdered: Boolean }})

【slots()和children對比】

為什么同時需要 slots() 和 children。slots().default 不是和 children 類似的嗎?在一些場景中,是這樣,但是如果是函數式組件和下面這樣的 children 呢?

<my-functional-component> <p slot="foo"> first </p> <p>second</p></my-functional-component>

對于這個組件,children 會給兩個段落標簽,而 slots().default 只會傳遞第二個匿名段落標簽,slots().foo 會傳遞第一個具名段落標簽。同時擁有 children 和 slots() ,因此可以選擇讓組件通過 slot() 系統分發或者簡單的通過 children 接收,讓其他組件去處理

模板編譯

Vue 的模板實際是編譯成了 render 函數。這是一個實現細節,通常不需要關心。下面是一個使用 Vue.compile 來實時編譯模板字符串的簡單 demo:

<my-functional-component> <p slot="foo"> first </p> <p>second</p></my-functional-component>

render:

function anonymous() { with(this){return _c('div',[_m(0),(message)?_c('p',[_v(_s(message))]):_c('p',[_v("No message.")])])}}

staticRenderFns:

_m(0): function anonymous() { with(this){return _c('header',[_c('h1',[_v("I'm a template!")])])}}

以上這篇Vue渲染函數詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97精品在线观看| 91产国在线观看动作片喷水| 欧美wwwxxxx| 国产91网红主播在线观看| 国产视频丨精品|在线观看| 国产精品一区二区三| 欧美性猛交xxxx乱大交极品| 亚洲电影在线看| 日本国产一区二区三区| 狠狠躁夜夜躁久久躁别揉| 亚洲欧美在线免费观看| 国产成人精品电影久久久| 最好看的2019年中文视频| 久久中文字幕视频| 午夜精品久久久久久久久久久久久| 欧美成人h版在线观看| 日本在线观看天堂男亚洲| 欧美日韩第一页| 国产午夜精品理论片a级探花| 亚洲国产美女精品久久久久∴| 亚洲欧美变态国产另类| 热99久久精品| 久久久久久久久久国产| 精品久久久久久久中文字幕| 亚洲精品狠狠操| 国产精品一区二区在线| 九九热99久久久国产盗摄| 精品日本高清在线播放| 欧美日韩在线一区| 欧美成人黄色小视频| 日韩人体视频一二区| 久久久天堂国产精品女人| 亚洲第一在线视频| 成人精品福利视频| 国产亚洲精品一区二555| 在线播放国产一区中文字幕剧情欧美| 亚洲欧美日韩一区二区在线| 麻豆乱码国产一区二区三区| 国产一区在线播放| 超碰精品一区二区三区乱码| 久久久久www| 国产91精品久久久久久| 欧美人与性动交| 亚洲欧美国产另类| 亚洲精品自拍视频| 欧美自拍视频在线| 日韩电视剧在线观看免费网站| 欧美精品久久久久久久久| 欧美激情videoshd| 国产玖玖精品视频| 色天天综合狠狠色| 在线观看欧美日韩国产| 少妇高潮久久77777| 亚洲人成在线观看网站高清| 国产成人高潮免费观看精品| 亚洲激情视频网| 国产大片精品免费永久看nba| 亚洲无限乱码一二三四麻| 91久久精品日日躁夜夜躁国产| 欧美成人激情在线| 日韩视频第一页| 国内外成人免费激情在线视频网站| 成人免费淫片aa视频免费| 亚洲韩国日本中文字幕| 亚洲美女在线看| 欧美网站在线观看| 国产91在线高潮白浆在线观看| 精品视频9999| 中文.日本.精品| 成人精品视频99在线观看免费| 日韩免费av一区二区| 欧美日韩在线影院| 欧美性生交xxxxxdddd| 国产成人久久久精品一区| 久久躁狠狠躁夜夜爽| 国产精品www网站| 亚洲码在线观看| 亚洲在线观看视频| 亚洲电影av在线| 亚洲免费福利视频| 亚洲网站在线观看| 亚洲日本欧美日韩高观看| 日韩电影免费观看中文字幕| 欧美成人在线免费| 欧洲美女7788成人免费视频| 亚洲人线精品午夜| 欧美午夜www高清视频| 97国产suv精品一区二区62| 2019中文字幕在线| 成人免费午夜电影| 欧美日韩国产va另类| 久久九九亚洲综合| 在线日韩第一页| 日韩视频欧美视频| 日韩精品在线免费观看| 中文字幕九色91在线| 色偷偷偷亚洲综合网另类| 欧美电影免费观看高清完整| 日本19禁啪啪免费观看www| 影音先锋日韩有码| 日韩精品久久久久| 欧美日韩国产影院| 日韩国产欧美区| 国产精品久久久久久网站| 麻豆一区二区在线观看| 琪琪亚洲精品午夜在线| 国产精品99久久久久久久久| 国产精品久久久久久久久久尿| 精品中文字幕久久久久久| 国产成人精品视频在线| 欧美高清在线视频观看不卡| 91精品国产色综合久久不卡98| 5252色成人免费视频| 亚洲成人中文字幕| 国产一区视频在线| 综合网中文字幕| 国产精品嫩草视频| 热久久美女精品天天吊色| 国产精品福利网站| 色综合久久中文字幕综合网小说| 国产一区二区三区高清在线观看| 国产精品伦子伦免费视频| 日本精品中文字幕| 亚洲欧美成人在线| 国产91色在线播放| 国产精品你懂得| 亚洲成人精品视频在线观看| 日本中文字幕久久看| 亚洲欧美一区二区三区四区| 国产精品自拍小视频| 成人激情视频在线播放| 亚洲欧美日韩在线一区| 欧美激情奇米色| 欧美日韩国产黄| 中文字幕亚洲欧美一区二区三区| 国产精品免费看久久久香蕉| 欧美另类69精品久久久久9999| 欧美激情精品久久久久久变态| 欧美在线视频导航| 欧美成人免费在线视频| 国产日韩欧美黄色| 疯狂做受xxxx欧美肥白少妇| 久久五月天色综合| 欧美另类老女人| 亚洲男人天堂古典| 隔壁老王国产在线精品| 亚洲美女av在线播放| 91九色国产在线| 亚洲一区二区三区xxx视频| 亚洲电影免费观看高清完整版在线观看| 日韩免费av一区二区| 26uuu另类亚洲欧美日本一| 91视频免费网站| 欧美大胆a视频| 亚洲第一在线视频| 国产在线播放不卡| 欧美精品videos| 亚洲国产成人在线播放| 成人激情电影一区二区| 亚洲国产另类 国产精品国产免费| 欧美日韩国产一区二区| 欧美在线一级视频| 久久99亚洲精品| 欧美极度另类性三渗透|