首页 > 其他分享 >day05

day05

时间:2024-02-19 22:56:38浏览次数:41  
标签:el 自定义 插槽 day05 指令 组件 路由

day05

目录

自定义指令

指令介绍

  • 内置指令:v-htmlv-ifv-bindv-on ... 这都是 Vue 内置的一些指令,提供好的可以直接使用的

  • 自定义指令:Vue 也支持让开发者,自己注册一些指令,这些指令被称为自定义指令

  • 每个指令都有自己各自独立的功能

自定义指令

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

自定义指令语法

  • 例如想要打开页面完成获取焦点的操作:

    <!-- 原本是 -->
    <input v-focus ref="inp" type="text">
    
    mounted(){
    	this.$refs.inp.focus()
    }
    
    • 但每次获取焦点的页面都要加上这几句,所以想是不是可以封装成一个自定义指令
  • 全局注册写法

    //在main.js中
    Vue.directive('指令名', {
      "inserted" (el) { // el就是指令所绑定的元素,如<input type="text">
        // 可以对 el 标签,扩展额外功能
        el.focus()
      }
    })
    
    • inserted:一个生命周期钩子,表示当指令被绑定的元素被添加到页面中的时候会自动调用
    • el:使用指令的那个 DOM 元素
  • 局部注册写法

    //在Vue组件的配置项中
    directives: {
      "指令名": {
        inserted (el) {
          // 可以对 el 标签,扩展额外功能
          el.focus()
        }
      }
    }
    
    • 只可以在当前组件范围内使用
  • 使用指令

    • 注意:在使用指令的时候,一定要先注册,再使用,否则会报错
    • 使用指令语法: v-指令名,如:<input type="text" v-focus/>
  • 注册指令时不用加 v- 前缀,但使用时一定要加 v- 前缀

自定义指令 — 携指令的值

需求

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

语法

  • 在绑定指令时,可以通过 “ 等号 ” 的形式为指令绑定具体的参数值
<template>
  <div>
     <!--显示红色--> 
    <h2 v-color="color1">指令的值1测试</h2>
     <!--显示蓝色--> 
    <h2 v-color="color2">指令的值2测试</h2>
     <button>
        改变第一个h1的颜色
    </button>
  </div>
</template>

<script>
export default {
  data () {
    return {
      color1: 'red',
      color2: 'blue'
    }
  }
}
</script>
  • 通过 binding.value 就可以拿到指令值,指令值修改会触发 update 函数
directives: {
  color: {
    inserted (el, binding) {
      el.style.color = binding.value
    }, // 元素添加到页面时的逻辑 —— 获取
    update (el, binding) {
      el.style.color = binding.value
    } // 提供值发生变化时的逻辑 —— 更新(不需要指定1还是2)
  }
}

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

场景

  • 实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态

需求

  • 为了复用,封装一个 v-loading 指令,实现加载中的效果

分析

  • 本质 loading 效果就是一个蒙层,盖在了盒子上
  • 数据请求中,开启 loading 状态,添加蒙层
  • 数据请求完毕,关闭 loading 状态,移除蒙层

实现

  • 准备一个 loading 类,通过伪元素定位,设置宽高,实现蒙层
  • 开启关闭 loading 状态(就是添加移除蒙层),本质只需要添加移除类即可
  • 结合自定义指令的语法进行封装复用
.loading:before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #fff url("./loading.gif") no-repeat center;
}

准备代码

<template>
  <div class="main">
    <!-- 写上loading就相当于加上蒙层了:<div class="box loading">,想做成动态可控的,就用自定义指令 -->
    <div class="box" v-loading="isLoading">
      <ul>
        <li v-for="item in list" :key="item.id" class="news">
          <div class="left">
            <div class="title">{{ item.title }}</div>
            <div class="info">
              <span>{{ item.source }}</span>
              <span>{{ item.time }}</span>
            </div>
          </div>
          <div class="right">
            <img :src="item.img" alt="">
          </div>
        </li>
      </ul>
    </div> 
  </div>
