组件模板
如果仅仅具备前面的知识,那你的前端开发与Jquery、甚至原生Javascript形式的开发别无二样,Vue真正的强大之处在于,它不仅彻底实现了网页组件的模块化,还提供了大量实用且美观的模块
基础组件
W3C命名规范:字母全小写,包含连字符
核心语法
// 组件定义(组件应定义在脚本内容的前方)- 参数1:组件名 参数2:组件逻辑
Vue.component('cmp-a', { // 组件名:cmp-a
props: [{},], // 组件属性
template: '', // 组件内容(只能有一个"根")
methods:{}, // 组件函数库
data:{} // 组件的变量
// ,...
}
);
简单样例
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script>
Vue.component('cmp-a', {
props: ['attr1'],
template: '<li>{{ attr1 }}</li>'
}
);
// ..
</script>
<div id="demo">
<!-- 值传递:message ~ x → attr1 → <cmp-a> -->
<cmp-a
v-for="x in message"
v-bind:attr1="x"
v-bind:key="x.id"> <!-- 为每个组件提供一个“key” -->
</cmp-a>
</div>
<script>
// ViewModel
let vue = new Vue({
el: '#demo',
data: {
message: [
{ id: 0, text: 'A' },
{ id: 1, text: 'B' },
{ id: 2, text: 'C' }
]
}
});
</script>
说明:<组件标签>
会被加载替换成其定义时的`组件内容
生存范围
全局作用
Vue.component()
方式定义的组件是全局可用的,如果打包程序不够细节,就意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中,用户需要无谓的下载。
局部作用
// 手动导入局部组件A和C,并存到组件库
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
ComponentA,
ComponentC
},
// ...
}
// 自动导入需要的组件(Vue CLI 3+)
// https://cn.vuejs.org/v2/guide/components-registration.html#%E5%9F%BA%E7%A1%80%E7%BB%84%E4%BB%B6%E7%9A%84%E8%87%AA%E5%8A%A8%E5%8C%96%E5%85%A8%E5%B1%80%E6%B3%A8%E5%86%8C
属性验证
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型检查验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
自定义属性的继承
Vue.component('base-input', {
inheritAttrs: false,
//...
})
跨级传递数据
如果A组件内嵌套着B组件,B组件内嵌套着C组件,那么A传递数据到C的办法是在B中:
<ComponentC v-bind="$attrs" />
文件组件
命名规范:文件名采用驼峰命名,导入时如果以驼峰命名则在使用时对应的是"-"式
<aside-list>:
import AsideList from "./components/List";
...
components: {Test, AsideList, Main, Login}
基本语法
<template> // 组件内容
<p>{{ greeting }} World!</p>
</template>
<script>
module.exports = {
data: function() { // 组件的变量
return {
greeting: "Hello"
};
}
};
export default { // 组件暴露
name: 'Xxx', // 组件名字【可选】
components: {} // 组件库
}
</script>
<style scoped> // 组件样式:scope指仅作用于当前
p {
font-size: 2em;
text-align: center;
}
</style>
return
(1) 返回对象,当前组件可引用来进行初始化,作用域仅限于当前组件!
(2) rules对象为组件配置监听,作用域仅限于当前组件!
集成样例:
</template>
<script>
import ChildComponents from './ChildComponents.vue' // 提前引入子组件
import Cookie from '../lib/cookie.js' // 引入公共Js脚本或类库
export default {
data () { //组件的私有数据(基于函数来返回私有对象)
return {
title: '...',
firstName: '...',
lastName: '...',
}
},
props: ['age'], // 父组件传递过来的数据(数组或对象)
computed: { // 计算属性
函数名: function (){}
},
watch: { // 监听
title (preVal, newVal) {
console.log(`改变之前的值:${preVal};改变之后的值:${newVal}`)
}
},
methods: {}, // 函数集
beforeCreated () {} // 各种钩子函数...
}
</script>
<style lang="scss" scoped> // CSS样式:scoped省略后会应用到全局
@import "../base/reset.css"; // 支持import语法引入css文件
...
</style>
插座插槽
为了拼接组合多个组件,Vue提出了插座插槽的概念,巧妙的是,Vue通过对插座插槽里的组件进行拔插就能实现局部更新(后续)!
编写组件
Vue.component('cmp-a', { // 组件名:cmp-a
props: ['a_attr1'], // 属性名:a_attr1
template: '<li>{{ a_attr1 }}</li>' // 组件内容
}
);
Vue.component('cmp-b', { // 组件名:cmp-b
props: ['b_attr1'], // 属性名:b_attr1
template: '<li>{{ b_attr1 }}</li>' // 组件内容
}
);
编写插座
Vue.component('slot-x', { // 组件名:slot-x
template: // 组件内容:插座=[slot-a]+[slot-b]+..
'<div>\
<slot name="slot-a"></slot>\
<ul>\
<slot name="slot-b"></slot>\
</ul>\
</div>'
}
);
编写视图模型
let vue = new Vue({
el: '#demo',
data: {
msg1: "Alpha",
msg2: ["A", "B", "C"]
}
});
使用插座
<div id="demo">
<!-- 插座:<slot-x>的作用是在此处放置一个插座 -->
<slot-x>
<cmp-a slot="slot-a" :a_attr1="msg1"></cmp-a>
<cmp-b slot="slot-b" v-for="ele in msg2" :b_attr1="ele"></cmp-b>
</slot-x>
</div>
组件事件
组件调用自定义事件的函数其实分为两种,一个是调用组件内部的函数,另一个是调用视图模型的函数。
组件函数
// 1、编写组件
Vue.component('cmp', { // 组件名:cmp
props: ['attr1'], // 属性:attr1
template: // 组件内容
'<li>{{ attr1 }} <button v-on:click="emit_remove">删除</button></li>',
methods: { // 函数:emit_remove()
emit_remove: function () {
alert("你点击了删除!");
}
}
}
);
说明:v-on:click
事件调用属性可以简写为@click
,二者作用完全相同!
使用组件
<div id="demo">
<cmp slot="slot-b" v-for="(ele, index) in msg" :attr1="ele"></cmp>
</div>
模型函数
由于ViewModel和组件分别属于两个访问域,所以组件内部并不能直接访问ViewModel,这样就一来,组件事件函数就不能修改视图模型中的数据。不过,Vue巧妙地借助事件分发解决了问题。
所谓分发,指当触发组件的事件函数时,this.$emit()
语句又会触发属性所绑定的函数,就像是一次触发被分给了两个函数!
编写组件
Vue.component('cmp', { // 组件名:cmp
props: ['attr1', 'del'], // 属性
template: '<li>{{ attr1 }} <button v-on:click="emit_remove">删除</button></li>', // 组件内容
methods: { // 函数
emit_remove: function (del) {
// 事件分发:触发emit_remove属性所绑定的函数
this.$emit('emit_remove', del);
}
}
}
);
编写视图模型
let vue = new Vue({
el: '#demo',
data: {
msg: ["A", "B", "C"]
},
methods: {
remove: function (begin) {
// splice(begin, len): 从指定索引开始删除数组的若干个元素
this.msg.splice(begin, 1);
}
}
});
使用组件
<div id="demo">
<cmp slot="slot-b" v-for="(ele, index) in msg"
:attr1="ele" :del="index" v-on:emit_remove="remove(index)"></cmp>
</div>