当前位置: 首页 > news >正文

玩转代码| Vue 中 JSX 的特性,这一篇讲的明明白白

目录

什么时候使用JSX

JSX在Vue2中的基本使用

配置

文本插值

条件与循环渲染

属性绑定

事件绑定

v-show与v-model

插槽

使用自定义组件

在method里返回JSX


JSX是一种Javascript的语法扩展,即具备了Javascript的全部功能,同时又兼具html的语义化和直观性。它可以让我们在JS中写模板语法:

const el = <div>Vue 2</div>;

上面这段代码既不是 HTML 也不是字符串,被称之为 JSX,是 JavaScript 的扩展语法。JSX 可能会使人联想到模板语法,但是它具备 Javascript 的完全编程能力。

什么时候使用JSX

当开始写一个只能通过 level prop 动态生成标题 (heading) 的组件时,你可能很快想到这样实现:

<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>

这里用template模板并不是最好的选择,在每一个级别的标题中重复书写了部分代码,不够简洁优雅。如果尝试用 JSX 来写,代码就会变得简单很多:

const App = {render() {const tag = `h${this.level}`return <tag>{this.$slots.default}</tag>}
}

或者如果你写了很多 render 函数,可能会觉得下面这样的代码写起来很痛苦:

createElement(  'anchored-heading', {  props: {  level: 1  }  }, [  createElement('span', 'Hello'),  ' world!'  ]  
)

特别是对应的模板如此简单的情况下:

<anchored-heading :level="1">  <span>Hello</span> world!  
</anchored-heading>

这时候就可以在 Vue 中使用 JSX 语法,它可以让我们回到更接近于模板的语法上:

import AnchoredHeading from './AnchoredHeading.vue'  new Vue({  el: '#demo',  render: function (h) {  return (  <AnchoredHeading level={1}>  <span>Hello</span> world!  </AnchoredHeading>  )  }  
})

在开发过程中,经常会用到消息提示组件Message,可能的一种写法是这样的:

Message.alert({messge: '确定要删除?',type: 'warning'
})

但是希望message可以自定义一些样式,这时候你可能就需要让Message.alert支持JSX了(当然也可以使用插槽/html等方式解决)

Message.alert({messge: <div>确定要删除<span style="color:red">xxx</span>的笔记?</div>,type: 'warning'
})

此外,一个 .vue 文件里面只能写一个组件,这个在一些场景下可能不太方便,很多时候写一个页面的时候其实可能会需要把一些小的节点片段拆分到小组件里面进行复用,这些小组件其实写个简单的函数组件就能搞定了。平时可能会由于SFC的限制让我们习惯于全部写在一个文件里,但不得不说可以尝试一下这种方式。

// 一个文件写多个组件
const Input = (props) => <input {...props} />
export const Textarea = (props) => <input {...props} />
export const Password = (props) => <input type="password" {...props} />export default Input

比如这里封装了一个 Input 组件,我们希望同时导出 Password 组件和 Textarea 组件来方便用户根据实际需求使用,而这两个组件本身内部就是用的 Input 组件,只是定制了一些 props。在 JSX 里面就很方便,写个简单的函数组件基本上就够用了,通过 interface 来声明 props 就好了。但是如果是用模板来写,可能就要给拆成三个文件,或许还要再加一个 index.js 的入口文件来导出三个组件。

由于 JSX 的本质就是 JavaScript,所以它具有 JavaScript 的完全编程能力。再举个例子,我们需要通过一段逻辑来对一组 DOM 节点做一次 reverse,如果在模板里面写,那估计要写两段代码。

虽然这个例子可能不太常见,但是不得不承认,在一些场景下,JSX 还是要比模板写起来更加顺手。

从 Vue 2 开始,template 在运行之前,会被编译成 JavaScript 的 render function

Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML。然而在一些场景中,就需要使用 render 函数,它比 template 更加灵活。这些 render function 在运行时阶段,就是传说中的 Virtual DOM

JSX在Vue2中的基本使用

配置

在 Vue 2 中,JSX 的编译需要依赖 @vue/babel-preset-jsx 和 @vue/babel-helper-vue-jsx-merge-props 这两个包。前面这个包来负责编译 JSX 的语法,后面的包用来引入运行时的 mergeProps 函数。

npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props

并在babel.config.js中添加配置:

module.exports = {presets: ['@vue/babel-preset-jsx'],
}

