Skip to content
本页目录

vue中怎么动态创建节点并绑定事件

一.前言

有原生js学习基础的都知道原生js是怎么动态创建节点并且绑定事件的,于是我在vue项目中使用原生js的形式动态创建了一个节点并且绑定了一个点击事件,但是,在浏览器中却发现这个点击事件失效了,下面就来探究一下该怎么样实现这个需求

二.原生js动态创建节点并绑定事件失效

vue
<template>
    <div>
        <div id="app2">
            <div class="wrapper" >
                <div @click="fn" >点击我查看事件</div>
            </div>
        </div>
        <button @click="createNode">创建新的节点</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg:'hello world'
        }
    },
    methods: {
        fn(){
            console.log(this.msg);
        },
        createNode(){
            var oDiv=document.createElement('div');
            oDiv.setAttribute('class','wrapper')
            oDiv.innerHTML='<div @click="fn">点击我查看事件</div>';
            var app2=document.querySelector('#app2');
            app2.appendChild(oDiv);
        }
    },
     
}
</script>
<style lang="">
    
</style>

上面的代码中我给按钮绑定了一个事件:每当点击这个按钮,就使用原生的js动态创建一个类名为wrapper的节点,并且对它的子节点绑定了一个点击事件fn,然后插入到idapp2的节点下

但是在浏览器中发现,动态创建的节点的点击事件是失效的,而一开始就绑定了同样事件的节点的事件却又是有效的

这是什么原因呢?

具体的原因我还不是特别的清楚,但是这可能是与vue周期与编译有关,其实在使用vue就是为了减少对DOM的操作,以数据来驱动试图,为什么要在vue中折腾DOM呢?

那么vue中怎么实现类似动态插入一个节点并且绑定事件的需求呢?

三.利用组件构造器实现动态添加一个节点并绑定事件

vue.extend()是一个构造器,用来创建一个子类,参数是一个包含组件选项的对象

下面介绍vue.extend()的使用方式

第一种:从外部引入节点组件

1.将需要动态添加的节点以及它所绑定的事件以组件的形式,先事先写好

新建一个组件文件NewComponent.vue

vue
<template>
    <div>
  <!-- 将要动态添加的节点 -->
        <button @click="fn">点我显示信息</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg:'hello world'
        }
    },
    methods: {
        fn:function(){
            console.log(this.msg);
        }
    },
}
</script>
<style lang="">
    
</style>

2.在你想要动态添加一个节点的那个组件中引入NewComponent.vue

vue
<template>
    <div id='app2'>
      <div id="mount-point"><h1>从外部引入新的节点</h1></div>
      <button @click = "addNode">添加节点</button>
    </div>
</template>
<script>
//引入将要动态添加节点所在的组件
import Append from './NewComponent.vue';
import Vue from 'vue';
export default {
    data(){
        return{
            
        }
    },
    methods:{
        addNode(){
        //创建一个构造器
    var Profile = Vue.extend(Append);
            // 创建 Profile 实例,并挂载到一个元素上。
     new Profile().$mount('#mount-point');
    }
    },
    
}
</script>

<style lang="">

</style>

第二种:在内部创建节点组件

vue
<template>
    <div id='app2'>
      <div id="mount-point"><h1>在本组件中动态创建新的节点</h1></div>
      <button @click = "addNode">添加节点</button>
    </div>
</template>
<script>
import Vue from 'vue';
export default {
    data(){
        return{
            
        }
    },
    methods:{
        addNode(){
            // 创建构造器
            var Profile = Vue.extend({
                template: '<button @click="fn">内部创建的节点组件</button>',
                data: function () {
                    return {
                        msg:'hello world'
                    }
                },
                methods:{
                    fn(){
                        console.log(this.msg)
                    }
                }
            })
            // 创建 Profile 实例,并挂载到一个元素上。
            new Profile().$mount('#mount-point')
        }
    },
    
}
</script>

<style lang="">

</style>

上面两种方式动态创建已经绑定事件的节点组件,在浏览器中运行,当点击"添加节点"按钮,便将节点所在的组件,挂载到id="mount-point"div中,注意这里的挂载是会把id="mount-point"div给完全的替换掉,然后你再点击新创建的节点上绑定的事件,是可以触发的

