【vue入门手册】五、vue组件进阶

简介: 【vue入门手册】、vue组件进阶

产出物

  • Dialog对话框

技术项

  • 组件插槽&具名插槽
  • 组件通信 props  、emit、eventBus

注意

  • 组件命名以两张方式二选一 :
  • 1、首字母大写 ( 大驼峰 ) , 比如 Dialog
  • 2、小写 用 短横线  -  连接  - my-dialog

/components/dialog

<template>
      <!--self:事件修饰符,只有点击自己才触发,点击子元素不触发  -->
      <div class="cat-dialog__wrapper" v-show="visible" @click.self="handleClose">
        <!-- 对话框 -->
        <div class="cat-dialog" :style="{ width, marginTop: top }">
          <!-- 对话框顶部 标题 + 关闭按钮 -->
          <div class="cat-dialog__header">
            <slot name="title">
              <span class="cat-dialog__title">{{ title }}</span> <button class="cat-dialog__headerbtn" @click="handleClose">
              <i class="cat-icon-close">  x</i>
            </button>
            </slot>
          </div>
          <!-- 对话框内容 -->
          <div class="cat-dialog__body">
            <slot></slot>
          </div>
          <!-- 对话框底部 一般都是一些操作,没有传入footer插槽就不显示v-if -->
          <div class="cat-dialog__footer" v-if="$slots.footer">
            <slot name="footer"></slot>
          </div>
        </div>
      </div>
  </template>
  <script>
  export default {
    name: "gygDialog",
    props: {
      title: {
        type: String,
        default: "提示",
      },
      // 弹框宽度
      width: {
        type: String,
        default: "30%",
      },
      // 弹框距离顶部距离
      top: {
        type: String,
        default: "15vh",
      },
      visible: {
        type: Boolean,
        default: false,
      },
    },
    methods: {
      handleClose() {
        //visible是父组件传过来的props,子组件不能直接修改,需要子传父
        this.$emit("update:visible", false);
      },
    },
  };
  </script>
<style>
  .cat-dialog__wrapper{
    position: fixed;
    left: 0;
    top: 0;
    width: 100vw;
    height: 100vh;
    background: gray;
    z-index: 100;
    display: flex;
    justify-content: center;
    align-content: center;
  }
  .cat-dialog {
    background: white;
    min-height: 30vh;
    max-height: 40vh;
  }
  .cat-dialog__header{
    height: 20%; 
  }
  .cat-dialog__body{
    height: 60%; 
  }
  .cat-dialog__footer{
    height: 20%; 
  }
</style>

引入组件


Slot 插槽

Vue插槽(Slot)是一种非常有用的机制,可以让你更加灵活地组织和渲染组件。Vue中的插槽可分为三种类型:默认插槽、具名插槽和作用域插槽。默认插槽是没有指定名称的插槽;具名插槽为插槽指定名称,通常用于需要特定命名的内容的子组件中;作用域插槽允许子组件向父组件传递数据,并通常用于父组件需要以某种方式处理子组件数据的情况下。


使用场景举例:


默认插槽:当在组件中只需要将内容插入容器中,而不需要其他处理时,可以使用默认插槽。


具名插槽:当在组件中需要更复杂的布局时,可以使用具名插槽来分别处理不同的内容,比如一个博客组件可以分别使用“博客头部”、“博客正文”、“博客底部”三个具名插槽。


作用域插槽:当需要在父组件中操作子组件中的数据时,可以使用作用域插槽将子组件的数据传递给父组件处理,比如一个评论组件中需要实现评论的回复功能,可以使用作用域插槽将被回复评论的内容传递给父组件。


示例代码:


<!-- 默认插槽 -->
<template>
  <div>
    <slot></slot>
  </div>
</template>
<!-- 具名插槽 -->
<template>
  <div>
    <slot name="header"></slot>
    <slot name="content"></slot>
    <slot name="footer"></slot>
  </div>
</template>
<!-- 作用域插槽 -->
<template>
  <div>
    <slot name="comment" v-for="(comment, index) in comments" 
      :comment="comment" :index="index" :key="comment.id">
      {{ comment.body }}
    </slot>
  </div>
</template>


在这个示例中,我们分别使用了默认插槽、具名插槽和作用域插槽来渲染组件中的内容。通过具名插槽,我们可以分别控制组件中的头部、正文和底部;而使用作用域插槽,我们可以将评论组件中的数据传递给父组件进行操作。当然,在实际开发中,你可以根据需要使用不同类型的插槽来满足具体需求。


