Javascript

VUE十个需要注意的细节(2.0)

TIP

关于为什么要使用VUE,很多已经熟悉并依赖 jquery 的前端小伙伴刚开始可能会不太能够理解,最开始我也是这么想的,主要原因还是开发的项目页面不够复杂,当一个页面无刷新交互以及组件足够复杂之后,你会发现用 jquery 将很难进行下去,更不用说 单页应用(spa)

这里我想要再次强调一个小细节,虽然在开发 VUE 的时候会被经常用到,当绝不一定只是在开发 VUE 才会运用到

当我们对某个对象或者数组 computed 或者 watch 时候,往往会遇到需要对此变量需要预处理的操作,这时候有可能涉及遍历,显然如果我们之间遍历原对象或者原数组明显不太合适,对于一维数组对象我们可以之间使用 Object.assign([], arr)Object.assign({}, obj) or [...arr]{...obj}来进行浅拷贝,但是对于一维以上的数组或者对象建议参考 深拷贝

关于 VUE 和 React 哪家强的问题,就目前全球市场而言 React 的占比确实第一,不过 VUE 作为一个 个人项目 并且出现时间 晚于 React 的情况下 github 上星数可以超越 React 这样 大厂出品 依旧是让人震惊

个人感觉 React 至今并没有什么神奇的地方时 VUE 所无法取代的,相反 VUE 的许多优点 React 却没有,就 中国的市场 个人觉得 VUE 和 React 应该算的上 平分秋色 ,因为国内 web 项目对 MVVM 的应用相比国外还是有一定差距的,所以再较晚的使用 JS 关于 MVVM 框架有了更多的选择招聘就可以看的出来,别的城市我不知道,但是就北京和南京来说,招聘信息上面 有需求 会 VUE 或者 React 的,甚至单招 VUE 熟手的,缺很少看到单招 React 的

这里我想要给大家推荐一个快速学习上手使用 VUE 不错的一个开源项目后台管理系统

vue-element-admin这个项目目前看来在github上关于VUE的项目星数最多的一个,作者更新也非常活跃几乎每天都有新的 commit ,提出 Issues 基本上能够得到很快的回应,也非常荣幸可以成为该项目contributors之一,虽然提交的代码可能并不算特别重要,但是第一次提交的代码被合并的感觉真的是超级开心

1. v-if && v-show

  • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建

  • v-show 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好

TIP

template标签无法使用 v-show ,不信的可以试试

2. deep

对于watch一个对象或者数组时候,若需要深度 watcher 则一定需要设置 deep:true

对于watch一个对象或者数组时候,若需要深度 watcher 则一定需要设置 deep:true

对于watch一个对象或者数组时候,若需要深度 watcher 则一定需要设置 deep:true

TIP

重要的事情说三遍

3. v-bind

  • 简介: 只要 自己定义的 标签属性前面有 : 则后面引号里面写JS(变量、函数),否则写字符串

  • 作用: (:v-bind:语法糖 )

TIP

很多人学习是从官方教程开始看的,在看到 Class 与 Style 绑定 这章节时候可能会有些误解,这里需要强调一点 v-bind 不仅仅用于style或者class属性一切属性皆可用,这才是关键

4. 子父组件通信

  • 父向子:props <my-component :name="name"></my-component> or <my-component name="马云海"></my-component>,这里需要注意一点在你提交的代码中,prop 的定义应该尽量详细, 至少需要指定其类型 ,当然如果你非要偷懒 props: ['status'] 这样定义,也没人可以咬你

  • 子向父: 子组件使用 API vm.$emit this.$emit('res', val) ,父组件使用 <my-component @res="fun"></my-component> ,然后在 methods 里面定义 fun(val){} 来获取值

如果是在看不懂的可以百度 "VUE子父"网上有很多,不过我依旧觉得我这里介绍的是最精辟的,哈哈

5. Vue Router

  • 简介:

    • 一个配置文件,里面主要配置了SPA页面URL命名以及关联文件位置
    • this.$router里面主要定义了路由的相关信息以及跳转等方法
  • 作用: 管理SPA所有虚拟页面 配置、跳转、跳转历史

