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
这个网站有相应的源码
https://github.surmon.me/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中的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中调用两次toGetShopCityGoods()方法接收到的数据会因为页码变化而变化。
但是出现的结果是两次都是同一个结果,通过在chrome中断点调试发现,Vue是在执行完两条语句之后才开始创建Vue实例。
由此猜测,之所以两次得到同样的结果是因为Vue实例还未创建,所以得到的page都是同一个,导致最后得到相同的结果
在组件上直接绑定方法使用常规的v-on并不可行,需要加上native属性
例如:
<back-top class="back-top" @click.native="backTop"></back-top>
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对象而是调用组件方法
在移动端如果使用原生的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的返回值,其本身只初始化了一次
这篇文章讲得很明白:
https://zhuanlan.zhihu.com/p/110733457
data中一般存储的是后台的数据或者个别插件的配置
在data中存储后台传递过来的值的时候,一般是先在data里面定义一个空值,然后create函数中进行axios请求,将请求到的值赋予data里面的属性值
这里要注意,切不要在data中直接存储一个大的对象,这样在进行props传参数的时候,只能通过对象属性的方式传递参数,由于子组件不知道父组件传递过来的是什么数据类型,因此会报错
当margin应用于行内元素时,会出现margin-top和margin-bottom失效的问题,具体什么原因不得而知
当使用margin时,需要将行内元素转换为块级元素或者行内块元素
同时,使用margin时,还需要注意外边距合并的问题
时间戳格式化需要先将时间戳转换为标准时间格式:
注意时间戳在JavaScript中要乘1000
const time = new Date(time * 1000); //const time = new Date(1535697272 * 1000);
接着有两种方式可以转换
例如:
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对象,第二个参数为指定显示的时间格式
安装方式
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="">
安装方式
npm install fastclick --save
使用方式
一、导入fastclick
import FastClick from 'fastclick';
二、使用fastclick
FastClick.attach(document.body);
安装方式
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)()
在主函数中导入该函数并立即执行