自定义事件
咱们知道,父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通讯呢?这个时候 Vue 的自定义事件系统就派得上用场了。php
使用 绑定自定义事件v-on
每一个 Vue 实例都实现了事件接口,即:html
使用 $on(eventName) 监听事件vue
使用 $emit(eventName) 触发事件ios
Vue 的事件系统与浏览器的 EventTarget API 有所不一样。尽管它们的运行起来相似,可是 $on 和 $emit 并非addEventListener 和 dispatchEvent 的别名。vue-router
另外,父组件能够在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
不能用 $on 侦听子组件释放的事件,而必须在模板里直接用 v-on 绑定,参见下面的例子。npm
//定义子组件 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>
使用插槽分发内容
注意两点:axios
<app> 组件不知道它会收到什么内容。这是由使用 <app> 的父组件决定的。api
<app> 组件极可能有它本身的模板。浏览器
为了让组件能够组合,咱们须要一种方式来混合父组件的内容与子组件本身的模板。这个过程被称为内容分发 (即 Angular 用户熟知的“transclusion”)。Vue.js 实现了一个内容分发 API,参照了当前 Web Components 规范草案,使用特殊的 <slot> 元素做为原始内容的插槽。app
//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> 密 码: <input type="password" /><br> </form>` } var Register = { template: `<form action="" id="register"> 用户名: <input type="text" /><br> 密 码: <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 }) } });