JSX 基本语法

JSX 基本语法

JSX 基本语法规则

  • 当遇到<>就会解析为标签:如果是 html 语法标准的标签就会直接解析为同名元素,比如 nav 或者 Nav 等 html 语法标准的标签不能做为组件的标签使用
  • 如果是其他标签需要特别解析就会创建一个对象即组件。而 vue 是在编译时 vue 文件中导出一个对象,导出的对象当做标签用。
  • 在使用标签时同样如果是驼峰命名在当做标签使用时可以用连字符号。
  • 遇到 { } 的就会以 JS 的语法解析,{ } 中的就是 js 变量或者 js 表达式,标签中的 js代码必须用 { } 包含。
  • 只有一个根标签。
  • 样式的类名指定不用用 class,要用 className。
  • 内联样式,要用 style={ {key:value } } 的形式去写。
  • 注释:{/* ….. */}

基本用法

创建 Vnodes:Vue 提供了一个 h() 函数用于创建 vnodes。

1
2
3
4
5
6
7
8
9
import { h } from 'vue'

const vnode = h(
'div', // type
{ id: 'foo', class: 'bar' }, // props
[
/* children */
]
)

h() 函数的使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 除了类型必填以外,其他的参数都是可选的
h('div')
h('div', { id: 'foo' })

// attribute 和 property 都能在 prop 中书写
// Vue 会自动将它们分配到正确的位置
h('div', { class: 'bar', innerHTML: 'hello' })

// 像 `.prop` 和 `.attr` 这样的的属性修饰符
// 可以分别通过 `.` 和 `^` 前缀来添加
h('div', { '.name': 'some-name', '^width': '100' })

// 类与样式可以像在模板中一样
// 用数组或对象的形式书写
h('div', { class: [foo, { bar }], style: { color: 'red' } })

// 事件监听器应以 onXxx 的形式书写
h('div', { onClick: () => {} })

// children 可以是一个字符串
h('div', { id: 'foo' }, 'hello')

// 没有 props 时可以省略不写
h('div', 'hello')
h('div', [h('span', 'hello')])

// children 数组可以同时包含 vnodes 与字符串
h('div', ['hello', h('span', 'hello')])
const vnode = h('div', { id: 'foo' }, [])

vnode.type // 'div'
vnode.props // { id: 'foo' }
vnode.children // []
vnode.key // null
  • 组件树中的 vnodes 必须是唯一的。
  • 如果想在页面上渲染多个重复的元素或者组件,你可以使用一个工厂函数来做这件事。
1
2
3
4
5
6
7
8
function render() {
return h(
'div',
Array.from({ length: 20 }).map(() => {
return h('p', 'hi')
})
)
}

render 函数:

  • 使用 render() 函数可以用 js 语言来构建 DOM。
  • Vue.js 还提供了 render() 函数来创建 HTML。让我们可以通过 JS 逻辑代码,更灵活的创建 HTML。
  • 例如在封装文章标题的 <Title> 组件时不确定最终生成的,具体是 h1~h6 的标签。这个时候,如果用 v-if 做判断,那么代码量太大,但用 render() 函数来实现就很简单。
  • vue3 的官方文档中,render 的参数都是用 h() 来表示。

模板语法

template 常用指令:v-html | v-text、v-if、v-for、v-moda l等。但是 template 的指令在JSX 是无法使用的,所以需要使用 JSX 的语法。

  1. v-html | v-text
  • 在 JSX 里面,如果要设置 dom 元素的 innerHTML,就用到 domProps。
1
2
3
4
5
6
7
8
render() {
const { htmlCode } = this
return (
<div>
<div domPropsInnerHTML={htmlCode}></div>
</div>
);
}
  1. v-on
  • 以 on 开头,并跟着大写字母的 props 会被当作事件监听器。比如,onClick 与模板中的 @click 等价。使用 bind,或者箭头函数来传参。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
h(
'button',
{
onClick(event) {
/* ... */
}
},
'click me'
)

<button
type="button"
onClick={this.handleClick.bind(this, event)}
>
click me
</button>

<button
onClick={(event) => {
/* ... */
}}
>
click me
</button>
  1. v-model
  • v-model 指令扩展为 modelValue 和 onUpdate:modelValue 在模板编译过程中,必须自己提供这些 props。
1
2
3
4
5
6
7
8
9
10
11
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
setup(props, { emit }) {
return () =>
h(SomeComponent, {
modelValue: props.modelValue,
'onUpdate:modelValue': (value) => emit('update:modelValue', value)
})
}
}

属性绑定(class,style,props,on)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
render:(h) => {
return h('div',{
   //给div绑定value属性
props: {
value:''
},
   //给div绑定样式
   style:{
     width:'30px'
   }, 
   //给div绑定点击事件  
on: {
click: () => {
console.log('点击事件')
}
},
})
}

{
// 和`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'
}

style 方式

内联样式可以直接写成 style=””。

