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文件本质无区别(更易维护)

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

相关推荐
weixin_382395235 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__5 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.6 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~7 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华8 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子10 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅10 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni10 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学12 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端