组件化开发

  • 一个页面(.vue文件)可能有一个或多个组件(.vue)组成完整的页面功能
  • 封装的思想:把页面上可重用的部分封装为组件,从而方便项目的 开发 和 维护

    vue组件-封装创建

  • 哪部分标签复用, 就把哪部分封装到组件内

  • 组件内template只能有一个根标签
  • 组件内data必须是一个函数, 独立作用域
  • 封装路径:components/Pannel.vue ```vue
  1. <a name="gIjef"></a>
  2. ### vue组件-复用
  3. 全局注册语法:<br />全局入口在main.js, 在new Vue之上注册<br />`import 组件对象 from 'vue文件路径'`<br />`Vue.component("组件名", 组件对象)`
  4. - 组件名和组件对象使用大驼峰命名
  5. ```javascript
  6. import PannelTest from './components/pannel.vue'
  7. Vue.component('Pannel', PannelTest)

在页面中使用:

  • 单闭合:<组件名 />
  • 双闭合:<组件名> <组件名 /> ```vue

  1. 局部导入语法:<br />`import 组件对象 from '文件路径'`<br />`components: { 组件名: 组件对象 }`(跟data同级)
  2. - components对象中,键值对一样可以只写一个
  3. - 组件名和组件对象使用大驼峰命名
  4. 在页面中使用:
  5. - 单闭合:`<组件名 />`
  6. - 双闭合:`<组件名> <组件名 />`
  7. ```vue
  8. <template>
  9. <div>
  10. <Pannel />
  11. </div>
  12. </template>
  13. <script>
  14. import Pannel from '../components/pannel.vue'
  15. export default {
  16. components: {
  17. Pannel: Pannel
  18. }
  19. }
  20. </script>
  21. <style lang="less" scoped>
  22. </style>

vue组件-scoped作用

  • 问题:组件化开发中,父子嵌套情况,样式会出现类名冲突时
  • 说明:父组件的样式会覆盖子组件的样式
  • 解决:在每个style标签中加入 scoped属性
  • 通过唯一的哈希值判断,这样互不冲突

    vue组件-/deep/深度作用选择符

  • 问题:父元素控制子元素的样式时,开启了样式模块化scoped就控制不到

  • 解决:在要控制的子组件类名或元素名前面加一个 /deep/(深度作用选择符)
  • 默认子组件的根元素,会带上父组件的data-v-hash属性,所以可以直接控制

vue组件通信(重点)

vue单向数据流(限制)

  • 保护数据的可维护性(只在一个位置可以修改数据)
  • 父组件传递的数据props,不能改,props只读
  • 只有父组件可以改
  • 简单类型的 不能直接改
  • 引用类型的 不能改地址,但是可以修改里面的值(部分改)
  • 总结:在哪儿定义就在那儿改

父传子-props

  • 在父组件中的子组件标签 :传递的数据名="data变量名"
  • 不加:传递的是固定值,加:必须传递变量
  • 子组件中接收:props: ['父组件传递的数据名']
  • 实际开发使用

props: {
父组件转递的数据名: {
type:设置(校验)接受数据的类型
default: ( ) => { return }
}
}
default是默认属性如果父组件没有传递数据,使用这个默认值
如果是复杂类型(对象,数组)的,默认值需要通过函数返回默认值

  1. export default {
  2. props: {
  3. tabList: {
  4. type: Array,
  5. default: () => [],
  6. require: true // 表示必须传递,不传递会报错
  7. },
  8. },
  9. }

子传父

在父组件中的子组件标签 @自定义事件="methods函数"
修改在父组件methods函数的内部修改
子组件的methods函数内部 this.$emit('自定义事件',参数1,参数2)
传递的参数会传递给自定义事件绑定的函数


组件生命周期

生命周期:一个组件从创建到销毁的过程(四个阶段)

阶段

  • 初始化 => 创建组件
  • 挂载 => 渲染显示组件
  • 更新 => 修改数据
  • 销毁 => 切换页面

    钩子函数

  • 初始化:(只执行一次,.vue文件实例化)

    • 创建前:beforeCreate ( ) { 实例化前 }
    • 创建后:created ( ) { 发送ajax请求 }
  • 挂载:(只执行一次,页面渲染)
    • 挂载前:beforeMounte () { 页面渲染前,不能操作dom }
    • 挂在后:mounted () { 可以操作dom和发送ajsx请求 }
  • 更新:(可以执行多次,data变量更新)
    • 更新前:beforeUpdate () { 数据更新,页面没有更新 }
    • 更新后:updated () { 数据更新,页面更新 }
  • 销毁:(只执行一次,切换页面或者组件从页面中被移除)
    • 销毁前:beforeDestroy () { 清除定时器,解绑js事件 }
    • 销毁后:destroyed () { 清除定时器,解绑js事件 }

