Skip to content

面试复盘

云之家

mixin

  1. 可以全局混入 Vue.mixin() 和局部混入

  2. Mixin中的数据和方法都是独立的,组件之间使用后是互相不影响的

应用场景:

公共逻辑的复用、生命周期钩子的扩展、全局配置的注入

比如多个组件都需要在 created 钩子中执行某个操作,就可以将该操作封装成一个mixin

优点:

  • 提高代码复用性
  • 无需传递状态
  • 维护方便,只需要修改一个地方即可

缺点:

  • 命名冲突
  • 滥用的话后期很难维护
  • 不好追溯源,排查问题稍显麻烦
  • 不能轻易的重复代码

自定义指令

v-imageeror:图片链接失效时,设置默认图片

js
export const imagerror = {
  /**
   * @description 指令对象,会在当前的 dom 元素插入之前执行
   * @param {String} 表示当前指令作用的 dom 对象
   * @param {Object} 指令中的变量的解释
   */
  inserted(dom, options) {
    dom.src = dom.src || options.value // 初始化的时候 如果有值 则赋值 如果没值 则需要进行默认值赋值
    // 当图片有地址 但是地址没有加载成功的时候 会报错 会触发图片的一个事件 => onerror
    dom.onerror = () => {
      // 当图片出现异常的时候 会将指令配置的默认图片设置为该图片的内容
      dom.src = options.value // 这里不要写死
    }
  },
  componentUpdated(dom, options) { // 这个也是个钩子函数,在组件数据更新时会调用
    dom.src = dom.src || options.value
  }
}

v-hasPermi:操作权限处理

js
import store from '@/store'

export default {
  inserted(el, binding, vnode) {
    // binding 中包含指令名name,绑定的值value,绑定的前一个值oldValue
    const { value } = binding // 绑定的值,如:['system:config:add']
    const all_permission = "*:*:*";
    // 获取 vuex 中存的权限
    const permissions = store.getters && store.getters.permissions
    // 判断值是否为数组,数组中是否有值
    if (value && value instanceof Array && value.length > 0) {
      const permissionFlag = value
	  // 判断 vuex 中的权限是不是超级管理员、是不是指令绑定的值
      const hasPermissions = permissions.some(permission => {
        return all_permission === permission || permissionFlag.includes(permission)
      })
      // 如果没有权限,移除元素节点
      if (!hasPermissions) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`请设置操作权限标签值`)
    }
  }
}

基于角色的权限管理如何实现

前端主要实现两种:页面访问权限按钮操作权限

  1. 用户登录过后,获取用户的所有信息,包括角色信息,存入 vuex
  2. 将路由分为公共路由和动态路由,动态路由添加 permissions 属性
  3. vuex 中编写生成路由函数,向后端请求路由数据,将请求的路由数据进行遍历,通过 route 中的 permissions 属性验证是否具备权限,然后使用 addRoutes 方法在路由中添加动态路由
  4. **页面访问权限:**在路由前置守卫中调用生成路由方法,根据 roles 权限生成可访问的路由表,动态添加可访问路由表
  5. **按钮操作权限:**实现自定义指令 v-hasPermi,判断页面中的使用指令的地方是否有权限

对组件库某些组件进行二次封装

二次封装 el-pagination 组件:分页组件的文本和样式和需求中的不一样,封装的组件,props 接收一些常用的绑定值,如:当前页码,页面大小,页面大小选项,数据总数。以及定义一个页面大小改变触发的函数和一个页码改变的函数,他们都 $emit 同一个事件,抛出改变的值。并用 v-bind="$attrs" 将 elment 支持的属性,如 small 等属性透传给 el-pagination

二次封装 el-table 组件:改变表格 loading 的样式,使用具名插槽添加名操作列。

封装过的工具函数

axios 二次封装。

日期格式化:

js
export function parseTime(time, pattern) {
  if (arguments.length === 0 || !time) {
    return null
  }
  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
      time = parseInt(time)
    } else if (typeof time === 'string') {
      time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
    }
    if ((typeof time === 'number') && (time.toString().length === 10)) {
      time = 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]
    // Note: getDay() returns 0 on Sunday
    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
    if (result.length > 0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  return time_str
}

列表转为树形结构:

js
export function tranListToTreeData(list, rootValue) {
  const arr = []
  // 遍历需要转换的数据
  list.forEach((value) => {
    // 如果 pid 为 根值,代表这条数据是根数据
    if (value.pid === rootValue) {
      // 递归,用 id 去找与之相同的 pid,id 的那条数据是 pid 那条数据的父级
      const children = tranListToTreeData(list, value.id)
      // 如果有子级,则将给这条数据添加一个 children 属性
      if (children.length) {
        value.children = children
      }
      arr.push(value) // 将遍历过后的数据添加到新数组 arr 中
    }
  })
  return arr
}

深拷贝:

js
export function deepClone(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  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
}

参数转为对象:

js
export function param2Obj(url) {
  const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
  if (!search) {
    return {}
  }
  const obj = {}
  const searchArr = search.split('&')
  searchArr.forEach(v => {
    const index = v.indexOf('=')
    if (index !== -1) {
      const name = v.substring(0, index)
      const val = v.substring(index + 1, v.length)
      obj[name] = val
    }
  })
  return obj
}

柚子街

微信小程序的生命周期

页面的生命周期:

生命周期说明作用
onLoad生命周期回调—监听页面加载发送请求获取数据
onShow生命周期回调—监听页面显示请求数据
onReady生命周期回调—监听页面初次渲染完成获取页面元素(少用)
onHide生命周期回调—监听页面隐藏终止任务,如定时器或者播放音乐
onUnload生命周期回调—监听页面卸载终止任务

url 参数转为 object

  1. split 函数 url 进行分割,通过传入 ? 分割,得到一个两个值的数组,取第二个,第二个就是 URI 了
  2. 对 URI 进行解码,考虑 URI 中存在空格的问题,解码后会将空格替换成 + 号,需要使用 replace(/+/g, ' ') 转换回空格
  3. 将得到的字符串用 & 进行分割
  4. 通过 indexOf 寻找 = 的下标,用 substring 方法获取 = 左边和右边的值
js
function params2Obj(url) {
    const uri = decodeURIComponent(url.split('?')[1]).replace(/+/g, ' ')
    if (!uri) {
        return {}
    }
    const obj = {}
    const arr = uri.split('&')
    arr.forEach(item => {
        const index = item.indexOf('=')
        if (index !== -1) {
            const name = item.substring(0, index)
            const value = item.substring(index + 1, item.length) 
        }
        obj[name] = value
    })
    return obj
}

HTTP 请求头和响应头

常见的请求头:

  • Accept:浏览器能够处理的内容类型
  • Accept-Charset:浏览器能够显示的字符集
  • Accept-Encoding:浏览器能够处理的压缩编码
  • Accept-Language:浏览器当前设置的语言
  • Connection:浏览器与服务器之间连接的类型
  • Cookie:当前页面设置的任何Cookie
  • Host:发出请求的页面所在的域
  • Referer:发出请求的页面的URL(通过这个可以实现图片防盗链)
  • User-Agent:浏览器的用户代理字符串

常见的响应头:

  • Date:表示消息发送的时间,时间的描述格式由rfc822定义
  • server:服务器名称
  • Connection:浏览器与服务器之间连接的类型
  • Cache-Control:控制HTTP缓存
  • content-type:表示后面的文档属于什么MIME类型

Released under the MIT License.