zoukankan      html  css  js  c++  java
  • Vue结合原生js实现自定义组件自动生成

      就目前三大前端主流数据驱动框架(vue,ng,react)而言,均具有创建自定义组件的api,但都是必须先做到事先写好挂载点,这个挂载点可以是原有静态元素标签也可以是自定义模板;对于多种组件通过同一数据流生成的,如果事先在页面上写好挂载点(mounted),然后通过dom操作去动态添加,会遇到类似这样一条错误提示信息:Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.(…)。这又是为何呢,下一步该怎么办?

      原因是任何dom操作的对象必须是符合W3C标准的元素,除非如下所述的,改写生成html元素对象的原型(HTMLElement.prototype)并注册自定义元素,从而实现动态生成自定义组件的效果。

      不过,大家都明白使用数据驱动框架的初衷就是尽可能避免dom操作,而如下代码中还是有一些dom操作的,就目前认知水平而言,感觉这些必要的dom操作还是避免不了的。其它不多说了,直接看代码。。。

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="Content-type" content="text/html,charset=utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE-edge">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link href="css/mui.min.css" rel="stylesheet">
        <link href="css/app.css" rel="stylesheet">
        <script src="js/vue.js" type="text/javascript"></script>
    </head>
    <body>
    <div id="main" class="mui-content">
    </div>
    </body>
    <script src="js/fuhao-components.js" type="text/javascript"></script>
    
    <script>
    
        var jsonData = [
            {
                "keyname": "姓名鄂然失色而热重重中之重重中之重杂志的热热",
                "type": "text",
                "key": "name11"
            }, {
                "value": "姓名鄂之重杂志的热热",
                "key": "name11"
            }, {
                "keyname": "姓名鄂然失色而热热热热是重中之重重中之重重中之重杂志的热热",
            },
            {
                "keyname": "姓名鄂然失色而热热热热是重中之重重中之重重中之重杂志的热热",
                "type": "textarea",
                "key": "name"
            },
            {
                "keyname": "性别",
                "type": "radio",
                "key": "sex",
                "values": [
                    {
                        "key": "man",
                        "value": "男辅导班"
                    },
                    {
                        "key": "women",
                        "value": "女"
                    }
                ]
            },
            {
                "keyname": "复选",
                "type": "checkbox",
                "key": "checkbox",
                "values": [
                    {
                        "key": "man",
                        "value": "男"
                    },
                    {
                        "key": "women",
                        "value": "女"
                    }
                ]
            },
            {
                "keyname": "类型",
                "type": "select",
                "key": "type1",
                "values": [
                    {
                        "key": "type1",
                        "value": "类型1"
                    },
                    {
                        "key": "type2",
                        "value": "类型2"
                    },
                    {
                        "key": "type3",
                        "value": "类型3"
                    },
                    {
                        "key": "type4",
                        "value": "类型4"
                    }
                ]
            },
            {
                "keyname": "定位",
                "type": "gps",
                "key": "btn",
                "value": "地图获取定位"
            },
            {
                "keyname": "拍照",
                "type": "photo",
                "key": "btn",
                "value": "拍照"
            }
        ];
        (function () {
            AnalyJson(jsonData);
        })();
        function AnalyJson(data) {
            if ('id' in data) {
                arguments.callee(data.values);
            } else {
                if ('name' in data) {
                    htmlname = data.name;
                    CreateInputViewer(data.name);
                    arguments.callee(data.values);
                } else {
                    if ('type' in data) {
                        CreateInputViewer(data);
                    } else {
                        for (var p in data) {
                            CreateInputViewer(data[p]);
                        }
                    }
                }
            }
        }
        function CreateInputViewer(data) {
            switch (data.type) {
                case 'text': {
                    fh_C(data, 'c-input-text' + '-' + data.key, 'fhInputText', textTpl);
                    break;
                }
                case 'textarea': {
                    fh_C(data, 'c-textarea' + '-' + data.key, 'fhInputTextarea', textareaTpl);
                    break;
                }
                case 'radio': {
                    fh_C(data, 'c-input-radio' + '-' + data.key, 'fhInputTextarea', radioTpl);
                    break;
                }
                case 'checkbox': {
                    fh_C(data, 'c-input-checkbox' + '-' + data.key, 'fhInputCheckbox', checkboxTpl);
                    break;
                }
                case 'select': {
                    fh_C(data, 'c-select' + '-' + data.key, 'fhSelect', selectTpl);
                    break;
                }
                case 'photo': {
                    fh_C(data, 'c-photo' + '-' + data.key, 'fhPhoto', photoTpl);
                    break;
                }
                case 'gps': {
                    fh_C(data, 'c-gps' + '-' + data.key, 'fhGPS', gpsTpl);
                    break;
                }
                default: {
                    fh_C(data, 'c-default' + '-' + data.key, 'fhInputDefault', defaultTpl);
                    break;
                }
    
            }
        }
        function fh_C(d, c, cn, tpl) {
            console.log(d);
            Vue.component(c, {
                template: tpl,
    //             props:['key','keyname','values','value'],
                data: function () {
                    return d
                }
            });
            new Vue({
                el: '.mui-content',
                components: {
                    cn: cn
                },
            });
            var MyElementProto = Object.create(HTMLElement.prototype);
            MyElementProto.createdCallback = function () {
                this.innerHTML = tpl
            };
            var MyComponent = document.registerElement(c, {prototype: MyElementProto});
            document.querySelector('.mui-content').appendChild(new MyComponent());
        }
    </script>
    </html>

    为了保持代码的可维护性及易读性,我将模板部分单独放在fuhao-components.js的文件里边,如下所示:


    var textTpl= '
    <div class="mui-content-padded">
    <input :type="type" :name="key" :placeholder="keyname" >
    </div>
    ';
    var textareaTpl= '
    <div class="mui-content-padded ">
    <textarea rows="5" :placeholder="keyname">
    </textarea>
    </div>
    ';
    var radioTpl= '
    <form class="mui-input-group mui-content-padded">
    <div class="mui-input-row mui-radio mui-left"v-for="value in values">
    <label>{{value.key}}</label>
    <input :name="key" :type="type" :value="key">
    </div>
    </form>
    ';
    var checkboxTpl= '
    <form class="mui-input-group mui-content-padded">
    <div class="mui-input-row mui-checkbox mui-left"v-for="value in values">
    <label>{{value.key}}</label>
    <input :name="key" :type="type" :value="key">
    </div>
    </form>
    ';
    var selectTpl= '
    <div class="mui-content-padded">
    <h5 class="mui-content-padded" v-text="keyname"></h5>
    <select class="mui-btn mui-btn-block " :name="key">
    <option value="key" v-text="value.key" v-for="value in values">{{value.key}}</option>
    </select>
    </div>
    ';
    var photoTpl= '
    <div class="mui-content-padded">
    <span v-text="keyname"></span>
    <button :name="key" onclick="takePhoto(this.name)" class="mui-btn mui-btn-primary">拍照</button>
    <img :id="key" height="70" width="100" class="img-rounded">
    </div>
    ';
    var gpsTpl= '
    <div class="mui-content col-xs-12">
    <button class="mui-btn mui-btn-primary" :id="key" onclick=" akeLocation(this.id)">
    获取定位
    </button>
    </div>
    ';
    var defaultTpl= '
    <div class="mui-content-padded " v-if="key">
    <ul class="mui-table-view">
    <li class="mui-table-view-cell mui-media">
    <span class="fuhaoKey" v-text="key"></span>
    <span class="fuhaoValue" v-text="value"></span>
    </li>
    </ul >
    </div>
    ';

    最终渲染效果如下:

    鉴于vue结合dom操作动态生成自定义组件,控制台会报一定的错误这一点bug还在努力修复中,可能需要更加深入地了解vue数据绑定及传递机制与js动态注册自定义组件的深入领会,继续努力中。。。

    谢谢阅览,不足之处还望多多指点,非常感谢。

    参考文献:1.http://www.html-js.com/article/2753

         2.http://vuejs.org/

  • 相关阅读:
    【Lua】Lua脚本及在Redis中的使用
    【php】php实现数组分块
    【git】git常用命令
    【PHP】php分隔字符串为数组
    【MYSQL】Mysql常用语句及命令
    键盘删除键长按只能删除一个字符,因为开启了键盘筛选键功能,解决方法
    JAVA基础知识总结:五
    linux常见命令
    JAVA基础知识总结:六
    写一个类时什么时候需要重写toString
  • 原文地址:https://www.cnblogs.com/frank26/p/5986205.html
Copyright © 2011-2022 走看看