vue2的:
1.生命周期函数:
1.1 什么是生命周期函数
我理解的生命周期函数就是 vue 实例从创建到销毁的过程中,到了某一个特定的时间节点会自动触发的钩子函数常用的钩子函数一共有八个beforeCreate 创建前 实例还未创建created 创建后 这个第一个能拿到 date 和 methods 来用的钩子函数beforeMount 挂载前 挂载的是节点mounted 挂载后以上四个是创建阶段的钩子函数 这些钩子函数只会执行 1 次
beforeUpdate 数据变了视图未更新updated 数据变了 视图更新更新阶段的钩子函数会执行 0 次或者无数次
beforeDestory 销毁之前 最后一个能使用 data 和 methods 的钩子函数destoryed 销毁之后销毁阶段的钩子函数 执行 1 次
除了这八个还有三个actived 激活缓存组件deactived 停用缓存组件errorCaptured 子组件出错的时候触发
1.2 在项目开发过程中哪些钩子函数用的比较多
在写代码的过程中,我用的比较多的钩子函数有 cretaed 和 mounted在 created 中我经常调用 axios 请求数据 或者获取本地存储里的数据在 mounted 中我经常获取 dom 节点
1.3 父组件和子组件的生命周期函数的执行顺序
父组件的创建前- 父组件的创建后 父组件的挂载前 子组件的创建前 子组件的创建后 子组件的挂载前 子组件的挂载后 父组件的挂载后
2.nextTick:
在 dom 更新之后执行的延迟回调 ,在 vue 中 dom 的更新是异步的,我理解的 nextTick 是把本身同步的代码变成异步执行,排在 dom 更新之后,所以能拿到 dom 节点
3.vuex:
3.1 什么是 vuex 能解决什么问题
vuex 是 vue 的状态管理工具,能解决组件之间数据共享的问题
3.2 五大核心以及怎么调用
state 存放数据的地方 this.$store.state.xxxmutations 修改state的地方 this.$store.commit()getters 计算 state 的地方 this.$store.getters.xxxactions 发送异步请求的地方 this.$store.dispatch()modules 模块化 把数据分模块管理
3.3 vuex 的语法糖辅助函数
mapStatemapMutationsmapActionsmapGetters
先引入 然后展开使用mapState 和 mapGetters 在计算属性下展开mapActions 和 mapMutations 在 methods 下展开展开的时候用扩展运算符...展开
3.4 vuex 的执行机制
如果要修改 state,先通过 dispatch 调用一个 action 再在 action 里通过 commit 调用一个 mutations 修改 state
3.5 vuex 的弊端
不过 vuex 也有⼀些弊端,⽐如浏览器刷新的时候,vuex 的数据会丢失,我们⼀般结合本地存储来解决,当我们在 mutations ⾥⾯改变state 的时候在通过 localStorage 或者 sessionStorage 或者持久化插件存储到本地,然后在 state 的状态的属性值那块写⼀个三元表达式,如果本地存在数据的话则读取本地存储的数据,否则就赋值为 null具体存了本地存储怎么拿来用:
在state中;
// list:JSON.parse(localStorage.getItem("list"))||[],
list:localStorage.getItem("list")
?JSON.parse(localStorage.getItem("list"))
: [];
3.6 怎么开启严格模式
跟 state 同级的地方 设置 strict: false,如果开启了严格模式 只能在 mutations 中修改 state 否则就会报错
3.7 modules
当在 vuex 中存储的数据很多的时候 需要用 modules 来分模块管理首先新建一个模块的 js 文件 里面有四大核心 state mutations actions getters并且要开启一个命名空间 namespaced:true然后在 index.js 中引入 这个 js 并在 modules 中注册模块用的时候 this.$store.state.a.arr 这就是调用模块a下面的arrthis.$store.commit("a/add") 这个就是调用模块 a 下面的 add 方法
4. vue 路由有⼏种模式?有什么区别?原理是什么?(必问) 讲解
⾯试官您好,接下来我给您介绍⼀下 vue 的路由模式,vue 的路由模式⼀共有两种,
分别是哈希和 history.
他们的区别是 hash 模式不会包含在 http 请求当中,并且 hash 不会重新加载⻚⾯,
⽽使⽤ history 模式的话,如果前端的 url 和后端发起请求的 url 不⼀致的话,会 报 404 错误,
所以使⽤ history 模块的话我们需要和后端进⾏配合.history 的原理就是利⽤ html5 新增的两个特性⽅法,分别是 pushState 和 replaceState 来完成的. 以上就是我对 vue 路由模式的理解.
5.路由导航守卫:
5.1 什么是导航守卫?
路由的导航守卫 又叫做路由的钩子函数 或者路由的生命周期函数 是在页面跳转的过程中到了某一个特定的时间节点会触发的钩子函数有三种七个全局守卫beforeEach 全局前置守卫beforeResolve 路由解析之前afterEach 全局后置守卫
组件级守卫beforeRouteEnter 路由进入之前beforeRouteUpdate 路由更新之前beforeRouteLeave 路由离开之前
单个路由独享的守卫beforeEnter 路由进入之前
这些导航守卫有三个参数 to from next to 代表到哪儿去 from 代表从哪儿来 next 代表执行下一步
5.2 导航守卫的使用场景
在写代码的过程中我一般用导航守卫进行页面的鉴权处理,在用户登陆成功的时候,我们会把 token 和用户信息存到 vuex 或者本地存储中,代表了用户的登陆状态,在访问某个页面的时候判断是否存的有 token,就能正常访问这个页面,如果没有 token,那么就用 next 跳转到登陆页面
5.3 导航守卫的执行顺序
全局前置单个路由规则的组件前置守卫全局解析守卫后置守卫
5.4 beforeEach 和 beforeResolve 的区别?
这两个都是全局守卫 都是在路由跳转之前就会执行的守卫beforeEach 比 beforeResolve 执行的时间要更早
6. v-if 与 v-show 的区别?(90%)
⾯试官您好,接下来我给您介绍⼀下 v-if 和 v-show 的区别? ⾸先 v-if 和 v-show 都是控制元素的显示与隐藏, 不过 v-if 控制元素的显示和隐藏的时候会删除对⽤的 dom 元素,当每⼀个显示的时候,都会重新创建 dom 和渲染. ⽽ v-show 则是通过 css 的 display:none和 display:block 来控制元素的显示与隐藏. v-if ⽐较耗费性能,所以我们涉及到频繁的显示隐藏操作我们建议使⽤ v-show,如果不是频繁操作的话,我们可以 v-if在项⽬中我会经常使⽤ v-if 和 v-show,⽐如我们在搜索功能的时候,他有⼀个历史记录,这个时候我们根据是否有搜索的结果来判断历史记录的显示与隐藏,这块我就可以使⽤ v-if ,当然⽤ v-show 也可以. 以上就是我对 v-if 和 v-show 的理解.
7. v-for 与 v-if 的优先级那个⾼?如果同时使⽤ v-for 和 v-if 怎么解决?(90%) 讲解
v-for 的优先级⾼. 因为 v-for 的时候我们才开始渲染 dom 元素,这个 v-if 还⽆法进⾏判断.v-for 和 v-if 不能同时使⽤,我们可以通过标签,⽐如 div 或者 template 标签来进⾏包裹,把 v-if 写到包裹的标签上⾯(写到 v-for 外⾯) 还有就是用计算属性算出来想要的数据再去循环在 vue3 中 v-if 的优先级更高
8.methods computed watch 的区别:
methods 是方法 各种事件放在这里 没有缓存功能
computed 是计算属性 需要 return 计算的结果 并且有缓存功能只有跟他有关的值发生变化才会重新计算 否则就从缓存里拿 通常用来计算总数和总价 比如购物车功能就会用到计算属性
watch 监听属性 有三个参数 handler(执行函数) deep(深度监听) immediate(立即监听)如果不需要 deep 和 immediate 的时候就会简写成一个函数watch 可以监听 data 数据还能监听路由的变化watch 里可以执行异步操作 但是计算属性不行watch 的使用场景 存本地存储的时候 只要数据变了就要重新存一次 那么就可以用 watch 实现
9. vue 常⽤的指令有哪些?(50%)
v-ifv-showv-htmlv-textv-onv-bindv-modelv-for
10. vue 常⽤的修饰符有哪些?(50%)
.trim 去除⾸尾多余的空格.stop 阻⽌事件冒泡.once 只渲染⼀次.self 事件只作⽤在元素本身.number 将值转化为 number 类型.capter 组件之间捕获.prevent 阻⽌元素的默认⾏为.native 事件穿透,让我们可以在⾃定义组件上定义事件和⽅法
11.组件通信?
父传子
首先在子组件标签上绑定一个自定义属性,然后在子组件里通过 props 接收这个属性,props 接收有两种方式 数组和对象的形式父传子的使用场景 封装列表的时候 把数据传到子组件渲染
子传父
在子组件的标签上绑定一个自定义事件,然后在子组件里通过$emit 调用这个自定义事件,调用这个事件的同时把要传的数据作为参数传到父组件子传父的使用场景:在子组件中有删除等方法的时候要在子组件把下标传到父组件操作数据
兄弟组件传值
中央事件总线 eventbus 在 main.js 定义一个空的 vue 实例并且挂载在原型上,通过$emit传数据,在要接收的子组件的created钩子函数下通过$on 接收
vuex 把公共数据存在 vuex 里 这样就能实现组件通信
本地存储 把要传递的数据存在本地存储里 在要接收的组件接收 也能实现组件通信
v-model v-model 是一个语法糖 是:value 和 @input 的合写 所以说能实现组件通信
$attr 和 $listener主要是祖孙之间传值 父组件下面有子组件a 子组件a下面又有 子组件b 在父组件里还是用自定义属性和自定义事件传 在子组件a下面绑定v-bind="$attrs" v-on="$listeners" 在子组件b下面通过$attrs 拿数据 通过 $emit 调用方法
provide 和 inject不管嵌套了几层都能拿到数据 把数据定义在 provide 里 在子组件通过 inject 拿到数据 缺点是不响应
children 和 parent在子组件里通过$parent 拿到父组件的数据和方法 在父组件里通过$children 拿到子组件的数据和方法
12.什么是 mvvm 模式?
vue 是一个 mvvm 模式的框架m 就是模型 model 在 vue 中指数据v 就是 view 视图vm 是 view-model 控制器 在 vue 中就是 vue 实例视图变了数据会变 数据变了视图会变 这中间就是 vm 在控制
除了 mvvm 模式之外 还有 mvp 和 mvc 模式
MVVM 模式的优点:1、低耦合: 视图(View)可以独⽴于 Model 变化和修改,⼀个 ViewModel 可以绑定到不同的"View"上,当 View 变化的时候 Model可以不变,当 Model 变化的时候 View 也可以不变。2、可重⽤性: 你可以把⼀些视图逻辑放在⼀个 ViewModel ⾥⾯,让很多 view 重⽤这段视图逻辑。3、独⽴开发: 开发⼈员可以专注于业务逻辑和数据的开发(ViewModel),设计⼈员可以专注于⻚⾯设计。4、可测试: 界⾯素来是⽐较难于测试的,⽽现在测试可以针对 ViewModel 来写。
MVVM 和 MVC 的区别:mvc 和 mvvm 其实区别并不⼤。都是⼀种设计思想。主要区别mvc 中 Controller 演变成 mvvm 中的 viewModel,mvvm 通过数据来显示视图层⽽不是节点操作。mvvm 主要解决了:mvc 中⼤量的 DOM 操作使⻚⾯渲染性能降低,加载速度变慢,影响⽤户体验。
13. vue 双向数据绑定原理?(必问) 过⼀下
vue.js 则是采⽤ 数据劫持 结合 发布者-订阅者 模式的⽅式,通过 Object.defineProperty() 来劫持各个属性的 setter , getter ,在数据变动时发布消息给订阅者,触发相应的监听回调。这个时候就可以实现数据的双向绑定Object.defineProperty 是对象的一个方法,当添加或者修改属性的时候会触发,第一个参数是目标对象,第二个参数是要修改的属性,第三个参数是配置项,可以是 value 和 writeable 等选项,也可以是 get 和 set 函数get 是获取值的时候触发 需要 return 一个值就是这个属性的值set 是修改值的时候触发 有一个默认参数就是修改后的值
Object.defineProperty 的缺点 是 对于新添加的属性检测不到解决的方法 this.$set
14. vue 如何封装可复⽤的组件?以及请举例说明你封装过的组件?(50%) 讲解
根据项目需求合理规划子组件(是封装一个页面还是一个页面多个子组件 具体情况具体分析)
定义 components 文件夹 定义子组件
引入 注册 作为标签使用
合理使用插槽和组件通信 保证 子组件的封闭性和开发性(有些东西是固定的,有些东西是动态的就需要插槽或者组件通信设置)
举例:封装头部子组件 标题部分用插槽封装列表子组件 商品数据通过父传子传递导航子组件 数据通过父传子传 点击事件通过子传父实现
15. vue 中 key 的作⽤是什么?
在用 for 循环的时候用 key 值,为了避免元素重复渲染,在设置 key 值的时候一般设置为 id 等唯一的值比如说循环 1-5 在 3 的后面插入一个 a 如果没有 key 值 那么从 a 开始 a 4 5 都要重新渲染 如果有 key 值 只会渲染 a 所以有 key 值能提高循环效率
16.说⼀下 vue 和 jquery 的区别?(50%)
⾸先呢 jquery 他是⽤ js 封装的⼀个类库,主要是为了⽅便操作 dom 元素,⽽ vue 他是⼀个框架,并且呢,他会从真实 dom 构建出⼀个虚拟的 dom 树,通过 di!算法渲染只发⽣改变的 dom 元素,其他的相同的 dom 元素不⽤在重新渲染. ⽽使⽤ jquery 去改变 dom 元素的时候,即使有相同的 dom 元素也会重新渲染,以上就是我对 vue 和 jquery 区别的理解.
17. vue 中 data 发⽣变化,视图不更新如何解决?(必问) 过⼀下
给对象或者数组新添加的属性不是响应式的,是因为 vue 的双向数据绑定的原理是 Object.defineProperty,由于这个方法的限制无法检测到属性的新增和删除,不会响应到视图上解决方法就是用 Vue.set/this.$set 在组件里就用this.$set 在 js 文件里用 Vue.set第一个参数 目标对象,第二个参数是要添加的属性,第三个是初始值
18. 为什么 vue 中 data 必须是⼀个函数?(必问) 过⼀下
如果 data 是⼀个函数的话,这样每复⽤⼀次组件,就会返回⼀份新的 data,类似于给每个组件实例创建⼀个私有的数据空间,让各个组件实例维护各⾃的数据。⽽单纯的写成对象形式,就使得所有组件实例共⽤了⼀份 data,就会造成⼀个变了全都会变的结果。所以说 vue 组件的 data 必须是函数。这都是因为 js 的特性带来的,跟 vue 本身设计⽆关。
19.说⼀下什么是 vue 过滤器? 有⼏种?项⽬中如何使⽤,请举例说明?(60%)
所谓的 vue 过滤器就是将数据进⾏⼆次处理,得到我们想要的结果数据vue 的过滤器分为两种,第⼀种是全局过滤器,通过 vue.filet 来进⾏定义,第⼆种是局部过滤器,需要定义在组件内部项⽬中我们通过过滤器将后台返回的状态 0 和 1 转化为⽀付或者未⽀付
20. 怎样理解 Vue 的单向数据流?
数据总是从⽗组件传到⼦组件,⼦组件没有权利修改⽗组件传过来的数据,只能请求⽗组件对原始数据进⾏修改。这样会防⽌从⼦组件意外改变⽗级组件的状态,从⽽导致你的应⽤的数据流向难以理解。
注意:在⼦组件直接⽤ v-model 绑定⽗组件传过来的 prop 这样是不规范的写法 开发环境会报警告如果实在要改变⽗组件的 prop 值 可以再 data ⾥⾯定义⼀个变量 并⽤ prop 的值初始化它 之后⽤$emit 通知⽗组件去修改
还有一个方法修饰符 sync 可以在子组件里用 emit 修改父组件传来的值 在父组件传值的时候加上这个修饰符就行了这个修饰符 就是:num @update:num 的简写
21.虚拟 dom
操作真实 dom 的代价是很昂贵的,所以在 vue 中采取虚拟 dom 提高渲染效率,虚拟 dom 就是根据真实的 dom 所生成的一个 js 对象,这个对象里包含了这个 dom 节点的标签属性子元素等信息
优点:保证性能下限: ⽐起粗暴的 DOM 操作性能要好很多⽆需⼿动操作 DOM: 我们不再需要⼿动去操作 DOM,我们可以以可预期的⽅式更新视图,极⼤提⾼我们的开发效率;跨平台: 虚拟 DOM 本质上是 JavaScript 对象,所以虚拟 DOM 可以进⾏更⽅便地跨平台操作缺点:⽆法进⾏极致优化: 虽然虚拟 DOM + 合理的优化,⾜以应对绝⼤部分应⽤的性能需求,但在⼀些性能要求极⾼的应⽤中虚拟DOM ⽆法进⾏针对性的极致优化。⾸次渲染⼤量 DOM 时,由于多了⼀层虚拟 DOM 的计算,会⽐ innerHTML 插⼊慢
( 不用背 参考虚拟 dom 具体长啥样
hello 111
</div>
letobj={
tagName:"div",
attr:[{
name:"class",
value:"box
},{
name:"id",
value:"home
}],
children:[{
type:"textNode",
vale:"hello"
},{
type:"htmlNode",
tagName:"span",
children:[{
}]
}]
}
```)
22. Vue 的 diff 算法原理是什么?
diff 算法就是把新生成的虚拟 dom 和旧的虚拟 dom 进行对比的过程就是 diff 算法Vue 的 diff 算法是平级⽐较,不考虑跨级⽐较的情况。内部采⽤深度递归的⽅式+双指针⽅式⽐较先⽐较两个节点是不是相同节点相同节点⽐较属性,复⽤⽼节点先⽐较⼉⼦节点,考虑⽼节点和新节点⼉⼦的情况优化⽐较:头头、尾尾、头尾、尾头⽐对查找,进⾏复⽤
23.说⼀下你对 keep-alive 的理解?以及在项⽬中如何使⽤?
keep-alive 是一个内置组件,作用是缓存不活动的组件不被销毁,下次访问这个组件的时候不会重新加载而是从缓存里拿。比如说刚填好的表单需要缓存 还有保持滚动条的距离keep-alive 的黑白名单:可以用 include 和 exclude 规定缓存谁不缓存谁 也可以 使用路由的元信息定义
跟他相关的两个钩子函数的 actived 和 deactived举例:保持滚动条的距离:给 router-view 外层套一个 keep-alive在想要保持滚动条的页面 的 beforeRouteLeave 的时候拿到当前滚动条饿的距离在 actived 里面 赋值给页面的滚动条拿滚动条或者赋值滚动条都是 document.documentElement.scrollTop
24.插槽
插槽分为 匿名插槽 具名插槽 和 作用域插槽匿名插槽指的是不起名字的插槽具名插槽是起了具体名字的插槽 v-slot 也可以简写为#起名字 用 slot 的时候加上 name 属性区分名字作用域插槽是把子组件里的数据拿到父组件的插槽位置那里使用 在子组件通过自定义属性传 在父组件插槽那里通过 v-slot=“”起个名字接收
匿名插槽和具名都用的很多比如在封装头部子组件的时候 中间的标题每个页面都不一样 就可以用插槽表示在封装弹框子组件的时候 弹框里的内容也可以用插槽表示
25.axios 的封装
先建一个 utils 文件夹 里面放 request.js 文件
在 request.js 文件用 create 方法创建 axios 实例,配置基本路径和超时时间
设置请求拦截和响应拦截
在请求拦截的成功的回调函数里 设置 token 以及 全局的 loading
在响应拦截的成功回调函数里 清除 loading 还有处理错误编码字典
最后把我们封装的 axios 实例 导出
再创建一个 api.js 统一管理我们的接口
26. 拦截器
⾸先呢,axios 拦截器是 axios 给我们提供的两个⽅法,通过这两个⽅法我们可以对请求发送之前以及响应之后进⾏逻辑的再次处理(拦截). 这两个拦截器不需要⼿动触发,只要发送 http 请求的时候就会⾃动触发. 我在项⽬中经常通过拦截器发送 token, 对token 进⾏过期处理,以及进⾏其他的⼀些操作
27.vue 本地的跨域 代理跨域 proxy
本地跨域是通过在 vue.config.js ⽂件⾥⾯的 devServer 属性⾥⾯的 proxy 属性⾥⾯配置,⼀共配置三个属性,分别是代理名称 代理地址 开启跨域 重写路径
devServer: {
// proxy: "https://sug.so.360.cn",
//第一种 直接定义跨域地址 这样所有的请求都会跨域
proxy: {
"/api": {
target:"https://sug.so.360.cn",
//跨域的目标
changeOrigin:true,
//开启跨域
pathRewrite: {
"^/api":"",
},
//真正请求的时候把 api代替为空
},
},
//第二种设置跨域关键字 只有看到api才会跨域到这个地址 其他没有api关键字的接口就不会跨域
},
28. Vue.use 是⼲什么的?
vue.use 是⽤来使⽤插件的。use 可以注册组件库,use 方法会调用 install 方法,install 方法里有一个参数 Vue,所以不需要再引入 Vue,可以理解为 Vue.component 是注册一个组件,但是用 use 方法可以实现一次注册多个组件除了可以注册组件之外还能注册全局的方法。
29.组件写 name 有啥好处?
增加 name 属性,会在 components 属性中增加组件本身,实现组件的递归调⽤。
可以表示组件的具体名称,⽅便调试和查找对应的组件 比如 keep-alive 缓存组件设置黑白名单的时候 就可以通过 name 设置
30.mixin?
mixin 混入 把公共的选项(生命周期函数,计算属性,watch 等)提到一个 js 文件里,哪个组件用就在哪个组件引入混入有全局混入和局部混入 局部混入是只在这个组件里能用 全局混入是这个项目的任何组件都能使用如果混入和组件都有这个选项的时候混入里的先触发
31.路由跳转方式 路由配置 嵌套和动态区别?
路由跳转:1.1router-link 标签跳转1.2.编程式导航 this.$router.push()1.3.this.$router.replace({path:‘/’ } ) (replace 跟 push 类似,只不过跳转之后不存历史记录)
传参是传参 跳转是跳转传参就是我们之前背的 query params 动态路由传参在路由跳转的时候可以携带参数 可以在这里说我们之前背的路由的三种传参query 可以通过 name 和 path 都能跳转 接收通过 query 接收 就相当于是 get 传输参数 因为参数会被拼接到 url 上刷新数据不丢失params 只能通过 name 跳转 接收通过 params 接收 相当于是 post 传参 刷新数据丢失 解决方法:采取动态路由传参 还有存本地存储里用的时候拿动态路由 通过 params 接收 还可以开启 props 传参 通过 props 接收 注意路由后面要拼上:xx 参数 刷新数据不丢失
路由的配置:在 router/index.js 中配置路由规则 路由规则中的 path 属性代表路径,name 属性是名字,component 属性代表在这个路径下加载什么组件,还可以用 children 属性配置子路由。还可以用 meta 定义路由的元信息。
动态路由:动态路由是指路由器能够自动的建立自己的路由表,并且能够根据实际情况的变化实时地进行调整。用:开头,:后面跟的值是不确定的。这个值是我们要传递的参数
嵌套路由:vue 项目中,界面通常由多个嵌套的组件构成,用 children 实现嵌套路由
32.自定义指令和自定义过滤器的使用场景和作用
自定义指令:使用位置:写在在标签里面 以 v-开头
使用场景:需要对普通 DOM 元素进行操作,这时候就会用到自定义指令比如 我们可以使用自定义指令让一个元素进入页面就获得焦点,拖拽
// 注册一个全局自定义指令 `v-focus`
Vue.directive("focus", {
// 当被绑定的元素插入到 DOM 中时……
inserted:function(el) {
// 聚焦元素
el.focus();
},
});
自定义指令的钩子函数一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:指令第一次绑定到元素时调用。
inserted:被绑定元素插入父节点时调用
update:所在组件的 虚拟 dom 更新时调用,
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
钩子函数的参数 即 el、bindingel 指令所绑定的元素binding:一个对象,包含这个自定义指令的一些信息
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子就可以简写为一个函数
过滤器:使用位置:双花括号插值 和 v-bind 表达式使用场景 :处理数据格式的比如说 我们可以用过滤器过滤时间格式
33.多环境变量
我们在 vue 项目开发中,项目在运行时会根据启动的指令来运行不同的环境,在不同的环境中,我们配置对应所需的变量来满足我们的开发需求,称为多环境变量。在项目根目录定义两个文件.env.dev 里面写上 NODE_ENV="development".env.prod 里面写上 NODE_ENV="production"在封装 axios 的文件里放一个 baseURL.js 里面判断环境 不同的环境 定义不同的 baseURl再把这个文件引入 封装的 axios 文件中使用(代码详见 10.21-day5-demo 文件夹下)
es6+js部分面试题:
1. 说⼀下你对 promise 的理解?(必问)
1.1 什么是 promise?通过 promise 能够解决什么问题?
promise 是一种异步的解决方案,在我的理解中就是把回调函数的写法变成链式写法,promise 是一个容器,里面可以放异步操作promise 能解决回调地狱的问题。
1.1 什么是回调地狱?
回调地狱就是回调函数嵌套回调函数,比如要拿到 a 接口的数据后才能请求 b 接口,拿到 b 接口的数据后才能请求 c 接口,这样的写法非常臃肿不易维护。用 promise 可以变成链式的写法,比着回调地狱的写法会更加的美观和方便维护,解决回调地狱 promise 还不是最终的方案,终极解决方案是 async 和 await async 函数就是把异步的代码变成同步的写法。
1.2 说⼀下 promise 的特点?也就是三种状态?
promise ⼀共有三个状态,分别是进⾏中,成功或者失败 如何成功的话可以通过 resolve ⽅法将正确结果返回出去,通过.then 的⽅式进⾏接受,失败的话可以通过 reject 的⽅式将失败的结果返回出去,通 过.catch 的⽅式进⾏接受,pending 状态是进⾏中,⼀旦进⾏之后,他的状态是不可逆的
1.3 说⼀下 promise 怎么⽤?
如果要使⽤ promise,我们需要对 promise 进⾏实例化, new Promise()里面接收一个回调函数 ,这个回调函数里有两个参数,分别是 resolve 和 reject,我们可以通过 promise 的实例化对象调用 .then 和 .catch⽅式接受结果。
实例化对象除了可以调用 then 和 catch 之外还有一个方法是 finilly,就相当于是完成的意思,不管成功还是失败都会执行这个方法。
1.4 在说⼀下 promise 的 all ⽅法和 race ⽅法?
这两个方法是构造函数的方法,所以直接被 Promise 调用,他们两个方法都是把多个接口放在一起请求,all 全部,都请求到了才执行 then,race 赛跑,谁先执行就执行 then
1.5 在说⼀下在项⽬中使⽤ promise 做过什么?
在原生的微信小程序中 因为 wx.request 本身不是一个 promise 对象 所以我们把他封装成了一个 promise 对象,就能用 then 和 catch 方法了我们常用的 jq 的 ajax 和 axios 本身就是一个 promise 所以我们可以直接用 then 和 catch 方法
1.6 promise 是解决什么问题的?
promise 是解决回调地狱的,回调地狱就是回调函数嵌套回调函数,存在的弊端就是写法⾮常臃肿,并且后期难以维护,除了 promise 之外 还能用 generator async await 解决回调地狱 async await 是最终解决方案
1.7 promise 本身是同步的 .then() 是异步的
2.原生 ajax 的步骤
创建 xhr 对象
创建和服务器的链接
发送请求
等待服务器的响应 并且拿到响应数据
3.说⼀下 async 和 await、以及他们和 promise 的区别?(必问)
⾸先 async 和 await 是解决异步的终极⽅案,他是 generatal 的语法糖.
async 和 await ⼀般配和使⽤,当我们给函数前⾯加上关键字 async,这个时候,这个函数的返回值就是⼀个 promise. ⽽ await 是⼀个同步的操作,await 只能配合 async ,不然会报错,await 后⾯可以是表达式,也可以是⼀个 promise,在await 下⾯的代码必须得等待 await 执⾏完之后才能在执⾏。他们和 promise 的区别就是在写法上更加的简洁.以上就是我对 async 和 await 的理解.
4. 说⼀下 es6 新增的特性 语法有那些?(必问)
1.箭头函数
2.展开运算符
3.解构赋值
4.变量声明⽅式 let 和 const
5.模板字符串
6.class 以及 继承
7.模块化
8.新增symbol基本数据类型
9.新增了⼀个数组⽅法:includes map filter find forEach some every...
字符串⽅法 正则表达的⽅法 函数的⼀些写法 对象的⽅法
10.promise 还 有 async await
10.⼤概能想到暂时只有这么多,在项⽬中我经常使⽤ let 和 const 箭头函数。解构赋值 promise 还 有 async await
4.1 es6 新增的数据类型和数据结构?
es6 新增了一种数据类型 Symbol ,symbol 的特点就是值永远都是唯一的 永远不会相等。
新增了两种数据结构 set 和 mapset 的特点是 里面的数据不会重复map 是一个键值对结构 对象也是 跟对象的区别是 key 值可以是任意类型的数据。
5.说⼀下箭头函数与普通函数的区别?(80%)
特点:箭头函数中的 this 始终指向箭头函数定义时的离 this 最近的⼀个函数,如果没有最近的函数就指向 window。箭头函数中没有this 用的是离它最近函数的this。
区别:
1.箭头函数不能⽤于构造函数,不能使⽤ new** ⽽普通函数可以
2.在普通函数中,this 总是指向调⽤它的对象,如果⽤作构造函数,this 指向创建的对象实例。
在 es6 中,提供了⼀种简洁的函数写法,我们称作“箭头函数”。写法:函数名=(形参)=>{……} 当函数体中只有⼀个表达式时,{}和 return 可以省略,当函数体中形参只有⼀个时,()可以省略。
6.说⼀下 for in 与 for of 的区别?(80%)
For in 可以遍历对象 ⽽ for of 遍历对象会报错
for in 遍历数组得到的数组的下表 ⽽ for of 遍历得到的时候数组⾥⾯的每⼀个元素
# 7.说⼀下 var 、let、const 之间的区别?(95%)
var 存在提升,我们能在声明之前使⽤。 let 、 const 不存在声明提升
var 定义的全局变量可以作为顶层属性使用 就是 window.xxx 使用,其他两者不会
let 和 const 作⽤基本⼀致,const 声明的是常量 不能再次赋值
let 和 const 不能重复声明变量
let 和 const 是块级作用域
扩展:const 定义的基本数据类型不能修改 定义的复杂数据类型能修改
因为基本数据类型存在栈里 复杂数据类型存在堆里
# 8.说⼀下数组去重的⽅法有哪些?es6 如何实现数组去重?(90%)
indexof
双层 for 循环
set ⽅法
let res2 = [...new Set(arr2)]
# 9.说⼀下如何检测对象⾥⾯有没有属性(或者如何检测⼀个对象是否为空)?以及如何获取对象⾥⾯所有的属性名?(70%)
通过 Object.keys ⽅法, 返回值数组,数组⾥⾯包含的是所有属性名
obj.hasOwnProperty()
使⽤ for in 的⽅式
# 10.如何将多个数组合并成为⼀个数组?(70%)
es5 :
concat
for 循环
Es6:
扩展运算符
map ⽅法
举例代码:
let res3 = [...arr3, ...arr4]
let res4 = res3.concat(res4)
# 11.说⼀下 es6 如何实现类以及如何实现类的继承?(80%)
class 是实现面向对象编程的语法 面向对象是一种编程思想 就是把所有的东西描述成一个对象,这个对象有属性有方法,在用的时候就可以实例化去使用
es6 提出了 class 类的概念,可以创建对象,es5 时候是构造函数创建的,所以 calss 构造函数的语法糖。
我们创建⼀个类只需要⽤过关键词 class 去声明就可以了 , 他的调⽤⽅式和构造函数的调⽤⽅式是
⼀样的
通过 es6 的类还给我们提供⼀个 extends 这样的⼀个关键字,来实现继承,用 super 调用父类的构造函数
以上就是我对类的理解
# 12.字符串的方法 **\*\***
字符串的方法
charAt( ) 方法从一个字符串中返回某个下标上的字符
concat( ) 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。
search( ) 获取某个字符或者字符串片段首次出现的位置
match( ) 检索字符串或者正则表达式
replace( ) 替换
replace(参数 1,参数 2); 参数 1:替换谁 参数 2:替换值
split( 参数 1,参数 2 ) 字符串切割,切割后返回数组
slice( ) 和 substring( ) ( 开始位置,结束位置 ) 获取两个索引值之间的字符串片段 从下标 2 开始截取,到下标 4 结束,但不包含 4
substr ( 开始位置,截取的长度 ) 获取两个索引值之间的字符串片段
indexOf( ) 获取某个字符或者字符串首次出现的位置,找到则返回索引值,找不到则返回-1
lastIndexOf( ) 获取某个字符或者字符串最后出现的位置,找到则返回索引值,找不到则返回-1
# 13. 数组的方法
1. Array.from 把类数组结构 或者带有 length 的结构转化成数组 from 是构造函数的方法
2. Array.of()可以把一组参数转换为数组 of 更方便 只要传参数就转化为数组
3. Array.isArray([]);true 也是构造函数的方法 用来检测是否是数组
还有 new Array() instanceof Array 也能检测数组
4. fill 填充数组
5. copyWithin 复制数组里的某一部分 插入到数组里的某个位置
第一个参数是插入的位置 第二个参数是开始复制的位置 第三个是结束的位置
6. reverse(倒序)不是排序 只是颠倒数组 原数组发生改变
7. sort 返回值 是排序后的数组 原数组也会发生改变
8. concat 合并数组 原数组不变 返回合并后的新数组
9. slice 截取数组 原数组不变 返回新的数组
参数是负数 就加上数组的长度 一个参数 就直接截到最后
10. splice 删除 替换 增加
原数组发生改变;
删除 传两个参数 开始的位置 删除的个数 返回值删除的值
替换和插入 是三个甚至更多参数
开始的位置 删除的长度(要插入的话第二个参数是 0) 要替换或者插入的值
11. indexOf 从左往右找下标
.lastIndexOf() 从右往左找下标
如果有就返回位置 没有就返回-1
12. includes 判断是否含有 返回 布尔值
push 末尾添加 返回值是新数组的长度
pop 末尾删除
unshift 开头添加
shift 开头删除
# 数组高阶函数 接收一个函数作为参数
13. find 查找 只要找到满足条件的就不再继续找了 返回值是找到的第一个值
14. filter 过滤 筛选满足条件的 放在一个新的数组中 返回值就是筛选出来的数据
15. findIndex 根据条件查找数组内匹配项的下标
16. every 都满足就返回 true
17. some 有一个满足就返回 true
18. map 和 foreach 都是循环遍历
forEach 没有返回值 对数组循环然后做一个操作 原数组会变
map 对原数组进行操作 得到一个新的数组 必须有返回值
19. flat
将多维数组扁平化,并返回一个新数组。方法接受一个数值,表示要扁平化的数组维度
20. reduce 方法接收一个函数作为累加器
13.1 *数组去重 :
1.使用 ES6 中的 set 是最简单的去重方法let arr = [1, 2, 3, 4, 5, 5, 4, 3];let res = [...new Set(arr)];
2.利用 Map 数据结构去重创建一个空 Map 数据结构,遍历需要去重的数组,把数组的每一个元素作为 key 存到 Map 中。由于 Map 中不会出现相同的 key 值,所以最终得到的就是去重后的结果。
3 递归去重
4.forEach+indexOf定义一个空数组,通过 forEach 循环,indexOf 判断值是否是-1,如果是 push 到新的数组中
14.说⼀下 map ⽅法、forEach ⽅法、filter ⽅法的作⽤以及他们之间的区别?
forEach() ⽅法: 循环原来的数组map() ⽅法: 循环原数组并映射⼀个新数组出来filter() ⽅法: 过滤不需要的数组元素
15.说说 JavaScript 中的数据类型?存储上的差别?
js 数据类型分为两种:基本数据类型 和 复杂数据类型(又叫引用数据类型)
差别是:基本数据类型存在栈里 复杂数据类型存在堆里
基本数据类型有: string number boolean undefined null symbol
复杂数据类型 统称为 Object 比较常用的有: array object 正则对象 时间对象等
除此之外还有一个新增的 bigInt 类型(可以表示范围更大的整数值)
16.检测数据类型的方法 (数据类型的判断 ):
typeof对于基本类型,除 null 以外,均可以返回正确的结果。对于引⽤类型,除 function 以外,⼀律返回 object 类型。对于 null ,返回 object 类型。对于 function 返回 function 类型。
instanceof可以判断 复杂数据类型 返回 布尔值 不能判断基本数据类型arr instanceof Array原理是 实例的隐式原型(proto)是等于构造函数的显式原型的(prototype)
constructor 构造函数因为每一个实例化的对象都有一个 constroctor(构造函数)指向构造函数本身基本和复杂类型都能判断出来 除了 null 和 undefined 是⽆效的对象,因此是不会有 constructor 存在的let num1 = 20;console.log(num1.constructor == Number);
toString借助 Object 对象的 toString 方法 检测数据类型Object.prototype.toString.call(10)
17 bind call apply 的区别?
这三个都可以改变 this 的指向call apply 会让函数立刻执行 call 接收的参数是一个参数列表 apply 是数组bind 不会让函数立刻执行
18. Javascript 本地存储的⽅式有哪些?区别及应⽤场景
一般有三种存储的方法:
cookie,
localStorage,
sessionStorage
cookie 的最大存储是 4kb 可以设置失效时间,到期自动清除 每次会跟随 http 请求一起发送所以不安全,而且操作麻烦,没有方便的 api 方法
localStorage 是 h5 新增的存储 不能设置过期时间 除非主动删除 否则就一直存在
有方便的 api 存储大小是 5msessionStorage 是临时存储 跟 localStorage 很像 不同的是关闭页面会立即清除
19. 什么是防抖和节流?有什么区别?如何实现? 很重要 10 遍
作⽤:本质上是优化⾼频率执⾏代码的⼀种⼿段 ,防止用户不停的触发某一操作节省流量。
防抖:就是指触发事件后 在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。(都是用计时器实现应用场景)
节流:就是指连续触发事件,但是在 n 秒中只执行一次函数。只有⼀次⽣效 (节流会稀释函数的执行频率)
应⽤场景防抖在连续的事件,只需触发⼀次回调的场景有:搜索框搜索输⼊。只需⽤户最后⼀次输⼊完,再发送请求⼿机号、邮箱验证输⼊检测窗⼝⼤⼩ resize 。只需窗⼝调整完成后,计算窗⼝⼤⼩。防⽌重复渲染。节流在间隔⼀段时间执⾏⼀次回调的场景有:滚动加载,加载更多或滚到底部监听搜索框,搜索联想功能
20.说说你对闭包的理解?闭包使⽤场景?
简单理解就是函数中套了一个函数,内层函数可以访问外层函数的变量,有时候需要用到函数内的局部变量,在正常情况下是不能读取到的,这时候需要用到闭包。
优点:闭包因为长期驻扎在内存中,可以重复使用变量,不会造成变量污染。
缺点:由于闭包⻓期驻留内存,则⻓期这样会导致内存泄露。解决方法是在退出函数之前,将不使用的变量全部删除。
# 21.谈谈 this 对象的理解? (不会 10 遍)
在 js 中,this 的意思为“这个;当前”,是一个指针型变量,它动态指向当前函数的运行环境。
this 跟函数相关,谁调用这个函数,this 就指向谁,所以 this 跟函数调用时候的环境而不是定义时候的环境相关。
如果没有调用者,就指向全局对象 window。
1. 构造函数
构造函数里的 this 指向实例化对象
2. 箭头函数
箭头函数并没有this对象,它内部的this是箭头函数声明所在的对象。
3,普通函数 的内部this 指向调用函数的对象
4. dom 节点调用一个事件 就指向这个 dom
5. 全局调用 就指向 widnow
6. 计时器里的 this 永远指向 window
# 22.绑定事件的方式
html 标签内绑定事件
dom0 级事件 box.onclick 直接绑定
dom2 级 事件监听 addEventListener
事件监听是一种绑定事件的方式 第一个参数是事件名字 第二个是执行函数 第三个是冒泡或者捕获
事件监听的好处是 可以 设置冒泡捕获 还能多次绑定 还能 移除事件
```js
box.addEventListener(
"click",
function () {
console.log(this.innerHTML);
},
false
);
```
# 23.同步异步 宏任务 微任务 事件循环(不会 10 遍)
在 js 中 任务的执行模式有两种 同步(log,for 循环等等) 和 异步(ajax 请求 计时器等)
在异步任务中 又将任务分成了宏任务和微任务,宏任务(计时器,ajax 请求)是浏览器发起的 微任务(promise.then)是 js 自身发起的 在异步任务中 微任务先执行 宏任务后执行
所以任务的执行顺序是:
同步先执行-》异步里的微任务-》异步里的宏任务
promise 本身是一个同步 then 是一个异步的微任务
事件循环 event loop
1. 整个 script 标签是一个宏任务 最开始的时候 会执行整体代码
2. 进行同步任务和异步任务的区分
3. 同步任务直接执行 异步任务进行微任务和宏任务的划分
4. 同步执行完了 执行微任务队列里的微任务 完成后再执行宏任务队列里的宏任务
5. 直到执行完成就进行下一次轮回
# 24.事件委托
把本来该绑定给自己的事件 委托给父元素甚至根元素 绑到他们身上 这叫事件委托
事件委托的原理是冒泡
使用场景
如果有一个列表 里面很多 li 如果都绑事件消耗很大 所以直接把事件绑给 ul 这就叫事件委托