6. Vuex

  • 简介: Vuex特别像一个全局对象,他和全局对象的区别有以下两点
    • 响应式,数据变动更新组件
    • 不能直接修改,修改唯一途径mutation

TIP

mutation 必须是同步函数

action 类似于 mutation,不同在于:

  • action 提交的是 mutation,而不是直接变更状态
  • action 可以包含任意异步操作
  • 作用: 多页面(SPA虚拟页面)多组件数据及时同步、通信

7. nextTick && v-cloak

这两个点和 生命周期 mounted有点类似,当然功能以及用法上面肯定有一定的区别

  • nextTick

这个算是VUE里面比较少见,但是很重要的接口官方解释:在下次 DOM 更新循环结束之后执行延迟回调在修改数据之后立即使用这个方法,获取更新后的 DOM是不是感觉挺难理解的,所以这里我们直接上一个实例:

//当我们使用element UI时候,这里的作用是当element 组件el-input渲染完成后获取焦点
this.$nextTick(_ => {
  this.$refs.input.focus()
})

这里如果我们直接使用 this.$refs.input.focus() 是没用的不信你可以尝试一下,这里的很细节,若你不会并没人咬你,但是如果你注意这些细节会让用户体验发生质变

  • v-cloak

这里呢类似上面那个问题,一样的就算你不做处理程序不是跑步起来,只是体验不好并且这里多半用于一些非 SPA 项目上面,有些MVC项目为了追逐潮流硬是在MVC基础上使用 VUE Element 等套件时候,会发现一个诡异的事情,就是网页在加载的最初会出现未被VUE渲染的代码比如 {{name}} ,直到VUE编译完成才会被编译成你想要它展示的变量比如:小王

官方说明:

这个指令保持在元素上直到关联实例结束编译和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕

<style>
[v-cloak] {
  display: none;
}
</style>
<div v-cloak>
  {{ message }}
</div>

不会显示,直到编译结束

8. v-text && v-html

这两个指令有一定的区别,第一个HTML标签不会被渲染,第二个不会被VUE渲染,用到的地方虽然不多但是真当你遇上不会的话还是很恼火的

  • v-text

这个和使用“Mustache”语法 (双大括号) 的文本插值是一样的,细心的小伙伴会发现如果试图通过 {{html}} 来给标签插入html代码是不会被解析的

  • v-html

官方说明:

更新元素的 innerHTML 注意:内容按普通 HTML 插入不会作为 Vue 模板进行编译 如果试图使用 v-html 组合模板,可以重新考虑是否通过使用组件来替代

本项目是基于 vuepress 所以当我在这个当前页面需要输入{{name}}的时候,必须用下面这样的代码,如果直接使用写的话会被VUE编译,并提示报错找不到 name 变量

<span v-html="'{{name}}'"></span>

9. set

官方说明:

向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = 'hi' )

this.$set(this.myObject,'newProperty','hi')

10. 自定义组件强化

只要弄清楚以上9点对于VUE应用开发基本上就没有太大问题了,关于自定义组件的封装其实只要熟练掌握 子父组件通信 也足以解决日常开发需求,下面介绍的只是更深层次一点,对于新手而言不懂也没有太大关系尤其是slot插槽这块 VUE 3.0 会对此API有大幅度变动,虽然会有兼容方案

显而易见就是官方提供了一个 API 让你能够在自己封装组件上面也可以使用 v-model 双向绑定,这个细节多用于封装自己的表单类组件或者二次封装别人的表单类组件用,并且对 element 这样的UI 进行单组件的二次封装我觉得意义不是很大,感兴趣的可以研究一下,这里我就不做过多解释官网说明的很清楚点击标题可以快速跳转官方文档的相关位置,或者参考 sync-修饰符

TIP

虽然不是特别重点,但是这里如果只是对 value 属性的进行双绑封装还是极为方便的,新手可以了解一下,只需要在子组件新增 this.$emit('input', val) 这样的事件即可

<input v-model="searchText">
<!-- 实际上上面的代码是下面代码的语法糖。 -->
<!-- 当在父级组件监听这个事件的时候,我们可以通过 $event 访问到被抛出的这个值 -->
<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

最主要的作用就是可以和 HTML 元素一样,我们经常需要向一个组件传递内容。通常是给组件传递一些 HTML 代码。

