原文传送门:https://blog.csdn.net/qq_39025670/article/details/106240355
1.VUE最全面试题
2.VUE之VUEX常见面试题大全汇总–史上最全【vuex面试题
3.Vue双向绑定原理(v-model)
4.ES6、ES7、ES8、ES9、ES10、ES11、ES12、ES13新特性大全
⬇️
⬇️⬇️
⬇️⬇️⬇️
一. 深拷贝,浅拷贝
深浅拷贝都是针对引用类型而言的,浅拷贝只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。只有深拷贝才是真正地对对象的拷贝。
1.浅拷贝
浅拷贝只是复制了引用,并没有实现真正的复制。这里,我们无论改变原来的值还是克隆的值,它们都会相互影响,并没有实现隔离
var arr = [1,2,3];
var obj = {a:'a', b:[1,2], c:{cc:'cc'}};
var cloneArr = arr;
var cloneObj = obj;
cloneArr.push(4);
cloneObj.a = 'aaa';
console.log(arr); // [1, 2, 3, 4]
console.log(cloneArr); // [1, 2, 3, 4]
console.log(obj); // {a:'aaa', b:[1,2], c:{cc:'cc'}}
console.log(cloneObj); // {a:'aaa', b:[1,2], c:{cc:'cc'}}
2.深拷贝
深拷贝则是完完全全的拷贝,它们之间彼此隔离,互不影响。
实现深拷贝的方法主要有两种:
- 利用 JSON 对象中的 parse 和 stringify
- 利用递归来实现每一层都重新创建对象并赋值
(1)JSON 对象中的 parse 和 stringify
var arr = [1,2,3];
var obj = {a:'a', b:[1,2], c:{cc:'cc'}};
var cloneArr = JSON.parse(JSON.stringify(arr));
var cloneObj = JSON.parse(JSON.stringify(obj));
cloneArr.push(4);
cloneObj.a = 'aaa';
cloneObj.b.push(3);
cloneObj.c = 'ccc';
console.log(arr); // [1, 2, 3]
console.log(cloneArr); // [1, 2, 3, 4]
console.log(obj); // {a:'a', b:[1,2], c:{cc:'cc'}}
console.log(cloneObj); // {a:'aaa', b:[1,2,3], c:{cc:'ccc'}}
JSON 对象中的 parse 和 stringify方法确实实现了深拷贝,也是我们最常用的一种方法,但对于一些复杂的引用类型,就存在问题
var obj = {
name: 'Tom',
sayName: function(){
console.log(this.name)
}
}
var cloneObj = JSON.parse(JSON.stringify(obj));
console.log(obj); // {name: "Tom", sayName: ƒ}
console.log(cloneObj); // {name: "Tom"}
我们发现,它并没有将方法复制下来。原因是:undefined、function、symbol 会在转换过程中被忽略…
(2)递归实现
利用递归实现深拷贝的思想是每一层都重新创建对象并赋值
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){
if(source.hasOwnProperty(keys)){ // 判断属性是否存在于实例中
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
// 测试
var obj = {
name: 'Tom',
sayName: function(){
console.log(this.name)
}
}
var cloneObj = deepClone(obj);
console.log(obj); // {name: "Tom", sayName: ƒ}
console.log(cloneObj); // {name: "Tom", sayName: ƒ}
在来测测最开始的例子
var arr = [1,2,3];
var obj = {a:'a', b:[1,2], c:{cc:'cc'}};
var cloneArr = deepClone(arr);
var cloneObj = deepClone(obj);
cloneArr.push(4);
cloneObj.a = 'aaa';
cloneObj.b.push(3);
cloneObj.c = 'ccc';
console.log(arr); // [1, 2, 3]
console.log(cloneArr); // [1, 2, 3, 4]
console.log(obj); // {a:'a', b:[1,2], c:{cc:'cc'}}
console.log(cloneObj); // {a:'aaa', b:[1,2,3], c:{cc:'ccc'}}
3. JavaScript 中的拷贝方法
JavaScript中的有的方法也能实现拷贝,比如:concat()和slice(),ES6中的Object.assgin()和…展开运算符。这里就不一一测试了,直接给出结论吧。
(1)、concat 只是对数组的第一层进行深拷贝
(2)、slice 只是对数组的第一层进行深拷贝
(3)、Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值
(4)、… 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值
二、性能优化
从输入 URL 到页面加载完成,发生了什么?
1、DNS(域名解析系统)将 URL 解析为对应的 IP 地址,建立起 TCP 网络连接(三次握手)
2、发送 HTTP 请求
3、接受 HTTP 响应
4、渲染页面,构建DOM树
5、关闭TCP连接(四次挥手)
1、前端性能优化比较常见的一些点:
- 使用缓存(最重要的一点),cookie与WebStorage
- 减少http请求
- 文件压缩合并:html,js,css压缩。删除一些无用代码:注释
- 图片无损压缩,安卓下可以使用webp格式图片
- 使用字体图标,矢量图svg,雪碧图,base64
- js文件一般放在页面底部,若放在head里一般要在 script 标签上加 async 或者 defer 进行异步加载
- 懒加载,预加载
- 服务端渲染(SSR),增强页面的呈现速度,同时能增强SEO,但页面切换不如单页应用(SPA)流畅
- 减少dom操作,规避重绘与回流;更改css属性较多时,以类名的形式操作dom;有复杂动画的DOM,可以考虑使其脱离文档流,从而减少重绘与回流的范围
- 事件的节流与防抖
- 事件委托。利用事件冒泡,通过指定一个事件处理程序,来管理某一类型的所有事件。这样能减少页面消耗的内存
- 启用DNS预解析。遇到网页中的超链接,DNS
prefetching从中提取域名并将其解析为IP地址,这些工作在用户浏览网页时,使用最少的CPU和网络在后台进行解析 - 若使用了闭包,当功能完成后,最好将其置null,以释放内部变量占用的内存
- 高并发之前端优化方案
(1)、利用缓存,精简请求
把基本不会经常改变的数据,比如:用户基本信息等,在进入页面时候就请求,之后都可以在缓存中获取
(2)、合并压缩
压缩资源可以在webpack中设置,在build的时候合并压缩。
(3)、静态资源上传cdn
一些比较大且不会经常变化的静态资源文件,可以上传的CDN上,使用时候,在项目中可以直接下载然后在HTML上引用,具体实现思路是在npm run build之后,执行额外的upload.js,服务器部署的时候只需要部署三个html文件就可以了
(4)、避免高频刷新页面获取数据
可以做了一个限定,比如5秒内刷新页面只获取一次列表数据,避免高频刷新带给服务器的压力
async init() {
try {
const store = JSON.parse(util.getStore('hopoActiveInfo'))
// 避免高频刷新增加服务器压力
if (store && (new Date() - new Date(store.getTime)) < 5000) {
this.basicInfo = store
} else {
this.basicInfo = await getActiveInfo()
this.basicInfo.getTime = new Date()
}
util.setStore(this.basicInfo, 'hopoActiveInfo')
this.btn.noPeopleAndStart.detail[0].text = `${
this.basicInfo.directBuyPrice
} 元直接购买`
this.computedStatus()
} catch (error) {
console.log(error)
}
},
(5)、设置响应头cache-control和last-modified
对于所有的数据和接口设置响应头,利用express模拟,如果两次请求间隔小于5秒,直接返回304,不需要服务器进行处理
app.all('*', function(req, res, next){
res.set('Cache-Control','public,max-age=5')
if ((new Date().getTime() - req.headers['if-modified-since'] )< 5000) {
// 检查时间戳
res.statusCode = 304
res.end()
}
else {
var time =(new Date()).getTime().toString()
res.set('Last-Modified', time)
}
next()
})
四.HTTP/HTTPS 网络/通信
1.GET 和 POST 的区别
- GET 在浏览器回退时是无害的,而 POST 会再次提交请求
- GET 产生的 URL 地址可以被收藏,而 POST 不可以
- GET 请求会被浏览器主动缓存,而 POST 不会,除非手动设置
- GET 请求只能进行 url 编码,而 POST 支 持多种编码方式
- GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留
- GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有限制对参数的数据类型
- GET 只接受 ASCII 字符,而 POST 没有限制
- GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息
- GET 参数通过 URL 传递,POST 放在 Request body 中
2.HTTP方法
- GET 获取资源
- POST 传输资源
- PUT 更新资源
- DELETE 删除资源
- HEAD 获得报文首部
3.HTTP状态码
- 1xx:指示信息-表示请求已接收,继续处理
- 2xx:成功-表示请求已被成功接收
- 3xx:重定向-要完成请求必须迸行更迸一步的操作
- 4xx:客戸端错误-请求有语法错误或请求无法实现
- 5xx:服各器错误-服务器未能实现合法的请求
常见状态码
- 200 OK:客户端请求成功
- 206 Partial Content:客户发送了一个带有Range头的GET请求,服务器完成了它
- 301 Moved Permanently:所请求的页面已经转移至新的url
- 302 Found:所请求的页面已经临时转移至新的url
- 304 Not Modified:客户端有缓冲的文档并发出了一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用
- 400 Bad Request:客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
- 403 Forbidden:对被请求页面的访问被禁止
- 404 Not Found:请求资源不存在
- 500 Internal Server Error:服务器发生不可预期的错误,原来缓冲的文档还可以继续使用
- 503 Server Unavailable:请求未完成,服务器临时过载或当机,一段时间后可能恢复正常
4.前后端如何通信
- Ajax
- WebSocket
- CORS
5.Ajax
Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
Ajax 常见应用:运用 XMLHttpRequest 或新的 Fetch API 与网页服务器进行异步资料交换。
这里我们手写一个简单的 XHR 请求过程
function request() {
// 1.获取一个 XHR 实例
const xhr = new XMLHttpRequest()
// 2.初始化请求
xhr.open('POST', 'http://192.168.3.195:8088/setUsername', true)
// 3.事件处理器 在每次 readyState 属性变化时被自动调用
xhr.onreadystatechange = function() {
// 请求完成且状态为 200
if (xhr.readyState == 4 && xhr.status == 200) {
// 成功
// 得到服务端返回的数据
let res = JSON.parse(xhr.responseText)
} else {
// 其他情况
}
}
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8')
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*')
// 允许跨域
xhr.withCredentials = true
// 设置发送的数据
const requestData = JSON.stringify({
username: 'Tom'
})
// 4.发送请求
xhr.send(requestData)
}
6.WebSocket
是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
常见应用:客服系统、物联网数据传输系统等。
推荐教程:WebSocket 教程 - 阮一峰
7.CORS
CORS 全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了AJAX只能同源使用的限制。
8.跨域通信及其方式**
跨域通信
协议、IP和端口只要其中之一不同就算跨域。
跨域通信方式(后面详细去写)
- Jsonp
- Hash
- postMessage
- WebSocket
- CORS
9.HTTPS
HTTPS 是在 HTTP 上建立 SSL 加密层,并对传输数据进行加密,是 HTTP 协议的安全版。
HTTPS 主要作用是:
(1)对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;
(2)对网站服务器进行真实身份认证。
10.HTTPS 与 HTTP 的区别:
- HTTP 是明文传输协议,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全;
- HTTPS 比 HTTP 更加安全,对搜索引擎更友好,利于 SEO,谷歌、百度优先索引 HTTPS 网页;
- HTTPS 需要用到 SSL 证书,而 HTTP 不用;
- HTTPS 标准端口 443,HTTP 标准端口 80;
- HTTPS 基于传输层,HTTP 基于应用层;
- HTTPS 在浏览器显示绿色安全锁,HTTP 没有显示。
参考文章:深入理解HTTPS工作原理
五.前端安全
1.CSRF,跨站请求伪造
定义:
- CSRF即Cross-site request
forgery(跨站请求伪造),是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
防御
- 添加校验token。系统开发人员可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务端进行token校验,如果请求中没有token或者token内容不正确,则认为是CSRF攻击而拒绝该请求。
- 尽量使用POST,限制GET。POST请求有一定作用,但并不一定完全安全。
- 检查Referer字段。通过验证请求头的Referer来验证来源站点,但请求头很容易伪造。
2.XSS,跨域脚本攻击
定义
跨站脚本攻击是指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
xss攻击原理
XSS(Cross-Site Sctipting,跨站脚本攻击) 是一种代码注入攻击,攻击者在目标网站上注入恶意代码,当被攻击者登陆网站时就会执行这些恶意代码,这些脚本可以读取cookie,session,tokens,或者其他铭感的网站信息,对用户进行钓鱼欺诈,甚至发起蠕虫攻击等。
防御
- url参数使用encodeURLComponent
- 设置httpOnly:很多XSS攻击目标都是窃取用户cookie伪造身份认证,设置此属性可防止JS获取cookie
- 开启CSP,即开启白名单,可阻止白名单以外的资源加载和运行
- 输入检查:对输入内容中 < script>< iframe>等标签或特殊符号进行转义
- 尽量不用InnerHtml插入HTML内容
六.浏览器渲染机制
1.浏览器渲染原理与过程
主要包括以下五步:
- 浏览器将获取的HTML文档解析成DOM树
- 处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)
- 将DOM和CSSOM合并为渲染树(rendering tree)
- 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有的元素
- 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制paintin
内容解释
1、HTML parser:HTML解析器,其本质是将HTML文本解释成DOM tree。
2、CSS parser:CSS解析器,其本质是讲DOM中各元素对象加入样式信息。
3、JavaScript引擎:专门处理JavaScript脚本的虚拟机,其本质是解析JS代码并且把逻辑(HTML和CSS的操作)应用到布局中,从而按程序要的要求呈现相应的结果
4、DOM tree:文档对象模型树,也就是浏览器通过HTMLparser解析HTML页面生成的HTML树状结构以及相应的接口。
5、render tree:渲染树,也就是浏览器引擎通过DOM Tree和CSS Rule Tree构建出来的一个树状结构,和dom tree不一样的是,它只有要最终呈现出来的内容,像或者带有display:none的节点是不存在render tree中的。
6、layout:也叫reflow 重排,渲染中的一种行为。当rendertree中任一节点的几何尺寸发生改变了,render tree都会重新布局。
7、repaint:重绘,渲染中的一种行为。render tree中任一元素样式属性(几何尺寸没改变)发生改变了,render tree都会重新画,比如字体颜色、背景等变化。
2.重排 Reflow(也叫回流)
定义
DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow。
3.触发Reflow
- 当你增加、删除、修改DOM结点时,会导致Reflow或Repaint
- 当你移动DOM的位置,或是搞个动画的时候
- 当你修改CSS样式的时候
- 当你Resize窗口的时候(移动端没有这个问题), 或是滚动的时候
- 当你修改网页的默认字体时
4.重绘 Repaint
定义
当我们改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸和位置没有发生改变,这个称为repaint。重绘的性能优于重排(回流)。
5.回流与重绘的区别:
- 回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流。
- 当页面布局和几何属性改变时就需要回流,比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变。
6.应该尽量减少回流和重绘,那么怎样优化浏览器渲染过程
其实优化就是减少对render tree的操作,并减少对一些style信息的请求,尽量利用好浏览器的优化策略。
- 用 transform 做形变和位移可以减少 reflow
- 避免逐个修改节点样式,尽量一次性修改
- 使用 DocumentFragment 将需要多次修改的 DOM 元素缓存,最后一次性 append 到真实 DOM 中渲染
- 可以将需要多次修改的 DOM 元素设置 display:none,操作完再显示。(因为隐藏元素不在 render
树内,因此修改隐藏元素不会触发回流重绘) - 避免多次读取某些属性
- 通过绝对位移将复杂的节点元素脱离文档流,形成新的 Render Layer,降低回流成本
- 不要一个一个改变元素的样式属性,最好直接改变className,但className是预先定义好的样式,不是动态的,如果你要动态改变一些样式,则使用cssText来改变
- DOM的增删行为让操作元素离线处理,即使用documentFragment或div等元素进行缓存操作,先把所有要添加到元素添加到1个div,最后才把这个div append到body中
- 先display:none 隐藏元素,然后对该元素进行所有的操作,最后再显示该元素。因对display:none的元素进行操作不会引起回流、重绘。
- 将引起回流的属性赋值给变量,进行缓存,需要用到的时候直接使用变量就行。例如获取一个元素的scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight之类的属性,浏览器为了保证值的正确也会回流取得最新的值,所以如果你要多次操作,最取完做个缓存。
- 减少操作影响的节点,影响的节点越多,则越消耗性能。
七.箭头函数与普通函数区别
箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异:
1、函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
2、不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
3、不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
4、不可以使用 new 命令,因为:没有自己的 this,无法调用 call,apply;没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 _ proto_
八.webpack
1.webpack是什么?
- 一种前端资源构建工具,一个静态模块打包器(nodule bundle)
- 前端所有资源文件(js/json/css/img…)都会作为模块处理
- 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)
2. webpack与grunt、gulp的不同?
-
grunt和gulp是基于任务流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程
-
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能
3. 什么是chunk?什么是bundle?
-
首先告诉 Webpack 一个入口文件,如 index.js为起点作为打包,将入口文件的所有依赖项引入进来,这些依赖会跟入口文件形成一个文件(代码块),这个文件(代码块)就是 chunk
-
将这个代码块(chunk)进行处理,比如把 less 文件编译成 css,js 资源编译成浏览器能识别的 js语法等等操作,这些就叫做打包,将打包好的资源再输出出去,这个输出的文件就叫 bundle
4. Webpack 五个核心概念分别是什么?
(1).Entry
入口(Entry)指示 Webpack 以哪个文件为入口起点开始打包,分析内部构件依赖图
(2).Output
输出(Output)指示 Webpack 打包后的资源 bundles 输出到哪里去,以及如何命名
(3).Loader
Loader 能让 Webpack 处理非 JavaScript/json 文件(Webpack 自身只能处理 JavaScript/json )
(4).Plugins
插件(Plugins)可以用于执行范围更广的任务,包括从打包优化和压缩到重新定义环境中的变量
(5).Mode
模式(Mode)指示 Webpack 使用相应模式的配置,只有development(开发环境)和production(生产环境)两种模式
5. 有哪些常见的Loader?它们是解决什么问题的?
- css-loader:将 css 文件变成 CommonJS 模块加载 js 中,里面内容是样式字符串
- style-loader:创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
- url-loader:在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
- file-loader:打包其他资源(除了css/js/html 资源)
- html-loader:处理 html 文件中的 img
- babel-loader:把 ES6 转换成 ES5
- eslint-loader:通过 ESLint 检查 JavaScript 代码
6. 有哪些常见的Plugin?它们是解决什么问题的?
- html-webpack-plugin:可以复制一个有结构的html文件,并自动引入打包输出的所有资源(JS/CSS)
- clean-webpack-plugin:重新打包自动清空 dist 目录
- mini-css-extract-plugin:提取 js 中的 css 成单独文件
- optimize-css-assets-webpack-plugin:压缩css
- uglifyjs-webpack-plugin:压缩js
- commons-chunk-plugin:提取公共代码
7.webpack的构建流程是什么?
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
- 确定入口:根据配置中的 entry 找出所有的入口文件;
- 编译模块:从入口文件出发,调用所有配置的 Loader
对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理; - 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk
转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会; - 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
8.webpack的热更新是什么?
热更新又称热替换(Hot Module Replacement),缩写为HMR,基于devServer,生产环境不需要devServer,所以生产环境不能用HMR功能
作用:
优化打包构建速度,一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
- 样式文件:可以使用HMR功能,因为style-loader内部实现了
- JS文件:默认没有HMR功能,需要修改js代码,添加支持HMR功能。入口文件做不了HMR功能,只能处理非入口js文件
- HTML文件:默认没有HMR功能,同时会导致 html 文件不能热更新(即修改没有任何反应)
解决方案:
修改entry入口,将html文件引入
entry:['./src/js/index.js','./src/index.html']
不用做HMR功能,因为只有一个html文件
9.如何利用webpack来优化前端性能?
开发环境下:
- 开启HMR功能,优化打包构建速度
- 配置 devtool: ‘source-map’,优化代码运行的性能
生产环境下:
- oneOf 优化
默认情况下,假设设置了7、8个loader,每一个文件都得通过这7、8个loader处理(过一遍),浪费性能,使用 oneOf找到了就能直接用,提升性能 - 开启 babel 缓存
当一个 js 文件发生变化时,其它 js 资源不用变 - code split 分割
将js文件打包分割成多个bundle,避免体积过大 - 懒加载和预加载
- PWA 网站离线访问
- 多进程打包
开启多进程打包,主要处理js文件(babel-loader干的活久),进程启动大概为600ms,只有工作消耗时间比较长,才需要多进程打包,提升打包速度 - dll 打包第三方库
- code split将第三方库都打包成一个bundle,这样体积过大,会造成打包速度慢
- dll 是将第三方库打包成多个bundle,从而进行速度优化
10.npm打包时需要注意哪些?如何利用webpack来更好的构建?
NPM模块需要注意以下问题:
- 要支持CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则
- Npm模块使用者的环境是不确定的,很有可能并不支持ES6,所以打包的最后结果应该是采用ES5编写的。并且如果ES5是经过转换的,请最好连同SourceMap一同上传
- Npm包大小应该是尽量小(有些仓库会限制包大小)
- 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况
- UI组件类的模块应该将依赖的其它资源文件,例如.css文件也需要包含在发布的模块里
webpack配置做以下扩展和优化:
- CommonJS模块化规范的解决方案:设置output.libraryTarget='commonjs2’使输出的代码符合CommonJS2模块化规范,以供给其它模块导入使用
- 输出ES5代码的解决方案:使用babel-loader把 ES6 代码转换成 ES5 的代码。再通过开启devtool: 'source-map’输出SourceMap以发布调试。
- Npm包大小尽量小的解决方案:Babel 在把 ES6 代码转换成 ES5
代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc文件,为其加入transform-runtime插件 - 不能将依赖模块打包到NPM模块中的解决方案:使用externals配置项来告诉webpack哪些模块不需要打包。
- 对于依赖的资源文件打包的解决方案:通过css-loader和extract-text-webpack-plugin来实现
11.hash、chunkhash、contenthash三者的区别?
浏览器访问网站后会强缓存资源,第二次刷新就不会请求服务器(一般会定个时间再去请求服务器),假设有了bug改动了文件,但是浏览器又不能及时请求服务器,所以就用到了文件资源缓存(改变文件名的hash值)
- hash:不管文件变不变化,每次wepack构建时都会生成一个唯一的hash值
- chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样问题:js和css同属于一个chunk,修改css,js文件同样会被打爆
- contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样
九.VUE
1.mvvm和mvc区别
2.vue 中 data 为什么是一个函数,而不是一个对象
vue组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
3.vue 组件的通信
(1)、ref通信
(2)、$children
(3)、$parent
(4)、props和$emit
async修饰符
this.$emit("update:变量",需要传递的值)
(5)、eventBus事件总线
EventHub事件派发和广播、发射与接收(可实现页面传值和非父子组件传值)
//(方法1)
vue.prototype.$EventBus = new Vue()
//(方法2)
// 新建EventBus.js 导出
import Vue from 'vue'
export const EventBus = new Vue()
// 也可以直接在APP.vue中
new Vue({
el: '#app',
data: {
EventBus: new Vue()
},
render: h => h(App)
})
// 在组件1中使用($emit派发)
// 在APP.vue 直接挂载的 可以通过this.$root.EventBus获取此对象,新建文件的需导入,然后直接使用 EventBus.$emit
this.$root.EventBus.$emit('事件名称', '这里是参数,选中后的地址')
// 在组件2中的mounted内使用($on监听)
mounted(){
this.$root.EventBus.$on('事件名称', (data)=>{
console.log('接受到的参数:'+data)
} )
}
// 当组件2销毁时使用($off取消),可在beforeDestroy
beforeDestroy () {
this.$root.EventBus.$off('事件名称')
}
(6)、provide/inject
(7)、vuex通信
(8)、attrs/listeners 适用于 隔代组件通信
inheritAttrs:false
<childDomChild v-bind="$attrs" v-on="$listeners"></childDomChild>
(9)、vue2.6新加的v-slot实现作用域插槽通信:在父组件中使用子组件的数据
<slot name="header" :data='user'>0000000000</slot>
<template v-slot:header='slotprops'> <p>{{slotprops.data}}</p></template>
4.vuex 的原理
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- (1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store
中的状态发生变化,那么相应的组件也会相应地得到高效更新。 - (2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
(1)State: 驱动应用的数据源
(2)Actions: 专门用来与后端backend API打交道
(3)Mutations: Vuex中专门用于更新state
(4)Modules:分模块管理vuex
(5)Getters: 当于组件中的计算属性computed,与state相关,发生变化
- 可以用mapState、mapGetters在computed进行简写。
- 可以用mapMutations、mapActions在methods进行简写。
- this.$store.dispatch触发actions上的方法进行调用后端数据。
- this.$store.commit触发mutations上的方法修改state上的变量。
5.vue 生命周期
vue的生命周期:Vue 实例从创建到销毁的过程;开始创建–>初始化数据–>编译模块–>挂载dom渲染页面–>更新dom渲染页面—>卸载。
生命周期钩子函数:组件不同阶段会触发相应的钩子函数
初始化阶段、模板编译阶段、挂载阶段、卸载阶段
beforeCreate 组件实例被创建之初,组件的属性生效之前
created 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用
mounted el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
beforeUpdate 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
update 组件数据更新之后
activited keep-alive 专属,组件被激活时调用
deactivated keep-alive 专属,组件被销毁时调用
beforeDestroy 组件销毁前调用
destroyed 组件销毁后调用
errorCaptured:当捕获一个来自子孙组件的错误时被调用
6.v-if 与 v-for 的优先级
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级;所以不一般不能用于同一个标签上,每一次判断都需要遍历整个数组,将会十分影响性能,所以在实际开发中,我们会先判断再循环
7.v-if 和 v-show 的区别
(1)v-if是通过控制dom节点的存在与否来控制元素的显隐;v-show是通过设置DOM元素的display样式,block为显示,none为隐藏
(2)v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换;如果是频繁的切换用v-show,否则用v-if
8. vue 中 key 的作用
(1)key的作用主要是为了高效的更新虚拟DOM
(2)不能使用index作为v-for中的key;你删除一个数组长度为3的第二项,vue会认为你删除的是第三项,因为index也是连续的
(3)当页面的数据发生变化时,Diff算法只会比较同一层级的节点
(4)有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误
9. 虚拟 dom
- virtual DOM:是将真实的DOM的数据抽取出来,以对象的形式模拟树形结构
- template模板(编译)>渲染函数(执行)>虚拟dom==>patch(patching算法)==>真实dom(视图)
- 虚拟DOM的最终目标是将虚拟节点渲染到视图上
10. diff 算法
diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁,虚拟DOM最核心的部分,它可以将vnode渲染成真实的DOM
(1)用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
(2)当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
(3)把所记录的差异应用到所构建的真正的DOM树上,视图就更新了
11. computed 和 watch 的区别
computed
(1)支持缓存,只有依赖数据发生改变,才会重新进行计算
(2)不支持异步,当computed内有异步操作时无效,无法监听数据的变化
(3)computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
(4) 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
(5)如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
watch
(1)不支持缓存,数据改变,直接会触发相应的操作;
(2)watch支持异步;
(3)监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
(4) 当一个属性发生变化时,需要执行对应的操作;一对多;
(5)监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
immediate:组件加载立即触发回调函数执行,
deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动
12. vue 的自定义指令
- {{}} 插值语法
- v-html 插入html片段,可以防止xss攻击,类似innerHTML
- v-once 只渲染一次
- v-text 插入文本,不解析html,类似innerText
- v-model 在表单元素上实现:双向数据绑定
- v-bind绑定一个value属性;v-on指令给当前元素绑定input事件。
- v-on
- v-bind
- v-show
- v-if
- v-else-if
- v-else
- v-for
13. $nextTick
- 在js中属于微任务
- $nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的DOM,
- Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM, 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳
14. keep-alive
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
实现keep-alive的方式
//1、
<router-view v-if="$route.meta.keepAlive"></router-view>
// 2、include等于组件名:
<keep-alive include="test-keep-alive">
// 将缓存name为test-keep-alive的组件
<component></component> //组件
</keep-alive>
15. 路由跳转传参 params 和 query 的区别
(1)接收参数的方式不同
this.$route.params
this.$route.query
(2)query的方法传参url路径会显示传递的参数
(3)params刷新页面会丢失参数
(4)params是用name属性,query是用path属性来编写传参地址
16. router和route
- $router:$router对象是全局路由的实例,是router构造方法的实例
- $route:$route对象表示当前的路由信息,包含了当前 URL 解析得到的信息。包含当前的路径,参数,query对象等
- this.$router.push()
- this.$router.replace()
- this.$router.go(n)
- this.$router.back()
17. Vue-router 跳转和 location.href 有什么区别
(1)vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载;location.href会触发浏览器,页面重新加载一次
(2)vue-router使用diff算法,实现按需加载,减少dom操作
(3)vue-router是路由跳转或同一个页面跳转;location.href是不同页面间跳转;
(4)vue-router是异步加载this.$nextTick(()=>{获取url});location.href是同步加载
18. Vue-router 导航守卫有哪些
beforeRouteEnter(to, from,) {
console.log("to", to)
console.log("from", from)
console.log("next", next)
next(vm => {
// 通过 `vm` 访问组件实例
})
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
},
//全局的前置路由
router.beforeEach
19. vue-router 中常用的 hash 和 history 路由模式实现原理
- hash 路由
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 ‘#search’:以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)
- history 路由
history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录;
- pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;以使用 popstate 事件来监听 url的变化,从而对页面进行跳转(渲染)
- history.pushState() 或 history.replaceState() 不会触发 popstate事件,这时我们需要手动触发页面跳转(渲染)。
20. vuex 和 localStorage 的区别
(1)vuex的数据刷新页面会丢失,而localStorage的数据是永久存储的,除非手动清除。
(2)vuex存储在内存,localstorage(本地存储)则以文件的方式存储在本地,永久保存;sessionstorage( 会话存储 ) ,临时保存。localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
(3)应用场景:vuex用于组件之间的传值,localstorage,sessionstorage则主要用于不同页面之间的传值。
(4)vuex的数据是响应式的
(5)localstorage无法存储function.
21. vue 中的动态路由
像后台管理系统的左边菜单的导航栏,要做角色权限的一个控制;路由由后端返回
- 简单的角色权限:管理员权限和普通用户
- 复杂的路由权限:OA系统,门店用户,个人用户,区域用户
22. vue 的插槽 v-slot
父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的活。在vue2.6版本以上使用v-slot代替了slot 和 scope-slot ,vue3.0中也弃用了。v-slot 只能添加到template 或自定义组件上;
可以在子组件上写插槽的默认内容:
<slot name='header'>11111111111</slot>
(1)默认的插槽
任何没有被包裹在带有 v-slot 的 组件标签 中的内容都会被视为默认插槽的内容。没有名字的 隐含有一个 “default” 名称
(2)具名插槽
<template v-slot:header> <p>111111111</p> </template>
<template #header> <p>111111111</p> </template>
(3)作用域插槽
<slot name="header" :data='user'>0000000000</slot>
<template v-slot:header='slotprops'> <p>{{slotprops.data}}</p></template>
(4)动态插槽名
<template v-slot:[dynamicSlotName]> 00000000 </template>
- 1、父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
- 2、标签里面的内容会作为后备内容且有默认的名字default
- 3、(特殊:独占默认插槽的缩写语法)—只要出现多个插槽,请始终为所有的插槽使用完整的基于 的语法,v-slot
只能添加在 上 - 4、作用域插槽:绑定在 元素上的 attribute 被称为插槽 prop
- 5、#简写: #default=“{ user }”
23. vue 中的混入 minx
混入对象的钩子将在组件自身钩子之前调用。
- mixins:局部混入
- mixin:全局混入,一旦使用全局混入对象,将会影响到所有之后创建的 Vue 实例
24. vue 中的过滤器
双花括号插值
(1) 当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!
(2) 一个表达式可以使用多个过滤器。过滤器之间需要用管道符“|”隔开。其执行顺序从左往右
25. v-model 双向数据绑定的原理
数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现。
- 数据劫持的实现主要依赖于Object.defineProperty(),通过这个函数可以监听到get,set事件。
- 其中observer是最主要的部分,
- 用Object.defineProperty来实现数据的劫持,
- 然后用他的set,get方法来通知订阅者,触发update方法,从而实现更新视图
- Object.defineProperty()
==>给对象添加一个属性并指定该属性的配置。
vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法。
26. vue 中的修饰符
事件修饰符
- .stop 阻止单机事件冒泡
- .prevent 阻止默认行为(比如 @submit.prevent 会阻止提交后刷新页面)
- .capture 添加事件侦听器时使用捕获模式
- .self 只有事件在元素本身(而不是子元素)触发时触发回调
- .once 只触发一次(组件也适用)
- .key 触发事件的按键
- .native 原生点击事件
v-model 修饰符
- .lazy在输入框中,默认是在input事件中同步输入框的数据,使用lazy修饰符会转变为在change事件中同步(失去焦点或按回车才更新)。
- . number将输入转换为Number类型,默认是String
- .trim 自动过滤输入的首尾空格
27. vue 中的自定义指令
directives: //自定义指令
//有三个钩子函数
bind://只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted://被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update://所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
el, binding, vnode,oldVnode:// 四个函数的入参
//el:指令所绑定的元素,可以用来直接操作 DOM。
//binding:一个对象,包含以下 property:
//vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
//oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
28. vue 框架的优点
- 轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb;
- 简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
- 双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
- 组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;
- 视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
- 虚拟DOM:dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;
- 运行速度更快:相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。
29. axios 的特点有哪些
- 从浏览器中创建XMLHttpRequests;
- node.js创建http请求;
- 支持Promise API;
- 拦截请求和响应;
- 转换请求数据和响应数据;
- 取消请求;
- 自动换成json。
- axios中的发送字段的参数是data跟params两个,两者的区别在于params是跟请求地址一起发送的,data的作为一个请求体进行发送
- params一般适用于get请求,data一般适用于post put 请求。
30. 封装 vue 组件的过程
(1). 建立组件的模板,先把架子搭起来,写写样式,考虑好组件的基本逻辑。(os:思考1小时,码码10分钟,程序猿的准则。)
(2). 准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型。
(3). 准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法。
(4). 封装完毕了,直接调用即可
31.vue-router 实现路由懒加载
结合 Vue 的异步组件 (opens new window)和 Webpack 的代码分割功能 (opens new window),轻松实现路由组件的懒加载
第一种:vue异步组件技术 ==== 异步加载,vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .但是,这种情况下一个组件生成一个js文件。
第二种:路由懒加载(使用import)。
第三种:webpack提供的require.ensure(),vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
32. SPA 单页面的理解
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
33. Vue 的单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
34. Vue 的父组件和子组件生命周期钩子函数执行顺序
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
加载渲染过程:
父 beforeCreate -> 父 created -> 父 beforeMount ->
子 beforeCreate -> 子 created -> 子 beforeMount ->
子 mounted -> 父 mounted
子组件更新过程:
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父组件更新过程:
父 beforeUpdate -> 父 updated
销毁过程:
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
!!!子组件的updated 、mounted 和destroyed 优先于父组件执行
35. SSR 服务器渲染
BSR 客户端渲染(前后端分离):视图与数据的组装是在客户端完成的
SSR 服务器渲染(前后端不分离):视图和数据的组装是在服务端完成的
BSR的优势和劣势有哪些?
- 服务器压力小
- 前后端分离,代码更容易维护
- 数据化应用,交互更加丰富
- 前端工程师来讲价值更高
- 在ToC产品上应用更广泛
SSR的优势和劣势有哪些?
- 前后端不分离,对后端的要求非常高
- 有利于SEO
- 对客户端的压力比较小,服务器压力较大
- 在ToB产品上应用比较广泛
36. vue 项目的前端代码优化
- 路由懒加载
- 图片懒加载:data-src
- cdn静态资源加速
- 第三方插件的按需引入
- 开启gzip压缩
- 防抖和节流
- 减少回流和重绘呀
- v-if和v-show的使用
- 离开路由时销毁定时器和监听事件
- keep-alive
37. vue3 有了解吗
(1)监测机制的改变:Proxy 替换了Object.defineProperty 去监听数据的变化
(2)模板: 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
(3)对象式的组件声明方式:3.0 修改了组件的声明方式,改成了类式的写法
Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:
Proxy 的优势如下:
- Proxy 可以直接监听对象而非属性;($set)
- Proxy 可以直接监听数组的变化;
- Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是Object.defineProperty 不具备的;
- Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
- Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;
Object.defineProperty 的优势如下:
- 兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
38.vue中解决跨域问题
(1)后台更改header
header('Access-Control-Allow-Origin:*');//允许所有来源访问
header('Access-Control-Allow-Method:POST,GET');//允许访问的方式
(2)使用JQuery提供的jsonp
methods: {
getData () {
var self = this
$.ajax({
url: 'http://f.apiplus.cn/bj11x5.json',
type: 'GET',
dataType: 'JSONP',
success: function (res) {
self.data = res.data.slice(0, 3)
self.opencode = res.data[0].opencode.split(',')
}
})
}
}
(3)使用http-proxy-middleware 代理解决(项目使用vue-cli脚手架搭建)
例如请求的url:“http://f.apiplus.cn/bj11x5.json”
- 打开config/index.js,在proxyTable中添写如下代码:
proxyTable: {
'/api': { //使用"/api"来代替"http://f.apiplus.c"
target: 'http://f.apiplus.cn', //源地址
changeOrigin: true, //改变源
pathRewrite: {
'^/api': 'http://f.apiplus.cn' //路径重写
}
}
}
- 使用axios请求数据时直接使用“/api”:
getData () {
axios.get('/api/bj11x5.json', function (res) {
console.log(res)
})
- 通过这中方法去解决跨域,打包部署时还按这种方法会出问题。解决方法如下:
let serverUrl = '/api/' //本地调试时
// let serverUrl = 'http://f.apiplus.cn/' //打包部署上线时
export default {
dataUrl: serverUrl + 'bj11x5.json'
}
十、浏览器
1、浏览器过程
浏览器第一次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与 response header,以供下次加载时对比使用;
下一次加载资源时,由于强制缓存优先级较高,先比较当前时间与上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,并命中强缓存,直接从本地读取资源。如果浏览器不支持 HTTP1.1,则使用 expires 头判断是否过期;
如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向服务器发送带有 If-None-Match 和 If-Modified-Since 的请求;
服务器收到请求后,优先根据 Etag 的值判断被请求的文件有没有做修改,Etag 值一致则没有修改,命中协商缓存,返回 304;如果不一致则有改动,直接返回新的资源文件带上新的 Etag 值并返回 200;
如果服务器收到的请求没有 Etag 值,则将 If-Modified-Since 和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回 304;不一致则返回新的 last-modified 和文件并返回 200
2、浏览器强缓存,协商缓存
(1)强缓存
使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。
强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性。
(2)协商缓存
如果命中强制缓存,我们无需发起新的请求,直接使用缓存内容,如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用了。
命中协商缓存的条件有两个:
- max-age=xxx 过期了
- 值为 no-store
使用协商缓存策略时,会先向服务器发送一个请求,如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本。如果资源发生了修改,则返回修改后的资源。
十一、canvas优化绘制性能
- 优化Canvas指令
Canvas执行的绘图指令越多,性能消耗就越大。尽可能的减少绘图指令,能够达到更好的性能。
例如,用在关系图谱中,用圆来代替多边形,就能够有效的降低计算开销。尤其是在节点很小的时候,其实多边形和圆的外观没多少差别,但计算开销相差很大。
- 使用缓存
可以提前将图形保存到离屏Canvas中,在需要展示的时候,用drawImage指令将图形当做图像来渲染,能够大大提高效率。
如果需要缓存的图形状态特别多,每个状态都对应一个离屏Canvas对象,那么这个方法对内存的消耗会比较大。所以缓存方法不适合状态经常发生变化的情景。
-
分层渲染
如果绘制的元素大部分都不变,只有少部分在变化,那么就可以将变化的元素和不变的元素进行分层处理。
一层Canvas负责绘制背景,另一层Canvas负责绘制变化的部分。这样大大减少了图形绘制的数量。 -
局部重绘
有些可视化场景中,只有屏幕的部分区域需要动态绘制。这时候可以利用clearRect方法控制要刷新的区域,只对这些区域进行擦除重绘。 -
多线程渲染
可以利用WebWorker,充分利用多核CPU计算资源。Canvas对象通过transferControlToOffscreen转成离屏Canvas对象,发送给Woker线程去进一步处理。
十二、Node是什么
Node是一个基于Chrome V8引擎的javaScript代码的运行环境
浏览器(软件)能够运行javaScript代码,浏览器就是javaScript代码的运行环境
Node(软件)能够运行javaScript代码,Node就是javaScript代码的运行环境