目录
一、组件间通信
以下5种组件间通信,数据传递的方法中,第1~3中是比较常用的。
1. 使用 props 和 $emit 进行父子组件通信
1) 父==>子组件传值
- Step1:先通过
v-bind
给父组件中绑定自定义的属性; - Step2: 在子组件中使用
props
接收父组件传递的数据; - Step3: 在子组件中使用接收的数据。
2) 子==>父组件传值
- Step1: 在父组件绑定自定义的事件;
- Step2: 在子组件中触发原生的事件,在事件函数中使用
$emit
触发父组件自定义的事件; - Step3:
$emit
的参数是回传给父组件的数据。
Vue.component("Child", {
template: `
<div>
<input type="text" v-model="childData" @input='changeValue'/>
</div>
`,
props: ['childData'],
methods: {
changeValue(val){
// 父组件自定义的事件一定是通过this.$emit()去触发
// $emit(自定义的事件名, 消息)
this.$emit('childHandler', val)
}
}
});
Vue.component("Parent", {
data: {
msg: "我是父组件"
},
template: `
<div>
<p>我是父组件</p>
<Child :childData='msg' @childHandler='getDataHandler' />
</div>,
`,
methods: {
getDataHandler(val){
console.log(val)
}
}
})
2. 使用 $attrs 和 $listeners 进行多层组件间通信
第一种方式处理组件之间的数据传输有一个问题:多层组件之间的数据传递只能一层一层传递。Vue2.4开始提供了$attrs
和$listeners
来解决这个问题。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件通信二</title>
</head>
<body>
<div id="app"></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component("C", {
data() {
return {}
},
// 通过$attrs接收父组件传递来的数据
template: `
<div>
<h4>这是子组件的内容</h4>
<p>父组件传入子组件的数据:{{$attrs.messageParent}}</p>
<button @click="cClickHandler">向父组件传递数据</button>
</div>
`,
methods: {
cClickHandler() {
// 通过$listeners触发父组件监听的自定义事件向父组件传递数据
this.$listeners.getSubData('组件C传入父组件数据')
}
}
});
Vue.component("B", {
data() {
return {}
},
template: `
<div>
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
`,
methods: {}
});
Vue.component("A", {
data() {
return {}
},
template: `
<div>
<B v-bind="$attrs" v-on="$listeners"></B>
</div>
`,
methods: {}
});
let App = {
data() {
return {
msg: "我是父组件的内容",
msg2: "Hello sub component"
}
},
template: `
<div>
<h4>这是一个父组件</h4>
<p>注意:{{msg}}</p>
<A :messageParent="msg2" v-on:getSubData="showData"></A>
</div>
`,
methods: {
showData(val) {
console.log(val);
this.msg = val;
}
},
};
new Vue({
el: "#app",
data() {
return {}
},
components: {
App
},
template: "<App/>"
})
</script>
</body>
</html>
3. 使用 中央事件总线 bus 进行组件间通信
上面两种方式处理的都是父子组件之间的数据传递,如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式:
新建一个Vue
对象bus
,然后通过bus.$emit
触发事件传递数据,通过bus.$on
监听触发的事件接收数据。
// 中央事件总线
var bus = new Vue();
Vue.component("brother1", {
data() {
return {
brother2Msg: ''
}
},
template: `
<div>
<p>我是同级组件A</p>
<p>同级组件B传递过来的数据:{{brother2Msg}}</p>
</div>
`,
mounted() {
// 绑定自定义的全局事件globalEvent
bus.$on('globalEvent', (val)=>{
this.brother2Msg = val;
})
}
});
Vue.component("brother2", {
data() {
return {
msg: ""
}
},
template: `
<div>
<p>我是同级组价B</p>
<input type="text" v-model="msg" @input="passData(msg)"/>
</div>
`,
methods: {
passData(val){
// 触发自定义的全局事件globalEvent
bus.$emit('globalEvent', val)
}
}
});
var App = { // 父组件
template: `
<div>
<brother1></brother1>
<brother2></brother2>
</div>
`
};
new Vue({
el: "#app",
components: {App},
template: "<App/>"
})
4. 使用 $parent 和 $children 实现父子组件之间的通信
在父组件中通过this.$children[<index>].<prop_name>
可以向子组件传递数据;在子组件中通过this.parent.<prop_name>
可以向父组件传递数据。
Vue.component("Child", {
props: {
value: String, //v-model会自动传递一个属性名为value的prop属性
},
data() {
return {
myMessage: this.value
}
},
methods: {
changeValue() {
// 通过如此调用,可以向父组件传递数据,改变父组件的值
this.$parent.message = this.myMessage;
}
},
template: `
<div>
<input type="text" v-model="myMessage" @change="changeValue">
<p>{{myMessage}}</p>
</div>
`
});
Vue.component('Parent', {
data() {
return {message: 'Hi, child'}
},
template: `
<div>
<p>我是父组件</p>
<p>{{message}}</p>
<button @click="changeChildValue">test</button>
<Child></Child>
</div>
`,
methods: {
changeChildValue() {
// 通过如此调用,可以向子组件传递数据,改变子组件的值
this.$children[0].myMessage = this.message;
}
},
});
var App = {
template: `
<div>
<h2>我是入口组件</h2>
<Parent/>
</div>
`
};
new Vue({
el: "#app",
components: {App},
template: "<App/>"
})
5. 使用 provide 和 inject 实现父组件向子组件的单向通信
父组件中通过provide
来提供变量,然后子组件中通过inject
来注入变量。不论子组件有多深,只要调用了inject
那么就可以注入provide
中的数据,而不是局限于只能从当前父组件的props
属性来获取数据。只要在父组件的生命周期内,子组件都可以调用。
Vue.component("Child", {
data() {
return {msg: ""}
},
template: "<div>我是子组件: {{msg}}</div>",
inject: ['for'],
created() {
this.msg = this.for;
}
});
Vue.component("Parent", {
template: `
<div>
<p>我是父组件</p>
<Child/>
</div>
`
});
var App = {
provide: {
for: "从祖先组件跨代传递到子孙组件的数据"
},
template: `
<div>
<h2>我是入口组件,也是祖先组件</h2>
<Parent/>
</div>
`
};
new Vue({
el: "#app",
components: {
App
},
template: "<App/>"
})
二、过滤器
- 过滤器的作用:为页面中的数据进行添油加醋。
- 过滤器的种类:局部过滤器、全局过滤器。
1. 局部过滤器
- 声明过滤器: 在组件的
filters
选项中定义函数。 - 使用过滤器:{{data|myFilter}}
2. 全局过滤器
使用Vue.filter(<name>, func)
创建全局过滤器。
Vue.filter("moneyFormat", function(value) {
return "¥" + value;
})
3. 过滤器中还可以传入参数
Vue.filter("myFilter", function(value, arg) {
return arg + value.split('').reverse().join('');
})
三、插槽
插槽slot
是Vue
内置的组件,它的作用是作为承载内容分发的出口。
具名插槽
为slot
添加name
属性的插槽叫做具名插槽,在使用时,将根据name
去分别替换内容。
Vue.component("myLi", {
template: `
<li>
<slot name="one">第一个插槽</slot>
<slot name="two">第二个插槽</slot>
</li>
`
});
new Vue({
el: "#app",
template: `
<ul>
<myLi>
<h3 slot="two">插入第二插槽</h3>
<h2 slot="one">插入第一插槽</h2>
</myLi>
</ul>
`
})
四、watch 监听
watch
监听的是单个属性,当监听基本数据类型时,使用简单监视;当监听复杂数据类型(引用数据类型),使用深度监视。
- 定义
watch
监听时,watch
对象的属性名称必须与被监听的data
对象中数据属性的名称相同。
示例代码:
new Vue({
el: "#app",
data: {
msg: "",
arr_obj: [
{
name: "Jack",
age: 18,
sex: male
}
]
},
watch: {
// 基本数据类型,简单监视
msg: function(newValue, oldValue) {
console.log(newValue, oldValue);
},
// 复杂数据类型,深度监视
arr_obj: {
deep: true,
handler: function(newValue, oldValue) {
console.log(newValue);
}
}
}
})
五、计算属性
计算属性可以同时监听多个数据属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue-computed</title>
<script src="node_modules/vue/dist/vue.js"></script>
<style>
.active{
background-color: #409eff;
}
</style>
</head>
<body>
<div id="app">
<audio autoplay controls :src="getCurrentMusicSrc"></audio>
<ul>
<li
v-for="(item, index) in musicData"
@click="playMusic(index)"
:class="{active: currentIndex == index}">
<h4>{{item.name}}</h4><span>--{{item.author}}</span>
</li>
</ul>
</div>
<script>
var musicData = [
{
name: "起风了",
author: "买辣椒也用券",
songSrc: './Music/起风了.mp3'
},
{
name: "芒种",
author: "赵方婧",
songSrc: './Music/芒种.mp3'
},
{
name: "野狼disco",
author: "宝石gem",
songSrc: './Music/野狼disco.mp3'
}
];
new Vue({
el: "#app",
data: {
musicData: musicData,
currentIndex: 0
},
computed: {
getCurrentMusicSrc(){
return this.musicData[this.currentIndex].songSrc
}
},
methods: {
playMusic(index){
this.currentIndex = index
}
}
})
</script>
</body>
</html>
六、生命周期
生命周期钩子函数:
- beforeCreate: 组件创建之前,此时组件对象实例已经创建,但实例的属性还没有初始化。
- created: 组件创建之后,在这个方法中通常会请求后端,获取数据。
- beforeMount: 挂载数据到
DOM
之前会调用。 - mounted: 挂载数据到
DOM
之后会调用,应用:操作DOM
。 - beforeUpdate: 在更新
DOM
之前会调用,应用:可以获取原始的DOM
。 - updated: 在更新
DOM
之后会调用,应用:可以获取最新的DOM
。 - activated: 当
keep-alive
组件激活时调用。 - deactivated: 当
keep-alive
组件停用时调用。 - beforeDestroy: 在组件销毁之前调用。
- destroyed: 在组件销毁之后调用。
- errorCaptured:
七、keep-alive 内置组件
Vue 内置组件keep-alive
能在组件的切换过程中将组件的状态保留在内存中,即缓存组件,防止重复渲染DOM
。
var App = {
data() {
return {
isShow: true
}
},
template: `
<div>
<keep-alive>
<sub-component v-if="isShow"></sub-component>
</keep-alive>
<button @click="isShow=!isShow">显示切换</button>
</div>
`
}
new Vue({
el: "#app",
components: {App}
})
八、其他补充
- 如果给HTML标签绑定
ref="xxx"
属性,使用this.$refs.xxx
获取原生的JS-DOM
对象。 - 如果给组件绑定
ref=“xxx”
属性,那么this.$refs.xxx
获取的是这个组件对象。 $nextTick()
会在DOM
更新循环结束之后自动触发,执行它的回调函数。在修改数据之后必须使用此方法,在其回调函数中获取更新之后的DOM
。- 获取更新后的
DOM
,除了使用$nextTick()
外,还可以在生命周期钩子函数updated
中获取。
let App = {
data() {
return {
isShow: false
}
},
template: `
<div class="app">
<input type="text" v-show="isShow" ref="input" />
</div>
`,
mounted() {
this.isShow = true;
/*this.$nextTick(function() { // 通过$nextTick的回调函数获取DOM,并获取焦点
this.$refs.input.focus();
});*/
this.$nextTick(()=>{ // 通过$nextTick定义回调函数为箭头函数,获取DOM,并获取焦点
this.$refs.input.focus();
})
}
};
new Vue({
el: "#app",
template: "<App/>",
components: {App}
})
九、RESTful 规范
RESTful规范是一种软件的架构风格、设计风格,而不是标准,为客户端和服务端的交互提供一组设计原则和约束条件。
前后端分离:
- 后端提供接口(API)
- 前端写页面和Ajax技术
1. 面向资源变成
每个URL代表一种资源,URL中尽量不要使用动词,要用名词,往往名词跟数据库表格相对应。一般来说,数据库中的表都是同种记录的集合,所有API中的名词也应该使用复数。
例如:一个提供动物园信息的API,包括各种动物和雇员的信息,它的路径应该设计成:
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees
2. 在URL中的过滤条件
如果记录数量很多,服务器不可能将所有的数据都返回给用户。API应该提供参数,用于过滤返回结果。
?limit=10: 指定返回记录的数量
?offset=10: 指定返回记录的开始位置
?page=2&per_page=100: 指定第几页,以及每页的记录数
?sortby=name&order=asc: 指定返回结果按照哪个属性排序,以及排序顺序
?item_id=1: 指定筛选条件
3. 尽量使用HTTPS
4. 相应时设置状态码
5. 返回错误信息
如果状态码是4XX,应该向用户返回错误信息。一般来说,返回的信息中将error
作为键名,错误信息作为键值即可。
6. Hypermedia API
如果遇到需要跳转的情况,那么就要携带跳转接口的URL。
Hypermedia API 的设计,比如github的API就是这种设计。访问api.github.com就会得到一个所有可用的API的网址列表。
7. 其他
- API的身份认证应该使用OAuth 2.0框架
- 服务器返回的数据格式,应该尽量使用JSON,避免使用XML