++++8.rem布局原理是什么?如果给你750的设计图,怎么在350的屏幕上适配?++++
1.如何遍历对象?
1.使用Object.keys()遍历
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性).
var obj = {'0':'a','1':'b','2':'c'}; Object.keys(obj).forEach(function(key){ console.log(key,obj[key]); });
2.使用for..in..遍历
循环遍历对象自身的和继承的可枚举属性(不含Symbol属性).
var obj = {'0':'a','1':'b','2':'c'}; for(var i in obj) { console.log(i,":",obj[i]); }
3.使用Object.getOwnPropertyNames(obj)遍历
返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性).
var obj = {'0':'a','1':'b','2':'c'}; Object.getOwnPropertyNames(obj).forEach(function(key){ console.log(key,obj[key]); });
4.使用Reflect.ownKeys(obj)遍历
返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举.
var obj = {'0':'a','1':'b','2':'c'}; Reflect.ownKeys(obj).forEach(function(key){ console.log(key,obj[key]); });
2.使用过导航守卫吗?都有哪些导航守卫?
导航守卫的用途主要是在用户离开页面前提醒用户,和页面访问前先登录。共有7个钩子,其中全局钩子有3个,组件内钩子有3个,路由独享钩子有1个。
全局钩子
const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { // ...(2) //全局前置守卫 }) router.beforeResolve((to, from, next) => { // ...(6) //全局解析守卫 }) router.afterEach((to, from) => { // ...(7) //全局后置守卫 }
组件内钩子
export default { data(){}, beforeRouteEnter (to, from, next) { //....(5) // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 }, beforeRouteUpdate (to, from, next) { // ... (3) // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, beforeRouteLeave (to, from, next) { // ... (1) // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } }
路由独享钩子
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ...(4) } } ] })
参数解析
每个守卫方法接收三个参数: to: Route: 即将要进入的目标 路由对象,该对象有目标页的路由信息。 from: Route: 当前导航正要离开的路由,该对象有当前页的路由信息。 next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。 next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。 next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。 next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。 next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。 确保要调用 next 方法,否则钩子就不会被 resolved。
导航流程
导航被触发。 在失活的组件里调用离开守卫。 调用全局的 beforeEach 守卫。 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。 在路由配置里调用 beforeEnter。 解析异步路由组件。 在被激活的组件里调用 beforeRouteEnter。 调用全局的 beforeResolve 守卫 (2.5+)。 导航被确认。 调用全局的 afterEach 钩子。 触发 DOM 更新。 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
导航流程图(页面切换的执行顺序)

