Apakah JSX dalam Vue? Artikel berikut akan memperkenalkan anda kepada JSX dalam Vue, memperkenalkan masa untuk menggunakan JSX, dan penggunaan asasnya dalam Vue2 saya harap ia akan membantu anda!
JSX ialah sambungan sintaks Javascript, yang mempunyai semua fungsi Javascript
dan pada masa yang sama Ia menggabungkan semantik dan intuitif html
. Ia membolehkan kami menulis sintaks templat dalam JS:
const el = <div>Vue 2</div>;
Kod di atas bukan HTML mahupun rentetan Ia dipanggil JSX, yang merupakan sintaks lanjutan JavaScript. JSX mungkin mengingatkan anda tentang sintaks templat, tetapi ia mempunyai kuasa pengaturcaraan penuh Javascript. [Cadangan berkaitan: tutorial video vuejs, pembangunan bahagian hadapan web]
Bila hendaknya mula menulis a Apabila anda hanya boleh menjana komponen tajuk secara dinamik melalui level
prop, anda boleh dengan cepat terfikir untuk melaksanakannya seperti ini:
<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> </script>
Menggunakan templat di sini bukanlah pilihan terbaik, pada setiap peringkat Sebahagian daripada kod itu diulang dalam tajuk, yang tidak ringkas dan cukup elegan. Jika anda cuba menulisnya dalam JSX, kod tersebut akan menjadi lebih mudah:
const App = { render() { const tag = `h${this.level}` return <tag>{this.$slots.default}</tag> } }
Atau jika anda menulis banyak fungsi render
, anda mungkin mendapati kod berikut sukar untuk ditulis:
createElement( 'anchored-heading', { props: { level: 1 } }, [ createElement('span', 'Hello'), ' world!' ] )
Terutama apabila templat yang sepadan sangat mudah:
<anchored-heading :level="1"> <span>Hello</span> world! </anchored-heading>
Pada masa ini, anda boleh menggunakan sintaks JSX dalam Vue, yang membolehkan kami kembali kepada sintaks lebih dekat dengan templat :
import AnchoredHeading from './AnchoredHeading.vue' new Vue({ el: '#demo', render: function (h) { return ( <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading> ) } })
Dalam proses pembangunan, komponen gesaan mesej Mesej sering digunakan Satu cara yang mungkin untuk menulisnya adalah seperti ini:
Message.alert({ messge: '确定要删除?', type: 'warning' })
Tetapi saya harap message
. boleh disesuaikan Untuk sesetengah gaya, pada masa ini anda mungkin perlu membuat Message.alert
sokongan JSX
(sudah tentu anda juga boleh menggunakan slot/html
dan kaedah lain untuk menyelesaikan masalah ini)
Message.alert({ messge: <div>确定要删除<span style="color:red">xxx</span>的笔记?</div>, type: 'warning' })
Selain itu, dalam fail .vue
Anda hanya boleh menulis satu komponen, yang mungkin tidak sesuai dalam sesetengah senario Banyak kali semasa menulis halaman, anda mungkin perlu membahagikan beberapa serpihan nod kecil kepada komponen kecil untuk digunakan semula komponen kecil sebenarnya mudah untuk ditulis Komponen fungsi boleh dilakukan. Biasanya, disebabkan oleh keterbatasan SFC, kita mungkin terbiasa menulis segala-galanya dalam satu fail, tetapi saya harus mengatakan bahawa kita boleh mencuba kaedah ini.
// 一个文件写多个组件 const Input = (props) => <input {...props} /> export const Textarea = (props) => <input {...props} /> export const Password = (props) => <input type="password" {...props} /> export default Input
Sebagai contoh, komponen Input dirangkumkan di sini Kami berharap untuk mengeksport komponen Kata Laluan dan komponen Textarea pada masa yang sama untuk memudahkan pengguna menggunakannya mengikut keperluan sebenar Komponen input secara dalaman, tetapi ia disesuaikan beberapa prop. Ia sangat mudah dalam JSX Ia pada asasnya cukup untuk menulis komponen fungsi mudah, dan hanya mengisytiharkan prop melalui antara muka. Tetapi jika anda menggunakan templat untuk menulisnya, anda mungkin perlu membahagikannya kepada tiga fail dan mungkin menambah satu lagi fail masukan index.js
untuk mengeksport tiga komponen.
Memandangkan intipati JSX ialah JavaScript, ia mempunyai keupayaan pengaturcaraan penuh JavaScript. Untuk contoh lain, kita perlu menggunakan sekeping logik untuk membalikkan set nod DOM Jika kita menulisnya dalam templat, kita mungkin perlu menulis dua keping kod.
Walaupun contoh ini mungkin tidak biasa, kita harus mengakui bahawa dalam beberapa senario, JSX masih lebih mudah untuk ditulis daripada templat.
Bermula dari Vue 2, templat akan disusun ke dalam JavaScript render function
sebelum dijalankan.
Vue mengesyorkan menggunakan templat untuk mencipta HTML anda dalam kebanyakan kes. Walau bagaimanapun, dalam beberapa senario, anda perlu menggunakan fungsi render, yang lebih fleksibel daripada templat. Ini render function
adalah legenda Virtual DOM
dalam fasa masa jalan.
dalam Vue 2 , penyusunan JSX memerlukan dua pakej @vue/babel-preset-jsx
dan @vue/babel-helper-vue-jsx-merge-props
. Pakej terdahulu bertanggungjawab untuk menyusun sintaks JSX, dan pakej terakhir digunakan untuk memperkenalkan fungsi mergeProps
runtime.
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
dan tambah konfigurasi dalam babel.config.js:
module.exports = { presets: ['@vue/babel-preset-jsx'], }
Interpolasi teks lalai dalam kod templat ialah Gunakan pendakap berganda:
<h1>{{ msg }}</h1>
Dalam JSX anda perlu menggunakan pendakap tunggal:
const name = 'Vue' const element = <h1>Hello, { name }</h1>
Seperti interpolasi teks dalam sintaks templat, mana-mana JS yang sah disokong dalam Ekspresi pendakap, seperti sebagai: 2 + 2
, user.firstName
, formatName(user)
, dsb.
在模板代码里面我们通过v-for
去遍历元素,通过v-if
去判断是否渲染元素,在JSX中,对于v-for
,可以使用for
循环或者array.map
来代替,对于v-if
,可以使用if-else
语句,三元表达式
等来代替
使用if-else
语句
const element = (name) => { if (name) { return <h1>Hello, { name }</h1> } else { return <h1>Hello, Stranger</h1> } }
使用三元表达式
const element = icon ? <span class="icon"></span> : null;
使用数组的map方法
const list = ['java', 'c++', 'javascript', 'c#', 'php'] return ( <ul> {list.map(item => { return <li>{item}</li> })} </ul> )
在模板代码中,一般通过 v-bind:prop="value"
或:prop="value"
来给组件绑定属性,在JSX
里面就不能继续使用v-bind指令了,而是通过单大括号的形式进行绑定:
const href = 'https://xxx.com' const element = <a href={href}>xxx</a>
const properties = {a: 1, b: 2}
此外,模板代码中能通过<div v-bind="properties"></div>
批量绑定标签属性。
在JSX中也有相应的替换方案:<div {...properties}></div>
。
class绑定同样也是使用单大括号的形式
const element = <div className={`accordion-item-title ${ disabled ? 'disabled' : '' }`}></div> const element = <div class={ [ 'accordion-item-title', disabled && 'disabled' ] } >Item</div>
style绑定需要使用双大括号
const width = '100px' const element = <button style={{ width, fontSize: '16px' }}></button>
在模板代码中通过v-on指令监听事件,在JSX中通过on
+ 事件名称的大驼峰写法来监听,且绑定事件也是用大括号,比如click事件要写成onClick
,mouseenter事件要写成onMouseenter
const confirm = () => { // 确认提交 } <button onClick={confirm}>确定</button>
有时候我们希望可以监听一个组件根元素上面的原生事件,这时候会用到.native
修饰符,但是在JSX中同样也不能使用,不过也有替代方案,监听原生事件的规则与普通事件是一样的,只需要将前面的on
替换为nativeOn
,如下
render() { // 监听下拉框根元素的click事件 return <CustomSelect nativeOnClick={this.handleClick}></CustomSelect> }
除了上面的监听事件的方式之外,我们还可以使用对象的方式去监听事件
render() { return ( <ElInput value={this.content} on={{ focus: this.handleFocus, input: this.handleInput }} nativeOn={{ click: this.handleClick }} ></ElInput> ) }
对于 .passive
、.capture
和 .once
这些事件修饰符,Vue 提供了相应的前缀可以用于 on
:
例如:
on: { '!click': this.doThisInCapturingMode, '~keyup': this.doThisOnce, '~!mouseover': this.doThisOnceInCapturingMode }
对于所有其它的修饰符,私有前缀都不是必须的,因为你可以在事件处理函数中使用事件方法:具体可查阅Vue规范文档。
大多数指令并不能在JSX中使用,对于原生指令,只有v-show
是支持的。
而v-model
是Vue
提供的一个语法糖,它本质上是由 value
属性(默认) + input
事件(默认)组成的,所以,在JSX
中,我们便可以回归本质,通过传递value
属性并监听input
事件来手动实现数据的双向绑定:
export default { data() { return { name: '' } }, methods: { // 监听 onInput 事件进行赋值操作 handleInput(e) { this.name = e.target.value } }, render() { // 传递 value 属性 并监听 onInput事件 return <input value={this.name} onInput={this.handleInput}></input> } }
此外,在脚手架vue-cli4
中,已经默认集成了对v-model
的支持,可以直接使用<input v-model={this.value}>
,如果项目比较老,也可以安装插件babel-plugin-jsx-v-model
来进行支持。
同样的,在JSX
中,对于.sync
也需要用属性+事件来实现,如下代码所示:
export default { methods: { handleChangeVisible(value) { this.visible = value } }, render() { return ( <ElDialog title="测试.sync" visible={this.visible} on={{ 'update:visible': this.handleChangeVisible }} ></ElDialog> ) } }
(1)默认插槽:
使用element-ui
的Dialog
时,弹框内容就使用了默认插槽,在JSX
中使用默认插槽的用法与普通插槽的用法基本是一致的,如下
render() { return ( <ElDialog title="弹框标题" visible={this.visible}> {/*这里就是默认插槽*/} <div>这里是弹框内容</div> </ElDialog> ) }
自定义默认插槽:
在Vue
的实例this
上面有一个属性$slots
,这个上面就挂载了一个这个组件内部的所有插槽,使用this.$slots.default
就可以将默认插槽加入到组件内部
export default { props: { visible: { type: Boolean, default: false } }, render() { return ( <div class="custom-dialog" vShow={this.visible}> {/**通过this.$slots.default定义默认插槽*/} {this.$slots.default} </div> ) } }
(2)具名插槽
有时候我们一个组件需要多个插槽,这时候就需要为每一个插槽起一个名字,比如element-ui
的弹框可以定义底部按钮区的内容,就是用了名字为footer
的插槽
render() { return ( <ElDialog title="弹框标题" visible={this.visible}> <div>这里是弹框内容</div> {/** 具名插槽 */} <template slot="footer"> <ElButton>确定</ElButton> <ElButton>取消</ElButton> </template> </ElDialog> ) }
自定义具名插槽:
在上节自定义默认插槽时提到了$slots
,对于默认插槽使用this.$slots.default
,而对于具名插槽,可以使用this.$slots.footer
进行自定义
render() { return ( <div class="custom-dialog" vShow={this.visible}> {this.$slots.default} {/**自定义具名插槽*/} <div class="custom-dialog__foolter">{this.$slots.footer}</div> </div> ) }
(3)作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的,这时候就需要用到作用域插槽,在JSX
中,因为没有v-slot
指令,所以作用域插槽的使用方式就与模板代码里面的方式有所不同了。比如在element-ui
中,我们使用el-table
的时候可以自定义表格单元格的内容,这时候就需要用到作用域插槽
data() { return { data: [ { name: 'xxx' } ] } }, render() { return ( {/**scopedSlots即作用域插槽,default为默认插槽,如果是具名插槽,将default该为对应插槽名称即可*/} <ElTable data={this.data}> <ElTableColumn label="姓名" scopedSlots={{ default: ({ row }) => { return <div style="color:red;">{row.name}</div> } }} ></ElTableColumn> </ElTable> ) }
自定义作用域插槽:
使用作用域插槽不同,定义作用域插槽也与模板代码里面有所不同。加入我们自定义了一个列表项组件,用户希望可以自定义列表项标题,这时候就需要将列表的数据通过作用域插槽传出来。
render() { const { data } = this // 获取标题作用域插槽 const titleSlot = this.$scopedSlots.title return ( <div class="item"> {/** 如果有标题插槽,则使用标题插槽,否则使用默认标题 */} {titleSlot ? titleSlot(data) : <span>{data.title}</span>} </div> ) }
只需要导入进来,不用再在components属性声明了,直接写在jsx中:
import MyComponent from './my-component' export default { render() { return <MyComponent>hello</MyComponent> }, }
我们可以定义method
,然后在method
里面返回JSX
,然后在render
函数里面调用这个方法,不仅如此,JSX
还可以直接赋值给变量,比如:
methods: { renderFooter() { return ( <div> <ElButton>确定</ElButton> <ElButton>取消</ElButton> </div> ) } }, render() { const buttons = this.renderFooter() return ( <ElDialog visible={this.visible}> <div>内容</div> <template slot="footer">{buttons}</template> </ElDialog> ) }
假设该消息聊天记录的消息类型只有三种:文本,图片,引用。一条消息里面可以包括任意类型的内容,引用类型消息内部可以不断嵌套引用其他任意类型消息。效果图大致如下:
消息数据结构如下:
message: [ // 每个数组的第一个参数为消息类型:0:文本 1:图片 2:引用。第二个参数为具体内容 [ 0, '文本' ], [ 1, '图片链接xxx' ], [ 2, [ [ 0, '引用文本文本文本' ], [ 1, '引用图片链接xxx' ] ] ] ]
主要有两个思路:
1、思路一:在render里返回一段用array.map渲染的消息模板,对于三种消息类型,使用if-else进行判断分别渲染,对于引用类型的消息,可以封装一个方法进行渲染,方法里面如果还有引用类型消息就继续递归渲染。
methods: { // 展示引用消息 showQuote (msg) { return ( <div class="content-quote"> <span class="quote-title">引用:</span> {msg.map(item => { if (item[0] === 0) { return <p class="content-text">{item[1]}</p> } else if (item[0] === 1) { return ( <el-image class="content-img" src={item[1]} preview-src-list={[item[1]]}> </el-image> ) } else { return this.showQuote(item[1]) } })} </div> ) } }, render (h) { return ( <ul class="chat-record-list"> {this.recordList.map(item => { return ( <li class="chat-record-item" key={item.timeStamp} > <div class="title"> <span class="person-info"> { `${item.sendUserNick}(${item.sendUserNet}) → ${item.receiverNick}(${item.receiverNet})` } </span> <span class="sendtime"> { this.formatTime('YYYY-mm-dd HH:MM:SS', item.timeStamp) } </span> </div> <div class="content"> {item.message.map(msg => { if (msg[0] === 0) { return <p class="content-text">{msg[1]}</p> } else if (msg[0] === 1) { return ( <el-image class="content-img" src={msg[1]} preview-src-list={[msg[1]]}> </el-image> ) } else { // 递归渲染引用类型消息 return this.showQuote(msg[1]) } })} </div> </li> ) })} </ul> ) }
2、思路二:第一种思路中封装的showQuote里面的代码与render中渲染消息内容的代码基本相似,因此其实现方式不够优雅。其实可以将整个消息的渲染封装成一个组件,在该组件内引入自己,然后再渲染自己。由于具体细节代码与上述类似,这里只给出思路代码,具体细节请忽略
// 当前组件就是RecordMessage组件,自己引入自己 import RecordMessage from './RecordMessage.vue' export default { props: { message: { type: Array, default: () => [] } }, render () { const parseMessage = msg => { const type = msg[0] if (type === 0) { // 文本 } else if (type === 2) { // 图片 } else { // 引用类型 return ( <div> <div>引用:</div> { msg[1].map(subMsg => ( // 自己递归渲染自己 <recored-message> </recored-message> )) } </div> ) } } return parseMessage(this.message) }
Atas ialah kandungan terperinci Apakah JSX dalam Vue? Bila nak guna? Bagaimana cara menggunakannya?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!