按照功能(或按照复用性)把一个页面拆成各个板块(模块),每一个模块都是一个单独的文件(单独的组件),最后把各个模块(组件)拼在一起即可!!
目的 :方便团队协作开发 实现复用
功能型组件「UI组件库中提供的一般都是功能型组件:element/iview/antdv/vant/cube..」
业务型组件
以后开发项目,拿到设计稿的第一件事情:划分组件「按照功能版块划分、本着复用性原则,拆的越细越好(这样才能更好的实现复用)」
组件的创建及使用
创建一个 Xxx.vue 就是创建一个vue组件{局部组件、私有组件},组件中包含:结构、样式、功能
结构:基于template构建
+ 只能有一个根元素节点(vue2)
+ vue的视图就是基于template语法构建的(各种指令&小胡子...),最后vue会把其编译为真实的DOM插入到页面指定的容器中
首先基于 vue-template-compiler 插件把template语法编译为虚拟DOM「vnode」
其次把本次编译出来的vnode和上一次的进行对比,计算出差异化的部分「DOM-DIFF」
最后把差异化的部分变为真实的DOM放在页面中渲染
样式:基于style来处理
功能:通过script处理
+ 导出的这个对象是VueComponent类的实例(也是Vue的实例):对象 -> VueComponent.prototype -> Vue.prototype
+ 在对象中基于各种 options api 「例如:data、methods、computed、watch、filters、生命周期函数...」实现当前组件的功能
+ 在组件中的data不再是一个对象,而是一个“闭包”
+ 各个组件最后会合并在一起渲染,为了保证组件中指定的响应式数据是“私有的”,组件之间数据即使名字相同,也不会相互污染...所以需要基于闭包来管理
注意;App.vue页面入口相当于首页,写好的组件都导入到这个里面
{{ msg }}
私有组件(使用的时候首先进行导入,然后注册,这样视图中就可以调用组件进行渲染了)
需要使用私有组件的时候,需要先导入import Test from "./Test.vue";
然后注册:这样就可以调用组件进行渲染了
//3.使用组件:可以使用单闭合或双闭合
创建全局组件
1. 创建一个局部组件
{{ msg }}
@2 在main.js入口中,导入局部组件Vote,把其注册为全局组件
import Vote from './Vote.vue';
Vue.component('Vote', Vote)
@3 这样在任何组件(视图中),无需基于components注册,直接可以在视图中调用
调用组件的方式
调用组件的时候,可以使用:
双闭合
双闭合的方式可以使用插槽slot
@1 在封装的组件中,基于
标签预留位置
@2 调用组件的时候,基于双闭合的方式,把要插入到插槽中的内容写在双闭合之间
单闭合
组件的名字可以在“kebab-case”和“CamelCase”来切换:官方建议组件名字基于CamelCase命名,渲染的时候基于kebab-case模式使用!
插槽的作用
插槽分为了默认插槽、具名插槽、作用域插槽,
默认插槽:只需要在调用组件
内插入我们想要的插入的html代码,会默认放到组件源代码的
插槽中
组件内部
slot预留位置 默认name:default
调用组件的时候
//只有一个的时候可以不用template包裹
头部导航
具名插槽:组件中预设好多插槽位置,为了后期可以区分插入到哪,我们把插槽设置名字
内自己写的代码,我们用template包裹代码,并把v-slot:xxx写在template上,这时就会将xxx里面的代码,包裹到组件源代码的
的标签中
默认名字是default 组件内部
默认名字是default
调用组件:需要把v-slot写在template上
...
...
v-slot可以简写为#:#xxx
作用域插槽:把组件内部定义的数据,拿到调用组件时候的视图中使用
组件中data内的数据只能在本模块中使用,如果想让调用组件的插槽也能获取数据,就需要对组件内对的slot做bind绑定数据,调用组件的template标签做#top="AAA"
,获取数
==组件内部==:
==调用组件==:
v-slot="AAA"
或:default="AAA"
获取数据 组件内部
把组件中的list赋值给list属性,把msg赋值给msg属性,插槽中提供了两个作用域属性:list/msg
调用组件
定义一个叫做AAA的变量,来接收插槽中绑定的数据
AAA={
list:[...],
msg:...
}
调用组件的时候
每创建一个组件其实相当于创建一个自定义类,而调用这个组件就是创建VueCommponent(或者Vue)类的实例
组件中的script中存在的状态值和属性值?
_vode
对象的私有属性中(所以状态值和属性值名字不能重复)template标签
中调用状态值和属性值,不需要加this,直接调用状态名或属性名script标签
中调用状态值和属性值,需要加this调用vue中的单向数据流
父子组件传递数据时,只能由父组件流向子组件,不能由子组件流向父组件。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
组件传参的分类7种:
1.父组件向子组件传参
父组件向子组件传参:props
VueComponent
的实例第一步:父组件在组件调用标签中自定义属性
//如果想把data中的状态值传递过去需要v-bind绑定
注意 如果想把data中的状态值传递过去需要v-bind绑定
第二步:子组件通过props接收(数组,对象)
// props的值是只读的 能改,会报错
//数组格式
props:["msg","num"]
//对象格式
props: {
msg: {
//传参类型必须是字符串
type: String,
//必须传参
required: true,
},
num: {
type: Number,
//如果不传参默认是102
default: 102,
},
},
//----------------------------------------
//用自定义变量numa接收num,然后页面使用numa(解决只读问题)
我是子组件 coma------{{ msg }}----{{ numa }}
props: ["msg", "num"],
data() {
return {
numa: this.num,
};
},
2.子组件向父组件传参
子组件向父组件传参,基于==发布订阅(@xxx给子组件标签自定义事件、$emit)==
第一步:父组件在调用子组件的标签上需要自定义一个事件,这个事件及绑定的方法就会添加到子组件的事件池中:底层实质上是调用了this.$on("myEvent",fn)
methods: {
getData() {},
},
第二步:子组件用this.$emit()接受(this.$emit(myEvent,参数1,参数2)), 参数可以是子组件的,顺便传给父组件,实现子组件向父组件传值
data() {
return {
flag: "你很美",
n: 101,
};
methods: {
goParentData() {
//执行父组件自定义的事件
this.$emit("myEvent", this.flag, this.n);
},
},
第三步:父组件使用传递过来的数据
data() {
return {
n: 0,
flag: "",
};
},
methods: {
getData(...parans) {
console.log(parans);
//传递过来的是数组
this.n = parans[0];
this.flag = parans[1];
},
},
3.组件之间相互传参 原生事件法 (发布订阅)
b--->c发送数据
第一步:全局的main.js中创建一个全局的EventBus,挂载 vue的原型上 this.$bus
$bus.$on()
绑定的事件函数,在哪个vue实例上都可以基于$bus.$empty()
执行,还可以传值//创建一个全局的 Eventbus
let Eventbus=new Vue();
//挂载 vue的原型上 后期基于this.$bus
Vue.prototype.$bus=Eventbus;
第二步:comc向事件池中绑定事件:this.$bus.$on("事件名",函数)
created() {
//向事件池中添加方法
this.$bus.$on("myEvent", () => {});
},
第三步:comb从事件池中获取事件函数并执行:this.$bus.$emit("事件名",想传的参数)
data() {
return {
msg: "我是comb",
};
methods: {
send() {
//执行事件池中的方法,并且传参
this.$bus.$emit("myEvent", this.msg);
},
第四步 comc使用传递过来的数据
组件 comc----{{ msg }}
data() {
return {
msg: "",
};
//创建之后的钩子函数向事件池中添加方法
created() {
//向事件池中添加方法
this.$bus.$on("myEvent", (value) => {
console.log(value);
this.msg = value;
});
},
4.祖先和后代相互传参
data() {
return {
title: "我是about祖先",
};
},
provide() {
return {
title: this.title,
};
},
第二步:后代使用inject属性接受祖先中的参数,inject是data中的数据,是数组类型
inject: ["title"],因为inject是数组类型,所以它符合如果数据项不是对象类型,则不做劫持,如果数据项是对象,则这个对象中的属性会做劫持。
data() {
return {
title: "我是about祖先",
};
},
//祖先 传递的title是非响应式
provide() {
return {
title: this.title,
};
},
//------------------------------
data() {
return {
//obj非响应式
obj: {
//title是响应式
title: "我是about祖先",
},
};
},
//祖先 传递的参数失去响应式,但里面的值会是响应式
provide() {
return {
obj: this.obj,
};
},
vue的实例中存在一些属性能够获取不同关系的元素,获取之后就可以基于这个元素获取其中的数据或方法了:
created() {
console.log(this.$parent.title);
},
this.$children[n]
:获取第n个子元素的vm实例 mounted() {
console.log(this.$children[0].msg);
},
this.$root
:获取根元素的vm实例(main.js中new 的Vue实例)et mv = new Vue({
router,
data() {
return {
rootmsg: "我是草根"
}
},
render: h => h(App)
}).$mount('#app')
---------------------
mounted() {
console.log(this.$root.rootmsg);
},
this.$refs
:this的子元素中需要定义ref
属性:比如ref="xxx"
:this.$refs.xxx
获取的是DOM对象this.$refs.xxx
获取的是子组件的vm实例 //获取的是dom元素
11111
mounted() {
console.log(this.$refs.one);
},
-----------------------------------
获取的是组件
mounted() {
console.log(this.$refs.b);
},
//如果不是组件获取的就是dom元素,如果是组件,获取的就是组件的实例
重点:父组件更新默认不会触发子组件更新,但是**==如果子组件中绑定调用了父组件的数据aaa,父组件的aaa数据更新触发重新渲染时,使用aaa数据{{$parent.aaa}}的子组件也会触发更新==**
一、父子组件生命周期执行过程:
beforeCreated
created
beforeMount
beforeCreate
created
beforeMount
mounted
mounted
二、子组件更新过程:
berforeUpdate
berforeUpdate
updated
updated
三、父组件更新过程:
berforeUpdate
updated
四、父组件销毁过程:
beforeDestory
beforeDestory
destoryed
destoryed
@xxx.native
: 监听组件根元素的原生事件。
例子:
原理:在父组件中给子组件绑定一个==原生(click/mouseover...)==的事件,就将子组件变成了普通的HTML标签,不加'. native'父组件绑定给子组件标签的事件是无法触发的
虚拟DOM
虚拟DOM对象:_vnode
,作用:
第一步:vue内部自己定义的一套对象,基于自己规定的键值对,来描述视图中每一个节点的特征:
- tag标签名
- text文本节点,存储文本内容
- children:子节点
- data:属性
vue-template-compiler
去渲染解析 template 视图,最后构建出上述的虚拟DOM对象组件库
element-ui
:饿了么antdv
:蚂蚁金服iview
:京东
- Element - The world's most popular Vue UI framework
- ==vue2.xx==:elemnetui
- ==vue3.xx==:element plus
如何在项目中使用功能性组件?
==第一步==:安装element-ui
:$npm i element-ui -s
==第二步==:导入:
完整导入:整个组件库都导入进来,想用什么直接用Vue.use(xxx)即可
缺点:如果我们只用几个组件,则无用的导入组件会造成项目打包体积变大[不好],所以项目中推荐使用按需导入
按需导入:
1、需要安装依赖$ npm install babel-plugin-component
样式私有化
在Vue中我们基于scoped设置样式私有化之后:
会给组件创建一个唯一的ID(例如:data-v-5f109989
)
在组件视图中,我们编写所有元素(包含元素调用的UI组件),都设置了这个ID属性;但是我们调用的组件内部的元素,并没有设置这个属性!!
而我们编写的样式,最后会自动加上属性选择器:
.task-box {
box-sizing: border-box;
...
}
---------编译后成为:---------
.task-box[data-v-5f1969a9]{
box-sizing: border-box;
}
/deep/
: /deep/.el-textarea__inner,
/deep/.el-input__inner{
border-radius: 0;
}
在真实项目中,我们会把数据请求和axios的二次封装,都会放到src/api路径下进行管理
//main.js
import api from '@/api/index';
// 把存储接口请求的api对象挂载搭配Vue的原型上:
后续在各个组件基于this.$api.xxx()就可以发送请求了,无需在每个组件中再单独导入这个api对象。
Vue.prototype.$api=api;