</template>

<script>
// 安装axios =>  yarn add axios || npm i axios
import axios from 'axios'

export default {
  data () {
    return {
      list: [],
      isLoading: true,
      isLoading2: false // 可以you
    }
  },
  async created () {
    // 1. 发送请求获取数据
    const res = await axios.get('http://')
    
    setTimeout(() => {
      // 2. 更新到 list 中,用于页面渲染 v-for
      this.list = res.data.data
    }, 2000) // 加载两秒数据
  },
  directives: {
    loading:{
      inserted (el, binding) { // binding.value为初始值的获取
        binding.value ? el.classList.add('loading') : el.classList.remove('loading')
      }, 
      update (el, binding) { // 使之可以被改变
        binding.value ? el.classList.add('loading') : el.classList.remove('loading')
      }
    }
  }
}
</script>

<style>
.loading:before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #fff url('./loading.gif') no-repeat center;
}
</style>

插槽 — 默认插槽

作用

  • 让组件内部的一些结构支持自定义

需求

  • 例如:将需要多次显示的对话框,封装成一个组件

问题

  • 组件的内容部分,不希望写死,希望能使用的时候自定义

插槽的基本语法

  • 子组件内需要定制的结构部分,改用 <slot></slot> 占位
  • 被使用时,父组件使用标签 <MyDialog>内部</MyDialog> 在内部,传入结构替换 slot
  • 给插槽传入内容时,可以传入纯文本、html 标签、组件

代码示例

  • MyDialog.vue
<template>
  <div class="dialog">
    <div class="dialog-header">
      <h3>友情提示</h3>
      <span class="close">✖️</span>
    </div>

    <div class="dialog-content">
      <slot></slot>
    </div>
    <div class="dialog-footer">
      <button>取消</button>
      <button>确认</button>
    </div>
  </div>
</template>
  • App.vue
<template>
  <div>
    <MyDialog>
      您确定要进行删除操作吗?
      <!--  自定义引入写入  -->
    </MyDialog>
  </div>
</template>

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

插槽的后备内容

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

语法

  • 在 MyDialog.vue 的 <slot> 标签内,放置内容,作为默认显示内容:<slot>默认内容</slot>

效果

  • 外部使用组件时,不传内容,则 slot 会显示 “ 默认内容 ”

  • 外部使用组件时,传东西了,则 slot 整体会被换掉

插槽 — 具名插槽

需求

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

  • 弹框中有三处不同,但是默认插槽只能定制一个位置

具名插槽语法

  • 多个 slot 使用 name 属性区分名字

    <div class="">
      <slot name = "head"></slot>
    </div>
    <div class="">
      <slot name = "content"></slot>
    </div>
    <div class="">
      <slot name = "footer"></slot>
    </div>
    
  • 需要用到 <template> 标签来配合 v-slot: 名字来分发对应标签

    <MyDialog>
      <template v-slot:head>
    		大标题
      </template>
      <template v-slot:content>
    		<div>文本内容</div>	
      </template>
      <template v-slot:footer>
    		<button>按钮</button>
      </template>
    </MyDialog>
    

v-slot 的简写

  • v-slot 写起来太长,vue 提供一个简单写法:#
    • 如:#head#content

作用域插槽

插槽分类

  • 默认插槽

  • 具名插槽

    • 插槽只有两种,作用域插槽不属于插槽的一种分类

作用

  • 定义 slot 插槽的同时,是可以传值的
  • 给插槽上可以绑定数据,将来使用组件时可以用

场景

  • 封装表格组件

使用步骤

  • 给 slot 标签,以添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
  • 所有添加的属性,都会被收集到一个对象中
{ id: 3, msg: '测试文本' }
  • 在 template 中, 通过 #插槽名= "obj" 接收

    • 这里的 obj 是一个封装好了的对象,你也可以选择用 {} 解构(下面示例有涉及)
  • 如果是默认插槽,默认插槽名为 default

