# rz_86
**Repository Path**: ifercarly/rz_86
## Basic Information
- **Project Name**: rz_86
- **Description**: ~~~~~~~~~~~~~
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 7
- **Forks**: 0
- **Created**: 2022-11-20
- **Last Updated**: 2023-01-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 1. 安装 vue-admin-template
vue-element-admin 是一个完整的继承方法,不方便直接拿来二次开发,所以咱们选中的是精简后的 vue-admin-template。
a,从码云拉取代码 。
```bash
git clone https://gitee.com/panjiachen/vue-admin-template.git
```
b,切换到具体目录下。
```bash
cd vue-admin-template
```
c,安装所有依赖。
保证你的 NPM 源是正确的(在国内),如何查看源呢?
```bash
npm get registry # 如果在这个 https://registry.npmjs.org/ 表示不对,因为是国外的,下的时候很慢
```
如何设置源到淘宝?
```bash
npm config set registry https://registry.npmmirror.com/
```
```bash
npm install
```
d,启动项目,查看 package.json 文件的 scripts 可知晓启动命令。
```bash
npm run dev
```
e,碰到的问题如下,自查自纠。
问题1. 'vue-cli-service' 不是内部或外部命令,也不是可运行的程序,解决如下:
```bash
npm i @vue/cli-service -D
```
问题2. 如果 Node 版本是 18.x 的,先卸载掉,再安装我发的 16.x 的。
问题3. 把网线拔了,换热点。
问题4. 注意 npm 镜像源。
## 2. 如何把代码提交到远端?
```bash
1. 把 .git 文件删掉,因为拉过来的 .git 信息是【花裤衩】的
2. git init # 变成自己的仓库
3. git remote add origin 你的远程仓库地址 # 把本地仓库和远端的自己的仓库关联起来
4. git remove -v # 查看关联的地址,保证是自己的
5. git add .
6. git commit -m init
# 第一次加 -u,后续再提交可以直接 git push 就行啦
7. git push -u origin master # 如果冲突了,想以当前提交的为准,强制提交 git push -u origin master --force
```
```bash
git branch 分支名 # 创建分支
git checkout 分支名 # 切换分支
git checkout -b 分支名 # 相当于上面两行,创建并切换分支
```
问题:我们之前切换分支之前,要先进行 `git add .`、`git commit -m msg`、然后才切换,你刚才为什么没有这样做呢?
如果你切换的是一个已经存在的分支,确实要保证当前分支是干净的,确实要 git add . git commit -m msg,然后切换,**建议**。
如果你切换的是一个新分支,无所谓,可以直接切换!
## 3. 你先做一下自我介绍吧?
你好面试官,我叫吕布,来自于**河南**,做前端开发 **3 年时间**了,上家公司在**杭州**那边工作;
我的主要技术栈是 **Vue**,对 Vue 比较熟一些,对 React 和小程序也有一些了解;
开发过的项目类型偏 PC **端**的会比较多一些,移动端的项目也有参与过开发;
我上家公司是做医疗方向的,所以对这方便的**业务**会比较熟一些;
大概是这样一个情况,谢谢你面试官。
## 4. 掌握 Sass 的混入语法?
```scss
// 定义:@mixin 名字 { 样式 }
// 使用:@include 名字
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
.box {
// 相当于把上面的代码全部抄过来了一份
@include clearfix;
}
```
## 5. 说一下 axios 你是怎么封装的?
请求文件的封装:在 `utils/request.js` 里面创建一个 axios 实例,然后封装了 baseURL、timeout、请求拦截器、响应拦截器,并导出。
项目中是怎么用的:在 api 目录根据 `request.js` 中封装好的请求对象,再次创建请求函数并导出,一般在组件或 vuex 中的 actions 中使用。
```js
import request from '@/request.js'
export const getUserList = () => {
return request.get('/user')
}
```
## 6. 如何更方便的设置 NPM 源?
```bash
npm i -g nrm # 全局安装一个命令行工具,叫 nrm,它可以很方便的设置源
nrm -V # 出来版本号,就表示安装好了
nrm ls # 查看有哪些源可以给我使用
nrm use taobao # 设置源到淘宝
npm get registry # 查看有没有设置成功
```
## 7. 关于跨域?
什么是跨域:当协议、域名、端口号任一不同就是跨域。
为什么有跨域:是**浏览器**处于安全性的考虑而做出的同源策略的限制。
解决方案如下。
后端处理:后端通过 CORS 去处理,原理就是通过设置一个 `Access-Control-Allow-Origin` 这个响应头来允许某些域名访问。
代理服务器:如果说是用 Vue CLI 创建的项目,可以在 `vue.config.js` 中配置 devServer 的 proxy 来代理到某个地址。
## 8. 说一下对 Vuex 的理解?
是什么:Vuex 是一个全局的状态管理的 JS 库,它解决了非关系型组件之间的数据传递和共享的问题。
怎么用/配置项:state、mutations、actions、modules、getters、plugins。
触发流程:例如点击按钮发请求,希望把请求的结果存储到 vuex,说一下触发流程?
给按钮绑定点击事件 => dispatch action => action 中发请求 => 拿到结果之后通过 commit mutaion => 修改 state => state 中的数据是响应式的 => 所有视图用到 state 的地方自然就变了。
## 9. 快捷键?
Ctrl + J 命令行展开/收起
Ctrl + B 侧边栏展开收起
Ctrl + K / Ctrl + J 展开所有代码
Ctrl + K / Ctrl + 0 收起所有代码
Ctrl + K / Ctrl + { 收起当前的代码块
Ctrl + K / Ctrl + } 展开当前的代码块
Ctrl + D 选中所有的
Ctrl + P 跳文件
HOME / END / 上下左右 / Ctrl / Shift / Alt 任意组合,你就是高手了。
## 9. 可选链操作符?
```js
error.response?.data // 如果说 error.response 不为 null 或 undefined 才往右边取值,否则整体返回 undefined
```
## 10. Hash 和 History 差异?
表现不一样:Hash 带 # 号,history 没有。
兼容性不同:Hash 不存在兼容性问题,History 是 H5 新增的,低版本 IE 不支持。
原理不一样:Hash 是监听 hashchange 事件拿到地址,用这个地址匹配对应的路由 path,展示对应的组件 component;History 是监听 popstate 事件,利用是 H5 history API 进行的跳转,也是拿到路由地址,匹配对应的路由 path,展示对应的组件 component。
History 需要服务端支持:History 对于服务端来说每次刷新都是一个新的请求,需要服务端支持(否则会 404),**服务端把所有浏览器地址发出的请求转发到 index.html 页面**,交给前端路由去托管。
## 12. 构造函数中手动返回数据的问题?
```js
// 什么是构造函数,取决于这个函数你怎么用
function Person(name, age) {
this.name = name;
this.age = age
// 如果手动返回的是一个简单数据类型,会忽略
// return 8
// 如果手动返回的是一个复杂数据类型,其实 new 出来的结果就不再是实例了,而是这个复杂数据
return {}
}
// Person() // 普通函数
const p = new Person('ifer', 18) // 构造函数
```
## 13. 界面访问控制?
在哪做的?
在全局路由前置导航守卫 beforeEach
做了什么?
如果说有 token,看一下访问的页面是不是登录页,如果访问的是登录页,拦截到首页,否则执行 next() 放行;
没有 token,看一下访问的页面在不在白名单,如果在,执行 next 放行,否则拦截到登录页。
## 14. Token 的失效处理?
前端主动处理:在登录成功后存一个时间戳(A),在请求拦截器处用当前的时间戳(B)减去 A,如果大于 Token 的有效时间了,就做退出相关的操作。
配合后端处理:在响应拦截器的 error 回调中,根据后端返回的错误状态码,如果是 401,表示 token 过期,直接做退出相关的操作。
## 15. 兄弟通信?
需求:点击 A 中的按钮,让 B 组件中的数据 +1,A 和 B 是兄弟关系。

