亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

vue渲染函數(shù)render的使用示例詳解

 更新時間:2025年06月21日 10:22:16   作者:沖浪的鵬多多  
Vue推薦使用模板,但需用render函數(shù)在高級場景下直接生成VNode,通過createElement參數(shù)控制虛擬DOM,適合復雜動態(tài)UI和高階組件,性能更高但需權衡可讀性,本文給大家介紹vue渲染函數(shù)render的使用,感興趣的朋友一起看看吧

1. 前言

Vue 推薦在絕大多數(shù)情況下使用模板來創(chuàng)建你的 HTML。然而在一些場景中,你真的需要 JavaScript 的完全編程的能力。這時你可以用渲染函數(shù) render,它比模板更接近編譯器,直接生成 VNode 虛擬 DOM。

下面是一個對比例子,通過 level prop 來動態(tài)生成標題的組件

  • template 寫法
<template>
	<h1 :class="[`title${level}`]" v-if="level === 1">
		<slot></slot>
	</h1>
	<h2 :class="[`title${level}`]" v-else-if="level === 2">
		<slot></slot>
	</h2>
	<h3 :class="[`title${level}`]" v-else-if="level === 3">
		<slot></slot>
	</h3>
	<h4 :class="[`title${level}`]" v-else-if="level === 4">
		<slot></slot>
	</h4>
	<h5 :class="[`title${level}`]" v-else-if="level === 5">
		<slot></slot>
	</h5>
	<h6 :class="[`title${level}`]" v-else-if="level === 6">
		<slot></slot>
	</h6>
</template>
<script>
	export default {
		name: 'Title',
		props: {
			level: {
				type: Number,
				required: true,
			},
		},
	}
</script>
  • render 寫法
export default {
	name: 'Title',
	props: {
		level: {
			type: Number,
			required: true,
		},
	},
	render(createElement) {
		const children = this.$slots.default || [];
		return createElement(`h${this.level}`, { class: [`title${this.level}`] }, children)
	},
}

2. 參數(shù)和語法

當使用 render 函數(shù)描述虛擬 DOM 時,vue 提供一個構建虛擬 DOM 的函數(shù),叫 createElement,約定的簡寫為 h。

2-1. 參數(shù)

createElement 函數(shù)有三個參數(shù):

  • 必填。一個 HTML 標簽名、組件名,類型:{String | Object | Function}(也可以是組件選項對象或返回組件選項的函數(shù))
  • 可選。一個與模板中屬性對應的數(shù)據(jù)對象,也就是與模板中屬性對應的數(shù)據(jù)對象,包含組件屬性、DOM 屬性、事件等。
  • 可選。子級虛擬節(jié)點 (VNodes),由 createElement() 構建而成,也可以使用字符串來生成"文本虛擬"節(jié)點。

語法:

createElement(TagName,Option,Content)

第一個參數(shù)也可以是組件選項對象或返回組件選項的函數(shù),例如:

// :
createElement({
  template: '<div>{{ msg }}</div>',
  data() {
    return { msg: 'Hello' };
  }
});

2-2. Option數(shù)據(jù)對象

createElement 函數(shù)的第二個參數(shù),是一個與模板中屬性對應的數(shù)據(jù)對象,也就是組件的屬性。

{
  // 與 `v-bind:class` 的 API 相同,接受一個字符串、對象或字符串和對象組成的數(shù)組
  'class': {
    foo: true,
    bar: false
  },
  // 與 `v-bind:style` 的 API 相同,接受一個字符串、對象,或對象組成的數(shù)組
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 普通的 HTML attribute
  attrs: {
    id: 'foo'
  },
  // 組件 prop
  props: {
    myProp: 'bar'
  },
  // DOM property
  domProps: {
    innerHTML: 'baz'
  },
  // 事件監(jiān)聽器在 `on` 內,但不再支持如 `v-on:keyup.enter` 這樣的修飾器。需要在處理函數(shù)中手動檢查 keyCode。
  on: {
    click: this.clickHandler
  },
  // 僅用于組件,用于監(jiān)聽原生事件,而不是組件內部使用`vm.$emit` 觸發(fā)的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定義指令。注意,你無法對 `binding` 中的 `oldValue`賦值,因為 Vue 已經自動為你進行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // 作用域插槽的格式為{ name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果組件是其它組件的子組件,需為插槽指定名稱
  slot: 'name-of-slot',
  // 其它特殊頂層 property
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函數(shù)中給多個元素都應用了相同的 ref 名,那么 `$refs.myRef` 會變成一個數(shù)組。
  refInFor: true
}

下面是一個 Button 按鈕的例子:

