Vue成神之路之实例和插槽

前言

记录平时学到的知识,标题写的大气一点,也算是给自己一点鼓励,希望在技术这条路可以远走越远,路越走越宽~

文中代码地址

PS:如果对你有一点帮助,请顺手给个小星星哦,鼓励我继续写下去~

引入的文件文件说明

vue.js——开发版本:包含完整的警告和调试模式 vue.min.js——生产版本:删除了警告,进行了压缩

通过new Vue({...})生成的实例

通过new Vue({...})生成的实例可以在在构造器外部操作构造器内部的属性选项或者方法。实例的作用就是给原生的或者其他javascript框架一个融合的接口或者说是机会,让Vue和其他框架一起使用。

1. 实例

引入jquery,在DOM被挂载后修改里边的内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="../assets/js/jquery.js"></script>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
    <title>实例</title>
</head>
<body>
    <h1>实例</h1>
    <hr>
    <div id="app">
        {{message}}
    </div>
    <script type="text/javascript">
        var app=new Vue({
            el:'#app',
            data:{
                message: 'Hello zaishuiyixia!'
            },
            mounted () {
                $('#app').html('我是jQuery!');
            }
        })
    </script>
</body>
</html>

现在页面显示是:我是jQuery,而不是Hello zaishuiyixia!了。

实例调用自定义方法:

在Vue的构造器里写一个add方法,然后用实例的方法调用它:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="../assets/js/jquery.js"></script>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
    <title>实例</title>
</head>
<body>
    <h1>实例</h1>
    <hr>
    <div id="app">
        {{message}}
    </div>
    <script type="text/javascript">
        var app=new Vue({
            el:'#app',
            data:{
                message: 'Hello zaishuiyixia!'
            },
            mounted() {
                $('#app').html('我是jQuery!');
            },
            methods: {
                add() {
                    console.log('通过实例调用了了add方法。')
                }
            }
        })

        app.add();
    </script>
</body>
</html>

打开控制台可以看到打印出了:通过实例调用了了add方法。

2. 实例方法

**2.1

mount() 手动地挂载一个未挂载的实例。

这个方法返回实例自身,因而可以链式调用其它实例方法。

$mount方法可以用来挂载扩展:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>实例方法</title>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
</head>
<body>
    <h1>实例方法</h1>
    <hr>
    <div id="app">
        {{message}}
    </div>
    <script type="text/javascript">
      var app = Vue.extend({
          template:`<p>{{message}}</p>`,
          data:function(){
              return {
                  message:'Hello ,I am zaishuiyixia!'
              }
          }
      })
      var vm = new app().$mount("#app")
    </script>
</body>
</html>

2.2 $destroy() 卸载/销毁 实例方法

完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。会触发 beforeDestroy 和 destroyed 的生命周期函数。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>实例方法</title>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
</head>
<body>
    <h1>实例方法</h1>
    <hr>
    <div id="app">
        {{message}}
        <br>    
    </div>
    <p><button onclick="destroy()">銷毀实例</button></p>
    <script type="text/javascript">
        var app = Vue.extend({
            template:`<p>{{message}}</p>`,
            data:function(){
                return {
                    message:'Hello ,I am zaishuiyixia!'
                }
            },
            destroyed() {
                console.log('实例已被销毁。')
            }
        })
        var vm = new app().$mount("#app");

        function destroy(){
           vm.$destroy();
        }
    </script>
</body>
</html>

2.3 forceUpdate() 强制实例重新渲染方法

强制 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。 会触发beforeUpdate和updateed生命周期函数。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>实例方法</title>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
</head>
<body>
    <h1>实例方法</h1>
    <hr>
    <div id="app">
        {{message}}
        <br>    
    </div>
    <p><button onclick="destroy()">銷毀实例</button></p>   
    <p><button onclick="forceUpdate()">强制实例重新渲染</button></p>
    <script type="text/javascript">
        var app = Vue.extend({
            template:`<p>{{message}}</p>`,
            data:function(){
                return {
                    message:'Hello ,I am zaishuiyixia!'
                }
            },
            beforeUpdate() {
                console.log('准备更新数据')
            },
            updated() {
                console.log('更新完成')
            },
            destroyed() {
                console.log('实例已被销毁。')
            }
        })
        var vm = new app().$mount("#app");

        function destroy(){
           vm.$destroy();
        }

        function forceUpdate(){
           vm.$forceUpdate();
        }
    </script>
