Vue学习笔记 (2023黑马版 第五天
第五天
1.自定义指令
自定义指令:自己定义的指令,可以封装一些dom操作,扩展额外功能
- 全局注册
Vue.directive('指令名', {
inserted (el) {
el.focus()
}
})
- 局部注册
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指令,实现加载中的效果
核心思路:
- 准备类名Loading,通过伪元素提供遮罩层
- 添加或移除类名,实现loading蒙层的添加移除
- 利用指令语法,封装v-loading通用指令
inserted钩子中,binding.value判断指令的值,设置默认状态
update钩子中,binding.value判断指令的值,更新类名状态
2.插槽
插槽 - 默认插槽
作用: 让组件内部的一些结构支持自定义
场景: 当组件内某一部分结构不确定,用slot占位封装
使用:
- 现在组件内用slot占位
- 使用组件时,传入具体标签内容插入
插槽 - 后备内容(默认值)
插槽后备内容: 封装组件时,可以为预留的"< slot >"插槽提供后备内容(默认内容)
给插槽设置默认显示内容:在slot标签内,写好后备内容,当使用组件并为给我们传入具体标签或内容时,插槽后备内容显示。
<slot>我是后备内容</slot>
插槽 - 具名插槽
需求: 一个组件内有多处结构,需要外部传入标签,进行定制
语法:
- 多个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>
- 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插槽的同时,是可以传值的。给插值上可以绑定数据,将来使用组件时可以用
基本使用步骤:
- 给slot标签,以添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
- 所有添加的属性,都会被收集到一个对象中
{id:3, msg:'测试文本'}
- 在template中,通过 #插槽名"=obj" 接收,默认插槽名为default
<MyTable :data="list">
<template #default="obj">
<button @click="del(obj.row.id)">删除</button>
</template>
</MyTable>
3.案例实战 - 商品列表
需求说明:
- my-tag标签组件封装
(1)双击显示输入框,输入框获取焦点
(2)失去焦点,隐藏输入框
(3)回显标签信息
(4)内容修改,回车 → 修改标签内容 - my-table表格组件封装
(1)动态传递表格数据渲染
(2)表头支持用户自定义
(3)主体支持用户自定义
思路:
my-tag 标签组件的封装
-
创建组件 - 初始化
-
实现功能
(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 表格组件的封装 -
数据不能写死,动态传递表格渲染的数据 props
-
结构不能写死 - 多处结构自定义 【具名插槽】
(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个基础步骤(固定)
- 下载:下载VueRouter模版到当前工程,版本3.6.5
yarn add vue-router@3.6.5
或者
npm add vue-router@3.6.5
- 引入
import VueRouter from 'vue-router'
- 安装注册
Vue.use(VueRouter)
- 创建路由对象
const router = new VueRouter()
- 注入,将路由对象注入到new Vue实例中,建立关联
new Vue({
render: h => h(App),
router:router
}).$mount('#app')
2个核心步骤:
- 创建需要的组件(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 },
]
})
- 配置导航,配置路由出口(路径匹配的组件显示的位置)
<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文件夹 → 封装复用