自定义事件
我们知道,父组件使用 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>
密 码: <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
})
}
});