<slot> 元素作为组件模板之中的内容分发插槽。<slot> 元素自身将被替换,也可以在封装组件时候预留一下默认值,多的不说直接上代码我觉得会比较直观一些。

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<div id="app">
  <alert-box>
    <b>Something bad happened.</b>
  </alert-box>
</div>
<script>
  Vue.component('alert-box', {
    template: `
      <div class="demo-alert-box">
        <strong>Error!</strong>
        <slot><b>这里可以定义默认值</b></slot>
      </div>
    `
  })
  const vm = new Vue({
    el: '#app'
  })
</script>

上面的代码会被解析成

<div class="demo-alert-box">
  <strong>Error!</strong>
  <b>Something bad happened.</b>
</div>

TIP

值得一提的这个 <slot></slot> 里面依然可以插入子组件并被解析

我先说一下使用场景,假设我们封装一个 使用echart渲染的图标组件,当我们这个页面多次使用此插件时候,你会发现如果你使用 this.chart = echarts.init(document.getElementById('chart')) 会出现id重复的情况,VUE就提供了一个dom选择器的API vm.$el 用来访问实例挂载之后的元素。

// ... 此处代码省略
mounted() {
  this.chart = echarts.init(this.$el)
  this.setOption(option)
},
// ... 此处代码省略

Array对象方法

基础处理

  • push()方法可向数组的末尾添加一个或多个元素,并返回新的长度
const arr = ['a']
console.log(arr.push(1)) //2 把指定的值添加到数组后的新长度
console.log(arr) //["a", 1]

  • pop()方法用于删除并返回数组的最后一个元素
const arr = ['a', 1]
console.log(arr.pop()) //1 数组最后一个元素
console.log(arr) //["a"]

  • unshift()方法可向数组的开头添加一个或更多元素,并返回新的长度
const arr = ['a']
console.log(arr.unshift(1)) //2 数组新长度
console.log(arr) //[1, "a"]

  • shift()方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
const arr = ['a', 1]
console.log(arr.shift()) //a 数组原来的第一个元素的值
console.log(arr) //[1]

  • slice(start,end)方法可从已有的数组中返回选定的元素。

TIP

该方法并不会修改数组,而是返回一个子数组。如果想删除数组中的一段元素,应该使用方法 Array.splice()

参数 描述
start 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推
end 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素
const arr = [0, 1, 2, 3, 4, 5, 6]
console.log(arr.slice(2, 3)) //[2]
console.log(arr.slice(2)) //[2, 3, 4, 5, 6]
console.log(arr.slice(-2)) //[5, 6]
console.log(arr.slice(-2, -1)) //[5]
console.log(arr) //不会修原数组  [0, 1, 2, 3, 4, 5, 6]

  • splice(index,howmany,item1,.....,itemX)方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素

TIP

该方法会改变原始数组

参数 描述
index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置
end 必需。要删除的项目数量。如果设置为 0,则不会删除项目
item1, ..., itemX 可选。向数组添加的新项目
const arr = [0, 1, 2, 3, 4, 5, 6]
console.log(arr.splice(2, 1, 9)) //[2]
console.log(arr) //[0, 1, 9, 3, 4, 5, 6]

const arr2= [0, 1, 2, 3, 4, 5, 6]
console.log(arr2.splice(2, 1)) //[2]
console.log(arr2) //[0, 1, 3, 4, 5, 6]

  • reverse()方法用于颠倒数组中元素的顺序

TIP

该方法会改变原来的数组,而不会创建新的数组

const arr = [0, 1, 2, 3, 4, 5, 6]
console.log(arr.reverse()) //[6, 5, 4, 3, 2, 1, 0]
console.log(arr) //[6, 5, 4, 3, 2, 1, 0]

  • concat(arrayX,arrayX,......,arrayX)方法用于连接两个或多个数组

TIP

该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本

const arr = [1,2,3]
console.log(arr.concat(3, 4)) //[1, 2, 3, 3, 4]
console.log(arr) //[1, 2, 3]

