3

自定义事件

我们知道,父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派得上用场了。

使用 绑定自定义事件v-on

每个 Vue 实例都实现了事件接口,即:

  • 使用 $on(eventName) 监听事件

  • 使用 $emit(eventName) 触发事件

Vue 的事件系统与浏览器的 EventTarget API 有所不同。尽管它们的运行起来类似,但是 $on 和 $emit 并不是addEventListener 和 dispatchEvent 的别名。

另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
不能用 $on 侦听子组件释放的事件,而必须在模板里直接用 v-on 绑定,参见下面的例子。

//定义子组件
var Child = {
//获取data()中的数据,并添加一个点击事件
    template: `<button @click="addCounter">{{counter}}</button>`,
    data(){
        return {
            counter: 0
        }
    },
    methods:{
        addCounter(){
            this.counter++;
            //自定义事件$emit传回根组件,同时调用根组件方法
            this.$emit('add-parent-total')
        }
    }
}
//根组件
var vm = new Vue({
    el: "#app",
    data:{
        total: 0
    },
    components: {
        Child
    },
    methods: {
//根组件中子组件改变根组件的方法
        addTotal(){
            this.total++
        }
    }
})

//html
<child @add-parent-total="addTotal"></child>

使用插槽分发内容

注意两点:

  • <app> 组件不知道它会收到什么内容。这是由使用 <app> 的父组件决定的。

  • <app> 组件很可能有它自己的模板。

为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发 (即 Angular 用户熟知的“transclusion”)。Vue.js 实现了一个内容分发 API,参照了当前 Web Components 规范草案,使用特殊的 <slot> 元素作为原始内容的插槽。

//slot插槽使其可以在html中传入数据,也可以在其中传入默认内容
var Child = {
    template: `<div>1 <slot>默认内容</slot></div>`
}
var vm = new Vue({
    el: "#app",
    components: {
        Child
    }
})

html
<child>hello</child>

有名slot

    //slot插槽使其可以在html中传入数据,也可以在其中传入默认内容
var Child = {
    template: `<div>1 
    <slot name="header">header</slot>
    <slot>默认内容</slot>
    <slot name="footer">footer</slot>
    </div>`
}
var vm = new Vue({
    el: "#app",
    components: {
        Child
    }
})

html

<child>
    <div slot="header">头部内容</div>
    22222
    <div slot="footer">底部内容</div>
</child>

非父子组件

有时候,非父子关系的两个组件之间也需要通信。在简单的场景下,可以使用一个空的 Vue 实例作为事件总线:

    var bus = new Vue()
        var AppHead = {
        template: '<div><button @click="add">添加<button></div>',
        methods: {
            add() {
                // 触发组件A中的事件
                bus.$emit('change', 1)
            }
        }
    }
    var AppBody = {
        template: '<div>{{ counter }}</div>',
        data() {
            return {
                counter: 0
            }
        },
        created() {
            //在组件B创建的钩子中监听事件
            bus.$on('change', count => {
                this.counter += count
            })
        }
    } 
    var vm = new Vue ({
        el: '#app',
        components: {
            AppHead,
            AppBody
        }
    })