</body>
</html>

2.4 $nextTick() 当Vue构造器里的data值被修改完成后会调用这个方法,也相当于一个钩子函数,和构造器里的updated生命周期作用很像。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>实例方法</title>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
</head>
<body>
    <h1>实例方法</h1>
    <hr>
    <div id="app">
        {{message}}
        <br>    
    </div>
    <p><button onclick="destroy()">銷毀实例</button></p>   
    <p><button onclick="forceUpdate()">强制实例重新渲染</button></p>
    <p><button onclick="tick()">更新数据</button></p>
    <script type="text/javascript">
        var app = Vue.extend({
            template:`<p>{{message}}</p>`,
            data:function(){
                return {
                    message:'Hello ,I am zaishuiyixia!'
                }
            },
            beforeUpdate() {
                console.log('准备更新数据')
            },
            updated() {
                console.log('更新完成')
            },
            destroyed() {
                console.log('实例已被销毁。')
            }
        })
        var vm = new app().$mount("#app");

        function destroy(){
           vm.$destroy();
        }

        function forceUpdate(){
           vm.$forceUpdate();
        }

        function tick(){
            vm.message="update message info ";
            vm.$nextTick(function(){
                console.log('message更新完后我被调用了');
            })
        }
    </script>
</body>
</html>

3. 实例事件

vm.

emit触发。回调函数会接收所有传入事件触发函数的额外参数。

$on 在构造器外部添加事件:

app.$on('reduce',function(){
    console.log('执行了reduce()');
    this.num--;
});

$on接收两个参数,第一个参数是调用时的事件名称,第二个参数是一个匿名方法。

事件可以由vm.$emit触发:

//外部调用内部事件
function reduce(){
    app.$emit('reduce');
}

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>实例事件</title>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
</head>
<body>
    <h1>Example Event Demo</h1>
    <hr>
    <div id="app">
        <p>{{num}}</p>
        <button @click="add">add</button>
        
    </div>
    <p><button onclick="reduce()">reduce</button></p>
     
    <script type="text/javascript">
        var app=new Vue({
            el:'#app',
            data:{num:1},
            methods:{
                add:function(){
                    this.num++;
                }
            }
        })

        //实例事件
        app.$on('reduce',function(){
            console.log('执行了reduce()');
            this.num--;
        });
    </script>
</body>
</html>

vm.$once( event, callback )监听一个自定义事件,但是只触发一次,在第一次触发之后移除监听器。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>实例事件</title>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
</head>
<body>
    <h1>Example Event Demo</h1>
    <hr>
    <div id="app">
        <p>{{num}}</p>
        <button @click="add">add</button>  
    </div>
     <p><button onclick="reduce()">reduce</button></p>
     <p><button onclick="reduceOnce()">reduceOnce</button></p>
     <p><button onclick="off()">off</button></p>
     

    <script type="text/javascript">
        var app=new Vue({
            el:'#app',
            data:{num:1},
            methods:{
                add() {
                    this.num++;
                }
            }
        })

        //实例事件
        app.$on('reduce',function(){
            console.log('执行了reduce()');
            this.num--;
        });

        //只使用一次的实例方法
        app.$once('reduceOnce',function(){
            console.log('只执行一次的方法');
            this.num--;
        });
    </script>
</body>
</html>

vm.$off( [event, callback] )移除自定义事件监听器:

  1. 如果没有提供参数,则移除所有的自定义事件监听器;
  2. 如果只提供了事件,则移除该自定义事件事件所有的监听器;
  3. 如果同时提供了事件与回调,则只移除这个回调的监听器。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>实例事件</title>
    <script type="text/javascript" src="../assets/js/vue.js"></script>
