uni-app + uView 实践片段总结

1,279 阅读4分钟

基础片段

安装uView

把 uview-ui 目录放入工程的 uni_modules 目录下

配置uView

  • main.js
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'


import uView from 'uni_modules/uview-ui';
Vue.use(uView);

const app = new Vue({
    ...App
})
app.$mount()
  • uni.scss
@import "uni_modules/uview-ui/theme.scss";
  • App.vue
<style lang="scss">
	@import "uni_modules/uview-ui/index.scss";
</style>
  • pages.json:
"easycom": {
    "^u-(.*)": "uni_modules/uview-ui/components/u-$1/u-$1.vue"
},

跳转到某个页面

  • 方式1: 使用 uni-app 的组件 navigator

    <navigator url="/pages/menu/menu" hover-class="navigator-hover">
    跳转到新页面
    </navigator>
    

    注: 其中的 url 也可以用相对路径: "../menu/menu"

  • 方式2: 使用uni-app 的api 方法 navigateTo

    template中

    <u-button type="primary" @click="go">跳转</u-button>
    

    script 中

    go(){
        uni.navigateTo({
            url: '/pages/menu/menu'
        })
    }
    

发出 request 请求

  • 使用 uni-app 的 api

  • script 中:

data() {
    return {
    title: 'Hello',
    list:[]
    }
},
onLoad() {
    uni.request({
        url: 'http://simbajs.com:7890/list',
        success: ({data}) => {
        this.list = data ;
    }
})
  • template中:
<view v-for="(item,index) in list"  :key="index"> 
    {{item}}
</view>

使用 tabBar

  • pages.json 中:
"tabBar": {
    "color": "#dbdbdb",
    "selectedColor": "#d81e06",
    "borderStyle": "black",
    "backgroundColor": "#ffffff",
    "list": [{
        "pagePath": "pages/index/index",
        "iconPath": "static/images/home_grey.png",
        "selectedIconPath": "static/home.png",
        "text": "首页"
    }, {
        "pagePath": "pages/menu/menu",
        "iconPath": "static/images/menu_grey.png",
        "selectedIconPath": "static/menu.png",
        "text": "菜单"
    }]
}
  • 注: 需要提前准备好 icon, 可以从 iconfont.cn 下载

  • 注: color 和 selectedColor 一般需要和 icon 的颜色一致

跳转到tabBar

  • 普通页面跳转, 可以使用navigator 组件或 navigatorTo 方法; 但如果要跳转到 tabBar , 只能用 switchTab 跳转。 此时需要在 navigator 组件中 加入属性 open-type=”switchTab“ 来表明跳转到 tabBar; 或者在javascript中使用 switchTo方法.

  • 代码修改:

    • 如果是使用 navigator 组件跳转, 则修改后的 navigator 组件, 使用方法如下, 加入了 open-type
    <navigator open-type="switchTab"  url="/pages/menu/menu" hover-class="navigator-hover">
    跳转到新页面
    </navigator>
    
    • 如果是使用uni API跳转, 则修改后的 javascript 代码如下:
    go(){
        // 跳转到 tabBar 页面用 switchTab
        uni.switchTab({
            url: '/pages/menu/menu'
        })
        // 跳转到 普通页面用 navigatorTo
        // uni.navigateTo({
        // 	url: '/pages/menu/menu'
        // })
    }
    

使用 轮播图 swipera

  • template 中:
<swiper class="swiper" indicator-dots circular autoplay
        indicator-color="#eee"  
        indicator-active-color="#c0133c" >
    <swiper-item>
        <view class="swiper-item uni-bg-red">A</view>
    </swiper-item>
    <swiper-item>
        <view class="swiper-item uni-bg-green">B</view>
    </swiper-item>
    <swiper-item>
        <view class="swiper-item uni-bg-blue">C</view>
    </swiper-item>
</swiper>
  • style 中:
.swiper {
    width:690rpx;
    width: 100%;;
    height: 300rpx;
}
.swiper-item {
    display: block;
    height: 300rpx;
    line-height: 300rpx;
    text-align: center;
}
.uni-bg-red{
    background:#F76260; color:#FFF;
}
.uni-bg-green{
    background:#09BB07; color:#FFF;
}
.uni-bg-blue{
    background:#007AFF; color:#FFF;
}

轮播图中填入宫格

  • template 中:
<swiper class="swiper" indicator-dots  circular autoplay
        indicator-color="#eee" 
        indicator-active-color="#c0133c">
   <swiper-item>
      <u-grid :col="3">
        <template v-for="_ in 2">
          <u-grid-item 
            v-for="(item, index) in baseList" 
                   :key="index">
            <u-icon :name="item.name" size="22" ></u-icon>
            <text>{{item.title}}</text>
          </u-grid-item>
         </template>
      </u-grid>
    </swiper-item>
</swiper>
  • script 中:
data() {
    return {
        baseList: [
            {
            	name: 'photo',
	            title: '图片'
        	},
			{
        	     name: 'lock',
            	 title: '锁头'
	         },
             {
                  name: 'star',
                  title: '星星'
             },
         ]
    }
}

轮播图中加入图片

  • template 中:
<swiper indicator-dots indicator-color="#eee" indicator-active-color="#c0133c" circular autoplay>
    <swiper-item>
        <image mode="aspectFill" src="http://p1.music.126.net/jKqekReDA3Q4l26sweNMcQ==/109951166925476325.jpg?imageView&quality=89"></image>
    </swiper-item>
    <swiper-item>
        <image mode="aspectFill" src="http://p1.music.126.net/r6aplDsvEDiL8-UJi5eVdw==/109951166925482617.jpg?imageView&quality=89"></image>
    </swiper-item>
    <swiper-item>
        <image mode="aspectFill" src="http://p1.music.126.net/qlBlZF_iHFWv_wcHix15Bw==/109951166923907526.jpg?imageView&quality=89"></image>
    </swiper-item>
</swiper>
  • style 中:
swiper {
	width: 100%;
	height: 400rpx;
	image {
		width: 100%;
		height: 100%;
	}
}

轮播图封装化为Banner组件( 示例组件封装和props使用)

  • 新建文件 components/Banner/Banner.vue

    <template>
    	<view>
    		<swiper 
    		   :indicator-dots="dots" indicator-color="#eee" indicator-active-color="#c0133c" circular :autoplay="autoplay">
    			<swiper-item v-for='item in info'>
    				<image mode="aspectFill" :src="item.img"></image>
    			</swiper-item>
    		</swiper>
    	</view>
    </template>
    
    <script>
    	export default {
    		name:"Banner",
    		props: {
    				dots: {
    					type: Boolean,
    					default: false,
    					required: false
    				},
    				autoplay: {
    					type: Boolean,
    					default: false,
    					required: false
    				},
    				images: {
    					type: Array,
    					// 返回数组(构造缺省数据)
    					default: ()=> [
    						{
    						  img: 'http://p1.music.126.net/jKqekReDA3Q4l26sweNMcQ==/109951166925476325.jpg?imageView&quality=89',
    						},
    						{
    						  img: 'http://p1.music.126.net/r6aplDsvEDiL8-UJi5eVdw==/109951166925482617.jpg?imageView&quality=89',
    						},
    						{
    						  img: 'http://p1.music.126.net/jKqekReDA3Q4l26sweNMcQ==/109951166925476325.jpg?imageView&quality=89',
    						},
    					] ,
    					required: false
    				}            
    		},        
    		data() {
    			return {
    				info: this.images
    			};
    		}
    	}
    </script>
    
    <style lang="scss" scoped>
    swiper {
    	width: 100%;
    	height: 400rpx;
    	image {
    		width: 100%;
    		height: 100%;
    	}
    }
    </style>
    

    注意: 当传入的参数时数组时, props 的default要使用构造函数 ()=>{} 方式. 而不能直接赋值.

    其他使用此组件的页面 template 中, 如下使用

    <Banner dots autoplay />	
    

    若不需要 标志器 和 autoplay, 则可以不用写 dots autoplay

    uni-app 的 特殊点记录

    • style标签中, 不需要手动加 scoped , 框架会自动隔离
    • 组件只要规范化命名, 就不需要手动引入, 即 @/components/<MyComponent>/<MyComponent.vue>, 其他页面即可直接使用 <MyComponent></MyComponent>
    • 路由配置在pages.json中, pages数组的第一项就是首页
    • 全局对象叫uni ,如请求数据, uni.request()
    • 新增响应式样式单位 rpx, 在750px宽的屏幕上, 1rpx = 1px
    • swiper有默认高度, 需要手动调整它, 否则图片会被盖住

CSS: 使用flex:1

  • flex-grow, 拉伸因子, 当flex元素宽度的未定义或未达到容器宽度时, 各元素会进行拉伸. 语法为: flex-grow: n; , n有三种取值: 0, 整数, inherit, 0表示不拉伸, 整数 表示拉伸时所占份数, inherit 表示继承上一级设置

    html 片段

    <div class='box' >
        <div id='box1'></div>
        <div id='box2'></div>
    </div>
    

    css 片段:

    <style>
    .box {
        height: 200px;
        width: 800px;
        display: flex;
    }
    #box1 {
        background: pink;
        flex-grw: 1;
    }
    #box2 {
        background: yellow;
        flex-grow: 3;
    }
    </style>
    

    以上代码, 一个box中套了两个box, 父box设置为flex , 则意味着两个子 box 将遵循flex布局规则, 一般是横向排列. 它们的 flex-grow 分别是1,3, 则代表着横向100%空间, 由两个子box按照 1:3 的比例拉伸占满.

  • flex-shrink, 收缩规则, 当flex元素宽度的和超过容器宽度时, 各元素会进行收缩. 语法: flex-shrink: n, n有三种取值: 0, 整数, inherit, 0表示不收缩, 整数 表示收缩时所占份数, inherit 表示继承上一级设置

    html 片段:

    <div class="box">
        <div id="box1"></div>
        <div id="box2"></div>
    </div>
    

    css 片段:

    <style lang="scss">
    .box {
        width: 500px;
        display: flex;
    }
    #box1 {
        width: 400px;
        background: #c5c5c5;
        flex-shrink: 1;
    }
    #box2 {
        width: 400px;
        background: red;
        flex-shrink: 3;
    }
    </style>
    

    由于box1和box2的宽度和为800px>500px, 因此子盒子会进行收缩,以满足总宽度500px的要求; 其中box1、box2本身宽度为0(没有内容撑开宽度), 则剩余空间500px将按照1:3 本分配给 box1和box2, 即325px和175px;

  • flex-basis: 元素的初始大小, 可以是一个数值+单位, 也可以是相对于父节点的百分比. 默认为auto

    html片段:

    <div class="box">
        <div id="box1">box1</div>
        <div id="box2">box2</div>
    </div>
    

    css片段:

    <style>
        .box {
            height: 200px;
            display: flex;
        }
        #box1 {
            background-color: lightcoral;
            flex-basis: 80%;
        }
        #box2 {
            background-color: lightpink;
            flex-basis: 100px;
        }
    </style>
    

    box1是占主轴宽度的80%, box2是占100px, 假设主轴宽982px, 则982px*80% -=788.8px, 此时box1和box2的宽度总和为888.8px.

  • flex:1 flex: 1 1 数值+单位 的简写方式, 即拉伸/收缩的因子均是1;

    html片段:

    <div class="box">
        <div id="box1">box1</div>
        <div id="box2">box2</div>
        <div id="box3">box3</div>
    </div>
    

    css片段:

    <style>
        .box {
            height: 200px;
            display: flex;
        }
        #box1 {
            background-color: lightpink;
            flex: 1;
        }
        #box2 {
            background-color: lightsalmon;
            flex: 1;
        }
        #box3 {
            background-color: lightslategray;
            flex: 3;
        }
    </style>
    

    主轴宽度按 box1,box2,box3的 1:1:3 进行自适应伸缩.

