一、前语
现在也算上暂时闲下来了。算算现已好久没写文章了。这篇文章记录下我运用的一些前端小功用吧。如有过错,请及时指出。
二、vue数字翻滚小组件
开发中要用到数字上下翻滚的组件。在github
上找了找这种功用。找到了一个vue-digitroll。周Q O 4 E末花了一下午的时刻研究了vue-dJ % k $ * ] { Oigitroll
的源码,很不错。vue-digitroll
而且还做了浏览器兼容。& X 9 3 @最终没有用在项目里,原因有三点= D c N o u b g U:
-
项目中暂时不需求考虑这种兼容性。 -
项目中也不需求这么多的功用。 -
vue-digitX J , ~ B Q Iroll
尽管很轻量,但毕竟也要装置。装置了就要多少占点体# 9 D , O积。
基于上面三点考虑,我就参阅了源码完成,自己写了一个简略的,易于了解的小组件。
大约原理$ + v s = I便是数字转为字符串,数字定高,宽度是自己的宽度。循环0到9,超出就往下排。通过
overflow:hidden
隐藏超出H 6 N S [ @ V ? G的数字。通过传入的数字找到f ! { 5 ~对应数字的高度方位。translateY
完成翻滚作用。
下面就贴出来L 5 H T y源码:
&P ; M ~ + tlt;template>
<4 K !;div class="rol$ { F ^l-wrap" :sj x C t ztyle="{fontSize:`${cellHeight}px` }">
<ul class=| @ l ~ O v"roll-box">
<li
class="roll-item"
v-for="(item, index) in nuA I 0 ; ] M 3 ymu / [ 3 { 0 ] : CberArr"
:key="index" q s 5 ` c y /
:style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}v y ! N ` R n"
>
<!-z c A p-小数点或其他状况-8 M ; k a ! 4 4->
<div v-if="isNaN(parseFloa{ L Bt(item))">{{o X } d ; 5 W item }}</div>
<div vg K * O Y , %-else :style="getStyles(index)">
<!--数字0到9-->
<div
:style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}"
v-for="(subc F I T V - 3 ^Item,subIndex) in onY y xeToNineArr"
:key="subIndex"
>{{ subItem }}</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
// 高度,默认30
cellHeight: {
type: Nu: A q tmber,
default: 30
},
// 需求传入的翻滚数字
rollNumber: {
type: [String, Number],
default: 0
},
// 翻滚持续4 Z 1 R J时刻,单位ms.h ~ U _ I (默认1.5s
dur: {; % ` ~ T 5 =
type: Number,
defaum c a h Dlt: 1500
},
// 缓动函数,默认ease
easeFn: {
type: String,
d8 i m gefault: 'ease'
}
},
data () {
const { rollNumber } = this
return {
// 传入的数字
number: `${roQ ^ U [llNumber}`,
// 传入的数字解析为] Y [ F 5数组
numberArr: [],
// 偏移量
numberOffsetArr: [],
// 0到9数组L s O y u K
oneToNineArr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
created () {
this.numberArr = this.number.split('K R 7')
this.resetState(this.numberArr.length)
},
watch: {
rollNumber (value, oldVal) {
tb b @ 1his.number = `${value}`
thi} A zs.numberArr = `${value}`.splitA G q ( 1 { $ G('')
this.resetState(this.numberArr.length)
}
},
methods: {
resetState (len) {
const newArr = new Array(len).join(',').split(S f h g J # ',')
this.numberOffsetArr = newArr.map(() => 0)
// 延迟P | ? v执行动画
setTimeout(() => {
// 设置传入的数字下标对应偏移量,从头赋值
this.numberArr.forEach((num, i) => {
this.$set(this.numberOffsetArr, i, num * this.cellHeight)
})
}, 30)
},
getStyles (index) {
const style = { transition: `${this{ M m z.easeFn} ${this.dur}ms`, transform: `translate(o D F i o Z 5 q r0%, -${this.numberOffsetArr[index]}px)` }
ra K ! seturn style
}
}
}
</script>
<style lang="styl0 ! lus" 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 浮点数圈套及解法和num _ V G / ; Kber-precision 这篇文章和number-precision开源库。
我也看了看源码,进行Q 6 f 9了一些测验,摘出来了一些,下面就贴一下我摘出来的源码:
/**
* 处理浮点运算问题,防止小J x 6 M T K数点后J H O } V C产生多位数和核算精度损失。
*/
export default {
/*9 x k N 8 } ) m*
* 回来数字长度
* @param {*number} num Input number
*/
digitLength (num) {
const len = (num.toString().split('.')[1] || '').length
retu+ q , Yrn len > 0 ? len : 0
},
/**O @ e C [ R
* 把小数转成整数,假如是小g p j s u ?数则放大成整数
* @param {*number} num 输入数
*/
float2Fixed (num) {
return Number(num.toS@ 3 Wtring().replace('.', ''))
},
/**
* 准确加法
* pluI ] K x ! 3 e k *s(0.1, 0.2) // = 0.3, not 0.30000000000000004
*/
plus (num1, num2) {
const baseNum = Math.pow(10, Mathy $ O } b.max(this.dig# W C / 5 ` ( jitLength(num1), this.digitLength(num2)))
return (num1 * baseNum + num2 * baseNum) / baseNum
},
/**
* 准确减法
* minL 7 u M ~ $ j vus(1.0, 0.9) // = 0.1, not 0.09999999999999998
*/
minus (num1,m S 6 , ^ P h % { num2) {3 ` E
cA $ #onst baseNum = Math.pow(10, Math.mO S x ) # R yax(this.digitLength(num1), this.digitLength(nuZ I K x N ,m2)))
return (num1 * baseNum - num2 * baseNum) / baseNum
},
/**
* 准确乘法
* times(3, 0.3) // = 0.9, not 0.8999999999999999
*/
times (num1, num2) {
const num1Changed = this.float2Fixed(num1)
const num2C: n 4 { C : 9hanged = this.float2Fixed(num2)
const b9 R t $ , Q 1 0 maseNum = this.digitLength(num1) + this.dig4 u WitLength(num2)
const leftValue = num1Changed * num2ChaQ ) I 6 ( v D ,nged
return leftValue / Math.E j S { +pow(10, baseNum)
},
/**
* 准确除法
* divide(1.21, 1.1) // = 1.1, not 1.0999/ * V / ^ } % 0 G999999999999
*/
divide (num1, num2) {
const num1Changed = this.float2Fixed(num1)
const num2Changed = this.float] + B g S W G q r2Fixed(num2)
return (num1Changed / num2Changed) * Math.pow(10, thi3 $ Ks.digitLength(num2)( ~ W d G e L g - 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{}
代码块包装了。由于大多7 l ) W ( j数按钮提交的时分要添加loading,就能够运用fianlly
以d , f 4 h下方法简化代码。
this.submitLoading = true
if(this.submitLoading) return
const res = await su; d m 4 C 2 d LbmitForm({name:'zhangsan',age:z g N 1 = 2 : ;'20'}).finally(() => { this.submitLoadingb 9 z K F K t n = false })
五、运用element的scroll-bar组件
许多开源库中都运用了: 8 &
element
的<el-scrollbar/>
组件。这个组件真的好用,假如你有定高可是需求显现翻滚+ r P U条完成翻滚的需求。就能够很简略的完成好看的翻滚条。比如如下面的代码:
<el-scrollbar style="height: 300px;">
<el-tree
:data="data"
/>
</el-scro? 1 Mllbh G J & p 7 # |ar>
六、封装一些简略的查找小组件
列表的查: 6 t m Q H ; P e找功用是必G R E备的。在运用库的时分防止大量引进组件的标签,封装一些不那么杂乱的查找小组件,运用起来很便$ , = ~ L u Y & t利。比如下面的代码:
<template>
<el-row type="flex" align="middle@ : s {">
<el-col :span="24">
<el-form @keyup.enter.native="querySearch()" @submit.native.preveH 4 4nt class="flex-center" :in^ e c L s e `line="inline">
<el-form-item v-for="(item,index) in formArr" :key="index" :label="item.label"n ! Y W g t } e>P Z 8 g;
<el-select v-if="item.tagName === 'seG X f f = Ilect'" v-model="item.value" placeholder="请挑选">
<el-option v-for="item in item.options || []" :key="item.value" :value="item.vH R 5 ealue" :label="item.labeI C ~ wl" />
</el-select>
<el-l t , S , ( y j Minput v-else v-model="itemU 3 O.value" :placeholder="item.placehold+ ` 6 wer"></el-input>
</el-fo{ X ;rm-item>
<z P G / b _ F;el-form-item&gr * & & ( }t;
<el-button icon="el-icon-search" type="warning" @I r ] ^ r % T #click="querySearch()"I 3 s Z B>查询&s C [ P 1 d ? *lt;/el-button>
&0 _ C ` ` dlt;/el-form-item>
</el-form>
</el-col>
</el-row>
</template>
<8 3 };script>
import { deepCopy } from 'utiw ` , h 8 ` els/utils'
expw w 9 9 R q $ort default {
props: {
searchColumns: {
type: Array,
default () {
return []
}
},
inline:2 ] { r 8 F {
typ# f 5 3 q 1 * q )e: Boolean,
default: true
}
},
data () {
rr 3 / beturn {
form^ ) _ % +Arr: []
}
},
met - +hods: {
querySearZ e B Q D S h 1ch () {
const obj = {}
this.formArr.forEach(el => {
obj[el.prN 2 n 2op] = e^ / } X [ J c a 3l.value
})
this.$emit('qA * e ( F P 6 -uery-search', obj)
}
},
watch: {
searchColumns: {
handler (val) {
this.formArr = deepCopy(this.searchColumnsL r S | D y + | _)
},
deep: true,
immediate: true
}
}
}
</script>
运用起来也很简略:
<simple-sM / D K S + x N Vearw ( ! - C ,ch :searchColumns="searchColumns" @query-search="querySearch" />
expo| m e k }rt default {
data () {
return {
// 查找条件
condition: {}
// 查找列
searchColumns: [
{
label: '称号',
prop: 'name',
value:7 L b @ . # '',
placeholder: '请输入称号'
}
}
},
methods: {
querySearch (queryForm) {
this.condition = queryForI - Q d P 6 Vm
this.gV e ,etC k H 9 V m hList()
}
}
}r % , D P n j
七、运用Vue的mixins简化代码
mixins
真是个好东西,善于运用mixins
能够简化不少代码。加快Vue项目的开发速度这篇文章挺不错。可r % ^ R 2是mixins
不能乱用,不要在大局中运用。
由于大部分} z q v ~ S c R后台列表页面都要恳求列表,都要分页,加载loading等,我们没有# Z 必要在每个Vue组件下面都H j o写这些属性。下面是我用mixins
的完成了一些简化这些代码的功用(基于ElementUI)。假如每次切换路由的时分,需求记住当前用户离开这个列表页y O – v $ h i b面之前的页码,能够运用localStorage
来存储页码。
/*
* 分页mixins
*/
export default {
data () {
return` u 2 . F r {
// 分页
pagination: {
// 当前页
page: 1,
// 页长
size: 10,
// 总个} 6 ~ 3 数
total: 0,
// 分页布局
layout: 'prev,pager,next,total,jumper'
},
// 增,删,改按钮loading
load: {
addLoading: fal, f Zsl U c 4 ` V _ T Le,
deleteLoadingt ^ w T: false,
editLoading: false
},
// 列表loading
l! ( j 2 E ; 5istLoading: true
}s U & 4 6 K
},
created () {
if (!(Object.prototype.toString.call(this.getList) === '[object Function]')) {
throw new Errc a k I _or('请在组件内界说getList方法加载数据!')
}
},
methods: {
// 改动页码handle
pageChange (val) {
tz * _ C B T xhis.pagination.page =n 1 s val
this.getList()
},B ? 2
// 移除一条数据后从头获取列表数据
getListForDelSingle (list = [], indeo j &x = 0) {
list.splice(index, 1)
// 假如当前页无数据] x # x ! 9 : I
if (list.length <= 0)K H - 7 A s ! {
thw n F l % x a 4is.pagination.page--
if (this.pagination.page <= 0)A p & % T Y T {
this.pagination.page = 1
}
}
this.getList()
},
// 移除多条数据后从头获取列表数据
getList& y kForDeltMany (delLenW ? a U R, listLen) {
if (!delLen || !listLen) return
if (delLel R k n O u I 1n >= listLen) {
this.pagination.page--
if (this.pagination.t v [ i x Kpage <= 0) {
this.pagination.page = 1
}
}
this.getList()
}
}
}
八、一些简( o # ? $ J W略的东西方法
假如说项目中没有还装置
lodash
的话,都能够加以下的,很轻量,很好用。能够节约许多时刻。还有便是许多开源库的} { t 6东西方法都十分棒,比如说Element
,iview
,ant-design
,vant
等都能够参阅学习或许在项目中直接拿来用。
-
一些 form
表单! G # *目标有许多时分需求初始化,假如手写代码一行一行的修改的话,代码会十分冗余。假如说form
表单不是很杂乱的话,就能够用下面这种方法完成表单初始化作用:
this.userForm.name = ''
this.userForm4 b R h S r l e.pwd = ''
// ...
function initForm (form = {}, init = { num: 0, str: '' }) {
const newForm = {}
const toString = Obs n Q rject.prototype.toString
for (const [key, value] of Objecth J t X F h.entries(form)) {
if (toString.call(value) === '{ A 7[object String]') {
newForm[key] = init.str
} else if (toString.call(value) === '[object Numb. 5 O h ^ y =er]R } } N c P 8 `') {
newForm[key] = init.num
} else if (toString.call(f k | r V hvalue) === '[object Array]') {
newForm[key] = []
} else if (toString.call(value) === '[object Object]') {
newForm[key] = {}
}
}f 7
return newForm
}
-
树结构转为一维数x o }组
function getFlattenDeepList (nodes = []) {
let list = []
nodes.forEach(item => {
list.push(item)
if (item.children && iE Y 4 H ~teL 6 Hm.children.length) {
const tempList = getFlattenDeepList(item.children)
list = [...l+ ; ] X Aist, ...tempList]
}
})
return list
}
-
根据最子项ID获取所有对应的树级h D & 0 ! 8 C g父级ID
function_ 3 a x getParentIdListByq e T n K =LeafId (leafIA V : 8d, nodes = [], newNodes = []) {
if (!leafId) return []
for (let i = 0, len = nodes.length; i < len; i++) {
consX ! b e S O w l -t tempList = newNodes.concat()
tem F v = 9 a F ` spList.push(nP 3 odes[i].id)
// 找到匹配回来结果
if (leafId === nodes[i].id) {
return tempList
}
if (nodes[i].children &f D w ; Bamp;& nodes[i].children.length) {
const result = getParentIdListByLeafId(leafId, nodes[i].children, tempList)
if (result) return result
}
}
}
-
一维数组转树状结构
let arr = [
{& E z g s j U } id: 1, pid: '', name: '1AAV 0 !' },
{ id( f n o D a # W: 2, pid: '', name: '2AA' },
{ id: 3, pid: '',f F M L name: '3AA' },
{ id: 4, pid: 1, name: '4AA' },
{ id: 5, pid: 2, name: '5AA' },
{ id: 6, pid: 3, name: '6AA' },
{ id] N 5 & ? ~ q: 7, pidV w + : ? : 4, name: '78 w h O q y L { (AA' },
{ id: 8, pid: 1, name: '8AA' },
{ id: 9, pid: 5, name: '9AA' }
]
const newArr = []
arr.forEach(es M J 0 b d C Ul => {
el.children = []
if (!el.pid) {
newArr.push(el)
} else {
const p5 p ~ n Z @ marent = arr.find(_ => _.id === el.pid)
pa2 5 y & Crent.children.push(el)
}
})
九、关于正则表达式
正则表达式本身是很杂乱的(其实我也不是很懂…),关于需求正则表达式来验证的功用。 e T 6 . 3假如项目时刻比较紧,拿一些比较谨慎的开源库里的正则直接用是能够的。推荐铁皮铁皮饭盒教师的正则大全这个库,几千个star
。应该是通过很谨慎的验证的,不要在通过网上随便搜出来的正e s Y则拿来直接用,我一直感觉不是正确的I R f o S l @ 9。不过正则确实是! = G a应该抽出一大段时刻好好学的d F k $ s A – ?。
十、运用Vue语法糖
运用好Vue
的语法糖(v-model
,v-on
,v-bind
)也能够简化代码q n s ; 6 q C的编写。比如说要封装<el-select/>
组件修( m } !改样式或进行特别定制。就能够像下面代码这样(v O * , o 1参阅自Vue-Element-Admin
):
<template>
<el-select ref="dragSelect" v-model="selectVal" v-bind=/ t d"$attrs" v-on="$listeners" multiple">
<slot />
</el-select>
</! X j J Stemplate>
<script>
import Sortable from 'sortablejs'
export deg & n a ! 5 L ofault {
name: 'DragSelect',
props: {
value: {
type: Array,L M N ~ ` U
required: true
}
},
computed: {
se^ q 6 ~ ) n rlectVal: {
get(` A j g J T) {
return [...this.value]
},
set(val) {
this.$emit('input', [...val])
}
}
}
}
</script>
<el-drag-select v-model="5 M $ ? mvalue" style="width:500px;" multiple placeholder="请挑选">
<el-option v-for="itemy - = B in option4 . : T B 0s" :key="item.value" :label="item.~ | A ? Ulabel" :value="item.value" />
</el-drag-sele; k Ict>
<script>
import ElDragSelecty * 1 y x 8 2 w from '@/components/Dm k Z g i I ,ragSe: / _ , 9 2 ^lect' // base on element-ui
export default {
name: '- & M a CDragSelectDemo',
components: { ElDragSelect },
data() {
return {
value: ['Apple'],
options: [{
value: _ g Y'Apple',
label: 'Apple'
}]
}
}
}
</script>$ } *;
十一、关于Vue生命周期
每个 Vuy ; P Ge 实例在被创建之前都要通过一系列的初始化进程。例如需求设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新DOM等。其实了解了初始化次序,就能够知道在钩子函数里该做什么事情了。这儿参阅VuW } n ae生命周期。u g $ T V Z
本文运用 mdnice 排版