<MyTable :list="list">
  <template #default="obj">
    <button @click="del(obj.id)">删除</button>
  </template>
</MyTable>

代码示例

  • MyTable.vue
<template>
  <table class="my-table">
    <thead>
      <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年纪</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="......">
        <td>...</td>
        <td>...</td>
        <td>...</td>
        <td>
          <!--  插槽占位,定制非写死的  -->
          <slot :row="item" msg="测试文本"></slot>
          <!--  用添加属性的方式传值  -->
          <!--  传值就是一整个对象,即 { row: {item对象}, msg: '测试文本' }  -->
        </td>
      </tr>
        
    </tbody>
  </table>
</template>

props:{
	data: Array
}
  • App.vue
<template>
  <div>
    <MyTable :data="list">
      <!--  通过template #接收子插槽传来的整个对象obj,可以{{ obj }} 看看  -->
      <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>

<script>
  import MyTable from './components/MyTable.vue'
  export default {
    data () {
      return {
     	list: [
            { id: 1, name: 'xxx', age: 18 },
            { id: 2, name: 'yyy', age: 19 },
            { id: 3, name: 'zzz', age: 17 },
          ],
          list2: [
            { id: 1, name: 'aaa', age: 18 },
            { id: 2, name: 'bbb', age: 19 },
            { id: 3, name: 'ccc', age: 17 },
          ]
      }
    },
    components: {
      MyTable
    },
    methods: {
      show (row) {
        console.log(row); // 获取的就是含有id、name、age的对象
        alert(`姓名:${row.name}; 年纪:${row.age}`)
      }
    }
  }
</script>

综合案例 — 自定义 MyTag 组件控制显示隐藏

  • MyTag.vue
<template>
  <div class="my-tag">
    <input
      v-if="isEdit"
      v-focus 
      ref="inp"
      ......
      :value="value"
      @blur="isEdit = false" 
      @keyup.enter="handleEnter"
    />
    <div 
      v-else
      @dblclick="handleClick"
      class="text">
       {{ props接收输入框输入的数据value }}
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      isEdit: false
    }
  },
  methods: {
    handleClick () {
      this.isEdit = true
      // 不用自定义的v-focus的话,可以
      // this.$nextTick(() => {
      //		this.$refs.inp.focus()
      // })
    },
    handleEnter (e) {
      // 非空处理
      if (e.target.value.trim() === '') return alert('标签内容不能为空')
      this.$emit('input', e.target.value) // 父是v-model,所以用 'input'
      // 提交完成,关闭输入状态
      this.isEdit = false
    }
  }
}
</script> 
  • @dblclick: 双击事件

  • v-focus 自定义获取焦点:

    • main.js:
    // 封装全局指令 focus
    Vue.directive('focus', {
      // 指令所在的dom元素,被插入到页面中时触发
      inserted (el) {
        el.focus()
      }
    })
    
    
  • @blur 失去焦点

  • :value 子回传输入的数据,回调回父做更改

综合案例 — 封装 MyTable 组件

  • 动态封装表格结构的组件

    • 动态渲染数据,表头自定义,显示数据跟着表头变化
      • 不写死就用插槽
    • 动态结构,具名插槽自定义
  • App.vue

<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 }">
				<!--  自定义表体,接收子传来的数据obj,也可写成上述样式,对应表头显示几列  -->
        <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>
import MyTag from './components/MyTag.vue'
import MyTable from './components/MyTable.vue'
export default {
  name: 'TableCase',
  components: {
    MyTag,
    MyTable
  },
  data () {
    return {
      ....
  }
}
</script>
 
  • 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">
        <!--  作用域插槽 :item传数据到父组件  -->
        <slot name="body" :item="item" :index="index" ></slot>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    data: {
      type: Array,
      required: true
    }
  }
};
</script>
  • 父传总数据给子,子中自定义部分插槽占位,父写进插槽的要与各个子组件相关,所以每个接收到父传来的属于自己的数据要用作用域插槽返还给父