CSS: 使用letter-spacing 调整字符间隔

letter-spacing: 2rpx; 此样式设置字符间间隔比一般默认情况下多2rpx, 即会稍稍宽一点;

CSS: 利用div+CSS制作左右布局的导航

html片段:

<view class="container flex">
    <view class="menu ">
        <view class='item' v-for="(item,index) in menus"
              @click='activeIndex=index'
              :class='{highLight:index===activeIndex}' >
            {{item}}
        </view>
    </view>
    <view class='content'>
        <view v-for='item in 10'>
            {{item}}
        </view>
    </view>
</view>

css片段:

.container{
	flex: 1;

	.menu {
		width: 200rpx;
		background: #eee;
		text-align: center;
		.item{
			padding: 10rpx;
			letter-spacing: 2rpx;
			&.highLight {
				background:#fff;
				font-weight: bold;
				border-left: 5rpx  solid $main-color;
				color: $main-color;
			}
		}
	}
	.content {
		flex: 1;
	}
}

script片段:

data() {
    return {
        activeIndex: 1,
        menus: [
            "服装",
            "食品",
            "饰品",
            "家具"
        ]				
    };
}
  • 其中view即为div, 顶层div设置为 container flex , 代表其孩子节点走flex布局,两个孩子节点左右平铺,分别是menu和content;

  • 在menu里面

    • 通过v-for循环生成菜单选项;

    • 通过class='item' 设置样式为item, item样式中 padding: 10rpx 加大菜单项竖向间隔, letter-spacing:2rpx 加大菜单项字符间的间隔

    • 通过 :class={highLight===activeIndex}来设置一个条件样式, 即只有 item===activeIndex 条件成立时, highLight样式才生效.

    • highLight 样式定义为, 改变背景颜色和字体颜色, 并加重字体, 设置左边框竖线.

      • &.highLight {
            background:#fff;
            font-weight: bold;
            border-left: 5rpx  solid $main-color;
            color: $main-color;
        }
        
    • 通过@click='activeIndex=item' 来切换当前选中项

    • 备注: 在item 样式里, 有border-left: 5rpx solid transparent; 这是为了让每个 item 都有边框(透明色), 这样选中的item有红色左边框时, 就不会显得和别的菜单项左边对不齐;