const arr2= [0, 1, 2]
const arr3= [0, 1]
console.log(arr2.concat(arr3)) //[0, 1, 2, 0, 1]
console.log(arr2) //[0, 1, 2]
console.log(arr3) //[0, 1]

  • fill()方法用于将一个固定值替换数组的元素
参数 描述
value 必需。填充的值
start 可选。开始填充位置(默认为 0)
end 可选。停止填充位置 (默认为 array.length)
const arr = [1,2,3]
console.log(arr.fill('a')) //["a", "a", "a"]
console.log(arr) //["a", "a", "a"]

const arr2 = [1,2,3]
console.log(arr2.fill('a', 1, 3)) //[1, "a", "a"]
console.log(arr2) //[1, "a", "a"]

  • copyWithin(target, start, end)方法用于从数组的指定位置拷贝元素到数组的另一个指定位置中
参数 描述
target 必需。复制到指定目标索引位置
start 可选。元素复制的起始位置(默认为 0)
end 可选。停止复制的索引位置 (默认为 array.length)。如果为负值,表示倒数
var arr = [0, 1, 2, 3, 4, 5];
console.log(arr.copyWithin(2, 0, 2)) //[0, 1, 0, 1, 4, 5]
console.log(arr) //[0, 1, 0, 1, 4, 5]

var arr2 = [0, 1, 2, 3, 4, 5];
console.log(arr2.copyWithin(2)) //[0, 1, 0, 1, 2, 3]
console.log(arr2) //[0, 1, 0, 1, 2, 3]

遍历

  • forEach(function(currentValue, index, arr), thisValue)方法用于调用数组的每个元素,并将元素传递给回调函数

TIP

forEach() 对于空数组是不会执行回调函数的

直接上例子说明一切

const arr = ['a', 'b'];
const arr2 = []
arr.forEach(function(v, i, arr) {
  console.log(v)
  console.log(i)
  console.log(arr)
})
// a
// 0
// ['a', 'b']
// b
// 1
// ['a', 'b']

arr.forEach(function(v) {
  this.push(v + '2')
}, arr2)
console.log(arr2) //["a2", "b2"]

  • map(function(currentValue, index, arr), thisValue)方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值

TIP

  • map() 不会对空数组进行检测
  • map() 不会改变原始数组
  • map() 返回一个新数组(不过一定不要忘记写return)
const arr = [{v: 1}, {v: 2}]
const arr2 = arr.map(function(element) {
  element.s = element.v * 2
  return element
})
console.log(JSON.stringify(arr)) //[{"v":1,"s":2},{"v":2,"s":4}]
console.log(JSON.stringify(arr2)) //[{"v":1,"s":2},{"v":2,"s":4}]

这里可能有人会觉得上面刚说的不会改变原始数组,这里怎么就改变了,因为这里是对象的地址没有被改变,具体可以看下例对比

const arr = [1, 2]
const arr2 = arr.map(function(element) {
  element = element * 2
  return element
})
console.log(JSON.stringify(arr)) //[1,2]
console.log(JSON.stringify(arr2)) //[2,4]

  • entries()方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)
const arr = [1, 2]
const iterator = arr.entries()
console.log(JSON.stringify(iterator.next())) //{"value":[0,1],"done":false}
console.log(JSON.stringify(iterator.next())) //{"value":[1,2],"done":false}
console.log(JSON.stringify(iterator.next())) //{"done":true}

判断

  • indexOf(searchvalue,fromindex)方法可返回某个指定的字符串值在字符串中首次出现的位置

TIP

  • 字符串也能使用
  • indexOf() 方法对大小写敏感
  • 如果要检索的字符串值没有出现,则该方法返回 -1
参数 描述
searchvalue 必需。规定需检索的字符串值
fromindex 可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的首字符开始检索
const arr = [1, 2]
console.log(arr.indexOf(1)) //0
console.log(arr.indexOf(3)) //-1
console.log(arr.indexOf(1, 2)) //-1
const str = 'abc'
console.log(str.indexOf('b')) //1

  • lastIndexOf()和indexOf()相反,方法可返回一个指定的字符串值最后出现的位置
const arr = [0, 1, 2, 1]
console.log(arr.lastIndexOf(1)) //3
console.log(arr.lastIndexOf(1, 1)) //1
console.log(arr.lastIndexOf(1, 0)) //-1
const str = 'babc'
console.log(str.lastIndexOf('b')) //2

  • includes()方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false
