Appearance
面试复盘
云之家
mixin
可以全局混入
Vue.mixin()
和局部混入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(`请设置操作权限标签值`)
}
}
}
基于角色的权限管理如何实现
前端主要实现两种:页面访问权限,按钮操作权限
- 用户登录过后,获取用户的所有信息,包括角色信息,存入
vuex
中 - 将路由分为公共路由和动态路由,动态路由添加
permissions
属性 - 在
vuex
中编写生成路由函数,向后端请求路由数据,将请求的路由数据进行遍历,通过route
中的permissions
属性验证是否具备权限,然后使用addRoutes
方法在路由中添加动态路由 - **页面访问权限:**在路由前置守卫中调用生成路由方法,根据
roles
权限生成可访问的路由表,动态添加可访问路由表 - **按钮操作权限:**实现自定义指令
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
- 用
split
函数 url 进行分割,通过传入?
分割,得到一个两个值的数组,取第二个,第二个就是 URI 了 - 对 URI 进行解码,考虑 URI 中存在空格的问题,解码后会将空格替换成
+
号,需要使用replace(/+/g, ' ')
转换回空格 - 将得到的字符串用
&
进行分割 - 通过
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类型