1. Введение
1.1 Дерево HTML DOM и виртуальный DOM Vue
- Мы знаем, что когда браузеры анализируют HTML-файлы, они анализируют теги HTML в дереве DOM (
tree of DOM nodes). Структурируя элементы узлов, браузеры могут легко отслеживать состояние всей страницы, но частые локальные обновления узлов обходятся дорого. - Чтобы более эффективно отображать HTML,
Vue.jsа такжеReactтак же какEmber.jsТочно так же строится соответствующий JS-объект по отображению реального DOM, то есть виртуального DOM (Virtual DOM). Создайте буферную зону между данными и DOM, не обновляя DOM каждый раз.Подробности смотрите на схематической диаграмме React Virtual DOM ниже:
1.2 Слоты в компонентах Vue
-
Vue.jsМеханизм Slot означает, что когда родительский компонент вкладывает дочерний компонент, он может передавать фрагменты HTML-кода дочернему компоненту для рендеринга, но вам не нужно заботиться о том, где HTML должен быть размещен в дочернем компоненте. - Вы запутались и не понимаете? Например:
<!-- 【需求】
封装一个文章标题组件,但是要根据情况给组件传递"分享到站外"的html代码片段
因为在调用组件的时候,才能根据当前用户角色确定是分享到微信还是微博的按钮 -->
<!-- 组件<article>期望的最终渲染的代码是这样的 -->
<div>
<h1>
vue.js Render()函数&JSX
</h1>
<!-- 这里是可能传递的代码片段1 -->
<span><Button>分享到新浪微博</Button></span>
<!-- 这里是可能传递的代码片段2 -->
<span><Button>分享到微信</Button></span>
</div>
- мы начинаем с
Vvue.jsСлот механизм для достижения
- определение
article.vueкомпоненты
<tempalte>
<div>
<h1>
{{title}}
</h1>
<!-- 子组件只关心slot摆放的位置,而不关心传进来的是什么 -->
<slot></slot>
</div>
</tempalte>
<script>
export default{
name : 'Article',
props : ['title']
}
</script>
-
main.vueИспользуйте заголовок
<template>
<div>
<article :title="titleStr">
<span>
<Button v-if="userType === 'weibo'">分享到新浪微博</Button>
<Button v-if="userType === 'weixin'">分享到微信</Button>
</span>
</article>
</div>
</template>
<script>
export default{
name : 'Main',
data() {
return: {
userType: 'weibo',
titleStr: 'vue.js Render()函数&JSX'
}
}
}
</script>
- скажи больше
slot, как подсказывает название,Vue.jsРаспространение контента достигается за счет настройки механизма, аналогичного слоту/монетному слоту для программы:
- Подкомпонент определяет положение слота;
- Родительский компонент может вставить любой фрагмент кода, соответствующий спецификации Slot;
- Таким образом, он не только реализует развязку программы, но и очень удобен в использовании.
- конечно,
Vue.jsОн также поддерживает именованные слоты, то есть путем определения имен ключей для различения различных фрагментов кода слотов, которые здесь не обсуждаются.
2. функция рендеринга()
2.1 Зачем использовать функцию render()
- использовал
Vue.jsВсе мои друзья знают, что большую часть времени они используют метод шаблона для создания HTML, потому что vue предоставляетv-if,v-forЖдем серию инструкций по управлению, чтобы сделать нашу разработку легкой и приятной. - Но кроме того, на самом деле
Vue.jsтакже обеспечиваетrender()функция для создания HTML. Давайте создадим HTML более гибко через код логики JS. - Как сказано на примере официального сайта Vue: при инкапсуляции компонента
заголовка статьи неизвестно, что будет в конечном итоге сгенерировано, в частности теги h1~h6. В это время, если вы используете v-ifДелайте выводы, тогда объем кода слишком велик, но сrender()Реализовать функцию очень просто.
<!-- 用template实现 -->
<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
}
}
})
<!-- 用render()函数实现 -->
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // 标签名称
this.$slots.default // 由子节点构成的数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
2.2 Параметры функции render()
render(crateElmentFn,context){}
-
createElemntFnЭто основной метод, используемый Vue для динамического создания HTML [Примечание: наименование функции createElement как h является общим соглашением vue.js, помните, что это одно и то же] -
contextконтекст экземпляра компонента, включая все свойства экземпляра компонента- реквизит: объект, предоставляющий реквизит
- Children: массив дочерних узлов VNode
- слоты: объект слотов
- data: объект данных, переданный компоненту
- parent: ссылка на родительский компонент
3. Функция createElement()
3.1 Параметры createElement()
createElemnt(newNode,newNodeConfig,childVNodeList)
-
createElemnt()Функция имеет три основных параметра:-
newNode: Создаваемый узел [Обязательный параметр]- Тип параметра: {String|Object|Function}, который может быть именем создаваемого тега HTML, объектом компонента или асинхронной функцией, возвращаемой как String или Vue Object.
-
newNodeConfig: Объект конфигурации нового узла [необязательно] -
childVNodeList: Набор дочерних узлов, которые будут включены в новый узел [необязательно]- Тип параметра: {Строка | Массив}
- Примечание: в официальном руководстве по vue указано, что переданные VNodes должны быть уникальными.Если вы хотите многократно создавать один и тот же элемент HTML, вам нужно использовать фабричную функцию для достижения этого.
-
- Пример вызова:
// @returns {VNode}
createElement(
'div',
{},
[
'Some text comes first.',
createElement('h1', 'A headline'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
3.2 Подробное объяснение параметров newNodeConfig
{
// 和 `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 值是否为 enter 键值。
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 slot)的格式如下
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果此组件是另一个组件的子组件,
// 需要为插槽(slot)指定名称
slot: 'name-of-slot',
// 其他特殊顶层(top-level)属性
key: 'myKey',
ref: 'myRef'
}
3.3 Используйте createElement() вместо шаблона для создания HTML
3.3.1 Условное суждение и рендеринг цикла
код шаблона:
<ul v-if="items.length">
<li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>
код рендеринга:
props: ['items'],
render: function (createElement) {
// 使用原生JS代码来做条件判断
if (this.items.length) {
// 使用map()来循环调用createElement()函数
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'No items found.')
}
}
3.3.2 Привязка данных
код рендеринга:
props: ['value'],
render: function (createElement) {
// 【注意:由于JS可以用function作为函数的参数传递,为了避免this指针的混乱,在render函数里,要记得对this指针进行缓存】
var self = this
return createElement('input', {
// 手动vue emit事件的触发,来手动控制value值的维护,这是深入底层实现需要付出的代价
domProps: {
value: self.value
},
on: {
input: function (event) {
self.$emit('input', event.target.value)
}
}
})
}
3.3.3 Привязка событий
- Vue.js в
createElemnt()В функции отображается ряд префиксов модификаторов инструкции события:
| модификатор | приставка |
|---|---|
.passive |
& |
.capture |
! |
.once |
~ |
.capture.onceили.once.capture |
~! |
- Пример:
createElement('input',{
on: {
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
}
})
3.3.4 передача слота
- Если, как упоминалось выше, родительский компонент хочет передать слот при вызове дочернего компонента, вы можете передать
this.$slotsвозражать против доступа
render: function (createElement) {
// 默认传递所有Slot
// 等同于:<div><slot></slot></div>
return createElement('div', this.$slots.default)
}
- Если вы хотите передать параметры при передаче Slot, вы можете передать
this.$scopedSlots
props: ['message'],
render: function (createElement) {
// 等同于`<div><slot :text="message"></slot></div>`
return createElement('div', [
this.$scopedSlots.default({
text: this.message
})
])
}
4. JSX
- несмотря на то что
render()Функция создания фрагментов HTML-кода очень гибкая, но весь код конфигурации JS действительно плохо читается. Чтобы упростить код, мы можем использовать JSX для написания HTML непосредственно в коде JS:
render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
- Но для поддержки JSX вам нужно передать плагин JSX Babel. Пример кода конфигурации выглядит следующим образом:
package.json
devDependencies: {
"babel-helper-vue-jsx-merge-props": "2.0.3",
"babel-plugin-syntax-jsx": "6.18.0",
"babel-plugin-transform-vue-jsx": "3.7.0"
}
.babelrc
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}