跨域如何解决
解决方案:
jsonp
(利用script
标签没有跨域限制的漏洞实现。缺点:只支持GET
请求)CORS
(设置Access-Control-Allow-Origin
:指定可访问资源的域名)postMessage
(message, targetOrigin, [transfer]
)(HTML5
新增API 用于多窗口消息、页面内嵌iframe消息传递),通过onmessage
监听 传递过来的数据Websocket
是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。Node
中间件代理Nginx
反向代理- 各种嵌套
iframe
的方式,不常用。 - 日常工作中用的最多的跨域方案是CORS和Nginx反向代理
什么是同源策略
一个域下的js脚本未经允许的情况下,不能访问另一个域下的内容。通常判断跨域的依据是协议、域名、端口号是否相同,不同则跨域。同源策略是对js脚本的一种限制,并不是对浏览器的限制,像img,script脚本请求不会有跨域限制。
前后端如何通信
ajax:短连接
websocket:长连接,双向的
Form表单(最原始的)
浏览器的本地存储?各自优劣如何?
-
浏览器的本地存储主要分为Cookie、WebStorage和IndexDB,其中WebStorage又可以分为localStorage和sessionStorage.
-
共同点:都是保存在浏览器端、且同源的
-
不同点:
-
cookie
数据始终在同源的http
请求中携带(即使不需要),即cookie
在浏览器和服务器间来回传递。cookie
数据还有路径(path
)的概念,可以限制cookie
只属于某个路径下sessionStorage
和localStorage
不会自动把数据发送给服务器,仅在本地保存。 -
存储大小限制也不同,
-
cookie
数据不能超过4K,sessionStorage和localStorage
可以达到5M -
sessionStorage
:仅在当前浏览器窗口关闭之前有效; -
localStorage
:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据; -
cookie
:只在设置的cookie
过期时间之前有效,即使窗口关闭或浏览器关闭 -
作用域不同
-
sessionStorage
:不在不同的浏览器窗口中共享,即使是同一个页面; -
localstorage
:在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在 -
cookie
: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在
-
token原理
客户端输入用户名和密码,将这次请求提交到服务器,服务器根据用户这次请求验证用户是否存在,验证成功之后,服务器端会向客户端返回一个唯一的token值,客户端需要把服务器端返回的token值记录到客户端本地,之后客户端要请求服务器端的接口就必须要携带token,才能正常的进行操作,因为token是保证你登录成功之后的唯一身份令牌。
(我们用的token,他的机制叫做json web token(简称jwt),前端调用登录接口,传入用户名,密码,后端校验,如果正确就通过jwt生成token,返回给前端,一般是保存到storage或者是cookie里面,每次调用需要依赖登陆的接口的时候携带上token,后台以此判断用户的登陆状态。因为token是后台加密生成的,一般很难破解,所以基本不会存在伪造token的风险。)
说一说你对闭包的理解
闭包是指有权访问另一个函数作用域中的变量的函数
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行
- 闭包用途:
- 能够访问函数定义时所在的词法作用域(阻止其被回收)
- 私有化变量
- 模拟块级作用域
- 创建模块
- 闭包缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏
vue组件的通信方式
父子组件通信
父->子props
,子->父 $on、$emit` 获取父子组件实例
parent、children、
Ref 获取实例的方式调用组件的属性或者方法
Provide、inject` 官方不推荐使用,但是写组件库时很常用
兄弟组件通信
Event Bus
实现跨组件通信 Vue.prototype.$bus = new Vue() Vuex
跨级组件通信
$attrs、$listeners
Provide、inject
组件化与模块化
模块化:从代码的逻辑操作划分的,将不同功能的代码逻辑划分成不同的模块,方便代码的分层开发,方便后期对于代码的维护,确保了每个功能模块功能单一
组件化:从UI设计角度出发划分的,前端组件化,为了方便UI组件重复使用,相当于在模块化的基础上添加了页面结构
git常用的命令以及与其他版本控制器的区别
常用命令:
git remote -v 查看远程仓库地址
git remote rm origin 删除当前远程仓库地址
git remote add origin url 添加新的远程仓库地址
git add 把要提交的所有修改放到暂存区
git commit -m ' ' 一次性把暂存区的所有修改提交到分支
rm -rf .git/ 删除git
git push origin master -u 将文件推送到远程仓库
git diff 比较的是工作区与资源库的不同
git diff --staged(比较的是暂存区和资源库的不同)
git log 查看提交日志(显示从最近到最远的日志)
git log --oneline 查看提交日志 在一行显示
git reset --soft HEAD~ 回到暂存区
git restore --staged 还原
git revert 恢复文件的历史版本
git checkout --<file> 撤销修改
git stash 保存,恢复,删除工作状态
git branch dev 创建分支 /git branch 查看当前分支
git checkout dev 切换分支
git checkout -b dev 创建并切换(相当于上面两条命令)
git merge dev 用于合并指定分支到当前分支
git switch master 切换分支
git switch -c dev 创建并切换到新的分支
git branch -m dev-3 dev-4 重命名
git branch -d dev-4 删除分支
git push origin dev 相当于在远程建立一个新的dev分支
git pull 拉取(每次提交前先pull一下)
git push 推送
GIT与SVN的区别:
SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而工作的时候,用的都是自己的电脑,所以首先要从中央服务器得到最新的版本,然后工作,完成工作后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,对网络带宽要求较高。
Git是分布式版本控制系统,每个人的电脑就是一个完整的版本库,工作的时候不需要联网了,因为版本都在自己电脑上。协同的方法是这样的:比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。Git可以直接看到更新了哪些代码和文件!
前端性能优化的几种方式
浏览器缓存
防抖、节流
资源懒加载、预加载
开启Nginx gzip压缩
三个方面来说明前端性能优化
webpack优化与开启gzip压缩
-
babel-loader用include或exclude来帮我们避免不必要的转译,不转译node_moudules中的js文件,其次在缓存当前转译的js文件,设置loader:'babel-loader?cacheDirectory=true'
-
文件采用按需加载等
-
具体的做法非常简单,只需要你在你的request headers中加上这么一句:accepc-encoding:gzip
-
图片优化,采用svg图片或字体图标
-
浏览器缓存机制,它又分为强缓存和协商缓存
本地存储——从Cookie到Web Storage、IndexeDB
- 说明一下sessionStorage和localStorage还有cookie的区别和优缺点
代码优化
-
事件代理
-
事件的节流和防抖
-
页面的回流和重绘
-
EventLoop事件循环机制
-
代码优化等
ES5的继承和ES6的继承有什么区别
ES5的继承是通过prototype或构造函数机制来实现。ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this))。
ES6的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改this。
具体的:ES6通过class关键字定义类,里面有构造方法,类之间通过extends关键字实现继承。子类必须在constructor方法中调用super方法,否则新建实例报错。因为子类没有自己的this对象,而是继承了父类的this对象,然后对其进行加工。如果不调用super方法,子类得不到this对象。
ps:super关键字指代父类的实例,即父类的this对象。在子类构造函数中,调用super后,才可使用this关键字,否则报错。
fetch发送2次请求的原因
fetch发送post请求的时候,总是发送2次,第一次状态码是204,第二次才成功?
原因很简单,因为你用fetch的post请求的时候,导致fetch 第一次发送了一个Options请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的请求。
为什么虚拟dom会提高性能
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。
具体实现步骤如下:
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
http和https的区别?
http传输的数据都是未加密的,也就是明文的,网景公司设置了SSL协议来对http协议传输的数据
进行加密处理,简单来说https协议是由http和ssl协议构建的可进行加密传输和身份认证的网络协
议,比http协议的安全性更高。主要的区别如下:
-
Https协议需要ca证书,费用较高。
-
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
-
使用不同的链接方式,端口也不同,一般而言,http协议的端口为80,https的端口为443
-
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
简单讲一讲ES6的一些新特性
- ES6 在变量的声明和定义方面增加了 let、const 声明变量,有局部变量的概念,赋值中有比较吸引人的解构赋值,同时 ES6 对字符串、 数组、正则、对象、函数等拓展了一 些方法,如字符串方面的模板字符串、函数方面的默认参数、对象方面属性的简洁表达 方式,ES6 也 引入了新的数据类型 symbol,新的数据结构 set 和 map,symbol 可以通过typeof 检测出来,为解决异步回调问题,引入了 promise 和 generator,还有最为吸引人的是实现了 Class 和模块,通过 Class 可以更好的面向对象编程,使用模块加载方便模块化编 程,当然考虑到 浏览器兼容性,我们在实际开发中需要使用 babel 进行编译
- 重要的特性:
- 块级作用域:ES5 只有全局作用域和函数作用域,块级作用域的好处是不再需要立即执行的函数表达式,循环体中的闭包不再有问题 rest 参数:用于获取函数的多余参数,这样就不需要使用 arguments 对象了,
- promise:一种异步编程的解决方案,比传统的解决方案回调函数和事件更合理强大
- 模块化:其模块功能主要有两个命令构成,export 和 import,export 命令用于规定模块的 对外接口,import 命令用于输入其他模块提供的功能。
vue的双向绑定原理
vue指令和vue事件
vue指令:
v-if:判断是否隐藏;
v-for:数据循环;
v-bind:class:绑定一个属性;
v-model:实现双向绑定
.stop 阻止冒泡
.prevent 阻止默认事件
.capture 添加事件侦听器时使用事件捕获模式
.once 事件只触发一次
.self 只有点击当前元素本身时才会触发回调
路由传参
- 使用query传参的时候,name,path都可以引入,但使用params传参的时候只能使用name进行引入。接收参数都是类似的,分别是this.$route.query.name和this.$route.params.name
- 进行路由跳转的时候,我们使用this.$router.push(‘路径')。
- query更加类似于我们ajax中get传参,params则类似于post,前者在浏览器地址栏中显示参数,后者则不显示。
v-if和v-show有什么区别
- v-if和v-show都可以显示和隐藏一个元素,但有本质区别
- v-if是惰性的,只是值为false就不会加载对应元素,为true才动态加载对应元素
- v-show:是无论为true和为false都会加载对应html代码,为false时用display:none隐藏不在页面显示,为true时页面上用display:block显示其效果
v-if和v-for的优先级
如何使用代码让360浏览器使用极速模式,而不是兼容模式
若页面需默认用极速核,增加标签:<meta name="renderer" content="webkit">
computed和watch的区别
computed
是计算属性,具有缓存性。
当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性computed
。
用于依赖发生变化时,触发属性重新计算。Computed
本质是一个具备缓存的watcher
,依赖的属性发生变化就会更新视图。 适用于计算比较消耗性能的计算场景。watch
更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props
,$emit
或者本组件的值,当数据变化时来执行回调进行后续操作。
无缓存性,页面重新渲染时值不变化也会执行。Watch
没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。当我们需要深度监听对象中的属性时,可以打开deep:true
选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用unWatch
手动注销。- 应用场景:
当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed。
如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化。
DOM的属性和方法
js原生dom的操作方法有:
- 查找:getElementById,getElementsByTagName,querySelector
- 插入:appendChild,insertBefore
- 删除:removeChild
- 克隆:cloneNode
- 设置和获取属性:setAttribute('属性名','值'),getAttribute('属性名')
DOM操作中哪些会造成内存泄露
内存泄漏:指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。
哪些操作会造成内存泄露
- 意外的全局变量引起的内存泄露
- 闭包引起的内存泄露
- 没有清理的DOM元素引用
- 被遗忘的定时器或者回调
- 子元素存在引起的内存泄露
- IE7/8引用计数使用循环引用产生的问题
怎样避免内存泄露
- 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
- 注意程序逻辑,避免死循环之类的;
- 避免创建过多的对象,原则:不用了的东西要及时归还
组件中的data
为什么是一个函数?
如果data
是对象的话,由于对象是引用类型,组件被复用的话,就会创建多个实例。本质上,这些实例用的都是同一个构造函数。这样就会影响到所有的实例,所以为了保证组件不同的实例之间data
不冲突,data
必须是一个函数。
export default{
data(){
return {//返回一个唯一的对象,不要和其他组件共用一个对象进行返回
menu:MENU.data,
poi:POILIST.data
}
}
}
因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的data对象,不要和其它组件共用一个对象。
因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的对象,不要和其它组件共用一个对象。
重绘(Repaint)和回流(Reflow)
重绘和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大。
-
重绘是当节点需要更改外观而不会影响布局的,比如改变
color
就叫称为重绘 -
回流是布局或者几何属性需要改变就称为回流,回流也称为重排。
回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。
所以以下几个动作可能会导致性能问题:
-
改变 window 大小
-
改变字体
-
添加或删除样式
-
文字改变
-
定位或者浮动
-
盒模型
很多人不知道的是,重绘和回流其实和 Event loop 有关。
-
当 Event loop 执行完 Microtasks 后,会判断 document 是否需要更新。因为浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。
-
然后判断是否有
resize
或者scroll
,有的话会去触发事件,所以resize
和scroll
事件也是至少 16ms 才会触发一次,并且自带节流功能。 -
判断是否触发了 media query
-
更新动画并且发送事件
-
判断是否有全屏操作事件
-
执行
requestAnimationFrame
回调 -
执行
IntersectionObserver
回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好 -
更新界面
-
以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行
requestIdleCallback
回调。
减少重绘和回流
-
使用
translate
替代top
- 使用
visibility
替换display: none
,因为前者只会引起重绘,后者会引发回流(改变了布局)把 DOM 离线后修改,比如:先把 DOM 给
display:none
(有一次 Reflow),然后你修改100次,然后再把它显示出来不要把 DOM 结点的属性值放在一个循环里当成循环里的变量
-
不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
-
动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用
requestAnimationFrame
-
CSS 选择符从右往左匹配查找,避免 DOM 深度过深
-
将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于
video
标签,浏览器会自动将该节点变为图层。
vue中如何监控某个属性值的变化
比如现在需要监控data中, obj.a 的变化
watch: {
'obj.a': {
handler (newName, oldName) {
console.log('obj.a changed')
}
}
}
普通watch无法监听到对象内部属性变化,只有data中数据改变时才能监听变化。因此可添加deep属性:深层遍历
computed: {
a1 () {
return this.obj.a
}
}
利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。
同步和异步的理解
同步即sync,形象的说就是代码一行行执行,前面的代码和请求没有执行完,后面的代码和请求就不会被执行。
- 缺点:容易导致代码阻塞
- 优点:程序员容易理解(因为代码从上往下一行行执行,强调顺序)
异步:即async,形象地说就是代码可以在当前程序没有执行完,也可以执行后面的代码。
- 缺点:程序员不易理解(因为不是按顺序执行的)
- 优点:可以解决代码阻塞问题,提升代码执行效率和性能
异步解决方案主要有三个
- 回调函数
- promise(重点掌握)
- generator(了解)
- async和await(重点掌握)
BOM和DOM对象
BOM(Browser Object Model)
是指浏览器对象模型,可以对浏览器窗口进行访问和操作。使用 BOM,开发者可以移动窗口、改变状态栏中的文本以及执行其他与页面内容不直接相关的动作。 使 JavaScript
有能力与浏览器"对话"。 DOM (Document Object Model)
是指文档对象模型,通过它,可以访问HTML
文档的所有元素。 DOM
是 W3C
(万维网联盟)的标准。DOM
定义了访问 HTML
和 XML
文档的标准: "W3C 文档对象模型(DOM)是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。" W3C DOM
标准被分为 3 个不同的部分:
- 核心
DOM
- 针对任何结构化文档的标准模型 XML DOM
- 针对 XML 文档的标准模型HTML DOM
- 针对 HTML 文档的标准模型
什么是 XML DOM
? XML DOM
定义了所有 XML 元素的对象和属性,以及访问它们的方法。 什么是 HTML DOM? HTML DOM 定义了所有 HTML 元素的对象和属性,以及访问它们的方法。
说说你对spa单页面应用的理解
-
SPA(single-page application)仅在web页面初始化时加载相应的HTML,JavaScipt和CSS.一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转,取而代之的是利用路由机制实现HTML内容的变化,UI与用户的交互,避免与页面的重新加载。
-
优点
-
用户的体验好,快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重新渲染
-
基于上面一点,SPA相对对服务器压力小
-
前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理
-
-
缺点:
-
初次加载时耗时多:为实现单页web应用功能及显示效果。需要在加载页面的时候将javaScript,css统一加载,部分页面按需加载
-
前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理
-
seo难度较大:由于所有的内容都在一个页面中动态替换显示,所以在seo上其有着天然的弱势
-
this指向,new关键字
this
对象是是执行上下文中的一个属性,它指向最后一次调用这个方法的对象,在全局函数中,this
等于window
,而当函数被作为某个对象调用时,this等于那个对象。 在实际开发中,this
的指向可以通过四种调用模式来判断。
- 函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,
this
指向全局对象。 - 方法调用,如果一个函数作为一个对象的方法来调用时,
this
指向这个对象。 - 构造函数调用,
this
指向这个用new
新创建的对象。 - 第四种是
apply 、 call 和 bind
调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply
接收参数的是数组,call
接受参数列表,`` bind方法通过传入一个对象,返回一个
this绑定了传入对象的新函数。这个函数的
this指向除了使用
new `时会被改变,其他情况下都不会改变。
new
- 首先创建了一个新的空对象
- 设置原型,将对象的原型设置为函数的
prototype
对象。 - 让函数的
this
指向这个对象,执行构造函数的代码(为这个新对象添加属性) - 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
GET和POST的区别
1.get方法通过url请求来传递用户的数据,将表单内容各字段名称与其内容,以成对的字符串连接,置于action属性所指程序的url后,数据都会直接显示在url上,就像用户点击一个链接一样,post方法通过HTTP post机制,将表单内各字段名称与其内容放置在HTML表头(header)内一起传送给服务器端交由action属性能所指的程序处理,该程序会通过标准输入(stdin)方式,见表单的数据读出并加以处理。
2.get方式需要使用request,QueryString来取得变量的值;而post方式通过RequestForm来访问提交的内容;
3.get方式传输的数据量非常小,一般限制在2kb左右,但是执行效率却比post方法好,而post方式传输的数据量相对较大,它是等待服务器来读取数据,不过也有字节限制,这是为了避免对服务器用大量数据进行恶意攻击。建议:除非你肯定你提交的数据可以一次性提交,否则请尽量用post方法;
4.get方式提交数据,会带来安全问题,比如一个登录页面,通过get方式提交数据时,用户名和密码将出现在url上,如果页面可以被缓存或者其他人可以访问客户这台机器,就可以从历史记录获得该用户的账号和密码,所以表单提交建议使用post方法;
5.get是从服务器上获取数据,post是向服务器传送数据。
6.get方式的安全性较post方式要差些,包含机密信息的化,建议用post数据提交方式;
注意:在做数据查询时,建议用get方式,而在做数据添加、修改或删除时,建议用post方式。
作用域,作用域链
1.js作用域也就是js识别变量的范围,作用域链也就是js查找变量的顺序
2.先说作用域,js作用于主要包含全局作用域、局部作用域和es6的块级作用域
全局作用域:也就是定义在window下的变量范围,在任何地方都可以访问
局部作用域:是指在函数内部定义的变量范围
块级作用域:简单来说用let和const在任意的代码块中定义的变量都认为是块级作用域中的变量,例如在for循环中用let定义的变量,在if语句中使用let定义的变量等。
注意:尽量不要使用全局变量,因为容易导致全局的污染,命名冲突,对bug查找不利。
3.所谓的作用域链就是由最内部的作用域往最外部查找变量的过程,形成的链条就是作用域链。
var、let、const的区别
var、let、const三者区别可以围绕下面五点展开:
变量提升
暂时性死区
块级作用域
重复声明
修改声明的变量
使用
变量提升
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined;
let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错。
暂时性死区
var不存在暂时性死区;
let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才能获取和使用该变量。
块级作用域
var不存在块级作用域;
let和const存在块级作用域
重复声明
var 允许重复声明变量
let和const在同一作用域下不允许重复声明变量
修改声明的变量
var和let可以修改;
const声明一个只读的常量。一旦声明,常量的值就不能改变。
使用
能用const的情况尽量使用const,其他情况下大多数使用let,避免使用var.