Vue.js 2.x render 渲染函数 JSX
Vue.js 2.x render
渲染函數 & JSX
Vue絕大多數情況下使用template創建 HTML。但是比如一些重復性比較高的場景,需要運用 JavaScript 的完全編程能力,可以使用render函數。
1. 節點、樹以及虛擬DOM
每個元素都是一個節點。每片文字也是一個節點。甚至注釋也都是節點。
一個節點就是頁面的一個部分。每個節點都可以有子節點。
比如上面的節點樹就表示下面的代碼:
<div><h1>My title</h1>Some text content<!-- TODO: 添加標簽行 --> </div>1.1 虛擬 DOM
Vue 通過建立一個虛擬 DOM 對真實 DOM 發生的變化保持追蹤。
虛擬 DOM 是對Vue組件樹建立起來的整個 VNode(Virtual Node)樹的稱呼。
1.1.1 createElement 參數
// @returns {VNode} createElement(// {String | Object | Function}// 一個 HTML 標簽字符串,組件選項對象,或者// 解析上述任何一種的一個 async 異步函數// 必須參數'div',// {Object}// 一個包含模板相關屬性的數據對象// 你可以在 template 中使用這些特性// 可選參數{ // 屬性參見 1.1.2 深入 data 對象},// {String | Array}// 子虛擬節點(VNodes) 由 createElement() 構建而成// 也可以使用字符串來生成“文本虛擬節點”// 可選參數['文字',createElement('h1', '一個標題'),createElement(MyComponent, {props: {someProp: 'foo'}})] )1.1.2 深入 data 對象
在 VNode 數據對象中,下列屬性名是級別最高的字段。該對象也允許綁定普通的 HTML 特性,就像 DOM 屬性一樣,比如innerHTML(這會取代v-html指令)。
{// 和 v-bind:calss 一樣的API// 接收一個字符串、對象或字符串和對象組成的數組'class': {foo: true,bar: false},// 和 v-bind:style 一樣的API// 接收一個字符串、對象或對象組成的數組style: {color: 'red',fontSize: '14px'},// 普通的 HTML 特性attrs: {id: 'foo'},// 組件的 propsprops: {myProp: 'bar'},// DOM屬性domProps: {innerHTML: 'baz'},// 事件監聽器基于 on// 所以不再支持如 v-on:keyup.enter 修飾器// 需要手動匹配 keyCodeon: {click: this.clickHandler},// 僅用于組件,用于監聽原生事件,而不是組件內部使用// vm.$emit 觸發的事件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',// 其他特殊頂層屬性key: 'myKey',ref: 'myRef',// 如果在渲染函數中向多個元素都應用了相同的 ref 名// 那么 $refs.myRef 會變成一個數組refInfor: true }1.1.3 完整示例
一個生成錨點標題的組件:
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) {// 創建 kebab-case 風格的IDvar 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}} })使用效果:
<div id="app"><anchored-heading :level="1">first</anchored-heading><anchored-heading :level="2">second</anchored-heading><anchored-heading :level="3">third<small>thirdSub</small></anchored-heading> </div>1.1.4 約束
組件樹中的所有 VNodes 必須是唯一的。
比如這樣寫是錯誤的:
render: function (createElement) {var myParagraphVNode = createElement('p', 'hi')return createElement('div', [// 錯誤-重復的 VNodesmyParagraphVNode, myParagraphVNode]) }如果需要重復多次元素/組件,可以用工廠函數來實現:
render: function(createElement) {return createElement('div',Array.apply(null, { length: 20 }.map(function() {return createElement('p', 'hello')}))); }1.2 使用 JavaScript 代替模板功能
1.2.1 v-if 和 v-for
只要在原生的 JavaScript 中可以輕松完成的操作,Vue的render函數就不會提供專有的替代方法。
比如這里的v-if和v-for。
使用 render 方法重寫:
props: ['items'], 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 here.');} }1.2.2 v-model
render 函數中沒有與 v-model直接對應的方法,需要手動實現:
props: ['value'], render: function (createElement) {var self = this;return createElement('input', {// DOM屬性domProps: {value: self.value},on: {input: function(e) {self.$emit('input', e.target.value);}}}) }1.3 事件&按鍵修飾符
對于.passive、.capture、.once事件修飾符,Vue提供了相應的前綴可以用于on:
| .passive | & |
| .capture | ! |
| .once | ~ |
| .capture.once或.once.capture | ~! |
比如:
on: {'!click': this.doThisInCapturingMode,'~keyup': this.doThisOnce,'~!mouseover': this.doThisOnceInCapturingMode }對于其他的修飾符,可以在事件處理函數中使用事件方法:
| .stop | e.stopPropagation() |
| .prevent | e.preventDefault() |
| .self | if (e.target !== e.currentTarget) return; |
| .enter, .13 | e.keyCode === 13 |
比如:
on: {keyup: function (event) {// 如果觸發事件的元素不是事件綁定的元素// 則返回if (event.target !== event.currentTarget) return// 如果按下去的不是 enter 鍵或者// 沒有同時按下 shift 鍵// 則返回if (!event.shiftKey || event.keyCode !== 13) return// 阻止 事件冒泡event.stopPropagation()// 阻止該元素默認的 keyup 事件event.preventDefault()// ...} }1.4 插槽
可以通過this.$slots訪問靜態插槽內容,得到的是一個 VNodes 數組:
render: function(createElement) {// `<div><slot></slot></div>`return createElement('div', this.$slots.default) }也可以通過this.$scopeSlots訪問作用域插槽,得到的是一個 VNodes 的函數:
props: ['message'], render: function(createElement) {// `<div><slot :text="message"></slot></div>`return createElement('div', [this.$scopedSlots.default({text: this.message})]) }如果要用渲染函數句子向子組件中傳遞作用域插槽,可以利用 VNode 數據對象中的scopedSlots域:
render: function(createElement) {return createElement('div', [createElement('child', {// 在數據對象中傳遞 scopedSlots// 格式:{ name: props => VNode | Array<VNode> }scopedSlots: {default: function(props) {return createElement('span', props.text)}}})]) }2. JSX
在Vue中使用JSX語法,可以避免書寫繁雜冗余的render代碼,Babel插件用于在Vue中使用JSX語法。
使用文檔
import AnchoredHeading from './AnchoredHeading.vue'new Vue({el: '#app',render: function(h) {return (<AnchoredHeading level={1}><span>Hello</span> world</AnchoredHeading>)} })3. 函數式組件
一個函數是組件,意味著它是無狀態(沒有響應式數據),無實例(沒有this上下文)的:
Vue.component('my-component', {functional: true,// props可選 v2.3.0^props: {},// 為了彌補缺少的實例// 提供第二個參數作為上下文render(createElement, context) {} })在v2.5.0^版本中,單文件組件基于模板的函數式組件可以這樣聲明:
<template functional></template>組件需要的一切都是通過上下文來傳遞:
- props 提供所有prop對象
- children VNode子節點的數組
- slots 返回所有插槽的對象的函數
- scopedSlots 一個暴露傳入的作用域插槽以及函數形式的普通插槽的對象
- data 傳遞給組件的數據對象,作為CreateElement的第二個參數傳入組件
- parent 對父組件的引用
- listeners 一個包含了所有在父組件上注冊的事件偵聽器的對象,data.on的別名
- injections 如果使用了inject選項,該對象包含了應當被注入的屬性
之前的錨點標題組件可以改為如下的函數式組件:
...Vue.component('anchored-heading', {functional: true,render: function (createElement, context) {// 創建 kebab-case 風格的IDvar headingId = getChildrenTextContent(context.children).toLowerCase().replace(/\W+/g, '-').replace(/(^-|-$)/g, '')return createElement('h' + context.props.level,[createElement('a', {attrs: {name: headingId,href: '#' + headingId}}, context.children)])},props: {level: {type: Number,required: true}} })3.1 想子組件或子元素傳遞特性和事件
在普通組件中,沒有被定義為 prop 的特性會自動添加到組件的根元素上,將現有的同名特性替換或智能合并。
而函數式組件要求必須顯示定義該行為:
如果使用模板的函數式組件,還需要手動添加特性和監聽器。
<template functional><buttonclass="btn btn-primary"v-bind="data.attrs"v-on="listeners"><slot/></button> </template>3.2 slots()和children對比
slots()和children對比
總的來說個人理解,slots()可以拿到具名插槽,children拿到所有。
4. 模板編譯
模板編譯
The end
2019-8-13 15:45:59
轉載于:https://www.cnblogs.com/jehorn/p/11346358.html
總結
以上是生活随笔為你收集整理的Vue.js 2.x render 渲染函数 JSX的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多线程中的应用之队列(queue)
- 下一篇: day09数据分析