css
1,盒模型 2,如何实现一个最大的正方形 3,一行水平居中,多行居左 4,水平垂直居中 5,两栏布局,左边固定,右边自适应,左右不重叠 6,如何实现左右等高布局 7,画三角形 8,link @import导入css 9,BFC理解
js
1,判断 js 类型的方式 2,ES5 和 ES6 分别几种方式声明变量 3,闭包的概念?优缺点? 4,浅拷贝和深拷贝 5,数组去重的方法 6,DOM 事件有哪些阶段?谈谈对事件代理的理解 7,js 执行机制、事件循环 8,介绍下 promise.all 9,async 和 await, 10,ES6 的 class 和构造函数的区别 11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式, 12,介绍一下rAF(requestAnimationFrame) 13,javascript 的垃圾回收机制讲一下, 14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?
开源分享:docs.qq.com/doc/DSmRnRG… this.dialogVisibility = false //关闭弹出框 next(false) //回到当前页面, 阻止页面跳转 }else if(this.saveMessage === false) { alert('请保存信息后退出!') //弹出警告 next(false) //回到当前页面, 阻止页面跳转 }else { next() //否则允许跳转 }
## 53.谈谈Vue路由模式,路由有哪些模式
在vue-router路由对象中,路由有两种模式:hash和history,而默认的是hash模式.前端路由目前主要有两种方法:
1、利用url的hash,就是常用的锚点(#)操作,类似页面中点击某小图标,返回页面顶部,JS通过hashChange事件来监听url的改变,IE7及以下需要轮询进行实现。一般常用框架的路由机制都是用的这种方法,例如Angualrjs自带的ngRoute和二次开发模块ui-router,react的react-route,vue-route…
2、利用HTML5的History模式,使url看起来类似普通网站,以”/”分割,没有”#”,但页面并没有跳转,不过使用这种模式需要服务器端的支持,服务器在接收到所有的请求后,都指向同一个html文件,通过historyAPI,监听popState事件,用pushState和replaceState来实现。
## 54.路由守卫的作用,全局和局部使用的差异是什么
全局路由守卫:就是在整个网页中,只要发生了路由的变化,都会触发。全局导航守卫主要包括两个函数,分别为:beforeEach、afterEach。局部路由守卫:(组件内的守卫)只有当前路由使用。
路由加载之前触发:beforeRouteEnter (to, from, next)
更新路由之前触发:beforeRouteUpdate (to, from, next)
离开当前路由之前触发:beforeRouteLeave (to, from, next)
## 55.vue路由有哪几种
this.$router.push(obj) 跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面this.$router.replace(obj) 跳转到指定url路径,但是history栈中不会有记录this.$router.go(n) 向前或者向后跳转n个页面,n可为正整数或负整数
## 56.说一下vue路由跳转方式
1、router-link 【实现跳转最简单的方法】<router-link to='需要跳转到的页面的路径>浏览器在解析时,将它解析成一个类似于<a>的标签。**div和css样式略**
2、this.$router.push({ path:’/user’})
3、this.$router.replace{path:‘/’ }类似,不再赘述
57.vue路由的数据传参,params,query的区别。使用params什么时候会生效,什么时候不会生效,params有时会失效,为什么
query和params区别query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传,刷新页面id还在 params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失 注意:如果提供了path,params会被忽略而导致失效。 通过官网我们知道路由中的name是不能重复的,而path是可以的。所以在函数式编程中,我们可以用变量来控制路径。
58.路由守卫路由拦截如何配置
通常在项目里,我们需要用户进行登录,才能让用户查看项目。在后台管理系统中,会根据不同的用户权限展示不同的内容。在用户访问页面之前,我们通过全局前置守卫对路由进行拦截,看看你是不是可以通过。通过的标准是否登录,如果登录就通过放行,没有通过就打回。
// 不需要路由验证页面
const whiteList = ['login', 'index']
router.beforeEach((to, from, next) => {
// 确定用户是否已登录
const hasToken = false // 这里就是路由是否通过标准,一般都是通过token来验证
if (hasToken) { // 登录
if (to.path === '/login') {
// 如果已登录,请重定向到主页
next({ path: '/index' })
return
}
next()
} else {
if (whiteList.indexOf(to.name) !== -1) {
// 在免费登录白名单中,直接进入
next()
} else {
// 没有访问权限的其他页将重定向到登录页。
next(`/login`)
}
}
})
需要注意的一点是,用户没有登录,是需要跳转到登录页面,如果在白名单里面没有登录页或者没有next(),页面一直跳转直到内存溢出。
每个项目的验证是否拥有权限不一样,权限判断那一块可以根据自己的实项目需求来进行操作。
59.写公用组件的时候,怎么提高可配置性
- 带着开放封闭原则的视角思考
- 开放原则,是说我们需要将有可能变动的属性,通过props接口的方式暴露给组件的调用者。
- 封闭原则,意思是我们需要将该组件的通用能力及逻辑封装再组件内部,让调用者使用更加方便
- 组件的可配置性需要结合实际的业务需求具体分析
- 假设我们要封装一个Tab选项卡组件,实际功能交互可参考组件 | Element
- 组件的开放原则实践
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| value / v-model | 绑定值,选中选项卡的 name | string | — | 第一个选项卡的 name |
| type | 风格类型 | string | card/border-card | — |
| closable | 标签是否可关闭 | boolean | — | false |
| addable | 标签是否可增加 | boolean | — | false |
| editable | 标签是否同时可增加和关闭 | boolean | — | false |
| tab-position | 选项卡所在位置 | string | top/right/bottom/left | top |
| stretch | 标签的宽度是否自撑开 | boolean | - | false |
| before-leave | 切换标签之前的钩子,若返回 false 或者返回 Promise 且被 reject,则阻止切换。 | Function(activeName, oldActiveName) | — | — |
上面的表格为Tab组件提供的props配置接口,它们需要遵循如下特点,可以极大提高可配置性:
- 配置项要尽可能多的,覆盖到业务中可能出现的每一种情况。
- 保证组件在每一项配置缺省状态下都能够正常表现
- 每一项配置都应该具备合理的默认值。
- 组件的封闭原则实践
| 事件名称 | 说明 | 回调参数 |
| tab-click | tab 被选中时触发 | 被选中的标签 tab 实例 |
| tab-remove | 点击 tab 移除按钮后触发 | 被删除的标签的 name |
| tab-add | 点击 tabs 的新增按钮后触发 | — |
| edit | 点击 tabs 的新增按钮或 tab 被关闭后触发 | (targetName, action) |
上面的表格为Tab组件所提供的自定义事件,但是事件相关的逻辑实现,已经完全在组件内部封装好了,组件使用者,只需要按需绑定事件即可对组件的行为做出监听,这些事件也需要遵循如下特点,才能保证该组件的可配置性:
- 事件函数相关逻辑,必须在组件内部封装完善
- 自定义事件函数在触发时,必须能够返回相关的参数
- 例如 @tab-click 事件会给函数返回,被点击菜单的index下标等信息
- 事件函数本身也需要能够在缺省状态下,保持组件的正常运行。
60.vue-router怎么生成动态地址
- 动态路由配置的应用场景
- 一般我们在使用vue-router进行路由管理的时候,是通过如下方式配置路由与组件之间的映射关系:
// router/index.js配置文件
const router = new VueRouter({
routes:[
{
path:'/login',
component:()=>import ('../views/Login') //登录路由
},
{
path:'/reg',
component:()=>import ('../views/Reg') //注册路由
},
{
path:'/admin',
component:()=>import ('../views/Admin') //这是一个管理员才能访问的路由
},
{
path:'/vip',
component:()=>import ('../views/Vip') //假设,这是要给vip用户才能访问的路由
},
]
})
但是在后台管理平台这种类型的项目中,我们需要让拥有不同角色权限的用户,访问不同的菜单及路由,如上述代码所示,部分路由只有管理员才能访问,而另外一部分路由只能vip用户才能访问,所以需要用到vue-router提供的addRoute方法来动态管理这一部分路由配置。
2.本地只配置通用路由
我们为了实现路由的动态配置,需要将上述路由配置进行拆分,本地配置文件中,只保留通用的路由映射。
const router = new VueRouter({
routes:[
{
path:'/login',
component:()=>import ('../views/Login') //登录路由
},
{
path:'/reg',
component:()=>import ('../views/Reg') //注册路由
}
]
})
3.后端为每个用户分配一个角色,随登录接口下发给前端
app.get('/login',(req,res)=>{
//此处需要实现登录相关逻辑
res.send({
username:'张三丰',
role:'admin', //标志当前用户角色
routerList:[ //此处的路由配置,也可以通过独立接口向前端提供
{
path:'/admin',
component:()=>import ('../views/Admin') //这是一个管理员才能访问的路由
},
...此处可能会有很多其他路由,这些路由数据应该由专门的数据表来存储
]
})
})
4.前端登录并动态获取路由配置
前端登录成功后,会得到后端动态下发的,跟自己账号角色相匹配的路由数据,此时可以通过addRoute方法,将这些动态获取的路由配置数据包,设置给router对象
// views/Login.vue 登录面板
axios.get('/login',(result)=>{
let {routerList} = result
routerList.forEach((item) => {
this.$router.addRoute(item)
})
})
61.vue-router有哪些方法
- router.beforeEach 路由守卫
- 我们可以使用这个方法,按需拦截用户访问某些敏感路由,例如:
router.beforeEach((to,from,next)=>{ //路由的全局前置守卫
if(to.path.indexOf('/account')==-1){ //判断用户访问的是不是个人中心
next() //不是个人中心,直接放行
}else{
if(store.state.my.userInfo){ //判断登录状态
next() //如果已经登录,直接放行
}else{
next('/login') //如果没有登录,则跳至登录页
}
}
})
2.router.push 编程式导航
通过编程式导航,我们可以通过事件的方式触发路由跳转
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
3.router.go、router.back、router.forward 路由的进入与返回
router.go 作用等同于window.history.go
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
4.router.addRoute 动态设置路由映射
添加一条新路由规则。如果该路由规则有 name,并且已经存在一个与之相同的名字,则会覆盖它。
axios.get('/login',(result)=>{ //通过异步接口获取对应用户的特有路由配置
let {routerList} = result
routerList.forEach((item) => {
this.$router.addRoute(item) //通过addRoute方法依次将路由配置设置给router对象
})
})
62.谈谈对MVVM的理解
- 什么是MVVM
- 不管是MVC,MVP,或者MVVM,都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式。不同于设计模式(Design Pattern),只是为了解决一类问题而总结出的抽象方法,一种架构模式往往使用了多种设计模式。MVVM,可以拆分为Model-View-ViewModel来理解:
- Model - 数据模型,可以对应到真实开发过程中的数据包
- View - 视图层,布局和外观,可以对应到真实开发中的DOM结构
- ViewModel - 扮演“View”和“Model”之间的使者,帮忙处理 View 视图层的全部业务逻辑
- 为什么使用MVVM框架
- 要回答这个问题,我们需要对比一下,在使用MVVM框架之前,我们是如何完成前端交互的。
- 为了修改某个视图节点中的内容信息,我们需要频繁人为操作DOM,效率低下
- 使用前
为了修改某个视图节点中的内容信息,我们需要频繁人为操作DOM,效率低下
var dom = document.querySelector('div');
dom.innerHTML = '张三丰';
dom.style.color = 'red';
- 使用后
当name数据发生变化的时候,视图区域的name自定触发更新,极大提高开发效率
<div>{{name}}</div>
data:{
name:'张三丰'
}
3.通过上述案例进一步理解MVVM
- name数据包可以认为就是那个Model
- div 节点可以认为就是那个View
- 而Vue提供的语法环境所支持的数据驱动能力,就可以认为是那个ViewModel
63.vue-router有什么组件
- router-link 组件
- 组件支持用户在具有路由功能的应用中 (点击) 导航。通过 to 属性指定目标地址,默认渲染成带有正确链接的 标签,可以通过配置 tag 属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名。
- 比起写死的 会好一些,理由如下:
- 无论是 HTML5 history 模式还是 hash 模式,它的表现行为一致,所以,当你要切换路由模式,或者在 IE9 降级使用 hash 模式,无须作任何变动。
- 在 HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面。
- 当你在 HTML5 history 模式下使用 base 选项之后,所有的 to 属性都不需要写 (基路径) 了。
- router-view组件
- 组件是一个 functional 组件,渲染路径匹配到的视图组件。 渲染的组件还可以内嵌自己的 ,根据嵌套路径,渲染嵌套组件。
- 其他属性 (非 router-view 使用的属性) 都直接传给渲染的组件, 很多时候,每个路由的数据都是包含在路由参数中。
- 因为它也是个组件,所以可以配合 和 使用。如果两个结合一起用,要确保在内层使用 :
<transition>
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
64.vue-cli怎么自定义组件
1.组件封装
// HelloWorld.vue组件
<template>
<div>
自定义组件
</div>
</template>
<script>
export default {
data() {
return {
key: 'value'
}
},
// 组件交互
}
</script>
<style scoped lang="less">
// 组件样式
</style>
2.局部注册调用组件
// Test.vue
<template>
<div>
<HelloWorld/>
</div>
</template>
<script>
import HelloWorld from './HelloWorld.vue'
export default {
components:{
HelloWorld
}
}
</script>
<style lang="less" scoped>
</style>
全局注册使用
- 先在main.js中全局注册该组件
import Vue from 'vue'
import App from './App.vue'
//全局注册
import HelloWorld from './components/HelloWorld.vue'
Vue.component('hello-world',HelloWorld)
new Vue({
render: h => h(App),
}).$mount('#app')
- 然后在需要使用公共组件的业务组件中,调用该组件
// Test.vue
<template>
<div>
<hello-world></hello-world>
</div>
</template>
<script>
export default {
}
</script>
<style lang="less" scoped>
</style>
65.谈谈对vue-loader的理解,实现原理是什么
一. vue-loader的作用是什么
- 首先我们需要达成共识的是,目前浏览器,只能识别普通的html、css、javascript。
- 但是为了能够方便使用vue的组件化开发,需要我们将代码写在.vue单文件组件中。
- .vue文件,以及其内部的template、style、script区域代码,不能直接交给浏览器去解析,因为它解析不了。
- 所以我们需要一个vue-loader进行.vue单文件组件代码的转换,也就是
- .vue方便开发 ------> vue-laoder协助翻译 -----> 浏览器才能展示
二. vue-loader 工作原理
vue-loader 的工作流程, 简单来说,分为以下几个步骤:
- 将一个 .vue 文件 切割成 template、script、styles 三个部分。
- template 部分 通过 compile 生成 render、 staticRenderFns。
- 获取 script 部分 返回的配置项对象 scriptExports。
- styles 部分,会通过 css-loader、vue-style-loader, 添加到 head 中, 或者通过 css-loader、MiniCssExtractPlugin 提取到一个 公共的css文件 中。
- 使用 vue-loader 提供的 normalizeComponent 方法, 合并 scriptExports、render、staticRenderFns, 返回 构建vue组件需要的配置项对象 - options, 即 {data, props, methods, render, staticRenderFns...}。
66.vue的路由有哪些钩子函数,可以用来做什么
一、全局守卫
顾名思义,是要定义在全局的,也就是我们 index.js 中的 router 对象。
1. beforeEach
全局前置守卫,在路由跳转前触发,它在 每次导航 时都会触发。
通过 router.beforeEach 注册一个全局前置守卫。
router.beforeEach((to, from, next) => {
console.log('??~ to:', to);
console.log('??~ from:', from);
next();
})
参数
beforeEach 全局前置守卫接收三个参数
- to: Route: 即将要进入的目标路由对象
- from: Route: 当前导航正要离开的路由对象
- next: Function: 一定要调用该方法不然会阻塞路由。
注意: next 参数可以不添加,但是一旦添加,则必须调用一次,否则路由跳转等会停止。
next()方法的几种情况
- next(): 进行管道中的下一个钩子。
- next(false): 中断当前的导航。回到 from 路由对应的地址。
- next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址,可传递的参数与 router.push 中选项一致。
- next(error): 导航终止,且该错误会被传递给 router.onError() 注册过的回调。、
2. beforeResolve
全局解析守卫,在路由跳转前,所有 组件内守卫 和 异步路由组件 被解析之后触发,它同样在 每次导航 时都会触发。
通过 router.beforeResolve 注册一个全局解析守卫。
router.beforeResolve((to, from, next) => {
next();
})
回调参数,返回值和 beforeEach 一样。也可以定义多个全局解析守卫。
3. afterEach
全局后置钩子,它发生在路由跳转完成后,beforeEach 和 beforeResolve 之后,beforeRouteEnter(组件内守卫)之前。它同样在 每次导航 时都会触发。
通过 router.afterEach 注册一个全局后置钩子。
router.afterEach((to, from) => {
console.log('??~ afterEach:');
})
这个钩子的两个参数和 beforeEach 中的 to 和 from 一样。然而和其它全局钩子不同的是,这些钩子不会接受 next 函数,也不会改变导航本身。
二、路由守卫
顾名思义,就是跟路由相关的钩子,我们的路由守卫只有一个,就是 beforeEnter。
1. beforeEnter
需要在路由配置上定义 beforeEnter 守卫,此守卫只在进入路由时触发,在 beforeEach 之后紧随执行,不会在 params、query 或 hash 改变时触发。
//index.js
{
path: '/a',
component: () => import('../components/A.vue'),
beforeEnter: (to, from) => {
console.log('??~ beforeEnter ');
},
},
beforeEnter 路由守卫的参数是 to、from、next ,同 beforeEach 一样。
三、组件守卫
顾名思义,是定义在路由组件内部的守卫。
1. beforeRouteEnter
//A.vue
beforeRouteEnter(to, from,next) {
console.log('??~ beforeRouteEnter');
},
路由进入组件之前调用,该钩子在全局守卫 beforeEach 和路由守卫 beforeEnter 之后,全局 beforeResolve 和全局 afterEach 之前调用。
参数包括 to,from,next。
该守卫内访问不到组件的实例,也就是 this 为 undefined,也就是他在 beforeCreate 生命周期前触发。
2. beforeRouteUpdate
//A.vue
beforeRouteUpdate(to, from) {
console.log('??~ beforeRouteUpdate');
},
对于 beforeRouteUpdate 来说,this 已经可用了,所以给 next 传递回调就没有必要了。
3. beforeRouteLeave
//A.vue
beforeRouteLeave(to, from) {
console.log('??~ beforeRouteLeave');
},
对于 beforeRouteLeave 来说,this 已经可用了,所以给 next 传递回调就没有必要了。
四、总结
完整的导航解析流程
导航被触发。
在失活的组件里调用 beforeRouteLeave 守卫。
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫。
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫。
导航被确认。
调用全局的 afterEach 钩子。
触发 DOM 更新。
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
67.计算属性通常用来干什么
一、理解计算属性的概念
计算属性的英文是computed,其实很多时候,一些概念从英文对照为中文后,会导致其中一些含义发生错位与丢失,我们要想更好的理解computed计算属性,可以将这个概念分为两部分来看:
- 计算
- 这里的计算,是有种宏观的概念,指的是对于数据包的一种操作,比如:筛选、过滤、新增、删除等。
- 说明computed本身是具有处理数据的能力。
- 属性
- 属性的意思是,一种可以读取、渲染的数据,性质跟data的作用相似
- 说明computed最终会给出一个数据供页面渲染使用。
由此,我们可以得出一个结论:
computed计算属性,负责将一些数据在其内部按照指定逻辑处理后,最终给出处理后的结果数据,给到组件、页面进行渲染使用,可以让开发者更加便捷的处理一些动态变化的数据需求。
二、computed计算属性的特点
- computed内部对data做出处理,其处理结果可以像data一样,可以直接在页面中渲染。
- computed内部逻辑会自动识别到data的变化,从而做出新的操作,得出新的数据,从而自动更新视图。
- computed处理的数据具有缓存的能力,如果data不变,则页面中调用的都是computed第一次执行时的运算结果,提高渲染性能。
三、computed应用场景
当某个前端已经指定的data数据包,如果我们渲染前,对其有过滤、筛选等操作需求,就可以使用computed
- 字符串翻转的官方案例,就是将一个普通字符串翻转后再渲染到视图。
new Vue({
el:'#app', //占地盘
data:{
'msg':'Hello Vue' //自定义数据包
},
computed:{
reMsg(){ //专门用于反转msg的计算属性
return this.msg.split('').reverse().join('')
}
}
})
- 如果一个班级中有很多学生,每个学生通过一个对象来表达,现在我们需要根据学员成绩来进行动态切换显示:全部学员、及格学员、不及格学员,这种在本地进行筛选的需求可以快速通过computed实现,代码逻辑大致如下:
new Vue({
el:'#app', //占地盘
data:{
stu:[
{name:'张三丰',score:100},
{name:'DDK',score:50},
{name:'张翠山',score:60},
{name:'张无忌',score:90},
{name:'PDD',score:45}
],
status:0 //0全部 1及格 2不及格
},
computed:{
filterStu(){
let {stu,status} = this
// let stu = this.stu
// let status = this.status
switch (status) {
case 1: //及格
return stu.filter(item=>{
return item.score>=60
})
case 2: //不及格
let arr = stu.filter(item=>{
return item.score<60
})
return arr
default: //全部
return stu
}
}
}
})
68.computed和watch的区别
上一个问题我们已经详细探讨了computed的相关特征,在这里我们可以逐一对比一下:
- 两者都跟data有关,区别在于
- computed 在处理完data后,会提供一个新的数据包以供使用
- watch 只会监听某个指定data的变化,执行相关的逻辑,不会提供新的数据
- 两者的使用倾向不同
- computed 内部虽然有逻辑,但是使用时更加多的关心其提供的新数据包
- watch 更加关注data数据变化所引发的行为逻辑
69.状态管理器的数据走向是什么
一、什么是状态管理?
状态管理就是,把组件之间需要共享的状态抽取出来,遵循特定的约定,统一来管理,让状态的变化可以预测。
二、为什么需要状态管理?
状态共享
组件之间通常会有一些共享的状态,在 Vue 或者 React 中我们一般会将这部分状态提升至公共父组件的 props 中,由父组件来统一管理共享的状态,状态的改变也是由父组件执行并向下传递。这样会导致两个问题:
- 需要将共享的状态提升至公共的父组件,若无公共的父组件,往往需要自行构造
- 状态由父组件自上而下逐层传递,若组件层级过多,数据传递会变得很冗杂
变化跟踪
在应用调试过程中,可能会有跟踪状态变化过程的需求,方便对某些应用场景的复现和回溯。这时候就需要统一对状态进行管理,并遵循特定的约定去变更状态,从而让状态的变化可预测。
三、单项数据流
因为在真实项目开发过程中,Store状态管理器中的数据会在很多组件中用到,如果不设定一个统一的规范去管理数据,最终将会导致数据混乱、使得项目变得难以维护。所以vuex状态管理器设计了如下几个核心api,与视图之间进行交互配合:
- state
- vuex提供的,用以集中存储共享的数据。
- mutations
- vuex提供的,专门用以触发state数据变化的方法集,并且要求mutations的方法执行结果必须时可预测的,在其内部不能出现异步请求等不可预测的逻辑。
- actions
- vuex提供的,专门用于让vuex进行异步请求处理的方法集,可选择使用。
- view
- 视图层,整个项目组件的代称,我们在此处消费状态管理器提供的数据、方法。
数据走向必须遵循单向数据流的规范:
- 当我们初始化使用状态机数据时的流程是
- store---->state----> view
- 当组件内部想要本地更新状态管理器的数据,其流程是
- view触发---->mutations---->state---->store---->view更新
- 当组件内部想要在异步请求后,再更新本地状态管理器的数据,其流程是
- view触发---->actions---->mutations---->state---->store---->view更新
70.vuex数据丢失怎么解决
vuex的 store 中的数据是保存在运行内存中的,当页面刷新时,页面会重新加载 vue 实例,vuex 里面的数据就会被重新赋值,这样就会出现页面刷新vuex中的数据丢失的问题。 如何解决浏览器刷新数据丢失问题呢?
方法一:手动操作本地存储
全局监听,页面刷新的时候将 store 里 state 的值存到 sessionStorage 中,然后从sessionStorage 中获取,再赋值给 store ,并移除 sessionStorage 中的数据。在 app.vue 中添加以下代码:
created() {
window.addEventListener('beforeunload',()=>{
sessionStorage.setItem('list', JSON.stringify(this.$store.state))
})
try{
sessionStorage.getItem('list') && this.$store.replaceState(Object.assign({},this.$store.state,JSON.parse(sessionStorage.getItem('list'))))
}catch(err) {
console.log(err);
}
sessionStorage.removeItem("list");
}
方法二:安装 vuex-persistedstate 插件
1. npm install vuex-persistedstate -S //安装插件
2. 在 store/index.js 文件中添加以下代码:
import persistedState from 'vuex-persistedstate'
const store = new Vuex.Store({
state:{},
getters:{},
...
plugins: [persistedState()] //添加插件
})
这时候就需要使用 sessionStorage 进行存储,修改 plugins 中的代码
plugins: [
persistedState({ storage: window.sessionStorage })
]
71.怎么理解v-for的key值
key的值一般为string或则number的类型,它用于解决在进行虚拟dom更新时的更新对象快速定位,虚拟dom更新需要逐个对比虚拟dom对象绑定的数据,这个过程称为diff算法,所以key也是提升diff算法的一个标识符,因为数组可能会进行排序以及增删等动作,所以使用数组的下标来定义key是没有任何作用的
72.能不能自己实现v-model的功能
可以的,自定义一个指令,给节点绑定上添加input的功能,数据使用value绑定,在用户输入数据的时候,采用oninput事件来进行数据获取,并实时更新value数据,即可实现v-model的功能
73.Vue双向数据绑定,怎么知道数据变了
在Vue2.x中采用ES5的对象属性定义方法(Object.defineProperty)来给每一个数据添加额外属性(getter和setter属性)进行数据拦截,在vue内部实现对拦截数据的setter方法数据更新消息发布机制来获取数据更新消息,对getter方法实现消息订阅机制来获取数据更新
74.退出登录头像还在是什么原因怎么办
多方面的原因引起的:
1、头像信息是否采用了应用缓存机制,如果有需要清除H5应用缓存
2、头像缓存在webStorage中,退出没有清除,直接清除
3、缓存在移动设备的数据,也需要清除
4、缓存在vuex或redux中的数据,直接清除即可
5、浏览器缓存,清除cookie或则强制清除浏览器缓存
75.vue数据双向绑定如何实现,如果不用vue,用原生js代码怎么实现
1、获取传递进来的数据,然后对所有数据添加getter和setter方法,并在setter方法中添加消息订阅回调方法,在getter方法中实现数据更新发布回调方法
2、对作用域内的所有dom节点进行数据以来处理,根据不同指令来实现不同订阅或发布回调方法绑定:如v-model绑定的数据对象,在oninput事件中添加消息发布回调方法绑定,在v-text中添加value数据更新消息订阅回调方法绑定
当在输入框输入值的时候,出发oninput事件,出发消息发布回调方法更新数据
当数据发生变化,触发订阅方法,执行dom数据更新方法,把最新数据更新到视图上
76.vue如何实现响应式,说一下原理,它有什么缺点?
它使用ES5的Object.defineProperty方法把所有的属性全部改为setter和getter属性,在每一个组件中都有一个watcher对象,当数据被赋值或变更的时候会通知页面的render方法对数据进行重新渲染,达到数据和视图的响应更新
因为js的固有特性,不能动态观察对象动态添加、删除属性和数组的长度添加,所以vue2.x不能够动态进行数据双向绑定,需要调用$set、$delete这些方法来实现动态添加双向绑定属性
77.include,exclude的区别
这个是webpack中常常用于指定加载,对哪些文件进行加载的排除或包含的一个属性,include配置的文件路径中的所有文件都将采用配置的loader进行文件加载处理,exclude是配置的路径都不要进行这个加载器的处理
78.你说你使用懒加载优化页面,用的哪个版本的vue,看过源码吗, vue2.0不能实现懒加载
这个与vue没有太大关系,采用的是ES6的动态加载机制来实现页面的懒加载,主要使用的webpack语法库为:@babel/plugin-syntax-dynamic-import,在对页面引入的时候,需要把引入方式从:import MyComponent from 'path' 修改为:const MyComponent = () => import('path')
79.运用多个组件库,提取公共代码进行压缩,发现js代码过大,怎么处理
对组件不要做全局引入,可以采用动态引入和页面局部引入机制,减少文件首次加载的文件大小和文件进行打包时把所有依赖进行一次性打包造成的文件过大问题
80.vue中爷孙通信怎么实现
**这里分享一份由字节前端面试官整理的「2021大厂前端面试手册」,内容囊括Html、CSS、Javascript、Vue、HTTP、浏览器面试题、数据结构与算法。全部整理在下方文档中,共计111道**
### HTML
* HTML5有哪些新特性?
* Doctype作⽤? 严格模式与混杂模式如何区分?它们有何意义?
* 如何实现浏览器内多个标签页之间的通信?
* ⾏内元素有哪些?块级元素有哪些? 空(void)元素有那些?⾏内元 素和块级元素有什么区别?
* 简述⼀下src与href的区别?
* cookies,sessionStorage,localStorage 的区别?
* HTML5 的离线储存的使用和原理?
* 怎样处理 移动端 1px 被 渲染成 2px 问题?
* iframe 的优缺点?
* Canvas 和 SVG 图形的区别是什么?

### JavaScript
**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://docs.qq.com/doc/DSmRnRGxvUkxTREhO)**
* 问:0.1 + 0.2 === 0.3 嘛?为什么?
* JS 数据类型
* 写代码:实现函数能够深度克隆基本类型
* 事件流
* 事件是如何实现的?
* new 一个函数发生了什么
* 什么是作用域?
* JS 隐式转换,显示转换
* 了解 this 嘛,bind,call,apply 具体指什么
* 手写 bind、apply、call
* setTimeout(fn, 0)多久才执行,Event Loop
* 手写题:Promise 原理
* 说一下原型链和原型链的继承吧
* 数组能够调用的函数有那些?
* PWA使用过吗?serviceWorker的使用原理是啥?
* ES6 之前使用 prototype 实现继承
* 箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?
* 事件循环机制 (Event Loop)