url字符串含有v-for内容怎么办?

  • 场景: 一个 v-for 列表, 每个item有不同的id, 点击其中一个item后, 跳转到item对应页面, 对应页面 url 为 /xxx/xxx/xxx?cate_id=nnn 这种形式, 其中nnn即为每个item的id. 此时可以使用 反引号 "`" 来做字符串拼接, 如下:

    <u-grid-item v-for="item in 11">
        <navigator :url='`/pages/goods-list/goods-list?cate_id=${item}`'>
            <image src="/static/good.png"</image>
            <view class='grid-text'>膨化食品-{{item}}</view>
    	</navigator>
    </u-grid-item>             
    

在多个页面间跳转时传递参数

  • 触发方:

    <u-grid-item v-for="item in 11">
    	<navigator 
    	   :url='`/pages/goods-list/goods-list?cate_id=${item.id}&cate_name=${item.name}`'>
            <image src="/static/good.png"</image>
            <view class='grid-text'>膨化食品-{{item}}</view>
    	</navigator>
    </u-grid-item>  
    

    传递了两个参数, 一个是 cate_id, 一个是 cate_name

  • 接收方:

    onLoad({cate_id, cate_name}) { // option为object, 序列化上个页面传递的参数
    	uni.setNavigationBarTitle({
    		title: cate_name
    	})
    },
    

