• Vue 之 JSX 初识篇


    Vue 之 JSX 初识篇

    介绍一下 JSX

    JSX 简介

    JSX 是一种 Javascript 的语法扩展,JSX = Javascript + XML,即在Javascript里面写XML,因为JSX的这个特性,所以他即具备了Javascript的灵活性,同时又兼具html的语义化和直观性。


    学习 JSX,先了解一下 createElement

    提到JSX,不可避免的就要提到createElement,当你看完本节,你会发现,奇怪的知识又增多了。

    无论是Vue还是React,都存在createElement,而且作用基本一致。可能你对createElement不是很了解,函数名翻译过来就是增加一个元素,但他的返回值你一定知道。createElement函数返回的值称之为虚拟节点,即VNode,而由VNode扎堆组成的树便是大名鼎鼎,面试必问的虚拟DOM
    createElement函数的参数,在这里抄一下Vue官方文档
    // @returns {VNode}
    createElement(
      // {String | Object | Function}
      // 一个 HTML 标签名、组件选项对象,或者
      // resolve 了上述任何一种的一个 async 函数。必填项。
      'div',
    
      // {Object}
      // 一个与模板中 attribute 对应的数据对象。可选。
      {
        // (详情见下一节)
      },
    
      // {String | Array}
      // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
      // 也可以使用字符串来生成“文本虚拟节点”。可选。
      [
        '先写一些文字',
        createElement('h1', '一则头条'),
        createElement(MyComponent, {
          props: {
            someProp: 'foobar'
          }
        })
      ]
    )

    从上面可以看出createElement一共有三个参数,三个参数分别是

    • 第一个参数是需要渲染的组件,可以是组件的标签,比如div;或者是一个组件对象,也就是你天天写的export default {};亦或者可以是一个异步函数。
    • 第二个参数是这个组件的属性,是一个对象,如果组件没有参数,可以传 null(关于组件的属性,下文将依次介绍)
    • 第三个参数是这个组件的子组件,可以是一个字符串(textContent)或者一个由 VNodes 组成的数组

    没有 v-model 怎么办,还有其他指令可以用吗?

    当你选择使用JSX的时候,你就要做好和指令说拜拜的时候了,在JSX中, 你唯一可以使用的指令是v-show,除此之外,其他指令都是不可以使用的,有没有感到很慌,这就对了。不过呢,换一个角度思考,指令只是Vue在模板代码里面提供的语法糖,现在你已经可以写Js了,那些语法糖用Js都可以代替了。

    v-model

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

    v-if 与 v-for

    在模板代码里面我们通过v-for去遍历元素,通过v-if去判断是否渲染元素,在jsx中,对于v-for,你可以使用for循环,array.map来代替,对于v-if,可以使用if语句,三元表达式等来代替

    循环遍历列表


    const list = ['java', 'c++', 'javascript', 'c#', 'php']
    return (
      <ul>
      {list.map(item => {
       return <li>{item}</li>
      })}
      </ul>
    )

     使用条件判断

    const isGirl = false
    return isGirl ? <span>小妹,哥哥教你写Vue</span> : <span>鸟你干啥</span>

    v-bind

    在模板代码中,我们一般通过 v-bind:prop="value":prop="value"来给组件绑定属性,在JSX里面写法也类似


    render() {
      return <input value={this.name}></input>
    }

    v-html 与 v-text

    在说v-htmlv-text之前,我们需要先了解一下Vue中的属性,Vue中的属性一共分为三种:

    第一种是大家写 bug 时候最常用的props,即组件自定义的属性;

    第二种是attrs,是指在父作用域里面传入的,但并未在子组件内定义的属性。

    第三种比较特殊,是domProps,经小编不完全测试,在Vue中,domProps主要包含三个,分别是innerHTML,textContent/innerTextvalue

    • v-html: 在模板代码中,我们用v-html指令来更新元素的innerHTML内容,而在JSX里面,如果要操纵组件的innerHTML,就需要用到domProps


    export default {
        data() {
          return {
            content: '<div>这是子君写的一篇新的文章</div>'
          }
        },
        render() {
          // v-html 指令在JSX的写法是 domPropsInnerHTML
          return <div domPropsInnerHTML={this.content}></div>
        }
      }
    • v-text: 看了上面的v-html,你是不是立即就想到了v-textJSX的写法domPropsInnerText,是的,你没有想错
    export default {
        data() {
          return {
            content: '这是子君写的一篇新的文章的内容'
          }
        },
        render() {
          return <div domPropsInnerText={this.content}></div>
        }
      }
    但实际上我们不需要使用domPropsInnerText,而是将文本作为元素的子节点去使用即可

    <div>{this.content}</div>
    实际上,对于domProps,只有innerHTML才需要使用domPropsInnerHTML的写法,其他使用正常写法即可
     
     

    我还要监听事件呢

    监听事件与原生事件

     
    当我们开发一个组件之后,一般会通过this.$emit('change')的方式对外暴露事件,然后通过v-on:change的方式去监听事件,很遗憾,在JSX中你无法使用v-on指令,但你将解锁一个新的姿势


    render() {
        return <CustomSelect onChange={this.$_handleChange}></CustomSelect>
      }

     

    JSX中,通过on + 事件名称的大驼峰写法来监听,比如事件icon-click,在JSX中写为onIconClick

    有时候我们希望可以监听一个组件根元素上面的原生事件,这时候会用到.native修饰符,有点绝望啊,修饰符也是不能用了,但好在也有替代方案,如下代码

    render() {
        // 监听下拉框根元素的click事件
        return <CustomSelect nativeOnClick={this.$_handleClick}></CustomSelect>
      }

     

    监听原生事件的规则与普通事件是一样的,只需要将前面的on替换为nativeOn

    除了上面的监听事件的方式之外,我们还可以使用对象的方式去监听事件


    render() {
        return (
          <ElInput
            value={this.content}
            on={{
              focus: this.$_handleFocus,
              input: this.$_handleInput
            }}
            nativeOn={{
              click: this.$_handleClick
            }}
          ></ElInput>
        )
      }

    事件修饰符

    和指令一样,除了个别的之外,大部分的事件修饰符都无法在JSX中使用,这时候你肯定已经习惯了,肯定有替代方案的。

    • .stop : 阻止事件冒泡,在JSX中使用event.stopPropagation()来代替
    • .prevent:阻止默认行为,在JSX中使用event.preventDefault() 来代替
    • .self:只当事件是从侦听器绑定的元素本身触发时才触发回调,使用下面的条件判断进行代替

    if (event.target !== event.currentTarget){
        return
    }
    • .enterkeyCode: 在特定键触发时才触发回调
    if(event.keyCode === 13) {
        // 执行逻辑
      }
    除了上面这些修饰符之外,尤大大为了照顾我们这群 CV 仔,还是做了一点优化的,对于.once,.capture,.passive,.capture.once,尤大大提供了前缀语法帮助我们简化代码

     render() {
        return (
          <div
            on={{
              // 相当于 :click.capture
              '!click': this.$_handleClick,
              // 相当于 :input.once
              '~input': this.$_handleInput,
              // 相当于 :mousedown.passive
              '&mousedown': this.$_handleMouseDown,
              // 相当于 :mouseup.capture.once
              '~!mouseup': this.$_handleMouseUp
            }}
          ></div>
        )
      }
     

    插槽

    插槽就是子组件中提供给父组件使用的一个占位符,插槽分为默认插槽,具名插槽和作用域插槽,下面小编依次为你带来每种在JSX中的用法与如何去定义插槽。

    默认插槽

    • 使用默认插槽

    使用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>
          )
        }
      }

    具名插槽

    • 使用具名插槽

    有时候我们一个组件需要多个插槽,这时候就需要为每一个插槽起一个名字,比如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>
        )
      }

    作用域插槽

    • 使用作用域插槽

      有时让插槽内容能够访问子组件中才有的数据是很有用的,这时候就需要用到作用域插槽,在JSX中,因为没有v-slot指令,所以作用域插槽的使用方式就与模板代码里面的方式有所不同了。比如在element-ui中,我们使用el-table的时候可以自定义表格单元格的内容,这时候就需要用到作用域插槽



    data() {
          return {
            data: [
              {
                name: '子君'
              }
            ]
          }
        },
        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>
        )
    }

    只能在 render 函数里面使用 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>
        )
      }

    本文转载自:https://www.jianshu.com/p/69bb9a65fba1

  • 相关阅读:
    工作中用到知识点
    工作中遇到问题的解决办法
    透明度兼容性(ie8以上)
    js阻止浏览器默认行为
    js停止冒泡和阻止浏览器默认行为
    js添加事件通用方法
    jquery常用插件
    延迟加载、异步加载js
    JavaScript兼容性问题
    创建对象的一种方式&一种继承机制(代码实例)
  • 原文地址:https://www.cnblogs.com/yeminglong/p/16415922.html
Copyright © 2020-2023  润新知