模态框实例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
      *{margin: 0;padding: 0;}
    html, body {
      height: 100%;
    }
    .dcj-modal {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.3);
    }
    .dcj-modal .dcj-modal-container {
      position: absolute;
      background: white;
      border: 1px dashed green;
      width: 300px;
      min-height: 200px;
      top: 50%;
      left: 50%;
      transform: translateX(-50%) translateY(-50%);
    }
    .dcj-modal .dcj-modal-container .dcj-modal-header {
      position: relative;
      line-height: 50px;
      text-align: center;
      border-bottom: 1px solid #ccc;
    }
    .dcj-modal .dcj-modal-container .dcj-modal-header button {
      position: absolute;
      right: 8px;
      top: 40%;
      transform: translateY(-50%);
    }
    .dcj-modal .dcj-modal-container .dcj-modal-body {
      padding: 8px;
    }
    .dcj-modal .dcj-modal-container .dcj-modal-footer {
      position: absolute;
      bottom: 0;
      width: 100%;
      height: 30px;
      text-align: center;
      line-height: 30px;
      border-top: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <div id="app">
    <button @click="openRegister">注册</button>
    <button @click="openLogin">登录</button>
    <modal v-show="showRegister" @close-modal="closeRegister">
        <p slot="header">注册</p>
        <register></register>
        <p slot="footer">
            <input type="submit" value="注册" form="register"/>
        </p>
    </modal>
    <modal v-show="showLogin" @close-modal="closeLogin">
        <p slot="header">登录</p>
        <login></login>
        <p slot="footer">
            <input type="submit" value="登录" form="login"/>
        </p>
    </modal>
  </div>
  <script src="vue.js" charset="utf-8"></script>
  <script>
      var Login = {
          template: `<form action="" id="login">
            用户名: <input type="text" /><br>
            密&nbsp;&nbsp;&nbsp;码:  <input type="password" /><br>

        </form>`
      }
      var Register = {
          template: `<form action="" id="register">
            用户名: <input type="text" /><br>
            密&nbsp;&nbsp;&nbsp;码:  <input type="password" /><br>
        </form>`
      }
    var Modal = {
      template: `<div class="dcj-modal" @click.self="closeModal">
        <div class="dcj-modal-container">
          <div class="dcj-modal-header">
            <slot name="header">title</slot>
            <button @click="closeModal">关闭</button>
          </div>
          <div class="dcj-modal-body">
            <slot>body</slot>
          </div>
          <div class="dcj-modal-footer">
            <slot name="footer">footer</slot>
          </div>
        </div>

      </div>`,
      methods: {
        closeModal() {
          this.$emit('close-modal');
        }
      }
    }
    var vm = new Vue({
      el: '#app',
      data: {
        showRegister: false,
        showLogin: false
      },
      methods: {
        openRegister() {
          this.showRegister = true
        },
        closeRegister() {
          this.showRegister = false
        },
        openLogin() {
          this.showLogin = true
        },
        closeLogin() {
          this.showLogin = false
        }
      },
      components: {
        Modal,
        Register,
        Login
      }
    })
  </script>
</body>
</html>

vue路由

对于大多数单页面应用,都推荐使用官方支持的 vue-router 库。更多细节可以看 vue-router 文档。

静态路由

不需要复杂的路由

首先需要
npm i -S vue-router 安装router库
        var NewsComponent = { template: `<div>新闻</div>`}
        var ShopComponent = { template: `<div>商场</div>`}
        var NotFoundComponent = { template: `<div>404</div>`}
        var routes = [
            {
                path: '/',
                redirect: '/news'//默认根目录跳转
            },
            {
                path: '/news',
                component: NewsComponent
            },
            {
                path: '/shop',
                component: ShopComponent
            },
            {
                path: '*',
                component: NotFoundComponent
            }
        ]
        var router = new VueRouter({
            routes
        })
//        var app = new Vue({
//            el: "#app",
//            router
//        })

        var app = new Vue({
            el: "#app",
            router
        })

动态路由

var Goods = {
      template: `<div>
        <ul>
          <li v-for="(good, index) in goods" :key="'goods' + index">
            <img :src="good.goods_thumb" :alt="good.goods_name" :title="good.goods_name"/>
            {{ good.goods_name }}
            {{ good.price }}
          </li>
        </ul>
      </div>`,
      watch: {
        $route (to, from) {
          console.log(to.params.id);
          axios.get('http://h6.duchengjiu.top/shop/api_goods.php?cat_id='+to.params.id).then(res => {
            console.log(res.data.data)
            this.goods = res.data.data
          })
        }
      },
      data() {
        return {
          goods: []
        }
      }
    }
    var router = new VueRouter({
      routes: [
        { path: '/cat/:id', component: Goods }
      ]
    });
    var vm = new Vue({
      el: '#app',
      router,
      data: {
        cats: []
      },
      created() {
        axios.get('http://h6.duchengjiu.top/shop/api_cat.php').then(res => {
          console.log(res)
          this.cats = res.data.data
        })
      }
    });

Allen
40 声望6 粉丝

新手上路,擅长急刹