模拟网络请求延迟

  • 有时需要模拟一次响应比较慢的网络请求, 比如请求后台数据列表. 纯前台可以用 setTimeout 来模拟.
getGoodsList(){
    uni.showLoading({ title: '加载中'});
    // 模拟ajax请求
    setTimeout(()=>{
        uni.hideLoading();
        this.goodslist = [+new Date]
    }, 1000);
}

设置100vh时出现竖向滚动条的处理

  • 当有顶部Navigator和底部tabBar时, 在内容区域设置 100vh 将会出现竖向滚动条. 在uni-app中, 顶部Navigator和底部tabBar的高度是固定的, 分别是44px和50px. 因此采用如下方式可以消除滚动条:

  • 由于在H5端和APP端, 100vh的表现不同(App端100vh即为不包括顶部导航栏和底部tabBar的高度, 此时不需要减去 44px和 50px), 对此使用条件编译的方式,

    .page-body {
    	/*#ifdef APP-PLUS */
    	height: 100vh;
    	/*#endif*/
       	/*#ifdef H5*/
    	height: calc(100vh - 44px);
    	/*#endif*/
    }
    .page-body-tab {
    	/*#ifdef APP-PLUS */
    	height: 100vh;
    	/*#endif*/
    	/*#ifdef H5*/
    	height: calc(100vh - 44px - 50px);
    	/*#endif*/
    }	
    

