在公司移动端和PC端都做过相应的开发,然后就介绍了移动端开发的一些东西,然后说了下H5是如何和客户端进行交互的,最后说了下自己参与的一些weex开发项目,说了下weex与H5的区别。
2. 能聊一聊weex么,原理是啥?
简单介绍了下weex出现的原因与好处,然后说了下weex的开发流程,最后介绍了下weex的实现原理,具体可以参考下这篇文章:
https://zhuanlan.zhihu.com/p/71064826说实话之前只是了解过flutter,对于他的开发方式不是怎么了解,反正就把我知道的说了一下,面试官也是似懂非懂吧
再好的文档也比不上官方文档,微信官方文档介绍了很清楚,
https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/#
%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%AE%80%E4%BB%8B 小程序的主要开发语言是 JavaScript ,小程序的开发同普通的网页开发相比有很大的相似性。对于前端开发者而言,从网页开发迁移到小程序的开发成本并不高,但是二者还是有些许区别的。网页开发渲染线程和脚本线程是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应,而在小程序中,二者是分开的,分别运行在不同的线程中。网页开发者可以使用到各种浏览器暴露出来的 DOM API,进行 DOM 选中和操作。而如上文所述,小程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。说实话这个真的不清楚,瞎扯了一堆,后来找到了这篇文章:
https://mp.weixin.qq.com/s/4ct3au_BsUHJb0wrXSKSHQ6. 平时我们浏览器打开一个链接会提示打开一个APP,知道是什么原理么,微信中貌似有很多限制你们有尝试过解决么?
这其实是一个很头疼的问题,国产浏览器有各种限制,微信限制就更多了,所以很多方案是没有办法实施的 最常规的方案还是通过Scheme进行唤起,微信中没有白名单的用户是没法用的,一般是引导打开默认浏览器,或者跳转应用宝 没有完美的方法更多方案可以看callapp库作者写的文章:
https://suanmei.github.io/2018/08/23/h5_call_app/太多了,这边不展开了,有机会写新的文章来聊一聊
前端客户端配合实现,通过提供前端资源离线包,和离线配置,同时客户端通过配置hook h5的请求来选择匹配本地资源或者请求线上,做到离线。
请求预加载就是在启动webview的时候,客户端同时进行接口数据的请求
GraphQL能够根据页面展示需求请求所需要的数据,不会有冗余的数据,传输效率上会更高
服务端渲染,之前写过vue的ssr,更好的首屏展示,更好的SEO,对服务器的压力会更大
前面提到性能优化中提到了边缘计算,也来自之前看到的文章中说的,文章在这里:
https://juejin.im/post/684490417378847950213. 聊了这么多你能用几个关键词对整个前端性能提升大致分类么?
页面是否可以快速加载 是否允许用户快速开始与之交互 滚动和动画是否流畅
网上一大把,含义的理解,关键头的理解
nginx里面,是由Last-Modified和content-length的十六进制组合而成 不同 Web 服务器或者 CDN 的 ETag 生成方式不一样。
用户行为监控 性能监控 异常监控
监听error事件处理常规js错误 监听unhandledrejection事件处理当Promise 被 reject 且没有 reject 处理器的时候 魔改ajax请求处理接口异常
跨域脚本的异常是捕获不到的,只是抛出来Script error,增加crossorigin="anonymous"属性就好了,当然脚本cdn那边跨域请求头也是需要的
不存在跨域问题 执行过程无阻塞 体积较小 不占用ajax的请求限额
多路复用、首部压缩、服务器推送
21. 为什么http1不能实现多路复用?
http1阶段是基于文本传输的,由于没有流的概念,在使用并行传输(多路复用)传递数据时,接收端在接收到响应后,并不能区分多个响应分别对应的请求,所以无法将多个响应的结果重新进行组装,也就实现不了多路复用。
因为每个HTTP 报文都要传输臃肿的首部字段导致的网络效率降低,解决思路,通信双方可以都维护一张HTTP 首部字段索引列表,报文中只传输对应字段的索引值,就能大大压缩报文首部的长度,提高网络利用率。HTTP/2 在客户端与服务器端都维护了一张首部字段索引列表, header 字段列表是以key - value 键值对元素构成的有序集合,每个header 字段元素都映射为一个索引值,报文中使用header 字段的索引值进行二进制编码传输,显然比HTTP/1.1 直接使用header 字段ASCII 编码传输,数据量小得多,这种减少header 字段传输开销的技术可以称为首部压缩HPACK。
23. 讲解一下https的工作原理?
HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺术品,TLS/SSL中使用了非对称加密,对称加密以及HASH算法。握手过程的简单描述如下:
浏览器将自己支持的一套加密规则发送给网站。
网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
获得网站证书之后浏览器要做以下工作:
验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
网站接收浏览器发来的数据之后要做以下的操作:
使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
使用密码加密一段握手消息,发送给浏览器。
浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。
跨域处理的方案是在太多太多了,我们公司主要是通过nginx进行转发,前端发起请求的地址是和页面地址一致的所以不存在跨域,nginx将请求转发到正确的服务。页面地址:web.taobao.com,请求接口地址
web.taobao.com/api.taobao.com/***1)为什么会有Event LoopJavaScript的任务分为两种同步和异步,它们的处理方式也各自不同,同步任务是直接放在主线程上排队依次执行,异步任务会放在任务队列中,若有多个异步任务则需要在任务队列中排队等待,任务队列类似于缓冲区,任务下一步会被移到调用栈然后主线程执行调用栈的任务。
调用栈:调用栈是一个栈结构,函数调用会形成一个栈帧,帧中包含了当前执行函数的参数和局部变量等上下文信息,函数执行完后,它的执行上下文会从栈中弹出。
JavaScript是单线程的,单线程是指 js引擎中解析和执行js代码的线程只有一个(主线程),每次只能做一件事情,然而ajax请求中,主线程在等待响应的过程中回去做其他事情,浏览器先在事件表注册ajax的回调函数,响应回来后回调函数被添加到任务队列中等待执行,不会造成线程阻塞,所以说js处理ajax请求的方式是异步的。
综上所述,检查调用栈是否为空以及讲某个任务添加到调用栈中的个过程就是event loop,这就是JavaScript实现异步的核心。
2)浏览器中的 Event Loop
Micro-Task 与 Macro-Task
浏览器端事件循环中的异步队列有两种:macro(宏任务)队列和 micro(微任务)队列。
常见的 macro-task:setTimeout、setInterval、script(整体代码)、 I/O 操作、UI 渲染等。
常见的 micro-task: new Promise().then(回调)、MutationObserve 等。
requestAnimationFrame
requestAnimationFrame也属于异步执行的方法,但该方法既不属于宏任务,也不属于微任务。按照MDN中的定义:
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
requestAnimationFrame是GUI渲染之前执行,但在Micro-Task之后,不过requestAnimationFrame不一定会在当前帧必须执行,由浏览器根据当前的策略自行决定在哪一帧执行。
event loop过程
检查macrotask队列是否为空,非空则到2,为空则到3
执行macrotask中的一个任务
继续检查microtask队列是否为空,若有则到4,否则到5
取出microtask中的任务执行,执行完成返回到步骤3
执行视图更新
当某个宏任务执行完后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后,再次读取微任务队列里的任务,依次类推。
26. 简单聊一聊Promise的原理?
https://zhuanlan.zhihu.com/p/58428287
采用双精度存储造成的问题,这篇文章说的挺清楚
https://www.cnblogs.com/snandy/p/4943138.html (0.1*10 + 0.2*10) / 10 == 0.3 // true因为CommonJS的require语法是同步的,所以就导致了CommonJS模块规范只适合用在服务端,而ES6模块无论是在浏览器端还是服务端都是可以使用的,但是在服务端中,还需要遵循一些特殊的规则才能使用
CommonJS 模块输出的是一个值的拷贝,而ES6 模块输出的是值的引用
CommonJS 模块是运行时加载,而ES6 模块是编译时输出接口,使得对JS的模块进行静态分析成为了可能
因为两个模块加载机制的不同,所以在对待循环加载的时候,它们会有不同的表现。CommonJS遇到循环依赖的时候,只会输出已经执行的部分,后续的输出或者变化,是不会影响已经输出的变量。而ES6模块相反,使用import加载一个变量,变量不会被缓存,真正取值的时候就能取到最终的值
关于模块顶层的this指向问题,在CommonJS顶层,this指向当前模块;而在ES6模块中,this指向undefined
关于两个模块互相引用的问题,在ES6模块当中,是支持加载CommonJS模块的。但是反过来,CommonJS并不能requireES6模块,在NodeJS中,两种模块方案是分开处理的
vdom使用js来描述真实的dom,我们不用直接操作dom,操作数据即可,可维护性更高 vdom使得跨平台更加的方便
介绍了工作中设计开发的一些组件
分析日志和代码,猜想问题出现的原因,使用工具分析,优化代码分析原因
这篇文章说的挺详细的:
https://segmentfault.com/a/1190000020077274?utm_source=sf-related34. pm2原理?
pm2 基于 cluster模块 进行了封装,它能自动监控进程状态、重启进程、停止不稳定进程、日志存储等。利用 pm2 时,可以在不修改代码的情况下实现负载均衡集群。
搜一下一大把,开新的文章来讲
2020全网最全前端安全综述
这边打算开新的文章来讲
Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,需要搭配各种插件使用
简单介绍了下自己写过的插件
AST (Abstract Syntax Tree)是抽象语法树的英文简称,它从源代码到运行的编译程序过程中起到重要的作用。AST 是源代码语法结构的一种抽象表现形式,通常以树的数据结构来表达,树上每一个节点都表现出源代码中的结构。Babel 编译器的编译过程,它将 JavaScript 的高级版本编译成低级版本,整个过程离不开抽象语法树对源代码的抽象过程。
41. 聊一聊Webpack热更新的原理?
**步,在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。
第二步是 webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。
第三步是 webpack-dev-server 对文件变化的一个监控,这一步不同于**步,并不是监控代码变化重新打包。当我们在配置文件中配置了
devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念。第四步也是 webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。
webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。
HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。
而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。
使用高版本的 Webpack、多线程/多实例构建、缩小打包作用域、充分利用缓存提升二次构建速度、DLL
这个最好自己能够看一看源码了
hooks和composition api是react和vue最新的代码组织方式 他们的出现是为了更好的逻辑复用,使得代码的逻辑更加的清晰 vue2.0中代码组织方式更多的是options,这样很多代码都揉在了一起,并不清晰,复用多用mixin来写,mixin也有很大的问题,多个mixin的时候不知道变量来自哪个mixin,代码跳跃感太强,因为根本没定义就使用了,给人一种难以理解的感觉,同时多个的时候可能会存在变量冲突
更快、更准确
Mutation是同步的,唯一改变store的方法 Action 提交的是 Mutation,而不是直接变更状态。
区分 actions 和 mutations 并不是为了解决竞态问题,而是为了能用 devtools 追踪状态变化。vuex 真正限制你的只有 mutation 必须是同步的,同步的意义在于这样每一个 mutation 执行完成后都可以对应到一个新的状态,这样 devtools 就可以打个 snapshot 存下来,然后就可以随便 time-travel 了。
力扣第141题:
https://leetcode-cn.com/problems/linked-list-cycle/ function hasCycle(head) { let temp = head const map = new Map() while (temp) { if (map.has(temp)) { return true } map.set(temp, true) temp = temp.next } return false };类似力扣第59题:
https://leetcode-cn.com/problems/spiral-matrix-ii/ function generateMatrix (n) { // 定义一个二维数组进行数据保存 const result = [] for (let i = 0; i < n; i++) { result.push(new Array(n)) } let left = 0 let right = n - 1 let top = 0 let bottom = n - 1 let current = 1 while(left <= right && top <= bottom) { // 左边从上到下 for (let i = top; i <= bottom; i++) { result[i][left] = current++ } if (++left > right) break // 下边从左到右 for (let i = left; i <= right; i++) { result[bottom][i] = current++ } if (--bottom < top) break // 右边从下到上 for (let i = bottom; i >= top; i--) { result[i][right] = current++ } if (--right < left) break for (let i = right; i >= left; i--) { result[top][i] = current++ } if (++top > bottom) break } return result }其实就是爬楼梯问题,剑指 Offer 10- II. 青蛙跳台阶问题:
https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/ var climbStairs = function(n) { if (n <= 2) return n let n1 = 1 let n2 = 2 let nn = 0 for (let i = 3; i <= n; i++) { nn = n1 + n2 n1 = n2 n2 = nn } return nn };11. 二叉树的最大深度
力扣第104题:
https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ var maxDepth = function(root) { if (!root) { return 0 } const left = maxDepth(root.left) const right = maxDepth(root.right) return Math.max(left, right) + 1 };剑指 Offer 30:
https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/ /** * initialize your data structure here. */ var MinStack = function() { this.stack=[]; this.stackmin=[]; }; /** * @param {number} x * @return {void} */ MinStack.prototype.push = function(x) { this.stack.push(x); if(this.stackmin.length===0 || this.stackmin[this.stackmin.length-1]>=x){ this.stackmin.push(x); } }; /** * @return {void} */ MinStack.prototype.pop = function() { let x=this.stack.pop(); if(x===this.stackmin[this.stackmin.length-1]){ this.stackmin.pop(); } }; /** * @return {number} */ MinStack.prototype.top = function() { return this.stack[this.stack.length-1]; }; /** * @return {number} */ MinStack.prototype.min = function() { return this.stackmin[this.stackmin.length-1]; };力扣第104题:
https://leetcode-cn.com/problems/maximum-subarray/ var maxSubArray = function(nums) { let current = nums[0] let max = nums[0] for (let i = 1; i < nums.length; i++) { if (current <= 0) { current = nums[i] } else { current += nums[i] } if (max < current) { max = current } } return max };力扣第64题:
https://leetcode-cn.com/problems/minimum-path-sum/ var minPathSum = function(grid) { let n = grid.length; let m = grid[0].length; let dp = Array.from(new Array(n), () => new Array(m)); for (let i = 0; i < n; i ++) { for (let j = 0; j < m; j ++) { if (i != 0 && j != 0) { dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; } else if (i == 0 && j != 0) { dp[i][j] = dp[i][j - 1] + grid[i][j]; } else if (i != 0 && j == 0) { dp[i][j] = dp[i - 1][j] + grid[i][j]; } else if (i == 0 && j == 0) { dp[i][j] = grid[i][j]; } } } return dp[n - 1][m - 1]; };力扣第226题:
https://leetcode-cn.com/problems/invert-binary-tree/ var invertTree = function(root) { if (root === null) return null; const left = invertTree(root.left); const right = invertTree(root.right); root.left = right; root.right = left; return root; };力扣第235题:
https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ const lowestCommonAncestor = (root, p, q) => { while (root) { if (p.val < root.val && q.val < root.val) { root = root.left; } else if (p.val > root.val && q.val > root.val) { root = root.right; } else { break; } } return root; };1. 项目开发中有遇到什么挑战没
可以从技术上说一说,也可以从业务处理上说一说
谈一谈自己学习的动力,以及怎么学习的
说了一些自己的想法,未来希望成长到什么程度
根据自己的项目来回答了,其实项目都上线了,并没有啥难不难的了,问题都解决了,主要是解决问题的过程比较重要,最后总结得出了啥
自己开发中扮演的角色
说一说现在的项目还存在的哪些问题,为啥没一开始就优化,自己打算以后怎么维护
说一说最近看的比较新的东西