在标签上使用 {…{}} 进行绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script>
export default {
data() {
return {
backgroundColor: 'blue',
styleObject: {
backgroundColor: 'red',
fontSize: '20px',
color: '#fff'
}
};
},
render() {
return (
<div>
<span {...{
style: {
backgroundColor: this.backgroundColor
},
props: {
menuStyle: 'left'
}
}}>我是蓝色背景</span>

<span {...{
style: this.styleObject
}}>我是红色背景</span>
</div>
);
}
};
</script>

class 方式

在 Vue 中 jsx 中可以直接写成 class=”xx”。实际上由于 class 是 JS 的保留字,因此在 DOM中其属性名为 className 而在 HTML 属性中为 class,我们也可以在 Vue 中这样写:

1
<div domPropsClassName="mt__xs"></div>

注意:如果同时写了 class=”xx” domPropsClassName=”yy” 那么后者的优先级较高,和位置无关。所以尽量还是采用 class 的写法。

在标签上使用 {…{ }} 进行绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
export default {
data() {
return {
isBlue: true,
classOjbect: ['red']
};
},
render() {
return (
<div>
<span {...{
class: {
blue: this.isBlue,
}
}}>我是蓝色背景</span>

<span {...{
class: this.classOjbect
}}>我是红色背景</span>
</div>
);
}
};
</script>

on 方式

事件绑定需要在事件名称前端加上 on 前缀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<button onClick={handleClick}>Click me</button>
</div>
</template>

<script>
export default {
methods: {
handleClick() {
console.log('Button clicked');
}
}
}
</script>

列表渲染

v-for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<ul>
<li v-for="{ id, text } in items" :key="id">
{{ text }}
</li>
</ul>
//等价写法1:
h(
'ul',
// assuming `items` is a ref with array value
items.value.map(({ id, text }) => {
return h('li', { key: id }, text)
})
)
//等价写法2:
<ul>
{items.value.map(({ id, text }) => {
return <li key={id}>{text}</li>
})}
</ul>

条件渲染

v-if

1
2
3
4
5
6
7
8
<div>
<div v-if="ok">yes</div>
<span v-else>no</span>
</div>
//等价写法1:
h('div', [ok.value ? h('div', 'yes') : h('span', 'no')])
//等价写法2:
<div>{ok.value ? <div>yes</div> : <span>no</span>}</div>

插槽语法

插槽就是子组件中提供给父组件使用的一个占位符。

在渲染函数中,插槽可以通过 setup() 的上下文来访问。每个 slots 对象中的插槽都是一个返回 vnodes 数组的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
export default {
props: ['message'],
setup(props, { slots }) {
return () => [
// 默认插槽:
// <div><slot /></div>
h('div', slots.default()),

// 具名插槽:
// <div><slot name="footer" :text="message" /></div>
h(
'div',
slots.footer({
text: props.message
})
)
]
}
}
//等价于
// 默认插槽
<div>{this.slots.default}</div>

<button>
{this.$scopedSlots.default()}
</button>

//具名插槽
<div>{slots.footer({ text: props.message })}</div>

let before = '';
if (this.$scopedSlots.before) {
before = this.$scopedSlots.before(props => props.text);
}
return (<button>
{ before }
{this.$scopedSlots.default()}
</button>)

//作用域插槽
{this.$scopedSlots.default({
isAdvancedPanelShow: this.isAdvancedPanelShow
})}

传递插槽

向组件传递子元素的方式与向元素传递子元素的方式有些许不同。我们需要传递一个插槽函数或者是一个包含插槽函数的对象而非是数组,插槽函数的返回值同一个正常的渲染函数的返回值一样——并且在子组件中被访问时总是会被转化为一个 vnodes 数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//单个默认插槽
h(MyComponent, () => 'hello')

// 具名插槽
// 注意 `null` 是必需的
// 以避免 slot 对象被当成 prop 处理
h(MyComponent, null, {
default: () => 'default slot',
foo: () => h('div', 'foo'),
bar: () => [h('span', 'one'), h('span', 'two')]
})
//等价于
// 默认插槽
<MyComponent>{() => 'hello'}</MyComponent>

// 具名插槽
<MyComponent>{{
default: () => 'default slot',
foo: () => <div>foo</div>,
bar: () => [<span>one</span>, <span>two</span>]
}}</MyComponent>
  • 插槽以函数的形式传递使得它们可以被子组件懒调用。这能确保它被注册为子组件的依赖关系,而不是父组件。这使得更新更加准确及有效。

事件修饰符

事件修饰符是通过『 : 』来修饰。

.stop : 阻止事件冒泡,在 JSX 中使用 event.stopPropagation() 来代替。

.prevent:阻止默认行为,在 JSX 中使用 event.preventDefault() 来代替。

.self:只当事件是从侦听器绑定的元素本身触发时才触发回调,使用下面的条件判断进行代替。

修饰符 前缀
.passive &
.capture !
.once ~
.capture.once或.once.capture ~!
作者

冷冷

发布于

2021-01-09

更新于

2021-03-10

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×