文字显示两行,超出部分显示为省略号的CSS

  • 如下情况, 当 item.name 内容很长时, 如何让它显示两行, 超过两行的部分, 自动使用省略号.

  • 实现方法有两种, 一种是CSS控制, 一种是使用JS把长字符串截断, 然后后面拼接省略号. 第二种方法对中文和英文混排效果较差, 因为中英文宽度不同. 下面介绍CSS方法.

    html片段:

    <image src="1.png"></image>
    <view class="flex flex-col j-s info">
        <view>
            <view class="title bold font16">{{ item.name }}</view>
            <view class="desc c-desc">销量 55 收藏 22 人气 3456</view>
        </view>
        <view class="price bold font16 c-m">¥ 456</view>
    </view>
    

    css片段: 重点看其中的 .title 样式

    .info {
        flex: 1;
        padding: 0 16rpx;
        .title {
            line-height: 20px;
            max-height: 40px;
            display: -webkit-box;
            -webkit-box-orient: vertical;
            -webkit-line-clamp: 2;
            overflow: hidden;
            word-break: break-all;
        }
    
        .desc {
            padding: 10rpx 0;
        }
    
        .price {
            font-size: 36rpx;
        }
    }
    

手机端(APP)和H5端非全屏时, 对顶部状态栏的处理

  • 在H5端, 顶部无状态栏, 但是在手机端, 顶部有手机的状态栏, 此时如果不预留这部分位置, 则内容会覆盖手机状态栏. uni-app 提供了一个变量 status-bar-height 来标记此位置的高度, 这个变量在不同的终端值不同, 在手机端有值, 在H5端为0. 具体过程如下

    • Step 1: 定义一个StatusBar组件, 在components目录下新增此组件, 内容如下:

      <template>
      	<view>
      		<view class="status_bar"><!-- 状态栏 --></view>
      	</view>
      </template>
      
      <script>
      	export default {
      		name:"StatusBar",
      	}
      </script>
      
      <style lang="scss">
      .status_bar	{
      	height: var(--status-bar-height);
      	width: 100%;
      }
      </style>
      
      
    • Step 2: 在全屏页面中使用这个组件占位

      全屏页面即顶部没有导航栏的页面(一般是主动设置为隐藏), 此类页面, 在顶部首先使用 StatusBar

      template片段:

      <template>
      	<view>
      		<StatusBar></StatusBar>
              
              <!-- 此处放置其他代码 -->
          </view>
      </template>
      

JS语法: 解构

  • 场景, swiper控件change事件中, 传入的参数event是个复杂对象, 内部有个成员 detail.current , 代表当前swiper item序号, 如下方式可以直接解构出这个current变量

    /*
    handleSwiperChange(e) {
        console.log(e);
        this.currendImgIdx = e.detail.current;
    }
    */
    // 两级解构写法
    handleSwiperChange( { detail: {current}} ) {
        console.log(current);
        this.currendImgIdx = current;
    }
    
    

使用父子两层相对定位_+ 绝对定位 把子节点固定在父节点某个位置

  • html片段:

    <view class="adv relative">
        <swiper>
            <swiper-item v-for='item in info'>
                <image mode="aspectFill" :src="item.img"></image>
            </swiper-item>
        </swiper>   
        <view class="indicator absolute flex a-c">
            {{currendImgIdx}} 
            <view class='split'>/</view>
            {{imgCount}}
        </view>
    </view>    
    
  • scss片段

    .relative {
        position: relative;
    }
    
    .absolute: {
        position: absolute;
    }
    
    .adv {
    	swiper {
    		width: 100%;
    		height: 400rpx;
    		image {
    			width: 100%;
    			height: 100%;
    		}
    	}	
    	
    	.indicator {
    		bottom: 10rpx;
    		right: 10rpx;
    		padding: 5rpx 10rpx;
    		font-size: 26rpx;
    		border-radius: 6rpx;
    		background: rgba(0, 0, 0, 0.2);
    		color: #fff;
            
    		.split {
    			padding: 0 6rpx;
    		}
    	}
    }
    

    以上代码把 第二个 div 'indicator' 固定在父div 'adv'的右下角位置

使用 fixed 定位把div固定在底部

  • 场景: 底部 购物车/付款 等按钮一直固定且浮动在页面上面

    html片段:

    <view>
        <view class="foot-bar">
        </view>
    </view>
    

    scss 片段:

    .foot-bar {
    	height: 100rpx;
    	width: 100vw;
    	background: red;
    	position: fixed;
    	bottom: 0rpx;
    }
    

    注意: fixed 之后, 就不会有自动100%宽度了, 因此宽度是需要指定的.

