JSX 简介:https://zh-hans.reactjs.org/docs/introducing-jsx.html
比较
template
学习成本低 大量内置指令简化开发 组件作用域
CSS
- 模版语法(
HTML
的扩展) 数据绑定使用
Mustache
语法(双大括号)<span>{{ msg }}</span>
jsx|tsx
灵活
JavaScript
的语法扩展- 数据绑定使用单引号
扩展
引入
Vue
官方文档(https://cn.vuejs.org/v2/guide/comparison.html#JSX-vs-Templates)更抽象一点来看,我们可以把组件区分为两类:一类是偏视图表现的
**(presentational)**
,一类则是偏逻辑的**(logical)**
。我们推荐在前者中使用模板,在后者中使用**JSX**
或渲染函数。这两类组件的比例会根据应用类型的不同有所变化,但整体来说我们发现表现类的组件远远多于逻辑类组件。
常用
v-for 与 v-if
jsx|tsx
没有v-for
与v-if
,分别条件运算符(?:)
替代v-if
、array.map
替代v-for
render() {
return (
// v-if
{this.type && <div>true</div>}
// v-if / v-else
{this.type ? <div>true</div> : <div>false</div>}
// v-for
{this.options.map(item => {
<div>{item.title}</div>
})}
)
}
指令
v-model
v-model_trim
domPropsInnerHTML
等价于v-html
domPropsTextContent
等价于v-text
render() {
return (
<div><input v-model={this.keyValue} /></div>
<div><input v-model_trim={this.keyValue} /></div>
<div><p domPropsInnerHTML={this.html} /></div>
<div><p domPropsTextContent={this.text} /></div>
)
}
事件
遵循一个规则或者如下:事件绑定需要在事件名称前端加上
on
前缀,原生事件添加nativeOn
@click
等价于on-click
等价于onClick
等价于vOn:click
vOn:keyup_enter_native
@click.native
等价于nativeOnClick
注意: 传递参数不能直接使用
**onClick={this.search('params')}**
,这会导致**jsx|tsx**
每次都会**render**
会自执行一次方法(重则会死循环) 处理: 应该使用**bind**
,或箭头函数来处理传参**onClick={()=> this.search('params')}**
render() {
return (
<div><input v-model={this.keyValue} on-input={this.inputText} /></div>
<div><input v-model={this.keyValue} vOn:click_stop_prevent={this.inputText} /></div>
<div><input v-model={this.keyValue} vOn:keyup_enter_native={() => this.search()} /></div>
<div><el-button nativeOnClick={this.handleClick}>Native click</el-button></div>
)
}
监听.sync修饰符的事件
https://github.com/vuejs/babel-plugin-transform-vue-jsx/blob/master/example/example.js
// jsx | tsx
<el-dialog
visible={this.dialogVisible}
{...{ on: { 'update:visible': console.log } }}
before-close={() => (this.dialogVisible = false)}
/>
// jsx 发现这样写也ok
// 在 vue2.x 版本使用 tsx 这种方式打包会报错
<el-dialog
visible:sync={this.dialogVisible}
before-close={() => (this.dialogVisible = false)}
/>
// template
// 在 vue2.x 版本使用 tsx 这种方式打包会报错
<el-dialog
:visible.sync="dialogVisible"
:before-close="handleClose"
/>
class绑定
注:与
react
的jsx
绑定的有区别,react
中使用className
,vue
中使用class
<button
onClick={handleClick}
disabled={disabled}
type={'button'}
v-loading={loading}
class={[
'ws-btn',
'ws-button',
type ? `ws-button--${type}` : '',
{
'is-disabled': disabled,
'is-circle': circle,
'is-block': block
}
]}
>
{defaultSlots}
</button>
自定义组件
导入直接使用,不需要在
components
属性声明
import HelloWolrd from './HelloWorld';
export default {
name: 'App',
render() {
return <HelloWorld msg='Welcome to Your Vue.js App' />;
}
};
新版本
所有形如 on-update:*
的 prop
都有一个对应的 onUpdate*
属性可供使用。
由于
JSX
自身的规定,on-update:*
和onUpdate:*
不是合法的prop
名称(如果你发现我的代码没有这样写,那一定是幻觉,请偷偷给我提醒下),如下所示
<d-select @update:value="..." />
// 在 JSX 中可以写为
<DSelect onUpdateValue={...} />
样式模块化
样式文件必须已
.module
文件名结尾才能使用模块的方式vue
单文件使用scoped
可以实现模块化:<style scoped>
Button.tsx
import Vue, { VNode } from 'vue';
// @ts-ignore
import ButtonModule from './Button.module.less';
export default Vue.extend({
name: 'Button',
props: {
type: String,
loading: Boolean,
disabled: Boolean,
circle: Boolean,
block: Boolean
},
methods: {
handleClick(event: MouseEvent) {
if (this.loading) {
event.preventDefault();
} else if (!this.disabled) {
this.$emit('click', event);
}
}
},
render(): VNode {
const { type, loading, disabled, circle, block, $slots, handleClick } = this;
const loadingNode = () => {
return loading && <van-loading color='inherit' size='15px' />;
};
const defaultSlots = () => {
return (
$slots?.default && (
<span class={loading ? ButtonModule['button--loading-text'] : null}>{$slots.default}</span>
)
);
};
return (
<button
onClick={handleClick}
disabled={disabled}
type={'button'}
class={[
ButtonModule['btn'],
ButtonModule['button'],
type ? ButtonModule[`button--${type}`] : '',
disabled ? ButtonModule['is-disabled'] : '',
loading ? ButtonModule['is-loading'] : '',
circle ? ButtonModule['is-circle'] : '',
block ? ButtonModule['is-block'] : '',
]}
>
{loadingNode()}
{defaultSlots()}
</button>
);
}
});
Button.module.less
@theme-color: #294ba3;
@theme-color-active: #26479a;
@white: #fff;
@secondary-font-color: #545454;
.button {
display: inline-block;
height: 0.88rem;
line-height: 0.88rem;
white-space: nowrap;
cursor: pointer;
background: @white;
border: 1px solid #ddd;
color: @secondary-font-color;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: 0.1s;
font-weight: 500;
user-select: none;
padding: 0 0.24rem;
font-size: 15px;
border-radius: 0.12rem;
&.is-circle {
height: 28px;
line-height: 28px;
border-radius: 0.28rem;
}
&.is-loading {
display: flex;
justify-content: center;
align-items: center;
.van-loading {
display: flex;
color: inherit;
font-size: inherit;
margin-right: 0.5em;
}
}
&.is-active,
&:active {
background: #fafafa;
}
&.is-disabled,
&.is-disabled:active,
&.is-disabled:focus,
&.is-disabled:hover {
cursor: not-allowed;
color: #ccc;
background-color: @white;
}
&--primary {
border-color: @theme-color;
background: @theme-color;
color: @white;
&.is-disabled,
&.is-disabled:active,
&.is-disabled:focus,
&.is-disabled:hover {
cursor: not-allowed;
color: @white;
background-color: #ddd;
border-color: #ddd;
}
&.is-active,
&:active {
background: @theme-color-active;
}
}
&--border {
color: #294ba3;
background: @white;
border-color: #3975c6;
&.is-disabled,
&.is-disabled:active,
&.is-disabled:focus,
&.is-disabled:hover {
cursor: not-allowed;
color: #ccc;
background-color: @white;
border-color: @white;
}
&.is-disabled,
&.is-disabled:active,
&.is-disabled:focus,
&.is-disabled:hover {
cursor: not-allowed;
color: #ccc;
background-color: @white;
border-color: @white;
}
}
&--text {
height: 0.88rem;
background: transparent;
border: none;
}
& + .button {
margin-left: 0.08rem;
}
&--loading-text {
opacity: 0.2;
}
}
.is-block {
width: 100%;
display: block;
}
效果如下
命名规则以
XXX.module
名称为主
coding
vue2:https://github.com/WuChenDi/Front-End/tree/master/05-Vue/vue2-jsx
参考:
vue2
https://github.com/vuejs/jsx#readme
Babel Plugin JSX for Vue 3.0
https://github.com/vuejs/jsx-next#readme
vite
https://github.com/vitejs/vite/tree/main/packages/plugin-vue-jsx#readme
注:
vue jsx 2X 版本不支持空标签 <></>
的写法,3X 支持
React 中可以使用空标签 <></>
和 <react.Fragment></react.Fragment>
来实现包裹元素,其实空标签本质就只是 react.Fragment
的一个语法糖