Vue.js概述
用于构建用户界面渐进式的MVVM框架,核心库只关注视图层
渐进式的含义:主张最少,vue包含了声明渲染,组件化系统,客户端路由,状态管理,构建工具等 这些我们都可以根据需求来逐渐扩展
组件机制
对一个功能和样式进行独立的封装,让HTML元素可以得到扩展,代码得到复用,开发灵活,高效。
vue组件拥有外部传入的属性(prop)和事件,自己的状态(data)和依据状态和数据计算的计算属性(computed),各个纬度组合起来最终呈现的样子和交互逻辑。
组件数据传递
每一个组件都是一个独立的作用域,组件间的数据不应该存在引用关系,即使出现引用,也不允许组件操作组件内部以外的的其他数据。
vue向组件内部传递prop数据,组件内部需要显性的声明该prop字段,如父子组件prop数据传递
<!-- child.vue -->
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: 'hello world' // 当default为引用类型时,需要使用 function 形式返回
}
}
}
</script>
父组件向子组件传递数据:
<!-- parent.vue -->
<template>
<child :msg="parentMsg"></child>
</template>
<script>
import child from './child';
export default {
components: {
child
},
data () {
return {
parentMsg: 'some words'
}
}
}
</script>
组件事件传递
Vue组件内部实现一个事件总线系统,即EventBus。每个组件的实例都继承了EventBus,都可以接受事件$on
和发送事件$emit
。
<!-- child.vue -->
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: 'hello world' // 当default为引用类型时,需要使用 function 形式返回
}
},
methods: {
changeMsg(newMsg){
this.$emit('updateMsg',newMsg)
}
}
}
</script>
<!-- parent.vue -->
<template>
<child :msg="parentMsg" @updateMsg="changeParentMsg"></child>
</template>
<script>
import child from './child';
export default {
components: {
child
},
data () {
return {
parentMsg: 'some words'
}
},
methods: {
changeParentMsg(newMsg){
this.parentMsg = newMsg
}
}
}
</script>
父组件 parent.vue 向子组件 child.vue 传递了 updateMsg 事件,在子组件实例化的时候,子组件将 updateMsg 事件使用$on
函数注册到组件内部,需要触发事件的时候,调用函数this.$emit
来触发事件。
除了父子组件的事件传递,还可以以vue实例做一个桥梁用于多层级父子组件间的通信
const eventBus = new Vue();
// 父组件中使用$on监听事件
eventBus.$on('eventName', val => {
// ...do something
})
// 子组件使用$emit触发事件
eventBus.$emit('eventName', 'this is a message.');
事件总线系统还提供所有事件如下:
-
$on:监听、注册事件。
-
$emit:触发事件。
-
$once:注册事件,仅允许该事件触发一次,触发结束后立即移除事件。
-
$off:移除事件
插槽内容分发
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。
slot核心问题是内容显示不显示以及怎样显示
可以合成内容
<!-- parentSlot.vue-->
<template>
<child>
自组件内容
</child>
</template>
<script>
import child from './childSlot'
export default {
name: "parentSlot"
}
</script>
<!-- childSlot.vue-->
<template>
<span>
<slot></slot>
</span>
</template>
<script>
export default {
name: "childSlot"
}
</script>
当组件渲染的时候,<slot></slot>
将会被替换为“自组件内容”。插槽内可以包含任何模板代码,包括 HTML:
<!-- parentSlot.vue-->
<template>
<child>
//添加一个图标
<i class='icon'></i>
自组件内容
</child>
</template>
<script>
import child from './childSlot'
export default {
name: "parentSlot"
}
</script>
<!-- childSlot.vue-->
<template>
<span>
<slot></slot>
</span>
</template>
<script>
export default {
name: "childSlot"
}
</script>
甚至其它的组件:
<!-- parentSlot.vue-->
<template>
<child>
//菜单组件
<menu-component></menu-component>
自组件内容
</child>
</template>
<script>
import child from './childSlot'
export default {
name: "parentSlot"
}
</script>
<!-- childSlot.vue-->
<template>
<span>
<slot></slot>
</span>
</template>
<script>
export default {
name: "childSlot"
}
</script>
如果 <child>
组件的 template
内容中没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
插槽分为了默认插槽(匿名插槽)和具名插槽,默认插槽没有 slot :name属性,一个组件只有一个该类插槽。具名插槽name也是唯一的
具名插槽可以在一个组件中出现N次,出现在不同的位置,只需要使用不同的name属性区分即可
<template>
<!-- 父组件 parent.vue -->
<div class="parent">
<h1>父容器</h1>
<child>
<div class="tmpl" slot="up">
<span>菜单up-1</span>
</div>
<div class="tmpl" slot="down">
<span>菜单down-1</span>
</div>
<div class="tmpl">
<span>菜单->1</span>
</div>
</child>
</div>
</template>
<template>
<div class="child">
<!-- 具名插槽 -->
<slot name="up"></slot>
<h3>这里是子组件</h3>
<!-- 具名插槽 -->
<slot name="down"></slot>
<!-- 匿名插槽 -->
<slot></slot>
</div>
</template>
如上,slot 标签会根据父容器给 child 标签内传入的内容的 slot 属性值,替换对应的内容。
其实,默认插槽也有 name 属性值,为default
,同样指定 slot 的 name 值为 default,一样可以显示父组件中传入的没有指定slot的内容。
作用域插槽:作用域插槽可以是默认插槽,也可以是具名插槽,不一样的地方是,作用域插槽可以为 slot 标签绑定数据,让其父组件可以获取到子组件的数据。
<template>
<!-- parent.vue -->
<div class="parent">
<h1>这是父组件</h1>
<current-user>
<template slot="default" slot-scope="slotProps">
{{ slotProps.user.name }}
</template>
</current-user>
</div>
</template>
<template>
<!-- child.vue -->
<div class="child">
<h1>这是子组件</h1>
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: '小赵'
}
}
}
}
</script>
子组件 child 在渲染默认插槽 slot 的时候,将数据 user 传递给了 slot 标签,在渲染过程中,父组件可以通过slot-scope
属性获取到 user 数据并渲染视图。
slot 实现原理:当子组件vm
实例化时,获取到父组件传入的 slot 标签的内容,存放在vm.$slot
中,默认插槽为vm.$slot.default
,具名插槽为vm.$slot.xxx
,xxx 为 插槽名,当组件执行渲染函数时候,遇到<slot>
标签,使用$slot
中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可曾该插槽为作用域插槽。
但是 自 2.6.0 起有所更新。已废弃的使用 slot|
slot-scope
。vue为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot
指令)。它取代了 slot
和 slot-scope
这两个目前已被废弃但未被移除且仍在文档中,还可以继续使用可以从官方文档浏览最新的API。