const arr = [1, 2, 3]
console.log(arr.includes(1)) //true
console.log(arr.includes(4)) //false

  • isArray()方法用于判断一个对象是否为数组
const arr = [1, 2, 3]
const str = 'abc'
console.log(Array.isArray(arr)) //true
console.log(Array.isArray(str)) //false

方法处理

  • filter(function(currentValue,index,arr), thisValue)方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素

TIP

  • 不会对空数组进行检测
  • 不会改变原始数组
const arr = [32, 33, 16, 40]
console.log(arr.filter((element) => {
    return element >= 18
})) //[32, 33, 40]


  • reduce(function(total, currentValue, currentIndex, arr), initialValue)方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值
const arr = [1, 2, 3]
console.log(arr.reduce((total, num) => {
    return total - num
})) //-4 (1 - 2 - 3)

  • reduceRight() 与上面相反
const arr = [1, 2, 3]
console.log(arr.reduceRight((total, num) => {
    return total - num
})) //0 (3 - 2 - 1)

方法判断

  • every()方法用于检测数组所有元素是否都符合指定条件

TIP

  • 不会对空数组进行检测
  • 不会改变原始数组
const arr = [32, 33, 16, 40]
const arr2 = [22, 23, 26, 20]
console.log(arr.every((element) => {
    return element >= 18
})) //false
console.log(arr2.every((element) => {
    return element >= 18
})) //true

  • some()方法用于检测数组中的元素是否满足指定条件

TIP

  • 不会对空数组进行检测
  • 不会改变原始数组
const arr = [32, 33, 16, 40]
const arr2 = [12, 13, 16,10]
console.log(arr.some((element) => {
    return element >= 18
})) //true
console.log(arr2.some((element) => {
    return element >= 18
})) //false

  • findIndex()方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置

TIP

  • 不会对空数组进行检测
  • 不会改变原始数组
const arr = [32, 33, 16, 40]
const arr2 = [12, 13, 20,10]
console.log(arr.findIndex((element) => {
    return element >= 18
})) //0
console.log(arr2.findIndex((element) => {
    return element >= 18
})) //2

转化

  • join数组转字符串 && split字符串转数组

这个算是比较常见处理方法,直接上例子了

const arr = ['a', 'b', 'c']
console.log(arr.join('=')) //a=b=c
console.log(arr) //["a", "b", "c"]
const str = 'a,b,c'
console.log(str.split(',')) //["a", "b", "c"]
console.log(str) //'a,b,c'

  • toString && toLocaleString(Date, number)方法可把数组转换为字符串,并返回结果
const arr = [12313, new Date()]
console.log(arr.toString()) //12313,Thu May 30 2019 13:48:52 GMT+0800 (中国标准时间)
console.log(arr) //[12313, Thu May 30 2019 13:48:52 GMT+0800 (中国标准时间)]
console.log(arr.toLocaleString()) //12,313,2019/5/30 下午1:48:52
console.log(arr) //[12313, Thu May 30 2019 13:48:52 GMT+0800 (中国标准时间)]
  • valueOf()是数组对象的默认方法

这个方法说实话至今不知道有什么用!!!

const arr = ['a', 'b', 'c']
console.log(arr.valueOf()) //["a", "b", "c"]
console.log(arr) //["a", "b", "c"]

web前端开发十个常用方法

本篇文章主要总结web前端开发过程当中一些相对比较细节的常用方法 (以下方法多使用ES6语法,若要使用需根据项目需求考虑兼容性问题)

1. 数组去重

function uniqueArr(arr) {
  return Array.from(new Set(arr))
}

运用场景:数组去重是一种比较常见的计算需求,方法也有很多种,这种算是利用ES6语法中Set对象比较优雅的一种,ES5其实也很简单提供一个思路:首选遍历然后利用 .indexOf() 去判断是否存在新的数组里面,如果不存在则插入新的数组即可

例如:

const arr = [1, 2, 3, 1, 1, 2, 3, 4]
console.log(uniqueArr(arr)) //输出[1, 2, 3, 4]

2. 去抖函数