</head>
<body>
    <h1>Example Event Demo</h1>
    <hr>
    <div id="app">
        <p>{{num}}</p>
        <button @click="add">add</button>  
    </div>
     <p><button onclick="reduce()">reduce</button></p>
     <p><button onclick="reduceOnce()">reduceOnce</button></p>
     <p><button onclick="off()">off</button></p>
     

    <script type="text/javascript">
        var app=new Vue({
            el:'#app',
            data:{num:1},
            methods:{
                add() {
                    this.num++;
                }
            }
        })

        //实例事件
        app.$on('reduce',function(){
            console.log('执行了reduce()');
            this.num--;
        });

        //只使用一次的实例方法
        app.$once('reduceOnce',function(){
            console.log('只执行一次的方法');
            this.num--;
        });

        //关闭事件
        function off(){
            app.$off('reduce');
        }

        //外部调用内部事件
        function reduce(){
            app.$emit('reduce');
        }

        function reduceOnce(){
            app.$emit('reduceOnce');
        }
    </script>
</body>
</html>

4. 插槽-slot

插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。 实际上,一个slot最核心的两个问题在这里就点出来了,是显示不显示和怎样显示。

由于插槽是一块模板,所以,对于任何一个组件,从模板种类的角度来分,其实都可以分为非插槽模板和插槽模板两大类。

非插槽模板指的是html模板,比如'div、span、ul、table'这些,非插槽模板的显示与隐藏以及怎样显示由组件自身控制;插槽模板是slot,它是一个空壳子,因为它的显示与隐藏以及最后用什么样的html模板显示由父组件控制。但是插槽显示的位置却由子组件自身决定,slot写在组件template的什么位置,父组件传过来的模板将来就显示在什么位置

4.1 默认插槽 | 匿名插槽:

默认插槽,也可以叫它匿名插槽,它不用设置name属性。它会作为所有未匹配到插槽的内容的统一出口。

一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了

父组件:

<template id="tmp">
    <div class="father">
        <h3>这里是父组件</h3>
        <child>
            <div class="tmpl">
              <span>菜单1</span>
              <span>菜单2</span>
              <span>菜单3</span>
              <span>菜单4</span>
              <span>菜单5</span>
              <span>菜单6</span>
            </div>
        </child>
    </div>
</template>

子组件:

<template id="tmp2">
    <div class="child">
        <h3>这里是子组件</h3>
        <slot></slot>
    </div>
</template>

在这个例子里,因为父组件在里面写了html模板,那么子组件的匿名插槽这块模板就是下面这样。也就是说,子组件的匿名插槽被使用了,是被下面这块模板使用了。

<div class="tmpl">
  <span>菜单1</span>
  <span>菜单2</span>
  <span>菜单3</span>
  <span>菜单4</span>
  <span>菜单5</span>
  <span>菜单6</span>
</div>

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="../assets/js/vue.js"></script>
    <title>Mixins Option</title>
    <style>
        .father {
            background: #ccc;
        }
        .child {
            background: green;
        }
    </style>
</head>
<body>
    <h1>内置组件Slot</h1>
    <hr>
    <div id="app">
        <father></father>
    </div>

    <template id="tmp">
        <div class="father">
            <h3>这里是父组件</h3>
            <child>
                <div class="tmpl">
                  <span>菜单1</span>
                  <span>菜单2</span>
                  <span>菜单3</span>
                  <span>菜单4</span>
                  <span>菜单5</span>
                  <span>菜单6</span>
                </div>
            </child>
        </div>
    </template>

    <template id="tmp2">
        <div class="child">
            <h3>这里是子组件</h3>
            <slot></slot>
        </div>
    </template>

    <script type="text/javascript">
        var child = {
            template: '#tmp2'
        }

        var father = {
            template:'#tmp',
            components: {
                "child": child
            }
        }

        var app=new Vue({
            el:'#app',
            data:{
               
            },
            components:{
                "father": father
            }
        })
    </script>
</body>
</html>

打开控制台可以查看效果,父组件以灰色背景填充,子组件都以绿色填充。

4.2 具名插槽 :

匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次,出现在不同的位置。下面的例子,就是一个有两个具名插槽和一个匿名插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。

父组件:

<template id="tmp">
  <div class="father">
    <h3>这里是父组件</h3>
    <child>
      <div class="tmpl" slot="up">
        <span>菜单1</span>
        <span>菜单2</span>
        <span>菜单3</span>
        <span>菜单4</span>
        <span>菜单5</span>
        <span>菜单6</span>
      </div>
      <div class="tmpl" slot="down">
        <span>菜单-1</span>
        <span>菜单-2</span>
        <span>菜单-3</span>
        <span>菜单-4</span>
        <span>菜单-5</span>
        <span>菜单-6</span>
      </div>
      <div class="tmpl">
        <span>菜单->1</span>
        <span>菜单->2</span>
        <span>菜单->3</span>
        <span>菜单->4</span>
        <span>菜单->5</span>
        <span>菜单->6</span>
      </div>
    </child>
  </div>