组件开发进阶


产出物

封装一个对话框组件

功能


  1. 自定义标题、内容、按钮
  2. 弹框打开、关闭


技术项


  1. 组件封装、引入
  2. 组件通信、props、eventBus(跨页面通信)
  3. slot (插槽)
  4. extend(创建复用组件)以API的形式调用组件


健壮性


  • 样式统一
  • props可扩展、校验字段类型


slot 插槽


如下弹框内容,需要考虑图片、文本、组件形式插入,  props 只能满足单一数据类型,怎么办呢?


使用 slot 插槽 ,一种特殊组件,作用组件的占位符。


父页面使用插槽,可以放入任意内容(文本、图片、组件、作用域数据...)到组件内部


<template>
    <div class="dialog-box">
        <!-- 弹框容器 -->
        <div id="dialog-body">
            <!-- 标题 -->
            <div class="dialog-title"> {{ title }}</div>
            <!-- 弹框内容    
            -->
            <slot></slot>
        </div>
    </div>
</template>


默认插槽

没有指定名称的 slot 插槽

父组件中没有指定插槽名称,内容会插入到组件内部中的 默认slot


比如:


Dialog 组件内部


<slot></slot>


父页面


<Dialog>
        <h1> 默认插槽进入 未指定 name 的slot 标签内 </h1>
    </Dialog>


具名插槽


当组件中需要复杂布局时,需要使用具名插槽,实现内容多样化


如:弹框的内容区域、底部按钮操作区域,使用具名插槽来分发内容


Dialog 组件中的内容和 页脚按钮,通过具名插槽插入内容


Dialog 组件内


<slot></slot> 
     <div class="dialog-btn">
                <!-- 底部按钮 具名插槽 -->  
                <slot name="footer" userName="李四">
                 </slot>
     </div>


父页面


<Dialog>
        <template> 内容分发到 默认插槽中</template>
        <template v-slot:footer>  内容分发到 footer 具名插槽</template>
     </Dialog>


作用域插槽


允许父组件获取子组件作用域

在组件内部 为 slot 标签绑定属性,父组件中使用 v-slot 绑定作用域、接收参数


子组件


<!-- 组件内部绑定 userName 参数 -->
   <slot name="footer" userName="李四"> </slot>


父页面


v-slot:footer 绑定具名插槽
v-slot:footer="scope" 获取组件内部作用域
v-slot:footer="{userName}" 可通过解构获取参数


<template v-slot:footer="scope" >  
                {{scope.userName}} 
                <!-- 李四 -->
      </template >


extend() 构建组件


组件构造器,创建一个 vue 子类,可挂载指定dom元素上

优点:快速构建组件,实现复用
确定:代码可读性差,复杂组件会造成问题

常用全局类组件,如:弹框、提示 等交互组件

如下,在main.js 中 注册一个全局的弹框组件

// 引入dialog组件
import dialog from "@/components/DialogGyg.vue"
// extend 组件构造器
const Dialog = Vue.extend(dialog)
// 封装一个全局api,打开弹框 
Vue.prototype.openAlert = (data)=>{
  // 创建一个 vue 组件实例
  let _alert = new Dialog()
  console.log('_alert',_alert)
  _alert.$data.show = true
  _alert.$slots.content =data.content
  _alert.$props.title = data.title  
   _alert.$mount() // 组件挂载完成
  //  将组件实例,插入到 页面中的body上
    document.body.appendChild(_alert.$el)
}

完整代码

app.vue 代码

<template>
  <div id="app">
    <el-table
    :data="tableData"
    border
    style="width: 100%">
    <el-table-column
      fixed
      prop="date"
      label="日期"
      width="150">
    </el-table-column>
    <el-table-column
      prop="name"
      label="姓名"
      width="120">
    </el-table-column>
    <el-table-column
      prop="province"
      label="省份"
      width="120">
    </el-table-column>
    <el-table-column
      prop="city"
      label="市区"
      width="120">
    </el-table-column>
    <el-table-column
      prop="address"
      label="地址"
      width="300">
    </el-table-column>
    <el-table-column
      prop="zip"
      label="邮编"
      width="120">
    </el-table-column>
    <el-table-column
      fixed="right"
      label="操作"
      width="100">
      <template slot-scope="scope">
        <el-button @click="tableRemove(scope.row)" type="text" size="small">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
    <button  @click="openalert"> 打开弹框</button>
     <DialogGyg ref="alert">
      <div slot="content" v-if="activeData">
         是否删除 {{ activeData.name }} 数据?
       </div>
      <!-- <template slot="footer" >  -->
        <template v-slot:footer="{age,slotName}" > 
        <van-button type="primary" size="small"  @click="closeBox('off')">取消{{ age }} </van-button>
          <van-button type="info" size="small" @click="closeBox('ok')">确定{{ slotName }}</van-button>
      </template >
    </DialogGyg>
  </div>
</template>
<script>
 import DialogGyg from './components/DialogGyg.vue';
export default {
  name: 'App',
  components: {
    DialogGyg
  },
  data(){
    return {
      // 当前编辑的表格数据
      activeData:null,
      tableData: [{
          date: '2016-05-02',
          name: '王小虎',
          id:0,
          province: '上海',
          city: '普陀区',
          address: '上海市普陀区金沙江路 1518 弄',
          zip: 200333
        }, {
          date: '2016-05-04',
          name: '王小虎1',
          province: '上海',
          city: '普陀区',
          id:1,
          address: '上海市普陀区金沙江路 1517 弄',
          zip: 200333
        }, {
          date: '2016-05-01',
          name: '王小虎2',
          province: '上海',
          city: '普陀区',
          id:2,
          address: '上海市普陀区金沙江路 1519 弄',
          zip: 200333
        }, {
          date: '2016-05-03',
          name: '王小虎3',
          province: '上海',
          city: '普陀区',
          id:3,
          address: '上海市普陀区金沙江路 1516 弄',
          zip: 200333
        }]
      // userName:"张三"
    }
  },
  methods:{
    openalert(){
      // 通过$refs 操作组件内部的方法
      // this.$refs.alert.openBox()
      this.openAlert({
        title:"警告",
        content:"是否确认删除"
      })
    },
    tableRemove(data){
      this.activeData = data;
      console.log('当前表格编辑数据')
        this.$refs.alert.openBox()
    },
    closeBox(type){
      this.$refs.alert.closeBox()
      if(type === 'ok'){
        // 删除 当前选中的数据
        // findIndex 找到当前选中数据,在数组中的索引位置 
          let Index = this.tableData.findIndex((item)=>item.id === this.activeData.id)
          console.log('Index',Index)
          this.tableData.splice(Index,1)
      }
    }
  }
}
</script>
<style>
#app {
}
</style>

Dialog 弹框组件

<template>
    <div class="dialog-box" v-if="show">
        <!-- 弹框容器 -->
        <div class="dialog-body">
            <!-- 标题 -->
            <div class="dialog-title"> {{ title }} <van-icon name="close" @click="closeBox" /> </div>
            <!-- 弹框内容
                需要考虑图片、文本、组件形式插入到弹框内容中,如何实现呢?
                props只能满足单一数据类型,怎么办呢?
                使用 slot 插槽 ,作用组件的占位符,父页面通过插槽往组件内部放入任意内容(文本、图片、组件...)
            -->
            <div class="dialog-content">
                <slot name="content"> </slot> 
            </div>
            <div class="dialog-btn">
                <!-- 底部按钮 具名插槽 -->     
                <slot name="footer" :slotName="name" age="18">
                 </slot>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    name: 'WebCodeDialogGyg',
    data() {
        return {
            name:"张三",
            show: false
        };
    },
    props: {
        title: {
            type: String,
            default: '弹框标题'
        },
    },
    mounted() {
    },
    methods: {
        openBox() {
            this.show = true
        },
        closeBox(){
            this.show = false
        }
    },
};
</script>
<style lang="scss" scoped>
.dialog-box {
    z-index: 1000;
    width: 100vw;
    height: 100vh;
    background: rgba($color: #000000, $alpha: 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    position: fixed;
    left: 0;
    top:0;
    .dialog-content{
        padding: 20px;
    }
    .dialog-body {
        width: 70vw;
        max-width: 300px;
        min-height: 100px;
        padding: 10px;
        background: white;
    }
    .dialog-title {
        display: flex;
        justify-content: space-between;
    }
    .dialog-btn{
        width: 70%;
        margin: 0 auto;
        display: flex;
        justify-content: space-between;
    }
}
</style>

main.js 全局配置

相关文章
|
5天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
5天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
5天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
5天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
4天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
6天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
4天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
|
6天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
JavaScript
Vue的非父子组件之间传值
全局事件总线 一种组件间通信的方式,适用于任意组件间通信
|
缓存 JavaScript 前端开发
Vue Props、Slot、v-once、非父子组件间的传值....
Vue Props、Slot、v-once、非父子组件间的传值....
84 0