单页应用程序介绍

概念

  • 单页应用程序:SPA(Single Page Application)是指所有的功能都在一个 html 页面上实现

具体示例

  • 单页应用网站: 网易云音乐 https://music.163.com/

    • 根据导航栏的点击改变下面的主体内容,改变的不是整个页面
  • 多页应用网站:京东 https://jd.com/

    • 点击导航栏会跳到对应的另一个页面

单页应用 VS 多页面应用

68244191297

  • 单页应用类网站:系统类网站 / 内部网站 / 文档类网站 / 移动端站点

  • 多页应用类网站:公司官网 / 电商类网站

路由介绍

思考

  • 单页面应用程序,之所以开发效率高,性能好,用户体验好

    • 最大的原因就是:页面按需更新
    • 不是更新整个页面,而是更新这个页面中需要的部分
  • 比如点击任意导航栏按钮时,只是更新导航栏下面的部分内容,对于头部的导航栏是不更新的

    • 要按需更新,首先就需要明确:访问路径和组件的对应关系
    • 访问路径和组件的对应关系的确定就需要:路由

路由的介绍

  • 生活中的路由:设备和 ip 的映射关系

    • 192.168.0.xx
    • 192.168.0.xy
  • Vue 中的路由:路径和组件的映射关系(不同的路径就对应着不同的组件)

    • http://localhost:8080/#/home
    • http://localhost:8080/#/car
    • http://localhost:8080/#/search

路由的基本使用

  • 认识插件 VueRouter,掌握 VueRouter 的基本使用步骤

作用

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

说明

VueRouter 的使用(5 + 2)

  • 5 个固定的步骤 — 初始
  1. 下载 VueRouter 模块到当前工程,版本 3.6.5

    yarn add [email protected]
    

    image-20240106205946964

  2. main.js 中引入 VueRouter

    import VueRouter from 'vue-router'
    
  3. 安装注册

    Vue.use(VueRouter)
    
  4. 创建路由对象

    const router = new VueRouter()
    
  5. 注入,将路由对象注入到 new Vue 实例中,建立关联

    new Vue({
      render: h => h(App),
      router:router
    	// 只有在第四步const时叫router,这里才能简写为:router
    }).$mount('#app')
    
  • 当我们配置完以上 5 步之后,就可以看到浏览器地址栏中的路由变成了 localhost:8080/#/ 的形式,表示项目的路由已经被 Vue-Router 管理了

两个核心步骤

  1. 创建需要的组件( views 目录),配置路由规则

    • main.js 的 const 实例中,例:

    68247963966

  2. 配置导航,配置路由出口(路径匹配的组件显示的位置)

    • App.vue
    <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>
    
    • <router-view> 就是匹配的组件所展示的位置

组件的存放目录问题

  • src 里 的 views 文件夹来存放上述组件页面

组件分类

  • .vue 文件分为两类,都是 .vue文件(但本质无区别)
    • 页面组件(配置路由规则时使用的组件)放 views 里
    • 复用组件(多个组件中都使用到的组件)

存放目录

  • 分类开来的目的就是为了更容易维护
  • src/views 文件夹:
    • 页面组件 — 页面展示 — 配合路由用
  • src/components 文件夹:
    • 复用组件 — 展示数据 — 常用于复用

路由的封装抽离

  • 随着日后功能的增加完善,所有的路由配置都在 main.js 中不合适

  • 所以要将路由模块抽离出来

    • 放在 src 下的 router 文件夹里的 index.js 文件中

    68248141030

    • 第一个图就是 router 里的 index.js(别忘了 import Vue from 'vue'),还要 export default router 导出 —— 第三个图 main.js 导入使用

    • ./ 是在本层里找,../ 才是在上一层找

      • 但这样层级较深或者换目录时就有些复杂,所以:
    • 路径简写:

      • 脚手架环境下 @ 指代 src 目录,可以用于快速引入组件

