v-model的实现以及它的实现原理吗?
1. vue中双向绑定是一个指令v-model,可以绑定一个动态值到视图,同时视图中变化能改变该值。v-model是语法糖,默认情况下相于:value和@input。
2. 使用v-model可以减少大量繁琐的事件处理代码,提高开发效率,代码可读性也更好
3. 通常在表单项上使用v-model
4. 原生的表单项可以直接使用v-model,自定义组件上如果要使用它需要在组件内绑定value并处理输入事件
5. 我做过测试,输出包含v-model模板的组件渲染函数,发现它会被转换为value属性的绑定以及一个事件监听,事件回调函数中会做相应变量更新操作,这说明神奇魔法实际上是vue的编译器完成的。
new Vue后整个的流程
- initProxy:作用域代理,拦截组件内访问其它组件的数据。
- initLifecycle:建立父子组件关系,在当前组件实例上添加一些属性和生命周期标识。如[Math Processing Error]parent,parent,refs,$children,_isMounted等。
- initEvents:对父组件传入的事件添加监听,事件是谁创建谁监听,子组件创建事件子组件监听
- initRender:声明[Math Processing Error]slots和slots和createElement()等。
- initInjections:注入数据,初始化inject,一般用于组件更深层次之间的通信。
- initState:重要)数据响应式:初始化状态。很多选项初始化的汇总:data,methods,props,computed和watch。
- initProvide:提供数据注入。
思考:为什么先注入再提供呢??
1、首先来自祖辈的数据要和当前实例的data,等判重,相结合,所以注入数据的initInjections一定要在InitState的上面。
2. 从上面注入进来的东西在当前组件中转了一下又提供给后代了,所以注入数据也一定要在上面。
keep-alive的实现
作用:实现组件缓存
钩子函数:
`activated `组件渲染后调用
`deactivated `组件销毁后调用
原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。
配置属性:
include 字符串或正则表达式。只有名称匹配的组件会被缓存
exclude 字符串或正则表达式。任何名称匹配的组件都不会被缓存
max 数字、最多可以缓存多少组件实例
vuex、vue-router实现原理
vuex是一个专门为vue.js应用程序开发的状态管理库。 核心概念:
- state(单一状态树) getter/Mutation显示提交更改state
- Action类似Mutation,提交Mutation,可以包含任意异步操作。
- module(当应用变得庞大复杂,拆分store为具体的module模块)
你怎么理解Vue中的diff算法?
在js中,渲染真实DOM的开销是非常大的, 比如我们修改了某个数据,如果直接渲染到真实DOM, 会引起整个dom树的重绘和重排。那么有没有可能实现只更新我们修改的那一小块dom而不要更新整个dom呢?此时我们就需要先根据真实dom生成虚拟dom, 当虚拟dom某个节点的数据改变后会生成有一个新的Vnode, 然后新的Vnode和旧的Vnode作比较,发现有不一样的地方就直接修改在真实DOM上,然后使旧的Vnode的值为新的Vnode。
diff的过程就是调用patch函数,比较新旧节点,一边比较一边给真实的DOM打补丁。在采取diff算法比较新旧节点的时候,比较只会在同层级进行。 在patch方法中,首先进行树级别的比较 new Vnode不存在就删除 old Vnodeold Vnode 不存在就增加新的Vnode 都存在就执行diff更新 当确定需要执行diff算法时,比较两个Vnode,包括三种类型操作:属性更新,文本更新,子节点更新 新老节点均有子节点,则对子节点进行diff操作,调用updatechidren 如果老节点没有子节点而新节点有子节点,先清空老节点的文本内容,然后为其新增子节点 如果新节点没有子节点,而老节点有子节点的时候,则移除该节点的所有子节点 老新老节点都没有子节点的时候,进行文本的替换
updateChildren 将Vnode的子节点Vch和oldVnode的子节点oldCh提取出来。 oldCh和vCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和vCh至少有一个已经遍历完了,就会结束比较。
浏览器从输入url到渲染页面,发生了什么?
三个方面:
网络篇:
1. 构建请求
2. 查找强缓存
3. DNS解析
4. 建立TCP连接(三次握手)
5. 发送HTTP请求(网络请求后网络响应)
浏览器解析篇:
1. 解析html构建DOM树
2. 解析css构建CSS树、样式计算
3. 生成布局树(Layout Tree)
浏览器渲染篇:
1. 建立图层树(Layer Tree)
2. 生成绘制列表
3. 生成图块并栅格化
4. 显示器显示内容
5. 最后断开连接:TCP 四次挥手
Http和Https区别(高频)
1.`HTTP` 是不安全的,而HTTPS 是安全的
2.`HTTP` 标准端口是80 ,而HTTPS 的标准端口是443
3.`HTTP` 无法加密,而HTTPS 对传输的数据进行加密
4.`HTTP`无需证书,而HTTPS 需要CA的SSL证书
GET和POST区别(高频)
1.GET在浏览器回退不会再次请求,POST会再次提交请求
2.GET请求会被浏览器主动缓存,POST不会,要手动设置
3.GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会
4.GET请求在URL中传送的参数是有长度限制的,而POST没有限制
5.GET参数通过URL传递,POST放在Request body中
6.GET参数暴露在地址栏不安全,POST放在报文内部更安全
7.GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作
8.GET产生一个TCP数据包;POST产生两个TCP数据包
理解xss,csrf,ddos攻击原理以及避免方式
XSS(Cross-Site Scripting,跨站脚本攻击)是一种代码注入攻击。攻击者在目标网站上注入恶意代码,当被攻击者登陆网站时就会执行这些恶意代码,这些脚本可以读取 cookie,session tokens,或者其它敏感的网站信息,对用户进行钓鱼欺诈,甚至发起蠕虫攻击等。
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
XSS避免方式:
1. url参数使用encodeURIComponent方法转义
2. 尽量不是有InnerHtml插入HTML内容
3. 使用特殊符号、标签转义符。
CSRF避免方式:
1. 添加验证码
2. 使用token
服务端给用户生成一个token,加密后传递给用户
用户在提交请求时,需要携带这个token
服务端验证token是否正确
http特性以及状态码
比如:
200响应成功
301永久重定向
302临时重定向
304资源缓存
403服务器禁止访问
404服务器资源未找到
500 502服务器内部错误
504 服务器繁忙
1xx Informational(信息状态码) 接受请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向状态码) 需要附加操作已完成请求
4xx Client Error(客户端错误状态码)服务器无法处理请求
5xx Server Error(服务器错误状态码)服务器处理请求出错复制代码
http如何实现缓存
1. 强缓存==>Expires(过期时间)/Cache-Control(no-cache)(优先级高) 协商缓存 ==>Last-Modified/Etag(优先级高)Etag适用于经常改变的小文件 Last-Modefied适用于不怎么经常改变的大文件
2. 强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。
什么是同源策略
一个域下的js脚本未经允许的情况下,不能访问另一个域下的内容。通常判断跨域的依据是协议、域名、端口号是否相同,不同则跨域。同源策略是对js脚本的一种限制,并不是对浏览器的限制,像img,script脚本请求不会有跨域限制。
前后端如何通信
Ajax : 短连接
Websocket : 长连接,双向的。
Form表单:最原始的
跨域通信的几种方式
解决方案:
1. jsonp(利用script标签没有跨域限制的漏洞实现。缺点:只支持GET请求)
2. CORS(设置Access-Control-Allow-Origin:指定可访问资源的域名)
3. postMessage(message, targetOrigin, [transfer])(HTML5新增API 用于多窗口消息、页面内嵌iframe消息传递),通过onmessage监听 传递过来的数据
4. Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。
5. Node中间件代理
6. Nginx反向代理
7. 各种嵌套iframe的方式,不常用。
8. 日常工作中用的最对的跨域方案是CORS和Nginx反向代理
前端工程化
webpack配置,webpack4.0有哪些优化点
module.exports={ entry: {}, output: {}, plugins: [], module: [rules:[{}]] }
webpack如何实现代码分离
1.入口起点:使用 entry 配置手动地分离代码。
2.防止重复:使用 CommonsChunkPlugin 去重和分离 chunk。
3.动态导入:通过模块的内联函数调用来分离代码。
常见的Webpack Loader? 如何实现一个Webpack Loader(NO)
loader: 是一个导出为函数的javascript模块,根据rule匹配文件扩展名,处理文件的转换器。
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
url-loader: 与file-loader类似,区别是用户可以设置一个阈值,大于阈值会交给file-loader处理,小于阈值时返回文件base64 形式编码 (处理图片和字体)
image-loader:加载并且压缩图片文件
babel-loader:把 ES6 转换成 ES5
sass-loader:将SCSS/SASS代码转换成CSS
css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS
postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀 eslint-loader:通过 ESLint 检查 JavaScript 代码
常见的Webpack Plugin? 如何实现一个Webpack Plugin(NO)
plugin:本质是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)
uglifyjs-webpack-plugin:压缩js文件
clean-webpack-plugin:目录清除
mini-css-extract-plugin:分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)
loader和plugin对比?
- Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
- Plugin 在 plugins 中单独配置,类型为数组,每一项是一个Plugin的实例,参数都通过构造函数传入。
前端模块化,CMD、AMD、CommonJS
CommonJS
CommonJS是服务器端模块的规范,由Node推广使用,webpack也采用这种规范编写
commonJs规范:
CommonJS模块规范主要分为三部分:模块定义、模块标识、模块引用。
模块定义:module对象:在每一个模块中,module对象代表该模块自身。 export属性:module对象的一个属性,它向外提供接口。输出模块变量的最好方法是使用module.exports对象。一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。
模块标识:传递给require方法的参数,必须是符合小驼峰命名的字符串,或者以 . 、.. 、开头的相对路径,或者绝对路径。
模块引用:加载模块使用require(同步加载),该方法读取一个文件并执行,返回文件内部的module.exports对象。
优势:
在后端,JavaScript的规范远远落后并且有很多缺陷,这使得难以使用JavaScript开发大型应用。比如:没有模块系统、标准库较少、没有标准接口、缺乏包管理系统、列表内容
CommonJS模块规范很好地解决变量污染问题,每个模块具有独立空间,互不干扰,命名空间相比之下就不太好。
CommonJS规范定义模块十分简单,接口十分简洁。
CommonJS模块规范支持引入和导出功能,这样可以顺畅地连接各个模块,实现彼此间的依赖关系
CommonJS规范的提出,主要是为了弥补JavaScript没有标准的缺陷,已达到像Python、Ruby和Java那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段
缺点:
没有并行加载机制
由于CommonJS是同步加载模块,这对于服务器端是很不好的,因为所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。但是,对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态。
所以浏览器端不是很适合Common.Js,出现另一种规范AMD
AMD
AMD 是运行在浏览器环境的一个异步模块定义规范 ,是RequireJS 在推广过程中对模块定义的规范化产出。
AMD规范
AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
优点
用户体验好,因为没有延迟,依赖模块提前执行了。
CMD
CMD是一个通用模块定义规范;是SeaJs推广过程中对模块定义的规范化产出
CMD规范
CMD推崇依赖就近,只有在用到某个模块的时候才会去require
优点
性能好,因为只有用户需要的时候才执行。
防抖节流
函数防抖关注一定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。
防抖
//定义:触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。 //搜索框搜索输入。只需用户最后一次输入完,再发送请求 //手机号、邮箱验证输入检测 onchange oninput事件 //窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。更多》》 const debounce = (fn, wait, immediate) => { let timer = null; return function (...args) { if (timer) clearTimeout(timer); if (immediate && !timer) { fn.call(this, args); } timer = setTimeout(() => { fn.call(this, args); }, wait); }; }; const betterFn = debounce(() => console.log("fn 防抖执行了"), 1000, true); document.addEventListener("scroll", betterFn);
节流
//定义:当持续触发事件时,保证隔间时间触发一次事件。//1. 懒加载、滚动加载、加载更多或监听滚动条位置;//2. 百度搜索框,搜索联想功能;//3. 防止高频点击提交,防止表单重复提交;更多》》 function throttle(fn,wait){ let pre = 0; return function(...args){ let now = Date.now(); if( now - pre >= wait){ fn.apply(this,args); pre = now; } } }function handle(){ console.log(Math.random()); }window.addEventListener("mousemove",throttle(handle,1000));
对象深浅拷贝
//浅拷贝 1. Object.assign(target,source) 2. es6对象扩展运算符。 //深拷贝 function deepClone(obj) { if (!obj || typeof obj !== "object") return; let newObj = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key]; } } return newObj; }
数组去重,数组对象去重
//数组 const arr = [2,7,5,7,2,8,9]; console.log([...new Set(arr)]);// [2,7,5,8,9]; //对象 const list = [{age:18,name:'张三'},{age:18,name:'李四'},{age:18,name:'王五'}] let hash = {}; const newArr = arr.reduce((item, next) => { hash[next.age] ? '' : hash[next.age] = true && item.push(next); return item; }, []);console.log(list);
数组扁平化
function flatten(arr) { return arr.reduce((result, item) => { return result.concat(Array.isArray(item) ? flatten(item) : item); }, []); }
你都做过哪些Vue的性能优化?
编码阶段
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
v-if和v-for不能连用
如果需要使用v-for给每项元素绑定事件时使用事件代理
SPA 页面采用keep-alive缓存组件
在更多的情况下,使用v-if替代v-show
key保证唯一
使用路由懒加载、异步组件
防抖、节流
第三方模块按需导入
长列表滚动到可视区域动态加载
图片懒加载
SEO优化
预渲染
服务端渲染SSR
打包优化
压缩代码
Tree Shaking/Scope Hoisting
使用cdn加载第三方模块
多线程打包happypack
splitChunks抽离公共文件
sourceMap优化
用户体验
骨架屏
PWA
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。