一、组件化开发思想

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

https://blog.csdn.net/Calvin_zhou/article/details/128246842

https://blog.csdn.net/Calvin_zhou/article/details/128261539