一、组件化开发思想
1. 什么是组件化开发
组件化开发指的是:根据封装的思想,把页面上可重用的部分封装为组件,从而方便项目的开发和维护。例如:ibootstrap - Bootstrap 可视化布局系统 所展示的效果,就契合了组件化开发的思想。用户可以通过拖拽组件的方式,快速生成一个页面的布局结构。
2. 组件化开发的好处
前端组件化开发的好处主要体现在以下两方面:
- 提高了前端代码的复用性和灵活性
- 提升了开发效率和后期的可维护性
3. vue 中的组件化开发
- vue 是一个完全支持组件化开发的框架。
- vue 中规定组件的后缀名是
.vue
。之前接触到的App.vue
文件本质上就是一个 vue 的组件。
二、vue 组件的构成
1. vue 组件组成结构
每个 .vue 组件都由 3 部分构成,分别是:
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。
2. 组件的 template 节点
vue 规定:每个组件对应的模板结构,需要定义到 <template> 节点中。
<template>
<!-- 当前组件的Dom结构需要定义到 template 标签的内部 -->
</template>
注意:
- template是vue提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的DOM元素
- template中只能包含唯一的根节点
3.script
vue 规定:开发者可以在 <script> 节点中封装组件的 JavaScript 业务逻辑 <script > 节点的基本结构如下
<script>
//今后,组件相关的data数据、methods方法等,都需要定义到meport default 所导出的对象中
</script>
.vue 组件中的 data 必须是函数 vue 规定:.vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象。 因此在组件中定义 data 数据节点时,下面的方式是错误的
data:{ //组件中,不能直接让data指向一个数据对象(会报错)
count:0
}
会导致多个组件实例共用同一份数据的问题,请参考官方给出的示例: https://v2.cn.vuejs.org/v2/guide/components.html#data-%E5%BF%85%E9%A1%BB%E6%98%AF%E4%B8%80%E4%B8%AA%E5%87%BD%E6%95%B0
data: function () {
return {
count: 0
}
}
4.style
vue 规定:组件内的 <style> 节点是可选的,开发者可以在 <style> 节点中编写样式美化当前组件的 UI 结构 <script > 节点的基本结构如下:
<style>
h1{
font-weight:normal;
}
</style>
让 style 中支持 less 语法
- 在 <style> 标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式
<sytle lang="less">
h1{
font-weight:normal;
span{
color:red;
}
}
</style>
三、Vue组件—示例
1、template
<template>
<div>
<div class="test-box">
<h3>这是用户自定义的 Test.vue --- {{ username }}</h3>
<button @click="chagneName">修改用户名</button>
</div>
<div>123</div>
</div>
</template>
2、script
<script>
// 默认导出。这是固定写法!
export default {
// data 数据源
// 注意:.vue 组件中的 data 不能像之前一样,不能指向对象。
// 注意:组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 { } 中,可以定义数据
return {
username: 'admin'
}
},
methods: {
chagneName() {
// 在组件中, this 就表示当前组件的实例对象
console.log(this)
this.username = '哇哈哈'
}
},
// 当前组件中的侦听器
watch: {},
// 当前组件中的计算属性
computed: {},
// 当前组件中的过滤器
filters: {}
}
</script>
3、style
<style lang="less">
.test-box {
background-color: pink;
h3 {
color: red;
}
}
</style>
四、Vue组件注册
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。
1、局部注册
说明:
- 组件在被封装好之后,彼此之间是相互独立的,不存在父子关系
- 在使用组件的时候,根据彼此的嵌套关系,形成了父子关系、兄弟关系
- 1、通过 components 注册的是私有子组件
import Left from '@/components/Left.vue'
- 2、使用 components 节点注册组件
export default {
components: {
Left,
}
}
- 3、以标签形式使用刚才注册的组件
<div class="box">
<Left></Left>
</div>
1.1 局部注册使用组件示例
- 在src/components目录下创建如下组件文件
left.vue
right.vue
在App.vue中使用Left.vue和Right.vue
- 1-srcipt中导入
<script>
// 1. 导入需要使用的 .vue 组件
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
import Test from '@/components/Test.vue'
export default {
// 2. 注册组件
components: {
Left,
Right,
}
}
</script>
- 2-布局文件中使用
<template>
<div class="app-container">
App 根组件
<div class="box">
<Left></Left>
<Right></Right>
</div>
</div>
</template>
2、注册全局组件
2.1 注册全局组件
在 vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件
import Count from './components/Count.vue'
//import Count from '@/components/Count.Vue'
Vue.component('MyCount',Count)
说明:
- Count:为导入需要全局注册的组件
- vue.component有两个参数:
- 第一个参数:字符串格式,表示组件的“注册名称”
- 第二个参数:需要被全局注册的那个组件
2.2 使用全局注册组件
- Left.vue中使用
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr />
<MyCount></MyCount>
</div>
</template>
- Right.vue中使用
<template>
<div class="right-container">
<h3>Right 组件</h3>
<hr />
<MyCount></MyCount>
</div>
</template>
五、组件自定义属性
5.1 组件的props
props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性! 它的语法格式如下
export default{
//组件的自定义属性
props:['自定义属性A','自定义属性B','其他自定义属性...'],
//组件的私有数据
data(){
return {}
}
}
5.2 自定义属性示例
Count.vue
<template>
<div>
<h5>Count 组件</h5>
<p>count 的值是:{{ count }}</p>
<button @click="count += 1">+1</button>
</div>
</template>
- 自定义属性
<script>
export default {
props: ['init'],
data() {
return {
count: this.init
}
},
}
</script>
Left.vue
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr />
<MyCount :init="9"></MyCount>
</div>
</template>
Right.vue
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr />
<MyCount :init="9"></MyCount>
</div>
</template>
为自定义属性添加v-bind
- 未添加v-bind(字符串):
<MyCount init=“6”></MyCount>
- 添加v-bind(数字):
<MyCount :init=“6”></MyCount>
5.3 自定义属性props是只读的
- 使用自定义属性计算时出错 Count.vue代码文件
<template>
<div>
<h5>Count 组件</h5>
<p>count 的值是:{{ init }}</p>
<button @click="init += 1">+1</button>
</div>
</template>
vue 规定:组件中封装的自定义属性是只读的,程序员不能直接修改 props 的值。否则会直接报错
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "init"
found in
- 如何修改自定义属性的值 要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据都是可读可写的!
Count.vue逻辑文件
<script>
export default {
props: ['init'],
data() {
return {
count: this.init
}
},
}
</script>
Count.vue布局文件
<template>
<div>
<h5>Count 组件</h5>
<p>count 的值是:{{ count }}</p>
<button @click="count += 1">+1</button>
</div>
</template>
5.4 自定义属性default、type、required
- 将数组类型的props修改为对象类型
修改前
props: ['init'],
修改后
props: {
// 自定义属性A : { /* 配置选项 */ },
// 自定义属性B : { /* 配置选项 */ },
// 自定义属性C : { /* 配置选项 */ },
init: {
// 如果外界使用 Count 组件的时候,没有传递 init 属性,则默认值生效
default: 0,
// init 的值类型必须是 Number 数字
type: Number,
// 必填项校验
required: true
}
},
说明
- 在声明自定义属性时,可以通过 default 来定义属性的默认值
- 在声明自定义属性时,可以通过 type 来定义属性的值类型
- 在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值
六、Vue组件-样式冲突
1、scoped的使用
组件之间的样式冲突问题(修改Left.vue中的h3属性,Right也被修改)
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
- 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的
- 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素
如何解决组件样式冲突的问题
为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域。
- Left.vue
- 布局代码
<template>
<div class="left-container" data-v-001>
<h3 data-v-001>Left 组件</h3>
<hr data-v-001/>
<MyCount :init="9" data-v-001></MyCount>
</div>
</template>
-
- 样式文件修改
h3[data-v-001] {
color: red;
}
- Right.vue
<template>
<div class="right-container" data-v-002>
<h3 data-v-002>Right 组件</h3>
<hr data-v-002 />
<MyCount :init="6" data-v-002></MyCount>
</div>
</template>
style 节点的 scoped 属性
为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题:
- 布局代码
<template>
<div class="left-container">
<h3 >Left 组件</h3>
<hr/>
<MyCount :init="9"></MyCount>
</div>
</template>
- 样式文件
<style lang="less" scoped >
h3 {
color: red;
}
</style>
2、使用deep修改子组件中的样式
冲突现象
在Left.vue中设置如下代码(给h5设置背景颜色),没有生效,现象同2
h5 {
color: pink;
}
/deep/ 样式穿透
如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样 式对子组件生效,可以使用 /deep/ 深度选择器
样式文件添加/deep/
/deep/ h5 {
color: pink;
}
参考: https://blog.csdn.net/haochangdi123/article/details/80308940
https://blog.csdn.net/web00_11/article/details/120180718
https://blog.csdn.net/Calvin_zhou/article/details/128204527
https://blog.csdn.net/Calvin_zhou/article/details/128230302