树形json扁平化,一维数组树状化,对象深拷贝,元素后插入新元素,格式或动态路由等常用js合集


记录一些工作中使用到的js函数

一、在元素后面插入一个新的元素。

晚上写demo,需要在一个元素后面创建并插入一个新的元素,但是突然发现js中好像没有直接实现这项功能的函数,所以今天赶紧的写了一个,记录一下。
原生js中有insertBefore()函数,语法是 parentNode.insertBefore(newElement, targetElement),在指定目标元素前面插入一个新的元素。
而查找元素有nextSibling,所以功能实现如下:

    //功能: 在targetElement之后插入 新节点newElement
    function insertAfter(newElement, targetElement){
        var parent = targetElement.parentNode;
        parent.insertBefore(newElement,targetElement.nextSibling);
    }

二、对象或者数组的深拷贝。

function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
  for(let keys in source){ // 遍历目标
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 如果不是,就直接赋值
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}

三、从服务器端获取到动态路由表的格式化。

基于vue中addRouter来从服务器动态获取路由表时,由于后端传来的json数据格式与需要的routes格式不一定一致,所以经常需要格式化。这里提供一个简单的封装函数。

/** 格式化路由。传入的路由不需要404
 * 根据服务器返回的component的字符串,通过require来获得真正的component
 *  component: require.ensure([], (require) => {
 *    resolve(require('@/page/home.vue'))
 *  })
 * @param permissionRoutes 服务器端返回的路由数组,其中的component是字符串。
 */
function formatRouter(permissionRoutes) {
  var formatRoutes = []
  permissionRoutes.forEach((routerItem) => {
    var {path, component, name, meta, children} = routerItem
    var formatItem = {path, name,meta,
      component(resolve) {
        let componentPath = ''
        if (component === 'Layout') {
          require(['@/views/layout/Layout'], resolve)
          return
        } else {
          componentPath = component
          require([`@/views${componentPath}/index`], resolve)
          // 如果后端瞎传不存在的路由对应到不存在的组件页面,则捕捉错误并重定向到404页面。缺点是热更新会非常慢,建议最后开启。
          // import(`@/views${componentPath}/index`).then((res) => {
          //   return resolve(res)
          // }).catch(r => {
          //   import('@/views/404').then(res => {
          //     return resolve(res)
          //   })
          // })
        }
      },
      // 递归调用,若children数组为空就停止。遍历所有层次。
      children: children.length > 0 ? formatRouter(children) : []
    }
    formatRoutes.push(formatItem)
  })
  // 最后来push上404页面,否则前面的都无法显示。
  formatRoutes.push({
    path: '*',
    redirect: '/404',
    hidden: true
  })
  return formatRoutes
}
// 这里最好再写一个函数判断childre是否[],因为如果后台传来的动态路由,忘了写children,即使只漏掉了一个,也会导致这里报错。
//因为漏了children,那么这里的children就为undefined,取length就会导致报错。

四、json树形数据扁平化处理(变成一维数组)

前后端交互的过程中,使用最多的应该是json数据了,树形的json数据很常见,而有时候我们需要将它转化为一维数组,例如定位查找功能。
这里提供一个非常简单的扁平化处理函数。

function _flatten(data, keysArray, level = 0) {
  // 剥离最外层数组[],获取每项item,并返回一个新数组[]来保存结果。
  return data.reduce((totalArray, itemArray) => {
    return [
      ...totalArray, // 打散最外层json数组的结构,合并成一个扁平化的数组。
      // 打散传入的所需键得数组,剥离出传入的需要的键。并将需要的键赋值,加上level键值对,合并成一个扁平化的新数组。
      keysArray.reduce((totalObject, key) => {
        // 将传入的键转为对象的属性,并且赋值。
        totalObject[key] = itemArray[key]
        // 返回转换好的对象,即所需键组成的对象。
        return totalObject
      }, {
        level // 传入level的初始值对象
      }),
      // 递归调用flatten函数,并且打散递归返回结果的数组,使其扁平化。
      ..._flatten(itemArray.children || [], keysArray, level + 1)
    ]
  }, [] // 传入初始值为一个空的数组[]
  )
}

上面是带有注释版本的,其实如果不注重可读性,该函数能够极大地简写。

function flatten(data, keys, level = 0) {
  return data.reduce(
    (arr, x) => [
      ...arr,
      keys.reduce((o, k) => ((o[k] = x[k]), o), {
        level
      }),
      ...flatten(x.children || [], keys, level + 1)
    ],
    []
  )
}

五、一维数组转化为树状结构对象。

上面介绍了一个树状对象转一维数组,这里就再介绍一个相反的函数,将一维数组转化为树状对象。

/**
 * @param data,需要转成树结构的一维数组数据。要求每个节点都含有父节点的id,最顶层的parent也需要设置。
 *        superlativeNodeId,最顶层的parent的id值。若为null则可以不设置。
 */
function filterArray(data, superlativeNodeId) {
   let vm = this;
   var tree = [];
   var temp;
   for (var i = 0; i < data.length; i++) {
       if (data[i].parentId== superlativeNodeId) {
           var obj = data[i];
           temp = filterArray(data, data[i].id);
           if (temp.length > 0) {
               obj.subs = temp;
           }
           tree.push(obj);
       }
   }
   return tree;
},

但是这里需要注意的是,一维数组的子对象中必须含有父对象的id,也就是父子对象必须依靠某个数据关联起来。
如:

var nodes = [
  {id: 10, title: 'dw10', parentId: 4},
  {id: 2, title: 'dw2', parentId: null},
  {id: 4, title: 'dw4', parentId: 2},
  {id: 12, title: 'dw12', parentId: 2},
  {id: 8, title: 'dw8', parentId: 4}]

console.log(filterArray(testlist))
// 因为这里的最外层parentId为null,所以后面的参数superlativeNodeId可以不用传递。

六、防抖函数在vue中的应用。

防抖函数都会写,就是不会写网上也能抄一堆。

/**
 * @desc 函数防抖
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表立即执行,false 表非立即执行
 */
export function debounce (func, wait, immediate = true) {
  let timeout
  return function () {
    let context = this
    let args = arguments

    if (timeout) clearTimeout(timeout)
    if (immediate) {
      var callNow = !timeout
      timeout = setTimeout(() => {
        timeout = null
      }, wait)
      if (callNow) func.apply(context, args)
    } else {
      timeout = setTimeout(function () {
        func.apply(context, args)
      }, wait)
    }
  }
}

七、获取树形节点的最大深度。

// 获取节点深度 参数为树结构array
function getMaxFloor (treeData) {
  let max = 0
  function each (data, currentDeep) {
    data.forEach(item => {
      if (currentDeep > max) {
        max = currentDeep
      }
      if (item.children && item.children.length > 0) {
        each(item.children, currentDeep + 1)
      }
    })
  }
  each(treeData, 1)
  return max
}

八、使树形数据嵌套深度和数据保持一致

// 使数据嵌套深度保持一致,如果部分选项没有子选项,使用text:'' 空字符串进行占位
function paddingEmptyTree (treeData, maxDeepCount) {
  function each (data, currentDeep) {
    // console.log('data', data)
    data.forEach(item => {
      if (currentDeep < maxDeepCount && item.children && item.children.length === 0) {
        item.children.push({
          text: '',
          children: []
        })
      }
      if (currentDeep < maxDeepCount) {
        // console.log('item', item)
        each(item.children, currentDeep + 1)
      }
      if (currentDeep === maxDeepCount) {
        // console.log('currentDeep === maxDeepCount', currentDeep, item)
        delete item.children
      }
    })
  }
  each(treeData, 1)
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值