整理常见组件封装 便于工作中使用
css核心布局: 如果纯是布局一分为四平均布局的话,只需要设父级为display: flex , 每个子元素设flex 1 ,便 可以平均支撑,
.tab-bar{
display: flex; //核心布局方式核心
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #999999;
box - shadow: 10px(x) 10px(y) 10px(模湖度) rgba 色()
}
.tab-bar-itme{
flex: 1; //核心
text-align: center;
line-height: 49px;
}
初步封装好组件,在下为初步封装,测试效果
<template>
<div id="app">
<div class="tab-bar" >
<div class = "tabbar-item">首页</div>
<div class = "tabbar-item">首页</div>
<div class = "tabbar-item">首页</div>
<div class = "tabbar-item">首页</div>
</div>
</div>
</template>
组件封装好后,可以在一个新的文件调用:
<template>
<div id="app">
<tabbar></tabbar>
</div>
</template>
<script>
import tabbar from '@/components/tabbar'
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'about',
components: {
tabbar
}
}
</script>
<style>
</style>
通过上面封装只是看一下css和组件是不是能用,实际中组件中有图件有事件,需要一步步解决。
实际工作中菜单 上有图标,,而图标大小又不一定,加入图标后会布局不是想要的。先来解决css布局。
版本1.0,没有考虚点击变色,核心理念是单独item 封成一个组件,外层定位是一个组件。下面红色文字有误, <div class="tab-bar-item">是定义外组件,说明里面有二个插巢,
通过上面方法可以正确组件样子了,只是缺少点击变红色,激活状态的样式。想做出激活样式,需要插巢中有二个图标,动态选择显示
用if语句动态指定使用那个图标,对于文字可以使动用动态样式,
插巢属性容易被替换,所以想动态绑定样式有点麻烦,需要外层加上div ;
解决了动态样式后,第三步需要解决跳转路由, 解决 方法,通过item绑定事件时,绑定在内部组件中,通过你组件传入要跳转路由
<template> <div class = "tab-bar-item" @click = "itemClick" > <div v- if = "!isActive" ><slot name = item-icon></slot></div> <div v- else ><slot name = "item-icon-active" ></slot></div> <div : class = "{active: isActive}" ><slot name = "item-text" ></slot></div> </div> </template> <script> export default { name: 'tabberItem' , // 这个组件跳转到那儿由父组件传过来 props:{ path: String }, data(){ return { isActive: true , } }, methods: { itemClick(){ // 点击时跳转 可以用$router.push,也可以用$router.replace this .$router.replace( this .path) } } } </script> <style scoped> </style> |
附注一下,通过父组件传入路由,关于路由模式,哈希模式是指/#/index 而history 模式则不带,直接/形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <template> <div id= "app" > <!-- 可以动态绑定path 也可以直接path= "/home" --> <div class = "tab-bar" path= "/home" > <div class = "tabbar-item" > <img src= "../../assets/img/tabbar/img.png" alt= "" > 首页 </div> <div class = "tabbar-item" > <img src= "../../assets/img/tabbar/img.png" alt= "" > 首页 </div> <div class = "tabbar-item" > <img src= "../../assets/img/tabbar/img.png" alt= "" > 首页 </div> <div class = "tabbar-item" > <img src= "../../assets/img/tabbar/img.png" alt= "" > 首页 </div> </div> </div> </template> <script lang= "ts" > import { defineComponent } from 'vue' export default defineComponent({ setup () { return {} } }) </script> <style> .tab-bar{ position: fixed; display: flex; bottom: 0; left: 0; right: 0; background-color: gainsboro; } .tabbar-item{ flex: 1; } .tab-bar-item img{ width: 24px; height: 24px; vertical-align: middle; //使图片居中一下, } </style> |
跳转路由解决了,还需要解决路由活跃时为高亮显示,解决思路是判断当时的路由是不是自己的一个路由值 相同,如果同样,就动态绑这个组件的动态style,判断这个值需要用到计算属性。
如果styles样式要由外界传入,css样式不能写在组件中,也要写一个计算属性,计算如果是激活状态的话,样式为属性的某一个值,不是的话返回空
<div> :style="activeStyle"> <slot name="item-text"></slot></div>
由于activeStyle是由外界传入,所以需要props 接收,整 个底部导航功能到此结束
props:{ path: String, activeColor:{ type:String, default : 'red' } } |
activeStyle() { return this.isActive ? {color: this.activeColor} : {} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
导航菜单方式二,带返回简易功能形式
导航菜单有时有返回,主要用于主页,不同位置显示不一样,。左边有返回,中间有菜单 ,右边是个人中心,有时又只有一个菜单 ,这种菜单封装也
比较简单,
<div class="nav-bar">
<div class="left"><slot name="left"></slot></div>
<div class="left"><slot name="center"></slot></div>
<div class="left"><slot name="right"></slot></div>
</div>
////////////////////////////////////////////////////////////////
.nav-bar{
display: flex;
height: 44px;
line-height: 44px;
text-align: center;
box-shadow: 0 1px 1px rgba(100,100,100,.1);
}
.left, .right{
width: 60px;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
使用的时候直接按需求替换一个具名的插巢
<div id="home>"
<nav-bar class="home-nav"><div slot = "center">购物车</nav-bar>
这种功能的菜单和第一种方案其乎一样,简要说一下,说细参考第一方案,接来谈第三种方案,第三种更简单
主要是连插巢都不要了,通过外面传入数组显示菜单,增加一个路由跳转功能。
导航菜单形式三。
切换菜单请求组件:tobControl 给选项卡一样的控制栏
<template>
<!--
<div class="tab-control">
<div v-for="item in titles">
<span>{{item}}</span>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "tabControl",
props: {
titles: {
type: Array,
default(){
return []
}
}
}
父组件使用的时候: <tab-control :titles="['流行', '新款', '精选']">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | <template> <!-- 导航栏,有些时候左边有图标,右边有也有,也些时候又没有,为了适应不同场景,定义三个具名插巢,使用的时候按名称替换--> <div class = "tab-control" > <div v- for = "(item, index) in titles" > <!-- 动态绑定css,使点击时切换激活css样式,然后需要编写一个函数切换点击后切换索引, --> class = "tab-control-item" : class = "{active: index === currentIndex}" @click = "tabclick(index)" > <span>{{item}}</span> </div> </div> </template> <script lang= "ts" > export default { name: "tabControl" , props: { titles: { type: Array, default (){ return [] } } }, data(){ return { currentIndex: 0 // 记录一个默契索引,用于点击时当前项变色 } }, methods:{ tabclick(index){ this .currentIndex = index 点击时还要向外发送一个事件,告诉索引几被点击了 this .$emit( 'tabClck' , index)??????????????????????????????????????????????????????????子组件向外发送事件和一个索引,父组件通过接收索引,在methods 方法中,用 switch case 语法方法将索引转换成currenType父组件接收事件 @tabclick = "tabclick" 类型。 父组件写法是 <good=list :goods= "goods[currentType].list" 上面的写法太长,太长的表达式尽量用计算属性转换一下 <good-list :goods= "showGoods" > 计算属性写法: computed:{showGoods(){ return goods[currentType].list}}tabclick(index){ switch (index){pop case 0: this .currentType =‘pop’ break case 1: this .currentType = "new" break case 2: this .currentType = "sell" 如果有设defalt值的话,防旧 case 穿透,下面需要加上 break }} } } } </script> <style> .tab-control{ display: flex; text-align: center; } .tab-control-item{ height: 40px; line-height: 40px; flex: 1; } /*主要是给itema项的spna 加一个空距,不要让下划线离太近*/ .tab-control-item span{ padding: 4px; } .active{ color: #FF0000; } /*给span加一个下划线,用户点击时高亮显示,*/ /*这个active由于是索引是点击时才会激活*/ .active span{ border: 1px solid #CC0000; } </style> |
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
附加功能 关健字 菜单停留
控制档菜单在下划拖动时不能离开屏幕,拖到顶部后到0后,就一直在那儿,实现思路一,先计算位置,当位置小于多少时,设置css属性
实现思路二,增加 position: sticky css
top: 44px
商品区菜单 封装 主要用于内容区,主要涉及到内容排列展示样式,和复杂数据请求处埋,滚动效果处理
??///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
第一步,请求内容,请求到的内容肯定要分类,先要一个大对象,大对象里面有三个小对象,由于数据太多,需要分页,所以小对象还要记录分页页码信息
请求时分别请求数据,并记录当前请求到的页码,下一次从下一页开始请求
1 2 3 4 5 | // goods 放所有分类的数据,goods:{ 'pos' : {page: 0, list:[]}, 'new' : {page: 0, list:[]}, 'sell' : {page: 0, list:[]}, } |
在生命周期创建时发送请求数据。
created(){
this.getHomeGoods("pop")
this.getHomeGoods("new")
this.getHomeGoods("sell")
}
在正式说组件前先看一下我们要做的效果,心中有数才能更好表达。这儿有三个技术解决问题,布局平分,滚动效果,菜单路由处理
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
商品列表展示组件
第一步,将获取到的数据传入组件展示。使用组件时向组件传入数据时,先清楚数据是什么样的格式,
这个数据格式一个大对象,里面有很多商品,每个商品又是一个小对象,有价格,图得等参数,所以需要拆分二层组件,一层名为所有商品,一层为
详细商品细节,
父组件(所有商品组件,也叫列表)
<goods :goods="goods['xxx'].list"/>
goods 商品组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <template> <div class = "goods" > <goods-item v- for = "item in goods" :goods-item= "item" /> </div> </template> <script> export default { name: 'Goods' , components: {}, props: { goods:{ type: Array, default (){ return [] } } } } </script> <style scoped> </style> |
详细商品item 组件(记录每个商品的价格,图片,等)
<template>
<div>
<div class="goods-item">
<img src="对象。图片" alt="">
<div class="goods-info">
<p>{{对象.描述}}</p>
<span class="price">{{对象.参数}}</span> <span class = "collect">{{对象.价格}}</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'goodsItem',
props:{
goodsitem:{
type:Object,
default () {
return {}
}
}
}
}
</script>
<style scoped>
/*本次对item 布局是先留个40px位置给padding占位,然后显示info的区别绝对定位*/
/*到个区域*/
.goods-item{
/*//这个40% 占位用于显示info内容*/
padding-bottom: 40px;
/*这个相对定位是给下面info区块定位参考,*/
position: relative;
//由于想让一列显示二个,所以设置每一个占48% 这个在留个空位 平分栏位需要父组件采用
// display: flex 平分布局
// flex-wrap: wrap 不有一行显示完
// justify-content:space-around 平分均等分
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | .goods-item img{ width: 100%; border-radius: 5px; } .goods-info{ font-size: 12px; position:absolute; bottom: 5px; left: 0; right: 0; overflow: hidden; text-align: center; } .goods-info p{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 3px; } .goods-info .price{ color: #999999; margin-right: 20px; } .goods-info .collect{ position: relative; } .goods-info .collect::before{ content: '' ; position: absolute; left: -15px; top: -1px; width: 14px; height: 14px; background: url( "图标地址" ); } </style> |
组件封装滚动简单示例,先看怎么使用,在逆向怎么封装,以下代码完成了监听滚动,和下拉到底提示,增加了一个按扭一健滚动到首部
<template>
<div>
<div class="home">
<div class=" text hiet">众油宝商城菜单</div>
<b-scroll class="wrapper"
ref='scroll'
@scroll="fn"
@pullingUp="pull"
:probe-type="2"
>
<ul>
<li>11111</li>
<li>2222</li>
<li>33333</li>
cd
<li>44444</li>
<li>666666</li>
<li>777777</li>
<li>88888</li>
<li>99999</li>
<li>1111111</li>
<li>aaaaaaaaaaaaaaaaaa</li>
<li>ddddddddd</li>
<li>rrrrrrrrrrrrrr</li>
<li>aaaaaaaaaaaaa</li>
<li>qqqqqqqqqqqqqqq</li>
<li>bbbbbbbbbbbbbb</li>
<li>jjjjjjjjjjjjjjj</li>
<li>yyyyyyyyyyyyyyy</li>
<li>oooooooooooooooo</li>
<li>mmmmmmmmmmmmmmm</li>
<li></li>
</ul>
</b-scroll>
<back-top @click.native="contentScroll" v-show="isshow"></back-top>
<div class="text bter">bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</div>
</div>
</div>
</template>
<script>
import bScroll from '../components/common/scroll/bScroll'
import backTop from '@/components/common/Topber/backTop'
export default {
name: 'bIndex',
components: {
bScroll,
backTop
},
data() {
return {
isshow: false
}
},
methods: {
contentScroll() {
console.log('ol')
// 方法一 this.$refs.scroll.scroll.scrollTo(0, 0, 500)
this.$refs.scroll.scrollTo(0, 0, 500)
},
fn (position) {
// fn是监听到滚动事件的处理函数,由于上下拉,y值为负,需要使用负号转正
this.isshow = (-position.y) > 1000
console.log(-position.y)
},
pull () {
alert('拉到底了')
}
}
}
</script>
<style scoped>
.home {
border: 2px solid #0074D9;
height: 100vh;
}
.text {
height: 45px;
line-height: 45px;
background: #7f7f7f;
}
.wrapper {
width: 450px;
height: calc(100% - 98px);
border: #C21F39 1px solid;
overflow: hidden;
}
ul {
padding: 0;
padding-left: 5px;
width: 440px;
height: auto;
margin: 0 auto;
border: 2px solid #C21F39;
overflow: hidden;
}
li {
list-style-type: none;
float: left;
margin: 2px;
width: 210px;
background: #999999;
height: 350px;
}
</style>
封装代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <template> <div ref= "scr" > <div class = "content" > <slot></slot> </div> </div> </template> <script> import BScroll from 'better-scroll' export default { name: 'scroll' , props: { // 定义滚动类型,0不滚动,本参数由外面父组件传入 probeType: { type: Number, default : 0 }, // 定义是否支持支底通知,用于上拉加载 pullUpLoad: { type: Boolean, defalut: false } }, data() { return { scroll: null } }, mounted () { // 监听和加载写在挂载生命周期,不能写在创建生命周期时,因为create时还没有加载dom this .scroll = new BScroll( this .$refs.scr, { click: true , probeType: this .probeType, pullUpLoad: true }) // 监听滚动 this .scroll.on( 'scroll' , (position) => { this .$emit( 'scroll' , position) }) // 监听下拉加载 this .scroll.on( 'pullingUp' , () => { this .$emit( 'pullingUp' ) }) }, // 所有常用方法写在methods methods: { scrollTo (x, y, time = 500) { this .scroll.scrollTo(x, y, time) } } } </script> <style scoped> </style> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)