【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 全局配置

相关文章
|
1天前
|
JavaScript 前端开发
vue(1),小白看完都会了
vue(1),小白看完都会了
|
1天前
|
JavaScript
Vue中避免滥用this去读取data中数据
Vue中避免滥用this去读取data中数据
|
1天前
|
JavaScript
vue中使用pinia及持久化
vue中使用pinia及持久化
4 0
|
1天前
|
JavaScript 前端开发 UED
Vue class和style绑定:动态美化你的组件
Vue class和style绑定:动态美化你的组件
|
1天前
|
JavaScript 前端开发 API
Vue 监听器:让你的应用实时响应变化
Vue 监听器:让你的应用实时响应变化
|
1天前
|
JavaScript
vue封装svg
vue封装svg
5 0
|
1天前
|
JavaScript
vue封装面包屑组件
vue封装面包屑组件
5 0
|
1天前
|
JavaScript
Vue 编写(preventReClick)防暴点 +防抖(debounce)和节流(throttle)函数
Vue 编写(preventReClick)防暴点 +防抖(debounce)和节流(throttle)函数
|
1天前
|
JavaScript 数据库
ant design vue日期组件怎么清空 取消默认当天日期
ant design vue日期组件怎么清空 取消默认当天日期
|
1天前
|
JavaScript C++
vue高亮显示组件--转载
vue高亮显示组件--转载
8 0