文本插值

模板代码里文本插值默认是用双大括号:

<h1>{{ msg }}</h1>

在JSX中则需要使用单大括号:

const name = 'Vue'
const element = <h1>Hello, { name }</h1>

和模板语法中的文本插值一样,大括号内支持任何有效的JS表达式,比如:2 + 2user.firstNameformatName(user)等。

条件与循环渲染

在模板代码里面我们通过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 (<ElInputvalue={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规范文档。

v-show与v-model

大多数指令并不能在JSX中使用,对于原生指令,只有v-show是支持的。

v-modelVue提供的一个语法糖,它本质上是由 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 (<ElDialogtitle="测试.sync"visible={this.visible}on={{ 'update:visible': this.handleChangeVisible }}></ElDialog>)}
}

插槽

(1)默认插槽:

使用element-uiDialog时,弹框内容就使用了默认插槽,在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}><ElTableColumnlabel="姓名"scopedSlots={{default: ({ row }) => {return <div style="color:red;">{row.name}</div>}}}></ElTableColumn></ElTable>)}

自定义作用域插槽:

使用作用域插槽不同,定义作用域插槽也与模板代码里面有所不同。加入我们自定义了一个列表项组件,用户希望可以自定义列表项标题,这时候就需要将列表的数据通过作用域插槽传出来。

render() {const { data } = this// 获取标题作用域插槽const titleSlot = this.$scopedSlots.titlereturn (<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里返回JSX

我们可以定义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>)}

相关文章:

玩转代码| Vue 中 JSX 的特性,这一篇讲的明明白白

目录 什么时候使用JSX JSX在Vue2中的基本使用 配置 文本插值 条件与循环渲染 属性绑定 事件绑定 v-show与v-model 插槽 使用自定义组件 在method里返回JSX JSX是一种Javascript的语法扩展&#xff0c;即具备了Javascript的全部功能&#xff0c;同时又兼具html的语义…...

(vue)el-descriptions 描述列表无效

(vue)el-descriptions 描述列表无效 原因&#xff1a;element 的版本不够 解决&#xff1a;运行下面两个命令 npm uninstall element-ui //卸载之前安装的版本 npm i element-ui -S //重新安装解决参考&#xff1a;https://blog.csdn.net/weixin_59769148/article/details/1…...

ios 苹果手机日期格式问题

目录 问题解决其他 问题 ios 无法识别的时间戳格式&#xff1a;2023-10-17 11:10:49 可识别的&#xff1a; 2023/10/17 11:10:49 解决 const startTime 2023/10/17 11:10:49 startTime.replace(/-/g, /)// 获取时间差值 export const useDateDiff (startTime , endTime …...

学习嵌入式系统的推荐步骤:

学习嵌入式系统的推荐步骤&#xff1a; 00001. 选择一款Linux发行版作为主要操作系统&#xff0c;如RedHat、Ubuntu、Fedora等。进入Linux后&#xff0c;使用终端进行任务操作。建议不要使用虚拟机&#xff0c;如有需要可考虑双系统安装。 00002. 00003. 学习C语言、数…...

勒索病毒LockBit2.0 数据库(mysql与sqlsever)解锁恢复思路分享

0.前言 今天公司服务器中招LockBit2.0勒索病毒&#xff0c;损失惨重&#xff0c;全体加班了一天基本解决了部分问题&#xff0c;首先是丢失的文件数据就没法恢复了&#xff0c;这一块没有理睬&#xff0c;主要恢复的是两个数据库&#xff0c;一个是16GB大小的SQLserver数据库&…...

超简单小白攻略:如何利用黑群晖虚拟机和内网穿透实现公网访问

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…...

Ubuntu 16.04 LTS third maintenance update release

Ubuntu 16.04 LTS (Xenial Xerus)今天迎来的第三个维护版本更新中&#xff0c;已经基于Linux Kernel 4.10内核&#xff0c;而且Mesa图形栈已经升级至17.0版本。Adam Conrad表示&#xff1a;“像此前LTS系列相似&#xff0c;16.04.3对那些使用更新硬件的用户带来了硬件优化。该版…...

Java学习_day01_hello java

构成 JDK JDK是java开发者工具&#xff0c;由JRE和一些开发工具组成。JRE JRE是java运行环境&#xff0c;由JVM和java核心类库组成。JVM JVM是java虚拟机&#xff0c;主要用来运行字节码。 执行过程 由IDE或文本编辑器&#xff0c;编写源代码&#xff0c;并将文件保存为*.ja…...

