前端:Vue学习-3

前端:Vue学习-3

    • [1. 自定义指令](#1. 自定义指令)
    • [2. 插槽](#2. 插槽)
      • [2.1 插槽 - 后备内容(默认值)](#2.1 插槽 - 后备内容(默认值))
      • [2.2 插槽 - 具名插槽](#2.2 插槽 - 具名插槽)
      • [2.3 插槽 - 作用域插槽](#2.3 插槽 - 作用域插槽)
    • [3. Vue - 路由](#3. Vue - 路由)
      • [3.1 路由模块封装](#3.1 路由模块封装)
      • [3.2 声明式导航 router-link 高亮](#3.2 声明式导航 router-link 高亮)
      • [3.3 自定义匹配的类名](#3.3 自定义匹配的类名)
      • [3.4 声明式导肮 - 跳转传参](#3.4 声明式导肮 - 跳转传参)
      • [3.5 Vue路由 - 重定向](#3.5 Vue路由 - 重定向)
      • [3.6 Vue路由 - 404](#3.6 Vue路由 - 404)
      • [3.7 Vue路由 - 模式设置](#3.7 Vue路由 - 模式设置)
      • [3.8 编程式导航 - 基本跳转,传参](#3.8 编程式导航 - 基本跳转,传参)

1. 自定义指令

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

全局注册,在main.js添加如下代码,以下代码为输入框自动聚焦。

js 复制代码
// focus为指令名
Vue.directive('focus',{
  inserted(el){
    el.focus();
  }
})
html 复制代码
<input type="text" v-focus>

运行结果:

局部注册,在组件内进行注册,只能在当前组件内使用该指令、

js 复制代码
directives:{
    focus:{
      inserted(el){
        el.focus();
      }
    }
  }

inserted提供的是元素被添加到页面时的逻辑,update 指令的值被修改时触发,提供值变化后,dom更新的逻辑。上述指令并没有给值,下述代码为给指令添加值。

html 复制代码
<template>
  <div id="app">
    <p v-color='color1'>北京</p>
    <p v-color='color2'>上海</p>
  </div>
</template>
<script>

export default {
  name: 'App',
  data(){
    return{
      color1:'red',
      color2:'blue'
    }
  },
  directives:{
    color:{
      inserted(el,binding){
        el.style.color = binding.value;
      },
      update(el,binding){
        el.style.color = binding.value;
      }
    }
  }
}
</script>
<style scoped>
  #app{
    width: 100px;
    height: 100px;
    margin: 20px auto;
  }
</style>

运行结果:

v-指令名="指令值",通过等号绑定指令的值;通过binding.value拿到指令的值

封装v-loading指令

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

html 复制代码
<template>
  <div id="app">
      <div v-loading="loading"></div>
      <div>哈哈</div>
  </div>
</template>
<script>

export default {
  name: 'App',
  data(){
    return{
      loading:true
    }
  },
  directives:{
    loading:{
      inserted(el,binding){
        binding.value ? el.classList.add('loading'):el.classList.remove('loading');
      },
      update(el,binding){
        binding.value ? el.classList.add('loading'):el.classList.remove('loading');
      }
    }
  },
  created(){
    setTimeout(()=>{
      this.loading = false;
    },3000);
    // 等待3秒,只是演示效果
  }
}
</script>
<style scoped>
  #app{
    width: 500px;
    height: 500px;
    margin: 20px auto;
    position: relative;
  }
  .loading:before{
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgb(242,94,94) url('./static/1.gif') no-repeat center;
  }
</style>

运行效果:

2. 插槽

作用:让组件内部的一些结构支持自定义,可以定制结构或内容。

在组件内需要定制的结构部分,改动<slot></slot> 占位;使用组件时,在组件内部传入结构替换slot。

注意:只有两种插槽,默认插槽和具名插槽;

MyBase组件:

html 复制代码
<template>
	<div>
		<slot></slot>
	</div>
</template>

使用

html 复制代码
<MyBase>你好</MyBase>
<MyBase>你好2</MyBase>

这样的效果就是和下述代码一样

html 复制代码
<div>
	你好
</div>
<div>
	你好2
</div>

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

封装组件时,可以为预留的'<slot>'插槽提供默认值;直接在==<slot></slot>标签内,放置内容,作为默认显示内容==

子组件:

html 复制代码
<template>
    <div>
        <slot>你好</slot>
    </div>
</template>

<script>
export default {
    name:'MyBaseSlot'
}
</script>

<style>

</style>

父组件:

html 复制代码
<template>
  <div id="app">
    <MyBaseSlot></MyBaseSlot>    
    <MyBaseSlot>你好2</MyBaseSlot>   
  </div>
</template>
<script>
import MyBaseSlot from './components/MyBaseSlot.vue'

export default {
  name: 'App',
  data(){
    return{
      loading:true
    }
  },
  components:{
    MyBaseSlot
  }
}
</script>
<style scoped>
  #app{
    width: 500px;
    height: 500px;
    margin: 20px auto;
  }
  
</style>

运行结果:

2.2 插槽 - 具名插槽

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

使用,在组件内多个slot使用name属性区分名字,在调用该组件时,在该组件内使用template配合v-slot:属性名来分发对应标签,可以简化为#属性名;

子组件:MyBaseSlot

html 复制代码
<template>
    <div>
        <slot name="head">标题</slot>
        <p>什么都不是</p>
        <slot name="content">hello world!</slot>
    </div>
</template>

<script>
export default {
    name:'MyBaseSlot'
}
</script>

<style>

</style>

父组件:

html 复制代码
<template>
  <div id="app">
    <MyBaseSlot>
      <template v-slot:head>
        <div class="title">
          我是大标题
        </div>
      </template>
      <template #content>
        <div class="content">
          <p>我是内容</p>
          <img src="./static/1.jpg" alt="">
        </div>
      </template>
    </MyBaseSlot>
  </div>
</template>
<script>
import MyBaseSlot from './components/MyBaseSlot.vue'

export default {
  name: 'App',
  data(){
    return{
      loading:true
    }
  },
  components:{
    MyBaseSlot
  }
}
</script>
<style scoped>
  #app{
    width: 500px;
    height: 500px;
    margin: 20px auto;
    border: 1px solid black;
  }
  .title{
    width: 100px;
    height: 40px;
    line-height: 40px;
    background-color: red;
  }
  .content{
    width: 200px;
    height: 240px;
  }
  .content img{
    width: 200px;
  }
</style>

运行结果:

2.3 插槽 - 作用域插槽

定义slot插槽时,是可以进行传值的。插槽上可以绑定数据,将来使用组件时可以用。

  1. 给slot标签,以添加属性的方式传值;
  2. 所有添加的属性,都会被收集到一个对象中;
  3. 在template中,通过 '#插槽名="obj"'接收,默认插槽名为default
html 复制代码
<template>
    <div>
       <table style="margin-left:10px;margin-top:10px">
        <tr>
            <th>id</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>操作</th>
        </tr>
        <tr v-for="item in lists" :key="item.id">
            <td>{{item.id}}</td>
            <td>{{item.name}}</td>
            <td>{{item.age}}</td>
            <td>
                <slot :id="item.id" name="btn"></slot>
            </td>
        </tr>
       </table>
    </div>
</template>

<script>
export default {
    name:'MyBaseSlot',
    props:{
        lists:Array
    }
}
</script>

<style>
    tr{
        height: 40px;
    }
    td{
        width: 40px;
        text-align: center;
        line-height: 40px;
    }
    table{
        border: 1px solid black;
    }
</style>
html 复制代码
<template>
  <div id="app">
      <MyBaseSlot :lists="list1">
        <template #btn="obj">
          <button @click="fn(obj.id)">删除</button>
        </template>
      </MyBaseSlot>
  </div>
</template>
<script>
import MyBaseSlot from './components/MyBaseSlot.vue'

export default {
  name: 'App',
  data(){
    return{
        list1:[
          {id:1,name:'zs',age:12},
          {id:2,name:'ls',age:22},
          {id:3,name:'ww',age:32},
          {id:4,name:'zl',age:19}
        ]
    }
  },
  components:{
    MyBaseSlot
  },
  methods:{
    fn(id){
      this.list1 = this.list1.filter((item)=>item.id != id)
    }
  }
}
</script>
<style scoped>
  #app{
    width: 500px;
    height: 500px;
    margin: 20px auto;
    border: 1px solid black;
  }
  .title{
    width: 100px;
    height: 40px;
    line-height: 40px;
    background-color: red;
  }
</style>

运行结果:

3. Vue - 路由

路径和组件的映射关系。

安装VueRouter

js 复制代码
npm install vue-router@3.6.5
js 复制代码
import VueRouter from 'vue-router'
// 引入
Vue.use(VueRouter)
// 安装注册
const router = new VueRouter();
// 创建路由对象
注入路由对象到Vue实例中
new Vue({
	render:h=>h(App),
	router
}).$mount('#app')

此时的访问路径出现了"#/"

创建views目录,用来存储组件,配置路由规则。

js 复制代码
const router = new VueRouter({
	routes:[
		{path:'/find',component:Find}
	]
})

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

html 复制代码
<a href='#/find'>发现</a>
<router-link to="/find">发现</router-link>
<router-view></router-view>
js 复制代码
const router = new VueRouter({
  routes: [
    { path: '/find', component: Find },
    { path: '/myMusic', component: My },
    { path: '/friend', component: Friend }
  ]
});
html 复制代码
<template>
  <div id="app">
      <a href="#/find">发现音乐</a>
      <router-link to="/myMusic">我的音乐</router-link>
      <router-link to="/friend">我的朋友</router-link>
      <router-view></router-view>
  </div>
</template>

运行结果:

组件存放目录问题,页面组件 放在views目录下,复用组件放在components目录下。

3.1 路由模块封装

把路由相关代码用单独js文件来实现,提高代码的可读性和可维护性。

index.js

js 复制代码
import Vue from 'vue'
import VueRouter from 'vue-router'
import Find from '@/views/Find.vue'
import My from '@/views/My.vue'
import Friend from '@/views/Friend.vue'

Vue.use(VueRouter);

const router = new VueRouter({
    routes: [
        { path: '/find', component: Find },
        { path: '/myMusic', component: My },
        { path: '/friend', component: Friend }
    ]
});

export default router

main.js

js 复制代码
import router from './router/index'

router-link 本质上就是a标签,配置其to属性,表示路由路径。默认会提供高亮类名。

router-link会自动添加两个高亮类名,为router-link-active和router-link-exact-active,其中 router-link-active 模糊匹配,而router-link-exact-active 为精确匹配。

router-link-active 能够匹配 /find /find/one /find/two

router-link-exact-active 只能匹配 /find

3.3 自定义匹配的类名

router-link的两个高亮类名太长了,如何进行自定义呢?直接在VueRouter中进行配置即可。

js 复制代码
const router = new VueRouter({
    routes: [
        { path: '/find', component: Find },
        { path: '/myMusic', component: My },
        { path: '/friend', component: Friend }
    ],
    linkActiveClass:'active',
    linkExactActiveClass:'exact-active'
});

3.4 声明式导肮 - 跳转传参

查询参数传参
to="/path?参数名=值"

对应页面接收传递的参数值

js 复制代码
$route.query.参数名
html 复制代码
<template>
  <div>
    发现音乐
    <p>{{$route.query.title}}</p>
  </div>
</template>


动态路由传参

配置动态路由

js 复制代码
{ path: '/find/:参数名', component: Find },

{ path: '/find/:参数名?', component: Find },这种方式表示在不传参时也可以匹配到对应组件;如果用上述方式,当不传参时,则不会匹配到对应的组件。

导航链接为:

html 复制代码
<router-link to='/find/我的梦'></router-link>

对应页面租价接收传递过来的值

js 复制代码
$route.params.参数名

两种传参方式的区别:

查询参数传参,比较适合多个参数,获取方式通过 route.query.参数名;动态路由传参,优雅简洁,传递单个参数比较方便,获取方式通过 route.params.参数名

3.5 Vue路由 - 重定向

在VueRouter配置项添加redirect,如下

js 复制代码
const router = new VueRouter({
    routes: [
        { path: '/', redirect: '/find'},
        { path: '/find/:title?', component: Find },
        { path: '/myMusic', component: My },
        { path: '/friend', component: Friend }
    ],
    linkActiveClass:'active',
    linkExactActiveClass:'exact-active'
});

运行结果:

3.6 Vue路由 - 404

当路径找不到匹配时,给个提示页面;新建页面组件NotFound,然后在VueRouter配置项最后添加=={path:'*',component:NotFound}==,表示前面路径都没有匹配到时,跳转到这个NotFound页面。

js 复制代码
routes: [
        { path: '/', redirect: '/find'},
        { path: '/find/:title?', component: Find },
        { path: '/myMusic', component: My },
        { path: '/friend', component: Friend },
        { path: '*', component: NotFound}
    ]

运行结果:

3.7 Vue路由 - 模式设置

两种模式:

hash模式(默认),比如:http://localhost:8080/#/find

history路由,比如:http://localhost:8080/find

直接在VueRouter配置即可,配置如下:

js 复制代码
const router = new VueRouter({
	routes,
	mode:'history'
})

3.8 编程式导航 - 基本跳转,传参

1.path路径跳转

js 复制代码
this.$router.push('/find')
// 简写
this.$router.push({
	path:'/find'
})
// 完整写法

2.name命名路由跳转(适合path路径长的场景)

js 复制代码
this.$router.push({
	name:'路由名'
})

// 路由配置为:
{name:'路由名',path:'路径',component:xxx}

路由传参

两种传参方式:查询参数、动态路由传参
1.查询参数

js 复制代码
this.$router.push('/find?title=我的梦&page=1&size=10')
// 简写
this.$router.push({
	path:'/find',
	query:{
		title:'我的梦',
		page:1,
		size:10
	}
})
// 详细写法

2.动态路由传参

js 复制代码
this.$router.push('/find/我的梦')
//或
this.$router.push({
		path:'/find/我的梦'
})

上述两种方式参数接收方和声明式导航一样。
使用命名路由进行传参:
查询参数进行传参 query

js 复制代码
this.$router.push({
	name:'路由名',
	query:{
		'参数名':'值',
		...
	}
})

动态路由进行传参 params

js 复制代码
this.$router.push({
	name:'路由名',
	params:{
		'参数名':'值',
	}
})
相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax