赞
踩
目录
近几年都比较流行vue3+tsx相信react开发应该还是很了解jsx语法的,但是很多vue初中级开发还是很多人不会使用jsx,不知道怎么写。接下来慢慢教你哈(项目中使用的是vue2+jsx,因为主题是讲在vue中使用jsx)!
首先vue中有个render函数,他只支持xml的形式返回dom结构。开发中需要使用jsx语法就需要对项目文件进行改造:
下面是一个home.jsx:
- export default {
- name:'home',
- components:{},
- props:{},
- data(){
- return {
- info:{
- age:18
- }
- }
- },
- computed:{},
- watch:{},
- mounted(){},
- methods:{},
- render() {
- return <div>我是小明,我今年{this.info.age}岁</div>
- }
- }
- <script lang="tsx">
- export default {
- name:'home',
- components:{},
- props:{},
- data(){
- return {
- info:{
- age:18
- }
- }
- },
- computed:{},
- watch:{},
- mounted(){},
- methods:{},
- render() {
- return <div>我是小明,我今年{this.info.age}岁</div>
- }
- }
- </script>
页面呈现是这样的:
其实在vue中写jsx并没有多难,只是语法稍微有点不同而已。比如上面的例子,home.jsx文件后缀名从.vue变成了.jsx。内部把 template 的结构搬到了,render函数中。当然在render函数中写 template模版 结构和大致写法都一样,但是还是有些地方需要注意的。
上面的例子中.vue中变量是这样写的{{info.age}},jsx中双花括号变成了单个,并且变量需要附带this。
在render函数注册事件和 template 中的 v-on:click||@click不同,偏向原声写法如click事件onClick={tins.click}、input事件onInput={this.input},写法如下:
- export default {
- name: 'home',
- components: {},
- props: {},
- data() {
- return {
- info: {
- age: 18,
- gender: ''
- },
- }
- },
- computed: {},
- watch: {},
- mounted() {
- },
- methods: {
- Gender() {
- this.info.gender = '男';
- }
- },
- render() {
- return <div className="home">
- <div>我是小明,我今年{this.info.age}岁</div>
- <div>我是{this.info.gender}性</div>
- <button onClick={this.Gender}>查询我的性别</button>
- </div>
- }
- }
页面如下:
在render函数中没有指令这个东西了,比如v-if,v-show等这些都是自定义指令。在vue中写v-if或者v-show是这样的,如下:
v-if其实就是控制{}内的代码返回值。返回dom或者返回空
- export default {
- name: 'home',
- components: {},
- props: {},
- data() {
- return {
- info: {
- age: 18,
- gender: ''
- },
- }
- },
- computed: {},
- watch: {},
- mounted() {
- },
- methods: {
- getGender() {
- this.info.gender = '男';
- },
- genderDom() {
- return this.info.gender ? <div>我是{this.info.gender}性</div> : ''
- }
- },
- render() {
- return <div className="home">
- <div>我是小明,我今年{this.info.age}岁</div>
- {/* 三元表达写法 */}
- {
- this.info.gender ? <div>我是{this.info.gender}性</div> : ''
- }
-
- {/* 也可以用&&判断写 */}
- {
- this.info.gender && <div>我是{this.info.gender}性</div>
- }
-
- {/* 函数返回写法 */}
- {
- this.genderDom()
- }
- {/* 错误写法,在render不能直接使用if else */}
- {/* {
- if(this.info.gender){
- return <div>我是{this.info.gender}性</div>
- }else{
- return ''
- }
- } */}
- <button onClick={this.getGender}>查询我的性别</button>
- </div>
- }
- }
vue中的v-show大家都应该知道它只是控制样式display的none||block,那知道这点其实就很简单了:
- export default {
- name: 'home',
- components: {},
- props: {},
- data() {
- return {
- info: {
- age: 18,
- gender: ''
- },
- }
- },
- computed: {},
- watch: {},
- mounted() {
- },
- methods: {
- getGender() {
- this.info.gender = '男';
- },
- genderDom() {
- return this.info.gender ? <div>我是{this.info.gender}性</div> : ''
- }
- },
- render() {
- return <div className="home">
- <div>我是小明,我今年{this.info.age}岁</div>
- {/* 控制性别这个div的样式即可 */}
- <div style={{ display: this.info.gender ? 'block' : 'none' }}>我是
- {this.info.gender}性</div>
- <button onClick={this.getGender}>查询我的性别</button>
- </div>
- }
- }
注:上面代码中style={{ display: this.info.gender ? 'block' : 'none' }} 可以这样写?style={obj};obj={display: this.info.gender ? 'block' : 'none'},这样是不是就理解了,里面只是一个对象,还是单个花括号放变量的!
jsx中实现v-for,只是用.map代替了而已:
map只是jsx实现v-for手段之一,简单的来说任何返回改造后数组的函数都可以实现v-for
- // this.arr = [1,2,3,4,5];
-
- // map
- <div>
- {this.arr.map((item,index)=><div key={index}>{item}</div>)}
- </div>
- // reduce
- <div>
- {this.arr.reduce((arr,index)=>{
- return [...arr,<div key={index}>{item}</div>]
- },[]))}
- </div>
说到v-model,这个你就要理解vue中v-model其实只是一个语法糖,它就是:value,@input 两个方法组合起来的而已。那么在render中写法如下:
index.jsx
- import hobby from './component/hobby';
- export default {
- name: 'home',
- components: { hobby },
- props: {},
- data() {
- return {
- info: {
- age: 18,
- gender: '',
- hobby: '我是一个没有爱好的木头人!'
- },
- }
- },
- computed: {},
- watch: {},
- mounted() {
- },
- methods: {
- getGender() {
- this.info.gender = '男';
- },
- genderDom() {
- return this.info.gender ? <div>我是{this.info.gender}性</div> : ''
- }
- },
- render() {
- return <div className="home">
- <div>我是小明,我今年{this.info.age}岁</div>
- {/* 控制性别这个div的样式即可 */}
- <div style={{ display: this.info.gender ? 'block' : 'none' }}>我是
- {this.info.gender}性</div>
- <button onClick={this.getGender}>查询我的性别</button>
- <hobby value={this.info.hobby} onInput={(value) => { this.info.hobby =
- value }} />
- </div>
- }
- }
index.jsx中引入的子组件,hobby.jsx :
- export default {
- name: 'hobby',
- components: {},
- props: {
- value: {
- type: String,
- debugger: '我是一个没有爱好的木头人!'
- }
- },
- data() {
- return {
- }
- },
- computed: {},
- watch: {},
- mounted() {
- },
- methods: {
- },
- render() {
- return <div className="hobby">
- 我的爱好是:{this.value}
- <input value={this.value} onInput={(e) => { this.$emit('input',
- e.target.value); }} />
- </div>
- }
- }
好麻烦我就不每次截页面的图了。这个你们自己拷贝的代码去试试看就知道了!
hobby.jsx中的input 其实就是一个v-model的实现,然后在看一下index.jsx中在hobby组件上直接写
v-model,其实就是hobby接受了个value,然后再用$emit一个input事件去修改v-model的值。当然你也可以用vue的model去改变v-model的value,和input。使用自定义名字(因为开发中可能存在value或者input被其它参数使用了),这里就不多介绍啦。
最后如果你觉得v-model写起来好麻烦,想直接在render中使用v-model。那么也不是不行,
去安装一个 babel-plugin-jsx-v-model 依赖。然后在项目根目录找到babel.config.js,加入
就可以直接使用了!
在template中使用.sync是这样的:visible.sync="dialogVisible",但是在render中使用.sync是如下这样的
父组件index.jsx:
- import hobby from './component/hobby';
- import child from './component/child';
- export default {
- name: 'home',
- components: { hobby, child },
- props: {},
- data() {
- return {
- info: {
- age: 18,
- gender: '',
- hobby: '我是一个没有爱好的木头人!'
- },
- childData: "父亲传给child的数据",
- }
- },
- computed: {},
- watch: {},
- mounted() {
- },
- methods: {
- getGender() {
- this.info.gender = '男';
- },
- genderDom() {
- return this.info.gender ? <div>我是{this.info.gender}性</div> : ''
- }
- },
- render() {
- return <div className="home">
- <div>我是小明,我今年{this.info.age}岁</div>
- {/* 控制性别这个div的样式即可 */}
- <div style={{ display: this.info.gender ? 'block' : 'none' }}>我是
- {this.info.gender}性</div>
- <button onClick={this.getGender}>查询我的性别</button>
- {/* <hobby value={this.info.hobby} onInput={(value) => {
- this.info.hobby = value }} /> */}
- <hobby value={this.info.hobby} onInput={(value) => { this.info.hobby =
- value }} />
- <div>模拟.sycn</div>
- {this.childData}
-
- <child
- {...{
- props: {
- value: this.childData
- },
- on: {
- 'update:value': (val) => {
- console.log(val)
- this.childData = val;
- },
- }
- }}
- />
- </div>
- }
- }
简单来说,有可能会有这种情况,就是你写了个jsx的组件,但是你的同事他不会jsx。但是他要使用你写的jsx组件,然后用.sync,那你同事是这样写的<child value.sync='value' />。然后你就不知道怎么接受了,并且去改变value.sync='value'中的value?其实.sycn和v-model一样只是个语法糖而已,它是由.sycn前面的变量名,也就是bind一个value(名字自定义的)和 on一个‘update:value’名字的方法而已。
默认插槽只是简写(不用自定义插槽名称)的具名插槽,默认名称为default;
在 template 中使用默认插槽:
- // 父组件
- <template>
- <child>
- 我是插入的内容
- </child>
- </template>
-
-
- // 子组件 child
-
- <template>
- <div>
- <slot />
- </div>
- </template>
-
在 render 中使用默认插槽:
- // 父组件 写法1
- render(){
- return <div>
- <child>
- 默认插槽
- </child>
- </div>
- }
-
- // 父组件也可以这样写 写法2
-
- render(){
- return <div>
- <child {
- ...{
- scopedSlots: {
- default:()=>'默认插槽'
- }
- }
- }/>
- </div>
- }
-
- // child 子组件
- render(){
- return <div>
- {this.$scopedSlots.default()} // 写法1、写法2 都可以在这里显示,但是写法2会覆盖写法1
- {this.$slots.default} // this.$slots.default 是一个数组,所以直接使用展示
- </div>
- }
同默认插槽写法基本一样,只需吧对接key改成对应的名称即可。(默认插槽其实也是具名插槽,只是它的名称叫default,为了简写所以不传名字而已)
- // 父组件 写法1
- render(){
- return <div>
- <child>
- <div slot="soltName">
- 具有名字的插槽1
- </div>
- </child>
- </div>
- }
-
- // 父组件也可以这样写 写法2
-
- render(){
- return <div>
- <child {
- ...{
- scopedSlots: {
- soltName:()=>'具有名字的插槽2'
- }
- }
- }/>
- </div>
- }
-
- // child 子组件
- render(){
- return <div>
- {this.$scopedSlots.soltName()} // 写法1、写法2 都可以在这里显示,但是写法2会覆盖写法1
- {this.$slots.soltName} // this.$slots.soltName 是一个数组,所以直接使用展示
- </div>
- }
- // 父组件
- render(){
- return <div>
- <child {
- ...{
- scopedSlots: {
- soltName:(scope)=><div>
- child对我说:{scope}
- </div>
- }
- }
- }/>
- </div>
- }
-
- // child 子组件
- render(){
- return <div>
- {this.$scopedSlots.soltName('我是child,我要告诉你我的真心话!')}
- </div>
- }
从上面代码可以发现,
插槽对象有两种:scopedSlots、slots。
scopedSlots 具备 默认、具名、作用域;支持深入对象写法;使用插槽时表现为函数返回值也是数组,但是需要注意的时深入对象在和普通写法中重名的情况,深入对象插槽优先级别更高;
slots 具备 默认、具名;不支持深入对象写法;使用插槽时直接就是vnode数组;
相信很多人可能看不懂之前例子中这种穿参数的方式,其实你需要了解dom节点的props、attrs、on...这些属性的区别即可。这只是一个写法的不同:
- // template 中 你使用组件可能是这样写的
- <child :value='value' @click="click" style="width=100px" calss='class'/>
-
- // 上面给 child组件传了一个value,绑定了一个点击事件,加了一个行内样式,绑定了一个样式
-
- // jsx中可以这样写
-
- <child {...{
- props:{
- value:value,
- },
- on:{
- click:click
- },
- attrs:{
- style="width=100px",
- class="class"
- }
- }}/>
-
- // 这样写是不是看的更明白了?
- const obj = {
- props:{
- value:value,
- },
- on:{
- click:click
- },
- attrs:{
- style="width=100px",
- class="class"
- }
- }
- <child {...obj}/>
-
-
-
注:
on:就是绑定的事件,比如你需要绑定input事件只需要在on 对象中添加一个值key:value的形式,key是你的事件名称、value是你绑定的function。
props:其实就是子组件接受的props,和on差不多也是key:value的形式。
attrs:就是需要绑定在当前dom上的行内属性,比如class、style、type、name、id等,可以在控制台中看见。
借鉴了一下官方的介绍,可以看看:
- {
- // 与 `v-bind:class` 的 API 相同,
- // 接受一个字符串、对象或字符串和对象组成的数组
- 'class': {
- foo: true,
- bar: false
- },
- // 与 `v-bind:style` 的 API 相同,
- // 接受一个字符串、对象,或对象组成的数组
- style: {
- color: 'red',
- fontSize: '14px'
- },
- // 普通的 HTML attribute
- attrs: {
- id: 'foo'
- },
- // 组件 prop
- props: {
- myProp: 'bar'
- },
- // DOM property
- 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
- }
- }
- ],
- // 作用域插槽的格式为
- // { name: props => VNode | Array<VNode> }
- scopedSlots: {
- default: props => createElement('span', props.text)
- },
- // 如果组件是其它组件的子组件,需为插槽指定名称
- slot: 'name-of-slot',
- // 其它特殊顶层 property
- key: 'myKey',
- ref: 'myRef',
- // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
- // 那么 `$refs.myRef` 会变成一个数组。
- refInFor: true
- }
index.jsx
- import child from './component/child';
- export default {
- name: 'Test',
- components: { child },
- props: {},
- data() {
- return {
- age: 17,
- arr: [{ name: '小明', age: 16 }, { name: '小明妈妈', age: 40 }, { name: '小明爸爸', age: 46 }]
- }
- },
- computed: {
- setOptions() {
- return {
- //插槽属性
- scopedSlots: {
- default: (scope) => <div>我是默认插槽,{this.showScope(scope)}
- </div>,
- slotsName: (scope) => <div>我是具名插槽,我的名字叫slotsName,
- {this.showScope(scope)}</div>,
- },
- props: {
- age: this.age,
- },
- attrs: {
- style: 'width:100%;height:200px;background-color:red'
- },
- on: {
- ageAdd: () => { this.age++ },
- input: (count) => { this.age = count }
- }
- }
- }
- },
- watch: {},
- mounted() {
- },
- methods: {
- showScope(scope) {
- return `子组件传给我的参数是:${scope}`
- }
- },
- render() {
- return <div class="Test">
- <div>父组件</div>
- 我的年龄是:{this.age}
- <div>子组建</div>
- <child {...this.setOptions} />
- <div>小明全家</div>
- <ul>
- {
- this.arr.map((item, index) => <div key={index}>
- {item.name}:{item.age}
- </div>)
- }
- </ul>
- {/* <child {...this.setOptions} v-model={this.age}/> */}
- </div>
- }
- }
child.jsx
- export default {
- name: 'child',
- components: {},
- // model: {
- // prop: "inputValue",
- // event: "up",
- // },
- props: ['age'],
- data() {
- return {
- }
- },
- computed: {},
- watch: {},
- mounted() {
- console.log(this.$attrs, this.$listeners)
- },
- methods: {
- },
- render() {
- return <div class="child">
- <button onClick={() => { this.$emit('ageAdd') }}>老一岁</button>
- <div>
- 修改年纪:
- <input value={this.age} onInput={(e) => { this.$emit('input',
- e.target.value) }} />
- {/* <input value={this.$attrs.inputValue} onInput={(e) => {
- this.$emit('up', e.target.value) }} /> */}
- </div>
- <div>插槽</div>
- {this.$scopedSlots.default&&this.$scopedSlots.default(666)}
- {this.$scopedSlots.slotsName&&this.$scopedSlots.slotsName(789)}
- </div>
- }
- }
可以去试试看,方法了解开发中需要灵活运用,逻辑清晰。
其实官方介绍的就挺详细的:渲染函数 & JSX — Vue.js
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。