</template>

子组件:

<template id="tmp2">
  <div class="child">
    <!--具名插槽 -->
    <slot name="up"></slot>
    <h3>这里是子组件</h3>
    <!--具名插槽-->
    <slot name="down"></slot>
    <!--匿名插槽-->
    <slot></slot>
  </div>
</template>

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="../assets/js/vue.js"></script>
    <title>具名插槽</title>
    <style>
        .father {
            background: #ccc;
        }
        .up {
            background: green;
        }
        .down {
            background: red;
        }
        .tmpl {
            background: blue;
        }
        .child {
            background: yellow;
        }
    </style>
</head>
<body>
    <h1>具名插槽</h1>
    <hr>
    <div id="app">
        <father></father>
    </div>

    <template id="tmp">
        <div class="father">
        <h3>这里是父组件</h3>
        <child>
          <div class="up" slot="up">
            <span>菜单1</span>
            <span>菜单2</span>
            <span>菜单3</span>
            <span>菜单4</span>
            <span>菜单5</span>
            <span>菜单6</span>
          </div>
          <div class="down" slot="down">
            <span>菜单-1</span>
            <span>菜单-2</span>
            <span>菜单-3</span>
            <span>菜单-4</span>
            <span>菜单-5</span>
            <span>菜单-6</span>
          </div>
          <div class="tmpl">
            <span>菜单->1</span>
            <span>菜单->2</span>
            <span>菜单->3</span>
            <span>菜单->4</span>
            <span>菜单->5</span>
            <span>菜单->6</span>
          </div>
        </child>
      </div>
    </template>

    <template id="tmp2">
        <div class="child">
            <!--具名插槽 -->
            <slot name="up"></slot>
            <h3>这里是子组件</h3>
            <!--具名插槽-->
            <slot name="down"></slot>
            <!--匿名插槽-->
            <slot></slot>
        </div>
    </template>

    <script type="text/javascript">
        var child = {
            template: '#tmp2'
        }

        var father = {
            template:'#tmp',
            components: {
                "child": child
            }
        }

        var app=new Vue({
            el:'#app',
            data:{
               
            },
            components:{
                "father": father
            }
        })
    </script>
</body>
</html>

打开浏览器可以看到,父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。

4.3 作用域插槽 | 带数据的插槽:

官方叫它作用域插槽,实际上,对比前面两种插槽,可以叫它带数据的插槽。意思就是前面两种,都是在组件的template里面写:

匿名插槽
<slot></slot>
具名插槽
<slot name="up"></slot>

但是作用域插槽要求,在slot上面绑定数据。也就是你得写成大概下面这个样子:

<slot name="up" :data="data"></slot>
 export default {
    data: function(){
      return {
        data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
      }
    },
}

插槽最后显示不显示是看父组件有没有在child下面写模板,像下面那样:

<child>
   html模板
</child>

写了的话,插槽就会在浏览器上显示内容,内容就是html该有的模样,没写,插槽就是空壳子,什么也没有。

作用域插槽跟单个插槽和具名插槽的区别,因为单个插槽和具名插槽不绑定数据,所以父组件提供的模板一般要既包括样式又包括内容。而作用域插槽绑定了一套数据,父组件可以拿来用,所以父组件只需要提供样式,内容可以显示子组件插槽绑定的数据。

父组件:

<template id="tmp">
  <div class="father">
    <h3>这里是父组件</h3>
    <!--第一次使用:用flex展示数据-->
    <child>
      <template slot-scope="user">
        <div class="tmpl">
          <span v-for="item in user.data">{{item}}</span>
        </div>
      </template>

    </child>

    <!--第二次使用:用列表展示数据-->
    <child>
      <template slot-scope="user">
        <ul>
          <li v-for="item in user.data">{{item}}</li>
        </ul>
      </template>

    </child>

    <!--第三次使用:直接显示数据-->
    <child>
      <template slot-scope="user">
       {{user.data}}
      </template>

    </child>

    <!--第四次使用:不使用其提供的数据, 作用域插槽退变成匿名插槽-->
    <child>
      我就是模板
    </child>
  </div>
