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' , { id : 'foo' , class : 'bar' }, [ ] )
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' })h ('div' , { class : 'bar' , innerHTML : 'hello' })h ('div' , { '.name' : 'some-name' , '^width' : '100' })h ('div' , { class : [foo, { bar }], style : { color : 'red' } })h ('div' , { onClick : () => {} })h ('div' , { id : 'foo' }, 'hello' )h ('div' , 'hello' )h ('div' , [h ('span' , 'hello' )])h ('div' , ['hello' , h ('span' , 'hello' )])const vnode = h ('div' , { id : 'foo' }, [])vnode.type vnode.props vnode.children vnode.key
组件树中 的 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 的语法。
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 > ); }
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 >
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' ,{ props : { value :'' }, style :{ width :'30px' }, on : { click : () => { console .log ('点击事件' ) } }, }) } { 'class' : { foo : true , bar : false }, style : { color : 'red' , fontSize : '14px' }, attrs : { id : 'foo' }, props : { myProp : 'bar' }, domProps : { innerHTML : 'baz' }, on : { click : this .clickHandler }, nativeOn : { click : this .nativeClickHandler }, directives : [ { name : 'my-custom-directive' , value : '2' , expression : '1 + 1' , arg : 'foo' , modifiers : { bar : true } } ], 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 () => [ h ('div' , slots.default ()), 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' )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
~!