页面跳转前需登录
1)首先需要给isLogin为false,表示未登录,将该值初始在store内,
export default new Vuex.Store({ state: { isLogin: false, }, mutations: { handleLogin(state, login) { state.isLogin = login; }, }, });
2)在登录页- 登录按钮 设置点击时修改store内的isLogin为true,同时跳转到首页'/home'
methods: { ...mapMutations(['handleLogin']), handleSubmit() { // 修改store里的isLogin为true,表示登录了 this.handleLogin(true) this.$router.push({path: '/home'}) } }
3)每一次页面切换时,全局钩子beforeEach都会触发,在该钩子内判断 是否登录了?若没有登录,需要跳转到 登录页 '/login',否则 可以跳转
router.beforeEach((to, from, next) => { const isLogin = obj.state.isLogin; if (to.path === '/login' || isLogin) { next(); } else { next('/login'); } });
3.说说对keep-alive的理解?
Props:include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例。
用法:<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
主要用于保留组件状态或避免重新渲染。比如A页面现在滚动的位置是1/2,然后切换到B页面,再切换回来的时候页面的滚动位置还会保留。
<!-- 基本 --> <keep-alive> <component :is="view"></component> </keep-alive> <!-- 多个条件判断的子组件 --> <keep-alive> <comp-a v-if="a > 1"></comp-a> <comp-b v-else></comp-b> </keep-alive> <!-- 和 `<transition>` 一起使用 --> <transition> <keep-alive> <component :is="view"></component> </keep-alive> </transition>
注意,<keep-alive> 是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive> 要求同时只有一个子元素被渲染。
include 和 exclude prop 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:
<!-- 逗号分隔字符串 --> <keep-alive include="a,b"> <component :is="view"></component> </keep-alive> <!-- 正则表达式 (使用 `v-bind`) --> <keep-alive :include="/a|b/"> <component :is="view"></component> </keep-alive> <!-- 数组 (使用 `v-bind`) --> <keep-alive :include="['a', 'b']"> <component :is="view"></component> </keep-alive>
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。
max 最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
<keep-alive :max="10"> <component :is="view"></component> </keep-alive>
<keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例。
4.数组去重有哪些方法?
1.双层for循环
function removeSameEle(arr){ let res=[]; for(var i=0,len=arr.length;i<len;i++){ for(var j=0,resLen=res.length;j<resLen;j++){ if(arr[i]===arr[j]){//判断结果中是否已存在该数据了 break; } } //判断是否执行到了res的末尾 if(j===resLen){ res.push(arr[i]); } } return res; }
2.indexof或者includes
function removeSameEle(arr){ let res=[]; for(var i=0,len=arr.length;i<len;i++){ //if(!res.includes(arr[i])){ if(res.indexof(arr[i])<0){//判断结果中是否存在该数据了 res.push(arr[i]) } } return res; }
3.filter
function removeSameEle(arr){ let res=arr.filter((item,index,arr)=>{ return arr.indexOf(item)===index; }); return res; }
4.reduce唯一值去重
function removeSameEle(arr){ return arr.reduce((accu,cur)=>{ return accu.includes(cur)?[...accu]:[...accu,cur] },[]) }) }
5.set唯一值去重.此方法只能用于对简单数组进行去重.
function removeSameEle(arr){ return [...new Set(arr)]; }
6.Map去重
function removeSameEle(arr){ let map=new Map(); let res=[]; for(let i=0;len=arr.length;i<len;i++){ if(!map.has(arr[i])){ map.set(arr[i],arr[i]); res.push(arr[i]); } } return res; }
7.sort排序后去重
function removeSameEle(arr){ let res=[]; let sortedArr=arr.sort(); for(let i=0,len<sortedArr.length;i<len-1;i++){ if(i==0||sortedArr[i]!==sortedArr[i+1]){ res.push(sortedArr[i]); if(i===(len-2)){ res.push(sortedArr[i+1]); } } } return res; }
8.使用object.keys()去重
function removeSameEle(arr){ let res=[]; let obj={}; arr.forEach((item,index)=>{ if(!obj[item]){ obj[item]=item } }); for(let value of Object.values(obj)){ res.push(value); } return res; }
5.谈谈this的指向问题?
this指向谁,关键看函数是如何调用的.
函数调用模式=>this指向了window.
构造函数调用模式=>构造函数中的this指向新创建的对象.
方法调用模式=>方法内的this指向了调用方法的那个对象(谁调用方法,方法内this指向谁).
上下文调用模式=>修改this指向.call,apply,bind三个方法,函数都可以访问这三个方法.
call方法可以用来调用函数,第一个参数可以用来修改函数的this指向,除了第一个参数外,其他所有的参数都是用来给函数传递实参的.
apply方法可以用来调用函数,第一个参数可以用来修改函数的this指向,第二个实例列表参数,是数组里面的每一项作为实参传递的.
var newFn = fn.bind(thisArg) bind方法是不会立即调用函数,会创建并返回新函数,新函数newFn和fn函数长的一样,新函数内的this指向bind参数thisArg.
6.深拷贝和浅拷贝的理解?
浅拷贝:拷贝对象的属性,只会拷贝一层属性,拷贝得到另外一个对象.
存在的问题:只拷贝对象的一层属性,如果对象里面还有对象的话,只会把里面那个对象的地址赋值了.导致两个对象之间不是完全独立的,修改其中一个对象,会影响到另一个对象.
浅拷贝的适用场景:对象里面简单数据类型.
浅拷贝实现方法:
1.简单的赋值操作;
2.es6的Object.assign({},obj);
3.jQuery的$.extend({},obj)
深拷贝:拷贝对象的属性,如果对象里面还有对象,把里面对象也会拷贝一份,拷贝出来的对象和原对象是完全独立的,之间的修改不会互相影响.
深拷贝实现方法:
1.JSON.parse(JSON.stringify(obj)),如果obj里面有时间对象,正则表达式,error对象,函数或者undefined值,该方法不行.
2.jQuery的$.extend(true,{},obj);
3.lodash的_.clone(obj,true)和_.cloneDeep(obj)方法;
function deepClone(obj) { if (typeof obj !== "object") return; let newObj = obj instanceof Array ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key]; } } return newObj; }
7.单页面应用的优缺点?
只有一张web页面的应用,是一种从web服务器加载的富客户端。单页面跳转仅刷新局部资源,公共资源仅刷新一次,之后的操作交互,数据交互是通过路由、ajax来进行的,页面本身没有进行刷新。
优点:①前后端分离------前端负责界面显示,后端负责数据存储和计算
②良好的交互体验------用户不用刷新页面,页面显示流畅
③减轻服务器压力------服务器只出数据
④共用一套后端代码------多个客户端可共用一套后端代码
缺点:①seo难度高------数据渲染在前端进行,seo主要是让网站对于搜索引擎的爬虫更加友好,spa页数据动态生成,爬虫识别不了
②前进后退管理------单页面不能使用浏览器的前进后退,页面切换要自己建立堆栈管理,解决方法:利用url的散列+iframe实现
③初次加载耗时长
单页面应用怎么优化?
①注重TDK的写法------注意网站标题、关键字和描述的写法。
②网页标签的合理使用------标签的规范使用可以让搜索引擎更好的理解网页内容层次。(例如:h1> h2的逐级展示)尽可能使网页符合W3C的标准
③注重外联锚文本多样化------单页面网站不代表网站只有一个关键字,避免网站的过度优化,所以要多样化外联锚文本。(外联关键词加超链接)
④增加一个二级博客目录------可通过博客内容增加单页面网站的权重
⑤合理设置锚点------页面上合理的设置锚点,有利于用户快速到达想看的内容区域
⑥网站内容高质量------将用户的关注点尽可能完整的展示出来,通过不同区域展示相关内容,解决用户烦恼
⑦避免全是图片展示------网站文字内容少不利于搜索引擎抓取和索引
单页面网站seo同样需要站在用户的角度去思考,一个页面是否可以满足用户的需求,页面内容是否有权威性,是否利于用户对需求的获取,减少可以优化,避免过度优化。
8.rem布局原理是什么?如果给你750的设计图,怎么在350的屏幕上适配?
能够等比例的适配所有的屏幕.原理:因为rem的基准点是根元素html的字体大小,因此我们只需要设置不同屏幕的html的font-size大小不一样就可以达到不同屏幕的适配了.
rem开发步骤:
根据设计图定一个html的font-size,通常是100或50,比较好计算.
根据媒体查询,等比例的设置每一个屏幕的html的字体大小.
根据设计图量出来的盒子大小全部换成rem单位即可.
9.事件循环的理解?
JavaScript的执行机制简单来说就先执行同步代码,然后执行异步代码,而异步的代码里面又分为宏任务代码和微任务代码,先执行微任务,然后执行宏任务。首先会将所有JavaScript作为一个宏任务执行,遇到同步的代码就执行,然后开始分配任务,遇到宏任务就将它的回调分配到宏任务的队列里,遇到微任务的回调就分配到微任务的队列里,然后开始执行所有的微任务。执行微任务的过程还是遵循先同步然后分配异步任务的顺序,微任务执行完毕之后,一次Event-Loop的Tick就算完成了。接着挨个去执行分配好的宏任务,在每个宏任务里又先同步后分配异步任务,完成下一次Tick,循环往复直到所有的任务全部执行完成。
微任务包括:process.nextTick ,promise ,MutationObserver。
宏任务包括:script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。
10.跨域的理解?有哪些方式可以跨域?
浏览器的同源策略,出于安全的考虑,只要是协议、域名、端口有一个不同就算是跨域,ajax请求就会失败。浏览器有同源策略主要是为了防止CSRF(跨站点请求伪造)攻击,防止利用户的登录状态发起恶意请求。
JSONP: jsonp 和 XMLHttpRequest对象无关;本质是利用script的src属性跨域请求服务器.
jsonp最终目标: 跨域取数据.
jsonp 具体的实现步骤:
1- 前端需要准好一个方法, 将方法名字通过script标签传递给后台;
2- 后台 需要先获取方法名字, 在方法后先拼(),在括号中添加json数据;
3- 后台把填充好数据的方法调用,返回给前端;
4- 前端接收到后台返回的方法调用后,会立即执行 , 即可获取去当前参数中数据.
jsonp注意点:jsonp 只能发送get请求; jsonp 需求前后端配合完成.
CORS:使用自定义的HTTP头部让浏览器和服务器进行沟通,实现CORS的关键是后端,服务端设置Access-Control-Allow-Origin就可以开启,表示哪些域名可以访问资源。
document.domain:当二级域名相同时,例如a.test.html和b.test.html,只需要给两个页面都设置document.domain = 'test.html',就可以实现跨域。
postMessage:如a.html页面通过iframe嵌入了b.html页面,其中一个可以通过postMessage方法发送信息,另一页面通过监听message事件判断来源并接受消息。
https://juejin.im/post/5e948bbbf265da47f2561705
11.CORS的原理解释下?
CORS是W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS是跨源AJAX请求的根本解决方法。JSONP只能发GET请求,但是CORS允许任何类型的请求。
整个CORS通信过程都是浏览器自动完成的,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
其实,不管我们有没有设置 Access-Control-Allow-Origin: *,浏览器都会向我们的服务端发送请求,并且接收返回的内容。当浏览器解析的时候,发现Response Headers中没有Access-Control-Allow-Origin: *的头,并且设置为允许的情况下,浏览器会将请求返回的内容忽略掉,并且在命令行中报出跨域的错误。这是浏览器提供的一个功能。
但是Access-Control-Allow-Origin: *这样的设置明显是不安全的,因为这样相当于允许所有的域名访问,因此我们也可以设置某个特定的域名访问。比如:Access-Control-Allow-Origin: http://www.baidu.com,这样就只有在http://www.baidu.com域名访问的时候浏览器才可以正常的解析。
https://juejin.im/post/5cef28af51882550d41745ea
12.对cookie和session理解?
cookie和session是什么:
cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器发起请求时被携带并发送到服务器,它通常是告知服务端两个请求是否来自同一浏览器,保持用户的登录状态。
session代表着服务器在和客户端一次会话的过程,存储着用户会话所需的属性及配置信息,当用户在不同页面之间跳转时,使整个用户会话一直存在。
cookie和session不同点:
作用范围不同:cookie存储在客户端,session保存在服务器端。
存取的方式不同:cookie只能保存ASCⅡ,session可以存取任意数据类型。
有效期不同:cookie可设置长时间保持,session一般失效时间较短,或客户端关闭就会失效。
存储大小不同:单个cookie保存的数据不能超过4k,session可存储数据远高于cookie。
为什么需要cookie和session:
让服务器知道根它打交道的用户是谁以及用户的状态,浏览器发起第一次请求后服务端会返回一个sessionID存储到cookie中,当再次发起请求时服务端根据携带的cookie里的sessionID来查找对应的session信息,没有找到就说明没登录或登录失效,找到说明已经登录,可以进行之后的操作。
浏览器禁用cookie怎么办:
每次请求都携带一个SessionID的参数;或者使用token令牌,登录后服务端生成一个Token返回给客户端,以后客户端携带token进行数据请求。
使用cookie注意点:
不建议作为存储方式使用。首先会随请求携带,影响请求的性能,其次存储空间也太小,最后一些属性的使用也需要注意。value:如果作用于登录状态,需要加密。http-only:不能通过JavaScript访问到cookie,防止XSS(跨站脚本攻击)攻击。same-site:不能在跨域请求中携带cookie,防止CSRF(跨站点请求伪造)攻击。
13.对http缓存的理解?
14.用过CDN吗,怎么使用的?
15.在PC和移动端如何使用JS判断端型?
16.浏览器渲染HTML的过程?
17.在单页面中前端路由和后端路由什么不一样的?
15、对node了解吗,谈谈你对node的理解
16、实际项目中用到过吗?哪些地方用到的
17、图片轮播怎么做的
18、布局中两栏布局,左边固定,右边怎么适配
19、实际的项目地址有吗,可以看看吗
2、一个页面是如何渲染的
3、call aply bind三者区别
4、闭包了解么?讲一下
6、左侧固定右侧自适应怎么做讲下
7、什么是强缓存?什么是协商缓存
8、http1.0、2.0、3.0的区别
- 如何判断用户是否登录
10、promise有几种状态,分别是什么?
11、前端路由和后端路由的区别是什么?
12、express中间件了解么,koa2用过么?
下拉列表城市:北京、上海、杭州将这3个城市放在前三,怎么实现?
实时返回和异步返回的情况?