标签:el,自定义,插槽,day05,指令,组件,路由
From: https://www.cnblogs.com/zhu-ya-zhu/p/18022115

相关文章

  • MetaGPT day05 MetaGPT 爬虫工程师智能体
    Metagpt爬虫智能体需求1.用ActionNode重写订阅智能体,实现自然语言爬取解析网站内容2.根据尝试实现思路1,即使用llm提取出需要的信息而不是写爬虫代码。3.目前,订阅智能体是通过RunSubscription运行的,即RunSubscription这个action,不仅创建了订阅智能体代码,并启动了Subscriptio......
  • Day05位运算符
    位运算符//位运算符:&,|,^,<<,>>//位运算,与二进制有关A=00111100B=00001101A&B=00001100//按位与(&),对于两个操作数的每一个对应位,如果两个位都是1,则结果位为1,否则为0A|B=00111101//按位或(|),对于两个操作数的每一个对......
  • Day05逻辑运算符
    逻辑运算符//与(and)或(or)非(!,取反)booleana=true;booleanb=false;System.out.println("a&&b:"+(b&&a));//与运算:两个変量都为真,结果才为trueSystem.out.println("a||b:"+(a||b));//算:两个量有一个真,结果为trueSystem.out.println("!(a......
  • Day05 变量
    Day05变量定义:在程序执行过程中,其值有可能发生改变的量(数据)使用场景:当某个数据经常发生改变时,我们也可以用变量存储。当数据变化时,只要修改变量里面变化的值即可。变量的定义格式数据类型变量名=数据值;(数据值:存在空间里面的数值)(变量名:为空间起的名)(数据类型:为空间......
  • day05
    1.今日内容今日内容:1、while循环2、for循环3、可变不可变类型4、基本数据类型常用操作及内置方法数字类型intfloat字符串类型2.流程控制之while循环"""语法while条件:代码1代码2代码3......
  • day05 K8S网络组件的深度剖析 (1.10.1 -1.10.3)
    一、K8S网络组件的深度剖析上1、认识FlannelFlannel是专为kubernetes定制的三层网络解决方案,主要用于解决容器的跨主机通信问题优势:kubernetes发行版都可以默认安装Flannel容器安装和配置中小型网络架构首选不需要专用的数据存储劣势:性能损耗高不支持NetworkPolic......
  • Day05 语法
    1.注释单行注释//注释多行注释/*注释*/文档注释/**注释*/2.标识符命名规则(与js规则一样)标识符可以含有字母,数字,下划线_,(*注意此处相较于c语言多出一个符)不能以数字开头不能是java中的保留字和关键字java也采用了unicode编码方式,所以......
  • Day05 Java程序运行机制
    Java程序运行机制编译型解释型如同中国人写了一本书美国人想看编译型就类似把整本书全部翻译成美国人看得懂的书(中文书-->英文书)解释型就类似美国人找了个翻译官翻译一段美国人看一段(说一句解释一句用一下编译一下)程序运行机制源程序(*.java)文件-->Jav......
  • day05
    IDEA使用java中的注释java中的注释有三种单行注释://+文字多行注释:可以注释一段文本/*中间*/文档注释JavaDoc:Java标识符 ......
  • JavaSEday05 泛型,数据结构,List,Set集合
    javSEday05泛型,数据结构,List,Set今日目标泛型使用数据结构ListSet1泛型1.1泛型的介绍泛型是一种类型参数,专门用来保存类型用的最早接触泛型是在ArrayList,这个E就是所谓的泛型了。使用ArrayList时,只要给E指定某一个类型,里面所有用到泛型的地方都会被......