vue(5)

一.自定义指令

每个指令有着自己各自独立的功能,可以封装一些dom操作,扩展额外功能·v-focus、v-loading、v-lazy
①全局注册语法:

css 复制代码
 Vue.directive('指令名',{
    // 指令名:指令的配置项
      "inserted "(el) {
        el.focus()
      }
  })

②局部注册语法:

css 复制代码
```css
 directive:{
 "指令名":{
    // 指令名:指令的配置项
      inserted () {
        el.focus()
      }
  	}
  }

使用:< input v-指令名 type="text" >

(1)指令的值

实现一个color指令,传入不同的颜色,给标签设置文字颜色

语法:再绑定指令时,可以通过等号的形式为指令绑定具体的参数值

通过binding.value可以拿到指令值,指令值修改会触发update函数

css 复制代码
directives: {
    color: {
      // 1. inserted 提供的是元素被添加到页面中时的逻辑
      inserted (el, binding) {
        // console.log(el, binding.value);
        // binding.value 就是指令的值
        el.style.color = binding.value
      },
      // 2. update 指令的值修改的时候触发,提供值变化后,dom更新的逻辑
      update (el, binding) {
        console.log('指令的值修改了');
        el.style.color = binding.value
      }
    }
  }

(2)v-loading指令封装

场景:实际开发中,发送请求需要时间,在请求的数据未回来时,页面处于空白状态 => 用户体验不好

需求:封装一个v-loading指令,实现加载中的效果

分析:1.本质loading效果就是一个蒙层,盖在盒子上

2.数据请求中,开启loading状态,添加蒙层

3.数据请求完毕,关闭loading状态,移除蒙层

实现:1.准备一个loading类,通过伪元素定位,设置宽高,实现蒙层

2.开启关闭loading状态(添加移除蒙层),本质只需要添加移除类即可

3.结合自定义指令的语法进行封装复用

css 复制代码
.loading:before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #fff url('./loading.gif') no-repeat center;
}

(3)默认插槽

作用:让组件内部的一些结构支持自定义

需求:要在一个页面中显示一个对话框,封装成一个组件

问题:组件的内容部分,不希望写死,希望能够自定义

①基本语法

1.组件内需要定制的结构部分,改用< slot >占位

2.使用组件时,< MyDialog>标签内部,传入结构替换slot

css 复制代码
<template>
  <div class="dialog">
    <div class="dialog-header">
      <h3>友情提示</h3>
      <span class="close">✖️</span>
    </div>

    <div class="dialog-content">
      <!-- 1. 在需要定制的位置,使用slot占位 -->
      <slot></slot>
    </div>
    <div class="dialog-footer">
      <button>取消</button>
      <button>确认</button>
    </div>
  </div>
</template>

②后备内容

通过插槽完成了内容的定制,传什么显示什么,但是如果不传,则是空白

封装组件时,可以为预留的< slot>插槽提供后备内容

**语法:**在< slot>标签内,放置内容,作为默认显示内容

**效果:**外部使用组件时,不传东西,则slot会显示后备内容;传东西了,则slot整体会被换掉

css 复制代码
<template>
  <div class="dialog">
    <div class="dialog-header">
      <h3>友情提示</h3>
      <span class="close">✖️</span>
    </div>

    <div class="dialog-content">
      <!-- 往slot标签内部,编写内容,可以作为后备内容(默认值) -->
      <slot>
        我是默认的文本内容
      </slot>
    </div>
    <div class="dialog-footer">
      <button>取消</button>
      <button>确认</button>
    </div>
  </div>
</template>

(4)具名插槽

语法:1.多个slot使用name属性区分内容

css 复制代码
<template>
  <div class="dialog">
    <div class="dialog-header">
      <!-- 一旦插槽起了名字,就是具名插槽,只支持定向分发 -->
      <slot name="head"></slot>
    </div>

    <div class="dialog-content">
      <slot name="content"></slot>
    </div>
    <div class="dialog-footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

2.template配合v-slot名字来分发对应标签

css 复制代码
<MyDialog>
      <!-- 需要通过template标签包裹需要分发的结构,包成一个整体 -->
      <template v-slot:head>
        <div>我是大标题</div>
      </template>
      
      <template v-slot:content>
        <div>我是内容</div>
      </template>

      <template #footer>
        <button>取消</button>
        <button>确认</button>
      </template>
    </MyDialog>

(5)作用域插槽

定义slot插槽的同时,是可以传值的,给插槽上可以绑定数据,将来使用组件时可以用
场景 :封装表格组件

1.父传子,动态渲染表格

2.利用默认插槽,定制操作列

3.删除或查看都需要用到当前项的id,属于组件内部的数据通过作用域插槽传值绑定,进而使用

基本使用步骤:

1.给slot标签,已添加属性的方式传值

2.所有添加的属性,都会被收集到一个对象中

3.在template,通过 #插槽名="obj" 接收,默认插槽为default

css 复制代码
<template>
  <div>
    <MyTable :data="list">
      <!-- 3. 通过template #插槽名="变量名" 接收 -->
      <template #default="obj">
        <button @click="del(obj.row.id)">
          删除
        </button>
      </template>
    </MyTable>
    
    <MyTable :data="list2">
      <template #default="{ row }">
        <button @click="show(row)">查看</button>
      </template>
    </MyTable>
  </div>
</template>

(6)综合案例 - 商品列表

css 复制代码
<template>
  <div class="my-tag">
    <input
      v-if="isEdit"
      v-focus
      ref="inp"
      class="input"
      type="text"
      placeholder="输入标签"
      :value="value"
      @blur="isEdit = false"
      @keyup.enter="handleEnter"
    />
    <div 
      v-else
      @dblclick="handleClick"
      class="text">
      {{ value }}
    </div>
  </div>
</template>

<script>
export default {
  props: {
    value: String
  },
  data () {
    return {
      isEdit: false
    }
  },
  methods: {
    handleClick () {
      // 双击后,切换到显示状态 (Vue是异步dom更新)
      this.isEdit = true
      
      // // 等dom更新完了,再获取焦点
      // this.$nextTick(() => {
      //   // 立刻获取焦点
      //   this.$refs.inp.focus()
      // })
    },
    handleEnter (e) {
      // 非空处理
      if (e.target.value.trim() === '') return alert('标签内容不能为空')

      // 子传父,将回车时,[输入框的内容] 提交给父组件更新
      // 由于父组件是v-model,触发事件,需要触发 input 事件
      this.$emit('input', e.target.value)
      // 提交完成,关闭输入状态
      this.isEdit = false
    }
  }
}
</script>

<style lang="less" scoped>
.my-tag {
  cursor: pointer;
  .input {
    appearance: none;
    outline: none;
    border: 1px solid #ccc;
    width: 100px;
    height: 40px;
    box-sizing: border-box;
    padding: 10px;
    color: #666;
    &::placeholder {
      color: #666;
    }
  }
}
</style>
css 复制代码
<template>
  <div class="table-case">
    <MyTable :data="goods">
      <template #head>
        <th>编号</th>
        <th>名称</th>
        <th>图片</th>
        <th width="100px">标签</th>
      </template>

      <template #body="{ item, index }">
        <td>{{ index + 1 }}</td>
        <td>{{ item.name }}</td>
        <td>
          <img
            :src="item.picture"
          />
        </td>
        <td>
          <MyTag v-model="item.tag"></MyTag>
        </td>
      </template>
    </MyTable>
  </div>
</template>

<script>
// my-tag 标签组件的封装
// 1. 创建组件 - 初始化
// 2. 实现功能
//    (1) 双击显示,并且自动聚焦
//        v-if v-else @dbclick 操作 isEdit
//        自动聚焦:
//        1. $nextTick => $refs 获取到dom,进行focus获取焦点
//        2. 封装v-focus指令

//    (2) 失去焦点,隐藏输入框
//        @blur 操作 isEdit 即可

//    (3) 回显标签信息
//        回显的标签信息是父组件传递过来的
//        v-model实现功能 (简化代码)  v-model => :value 和 @input
//        组件内部通过props接收, :value设置给输入框

//    (4) 内容修改了,回车 => 修改标签信息
//        @keyup.enter, 触发事件 $emit('input', e.target.value)

// ---------------------------------------------------------------------

// my-table 表格组件的封装
// 1. 数据不能写死,动态传递表格渲染的数据  props
// 2. 结构不能写死 - 多处结构自定义 【具名插槽】
//    (1) 表头支持自定义
//    (2) 主体支持自定义

import MyTag from './components/MyTag.vue'
import MyTable from './components/MyTable.vue'
export default {
  name: 'TableCase',
  components: {
    MyTag,
    MyTable
  },
  data () {
    return {
      // 测试组件功能的临时数据
      tempText: '水杯',
      tempText2: '钢笔',
      goods: [
        { id: 101, picture: 'https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg', name: '梨皮朱泥三绝清代小品壶经典款紫砂壶', tag: '茶具' },
        { id: 102, picture: 'https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg', name: '全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌', tag: '男鞋' },
        { id: 103, picture: 'https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png', name: '毛茸茸小熊出没,儿童羊羔绒背心73-90cm', tag: '儿童服饰' },
        { id: 104, picture: 'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg', name: '基础百搭,儿童套头针织毛衣1-9岁', tag: '儿童服饰' },
      ]
    }
  }
}
</script>

<style lang="less" scoped>
.table-case {
  width: 1000px;
  margin: 50px auto;
  img {
    width: 100px;
    height: 100px;
    object-fit: contain;
    vertical-align: middle;
  }
}

</style>

二.单页应用程序:SPA - Single Page Application

所有功能在一个html页面上实现

三.VueRouter

修改地址栏路径时,切换显示匹配的组件

四.组件存放目录问题 . vue文件本质无区别(更易维护)

组件分类:页面组件和复用组件

相关推荐
天堂的恶魔9461 小时前
C++设计模式 —— 工厂模式
javascript·c++·设计模式
轻口味1 小时前
Vue.js 如何自定义主题和样式
前端·javascript·vue.js
独莫子凡2 小时前
Vue如何处理浏览器跨域问题
前端·vue.js
bin91532 小时前
DeepSeek赋能Vue:打造超丝滑进度条开发指南
vue.js·deepseek
黑白两客6 小时前
自定义vue摄像头 自定义时长,自定义大小
前端·javascript·vue.js
customer086 小时前
【开源免费】基于SpringBoot+Vue.JS乐享田园系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
cdcdhj8 小时前
bind绑定类,使this在上下文章中指向类,然后利用debug绑定类中的方法输出缓存中的日志
前端·缓存·node.js
haomo20148 小时前
AI赋能前端开发:构建你的高效学习与职业发展之路
前端·人工智能·学习
阿芯爱编程8 小时前
react高级面试题
前端·javascript·react.js
zhanggongzichu8 小时前
Vue3中watch和watchEffect的使用场景和区别
前端·javascript·vue.js·watch·watcheffect