索引
记录一些工作中使用到的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)
}