首页 > 代码库 > Vue结合原生js实现自定义组件自动生成

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="http://www.mamicode.com/css/mui.min.css" rel="stylesheet">    <link href="http://www.mamicode.com/css/app.css" rel="stylesheet">    <script src="http://www.mamicode.com/js/vue.js" type="text/javascript"></script></head><body><div id="main" class="mui-content"></div></body><script src="http://www.mamicode.com/js/fuhao-components.js" type="text/javascript"></script><script>    var jsonData =http://www.mamicode.com/ [        {            "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="http://www.mamicode.com/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="http://www.mamicode.com/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="http://www.mamicode.com/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="\takeLocation(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/

Vue结合原生js实现自定义组件自动生成