</template>

子组件:

<template>
  <div class="child">

    <h3>这里是子组件</h3>
    // 作用域插槽
    <slot  :data="data"></slot>
  </div>
</template>

 export default {
    data: function(){
      return {
        data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
      }
    }
}

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="../assets/js/vue.js"></script>
    <title>作用域插槽</title>
    <style>
        .father {
            background: #ccc;
        }
        .child {
            background: green;
        }
    </style>
</head>
<body>
    <h1>作用域插槽</h1>
    <hr>
    <div id="app">
        <father></father>
    </div>

    <template id="tmp">
        <div class="father">
            <h3>这里是父组件</h3>
            <!--第一次使用:用flex展示数据-->
            <child>
                <template slot-scope="user">
                    <div class="tmpl">
                        <span v-for="item in user.data">{{item + '、'}}</span>
                    </div>
                </template>
            </child>

            <!--第二次使用:用列表展示数据-->
            <child>
              <template slot-scope="user">
                <ul>
                  <li v-for="item in user.data">{{item}}</li>
                </ul>
              </template>
            </child>

            <!--第三次使用:直接显示数据-->
            <child>
                <template slot-scope="user">
                    {{user.data}}
                </template>

            </child>

            <!--第四次使用:不使用其提供的数据, 作用域插槽退变成匿名插槽-->
            <child>
                我就是模板
            </child>
        </div>
    </template>

    <template id="tmp2">
        <div class="child">

            <h3>这里是子组件</h3>
            <!--作用域插槽-->
            <slot  :data="data"></slot>
        </div>
    </template>

    <script type="text/javascript">
        var child = {
            template: '#tmp2',
            data: function(){
                return {
                    data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
                }
            }
        }

        var father = {
            template:'#tmp',
            components: {
                "child": child
            }
        }

        var app=new Vue({
            el:'#app',
            data:{
               
            },
            components:{
                "father": father
            }
        })
    </script>
</body>
</html>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java技术分享圈

杨老师课堂之Jquery的筛选,事件,效果,Ajax,javascript跨域)

D.closest()获得D所有父元素,含自己(D、B、A)与 D.parents() 对比

1412
来自专栏技术墨客

React 状态、事件与动态渲染

例子中使用map方法将每个元素的值*2,最后得到的数组为:[2, 4, 6, 8, 10]。在React中,处理组件数组的方式与之类似。

830
来自专栏JetpropelledSnake

Vue学习笔记之Vue指令系统介绍

所谓指令系统,大家可以联想咱们的cmd命令行工具,只要我输入一条正确的指令,系统就开始干活了。

1024
来自专栏更流畅、简洁的软件开发方式

表单控件续(1)——应用接口来简化和分散代码

上次有点仓促,有几个地方没有明确。 后者是整个流程,前者是其中的一个步骤,是一个简单的思路说明,其中前三段代码都是表单控件里面的。 2、我要写的是一个表单控...

2029
来自专栏代码世界

前端之BOM和DOM

BOM和DOM简介 BOM(Browser Object Model)是指浏览器对象模型,它使JavaScript有能力与浏览器进行“对话”。 DOM(Docu...

3845
来自专栏软件开发

前端MVC Vue2学习总结(五)——表单输入绑定、组件

你可以用 v-model 指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法...

862
来自专栏简书专栏

基于Excel2013的数据转换和清洗

数字可以被设成的格式有12种:常规、数值、货币、会计专用、日期、时间、百分比、分数、科学记数、文本、特殊、自定义

1542
来自专栏云瓣

从 0 到 1 实现 React 系列 —— JSX 和 Virtual DOM

看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/...)

1021
来自专栏马涛涛的专栏

虚拟DOM

DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScr...

3082
来自专栏技术墨客

React学习(3)——列表、键值与表单 原

    例子中使用map方法将每个元素的值*2,最后得到的数组为:[2, 4, 6, 8, 10]。在React中,处理组件数组的方式与之类似。

1573

扫码关注云+社区

领取腾讯云代金券