ref和$refs的使用

获取DOM对象

  • 在获取DOM的元素身上写ref属性并赋值
  • 通过this.$refs.ref属性值获取DOM对象
  • 通过this.$refs.ref属性值.style.css属性操作dom

获取组件实例

  • 在获取组件的自定义标签身上写ref属性并赋值
  • 通过this.$refs.ref的属性值获取组件实例
  • this.$refs.ref的属性值 === 获取当前的组件实例this

    $nextTick使用

    语法:this.$nextTick( () => {})

  • vue修改data变量后,不能立马获取最新的dom (获取上一次的dom)

  • vue做了优化,dom的更新是异步的
  • 获取异步更新之后的dom使用this.$nextTick(callback回调函数)
  • callback函数内部就可以获取到最新的dom
  • 可以使用async``await this.$nextTick()的写法

动态组件

语法:<component :is="data变量"><component>

  • data变量值为动态切换组件的标签名
  • 可以动态切换多个子组件 ```vue

  1. ```vue
  2. <template>
  3. <div>
  4. <div class="box">
  5. <p>登录页面</p>
  6. </div>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. }
  12. </script>
  13. <style scoped>
  14. .box {
  15. border: 2px solid pink;
  16. background-color: skyblue;
  17. }
  18. </style>
  1. <template>
  2. <div class="box">
  3. <p>注册页面</p>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. }
  9. </script>
  10. <style scoped>
  11. .box {
  12. border: 2px solid skyblue;
  13. background-color: pink;
  14. }
  15. </style>

组件缓存

现象:动态切换的组件,切换时会被销毁每次都会被重新创建(浪费性能)
语法:<keep-alive><keep-alive>

  • 包裹后组件只会在第一次被创建一次,之后会被缓存
  • 缓存的生命周期
    • 激活:activated() { }
    • 失去激活状态:deactivated() { }
  • include属性
    • include="被缓存组件的name值"
    • nama值是唯一的(不能使用html元素名,会报错) ```vue

  1. ```vue
  2. <template>
  3. <div>
  4. <div class="box">
  5. <p>登录页面</p>
  6. </div>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. name: 'lg',
  12. deactivated () {
  13. console.log('离开了缓存')
  14. },
  15. activated () {
  16. console.log('进入了缓存')
  17. },
  18. created () {
  19. console.log('创建了组件')
  20. },
  21. destroyed () {
  22. console.log('被销毁了')
  23. },
  24. }
  25. </script>
  26. <style scoped>
  27. .box {
  28. border: 2px solid pink;
  29. background-color: skyblue;
  30. }
  31. </style>
  1. <template>
  2. <div class="box">
  3. <p>注册页面</p>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: 'rg'
  9. }
  10. </script>
  11. <style scoped>
  12. .box {
  13. border: 2px solid skyblue;
  14. background-color: pink;
  15. }
  16. </style>

组件插槽

通过组件标签传递内容给子组件,子组件通过slot元素接收展示渲染

默认插槽

语法:
传递<组件标签>插槽内容</组件标签>子组件接收<solt></solt>

  • solt写一个,多次写回显示重复的内容
  • solt不设置内容也可以单闭合<solt />
  • 如果没有传递插槽,solt内可以设置默认值 ```vue

  1. ```vue
  2. <template>
  3. <div>
  4. <h2>我是子组件</h2>
  5. <slot> <h4>插槽没传递,显示我</h4> </slot>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. }
  11. </script>
  12. <style scoped>
  13. </style>

具名插槽

语法:
传递<组件标签><template v-slot:名字>插槽内容</template></组件标签>
子组件接收<solt name="名字"></solt>

  • v-solt:名字可以简写#名字
  • template标签外面都属于默认插槽
  • v-slot和name的名字需要一样
  • 可以传递多个,name不能重复 ```vue

  1. ```vue
  2. <template>
  3. <div>
  4. <slot name="title"></slot>
  5. <slot></slot>
  6. <slot name="foot"> </slot>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. }
  12. </script>
  13. <style scoped>
  14. </style>

作用域插槽

语法:
传递:子组件在slot元素上,通过动态绑定:属性名="data变量"
接收:通过<template #名字="变量">接收传递的数据

  • 传递多个变量,写多个:属性名="data变量"
  • 接收到的数据是一个对象类型,可以解构 ```vue

  1. ```vue
  2. <template>
  3. <div>
  4. <slot name="one" :obj="obj" :num="num"> </slot>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. data () {
  10. return {
  11. obj: { name: '张三', age: 20 },
  12. num: 123
  13. }
  14. },
  15. }
  16. </script>
  17. <style scoped>
  18. </style>