使用Flex布局横向铺满

  • 场景: 底部foot bar区域, 横向分为三部分, 购物车图标、加入购物车、立即购买, 比例为1:3:3。 使用Flex布局如下

    html片段:

    <view class="foot-bar fixed flex j-c">
        <view class='cart'>1</view>
        <view class='add2cart flex flex-c'>加入购物车</view>
        <view class='pay flex flex-c'>立即购买</view>
    </view>
    

    scss片段:

    .fixed {
    	position: fixed;
    }
    
    .j-c {
    	justify-content: center;
    }
    .a-c {
    	align-items: center;
    }
    
    .flex {
    	display: flex;
    	flex-wrap: wrap;
    }
    
    .flex-c {
    	align-items: center;
    	justify-content: center;
    }
    
    .foot-bar {
    	height: 100rpx;
    	width: 100%;
    	background: red;
    	bottom: 0rpx;
    	
    	.cart {
    		flex: 1;
    		background: #eee;
    	}
    	
    	.add2cart {
    		flex: 3;
    		color: #fff;
    		background: $main-color;
    	}
    	
    	.pay {
    		flex: 3;
    		color: #fff;
    		background: orangered;
    	}
    }
    

uni-app使用iconfont图标

  • 使用iconfont, 在H5正常, 手机端不现实图标, 此时需要使用base64方式的图标来解决此问题.
  • 如果图标的css文件太大超过64K时, 也需拆成两个文件分别导入;

过程如下:

  • 把图标加入到 "资源管理/我的项目"

  • 在"我的项目"中, 选择"项目设置", 在"字体格式"后只勾选"Base64"

  • 在"我的项目"中, 选择"下载至本地", 解压缩zip包, 把其中的"iconfont.css"放置工程的目录assets/font/

  • 在App.vue 中引入 此css文件:

    <script>
    	export default {
    
    	}
    </script>
    
    <style lang="scss">
    	@import "uni_modules/uview-ui/index.scss";
    	@import "assets/font/iconfont.css";
    </style>
    
    
  • 此时, 即可在页面上使用iconfont图标了]

    template 片段

    <view class='cart flex flex-col a-c'>
        <view class="iconfont icon-gouwuche"></view>
        <view class='text'>购物车</view>
    </view>
    
  • 还可以使用css对图标进行调整(颜色/大小等)

    scss片段

    .cart {
        flex: 1;
        background: #eee;
        color: #666;
    
        .iconfont {
            font-size: 42rpx;	
        }
        .text {
            font-size: 24rpx;
            padding: 3rpx 0;		
        }
    }
    

图片上放置回退/收藏/转发图标的布局

  • 场景: 商城的商品详情页, 商品图片左边是返回图标, 右边是收藏和转发图标

  • 实现上依然采用父div相对布局, 子div绝对布局来实现三个图标的放置

    template 片段:

    <view class="adv relative">
        <swiper indicator-color="#eee" indicator-active-color="#c0133c" circular>
            <swiper-item v-for='item in info.imgList'>
                <image mode="aspectFill" :src="item"></image>
            </swiper-item>
        </swiper>
        
        <u-icon class="icon back absolute flex-c" 
                name="arrow-left" color="#eee" size="18" bold></u-icon>
        <u-icon class="icon collect absolute flex-c" 
                @click="info.hasCollected=!info.hasCollected"
                :name="info.hasCollected?'star-fill':'star'" 
                :color="info.hasCollected?'#f00':'#eee'" size="18" bold></u-icon>
        <u-icon class="icon share absolute flex-c" 
                name="share" color="#eee" size="18" bold></u-icon>
    </view>
    

    注: 其中通过三元运算符, 切换收藏图标点击后的显示样式

    还可以通和计算属性, 使得html部分代码更为简洁

    <u-icon class="icon collect absolute flex-c" 
                @click="info.hasCollected=!info.hasCollected"
                :name="collectIconName" 
                :color="collectIconColor" 
            	size="18" bold></u-icon>
    
    computed: {
        collectIconName() {
            return this.info.hasCollected? 'star-fill': 'star'
        }
        collectIconColor() {
            return this.info.hasCollected? '#f00' : '#eee'
        }
    }
    

    scss 片段

    .relative {
    	position: relative;
    }
    .absolute {
    	position: absolute;
    }
    .adv {
    	swiper {
    		width: 100%;
    		height: 400rpx;
    		image {
    			width: 100%;
    			height: 100%;
    		}
    	}	
    	.icon {
    		top: 30rpx;
    		width: 60rpx;
    		height: 60rpx;
    		border-radius: 50%;
    		background: rgba(0,0,0,0.3);
    		
    		&.back {
    			left: 30rpx;
    		}
    		
    		&.collect {
    			right: 120rpx;
    		}
    	
    		&.share {
    			right: 30rpx;
    		}
    	}
    }
    

    注: 通过绝对定位

    javascript片段:

    data() {
        return {
            info: {
                hasCollected: false,
                imgList: [
                    '1.png',
                    '2.png',
                ]
            }
        };
    },
    

