Vue render заглядывает глубоко в тайну

внешний интерфейс JavaScript Vue.js
Vue render заглядывает глубоко в тайну

Введение

При использовании Vue для разработки в большинстве случаев для разработки используется шаблон, использование шаблона простое, удобное и быстрое, но иногда не очень подходит использование шаблона в особых сценариях. Итак, чтобы эффективно использовать функцию рендеринга, я решил копнуть немного глубже. Если вы считаете, что следующее неверно, пожалуйста, укажите, что ваше взаимодействие со мной является самой большой мотивацией для написания. Началось в:Vue render заглядывает глубоко в тайну

Сцены

Сценарий, описанный на официальном сайте Когда мы начинаем писать компонент, динамически генерирующий теги заголовков через level prop, вы можете быстро подумать об этом:

<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>
Vue.component('anchored-heading', {
  template: '#anchored-heading-template',
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

Использование шаблона в этом случае не лучший вариант: во-первых, код многословный, чтобы вставить элементы привязки в разные уровни заголовков, нам нужно использовать его многократно.

Хотя шаблоны отлично работают в большинстве компонентов, здесь они не очень кратки. Итак, давайте попробуем переписать приведенный выше пример, используя функцию рендеринга:

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // tag name 标签名称
      this.$slots.default // 子组件中的阵列
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

Намного проще и понятнее! Короче говоря, этот код намного проще, но требует хорошего знакомства со свойствами экземпляра Vue. В этом примере вам нужно знать, что когда вы передаете контент в компонент без использования атрибута слота, такого как Hello world!, в заголовке с привязкой, эти дочерние элементы сохраняются в $slots.default в экземпляре компонента.

Введение параметра createElement

Следующее, с чем вам нужно ознакомиться, это как генерировать шаблоны в функции createElement. Вот параметры, которые принимает createElement:

createElement(
  // {String | Object | Function}
  // 一个 HTML 标签字符串,组件选项对象,或者
  // 解析上述任何一种的一个 async 异步函数,必要参数。
  'div',

  // {Object}
  // 一个包含模板相关属性的数据对象
  // 这样,您可以在 template 中使用这些属性。可选参数。
  {
    // (详情见下一节)
  },

  // {String | Array}
  // 子节点 (VNodes),由 `createElement()` 构建而成,
  // 或使用字符串来生成“文本节点”。可选参数。
  [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

Детализация объекта данных

Одно замечание: как и в синтаксисе шаблона, 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
  },
  // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
  // 赋值,因为 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'
}

условный рендеринг

Теперь, когда мы знакомы с вышеупомянутыми API, давайте взглянем на реальный бой. писал это раньше

//HTML
<div id="app">
   <vv-isshow :show="isShow"></vv-isshow>
</div>

//js
//组件形式			
Vue.component('vv-isshow', {
	props:['show'],
	template:'<div v-if="show">我被你发现啦2!!!</div>',
});
var vm = new Vue({
	el: "#app",
	data: {
		isShow:true
	}
});

рендер пиши вот так

//HTML
<div id="app">
   <vv-isshow :show="isShow"><slot>我被你发现啦3!!!</slot></vv-isshow>
</div>
//js
//组件形式			
Vue.component('vv-isshow', {
	props:{
		show:{
			type: Boolean,
			default: true
		}
	},
	render:function(h){	
		if(this.show ) return h('div',this.$slots.default);
	},
});
var vm = new Vue({
	el: "#app",
	data: {
		isShow:true
	}
});

Рендеринг списка

Это было написано так раньше, и шаблон должен быть обернут меткой, когда используется v-for

//HTML
<div id="app">
   <vv-aside v-bind:list="list"></vv-aside>
</div>
//js
//组件形式			
Vue.component('vv-aside', {
	props:['list'],
	methods:{
		handelClick(item){
			console.log(item);
		}
	},
	template:'<div>\
	              <div v-for="item in list" @click="handelClick(item)" :class="{odd:item.odd}">{{item.txt}}</div>\
	          </div>',
	//template:'<div v-for="item in list" @click="handelClick(item)" :class="{odd:item.odd}">{{item.txt}}</div>',错误          
});
var vm = new Vue({
	el: "#app",
	data: {
		list: [{
			id: 1,
			txt: 'javaScript',
			odd: true
		}, {
			id: 2,
			txt: 'Vue',
			odd: false
		}, {
			id: 3,
			txt: 'React',
			odd: true
		}]
	}
});

рендер пиши вот так

//HTML
<div id="app">
   <vv-aside v-bind:list="list"></vv-aside>
</div>
//js
//侧边栏
Vue.component('vv-aside', {
	render: function(h) {
		var _this = this,
			ayy = this.list.map((v) => {
				return h('div', {
					'class': {
						odd: v.odd
					},
					attrs: {
						title: v.txt
					},
					on: {
						click: function() {
							return _this.handelClick(v);
						}
					}
				}, v.txt);
			});
		return h('div', ayy);

	},
	props: {
		list: {
			type: Array,
			default: () => {
				return this.list || [];
			}
		}
	},
	methods: {
		handelClick: function(item) {
			console.log(item, "item");
		}
	}
});
var vm = new Vue({
	el: "#app",
	data: {
		list: [{
			id: 1,
			txt: 'javaScript',
			odd: true
		}, {
			id: 2,
			txt: 'Vue',
			odd: false
		}, {
			id: 3,
			txt: 'React',
			odd: true
		}]
	}
});

v-model

предыдущее написание

//HTML
<div id="app">
	<vv-models v-model="txt" :txt="txt"></vv-models>
</div>
//js
//input
Vue.component('vv-models', {
	props: ['txt'],
	template: '<div>\
				  <p>看官你输入的是:{{txtcout}}</p>\
	              <input v-model="txtcout" type="text" />\
	          </div>',
	computed: {
		txtcout:{
			get(){
				return this.txt;
			},
			set(val){
				this.$emit('input', val);
			}
			
		}
	}
});
var vm = new Vue({
	el: "#app",
	data: {
	    txt: '', 
	}
});

рендер пиши вот так

//HTML
<div id="app">
	<vv-models v-model="txt" :txt="txt"></vv-models>
</div>
//js
//input
Vue.component('vv-models', {
	props: {
		txt: {
			type: String,
			default: ''
		}
	},
	render: function(h) {
		var self=this;
		return h('div',[h('p','你猜我输入的是啥:'+this.txt),h('input',{
			on:{
				input(event){
                    self.$emit('input', event.target.value);
				}
			}
		})] );
	},
});
var vm = new Vue({
	el: "#app",
	data: {
	    txt: '', 
	}
});

Суммировать

Функция рендеринга использует все программные возможности JavaScript, что имеет абсолютное преимущество в производительности, редактор просто анализирует его. Что касается фактического проекта, способ рендеринга, который вы выберете, по-прежнему зависит от вашего проекта и реальной ситуации.