function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result
  const later = function () {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp
    // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
    if (last < wait && last  0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }
  return function (...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    // 如果延时不存在,重新设定延时
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }
    return result
  }
}

运用场景:在规定延迟内连续触发只执行一次,如果第三个参数传true则在最开始就触发。去抖函数其实在很多UI中模糊远程查询都有集成,在拥有echart等(canvas或SVG图表插件)自适应页面中 window.addEventListener('resize', function)配合抖动函数在改变窗口大小时候对图表进行重绘会比较合适,有效减少触发重绘次数,保证良好用户体验

例如:

<button id="test"测试</button
<script
  const Hanlder = debounce(() = {
    console.log('触发成功') //连续点击按钮,在控制台即可看到效果
  }, 300)
  document.querySelector("#test").addEventListener("click", Hanlder)
</script

3. 格式化时间戳

function parseTime(time, cFormat) {
  if (arguments.length === 0) {
    return null
  }
  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if (('' + time).length === 10) time = parseInt(time) * 1000
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  }
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) = {
    let value = formatObj[key]
    if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
    if (result.length  0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  return time_str
}

运用场景:轻轻松松把时间戳或者时间日期对象转化成自己想要输出的格式,可以减少服务器运算同是若是时间输出格式变更需求无需更改后端,前端修改即可,或许这点计算并不算什么,但是积羽沉舟

例如:

const now = new Date()
//以'{y}-{m}-{d} {h}:{i}:{s}'格式化的当前时间
console.log(parseTime(now)) 
//以'{y}年{m}月{d}日 {h}时{i}分{s}秒'格式化的昨天时间
console.log(parseTime(now.getTime() - 3600 * 1000 * 24, '{y}年{m}月{d}日 {h}时{i}分{s}秒')) 

4. 千分号格式化数字

function fomatNumber(s, n) {
  n = n = 0 && n <= 20 ? n : 2
  s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + ""
  var l = s.split(".")[0].split("").reverse()
  r = s.split(".")[1]
  t = ""
  for (i = 0 i < l.length i++)
    t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? "," : "")
  if (n == 0)
    return t.split("").reverse().join("")
  else
    return t.split("").reverse().join("") + "." + r
}

运用场景:把过长的数字尤其是金额用千分号隔开,方便用户能够更快的识别数字金额大小

例如:

console.log(fomatNumber(23123.123) //23,123.12
console.log(fomatNumber(23123.123, 0) //123,123

5. 克隆一个对象或者数组(深拷贝)

function deepClone(source) {
  if (typeof source == 'object') {
    const targetObj = source.constructor === Array ? [] : {}
    Object.keys(source).forEach((keys) = {
      if (source[keys] && typeof source[keys] === 'object') {
        targetObj[keys] = deepClone(source[keys])
      } else {
        targetObj[keys] = source[keys]
      }
    })
    return targetObj
  }
}

运用场景:这个方法就是克隆一个对象或者数组,在开发当中接口返回的数据往往并不是特别理想,在需要对接口数据做处理运算的时候克隆往往是一个经常可以用的到的方法

例如:

const arr = [1, 2, 3, 4]
const obj = {a: 1, b: 2, c: 3}
const arrClone = deepClone(arr)
const objClone = deepClone(obj)
arr.push(6)
obj.a = 6
console.log(arr) //[1, 2, 3, 4, 6]
console.log(obj) //{a: 6, b: 2, c: 3}
console.log(arrClone) //[1, 2, 3, 4]
console.log(objClone) //{a: 1, b: 2, c: 3}

6. 计算字符串相对长度

function getByteLen(val) {
  let len = 0
  for (let i = 0 i < val.length i++) {
    if (val[i].match(/[^\x00-\xff]/ig) != null || (val[i] = 'A' && val[i] <= 'Z')) {
      len += 1
    } else { len += 0.5 }
  }
  return Math.floor(len)
}

运用场景:这个方法就是把所有中文字符算成1,其他字符串计算为0.5,然后向下取整(这里我们可以延伸一个有趣的问题 Math.floor和parseInt()的区别 有兴趣的小伙伴可以试试 Math.floor(-1.5)、parseInt(-1.5)、parseInt(1111,2))。看起来毫无意义但是在某些特殊场景还是很有必要,比如遍历一个未知的表格时候,对于表头的长度我们无法控制(接口或者计算返回)的情况下对于过长表头我们不想出现换行以及省略号的形式的时候,可以用次方法计算字符串长度然后乘以一个值来定义table-column的长度

例如:

console.log(getByteLen(1231231231231)) //0
console.log(getByteLen('1231231231231')) //6
console.log(getByteLen('asdasdasdasda')) //6
console.log(getByteLen('我就想测试一下')) //7

7. 相除

  function divide(a, b) {
    if ((a !== 0 && !a) || (b !== 0 && !b)) {
      return 0 //这里当a或者b不存在时默认输出为0,可根据需求自行修改
    } else {
      a = parseFloat(a)
      b = parseFloat(b)
      if (b === 0) {
        return 0 //这里当分母为0时候默认输出为0了,可根据需求自行修改
      } else {
        const res = a / b
        return res
      }
    }
  }

运用场景:需要计算两个值相除的值,对于同比环比也可以根据业务需求进行类似封装

例如:

const { a, b } = { a: 1, b: 2 }
console.log(divide(a, b)) //输出0.5

8. 对象拼接URL传参字符串

  function param(json) {
    if (!json) return ''
    return Object.assign([], Object.keys(json).map(key = {
      if (json[key] === undefined) return ''
      return encodeURIComponent(key) + '=' +
        encodeURIComponent(json[key])
    })).join('&')
  }

运用场景:把对象转化成GET传参url可用参数字符串,大部分ajax库都有封装,特殊情况下使用

例如:

obj = {
  id: 1,
  name: 'mayunhai',
  sex: 1
}
console.log(param(obj)) //id=1&name=mayunhai&sex=1

9. 获取URL参数

function param2Obj(url) {
  const search = url.split('?')[1]
  if (!search) {
    return {}
  }
  return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
}

运用场景:用户把url中GET传参的参数转化成对象方便使用的场景,不过目前主流框架路由都有相对于的方法可以得到一样的效果

例如:

const url = 'www.tuniu.com?id=1231&name=mayunhai&sex=1'
console.log(param2Obj(url)) //{id: "1231", name: "mayunhai", sex: "1"}

10. 垂直滚动

function scrollTo(element, to, duration) {
  if (duration <= 0) return
  const difference = to - element.scrollTop
  const perTick = difference / (duration / 50)
  setTimeout(() = {
    element.scrollTop = element.scrollTop + perTick
    if (element.scrollTop === to) return
    scrollTo(element, to, duration - 50)
  }, 50)
}

运用场景:让一个拥有垂直滚动条的元素在duration时间内滚动到制定高度

例如:

<div id="test" style="height:300px;overflow-y:auto;overflow-x:hidden" </div>
<script>
  let html = ''
  let i = 0
  while (i < 100) {
    html += `${i}test<br`
    i++
  }
  document.querySelector("#test").innerHTML = html
  scrollTo(document.querySelector("#test"), 1000, 400)
</script>

按照拼音首字母排序

首先先简单介绍一下JS里面自带的排序函数 sort() 的用法,关键还是传参,多的不说直接上代码

const arr = [3, 2, 1 ,10]  
console.log(arr.sort()) //[1, 10, 2, 3]
console.log(arr.sort((a, b) => a - b)) //[1, 2, 3, 10]
console.log(arr.sort((a, b) => b - a)) //[10, 3, 2, 1]

这里关于升序降序的规律自行总结,下面举例介绍一下衍生用法

const arr = [{
  name: '小马',
  value: 90
},{
  name: '小王',
  value: 80
},{
  name: '小张',
  value: 100
}] 
console.log(arr.sort((a, b) => a.value - b.value))

这个例子在很多数据处理中是非常常见的前端排序方法,结果感兴趣的自行打印试试,下面介绍本文最想要说的按照拼音字母排序

const arr = ['客服部门', '安保部门', '产品组'] 
console.log(arr.sort((a, b) => a.localeCompare(b))) //['安保部门', '安产品组保部门', '客服部门']

未完待续...

上次更新: 5/17/2019, 11:02:52 AM