一、 子组件传值父组件
子组件调用父组件的方法
- 在父组件中给引用的子组件注册一个事件(这个事件的名字是自定义的)
- 子组件可以触发这个事件$emit('事件名字',传递的参数)
<!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>组件自定义事件</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- 1.给组件添加事件 -->
<div id="app">
<son v-on:click-son='clickParent($event,"自定义参数")' v-on:click-son2='clickParent2'></son>
</div>
<template id='sonTemp'>
<div>
<button @click='clickSon'>点击事件</button>
<button @click='clickSon2'>点击事件2</button>
<h3>这是子组件的内容</h3>
</div>
</template>
<script>
var son = {
template: '#sonTemp',
methods: {
clickSon() {
console.log('子组件的方法');
// 2. 在子组件中触发这个事件
// 发射,触发
// 通过这种方式,子组件也可以给父组件传值
this.$emit('click-son', {
name: '张三'
})
},
clickSon2(){
console.log('子组件的方法2');
this.$emit('click-son2', 'nihao')
}
},
}
var vm = new Vue({
el: '#app',
data: {},
methods: {
// e是接受子组件传过来的参数,msg是这个方法自己参数
clickParent(e, msg) {
console.log(e);
console.log('父组件的方法,数据为:' + msg);
},
clickParent2(e) {
console.log(e);
}
},
components: {
son
}
})
</script>
</body>
</html>
二、 评论列表案例
- 将评论发表做成一个组件,发表后保存到localStorage中
- 获取用户输入的值
- 构造一个对象
- 把数据存起来(localStorage)
3.1 首先获取已经保存的数据(localStorage.getItem('key')).
3.2 把数据添加进去
3.3 在把数据保存到localStorage中(localStorage.setItem('key','value'))
- 子组件更新后父组件同步更新
- 子组件更新数据后,通知父组件
1.1 父组件引用子组件的时候注册一个事件
1.2子组件更新完数据之后调用$emit方法通知父组件
<!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>评论发表</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
.container {
padding-top: 30px
}
.list {
margin-top: 30px
}
</style>
</head>
<body>
<div id="app">
<div class='container'>
<add-comp @refresh-list='initList'></add-comp>
<ul class="list-group list">
<li v-for='item in list' :key='item.id' class="list-group-item">
<span class="badge">评论人:{{item.name}}</span>
{{item.content}}
</li>
</ul>
</div>
</div>
<template id='addTemp'>
<div>
<form>
<legend>表单提交</legend>
<div class="form-group">
<label for="">评论人</label>
<input type="text" class="form-control" v-model='name' placeholder="输入评论人">
</div>
<div class="form-group">
<label for="">评论内容</label>
<textarea class='form-control' v-model='content' placeholder="输入评论内容"></textarea>
</div>
<button type="submit" class="btn btn-primary" @click.prevent='add'>提交</button>
</form>
</div>
</template>
<script>
var addComp = {
template: '#addTemp',
data() {
return {
name: '',
content: ''
}
},
methods: {
add() {
// 1、获取用户输入的值
// 2、构造一个对象
// 3、把数据存起来(localStorage)
// 3.1 首先获取已经保存的数据
// 3.2 把数据添加进去
// 3.3 在把数据保存到localStorage中
console.log(this.name + this.content);
let obj = {
id: Date.now(),
name: this.name,
content: this.content
}
// 定义一个key,localStorage的key(这个key是我们自定义的)
// 假定这个key是commentList
// localStorage里存的都是字符串类型
// sessionStorage
// 如果localStorage内容为空说明我们没有任何数据,这个时候创建一个新的数组就行了
let commentList = JSON.parse(localStorage.getItem('commentList') || '[]');
commentList.unshift(obj);
// commentList只在这个作用域里生效
// 我们需要把它保存到localStorage中,在别的地方也可以使用
localStorage.setItem('commentList', JSON.stringify(commentList));
// 提醒父组件该更新数据
this.$emit('refresh-list')
}
},
}
var vm = new Vue({
el: '#app',
data: {
list: []
},
methods: {
initList() {
this.list = JSON.parse(localStorage.getItem('commentList') || '[]');
}
},
components: {
addComp
},
created() {
this.initList();
},
})
// 1. 先画了页面
// 2. 把表单提交抽象成了一个组件
// 3. 组件定义add方法,把数据保存到localStrorage中
// 4. 子组件通知父组件修改列表
</script>
</body>
</html>
三、 ref的使用
- 获取dom节点
- 给dom节点记上ref属性,可以理解为给dom节点起了个名字。
- 加上ref之后,在$refs属性中多了这个元素的引用。
- 通过vue实例的$refs属性拿到这个dom元素。
- 获取组件
- 给组件记上ref属性,可以理解为给组件起了个名字。
- 加上ref之后,在$refs属性中多了这个组件的引用。
- 通过vue实例的$refs属性拿到这个组件的引用,之后可以通过这个引用调用子组件的方法,或者获取子组件的数据。
<!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>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="获取元素" @click="getElement" ref="mybtn">
<h3 id="myh3" ref="myh3">哈哈哈, 今天天气太好了!!!</h3>
<hr>
<login ref="mylogin"></login>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>',
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('调用了子组件的方法')
}
}
}
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
// console.log(document.getElementById('myh3').innerText)
// console.log(this.$refs.myh3.innerText)
// console.log(this.$refs.mylogin.msg)
// this.$refs.mylogin.show()
}
},
components: {
login
}
});
</script>
</body>
</html>
四、 Vue中路由的使用
4.1 什么是路由
- 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源
- 前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
- 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由)
路由的基本使用
- 引入js文件,这个js需要放在vue的js后面,自动安装(提供了一个VueRouter的构造方法)
- 创建路由new VueRouter(),接受的参数是一个对象
- 在实例化的对象里配置属性routes:[],这个数组里的对象包含path属性和component属性
- path属性是url的地址,component属性就是显示的组件(传组件的对象)
- 创建的路由需要和vue实例关联一下
- 路由到的组件显示在哪个位置<router-view></router-view>
路由的跳转
- router-link标签可以设置to属性
- 默认是a标签,可以通过tag设置包裹标签
路由重定向
edirect可以进行路由的重定向
选中路由高亮
- 使用默认的样式
直接设置router-link-active
- 自定义样式
配置 linkActiveClass:'自定义的类名'
组件的嵌套
- 声明路由的时候设置children,这是children是一个数组,数组里是路由对象
- 这个children的组件就会渲染在它父组件的<router-view>中
命名视图
- 我们之前只能一个地址对应一个组件,现在可以一个地址对应多个组件
- components属性设置的
- 给router-view设置名字,这个名字和components组件名字是对应的
- 设置默认值default对应组件可以设置名字也可以访问
计算属性和监听器
名称案例
- 获取完整的名字,需要把姓和名字拼接在一起
- 什么时候去拼接在一起(input值改变的时候)
- 监听keyup知道input什么时候改变了,在这里就可以获取完整的名字
3.1 Method实现
3.2 Watch用法
监听data中属性的改变:
<div id="app">
<input type="text" v-model="firstName"> +
<input type="text" v-model="lastName"> =
<span>{{fullName}}</span>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstName: 'jack',
lastName: 'chen',
fullName: 'jack - chen'
},
methods: {},
watch: {
'firstName': function (newVal, oldVal) { // 第一个参数是新数据,第二个参数是旧数据
this.fullName = newVal + ' - ' + this.lastName;
},
'lastName': function (newVal, oldVal) {
this.fullName = this.firstName + ' - ' + newVal;
}
}
});
</script>
监听路由对象的改变:
<div id="app">
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = Vue.extend({
template: '<h1>登录组件</h1>'
});
var register = Vue.extend({
template: '<h1>注册组件</h1>'
});
var router = new VueRouter({
routes: [
{ path: "/login", component: login },
{ path: "/register", component: register }
]
});
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: router,
watch: {
'$route': function (newVal, oldVal) {
if (newVal.path === '/login') {
console.log('这是登录组件');
}
}
}
});
</script>
3.3 Computed用法
默认只有getter的计算属性:
<div id="app">
<input type="text" v-model="firstName"> +
<input type="text" v-model="lastName"> =
<span>{{fullName}}</span>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstName: 'jack',
lastName: 'chen'
},
methods: {},
computed: { // 计算属性; 特点:当计算属性中所以来的任何一个 data 属性改变之后,都会重新触发 本计算属性 的重新计算,从而更新 fullName 的值
fullName() {
return this.firstName + ' - ' + this.lastName;
}
}
});
</script>
定义有getter和setter的计算属性:
<div id="app">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<!-- 点击按钮重新为 计算属性 fullName 赋值 -->
<input type="button" value="修改fullName" @click="changeName">
<span>{{fullName}}</span>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstName: 'jack',
lastName: 'chen'
},
methods: {
changeName() {
this.fullName = 'TOM - chen2';
}
},
computed: {
fullName: {
get: function () {
return this.firstName + ' - ' + this.lastName;
},
set: function (newVal) {
var parts = newVal.split(' - ');
this.firstName = parts[0];
this.lastName = parts[1];
}
}
}
});
</script>
3.4 method、computed和watch的区别
- computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用,使用的时候不加();
- methods方法表示一个具体的操作,主要书写业务逻辑;
- watch一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed和methods的结合体