Vue JSX 和 函数式组件

2,296 阅读2分钟

Vue框架结合elementUI组件可以进行相应使用数据来驱动的组件化管理,但是在实际动作中,会出现很多需要自定义DOM的部分,但是因为这些自定义的DOM实在是太小了,没必要因此做一个组件,因为就需要使用Vue函数式组件结合JSX的方式来开发,这样就可以把自定义的DOM以及这个DOM绑定的事件留在处理数据js脚本中,这样可以避免页面或者组件中的DOM数据过大影响后期的维护。JSX和函数式编程组合起来也解决替代了VNode复杂的render函数

JSX:

JSX是对JavaScript的扩展,JS+XML的集合,既具有js的灵活,又有Html的语义化的特性。

  1. 安装

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

  1. 配置 .babelrc
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset',
    ['@vue/babel-preset-jsx',
      {
        'injectH': false
      }]
  ]
}

基础示例

render() {
  return (
    <div>
      <h3>内容</h3>
      {/* 纯文本 */}
      <p>hello, I am Gopal</p>
      {/* 动态内容 */}
      <p>hello { this.msg }</p>
      {/* 输入框 */}
      <input />
      {/* 自定义组件 */}
      <myComponent></myComponent>
    </div>
  );
}

动态属性

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

插槽

  1. 具名插槽 父组件的写法和单文件组件模板的类似,通过 slot="header" 这样方式指定要插入的位置。子组件通过 this.$slots.header 方式指定插槽的名称,其中 header 就是插槽的名称。

父组件:

render() {
  {/* 具名插槽  */}
  <myComponent>
    <header slot="header">header</header>
    <header slot="content">content</header>
    <footer slot="footer">footer</footer>
  </myComponent>
}

子组件:

render() {
  return (
    <div>
      {/* 纯文本 */}
      <p>我是自定义组件</p>
      {this.$slots.header}
      {this.$slots.content}
      {this.$slots.footer}
    </div>
  );
}
  1. 作用域插槽 子组件中通过 {this.$scopedSlots.test({ user: this.user })} 指定插槽的名称是 test,并将 user 传递给父组件。父组件在书写子组件标签的时候,通过 scopedSlots 值指定插入的位置是 test,并在回调函数获取到子组件传入的 user

父组件:

render() {
  {/* 具名插槽 作用域插槽 */}
  <myComponent {
    ...{
      scopedSlots: {
        test: ({user}) => (
          <div>{user.name}</div>
        )
      }
    }
  }>
  </myComponent>
}

子组件:

render() {
  return (
    <div>
      {this.$scopedSlots.test({
        user: this.user
      })}
    </div>
  );
}

Vue指令在JSX中的应用

render() {
  {/* 指令 */}
  {/* v-model */}
  <div><input vModel={this.newTodoText} /></div>
  {/* v-model 以及修饰符 */}
  <div><input vModel_trim={this.tirmData} /></div>
  {/* v-on 监听事件 */}
  <div><input vOn:input={this.inputText} /></div>
  {/* v-on 监听事件以及修饰符 */}
  <div><input vOn:click_stop_prevent={this.inputText} /></div>
  {/* v-html */}
  <p domPropsInnerHTML={html} />
}

函数式组件

对于要编写一个没有响应式数据,也无需监听状态,只接受props值的简单组件,就可以直接使用函数式组件,在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。函数式组件在实际运用中主要是针对,大型组件中的一些小型自定义DOM组件。

Vue.component('my-component', { 
    functional: true, 
    // Props 是可选的 
    props: { // ... }, 
    // 为了弥补缺少的实例 
    // 提供第二个参数作为上下文 
    render: function (createElement, context) {
    // ... 可以在这里结合JSX语法,渲染一个简单复用得DOM组件
    } 
})

自定义函数式组件的简单示例:

<template>
  <ex-slot
     v-if="obj.render"
     :render="obj.render"
     :row="obj.row"
     :index="3"
     :column="obj.column"
   />
</template>
<script>
//自定义函数式组件 ==START
let exSlot = {
  functional: true,//代表这是一个函数式组件
  props: {
    row: Object, //接受父组件传来得数据
    render: Function, //接受父组件传来的回调函数,用于把JSX编写的DOM以及DOM上的事件抽取出来到数据脚本页面,尽量不污染外部组件,使组件更加的灵活轻便可复用。
    className: String,//接受父组件传来的样式class,配合css更改样式
    index: Number, // 接受父组件的数据
    column: { // 接受父组件的数据
      type: Object,
      default: null,
    },
  },
  render: (h, context) => { //函数式组件用来渲染相应的DOM,有两个参数,h:render渲染函数,context:上下文对象,可以获取相应父组件传到props中的数据
    const params = {
      row: context.props.row,
      index: context.props.index,
      className: context.props.row.className,
    };
    if (context.props.column) params.column = context.props.column;
    //使用父组件传来的render回调函数,将JSX编写的DOM对象抽取出去,保持组件的灵活性。*****关键*****
    return context.props.render(h, params); 
  },
};
//自定义函数式组件 ==END
export default {//父组件
    components: { exSlot },
    data:{
        return{
            obj:{
                row:{
                    a:1,
                    b:2
                },
                className:'flex-cus',
                column:{
                    prop:'operation',
                    label:'操作'
                },
                render:(h, params) =>{
                    //这里返回的是JSX编写的DOM数据,这样就把本来应该写在html的DOM抽出来到JS中,然后也可以对这个自定义组件进行处理。这样exSlot这个组件可以进行最大程度的重用。可以保持html的相对简洁。
                    return (
                        <div class="operate">
                            <div onClick = {()=>this.handleCancelClick(params)}>取消</div>
                            <div onClick = {()=>this.handleSeeClick(params)}>查看</div>
                        </div>
                    )
                }
            }
        }
    }
    methods:{
        handleCancelClick(param){
            console.log('handleCancelClick',param)
        },
        handleSeeClick(param){
            console.log('handleSeeClick',param)
        }
    }
}
</script>