JSX 简介
JSX = Javascript + XML。在 Javascript 里写 XML,同时拥有 Javascript 的灵活性和 HTML 的语义化。
Template vs JSX
template 是 Vue 的默认写法,也更推荐。因为 template 语法是固定的,Vue 在编译层面为它做了很多静态标记的优化。JSX 虽然性能不如 template,但更加灵活。
Vue 会将 template 解析为 render 函数,运行时,通过 render 函数返回虚拟 dom。可通过 Vue Devtools 的 “show render code” 看到组件编译后的结果
_c: createElement、_v: createTextNode、_s: toString
通过 @vue/babel-preset-jsx( babel 7+)或 vuejs/babel-plugin-transform-vue-jsx(Babel 6)可以在 Vue 里去转换 jsx 语法,最终转换为 createElement 调用的形式。可在 render 里直接返回,或者写在 methods 里,不建议写在 computed 里,会缓存。
附个 render 生命周期:
babel-preset-jsx 的安装和配置
JSX 语法
建议先学习下 Vue 的渲染函数
JSX 的使用可从 createElement 的数据对象上借鉴,亲测都可用,下面来自官方:
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML attribute
attrs: {
id: 'foo'
},
// 组件 prop
props: {
myProp: 'bar'
},
// DOM property
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
}
}
],
// 作用域插槽的格式为
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其它特殊顶层 property
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 那么 `$refs.myRef` 会变成一个数组。
refInFor: true
}
上面的直接搬到 JSX 中:
render (h) { // 最好带个 h,否则无法生效
const data = {
ref: 'myRef',
// 指令
directives: [{
name: 'my-custom-directive',
value: '2'
}],
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
props: {
level: this.level
},
on: {
'click': this.clickHandler
}
}
// 也可以分开写, 插件会智能合并
return <div class="a" {...data}></div>
}
或者
<custom-button
id="custom-button"
style={{ marginTop: "10px" }}
count={count}
type="button"
domPropsInnerHTML={`hello ${this.count}.`}
onChange={onChange}
/>
class,staticClass,style,key,ref,refInFor,slot,scopedSlots 这些被认为是顶级属性,而 props、html 的 attrs 属性,不需要加前缀,插件会统一分类到 attrs 属性下,在运行阶段根据是否 props 声明,再决定归属是 props 还是 attrs。
上面没有覆盖到的用法,如下
v-model
如果安装了 babel-preset-jsx,可直接用
<custom-dialog vModel={this.visible}></custom-dialog>
如果不支持 vModel,可以用事件监听的方式
<custom-dialog on-input={(val) => this.visible = val} value={this.visible}></custom-dialog>
若 custom-dialog 使用的是自定义 model
model: {
prop: 'visible',
event: 'change'
}
那么,prop 和 event 都要改为对应的
<custom-dialog on-change={this.handleChange} visible={this.dialogVisible}></custom-dialog>
.sync
需要改为事件的写法
<custom-dialog
on: {{
'update:visible': val => {
this.dialogVisible= val
}
}}
visible={this.dialogVisible}
></custom-dialog>
v-if 和 v-for
只要在原生 Javascript 能够实现的,Vue 的 JSX 就不会有专门的替代方案,比如 v-if 和 v-for :
v-if:
return type ? <div> type 1 </div> : <div> type 0 </div>
// 或
if (type) {
return <div> type 1 </div>
} else {
return <div> type 0 </div>
}
v-for:
<div>
{ arr.length ? arr.map(item => <li>item</li>) : <li>empty</li> }
</div>
默认插槽、具名插槽、作用域插槽
需要借由 this.$slots 或 this.$scopedSlots 来传入
默认插槽:
<div> {this.$slots.default} </div>
具名插槽:
<div> {this.$slots.title} </div>
作用域插槽:
<div> {this.$scopedSlots.title({text:'hello scope'})} </div>
引入图片
<img src={require('../images/icon.svg')} alt="图片" />
函数式组件
这里直接看 Vue 官方文档 就很详尽了。需要注意下,render 的第二个参数 context 的传入,组件的一切需要都是通过 context 参数传递的
Vue.component('my-component', {
functional: true,
// Props 是可选的
props: {
// ...
},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {
// ...
}
})
事件 & 按键修饰符
事件 & 按键修饰符
以上是平常使用时,踩过的坑。后续有新增的再往里补充。友情提醒,官方文档也过几眼,能解决大部分问题。