UnitTesting 单元测试

1. 测试分为两种及详细介绍测试书籍: 1.1 Unit Test : 单元测试 - test the business logic in your app : 测试应用中的业务逻辑 1.2 UI Test : 界面测试 - test the UI of your app : 测试应用中的界面 1.3 测试书籍网址:《Testing Swift》 https://www.hackingwithswift.c…...

C++内存管理:其五、指针类型转换与嵌入式指针

一、内存池的缺陷 作者在上一版本里面介绍了链表实现内存池&#xff0c;其中有一个小缺陷&#xff1a;虽然较少了cookie的内存损耗&#xff0c;但是加入了一个额外的指针&#xff0c;仍然需要占用内存。我们仔细看内存池的设计思想&#xff0c;可以发现一个关键点&#xff1a;…...

常见锁的分类

入职体验&#xff1a; 今天运维岗位刚入职&#xff0c;但是目前还没有办理入职手续&#xff0c;但是领导发了一堆资料&#xff01;看了一下&#xff0c;非常多的新东西&#xff0c;只能说努力一把&#xff01;&#xff01;&#xff01; 一、锁的分类 1.1 可重入锁、不可重入锁…...

vue 鼠标划入划出多传一个参数

// item可以传递弹窗显示数据&#xff0c; $event相关参数可以用来做弹窗定位用 mouseover"handleMouseOver($event, item)" mouseleave"handleMouseLeave($event, item)"举个栗子&#xff1a; 做一个hover提示弹窗组件(用的vue3框架 less插件) 可以将组件…...

svn项目同步到gitLab

安装git 确保安装了git 新建一个文件夹svn-git 在文件夹中新建userinfo.txt文件&#xff0c;映射svn用户,这个文件主要是用于将SVN用户映射为Git用户&#xff08;昵称及其邮箱&#xff09;。 userinfo.txt具体格式如下&#xff1a; admin admin <admin163.com> lis…...

图解Dubbo,Dubbo 服务治理详解

目录 一、介绍1、介绍 Dubbo 服务治理的基本概念和重要性2、阐述 Dubbo 服务治理的实现方式和应用场景 二、Dubbo 服务治理的原理1、Dubbo 服务治理的架构设计2、Dubbo 服务治理的注册与发现机制3、Dubbo 服务治理的负载均衡算法 三、Dubbo 服务治理的实现方式1、基于 Docker 容…...

Css 如何取消a链接点击时的背景颜色

要取消 <a> 链接点击时的背景颜色&#xff0c;可以使用 CSS 的伪类 :active。你可以通过为 a:active 应用 background-color 属性设置为 transparent 或者 none&#xff0c;来取消点击时的背景色。下面是一个示例&#xff1a; a:active {background-color: transparent;…...

1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计

文章目录 一、HttpContext模块二、HttpServer模块三、HttpContext模块实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;接口 四、HttpServer模块实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#…...

ABAP 新增PO计划行时 新增行交货日期默认当前最大交期

ABAP 新增PO计划行时 新增行交货日期默认当前最大交期 DATA: ls_poitem TYPE mepoitem. DATA: ls_jhh TYPE meposchedule. DATA: ls_poitemc TYPE REF TO if_purchase_order_item_mm. DATA: is_persistent TYPE mmpur_bool. DATA: lt_eket TYPE TABLE OF eket. DATA: ls_e…...

VSCode怎么创建Java项目

首先安装好Java的开发环境&#xff1a;JDK在VSCode中安装适用于Java开发的插件。打开VSCode&#xff0c;点击左侧的扩展图标&#xff0c;搜索并安装Java Extension Pack插件。等待安装完成后&#xff0c;重启VSCode生效。创建一个新的Java项目&#xff0c;按下Ctrl Shift P&a…...

软件工程与计算(十四)详细设计中面向对象方法下的模块化

一.面向对象中的模块 1.类 模块化是消除软件复杂度的一个重要方法&#xff0c;每个代码片段相互独立&#xff0c;这样能够提高可维护性。在面向对象方法中&#xff0c;代码片段最重要的类&#xff0c;整个类的所有代码联合起来构成独立的代码片段。 模块化希望代码片段由两部…...

商城免费搭建之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...