使用JavaScript和Vue简化代码的功能
一、前言
现在也算上暂时闲下来了。算算已经好久没写文章了。这篇文章记录下我使用的一些前端小功能吧。如有错误,请及时指出。
二、vue数字滚动小组件
开发中要用到数字上下滚动的组件。在github
上找了找这种功能。找到了一个vue-digitroll。周末花了一下午的时间研究了vue-digitroll
的源码,很不错。vue-digitroll
而且还做了浏览器兼容。最终没有用在项目里,原因有三点:
- 项目中暂时不需要考虑这种兼容性。
- 项目中也不需要这么多的功能。
vue-digitroll
虽然很轻量,但毕竟也要安装。安装了就要多少占点体积。
基于上面三点考虑,我就参考了源码实现,自己写了一个简单的,易于理解的小组件。
大概原理就是数字转为字符串,数字定高,宽度是自己的宽度。循环0到9,超出就往下排。通过overflow:hidden
隐藏超出的数字。通过传入的数字找到对应数字的高度位置。translateY
实现滚动效果。
下面就贴出来源码:
<template> <div class="roll-wrap" :style="{fontSize:`${cellHeight}px` }">
<ul class="roll-box">
<li
class="roll-item"
v-for="(item, index) in numberArr"
:key="index"
:style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}"
>
<!--小数点或其他情况-->
<div v-if="isNaN(parseFloat(item))">{{ item }}</div>
<div v-else :style="getStyles(index)">
<!--数字0到9-->
<div
:style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}"
v-for="(subItem,subIndex) in oneToNineArr"
:key="subIndex"
>{{ subItem }}</div>
</div>
</li>
</ul>
</div>
</template>
<script>export default {
props: {
// 高度,默认30
cellHeight: {
type: Number,
default: 30
},
// 需要传入的滚动数字
rollNumber: {
type: [String, Number],
default: 0
},
// 滚动持续时间,单位ms.默认1.5s
dur: {
type: Number,
default: 1500
},
// 缓动函数,默认ease
easeFn: {
type: String,
default: 'ease'
}
},
data () {
const { rollNumber } = this
return {
// 传入的数字
number: `${rollNumber}`,
// 传入的数字解析为数组
numberArr: [],
// 偏移量
numberOffsetArr: [],
// 0到9数组
oneToNineArr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
created () {
this.numberArr = this.number.split('')
this.resetState(this.numberArr.length)
},
watch: {
rollNumber (value, oldVal) {
this.number = `${value}`
this.numberArr = `${value}`.split('')
this.resetState(this.numberArr.length)
}
},
methods: {
resetState (len) {
const newArr = new Array(len).join(',').split(',')
this.numberOffsetArr = newArr.map(() => 0)
// 延迟执行动画
setTimeout(() => {
// 设置传入的数字下标对应偏移量,重新赋值
this.numberArr.forEach((num, i) => {
this.$set(this.numberOffsetArr, i, num * this.cellHeight)
})
}, 30)
},
getStyles (index) {
const style = { transition: `${this.easeFn} ${this.dur}ms`, transform: `translate(0%, -${this.numberOffsetArr[index]}px)` }
return style
}
}
}
</script><style lang="stylus" scoped>
.roll-wrap
ul.roll-box
display flex
padding 0
margin 0
text-align center
overflow hidden
li
overflow hidden
</style>
使用方式也很简单,如下:
<number-roll :roll-number="9999" />
三、前端JS计算丢失精度问题
具体参考 JavaScript 浮点数陷阱及解法和number-precision 这篇文章和number-precision开源库。
我也看了看源码,进行了一些测试,摘出来了一些,下面就贴一下我摘出来的源码:
/** * 解决浮点运算问题,避免小数点后产生多位数和计算精度损失。
*/
export default {
/*** 返回数字长度
* @param {*number} num Input number
*/
digitLength (num) {
const len = (num.toString().split('.')[1] || '').length
return len > 0 ? len : 0
},
/**
* 把小数转成整数,如果是小数则放大成整数
* @param {*number} num 输入数
*/
float2Fixed (num) {
return Number(num.toString().replace('.', ''))
},
/**
* 精确加法
* plus(0.1, 0.2) // = 0.3, not 0.30000000000000004
*/
plus (num1, num2) {
const baseNum = Math.pow(10, Math.max(this.digitLength(num1), this.digitLength(num2)))
return (num1 * baseNum + num2 * baseNum) / baseNum
},
/**
* 精确减法
* minus(1.0, 0.9) // = 0.1, not 0.09999999999999998
*/
minus (num1, num2) {
const baseNum = Math.pow(10, Math.max(this.digitLength(num1), this.digitLength(num2)))
return (num1 * baseNum - num2 * baseNum) / baseNum
},
/*** 精确乘法
* times(3, 0.3) // = 0.9, not 0.8999999999999999
*/
times (num1, num2) {
const num1Changed = this.float2Fixed(num1)
const num2Changed = this.float2Fixed(num2)
const baseNum = this.digitLength(num1) + this.digitLength(num2)
const leftValue = num1Changed * num2Changed
return leftValue / Math.pow(10, baseNum)
},
/*** 精确除法
* divide(1.21, 1.1) // = 1.1, not 1.0999999999999999
*/
divide (num1, num2) {
const num1Changed = this.float2Fixed(num1)
const num2Changed = this.float2Fixed(num2)
return (num1Changed / num2Changed) * Math.pow(10, this.digitLength(num2) - this.digitLength(num1))
},
/**
* 四舍五入
* round(0.105, 2); // = 0.11, not 0.1
*/
round (num, ratio) {
const base = Math.pow(10, ratio)
return this.divide(Math.round(this.times(num, base)), base)
}
}
四、async await 简化代码
因为项目里使用axios
进行了全局异常处理的提示,不需特殊处理的情况下,没有必要进行try{}catch{}
代码块包装了。因为大多数按钮提交的时候要增加loading,就可以使用fianlly
以下方式简化代码。
this.submitLoading = trueif(this.submitLoading) return
const res = await submitForm({name:'zhangsan',age:'20'}).finally(() => { this.submitLoading = false })
五、使用element的scroll-bar组件
很多开源库中都使用了element
的<el-scrollbar/>
组件。这个组件真的好用,如果你有定高但是需要显示滚动条实现滚动的需求。就可以很简单的实现好看的滚动条。比如如下面的代码:
<el-scrollbar style="height: 300px;"> <el-tree
:data="data"
/>
</el-scrollbar>
六、封装一些简单的搜索小组件
列表的搜索功能是必备的。在使用库的时候避免大量引入组件的标签,封装一些不那么复杂的搜索小组件,使用起来很方便。比如下面的代码:
<template> <el-row type="flex" align="middle">
<el-col :span="24">
<el-form @keyup.enter.native="querySearch()" @submit.native.prevent class="flex-center" :inline="inline">
<el-form-item v-for="(item,index) in formArr" :key="index" :label="item.label">
<el-select v-if="item.tagName === 'select'" v-model="item.value" placeholder="请选择">
<el-option v-for="item in item.options || []" :key="item.value" :value="item.value" :label="item.label" />
</el-select>
<el-input v-else v-model="item.value" :placeholder="item.placeholder"></el-input>
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="warning" @click="querySearch()">查询</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</template>
<script>
import { deepCopy } from 'utils/utils'
export default {
props: {
searchColumns: {
type: Array,
default () {
return []
}
},
inline: {
type: Boolean,
default: true
}
},
data () {
return {
formArr: []
}
},
methods: {
querySearch () {
const obj = {}
this.formArr.forEach(el => {
obj[el.prop] = el.value
})
this.$emit('query-search', obj)
}
},
watch: {
searchColumns: {
handler (val) {
this.formArr = deepCopy(this.searchColumns)
},
deep: true,
immediate: true
}
}
}
</script>
使用起来也很简单:
<simple-search :searchColumns="searchColumns" @query-search="querySearch" />export default {
data () {
return {
// 搜索条件
condition: {}
// 搜索列
searchColumns: [
{
label: '名称',
prop: 'name',
value: '',
placeholder: '请输入名称'
}
}
},
methods: {
querySearch (queryForm) {
this.condition = queryForm
this.getList()
}
}
}
七、使用Vue的mixins简化代码
mixins
真是个好东西,善于使用mixins
可以简化不少代码。加快Vue项目的开发速度这篇文章挺不错。但是mixins
不能滥用,不要在全局中使用。
因为大部分后台列表页面都要请求列表,都要分页,加载loading等,我们没有必要在每个Vue组件下面都写这些属性。下面是我用mixins
的实现了一些简化这些代码的功能(基于ElementUI)。如果每次切换路由的时候,需要记住当前用户离开这个列表页面之前的页码,可以使用localStorage
来存储页码。
/* * 分页mixins
*/
export default {
data () {
return {
// 分页
pagination: {
// 当前页
page: 1,
// 页长
size: 10,
// 总个数
total: 0,
// 分页布局
layout: 'prev,pager,next,total,jumper'
},
// 增,删,改按钮loading
load: {
addLoading: false,
deleteLoading: false,
editLoading: false
},
// 列表loading
listLoading: true
}
},
created () {
if (!(Object.prototype.toString.call(this.getList) === '[object Function]')) {
throw new Error('请在组件内定义getList方法加载数据!')
}
},
methods: {
// 改变页码handle
pageChange (val) {
this.pagination.page = val
this.getList()
},
// 移除一条数据后重新获取列表数据
getListForDelSingle (list = [], index = 0) {
list.splice(index, 1)
// 如果当前页无数据
if (list.length <= 0) {
this.pagination.page--
if (this.pagination.page <= 0) {
this.pagination.page = 1
}
}
this.getList()
},
// 移除多条数据后重新获取列表数据
getListForDeltMany (delLen, listLen) {
if (!delLen || !listLen) return
if (delLen >= listLen) {
this.pagination.page--
if (this.pagination.page <= 0) {
this.pagination.page = 1
}
}
this.getList()
}
}
}
八、一些简单的工具方法
如果说项目中没有还安装lodash
的话,都可以加以下的,很轻量,很好用。能够节省很多时间。还有就是好多开源库的工具方法都非常棒,比如说Element
,iview
,ant-design
,vant
等都可以参考学习或者在项目中直接拿来用。
- 一些
form
表单对象有很多时候需要初始化,如果手写代码一行一行的修改的话,代码会非常冗余。如果说form
表单不是很复杂的话,就可以用下面这种方式实现表单初始化效果:
this.userForm.name = ''this.userForm.pwd = ''
function initForm (form = {}, init = { num: 0, str: '' }) { const newForm = {}
const toString = Object.prototype.toString
for (const [key, value] of Object.entries(form)) {
if (toString.call(value) === '[object String]') {
newForm[key] = init.str
} else if (toString.call(value) === '[object Number]') {
newForm[key] = init.num
} else if (toString.call(value) === '[object Array]') {
newForm[key] = []
} else if (toString.call(value) === '[object Object]') {
newForm[key] = {}
}
}
return newForm
}
- 树结构转为一维数组
function getFlattenDeepList (nodes = []) { let list = []
nodes.forEach(item => {
list.push(item)
if (item.children && item.children.length) {
const tempList = getFlattenDeepList(item.children)
list = [...list, ...tempList]
}
})
return list
}
- 根据最子项ID获取所有对应的树级父级ID
function getParentIdListByLeafId (leafId, nodes = [], newNodes = []) { if (!leafId) return []
for (let i = 0, len = nodes.length; i < len; i++) {
const tempList = newNodes.concat()
tempList.push(nodes[i].id)
// 找到匹配返回结果
if (leafId === nodes[i].id) {
return tempList
}
if (nodes[i].children && nodes[i].children.length) {
const result = getParentIdListByLeafId(leafId, nodes[i].children, tempList)
if (result) return result
}
}
}
- 一维数组转树状结构
let arr = [ { id: 1, pid: '', name: '1AA' },
{ id: 2, pid: '', name: '2AA' },
{ id: 3, pid: '', name: '3AA' },
{ id: 4, pid: 1, name: '4AA' },
{ id: 5, pid: 2, name: '5AA' },
{ id: 6, pid: 3, name: '6AA' },
{ id: 7, pid: 4, name: '7AA' },
{ id: 8, pid: 1, name: '8AA' },
{ id: 9, pid: 5, name: '9AA' }
]
const newArr = []
arr.forEach(el => {
el.children = []
if (!el.pid) {
newArr.push(el)
} else {
const parent = arr.find(_ => _.id === el.pid)
parent.children.push(el)
}
})
九、关于正则表达式
正则表达式本身是很复杂的(其实我也不是很懂…),关于需要正则表达式来验证的功能。如果项目时间比较紧,拿一些比较严谨的开源库里的正则直接用是可以的。推荐铁皮铁皮饭盒老师的正则大全这个库,几千个star
。应该是经过很严谨的验证的,不要在通过网上随便搜出来的正则拿来直接用,我始终感觉不是正确的。不过正则确实是应该抽出一大段时间好好学的。
十、使用Vue语法糖
使用好Vue
的语法糖(v-model
,v-on
,v-bind
)也可以简化代码的编写。比如说要封装<el-select/>
组件修改样式或进行特殊定制。就可以像下面代码这样(参考自Vue-Element-Admin
):
<template> <el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" v-on="$listeners" multiple">
<slot />
</el-select>
</template>
<script>import Sortable from 'sortablejs'
export default {
name: 'DragSelect',
props: {
value: {
type: Array,
required: true
}
},
computed: {
selectVal: {
get() {
return [...this.value]
},
set(val) {
this.$emit('input', [...val])
}
}
}
}
</script>
<el-drag-select v-model="value" style="width:500px;" multiple placeholder="请选择"> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-drag-select>
<script>import ElDragSelect from '@/components/DragSelect' // base on element-ui
export default {
name: 'DragSelectDemo',
components: { ElDragSelect },
data() {
return {
value: ['Apple'],
options: [{
value: 'Apple',
label: 'Apple'
}]
}
}
}
</script>
十一、关于Vue生命周期
每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新DOM等。其实理解了初始化顺序,就可以知道在钩子函数里该做什么事情了。这里参考Vue生命周期。
十二、关于文章排版
文章排版使用MD2All。使用起来很简单,把在掘金上写的文字和代码复制到左侧黑色区域,然后点击一键排版,然后点保存,复制带样式的文字和代码后,在粘贴回来就可以了。注意以下,最好自己原来的文章留个备份,不然生成的很多样式代码无法理解。
有一个坑需要注意以下当使用``来解析代码的时候,注意后面不要加
html或者
js了。不然
MD2All`解析不友好。
以上是 使用JavaScript和Vue简化代码的功能 的全部内容, 来源链接: utcz.com/a/21928.html