export default {
	name: 'Button',
	props: {
		text: {
			type: String,
			required: true,
		},
	},
	methods: {
    handleClick() {
      console.log('按鈕被點擊了!');
    }
  },
	render(h) {
		return h(
			'div',
			{
				class: 'button-wrapper',
				on: {
					click: handleClick,
				},
			},
			[h('span', { class: 'button-text' }, this.text)]
		)
	},
}

2-3. 指令變化

指令的寫法發(fā)生了變化,常用的 v-if/else,還有 v-for,v-model,事件修飾符等都有變化。

2-3-1. v-if和else

  • 模板寫法:
<ul v-if="items.length">
  <li v-for="item in items" :key="item">{{ item }}</li>
</ul>
<p v-else>空空如也</p>
  • render 函數(shù)寫法:
export default {
	// 省略......
	render(h) {
		if (this.items.length) {
			return h('ul', this.items.map((item) => h('li', { key: item }, item)));
		} else {
			return h('p', '空空如也');
		}
	}
}

2-3-2. v-model

render 函數(shù)中沒有與 v-model 的直接對應,需要自己實現(xiàn)相應的邏輯。

  • 模板寫法
<input v-model="message" placeholder="請輸入" />
  • render 函數(shù)寫法
export default {
	// 省略......
	props: ['message'],
	render(h) {
		const self = this
		return h('input', {
			domProps: {
				value: self.message,
			},
			on: {
				input: (event) => {
					// 組件綁定使用了sync語法糖
					self.$emit('update:message', event.target.value)
				},
			},
		})
	},
}

2-3-3. 事件按鍵修飾符

對于事件修飾符,vue 官方提供了部分的特殊前綴來處理,其余的,則需要自己在函數(shù)中處理。

修飾符前綴說明
.passive&滾動事件的默認行為將會立即觸發(fā),而不是等到事件觸發(fā)完再觸發(fā)
.capture!捕獲模式
.once~只觸發(fā)一次回調
.capture.once~!只觸發(fā)一次回調 的 捕獲模式

例子如下:

on: {
  '!click': this.func,
  '~keyup': this.func,
  '~!mouseover': this.func,
  keyup: (event) => {
    // 如果觸發(fā)事件的元素不是事件綁定的元素
    if (event.target !== event.currentTarget) return
    // 如果按下去的不是 enter 鍵或者沒有同時按下 shift 鍵
    f (!event.shiftKey || event.keyCode !== 13) return
    // 阻止 事件冒泡
    event.stopPropagation()
    // 阻止該元素默認的 keyup 事件
    event.preventDefault()
  }
}
修飾符操作說明
.stopevent.stopPropagation()阻止冒泡
.preventevent.preventDefault()阻止元素發(fā)生默認的行為
.selfif (event.target !== event.currentTarget) return自身觸發(fā)
.enterif (event.keyCode !== 13) return按鍵匹配
.shiftif (!event.shiftKey) return按鍵匹配

2-3-4. 插槽

可以通過 this.$slots 訪問靜態(tài)插槽的內容,每個插槽都是一個 VNode 數(shù)組:

render: function (h) {
  return h('div', this.$slots.default)
}

也可以通過 this.$scopedSlots 訪問作用域插槽,每個作用域插槽都是一個返回若干 VNode 的函數(shù):

props: ['message'],
render: function (h) {
  return h('div', [
    this.$scopedSlots.default({
      text: this.message
    })
  ])
}

如果要用渲染函數(shù)向子組件中傳遞作用域插槽,可以利用 VNode 數(shù)據(jù)對象中的 scopedSlots 字段:

render: function (h) {
  return h('div', [
    h('child', {
      // 在數(shù)據(jù)對象中傳遞 `scopedSlots` 格式為 { name: props => VNode | Array<VNode> }
      scopedSlots: {
        default: function (props) {
          return h('span', props.text)
        }
      }
    })
  ])
}

例子如下:

  • list.js
const handleClick = (index) => {
	return () => {
		console.log(`${index}按鈕被點擊了!`)
	}
}
export default {
	name: 'List',
	props: {
		data: {
			type: Array,
			required: true,
			default: () => [],
		},
	},
	render(h) {
		if (this.data.length > 0) {
			return h(
				'ul',
				{
					class: 'ul-box',
				},
				this.data.map((item, i) => {
					return h(
						'li',
						{
							class: 'li-box',
							on: {
								click: handleClick(i),
							},
						},
						this.$scopedSlots.default({
							data: `${item}+${i}`,
						})
					)
				})
			)
		}
		return h('div', '暫無數(shù)據(jù)')
	},
}
  • home.vue
