Vue学习笔记 (2023黑马版 第五天)

第五天

1.自定义指令

自定义指令:自己定义的指令,可以封装一些dom操作,扩展额外功能

  1. 全局注册
Vue.directive('指令名', {
  inserted (el) {
    el.focus()
  }
})
  1. 局部注册
directives: {
    指令名 : {
      inserted(el){
        el.focus()
      }
    }
  }

自定义指令 - 指令的值

语法:
①v-指令名 = “指令值” ,通过等号可以绑定指令的值
②通过binging.value可以拿到指令的值
③通过update钩子,可以监听指令值的变化,进行dom更新操作
实例代码:

<template>
  <div>
    <h1 v-color="color1">指令的值1测试</h1>
    <h1 v-color="color2">指令的值2测试</h1>
  </div>
</template>

<script>
export default {
  data () {
    return {
     color1:'red',
     color2:'green' 
    }
  },
  directives: {
    color: {
      inserted (el, binding) {
        el.style.color=binding.value
      },
      update (el,binding) {
        el.style.color=binding.value 
      }
    }
  }
}
</script>

<style>

</style>

自定义指令 - v-loading指令封装

场景: 实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态 => 用户体验不好
需求: 封装一个v-loading指令,实现加载中的效果
核心思路:

  1. 准备类名Loading,通过伪元素提供遮罩层
  2. 添加或移除类名,实现loading蒙层的添加移除
  3. 利用指令语法,封装v-loading通用指令
    inserted钩子中,binding.value判断指令的值,设置默认状态
    update钩子中,binding.value判断指令的值,更新类名状态

2.插槽

插槽 - 默认插槽

作用: 让组件内部的一些结构支持自定义
场景: 当组件内某一部分结构不确定,用slot占位封装
使用:

  1. 现在组件内用slot占位
  2. 使用组件时,传入具体标签内容插入

插槽 - 后备内容(默认值)

插槽后备内容: 封装组件时,可以为预留的"< slot >"插槽提供后备内容(默认内容)
给插槽设置默认显示内容:在slot标签内,写好后备内容,当使用组件并为给我们传入具体标签或内容时,插槽后备内容显示。

<slot>我是后备内容</slot>

插槽 - 具名插槽

需求: 一个组件内有多处结构,需要外部传入标签,进行定制
语法:

  1. 多个slot使用name属性区分名字
<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>
  1. template配合v-slot:名字 来分发对应标签,可简写为==#名字==
<MyDialog>
      <template  v-slot:head><div>我是标题</div></template>
      <template v-slot:content><div>我是内容</div></template>
      <template #footer>
        <button>确认</button>
        <button>取消</button>
      </template>
    </MyDialog>

插槽 - 作用域插槽

作用域插槽: 定义slot插槽的同时,是可以传值的。给插值上可以绑定数据,将来使用组件时可以用
基本使用步骤:

  1. 给slot标签,以添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
  1. 所有添加的属性,都会被收集到一个对象中
{id:3, msg:'测试文本'}
  1. 在template中,通过 #插槽名"=obj" 接收,默认插槽名为default
<MyTable :data="list">
      <template #default="obj">
        <button @click="del(obj.row.id)">删除</button>
      </template>
</MyTable>

3.案例实战 - 商品列表

需求说明:

  1. my-tag标签组件封装
    (1)双击显示输入框,输入框获取焦点
    (2)失去焦点,隐藏输入框
    (3)回显标签信息
    (4)内容修改,回车 → 修改标签内容
  2. my-table表格组件封装
    (1)动态传递表格数据渲染
    (2)表头支持用户自定义
    (3)主体支持用户自定义

思路:
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 表格组件的封装

  3. 数据不能写死,动态传递表格渲染的数据 props

  4. 结构不能写死 - 多处结构自定义 【具名插槽】
    (1) 表头支持自定义
    (2) 主体支持自定义

效果图:
在这里插入图片描述

核心代码:

<!--MyTable.vue-->
<template>
  <table class="my-table">
      <thead>
        <tr>
          <slot name="head"></slot>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item,index) in data" :key="item.id">
          <slot name="body" :item="item" :index="index"></slot>
        </tr>
      </tbody>
    </table>
</template>

<script>
export default {
    props:{
        data:{
            type: Array,
            required: true
        }
    }

}
</script >

<style lang="less" scoped>
.my-table {
    width: 100%;
    border-spacing: 0;
    img {
      width: 100px;
      height: 100px;
      object-fit: contain;
      vertical-align: middle;
    }
    th {
      background: #f5f5f5;
      border-bottom: 2px solid #069;
    }
    td {
      border-bottom: 1px dashed #ccc;
    }
    td,
    th {
      text-align: center;
      padding: 10px;
      transition: all 0.5s;
      &.red {
        color: red;
      }
    }
    .none {
      height: 100px;
      line-height: 100px;
      color: #999;
    }
  }
</style>
<!--MyTag.vue-->
<template>
    <div class="my-tag">
        <input
        v-if="isEdit"
        v-focus
        ref="inp"
        class="input" type="text" placeholder="输入标签"
        @blur="isEdit = false"
        :value="value"
        @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() {
            this.isEdit = true
            // this.$nextTick(() => {
            //     this.$refs.inp.focus()
            // })
        },
        handleEnter(e) {
            //非空处理
            if(e.target.value.trim() === "") return alert('标签内容不能为空')

            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>
<!--App.vue-->
<template>
  <div class="table-case">
    <MyTabel :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>
    </MyTabel>
  </div>
</template>

<script>
import MyTag from './components/MyTag.vue'
import MyTabel from './components/MyTable.vue'
export default {
  name: 'TableCase',
  components: {
   MyTag,
    MyTabel
  },
  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>

4.单页应用程序:SPA

单页应用程序: 所有功能在一个html页面上实现
在这里插入图片描述
应用场景: 系统类网站、内部网站、文档类网站、移动端站点
多页应用程序应用场景: 公司官网、电商类网站

5.路由

路由: 一种映射关系
Vue中的路由: 路径组件的映射关系。根据路由就能知道不同路径的,应该匹配渲染哪个组件。

6.VueRouter

作用:修改地址栏路径时,切换显示匹配的组件
说明:Vue官方的一个路由插件,是一个第三方包
VueRouter的使用(5 + 2):
5个基础步骤(固定)

  1. 下载:下载VueRouter模版到当前工程,版本3.6.5
yarn add vue-router@3.6.5

或者

npm add vue-router@3.6.5
  1. 引入
import VueRouter from 'vue-router'
  1. 安装注册
Vue.use(VueRouter)
  1. 创建路由对象
const router = new VueRouter()
  1. 注入,将路由对象注入到new Vue实例中,建立关联
new Vue({
	render: h => h(App),
	router:router
}).$mount('#app')

2个核心步骤:

  1. 创建需要的组件(views目录),配置路由规则
import Find from './views/Find'
import My from './views/My'
import Friend from './views/Friend'

const router = new VueRouter({
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
})
  1. 配置导航,配置路由出口(路径匹配的组件显示的位置)
<div class="footer_wrap">
      <a href="#/find">发现音乐</a>
      <a href="#/my">我的音乐</a>
      <a href="#/friend">朋友</a>
    </div>
    <div class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>

7.组件存放目录问题(组件分类)

组件分类:页面组件 和 复用组件
对应文件夹:
页面组件 - views文件夹 → 配合路由,页面展示
复用组件 - components文件夹 → 封装复用

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值