**状态提升**
1\. 把操作的 B 组件的数据 count 提升到(剪切)公共的父组件 App。
2\. 利用自定义事件,在 A 组件点击按钮的时候修改父组件 App 的数据(子传父)。
3\. 再把父组件的变化后的 count 传递给 B 组件。

## 16. array-to-tree
```js
function transArray2tree(list, id = '') {
const arr = []
list.forEach(item => {
if(item.pid === id) {
const children = transArray2tree(list, item.id)
if(children.length) {
item.children = children
}
arr.push(item)
}
})
return arr
}
```
## 17. .sync 修饰符
干什么的:父传子,子再修改传递过来数据的一种便捷写法。
```html
Hello: {{ this.$route.params.id }}
``` **到底什么情况下才会丢失呢?** 使用 name 跳转,例如下面写法。 ```js this.$router.push({ name: 'user', params: { id: 'ls' } }) ``` **并且**路由配置的地方“写错了”。 ```js { path: '/user', // 这儿写错了 name: 'user', component: () => import('@/views/user/index.vue'), hidden: true, meta: { title: '用户' } } ``` ## 31. props 传值是异步的,如何解决异步带来的问题呢? ```js async editRole(userId) { this.userId = userId // !#1 调用子组件获取角色列表的方法 this.$refs.assignRole.getUserDetailById() // 上面代码报错了 // 解决方式一:nextTick /* this.$nextTick(() => { this.$refs.assignRole.getUserDetailById() }) */ // this.$nextTick(this.$refs.assignRole.getUserDetailById) /* this.$nextTick().then(() => { this.$refs.assignRole.getUserDetailById() }) */ // 解决方式二:直接调用子组件的方法并传参 await this.$refs.assignRole.getUserDetailById(userId) this.showRoleDialog = true } ``` ## 32. 生命周期钩子? ```js created: 时机相对较早,同时又能访问 data 里面的数据或 methods 里面的方法(因为创建好了)。 beforeDestroy: 解绑事件或清理定时器。 ``` 为什么异步放到 Action 里面? 其实异步代码放到 mutation 里面,默认情况下(非严格模式)能正常发,没什么问题。 之所以放到 action 里面,是为了形成数据快照,配合 Vue Devtools 观测到每一次对 Mutation 的触发的信息(包括类型和 payload)。 ## 33. 权限分配的流程? 通过【员工管理】页面给用户分角色。  通过【公司设置】页面,给角色分权限。  权限有哪些分类? 页面级别的按钮级别的权限。 权限从何而来呢?是通过【权限管理】页面而管理起来的。  ## 34. ❗路由权限是如何产生实际效果的,怎么做的? 1\. 用户登录成功后,后端会返回当前用户的标识,假如返回的结果如下。 ```js ['employees', 'settings', 'departments'] ``` 2\. 前端拿到标识后,去从 asyncRoutes 中筛选出当前用户有权限的路由(有权限的路由就是和标识对应的路由对象),假如筛选的结果如下。 ```js const filterRoutes = [ { path: '/employees', name: 'employees', component: () => import('@/views/employees') }, { path: '/settings', name: 'settings', component: () => import('@/views/settings') }, { path: '/departments', name: 'departments', component: () => import('@/views/departments') } ] ``` 3\. 前端通过 `router.addRoutes` 添加到路由实例(可以理解为把上面的路由对象怼到了路由配置想 routes 里面了)。 ```js router.addRoutes(filterRoutes) ``` 4\. 此时当前用户就具有了访问某个页面的权限啦! **碰到的问题?** addRoutes 之后,刷新的时候碰到了 404 问题?解决如下。 ```js // 注释掉曾经手动配置的 404 路由,通过下面方式添加到路由实例的最后面 router.addRoutes([...otherRoutes, { path: '*', redirect: '/404', hidden: true }]) ``` **白屏问题?** ```js // 通过 addRoutes 后续添加的路由是“异步”的,不会马上生效,需要重新从 beforeEach 的再执行一遍就好了。 next({ ...to, replace: true }) ``` **侧边栏问题?** ```js // addRoutes 后续添加的路由配置项,通过 this.$router.options.routes 直接拿不到,而侧边栏的信息就是这样获取的,所以展示不出来 // 解决:在 Vuex 中也存放了一份完整的路由(其中就包含筛选过后的动态路由) ``` **后续的用户可能会收到上一个用户的路由信息的影响?** 所以退出的时候,要重置一下路由(包含路由实例 router 和 Vuex 中的筛选过来的 routes) 路由配置信息添加到 router 实例:能保证通过浏览器地址栏访问! Vuex 中的筛选过来的 routes:能保证侧边栏看到。 ## 35. 功能级别的/按钮级别的权限怎么做的? 封装一个全局的指令或方法,这个方法只做 1 件事情,接收标识,内部判断一下在不在后端返回的**功能列表**里面,在的话就返回 true,不在的话就返回 false。 ```js function checkPermission(tag) { // 后端返回的功能列表 const list = ['DEPARS_DELETE', 'DEPARS_ADD'] return list.includes(tag) } ``` 接下来我只需要在做按钮权限控制的地方调用一下这个方法,传递过去标识,根据方法返回的是 true 还是 false,对这个按钮做一个禁用或启用,显示或隐藏的操作。 ```html ``` ## 36. 一个小问题? ```js const o = {} const a = 'bbb' // 如果用点去设置属性,点后面的就是一个普普通通的字符串 o.a = 88 // { 'a': 88 } // 中括号里面这个东西没有加引号,会当做变量 o[a] = 77 // { 'a': 88, 'bbb': 77 } o['a'] = 99 // { 'a': 99, 'bbb': 77 } console.log(o.a) ``` ## 37. 工作开发流程? 1\. 产品经理提出需求(有的外包公司直接拿的是甲方的需求,有我们自己公司的项目经理和他们对接)。 2\. 会召集大伙(前端、后端、UI、测试)开会,分析需求的合理性。 3\. 前端拿到原型图,老大协调拆分项目的开发周期(一般是按小时为单位的),一般新手往往过于乐观,评估的时间太短了。 4\. 老大会把项目的基本架子搭建好,把初始代码提交到内部 Gitlab 平台,建好对应的分支(Git 工作流)。 5\. 我在自己的功能分支去开发,完毕之后合并到 develop 开发分支,最后测试的时候由老大合并到 release 分支交给测试去测试。 6\. 测试发现 Bug 会通过内部的禅道管理平台提给对应的负责人,对应的人修复完之后,测试再进行复测,没问题之后关闭这个 Bug。 7\. 一切完毕之后有老大负责打包上线,这一块个工作我参与的不多。 ## 38. ElementUI 弹框的时候后面的如何覆盖前面的? ### 方法 1 参考:https://www.jianshu.com/p/a07aa3192edf `utils/resetMessage.js` ```js import { Message } from 'element-ui' let messageInstance = null // 声明一个 resetMessage 方法,接收配置项 const resetMessage = (options) => { // 第二次及以后调用的时候 messageInstance 已经存在了 // 如果已经存在实例则关闭 if (messageInstance) { messageInstance.close() } // messageInstance 就是 Message 的实例,可以直接像 Message 一样调用进行弹框,例如 messageInstance() messageInstance = Message(options) } ['error', 'success', 'info', 'warning'].forEach(type => { // 往 resetMessage 这个函数上加了 4 个属性叫 'error', 'success', 'info', 'warning' // 这四个属性对应的值又是一个一个的函数,所以我可以这样用 resetMessage.success() resetMessage[type] = options => { if (typeof options === 'string') { options = { message: options } } options.type = type return resetMessage(options) } }) export const message = resetMessage ``` `main.js` ```js import { message } from '@/utils/resetMessage.js' Vue.prototype.$message = message ``` 以后可以直接下面两种方式使用。 ```js import { message } from '@/utils/resetMessage.js' // JS 文件中 message.success('tip') // Vue 文件中 this.$message.success('tip') ``` ### 方法 2 ```js // 调用 Message 之前,先关闭所有的 Message.closeAll() Message.error(error.message) ``` ## 39. this 指向? ```js 'use strict' // 对语法的解析更加严格,开启严格模式 // 普通函数中的 this 调用的时候才能确定 function fn() { // 严格模式下是 undefined console.log(this) } fn() const o = { fn } o.fn() // 箭头函数中的 this 在定义的时候就已定确定了 const test = () => { console.log(this) } ``` ## 40. mixins? 作用:可以实现不同组件间的相同的数据和业务逻辑的复用,提高开发效率。 **怎么用呢?** 1\. 先定义一个对象并导出, `mixins/check.js`。 ```js export default { data() { return { age:18 } }, methods: { checkPermission(tag) { // 后端返回的功能列表 const list = ['DEPARS_DELETE', 'DEPARS_ADD'] return list.includes(tag) } } } ``` 2\. 在组件中导入这个对象,`employees.vue`,这叫局部 mixin。 ```js import check from '@/mixins/check' export default { mixins: [check, a, b, c] } ``` 3\. 全局。 ```js Vue.mixin({ data() { return { age: 19 // 这个 age 任何一个组件都可以用 } } }) ``` **问题?** 命名冲突;数据来源不清晰; **优先级?** 混入过来的内容,执行顺序是怎么样的? 如果配置了相同的生命周期函数,先执行混入的,再执行组件自身的。 data 和 methods:相同的会覆盖,组件内的会覆盖 mixin 中的。 ## 41. Vue2 中 v-model 和 sync 在组件上使用的区别? v-model 和 .sync 修饰符都是组件传值时候的一种便捷写法,不同的是 v-model 在组件上只能写一次,.sync 可以写任意多次。 ```js // v-model 相当于 v-bind:value 和 v-on:input 的语法糖,在组件上只能写一次age: {{ value }}
age: {{ value2 }}
age: {{ age }}