新动态添加的节点会覆盖被挂载的节点,那么如果我想要保留被挂载的节点怎么办呢?

我们可以采用不直接挂载,采用间接挂载的方式,在原先想要挂载的那个挂载点的内部,使用原生js动态创建一个新节点,将节点组件挂载到这个节点上

vue
<template>
    <div id='app2'>
      <div id="mount-point"><h1>在本组件中动态创建新的节点</h1></div>
      <button @click = "addNode">添加节点</button>
    </div>
</template>
<script>
import Vue from 'vue';
export default {
    data(){
        return{
            
        }
    },
    methods:{
        addNode(){
            //手动创建节点,实现间接挂载
                var mpNode=document.createElement('div');
                mpNode.setAttribute('id','mp');
                var pnode=document.querySelector("#mount-point");
                pnode.appendChild(mpNode);
                
            // 创建构造器
            var Profile = Vue.extend({
                template: '<button @click="fn">内部创建的节点组件</button>',
                data: function () {
                    return {
                        msg:'hello world'
                    }
                },
                methods:{
                    fn(){
                        console.log(this.msg)
                    }
                    
                }
            })
            // 创建 Profile 实例,并挂载到一个元素上。
            new Profile().$mount('#mp')
        }
       
    },
    
}
</script>

<style lang="">

</style>

四.组件构造器创建的组件与外部组件之间怎么传值

使用组件构造器创建组件是解决了原生js创建DOM节点,绑定的事件失效的问题,那现在问题又来了,新创建的组件和原来组件之间怎么传值呢?

外部组件传到新创建组件

  • 新创建的组件用props将外部传进来的参数存起来
  • 新创建的组件在挂载时以对象的方式传入参数,传入的值存在propsData
vue
<template>
    <div id='app2'>
      <div id="mount-point"><h1>在本组件中动态创建新的节点</h1></div>
      <button @click = "addNode">添加节点</button>
    </div>
</template>
<script>
import Vue from 'vue';
export default {
    data(){
        return{
            
        }
    },
    methods:{
        addNode(){
            //手动创建节点,实现间接挂载
                var mpNode=document.createElement('div');
                mpNode.setAttribute('id','mp');
                var pnode=document.querySelector("#mount-point");
                pnode.appendChild(mpNode);
                
            // 创建构造器
            var Profile = Vue.extend({
                template: '<button @click="fn">内部创建的节点组件</button>',
                data: function () {
                    return {
                        msg:'hello world'
                    }
                },
                props:['str'],
                methods:{
                    fn(){
                        console.log(this.str)
                    }
                }
            })
            // 创建 Profile 实例,并挂载到一个元素上。
            new Profile({
            propsData:{str:'你好呀'}
            }).$mount('#mp')
        }
       
    },
    
}
</script>

<style lang="">

</style>

新创建组件传到外部组件

对于新创建组件传到外部组件,目前我尚未查到有效的方法,希望有知道的小伙伴可以留言告诉我

写到这里,不知大家有没有觉得这样创建一个新的节点很麻烦,至少在数据处理上不是很方便

下面我提供另一种思路来实现动态创建节点

五.利用数组以及v-for实现动态创建节点

vue
<template>
    <div id='app2'>
      <div v-for="(v,i) in data" v-bind:key="i">
          <input type="text" placeholder="type it" v-model="data[i]"><button @click="del()">取消</button>
      </div>
      <button @click = "createNode">创建表单input节点</button>
      <button @click = "getdata">获取所有input值</button>
    </div>
</template>
<script>
import Vue from 'vue';
export default {
    data(){
        return{
            data:[],
            index:0
           
        }
    },
    methods:{
        getdata(){
console.log(this.data);
        },
        del(){
           event.currentTarget.parentNode.style.display='none';
        },
        createNode(){
            this.index++;
            console.log(this.index);
            var str="请输入选项"
            var ele=str+this.index;
            this.data.push(ele);
        }
       
    },
    
}
</script>

<style lang="">

</style>