Swiper官方组件仅支持Vue3,如果需要在Vue2中使用Swiper,可以使用vue-awesome-swiper
vue-awesome-swiper也需要官方Swiper插件的支持,不过支持最好的版本是Swiper5,Swiper4和Swiper6都会出现一些bug
安装方法
npm安装
npm install vue-awesome-swiper --save
npm install swiper@5.2.0 --save
使用方法
vue-awesome-swiper相当于是对swiper进行了包装,swiper的API同样适用于vue-awesome-swiper
这个网站有相应的源码
在Vue组件中可以这样使用
<template>
<swiper class="swiper" :options="swiperOption">
<swiper-slide class="swiper-slide" v-for="(item, index) in banners" :key="index">
<a :href="item.link" >
<img :src="item.image" alt="">
</a>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
//Swiper配置
const swiperOption = {
initialSlide :1,
spaceBetween: 30,
centeredSlides: true,
loop: true,
pagination: {
el: '.swiper-pagination',
clickable: true
},
autoplay: {
delay: 2500,
disableOnInteraction: false
}
}
export default {
name: 'MainSwiper',
components: {
Swiper,
SwiperSlide
},
props: {
banners: {
type: Array,
default() {
return []
}
}
},
data() {
return {
swiperOption
}
}
}
</script>
<style scoped>
.swiper-slide {
height: 200px;
width: 100%;
}
.swiper-slide img {
height: 100%;
width: 100%;
}
.swiper {
--swiper-pagination-color: var(--color-high-text); /* 两种都可以 */
}
</style>
Vue实现选中当前按钮
Vue中的v-bind绑定class可以很方便的呃实现选中当前按钮点击,我们可以给每一个按钮绑定一个样式,例如:
:class="{tabControlItemActive: currentIndex === index}
初始值令currentIndex为0,并且绑定一个方法
@click="showIndex(index)
该方法就收一个参数index,也就是当前点击的按钮的index
showIndex(index) {
this.currentIndex = index;
}
在接收到index之后,将index的值赋给currentIndex,这样一来,currentIndex的值必定为每次点击的按钮的index值
在两者相同时,tabControlItemActive样式的值为true,便会显示出来
<template>
<div class="tab-control">
<div v-for="(item, index) in tabItems" :key="index"
class="tab-control-item" :class="{tabControlItemActive: currentIndex === index}"
@click="showIndex(index)">
<span>{{item}}</span>
</div>
</div>
</template>
<script>
export default {
name: "TabControl",
props: {
tabItems: {
type: Array,
default() {
return []
}
}
},
data() {
return {
currentIndex: 0
}
},
methods: {
showIndex(index) {
this.currentIndex = index;
}
}
}
</script>
<style scoped>
.tab-control {
display: flex;
height: 44px;
line-height: 44px;
}
.tab-control-item {
flex: 1;
text-align: center;
}
.tabControlItemActive {
color: var(--color-high-text);
}
.tabControlItemActive span {
padding: 4px;
border-bottom: 2px solid var(--color-high-text);
}
</style>
将一个数组赋值给另一个数组
可以使用ES6的数组解构,将数组解构之后push进新数组
this.goods[type].list.push(...data.data.list);
关于create
在编写代码的时候踩的一个坑,原本想实验在create中调用两次toGetShopCityGoods()方法接收到的数据会因为页码变化而变化。
但是出现的结果是两次都是同一个结果,通过在chrome中断点调试发现,Vue是在执行完两条语句之后才开始创建Vue实例。
由此猜测,之所以两次得到同样的结果是因为Vue实例还未创建,所以得到的page都是同一个,导致最后得到相同的结果
组件绑定方法在组件上直接绑定方法使用常规的v-on并不可行,需要加上native属性
例如:
<back-top class="back-top" @click.native="backTop"></back-top>
ref
Vue可以使用ref绑定一个组件,例如:
<div class="wrapper" ref="wrapper">
这样就在当前的组件中绑定了另外一个ref组件,可以在任何地方调用这个组件
相比较通过类名或者id查询,Vue更推荐使用此方法绑定组件
同时,也可以在任何地方通过ref属性调用这个组件的方法
这样一来,在封装的第三方插件中,如果想在其他地方调用这个插件对象极其方法时,就可以使用ref属性
<b-scroll class="shop-city-scroll" ref="BScroll">
例如,在better-scroll中,想做一个返回顶部的组件,需要调用better-scroll的方法,我们便有两种可行的方法
其一:直接调用better-scroll对象,调用其scrollTo方法
其二:在封装better-scroll的组件中,封装一个核心为scrollTo的方法,这样就可以不用调用scroll对象而是调用组件方法
better-scroll在移动端如果使用原生的js滑动效果往往差强人意,滑动不够顺畅,没有回弹效果
better-scroll是一个基于iscroll,使用原生js包装的屏幕滑动插件,目前已经支持Vue
但是在Vue2中似乎并不能很好的支持better-scroll2.0,会出现各种各样的bug,于是在该项目中最终使用的是better-scroll1.0
安装方法
npm install better-scroll@1.13.0
使用方法
better-scroll的基本原理是一个固定大小的外层wrapper盒子,里面包含的是content盒子,content必须要比wrapper盒子更大,这样才能产生滚动效果。
<div class="wrapper" ref="wrapper">
<div class="content">
<slot></slot>
</div>
</div>
better-scroll最好在mounted()生命周期函数中进行初始化,因为此时DOM元素已经创建完毕,为了保证,可以将初始化放进$nextTick()回调函数中
mounted() {
this.$nextTick(() => {
this.bScroll = new BScroll(this.$refs.wrapper, {
click: true,
});
}
)
},
一个简单的better-scroll滑动组件就创建好了,该插件还支持很多API,例如下拉刷新等
事件总线Vue中组件通信可以通过$emit发送方法,但是当组件之间的关系比较复杂的时候,就不那么方便
因此,可以使用事件总线
使用方法
首先在main.js里面,给Vue实例新建一个属性,该属性的值为一个新的Vue实例
Vue.prototype.$bus = new Vue()
如果在任何一个组件中需要通过emit传递方法,则可以
this.$bus.$emit('show', 'show me')
在另一个组件中通过on来加载方法
this.$bus.$on('show', message => {
console.log(message)
})
事件总线的一个注意事项
当在生命周期钩子里使用事件总线的过程中,发现事件总线会触发多次,调试无果,遂去百度,得到以下结论:
如果事件注册在外部总线上,则需要在
beforeDestroy
或中将其拆除destroyed
。如果这变得重复,您可以编写一个 mixin 来自动执行此操作。
也就是说,在我退出界面时,并没有将事件销毁,那么在下次创建组件时在生命周期中又会再次触发事件。
防抖动函数在有些时候,比如输入框输入关键字进行查询时,如果使用onchange每次改变都向后台请求数据的话,这样消耗的资源是很大的
如果有这样一个需求,在进行关键字输入时,如果间隔的时间很小,那么就不需要请求多次只需要请求一次
这就是防抖动,我们可以设置一个定时器,在间隔的时间很短的情况下,取消前面一次发送请求,而是在时间间隔比较大的时候发送请求
export function debounce(func, delay) {
let timer = null;
console.log(timer);
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func()
}, delay)
}
}
这里其实是使用了闭包,总结各个书籍和博文,闭包其实就是一个函数,假设这个函数为fun2,这个函数通常在另一个函数fun1的作用域中,因此,fun2能够访问到fun1的变量。
那么在fun1函数作用域外调用fun2的话,就可以通过fun2来访问fun1的变量。同时,因为fun2在fun1的作用域中,所以fun2的存在依赖于fun1,所以fun1会一直在内存中,同时fun1里面的变量也会得以保存。
function debounce(func, delay) {
let timer = null;
console.log(timer);
return function () {
console.log('time show');
clearTimeout(timer);
timer = setTimeout(() => {
func()
}, delay)
}
}
const show = debounce(() => {
console.log('hello world');
},500);
for (let index = 0; index < 30; index++) {
show()
}
例如这个例子,这里其实是使用了两个闭包,第一层闭包是debounce以及它的返回值,第二层是返回值与setTimeout定时器。show保存的是第一层闭包,因此timer不会重复定义,整个debounce会被内存保存。
回到for循环中,show函数被执行,其执行的是debounce的返回值,也就是匿名函数,由于debounce还在内存中,所以show函数能够接收到参数func和delay。
此时进入第二个闭包,也就是show与setTimeout,setTimeout的定义是这样说的:
内置的工具函数setTimeout(...)持有对一个参数的引用,这个参数也许叫fn或者func,或者其他类似的名字,引擎会调用这个函数,在例子中就是内部的timer函数,而词法作用域在这个过程中保持完整
也就是说setTimeout函数里面传入的函数会被引擎自动执行,也就满足了闭包外部调用函数的条件,因此show的作用域也得以保存,所以timer的值不会被清空,在500ms的延迟内,下个show函数执行时,如果timer定时器存在,那么就删除定时器timer并重新定义
其实这个debounce函数,应该关注的是它返回值而不是它本身,因为这个函数它只会执行一次,被多次执行的是它的返回值,它的返回值才是真正应该注意的闭包,外层这个debounce闭包,其实就是为了保存第一次的timer,因为如果不这样将timer定义在函数内,那么就只有作为一个全局变量了,这时非常危险的
同时可以去chrome里面进行调试,可以发现,重复执行的其实是debounce的返回值,其本身只初始化了一次
这篇文章讲得很明白:
Data存储的值
data中一般存储的是后台的数据或者个别插件的配置
在data中存储后台传递过来的值的时候,一般是先在data里面定义一个空值,然后create函数中进行axios请求,将请求到的值赋予data里面的属性值
这里要注意,切不要在data中直接存储一个大的对象,这样在进行props传参数的时候,只能通过对象属性的方式传递参数,由于子组件不知道父组件传递过来的是什么数据类型,因此会报错
margin遇到的坑当margin应用于行内元素时,会出现margin-top和margin-bottom失效的问题,具体什么原因不得而知
当使用margin时,需要将行内元素转换为块级元素或者行内块元素
同时,使用margin时,还需要注意外边距合并的问题
时间戳格式化时间戳格式化需要先将时间戳转换为标准时间格式:
注意时间戳在JavaScript中要乘1000
const time = new Date(time * 1000);
//const time = new Date(1535697272 * 1000);
接着有两种方式可以转换
通过JavaScript内置方法
例如:
options = {
year: 'numeric', month: 'numeric', day: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric',
hour12: false
};
console.log(time.toLocaleString('zh-CN', options));
JavaScript内置了许多的格式化方法,还有DateTimeFormat
等等,这样做的优点是代码简洁,但是同时也会有转换不够灵活,部分浏览器不支持等问题
通过自定义函数(正则表达式)
自定义一个formatDate函数:
function formatDate(date, fmt) {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
}
}
return fmt;
};
function padLeftZero(str) {
return ('00' + str).substr(str.length);
}
const formatTime = formatDate(time, 'yyyy/MM/dd hh:mm');
console.log(formatTime);
formatDate接收两个参数,第一个参数为实例化Date对象,第二个参数为指定显示的时间格式
Vue插件lazyload
安装方式
npm install vue-lazyload --save
使用方式
一、导入lazyload
import VueLazyLoad from "vue-lazyload";
二、使用lazyload
const errorImage = require('./assets/img/common/errorImg.png');
const loadImage = require('./assets/img/common/lazyLoadImg.gif');
Vue.use(VueLazyLoad, {
preLoad: 1.3,
error: errorImage,
loading: loadImage,
attempt: 1
});
三、替换img标签的src属性
<img v-lazy="goodsItem.show.img" alt="">
fastclick
安装方式
npm install fastclick --save
使用方式
一、导入fastclick
import FastClick from 'fastclick';
二、使用fastclick
FastClick.attach(document.body);
postcss-pxtorem
安装方式
npm install postcss postcss-pxtorem --save-dev
注意:该插件依赖postcss,如果版本不正确便会报错
这里使用的版本为
"postcss": "^8.3.6",
"postcss-pxtorem": "^5.1.1",
使用方式
一、配置postcss.config
module.exports = {
plugins: {
// 兼容浏览器,添加前缀
'autoprefixer':{
overrideBrowserslist: [
'last 10 versions', // 所有主流浏览器最近10版本用
],
grid: true
},
'postcss-pxtorem': {
rootValue: 16, //结果为:设计稿元素尺寸/16,比如元素宽320px,最终页面会换算成 20rem
propList: ['*']
}
}
}
这里是将全部的px都转换为了rem,使用了通配符
二、配置rem.js
export default function getFontSize () {
const initFontSize = 16
const iPhone6Width = 375
const clientWidth = window.document.documentElement.clientWidth || iPhone6Width
const newFontSize = initFontSize * (clientWidth / iPhone6Width)
document.documentElement.style.fontSize = newFontSize + 'px'
}
这里是通过js获取到当前屏幕大小,并根据屏幕大小转换相应的root-font-size
三、导入rem.js并执行
import getFontSize from "@/rem";
(getFontSize)()
在主函数中导入该函数并立即执行