zoukankan      html  css  js  c++  java
  • vue jsx与render的区别及基本使用

      vue template语法简单明了,数据操作与视图分离,开发体验友好。但是在某些特定场合中,会限制一些功能的扩展,如动态使用过滤器解析字符串类型的模板文件等。以上功能的实现可以借助vue的render语法,render语法比template更偏底层,允许在HTML中使用js语法,可以极大的扩展HTML的能力。

      render函数注入了一个参数createElement,用来创建我们所需要的标签内容,有三个参数:HTML标签(elementTag)标签属性(option)子元素(children);从createElement的参数列表里面可以看出,如果组件内部结构嵌套比较深,render的语法写起来会比较繁琐,需要不断的调用createElement,对于想偷懒的我,还是想想有没有什么比较简易的写法,jsx无疑是一种很好的选择,区别在于jsx可以像我们写HTML文件一样写业务代码,借助于babel,会将jsx语法转换成render语法,没有什么副作用。

      最近在使用ant-design-vue进行项目重构,现就以ant-desigin-vue为例,介绍下vue jsx的基本使用。

      1、安装vue jsx开发环境

        已vue-cli 3.0脚手架为例,首先需要安装babel转义插件,将vue jsx转义成vue的render语法,执行以下命令:

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

        其次,修改babel配置文件(babel.config.js/.babelrc),如下:

    module.exports = {
        presets: [
           ['@vue/app', {
               useBuiltIns: 'entry'
           }],
            ['@vue/babel-preset-jsx',
            {
                "injectH": false
            }]
        ]
    };

      注意@vue/babel-preset-jsx默认会注入一个h(createElement的语法糖),会与vue render函数注入的createElement冲突,这个配置要设置false,否则项目启动会报错

      2、基本使用

        jsx并不能解析vue指令,因此template转jsx语法时,每一部分的对应写法和vue官网文档一致,如下:

    {
      // 与 `v-bind:class` 的 API 相同,
      // 接受一个字符串、对象或字符串和对象组成的数组
      'class': {
        foo: true,
        bar: false
      },
      // 与 `v-bind:style` 的 API 相同,
      // 接受一个字符串、对象,或对象组成的数组
      style: {
        color: 'red',
        fontSize: '14px'
      },
      // 普通的 HTML 特性
      attrs: {
        id: 'foo'
      },
      // 组件 prop
      props: {
        myProp: 'bar'
      },
      // DOM 属性
      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',
      // 其它特殊顶层属性
      key: 'myKey',
      ref: 'myRef',
      // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
      // 那么 `$refs.myRef` 会变成一个数组。
      refInFor: true
    }

      具名插槽:{ this.$slots.slotName }

      v-for转换:

    const formItems = this.formFields.map(vv => {
                const itemProps = {
                    props: {
                        label: vv.label
                    }
                };
                if (Object.is(vv.type, 'input')) {
                    const inputProps = {
                        domProps: {
                            autocomplete: 'off'
                        },
                        directives: [{
                            name: 'decorator',
                            rawName: 'v-decorator',
                            value: [vv.prop, {rules: vv.rules}]
                        }]
                    };
                    return <a-form-item {...itemProps}>
                        <a-input {...inputProps} />
                    </a-form-item>
                }
            });

      附一个基于ant-design-vue form封装的弹窗表格组件:

    /**
     * 简单的通用表单弹框
     * 配置文件以vue jsx为准
     */
    //中间量,用于CustomizedForm与keCommonForm之间数据的传输
    const CustomizedForm = {
        props: {
            formFields: {
                type: Array,
                default() {
                    return [];
                }
            },
            layout: {
                type: String,
                default: 'inline'
            }
        },
        created () {
            let _this = this;
            this.form = this.$form.createForm(this, {
                onFieldsChange: (props, changedFields) => {
                    _this.$emit('fieldsChange', changedFields);
                },
                mapPropsToFields: () => {
                    let formFieldsBean = {};
                    _this.formFields.forEach(vv => {
                        formFieldsBean[vv.prop] = _this.$form.createFormField({
                            value: vv.value
                        });
                    });
                    return formFieldsBean;
                },
                onValuesChange (props, value, currentValue) {
                    _this.$emit('change', currentValue);
                }
            });
            //传递form对象
            this.$emit('childScope', this.form);
        },
        render() {
            let _this = this;
            
            const formProps = {
                props: {
                    layout: this.layout,
                    form: this.form
                },
                attrs: {
                    class: 'ke-form ke-form-inline'
                }
            };
            
            const formItems = this.formFields.map(vv => {
                const itemProps = {
                    props: {
                        label: vv.label
                    }
                };
                //深拷贝一份表单属性,防止报错
                const inputProps = {
                    attrs: {
                        autocomplete: 'off'
                    },
                    scopedSlots: {},
                    directives: [{
                        name: 'decorator',
                        rawName: 'v-decorator',
                        value: [vv.prop, {rules: vv.rules}]
                    }]
                };
                //如果有配置表单信息,合并表单属性
                if (_.isObject(vv.options)) {
                    Object.assign(inputProps, vv.options);
                }
                //如果存在插槽,则配置插槽信息
                if (_.isObject(vv.scopedSlots)) {
                    Object.keys(vv.scopedSlots).forEach(key => {
                        let formItemOptions = _.cloneDeep(vv.scopedSlots[key].options);
                        inputProps.scopedSlots[key] =
                                () => _this.$createElement(vv.scopedSlots[key].tagName, formItemOptions);
                    })
                }
                //获取创建元素的tagName
                let tagName = vv.tagName || 'a-input';
                let item = this.$createElement(tagName, inputProps);
                return <a-form-item {...itemProps}>
                    { item }
                </a-form-item>
            });
            
            return (
                <a-form {...formProps}>
                    { formItems }
                </a-form>
            );
        }
    };
    
    /**
     * 简单的表单可以引用此组件
     */
    export default {
        name: 'keCommonForm',
        model: {
            prop: 'value',
            event: 'change'
        },
        props: {
            title: {
                type: String,
                default: '提示'
            },
            visible: {
                type: Boolean,
                default: false
            },
            okText: {
                type: String,
                default: ''
            },
            layout: {//表格的排列方式 'horizontal'|'vertical'|'inline'
                type: String,
                default: 'inline'
            },
            formFields: {//表格的配置文件Array<Object>
                type: Array,
                default() {
                    return []
                }
            },
            value: {
                type: Object,
                default() {
                    return {}
                }
            },
            submitFun: {//提交表单触发父组件内部的方法
                type: String,
                default: 'test'
            },
            closeResetFields: {//关闭模态框重置表单内容
                type: Boolean,
                default: true
            },
            commonFormOption: {
                type: Object,
                default() {
                    return {}
                }
            }
        },
        components: { CustomizedForm },
        methods: {
            onCancel() {
                this.$emit('closeCommonForm')
            },
            onOk() {
                let checkedList = this.formFields.map(vv => vv.prop);
                if (this._form && _.isFunction(this._form.validateFieldsAndScroll)) {
                   this._form.validateFieldsAndScroll(checkedList, (errors, value) => {
                       if (errors && _.isObject(errors) && Object.keys(errors).length > 0) {
                           // Object.keys(errors).forEach(vv => {
                           //     this.$message.error(errors[vv].errors[0].message, 2);
                           // })
                           return;
                       }else {
                           this.$emit('submit', this.submitFun);
                       }
                   })
                }
                
            },
            formChange(value) {
                this.$emit('change', value);
            }
        },
        render() {
            //模态框关闭后的回调
            let _this = this;
            function afterClose() {
                if (_this.closeResetFields) {
                    _this._form.resetFields();
                }
            }
            
            const confirmBtnProps = {
                props: {
                    type: 'primary',
                    'html-type': 'submit'
                },
                on: {
                    click: this.onOk
                }
            };
            
            const modalProps = {
                props: {
                    title: this.title,
                    okText: this.okText,
                    visible: this.visible,
                    afterClose: afterClose
                },
                on: {
                    cancel: this.onCancel,
                    on: this.onOk
                },
                scopedSlots: {
                    footer: scope => {
                        return <div>
                            <a-button { ...confirmBtnProps }>提交</a-button>
                            <a-button onClick={ this.onCancel }>取消</a-button>
                        </div>
                    }
                }
            };
            //合并通用表单配置文件,支持jsx语法
            if (_.isObject(this.commonFormOption)) {
                Object.assign(modalProps, this.commonFormOption);
            }
        
            
            const customizedFormProps = {
                props: {
                    formFields: this.formFields,
                    layout: this.layout
                },
                on: {
                    change: this.formChange,
                    childScope: form => {
                        this._form = form;
                    }
                }
            };
            return (
                <a-modal {...modalProps}>
                    { this.$slots.content }
                    <CustomizedForm { ...customizedFormProps } />
                </a-modal>
            )
        }
    }
  • 相关阅读:
    尝试用phpmyadmin去getshell
    docker入门及环境搭建
    初始sql注入
    用BurpSuite爆破DVWA
    Sudo提权漏洞复现
    maccms后门分析
    webpack构建流程分析笔记
    《图解 HTTP》 阅读摘要
    offsetHeight、clientHeight、scrollHeight、offsetLeft 区别笔记
    js ==和===的区别
  • 原文地址:https://www.cnblogs.com/gerry2019/p/11023241.html
Copyright © 2011-2022 走看看