List<template>
  <div class="home_box">
    <List :data="list">
      <template slot-scope="scope">
        <span>{{ scope.data }}</span>
      </template>
    </List>
  </div>
</template>
<script>
import List from '@/views/render/list.js'
export default {
  name: 'Home',
  components: { List },
  data() {
    return {
      list: [100, 200, 300, 400, 500]
    }
  }
}
</script>

3. 編譯jsx

如果你寫了很多 render 函數(shù),可能會覺得這樣的代碼寫起來很痛苦,并且可讀性也不好。這時候可以使用 JSX 插件:傳送門

  • 使用JSX的render函數(shù)示例:
render() {
  return (
    <div class={`title${this.level}`}>
      {this.$slots.default}
    </div>
  );
}

4. 典型應用場景

render 函數(shù)非常適合實現(xiàn)高階組件以及復雜的動態(tài) UI,因為它可以動態(tài)創(chuàng)建和組合組件。

4.1 高階組件示例

// 動態(tài)加載組件
export default function asyncComponentLoader(componentName) {
  return {
    name: `AsyncLoader(${componentName})`,
    data() {
      return {
        Component: null,
        loading: true,
        error: null,
      };
    },
    async created() {
      try {
        const componentModule = await import(`@/components/${componentName}.vue`);
        this.Component = componentModule.default || componentModule;
      } catch (err) {
        this.error = err.message;
      } finally {
        this.loading = false;
      }
    },
    render(h) {
      if (this.loading) return h('div', 'Loading...');
      if (this.error) return h('div', { style: { color: 'red' } }, this.error);
      if (this.Component) return h(this.Component, { props: this.$props });
      return h('div', 'Component not found');
    },
  };
}

4.2. 復雜的動態(tài)UI示例

// 動態(tài)表單生成器
export default {
  name: 'DynamicForm',
  props: {
    formConfig: {
      type: Array,
      required: true,
    },
    formData: {
      type: Object,
      default: () => ({}),
    },
  },
  methods: {
    handleInput(field, event) {
      this.$emit('input', { ...this.formData, [field]: event.target.value });
    },
  },
  render(h) {
    const formItems = this.formConfig.map((field) => {
      switch (field.type) {
        case 'text':
          return h('div', [
            h('label', field.label),
            h('input', {
              attrs: { type: 'text', placeholder: field.placeholder },
              domProps: { value: this.formData[field.name] || '' },
              on: { input: (e) => this.handleInput(field.name, e) },
            }),
          ]);
        case 'select':
          return h('div', [
            h('label', field.label),
            h('select', {
              domProps: { value: this.formData[field.name] || '' },
              on: { input: (e) => this.handleInput(field.name, e) },
            }, field.options.map(option => 
              h('option', { attrs: { value: option.value } }, option.label)
            )),
          ]);
        case 'checkbox':
          return h('div', [
            h('label', [
              h('input', {
                attrs: { type: 'checkbox' },
                domProps: { checked: this.formData[field.name] || false },
                on: { input: (e) => this.handleInput(field.name, e) },
              }),
              field.label,
            ]),
          ]);
        default:
          return null;
      }
    });
    return h('form', {
      on: {
        submit: (e) => {
          e.preventDefault();
          this.$emit('submit', this.formData);
        },
      }
    }, [
      ...formItems,
      h('button', { attrs: { type: 'submit' } }, '提交')
    ]);
  },
};
// 使用示例
<DynamicForm 
  :form-config="formConfig" 
  :form-data="formData" 
  @input="formData = $event" 
  @submit="handleSubmit" 
/>
// 配置示例
formConfig: [
  { type: 'text', name: 'username', label: '用戶名', placeholder: '請輸入用戶名' },
  { type: 'text', name: 'email', label: '郵箱', placeholder: '請輸入郵箱' },
  { type: 'select', name: 'role', label: '角色', options: [
    { value: 'admin', label: '管理員' },
    { value: 'user', label: '普通用戶' },
  ]},
  { type: 'checkbox', name: 'agreement', label: '我同意條款' },
],

5. 性能

render 函數(shù)通常比 template 性能更高,原因如下:

  • 避免了模板編譯過程
  • 可以更精確地控制虛擬 DOM 的創(chuàng)建
  • 在復雜動態(tài) UI 場景中減少不必要的重新渲染

但請注意,不要為了性能而過度使用 render 函數(shù),保持代碼可讀性同樣重要。在大多數(shù)情況下,template 的性能已經足夠好,沒必要為了什么原因全部使用render。

到此這篇關于vue渲染函數(shù)render的使用的文章就介紹到這了,更多相關vue渲染函數(shù)render內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論