如何实现navigator的返回: uni.navigateBack()

  • 当使用 navigator 进入某一页后, 点击返回时返回来的页面

    template 片段:

    	<u-icon class="icon back absolute flex-c" 
    				@click="handleBack"
    				name="arrow-left" color="#eee" size="18" bold></u-icon>
    

    javascript 片段:

    methods: {
        handleBack() {
            uni.navigateBack()
        }
    }
    

如何通过图片边缘模糊, 实现边缘上的按钮更醒目

  • 场景: 商品图片上面往往浮动一些icon, 若左上角的返回, 右上角的 收藏/分享, 右下角的图片滑动角标; 有时候由于图片颜色较深, 导致这些icon不明显.

  • 实现: 通过图片边缘模糊, 让图片边缘浅色化, 使得icon显性化 template 片段:

    <view class="adv relative">
    			<swiper>
    				<swiper-item v-for='item in info.imgList'>
    					<image mode="aspectFill" :src="item"></image>
    				</swiper-item>
    			</swiper>
    			<view class="indicator absolute flex a-c">
    				{{currendImgIdx}} 
    				<view class='split'>/</view>
    				{{imgCount}}
    			</view>
        		<!-- 左上角 "返回" -->
    			<u-icon class="icon back absolute flex-c" 
    				@click="handleBack"
    				name="arrow-left" color="#eee" size="18" bold>
        		</u-icon>
        		<!-- 右上角 "收藏" -->    
    			<u-icon class="icon collect absolute flex-c" 
    					@click="info.hasCollected=!info.hasCollected"
    					:name="info.hasCollected?'star-fill':'star'" 
    					:color="info.hasCollected?'#f00':'#eee'" size="18" bold>
        		</u-icon>
        		<!-- 右上角 "分享" -->        
    			<u-icon class="icon share absolute flex-c" 
    				@click="handleShare"
    				name="share" color="#eee" size="18" bold>
        		</u-icon>
    		</view>
    

    scss片段:

    image {
        width: 100%;
        height: 100%;
    }
    
    image:after {
        position: absolute;
        content: '';
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        box-shadow: 0 0 50px 10px #fff inset;			
    }
    

    注1: 直接为外层DIV添加边缘阴影是看不到效果的, 因为阴影会被DIV中的图片遮住. 因此, 必须通过一个绝度定位的伪元素 image:after 添加阴影.

文字和控件左右靠边对齐

  • 效果: 文字在左边, 比如"购买信息", 控件在右边, 例如"步进器"

    template片段:

    <view class="flex j-s">
        <view>购买数量</view>
    	<u-number-box v-model="buyNumber" ></u-number-box>
    </view>
    

    scss片段:

    .flex {
    	display: flex;
    	flex-wrap: wrap;
    }
    
    .j-s {
    	justify-content: space-between;
    }
    

    如果不加flex样式, 则文字"购买数量"和步进器控件u-number-box 会处于两行. 通过在其父节点增加 class="flex j-s"使得它们在一行上.其中的j-s确保两者横向两端对齐.

使用样式交集复用并部分改写原样式

  • 场景: 已有样式 foot-bar , 想在本div中复用既有 foot-bar 样式, 但修改其中的部分属性

  • 方式: 通过设置本div的样式为 class ="sku foot-bar " ,并且在 scss 中取两个样式交集

    template 片段:

    <view class="sku foot-bar fixed flex j-c font16">
        <view class='add2cart flex flex-c' >加入购物车</view>
        <view class='pay flex flex-c' >立即购买</view>
    </view>
    

    注: 其中的foot-bar, add2cart, pay 三者都是已经存在且定义的css样式

    scss片段:

    .sku.foot-bar {
        border-radius: 50rpx;
        overflow: hidden;
        
        bottom: 10rpx;
    	width: 90%;    
        left: 0;
        right: 0;
        margin: 0 auto;
    
        .add2cart,
        .pay {
            flex: 1;
        }
    }
    
    • 使用 .sku.foot-bar 交集写法
    • 新覆盖了原来 .add2cart,.pay 中的 flex:n 的设置值, 重新设置为flex:1;
    • div改为圆角: border-radius: 50rpx; overflow: hidden; 其中 overflow 把圆角以外的隐藏不显示
    • div居中: 使用了 width:90%; left:0; right:0; margin:0 auto; 联合, 整个div居中

