补充
render
import logo from '../assets/logo.png'
export default {
render() {
return <div>
<img alt="Vue logo" src={logo} />
</div>
}
}
- render中使用的组件,只需要import引入便可,不需要components注册
JSX
- 好像jsx和tsx不支持绑定key中包含:,例如:
vOn:click
会引发报错
————————————————————————————————————————————————————————————————————
总结
JSX
- 根据babel-core的版本不同,使用了不同插件,导致存在两个版本的JSX语法
- @vue/babel-preset-jsx 这个插件只适用于 Babel 7.x
- vuejs/babel-plugin-transform-vue-jsx 这个插件适用于 6.x
- babel-plugin-transform-vue-jsx插件从3.4.0开始,自动向使用了JSX语法并具有get方法的函数注入变量h指向渲染函数。例如:render、computed下的方法等,而自定义属性不会。@vue/babel-preset-jsx没有版本划分
@vue/babel-preset-jsx
// Attributes/Props
return <input
type="email"
placeholder={this.placeholderText}
/>
const inputAttrs = {
type: 'email',
placeholder: 'Enter your email'
}
return <input {...{ attrs: inputAttrs }} />
// Slots
<MyComponent>
<header slot="header">header</header>
<footer slot="footer">footer</footer>
</MyComponent>
// scoped slots
const scopedSlots = {
header: (props) => <header>{props.text}</header>,
footer: (propst) => <footer>{props.text}</footer>
}
return <MyComponent scopedSlots={scopedSlots} />
// v-model
<input vModel_trim={this.newTodoText} />
// v-on
<input vOn:click_stop_prevent={this.newTodoText} /> // jsx 不支持 vOn:
<input onClick_stop={this.doSome} /> // 并不是所有的修饰符都支持,目前已知按键修饰不支持,例如:_enter
<Component nativeOn-click={ ... } />
<Component nativeOnClick={ ... } />
// v-html
<p domPropsInnerHTML={html} />
<Component domProp-innerHTML={ ... } />
// v-自定义指令, 例如:vClamp
// 注释:个人经验,并非官方文档说明的
<div vClamp={{clamp}}>需要出现超出省略的文本</div>
// 函数式组件
// 注释:小写的导出是不会被转换为选项对象的,这里的const b
export const A = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
export const b = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
export default ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
export const A = { // 转换后的结果
functional: true,
render: (h, {
props,
listeners
}) => <div onClick={listeners.click}>{props.msg}</div>
}
- 如果组件名以小写开头,那么需要进行components注册后才能使用。如果是大写开头可以直接在JSX中使用该组件无需注册。
- 在JSX中vue原生指令除了
v-show={value}
其他指令都不支持
- 自定义指令可以通过
v-name={value}
使用,但是不支持参数和修饰符
- Pass everything as an object via value, e.g. v-name={{ value, modifier: true }}
- 注释:可以通过传入一个对象来解决,应该是插件提供的,并不是模板语言原有。
- 疑问:只支持修饰符?
- 使用渲染函数第2个参数中的directives绑定指令
render (h) {
return h('div', {
props: {
msg: 'hi',
onCustomEvent: this.customEventHandler // propsOnCustomEvent={this.customEventHandler}
},
attrs: {
id: 'foo' // id="foo"
},
domProps: {
innerHTML: 'bar' // domPropsInnerHTML="bar"
},
on: {
click: this.clickHandler // onClick={this.clickHandler}
},
nativeOn: {
click: this.nativeClickHandler // nativeOnClick={this.nativeClickHandler}
},
class: { // class={{ foo: true, bar: false }} 注释:通过{...}绑定一个表达式
foo: true,
bar: false
},
style: { // style={{ color: 'red', fontSize: '14px' }}
color: 'red',
fontSize: '14px'
},
key: 'key', // key="key"
ref: 'ref', // ref="ref"
// assign the `ref` is used on elements/components with v-for
refInFor: true, // refInFor
slot: 'slot' // slot="slot"
})
}
函数式组件
- 使用场景
- 程序化地在多个组件中选择一个来代为渲染;
- 在将 children、props、data 传递给子组件之前操作它们。
- 特点
- 没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期,可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
- 当使用函数式组件时,该引用将会是 HTMLElement,因为他们是无状态的也是无实例的
透传
- 在普通组件内实现透传
- vm.$attrs 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
- vm.$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
- vm.$scopedSlots 用来访问作用域插槽。对于每一个插槽(包括 slot),该对象都包含一个返回相应 VNode 的函数。注释:不能重名,包括default
- vm.$slots 用来访问被插槽分发的内容。每个具名插槽 有其相应的属性 (例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到)。default 属性包括了所有没有被包含在具名插槽中的节点,或 v-slot:default 的内容。注释:同一个具名插槽不知道绑定顺序
- 函数式组件 context 参数
- props:提供所有 prop 的对象
- children: VNode 子节点的数组。注释:只包含普通插槽的,等价于slots().values(),反应了父组件中子集的插入顺序,方便透传
- slots: 一个函数,返回了包含所有插槽的对象。
- scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。注释:如果同名作用域插槽会覆盖普通插槽
- data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件。注释:即当前组件被实例化时传入的createElement函数的第二个参数,方便透传
- parent:对父组件的引用
- listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
- injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。
Vue.component('my-functional-button', {
functional: true,
render: function (createElement, context) {
// 完全透传任何 attribute、事件监听器、子节点等。
return createElement('button', context.data, context.children)
}
})
createElement 参数
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
// 注释:当前组件的根元素,可以是dom名或者其他组件的选项对象
'div',
// {Object}
// 一个与模板中属性对应的数据对象。可选。(可省略)
// 注释:绑定到根元素上的属性,当根元素是一个组件时可以视为传入组件的组件选项值
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML attribute
// 注释:这是真实写入html的属性,在控制台html中可以看见。(性能不好)
// 注释:在普通组件中没有被定义为 prop 的 attribute 会自动添加到组件的根元素上,将已有的同名 attribute 进行替换或与其进行智能合并。
// 注释:在函数式组件如果有需要的话要求你显式定义该行为。在 2.3.0 之前的版本中,只能通过 context.data.attrs 获得,在 2.3.0 或以上的版本中,所有组件上的 attribute 都会被自动隐式解析为 prop。直接通过 context.props 获得
attrs: {
id: 'foo'
},
// 组件 prop
// 注释:在函数组件中可以不设置props选项,这么所有绑定的属性都可以在props中访问
props: {
myProp: 'bar'
},
// DOM 属性
// 注释:和根元素的子级不同,innerHTML 是v-html在渲染函数中的实现
// 注释:这是给某个dom对象的属性进行赋值,不在html中表现(性能应该更高)
// 注释:使用模板语法或JSX时直接绑定到元素上的属性会优先判断是否props > domProps > attrs
domProps: {
innerHTML: '<div>some</div>',
value: self.value // 等价于模板中<div :value="value"></div>
},
// 事件监听器在 `on` 属性内,
// 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
// 需要在处理函数中手动检查 keyCode。
// 对于 .passive(立即触发)、.capture(捕获阶段触发) 和 .once 这些事件修饰符, Vue 提供了相应的前缀。对于.stop、.prevent(禁止默认)、.self、按键、修饰键 可以在事件处理函数中使用事件方法
on: {
click: this.clickHandler
},
// 仅用于组件,用于监听原生事件,而不是组件内部使用 `vm.$emit` 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
// 赋值,因为 Vue 已经自动为你进行了同步。
directives: [ // v-my-custom-directive:foo.bar="1 + 1"
{
name: 'my-custom-directive',
value: '2', // 表达式的计算结果
expression: '1 + 1', // 绑定的表达式
arg: 'foo', // 参数,参数可以由外部决定是什么,可以配合value动态传对象
modifiers: { // 修饰符,由指令内部决定好了有哪些,进行真假判断后走不同程序分支
bar: true
}
}
],
// 作用域插槽的格式为
// { name: props => VNode | Array<VNode> }
// 注释:当前元素是组件并具有作用域插槽时使用,该值为传入作用域插槽的渲染函数,props获取组件中作用域插槽传入的值。注意,作用传入域插槽的元素并不是写在子级中而是在这里作为参数
// 注释:当前根组件中具有作用域插槽,使用当前根组件时向作用域插槽内插入元素。下面例子对应的当前根组件为
// render(h) {
// return h("div", {}, [this.$scopedSlots.default && this.$scopedSlots.default({ text: "some" })]);
// }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
// 注释:在子级元素中使用,当前元素在子级中出现,作为某个组件的插槽元素。
// 注释:指明当前元素插入的插槽名,它对应的父组件为
// render(h) {
// return h("div", {}, [this.$slots.default && this.$slots.default]);
// }
slot: 'default',
// 其它特殊顶层属性
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 当设置 refInFor 为 true 时 `$refs.myRef` 会变成一个元素数组,否则为最后一个应用该名称的元素
refInFor: true
},
// {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
// 注释:根元素下包含的元素,当根元素是组件时可以视为插入插槽的行为
// 注释:可以为单个 vnode/String ,也可以示 vnode/String 的二维数组,例如:[[vnode,vnode],vnode](不建议,按官网文档来)
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
———————————————————————————————————————————————————————————————————————————————————————————
基础
实例属性 API
vm.$data
- 类型:Object
- 详细:Vue 实例观察的数据对象。Vue 实例代理了对其 data 对象属性的访问。
vm.$props
- 2.2.0 新增
- 类型:Object
- 详细:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问。
- 注释:没有传入对用props时返回空对象,即使该props有默认值
vm.$el
- 类型:Element
- 只读
- 详细:Vue 实例使用的根 DOM 元素。
- 注释:当前实例 render 中的根dom元素,注意不是他替换的元素,而是组件内元素
vm.$options
- 类型:Object
- 只读
- 详细:用于当前 Vue 实例的初始化选项。需要在选项中包含自定义属性时会有用处:
- 注释:
this.$options
返回的对象中不包含自定义属性,但是通过this.$options.customOption
可以访问到,自定义属性实际绑定到当前实例原型上的
- 注释:这个对象是响应的,对其值得修改会导致当前组件重新渲染
new Vue({
customOption: 'foo',
created: function () {
console.log(this.$options.customOption) // => 'foo'
}
})
vm.$parent
- 类型:Vue instance(Vue实例)
- 只读
- 详细:父实例,如果当前实例有的话。
vm.$root
- 类型:Vue instance
- 只读
- 详细:当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。
vm.$children
- 类型:Array
- 只读
- 详细:当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。
- 注释:由于生命周期的缘故,在created中返回空数组。mounted中能够正常获取,当然渲染函数中不能正常获取
- 注释:一般在methods中获取全部子组件
- 疑问:具体作用?
vm.$slots
- 类型:{ [name: string]: ?Array }
- 只读
- 详细:
- 用来访问被插槽分发的内容。每个具名插槽 有其相应的属性 (例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到)。default 属性包括了所有没有被包含在具名插槽中的节点,或 v-slot:default 的内容。
- 注意: v-slot:foo 在 2.6 以上的版本才支持。对于之前的版本,你可以使用废弃了的语法.
- 注释:即使当前组件没有插槽,但在父组件插入内容时依然可以通过default获取
Vue.component('blog-post', {
render: function (createElement) {
var header = this.$slots.header
var body = this.$slots.default
var footer = this.$slots.footer
return createElement('div', [
createElement('header', header),
createElement('main', body),
createElement('footer', footer)
])
}
})
vm.$scopedSlots
- 2.1.0 新增
- 类型:{ [name: string]: props => Array | undefined }
- 只读
- 详细:用来访问作用域插槽。对于每一个插槽(包括 slot),该对象都包含一个返回相应 VNode 的函数。
- 注意:从 2.6.0 开始,这个属性有两个变化:
- 作用域插槽函数现在保证返回一个 VNode 数组,除非在返回值无效的情况下返回 undefined。
- 所有的 $slots 现在都会作为函数暴露在 $scopedSlots 中。Vue3趋势
- 注释:和$slot相同,未具名的插入组件存放在default属性下
- 注释:v-slot只能使用在组件上,可以是用template组件包裹原生dom
- 注释:$scopedSlots和$slots共用相同的key名,所以同时为具名插槽时,作用域插槽会覆盖掉具名插槽
vm.$refs
- 类型:Object
- 只读
- 详细:一个对象,持有注册过 ref 特性 的所有 DOM 元素和组件实例。
- 注释:ref可以绑定到组件或dom上,相应的$refs返回组件实例或dom,或者他们组成的数组
vm.$isServer
- 类型:boolean
- 只读
- 详细:当前 Vue 实例是否运行于服务器。
vm.$attrs
- 2.4.0 新增
- 类型:{ [key: string]: string }
- 只读
- 详细:包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
vm.$listeners
- 2.4.0 新增
- 类型:{ [key: string]: Function | Array }
- 只读
- 详细:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
节点、树以及虚拟 DOM
虚拟 DOM
createElement 参数
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
// 注释:当前组件的根元素,可以是dom名或者其他组件的选项对象
'div',
// {Object}
// 一个与模板中属性对应的数据对象。可选。(可省略)
// 注释:绑定到根元素上的属性,当根元素是一个组件时可以视为传入组件的组件选项值
{
// (详情见下一节)
},
// {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
// 注释:根元素下包含的元素,当根元素是组件时可以视为插入插槽的行为
// 注释:可以为单个 vnode/String ,也可以示 vnode/String 的二维数组,例如:[[vnode,vnode],vnode](不建议,按官网文档来)
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
深入数据对象
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML attribute
// 注释:这是真实写入html的属性,在控制台html中可以看见。(性能不好)
// 后文:在 2.3.0 之前的版本中,如果一个函数式组件想要接收 prop,则 props 选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
// 后文:函数式组件要求你显式定义该行为。注释:函数组件不会自动将 attribute 添加到根元素上
// 后文:在普通组件中,没有被定义为 prop 的 attribute 会自动添加到组件的根元素上,将已有的同名 attribute 进行替换或与其进行智能合并。注释:普通组件是指非函数组件
attrs: {
id: 'foo'
},
// 组件 prop
props: {
myProp: 'bar'
},
// DOM 属性
// 注释:和根元素的子级不同,这是v-html在渲染函数中的实现
// 注释:这是给某个dom对象的属性进行赋值,不在html中表现(性能应该更高)
// 注释:使用模板语法或JSX时直接绑定到元素上的属性会优先判断是否props > domProps > attrs
domProps: {
innerHTML: '<div>some</div>',
value: self.value // 等价于模板中<div :value="value"></div>
},
// 事件监听器在 `on` 属性内,
// 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
// 需要在处理函数中手动检查 keyCode。
on: {
click: this.clickHandler
},
// 仅用于组件,用于监听原生事件,而不是组件内部使用
// `vm.$emit` 触发的事件。注释:这句话需要和上一句一起读
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
// 赋值,因为 Vue 已经自动为你进行了同步。
directives: [ // v-my-custom-directive:foo.bar="1 + 1"
{
name: 'my-custom-directive',
value: '2', // 表达式的计算结果
expression: '1 + 1', // 绑定的表达式
arg: 'foo', // 参数,参数可以由外部决定是什么,可以配合value动态传对象
modifiers: { // 修饰符,由指令内部决定好了有哪些,进行真假判断后走不同程序分支
bar: true
}
}
],
// 作用域插槽的格式为
// { name: props => VNode | Array<VNode> }
// 注释:当前元素是组件并具有作用域插槽时使用,该值为传入作用域插槽的渲染函数,props获取组件中作用域插槽传入的值。注意,作用传入域插槽的元素并不是写在子级中而是在这里作为参数
// 注释:当前根组件中具有作用域插槽,使用当前根组件时向作用域插槽内插入元素。下面例子对应的当前根组件为
// render(h) {
// return h("div", {}, [this.$scopedSlots.default && this.$scopedSlots.default({ text: "some" })]);
// }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
// 注释:在子级元素中使用,当前元素在子级中出现,作为某个组件的插槽元素。
// 注释:指明当前元素插入的插槽名,它对应的父组件为
// render(h) {
// return h("div", {}, [this.$slots.default && this.$slots.default]);
// }
slot: 'default',
// 其它特殊顶层属性
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 当设置 refInFor 为 true 时 `$refs.myRef` 会变成一个元素数组,否则为最后一个应用该名称的元素
refInFor: true
}
完整示例
- 注释:vnode.text包含文本节点的字符串内容,dom节点为undefined
约束
使用 JavaScript 代替模板功能
v-if 和 v-for
v-model
事件 & 按键修饰符
- 对于 .passive、.capture 和 .once 这些事件修饰符, Vue 提供了相应的前缀
- .passive 前缀 & (默认事件立即被触发)
- .capture 前缀 ! (在捕获阶段触发)
- .once 前缀 ~
- .capture.once 或 .once.capture 前缀 ~!
on: {
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
}
- 对于所有其它的修饰符,私有前缀都不是必须的,因为你可以在事件处理函数中使用事件方法:
- .stop:停止事件冒泡 event.stopPropagation()
- .prevent:禁止默认事件触发 event.preventDefault()
- .self:只允许当前元素触发 if (event.target !== event.currentTarget) return
- 按键:.enter, .13:按动某个按键 if (event.keyCode !== 13) return
- 修饰键:.ctrl, .alt, .shift, .meta:按动某些组合键 if (!event.ctrlKey) return (将 ctrlKey 分别修改为 altKey、shiftKey 或者 metaKey)
插槽
JSX
- 有一个 Babel 插件,用于在 Vue 中使用 JSX 语法,它可以让我们回到更接近于模板的语法
- 将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。
- 疑问:从 Vue 的 Babel 插件的 3.4.0 版本开始,我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入 const h = this.$createElement,这样你就可以去掉 (h) 参数了。
函数式组件
- 没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期,可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
- 注释:无状态是指没有data,但是当传入的props改变时,依然能够响应重新渲染
- 注释:没有监听任何传递给它的状态是指没有对任何对象设置getset方法,props导致的渲染只是传参改变导致渲染函数运算结果的改变而已。
- 注释:整个vnode树上的每一个vnode节点实际上都是一个computed
Vue.component('my-component', {
functional: true,
// Props 是可选的
props: {
// ...
},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {
// ...
}
})
- 在 2.3.0 之前的版本中,如果一个函数式组件想要接收 prop,则 props 选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
- 注释:在函数式组件中必须没有props选项这种隐式解析才会实现。一旦有,即使是空props,attribute 依然不会被解析为 props
- 当使用函数式组件时,该引用将会是 HTMLElement,因为他们是无状态的也是无实例的
- 在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:
<template functional>
</template>
- 组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象
- props:提供所有 prop 的对象
- children: VNode 子节点的数组。注释:只包含普通插槽的,等价于slots().values(),反应了父组件中子集的插入顺序,方便透传
- slots: 一个函数,返回了包含所有插槽的对象。
- scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。注释:如果同名作用域插槽会覆盖普通插槽
- data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件。注释:即当前组件被实例化时传入的createElement函数的第二个参数,方便透传
- parent:对父组件的引用
- listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
- injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。
- 因为函数式组件只是函数,所以渲染开销也低很多。
- 作为包装组件时它们也同样非常有用。比如,当你需要做这些时:
- 程序化地在多个组件中选择一个来代为渲染;
- 在将 children、props、data 传递给子组件之前操作它们。
向子元素或子组件传递 attribute 和事件
- 在普通组件中,没有被定义为 prop 的 attribute 会自动添加到组件的根元素上,将已有的同名 attribute 进行替换或与其进行智能合并。函数式组件要求你显式定义该行为。
- 注释:智能合并应该只支持class
- 注释:普通组件是指非函数组件。函数组件不会自动将 attribute 添加到根元素上
- 完全透传任何 attribute、事件监听器、子节点等。这是非常透明的,以至于那些事件甚至并不要求 .native 修饰符。
- 注释:只是实例化时选项的传递,返回的vnode根据这个选项生成。该例子中button是原生元素,my-functional-button是组件,但是他们都共用on这个选项来绑定事件,一个绑定原生事件一个绑定组件事件
- 注释:同理,如果组件上传入nativeOn事件,由于原生button禁止使用该选项,所以会报错
Vue.component('my-functional-button', {
functional: true,
render: function (createElement, context) {
// 完全透传任何 attribute、事件监听器、子节点等。
return createElement('button', context.data, context.children)
}
})
- 如果你使用基于模板的函数式组件,那么你还需要手动添加 attribute 和监听器
- 疑问:函数的模板是以 context 为上下文的?
<template functional>
<button
class="btn btn-primary"
v-bind="data.attrs"
v-on="listeners"
>
<slot/>
</button>
</template>
slots() 和 children 对比
模板编译
- 使用 Vue.compile 来实时编译模板字符串(文档突然没了)
——————————————————————————————————————————————————————————————————————————
- 注释:使用 Vue JSX 插件后的支持的JSX语法
Babel Preset JSX
Installation
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
- Then add the preset to .babelrc
{
"presets": ["@vue/babel-preset-jsx"]
}
Syntax
Content
render() {
return <p>hello { this.message }</p>
}
Attributes/Props
render() {
return <input
type="email"
placeholder={this.placeholderText}
/>
}
- with the spread operator (object needs to be compatible with Vue Data Object):
- 注释:使用扩展运算符操作对象,该对象等价于渲染函数的第二个参数
render() {
const inputAttrs = {
type: 'email',
placeholder: 'Enter your email'
}
return <input {...{ attrs: inputAttrs }} />
}
Slots
render() {
return (
<MyComponent>
<header slot="header">header</header>
<footer slot="footer">footer</footer>
</MyComponent>
)
}
render() {
const scopedSlots = {
header: (props) => <header>{props.text}</header>,
footer: (propst) => <footer>{props.text}</footer>
}
return <MyComponent scopedSlots={scopedSlots} />
}
Directives
<input vModel={this.newTodoText} />
- with a modifier:
- 注释:v-model的3个修饰符。.trim:自动过滤用户输入的首尾空白字符
<input vModel_trim={this.newTodoText} />
- with an argument:
- 注释:传入参数click字符串
<input vOn:click={this.newTodoText} />
- with an argument and modifiers:
<input vOn:click_stop_prevent={this.newTodoText} />
- v-html:
- 注释:v-html实际上是对dom的innerHTML进行赋值
- 疑问:可以用Attributes/Props中的domProps传入吗?
<p domPropsInnerHTML={html} />
Functional Components
- Transpiles arrow functions that return JSX into functional components, when they are either default exports:
- 疑问:一定要箭头函数吗?
export default ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
// 等价于
export default {
functional: true,
render: (h, {
props,
listeners
}) => <div onClick={listeners.click}>{props.msg}</div>
}
Compatibility
- Babel 7+. For Babel 6 support, use vuejs/babel-plugin-transform-vue-jsx
- 注释:这个插件只适用于 Babel 7+ , 6.x 版本请使用 vuejs/babel-plugin-transform-vue-jsx
- Vue 2+. JSX is not supported for older versions.
——————————————————————————————————————————————————————————————————————————
@vue/babel-preset-jsx
Babel Compatibility Notes
- This repo is only compatible with Babel 7.x, for 6.x please use vuejs/babel-plugin-transform-vue-jsx
- 注释:这个插件只适用于 Babel 7.x , 6.x 版本请使用 vuejs/babel-plugin-transform-vue-jsx
Usage
- You can toggle specific features, by default all features are enabled, e.g.:
- 注释:可以自定义哪些转换生效(似乎很鸡肋),默认全部生效
{
"presets": [
[
"@vue/babel-preset-jsx",
{
"vModel": false
}
]
]
}
- Options are:
- functional @vue/babel-sugar-functional-vue - Functional components syntactic sugar
- injectH @vue/babel-sugar-inject-h - Automatic h injection syntactic sugar。注释:render中自动注入h为渲染函数
- vModel @vue/babel-sugar-v-model - vModel syntactic sugar
- vOn @vue/babel-sugar-v-on - vOn syntactic sugar
——————————————————————————————————————————————————————————————————
- 注释:@vue/babel-preset-jsx 下的一部分,用来处理函数式组件的转换
@vue/babel-sugar-functional-vue
Babel Compatibility Notes
Usage
- Install the dependencies:
# for yarn:
yarn add @vue/babel-sugar-functional-vue
# for npm:
npm install @vue/babel-sugar-functional-vue --save
{
"plugins": ["@vue/babel-sugar-functional-vue"]
}
Details
- This plugin transpiles arrow functions that return JSX into functional components but only if it's an uppercase variable declaration or default export:
- 注释:小写的导出是不会被转换为选项对象的
// Original:
export const A = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
export const b = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
export default ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
// Result:
export const A = {
functional: true,
render: (h, {
props,
listeners
}) => <div onClick={listeners.click}>{props.msg}</div>
}
export const b = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
export default {
functional: true,
render: (h, {
props,
listeners
}) => <div onClick={listeners.click}>{props.msg}</div>
}
——————————————————————————————————————————————————————————————————
@vue/babel-sugar-inject-h
- 注释:@vue/babel-preset-jsx 下的一部分,用来向渲染函数注入变量h为渲染函数
Babel Compatibility Notes
Usage
- Install the dependencies:
# for yarn:
yarn add @vue/babel-sugar-inject-h
# for npm:
npm install @vue/babel-sugar-inject-h --save
{
"plugins": ["@vue/babel-sugar-inject-h"]
}
Details
- This plugin automatically injects h in every method that has JSX. By using this plugin you don't have to always specifically declare h as first parameter in your render() function.
- 注释:只有在使用了JSX的渲染函数中注入
——————————————————————————————————————————————————————————————————
@vue/babel-sugar-v-model
- 注释:@vue/babel-preset-jsx 下的一部分,用来处理JSX中的vModel
Babel Compatibility Notes
Usage
- Install the dependencies:
# for yarn:
yarn add @vue/babel-sugar-v-model
# for npm:
npm install @vue/babel-sugar-v-model --save
{
"plugins": ["@vue/babel-sugar-v-model"]
}
Details
- It is recommended to use camelCase version of it (vModel) in JSX, but you can use kebab-case too (v-model).
- 注释:也可以使用v-model的写法(但不建议)
——————————————————————————————————————————————————————————————————
@vue/babel-sugar-v-on
- 注释:@vue/babel-preset-jsx 下的一部分,用来处理JSX中的v-on
Babel Compatibility Notes
Usage
- Install the dependencies:
# for yarn:
yarn add @vue/babel-sugar-v-on
# for npm:
npm install @vue/babel-sugar-v-on --save
{
"plugins": ["@vue/babel-sugar-v-on"]
}
Details
- It is recommended to use camelCase version of it (vOn) in JSX, but you can use kebab-case too (v-on).
- 注释:可以使用v-on的形式,但不建议
————————————————————————————————————————————————————————————————————
Babel Compatibility Notes
- If using Babel 7, use 4.x
- If using Babel 6, use 3.x
- 疑问:Babel 7+ 不是使用 @vue/babel-preset-jsx 吗?
Requirements
- 疑问:Assumes you are using Babel with a module bundler e.g. Webpack, because the spread merge helper is imported as a module to avoid duplication.
Usage
npm install
babel-plugin-syntax-jsx
babel-plugin-transform-vue-jsx
babel-helper-vue-jsx-merge-props
babel-preset-env
--save-dev
{
"presets": ["env"],
"plugins": ["transform-vue-jsx"]
}
h auto-injection
- Starting with version 3.4.0 we automatically inject const h = this.$createElement in any method and getter (not functions or arrow functions) declared in ES2015 syntax that has JSX so you can drop the (h) parameter.
- 注释:从3.4.0开始,自动注入变量h指向渲染函数。
- 疑问:只有具有getter的函数才会自动注入
Vue.component('jsx-example', {
render () { // h will be injected
return <div id="foo">bar</div>
},
myMethod: function () { // h will not be injected
return <div id="foo">bar</div>
},
someOtherMethod: () => { // h will not be injected
return <div id="foo">bar</div>
}
})
@Component
class App extends Vue {
get computed () { // h will be injected
return <div id="foo">bar</div>
}
}
Difference from React JSX
- The equivalent of the above in Vue 2.0 JSX is:
- 注释:这里的语法和@vue/babel-preset-jsx插件支持的语法不同
render (h) {
return h('div', {
props: {
msg: 'hi',
onCustomEvent: this.customEventHandler // propsOnCustomEvent={this.customEventHandler}
},
attrs: {
id: 'foo' // id="foo"
},
domProps: {
innerHTML: 'bar' // domPropsInnerHTML="bar"
},
on: {
click: this.clickHandler // onClick={this.clickHandler}
},
nativeOn: {
click: this.nativeClickHandler // nativeOnClick={this.nativeClickHandler}
},
class: { // class={{ foo: true, bar: false }} 注释:通过{...}绑定一个表达式
foo: true,
bar: false
},
style: { // style={{ color: 'red', fontSize: '14px' }}
color: 'red',
fontSize: '14px'
},
key: 'key', // key="key"
ref: 'ref', // ref="ref"
// assign the `ref` is used on elements/components with v-for
refInFor: true, // refInFor
slot: 'slot' // slot="slot"
})
}
Component Tip
- If a custom element starts with lowercase, it will be treated as a string id and used to lookup a registered component. If it starts with uppercase, it will be treated as an identifier, which allows you to do:
- 注释:如果组件名以小写开头,那么需要进行注册后才能使用。如果是大写开头可以直接在JSX中使用该组件无需注册。新版本插件没有这个限制
import Todo from './Todo.js'
export default {
render (h) {
return <Todo/> // no need to register Todo via components option
}
}
JSX Spread
- JSX spread is supported, and this plugin will intelligently merge nested data properties. For example:
const data = {
class: ['b', 'c']
}
const vnode = <div class="a" {...data}/>
{ class: ['a', 'b', 'c'] }
Vue directives
- 在JSX中vue原生指令除了
v-show={value}
其他指令都不支持
- 自定义指令可以通过
v-name={value}
使用,但是不支持参数和修饰符
- Pass everything as an object via value, e.g. v-name={{ value, modifier: true }}
- 注释:可以通过传入一个对象来解决,应该是插件提供的,并不是模板语言原有。
- 疑问:只支持修饰符?
- 使用渲染函数第2个参数中的directives绑定指令