使用 scroll-view 达到popup中部分内容滚动的效果

  • 场景: 在popup 底部弹出的div中, 其最下面 foot-bar 区域的 "加入购物车"和"立即购买" 两个按钮不滚动, 但上面的商品规格介绍区域需滚动,

  • 方式: 可以把商品高规格介绍区域放入 scrolll-view 进行滚动, 然后把 foot-bar 区域相对于popup 层绝对定位在底部

    template片段:

    <u-popup :show="showSKU" :round="10" mode="bottom">
        <!-- 滚动区域, 设置高度为700rpx , 超出部分沿y轴滚动-->
        <scroll-view class="u-popup" scroll-y="true" style="height:700rpx">    
            <!-- 内容区域略  -->
        </scroll-view>
    
        <!-- 非滚动区域, 使用 absolute 定位 -->
        <view class="sku foot-bar absolute flex font16">
            <view class='add2cart flex flex-c' @click="add2Cart" >加入购物车</view>
            <view class='pay flex flex-c' @click="buy">立即购买</view>
        </view>    
    </u-popup>
    

    scss 片段:

    .u-popup{
    	padding: 20rpx;
    }
    
    .sku.foot-bar {
        // 底部按钮区域, 居中圆角效果
        width: 90%;
        border-radius: 50rpx;
        overflow: hidden;
        bottom: 10rpx;
        left: 0;
        right: 0;
        margin: 0 auto;    
    }
    

使用 mixin 制作Vue插件

  • 场景: 大量的方法, 是否可以通过 mixin 混入到 Vue 对象上, 并简化其使用方式? 类似 uView 被 Vue对象use. 这样调用时就可以使用 this.xxx 直接调用

  • 方式: 使用 Vue.mixin() 方法

    • step1: 在项目目录下新建 plugins 目录, 下面新建 mixin.js 内容如下

      // 导出一个函数, 函数需传入一个参数 Vue,  这个Vue 就是插件调用本方法时传入的Vue
      // export  default function(Vue) {
      //
      // }
      export default Vue=>{
          Vue.mixin({
              data(){
                  return {
                      
                  }
              },
              methods:{
                  $go(url, isTab=false) {
                      if(isTab) { 
                          uni.switchTab({url})
                      }else{ 
                          uni.navigateTo({url}) 
                      }
                  },
                  __msg__(type, message, desc) {
                      this.$notification[type]({
                          message,
                          desc,
                          duration: 2,
                      })
                  },
                  $ms(message="操作成功", desc="稍后将为您呈现最新效果") {
              		this.__msg__('success', message, desc)
      		    },
                  $me(message="操作失败", desc="请刷新页面后重试") {
                      this.__msg__('error', message, desc)
                  }
              }
          })    
      )
      
      

      备注: 主要是对Vue的方法进行混入(mixin). 此处是混入 go 方法

    • step2: 在项目目录 plugins 目录下面新建 index.js, 内容如下

      // 此文件负责 
      //   import installMixins from './mixin'
      //   import installDirectives from './directive'
      //   import installFilters from './filter'
      //   import extendVuew from './vue-extend'
      //   import registerComponents from './component'
      
      // 导入 mixin 函数, 便于调用它把go 方法注册到全局Vue对象
      // install(Vue) 是缺省被回调的函数, 当main.js调用 Vue.use(xxx)时, 就会调用此方法
      import installMixins from "./mixin"
      export default {
          install(Vue) {
              installMixins(Vue)
              // installDirectives(Vue)
              // installFilters(Vue)
              // registerComponents(Vue)
              // registerComponents(Vue)
          }
      }
      

      备注: 插件的写法是必须安装Vue, 然后使用方法

    • step3: 在main.js中增加如下代码, 把插件挂接到 Vue 上

      import Vue from 'vue'
      
      // 新增的两行如下
      import naviPlugin from "./plugins"
      Vue.use(naviPlugin)
      
      const app = new Vue({
          ...App
      })
      app.$mount()
      
    • step4: 使用插件, 通过 this.go() 可以直接使用此插件

      this.$go('/pages/shop-cart/shop-cart', false)