10个AI前端开发工具:提升你的开发效率

十个前端开发AI工具

AI 正在彻底改变软件开发领域,前端开发人员也不例外。以下是10个专门针对前端开发的AI工具,它们将帮助你提高工作效率,优化开发流程。

十个前端开发AI工具

1. Webcrumbs:即时生成组件

Webcrumbs 是一款强大的前端AI助手,能够根据用户请求、图像或屏幕截图即时生成UI组件代码。它支持 TailwindCSS 和常规 CSS,并允许你轻松复制粘贴代码到项目中。此外,Webcrumbs 还提供实时协作、语法突出显示等功能,是一个开源工具。

2. Watsonx Code Assistant:企业级代码生成

由IBM团队开发的 Watsonx Code Assistant 是一款企业级的编码助手,利用生成式AI加快开发速度。它可以根据自然语言请求生成代码,并提供代码建议的来源,提高透明度。此外,它还支持多种产品,如 Red Hat Ansible Lightspeed 和 Z。

3. Coderabbit:团队代码生成和错误检测

Coderabbit 是 GitHub 和 GitLab 上安装最多的AI应用程序,它通过生成AI驱动的上下文反馈来减少代码审查时间。Coderabbit 提供实时聊天评论、带有序列图的拉取请求摘要等功能,帮助团队更高效地协作。

4. v0:React 和 TailwindCSS 智能 UI 合成

v0 是 Vercels 的AI团队创建的一款前端助手,专注于 React、Next.js App Router 和现代 Web 开发实践。它可以将上传的图像转换为 React 代码,并根据 UI 的文本描述生成代码,提供清晰的编码解决方案。

5. Code Llama:代码生成和调试AI

Code Llama 是由 Meta AI 团队构建的一款基于 Llama 2 的AI助手,支持多种流行语言。它通过文本提示生成和讨论代码,特别适用于 Python 开发人员。Code Llama 还提供三种变体,满足不同的需求。

6. CodeParrot:用于 UI 组件的设计到代码AI

CodeParrot 是一个 VS Code 插件,使用 AI 将 Figma 组件或屏幕截图转换为代码。它支持多种编程语言和框架,如 React、Tailwind 和 TypeScript,帮助你快速构建令人惊叹的应用程序 UI。

7. MutableAI:AI 重构和代码优化

MutableAI 的目标是提高生产力 10 倍,帮助开发人员编写代码文档并自动更新 wiki 文章。它还提供 AI 聊天和面向整个团队的非技术模式,快速提取答案。

8. ellipsis.dev:拉取请求审查和代码生成

ellipsis.dev 是一个 AI 助手,可以审查拉取请求、创建发行说明并直接在 GitHub 上修复错误。它支持 20 多种语言,并允许你添加自然语言规则来自定义审查过程。

9. CodeT5+:开源代码生成和理解

CodeT5+ 是基于 T5 架构的代码理解和生成的 AI 助手。它可以生成代码、完成函数块代码,并使用自然语言描述生成函数摘要。CodeT5+ 是一个开源工具,支持多种编码任务。

10. Jam.dev:一键错误报告

Jam.dev 将你的错误报告时间缩短了 20 倍。它是一个浏览器扩展,只需两次点击即可报告错误,并自动包含错误上下文。Jam.dev 还提供 AI 调试助手,帮助开发人员更快地调试和修复代码。

工具再好,也得用起来。当然,如果有更好的工具,我也会持续分享出来。

 

HTML迎来一个新元素::很好用,我正在努力转正

前端发展堪称变异级的发展,五花八门,眼花缭乱,但html元素的变化一般相对稳定,只有在重大版本更新时候才会有新html元素的加入和旧html元素的弃用。最近Chrome 126 于发布了稳定版本,其中一个比较有趣的变化就是给 HTML 带来一个新的元素:<permission> ,它将从这个版本开始试用,并且正在努力走向标准化,也就是这个新成员还在试用期,尚未转正。

但为啥要新加一个元素呢?那肯定有啥绝活呗,下面这篇文章非常详细的介绍了 <permission> 元素的用法,大家可以试试,发表下自己的试用感受。

Web 权限提示的问题

当 Web 应用程序需要访问浏览器的高级功能时,需要向用户主动请求许可。例如,当百度地图使用 Geolocation API 获取用户的地理位置时,浏览器会提示用户申请权限,这是权限规范中定义明确的概念。

new-element-permisson-html

new-element-permisson-html

申请权限的触发方式一般分为两类,被动隐式触发,或者主动显示触发:

例如,Geolocation API 是一个强大的 API,它的使用依赖于首次使用时隐式询问的方法。例如,当程序调用 navigator.geolocation.getCurrentPosition() 方法时,权限提示框会在第一次调用时自动弹出,还有另外一个例子是 navigator.mediaDevices.getUserMedia()

一些其他的 API,如 Notification API 或 Device Orientation API,通常有一种显式的方式通过静态方法来请求权限,如 Notification.requestPermission() 或 DeviceMotionEvent.requestPermission()

网站可以在加载时立即调用诸如 navigator.mediaDevices.getUserMedia() 或 Notification.requestPermission() 等方法。这会导致在用户还没与网站进行交互时就弹出权限提示。这就是明显的权限滥用行为,并且影响到两种方式,既包括首次使用时的隐含询问,也包括提前明确请求。

new-element-permisson-html-2


new-element-permisson-html-2

权限滥用导致浏览器厂商要求有像点击按钮或按下按键这样的用户操作,然后才会显示权限提示。这种方法的问题在于,浏览器很难确定某个特定的用户操作是否应该导致显示权限提示。也许用户只是因为页面加载时间太长而在页面上随意某个地方随便点击,有些网站也变得非常擅长诱骗用户点击内容来触发提示。

另一个问题是权限提示框通常显示的方式:在网站的 “死亡线” 之上(特别是在大屏幕上),也就是说,在应用程序能够绘制到的浏览器窗口区域之外。用户在刚刚点击了窗口底部的一个按钮后,可能会错过浏览器窗口顶部的提示,这种情况还是挺常见的。当浏览器有应对权限滥用的缓解措施时,这个问题往往会更加严重。

new-element-permisson-html-2


new-element-permisson-html-2

另外,用户一旦做出了拒绝某个权限的操作,之后想要改变就不太容易了。他们得找到特定的地方,比如那个网站信息下拉菜单,然后去进行重置或调整权限的操作,而且还得重新加载页面才行。网站也没办法提供很方便的途径让用户快速改变权限状态,还得详细地告诉用户怎么去找到地方改变设置。

 new-element-permisson-html-2

如果某个权限是非常重要的,比如视频会议软件要用麦克风权限,那像谷歌会议这类的软件就会弹出很显眼的对话框来告诉用户怎么去把之前阻止的权限给开通。

 new-element-permisson-html

<permission> 元素

为了解决上面的这些问题,<permission> 元素诞生了。这个元素允许开发者以声明方式请求使用权限,如下例所示:

<permission type="camera" />

“type” 属性代表你正在请求的权限列表(如果有多个可以以空格分割)。目前,允许的值是 'camera''microphone' 以及 'camera microphone'。默认情况下,这个元素呈现出来的样子类似于具有最简用户代理样式的按钮。

 new-element-permisson-html-2

对于某些允许附加参数的权限,type-ext 属性接受以空格分隔的键值对,例如 precise:true 地理位置权限。

当用户与 <permission> 元素交互时,他们可以循环经历各个阶段:

如果他们之前不允许某项功能,他们可以在每次访问时允许该功能,或者在当前访问时允许该功能。

图片

如果他们之前允许该功能,他们可以继续允许,或者停止允许。

图片

如果他们之前不允许某项功能,他们可以继续不允许它,或者这次允许它。

图片

<permission> 元素的文本会根据状态自动更新。例如,如果已授予使用某项功能的权限,则文本会更改为表示允许使用该功能。如果需要先授予权限,则文本会更改为邀请用户使用该功能。将之前的屏幕截图与以下屏幕截图进行比较,以查看这两种状态。

图片

<permission> 元素可以与 Permissions API 一起使用。有许多事件可供监听:

  • onpromptdismiss:当元素触发的权限提示被用户关闭(例如,单击关闭按钮或单击提示之外)时,会触发此事件。
  • onpromptaction:当元素触发的权限提示已被用户对提示本身采取某种操作解决时,触发此事件。这并不一定意味着权限状态已经改变,用户可能已经采取了维持现状的操作(例如继续允许权限)。
  • onvalidationstatuschange:当元素从 "valid" 切换到 "invalid" 时触发此事件,例如当元素被其他超文本标记语言内容部分遮挡时,会认为是 "invalid"

我们可以直接在 HTML 代码中内联注册这些事件的事件监听器(<permission type="…" onpromptdismiss="alert('The prompt was dismissed');" />),或者在 <permission> 元素上使用 addEventListener()

<permission type="camera" />
<script>
  const permission = document.querySelector('permission');
  permission.addEventListener('promptdismiss', showCameraWarning);

  function showCameraWarning() {
    // Show warning that the app isn't fully usable
    // unless the camera permission is granted.
  }

  const permissionStatus = await navigator.permissions.query({name: "camera"});
  permissionStatus.addEventListener('change', () => {
    // Run the check when the status changes.
    if (permissionStatus.state === "granted") {
      useCamera();
    }
    // Run the initial check.
    if (permissionStatus.state === "granted") {
      useCamera();
    }
  });
</script>


你觉得这个新html元素z

文章来源:

来源:code秘密花园

参考:https://developer.chrome.com/blog/permission-element-origin-trial

jQuery已死?No!JQuery4.0已完成100%,它依旧锋利,或将王者归来

jQuery,曾经的前端王者,几乎无处不在。以前可以说是无jq,不前端。
但随着各大框架风气云涌,jquery逐渐淡出人们的视野,现在开发还用jq,都有被鄙视的风险。
江湖甚至传言,jquery已死。
但jQuery真的成为历史了吗?不是的,jQuery正在偷偷的进化,jQuery 4.0正准备以王者的姿态重归王位。
Github上jQuery 4.0的开发进度显示已100%完成。

jQuery 4.0

jQuery 4.0

 

前几天看还是99%,士别三日,就全部完成了,得加鸡腿。

jq4.0如此快的进度,可能也是为了回答那个一直被追问的问题:jquery 4.0 为什么这么久都没有发布?

这里想说,既然已经100%完成,想必离发布的日子也不远了。

大家保持期待,想象一下曾经锋利的JQ是否依旧,江湖虽变,但宝刀未老依旧锋利。

前端开发又会发生怎么样的变化呢?

2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(三)

本文是2022年来前端开发人员自求职过程中最新面试题系列的第三篇,主要包括函数组件和类组件、for in和for of、react与vue、react更新机制、BFC及其优缺点、SPA单页面、盒模型、前端登录流程、Http与Https协议区别、vue中购物车实现原理、常见的git状态。

下面开始挨个过一遍。

51、说一下for…in 和 for…of的区别?

for...of遍历获取的是对象的键值, for...in获取的是对象的键名;
for...in会遍历对象的整个原型链, 性能非常差不推荐使用,而for...of只遍历当前对象不会遍历原型链;
对于数组的遍历,for...in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for...of只返回数组的下标对应的属性值;
总结:for...in循环主要是为了遍历对象而生,不适用遍历数组; for....of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象
复制代码

52、说一下类组件和函数组件的区别?

1. 语法上的区别:

函数式组件是一个纯函数,它是需要接受props参数并且返回一个React元素就可以了。类组件是需要继承React.Component的,而且class组件需要创建render并且返回React元素,语法上来讲更复杂。

2. 调用方式

函数式组件可以直接调用,返回一个新的React元素;类组件在调用时是需要创建一个实例的,然后通过调用实例里的render方法来返回一个React元素。

3. 状态管理

函数式组件没有状态管理,类组件有状态管理。

4. 使用场景

类组件没有具体的要求。函数式组件一般是用在大型项目中来分割大组件(函数式组件不用创建实例,所有更高效),一般情况下能用函数式组件就不用类组件,提升效率。
复制代码

53、说一下react的更新机制

54、说一下redux 里面有什么

55、说一下react和vue框架的区别

56、说一下proxy  它有什么优点

57、说一下vue3.0你了解多少?

 <!-- 响应式原理的改变 Vue3.x 使用Proxy取代 Vue2.x 版本的Object.defineProperty -->
 <!-- 组件选项声明方式Vue3.x 使用Composition API setup 是Vue3.x新增的一个选项,他
    是组件内使用Composition API 的入口 -->
 <!-- 模板语法变化slot具名插槽语法 自定义指令 v-model 升级 -->
 <!-- 其它方面的更改Suspense支持Fragment(多个根节点) 和Protal (在dom其他部分渲染组建内容)组件
     针对一些特殊的场景做了处理。基于treeshaking优化,提供了更多的内置功能。 -->
复制代码

58、说一下bfc bfc有什么优缺点

59、说一下你对盒模型的理解?

CSS3中的盒模型有以下两种:标准盒模型、IE盒模型
盒模型都是由四个部分组成的,分别是margin、border、padding和content
标准盒模型和IE盒模型的区别在于设置width和height时, 所对应的范围不同
1、标准盒模型的width和height属性的范围只包含了content
2、IE盒模型的width和height属性的范围包含了border、padding和content
可以通过修改元素的box-sizing属性来改变元素的盒模型;
1、box-sizing:content-box表示标准盒模型(默认值)
2、box-sizing:border-box表示IE盒模型(怪异盒模型)
复制代码

60、说一下SPA单页面有什么优缺点?

优点:

1.体验好,不刷新,减少 请求  数据ajax异步获取 页面流程;

2.前后端分离

3.减轻服务端压力

4.共用一套后端程序代码,适配多端

缺点:

1.首屏加载过慢;

2.SEO 不利于搜索引擎抓取
复制代码

61、说一下前端登录的流程?

初次登录的时候,前端调后调的登录接口,发送用户名和密码,后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token,和一个用户信息的值,前端拿到token,将token储存到Vuex中,然后从Vuex中把token的值存入浏览器Cookies中。把用户信息存到Vuex然后再存储到LocalStroage中,然后跳转到下一个页面,根据后端接口的要求,只要不登录就不能访问的页面需要在前端每次跳转页面师判断Cookies中是否有token,没有就跳转到登录页,有就跳转到相应的页面,我们应该再每次发送post/get请求的时候应该加入token,常用方法再项目utils/service.js中添加全局拦截器,将token的值放入请求头中 后端判断请求头中有无token,有token,就拿到token并验证token是否过期,在这里过期会返回无效的token然后有个跳回登录页面重新登录并且清除本地用户的信息

62、说一下前端权限管理怎么实现

63、说一下购物车的逻辑?

//vue中购物车逻辑的实现
1. 购物车信息用一个数组来存储,数组中保存对象,对象中有id和count属性

2. 在vuex中state中添加一个数据 cartList 用来保存这个数组

3. 由于商品详情页需要用到加入购物车功能,所以我们需要提供一个mutation, 用来将购物车信息加入 cartList中

4. 加入购物车信息的时候,遵照如下规则: 如果购物车中已经有了该商品信息,则数量累加,如果没有该商品信息,则新增一个对象

5. 在商品详情页,点击加入购物车按钮的时候,调用vuex提供的addToCart这个mutation将当前的商品信息 (id count)传给addTocart  this.$store.commit("addToCart", {id:  , count:})

// js中购物车逻辑的实现
1.商品页点击“加入购物车”按钮,触发事件

2.事件调用购物车“增加商品”的Js程序(函数、对象方法)

3.向Js程序传递传递“商品id”、“商品数量”等数据

4.存储“商品id”、“商品数量”到浏览器的localStorage中

**展示购物车中的商品******

1.打开购物车页面

2.从localStorage中取出“商品Id”、“商品数量”等信息。

3.调用服务器端“获得商品详情”的接口得到购物车中的商品信息(参数为商品Id)

4.将获得的商品信息显示在购物车页面。

**完成购物车中商品的购买******

1.用户对购物车中的商品完成购买流程,产生购物订单

2.清除localStorage中存储的已经购买的商品信息

备注1:购物车中商品存储的数据除了“商品id”、“商品数量”之外,根据产品要求还可以有其他的信息,例如完整的商品详情(这样就不用掉服务器接口获得详情了)、购物车商品的过期时间,超过时间的购物车商品在下次打开网站或者购物车页面时被清除。

备注2:购物车商品除了存储在localStorage中,根据产品的需求不同,也可以存储在sessionStorage、cookie、session中,或者直接向服务器接口发起请求存储在服务器上。何种情况使用哪种方式存储、有啥区别请自己分析。
复制代码

64、说一下HTTP和HTTPS协议的区别?

1、HTTPS协议需要CA证书,费用较高;而HTTP协议不需要
2、HTTP协议是超文本传输协议,信息是明文传输的,HTTPS则是具有安全性的SSL加密传输协议;
3、使用不同的连接方式,端口也不同,HTTP协议端口是80,HTTPS协议端口是443;
4、HTTP协议连接很简单,是无状态的;HTTPS协议是具有SSL和HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP更加安全
复制代码
65、说一下常见的HTTP状态码?说一下状态码是302和304是什么意思?你在项目中出现过么?你是怎么解决的?
    <!-- 状态码:由3位数字组成,第一个数字定义了响应的类别 -->
    <!-- 1xx:指示消息,表示请求已接收,继续处理 -->
    <!-- 2xx:成功,表示请求已被成功接收,处理 -->
    <!-- 200 OK:客户端请求成功
         204 No Content:无内容。服务器成功处理,但未返回内容。一般用在只是客户端向服务器发送信息,而服务器不用向客户端返回什么信息的情况。不会刷新页面。
         206 Partial Content:服务器已经完成了部分GET请求(客户端进行了范围请求)。响应报文中包含Content-Range指定范围的实体内容
 -->
    <!-- 3xx 重定向 -->
    <!-- 301 Moved Permanently:永久重定向,表示请求的资源已经永久的搬到了其他位置。
         302 Found:临时重定向,表示请求的资源临时搬到了其他位置
         303 See Other:临时重定向,应使用GET定向获取请求资源。303功能与302一样,区别只是303明确客户端应该使用GET访问
         307 Temporary Redirect:临时重定向,和302有着相同含义。POST不会变成GET
         304 Not Modified:表示客户端发送附带条件的请求(GET方法请求报文中的IF…)时,条件不满足。返回304时,不包含任何响应主体。虽然304被划分在3XX,但和重定向一毛钱关系都没有
 -->
    <!-- 4xx:客户端错误 -->
    <!-- 400 Bad Request:客户端请求有语法错误,服务器无法理解。
         401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用。
         403 Forbidden:服务器收到请求,但是拒绝提供服务
         404 Not Found:请求资源不存在。比如,输入了错误的url
         415 Unsupported media type:不支持的媒体类型
 -->
    <!-- 5xx:服务器端错误,服务器未能实现合法的请求。 -->
    <!-- 500 Internal Server Error:服务器发生不可预期的错误。
         503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常,
 -->
复制代码

66、说一下常见的git操作

git branch 查看本地所有分支
git status 查看当前状态 
git commit 提交 
git branch -a 查看所有的分支
git branch -r 查看远程所有分支
git commit -am "init" 提交并且加注释 
git remote add origin git@192.168.1.119:ndshow
git push origin master 将文件给推到服务器上 
git remote show origin 显示远程库origin里的资源 
git push origin master:develop
git push origin master:hb-dev 将本地库与服务器上的库进行关联 
git checkout --track origin/dev 切换到远程dev分支
git branch -D master develop 删除本地库develop
git checkout -b dev 建立一个新的本地分支dev
git merge origin/dev 将分支dev与当前分支进行合并
git checkout dev 切换到本地dev分支
git remote show 查看远程库
git add .
git rm 文件名(包括路径) 从git中删除指定文件
git clone git://github.com/schacon/grit.git 从服务器上将代码给拉下来
git config --list 看所有用户
git ls-files 看已经被提交的
git rm [file name] 删除一个文件
git commit -a 提交当前repos的所有的改变
git add [file name] 添加一个文件到git index
git commit -v 当你用-v参数的时候可以看commit的差异
git commit -m "This is the message describing the commit" 添加commit信息
git commit -a -a是代表add,把所有的change加到git index里然后再commit
git commit -a -v 一般提交命令
git log 看你commit的日志
git diff 查看尚未暂存的更新
git rm a.a 移除文件(从暂存区和工作区中删除)
git rm --cached a.a 移除文件(只从暂存区中删除)
git commit -m "remove" 移除文件(从Git中删除)
git rm -f a.a 强行移除修改后文件(从暂存区和工作区中删除)
git diff --cached 或 $ git diff --staged 查看尚未提交的更新
git stash push 将文件给push到一个临时空间中
git stash pop 将文件从临时空间pop下来

//  我是cv的自取吧

限于篇幅,有些前端面试题没有提供答案,如果有答案的可以评论区贴出来共勉。

本篇标志本系列完结,这些都是2022年开年以来前端工程师在实际找工作面试或者笔试遇到的技术问题,也是用人单位最看重的你必须过硬的吃饭的家伙,大家务必牢牢掌握,争取早日拿到offer。

如有新的题目也欢迎留言分享,让更多的人看见。

第一篇:2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(一)

第二篇:2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(二)

致敬作者:villay
链接:juejin.cn/post/7073869980411887652
来源:稀土掘金

2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(二)

本文是2022年来前端开发人员自求职过程中最新面试题系列的第二篇,主要包括Vuex、Loader和Plugin、浏览器浏览URL时的工作原理、UDP-TCP、Vue生命周期和钩子以及父子组件、项目性能优化方式、watch、组件之间值传递、布局有关的水平垂直、浏览器性能监控、diff、防抖和节流、虚拟列表、内存泄漏、常用检测数据类型的方式、三个切割函数slice splice split的区别、数组相关的数组转换和去重、JSON。

下面开始。

14、Vuex有哪些基本属性?为什么 Vuex 的 mutation 中不能做异步操作?

有五种,分别是 State、 Getter、Mutation 、Action、 Module
1、state => 基本数据(数据源存放地)
2、getters => 从基本数据派生出来的数据
3、mutations => 提交更改数据的方法,同步
4、actions => 像一个装饰器,包裹mutations,使之可以异步。
5、modules => 模块化Vuex

1、Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
2、每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
复制代码

15、Loader和Plugin 有什么区别

Loader:直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到`loader`。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。 Plugin:直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

16、在地址栏里输入一个地址回车会发生哪些事情

1、解析URL:首先会对 URL 进行解析,分析所需要使用的传输协议和请求的资源的路径。如果输入的 URL 中的协议或者主机名不合法,将会把地址栏中输入的内容传递给搜索引擎。如果没有问题,浏览器会检查 URL 中是否出现了非法字符,如果存在非法字符,则对非法字符进行转义后再进行下一过程。
2、缓存判断:浏览器会判断所请求的资源是否在缓存里,如果请求的资源在缓存里并且没有失效,那么就直接使用,否则向服务器发起新的请求。
3、DNS解析: 下一步首先需要获取的是输入的 URL 中的域名的 IP 地址,首先会判断本地是否有该域名的 IP 地址的缓存,如果有则使用,如果没有则向本地 DNS 服务器发起请求。本地 DNS 服务器也会先检查是否存在缓存,如果没有就会先向根域名服务器发起请求,获得负责的顶级域名服务器的地址后,再向顶级域名服务器请求,然后获得负责的权威域名服务器的地址后,再向权威域名服务器发起请求,最终获得域名的 IP 地址后,本地 DNS 服务器再将这个 IP 地址返回给请求的用户。用户向本地 DNS 服务器发起请求属于递归请求,本地 DNS 服务器向各级域名服务器发起请求属于迭代请求。
4、获取MAC地址: 当浏览器得到 IP 地址后,数据传输还需要知道目的主机 MAC 地址,因为应用层下发数据给传输层,TCP 协议会指定源端口号和目的端口号,然后下发给网络层。网络层会将本机地址作为源地址,获取的 IP 地址作为目的地址。然后将下发给数据链路层,数据链路层的发送需要加入通信双方的 MAC 地址,本机的 MAC 地址作为源 MAC 地址,目的 MAC 地址需要分情况处理。通过将 IP 地址与本机的子网掩码相与,可以判断是否与请求主机在同一个子网里,如果在同一个子网里,可以使用 APR 协议获取到目的主机的 MAC 地址,如果不在一个子网里,那么请求应该转发给网关,由它代为转发,此时同样可以通过 ARP 协议来获取网关的 MAC 地址,此时目的主机的 MAC 地址应该为网关的地址。
5、TCP三次握手: 下面是 TCP 建立连接的三次握手的过程,首先客户端向服务器发送一个 SYN 连接请求报文段和一个随机序号,服务端接收到请求后向客户端发送一个 SYN ACK报文段,确认连接请求,并且也向客户端发送一个随机序号。客户端接收服务器的确认应答后,进入连接建立的状态,同时向服务器也发送一个ACK 确认报文段,服务器端接收到确认后,也进入连接建立状态,此时双方的连接就建立起来了。
6、HTTPS握手: 如果使用的是 HTTPS 协议,在通信前还存在 TLS 的一个四次握手的过程。首先由客户端向服务器端发送使用的协议的版本号、一个随机数和可以使用的加密方法。服务器端收到后,确认加密的方法,也向客户端发送一个随机数和自己的数字证书。客户端收到后,首先检查数字证书是否有效,如果有效,则再生成一个随机数,并使用证书中的公钥对随机数加密,然后发送给服务器端,并且还会提供一个前面所有内容的 hash 值供服务器端检验。服务器端接收后,使用自己的私钥对数据解密,同时向客户端发送一个前面所有内容的 hash 值供客户端检验。这个时候双方都有了三个随机数,按照之前所约定的加密方法,使用这三个随机数生成一把秘钥,以后双方通信前,就使用这个秘钥对数据进行加密后再传输。
7、返回数据: 当页面请求发送到服务器端后,服务器端会返回一个 html 文件作为响应,浏览器接收到响应后,开始对 html 文件进行解析,开始页面的渲染过程。
8、页面渲染: 浏览器首先会根据 html 文件构建 DOM 树,根据解析到的 css 文件构建 CSSOM 树,如果遇到 script 标签,则判端是否含有 defer 或者 async 属性,要不然 script 的加载和执行会造成页面的渲染的阻塞。当 DOM 树和 CSSOM 树建立好后,根据它们来构建渲染树。渲染树构建好后,会根据渲染树来进行布局。布局完成后,最后使用浏览器的 UI 接口对页面进行绘制。这个时候整个页面就显示出来了。
9、TCP四次挥手: 最后一步是 TCP 断开连接的四次挥手过程。若客户端认为数据发送完成,则它需要向服务端发送连接释放请求。服务端收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明客户端到服务端的连接已经释放,不再接收客户端发的数据了。但是因为 TCP 连接是双向的,所以服务端仍旧可以发送数据给客户端。服务端如果此时还有没发完的数据会继续发送,完毕后会向客户端发送连接释放请求,然后服务端便进入 LAST-ACK 状态。客户端收到释放请求后,向服务端发送确认应答,此时客户端进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有服务端的重发请求的话,就进入 CLOSED 状态。当服务端收到确认应答后,也便进入 CLOSED 状态。
复制代码

17、UDP和TCP有什么区别

TCP和UDP的区别.png

18、项目中常用的性能优化方式有哪些?

太多了,自己整理吧

19、怎么解决跨域问题的,你是怎么配置的

20、计算属性和watch有什么区别?以及它们的运用场景?

// 区别
  computed 计算属性:依赖其它属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值时才会重新计算computed的值。
  watch 侦听器:更多的是观察的作用,无缓存性,类似与某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作
//运用场景
  当需要进行数值计算,并且依赖与其它数据时,应该使用computed,因为可以利用computed的缓存属性,避免每次获取值时都要重新计算。
  当需要在数据变化时执行异步或开销较大的操作时,应该使用watch,使用watch选项允许执行异步操作(访问一个API),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
复制代码

21、Vue的生命周期是什么 每个钩子里面具体做了什么事情

Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。
1、beforeCreate(创建前) :数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
2、created(创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 `$el` 属性。
3、beforeMount(挂载前) :在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
4、mounted(挂载后) :在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
5、beforeUpdate(更新前) :响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
6、updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7、beforeDestroy(销毁前) :实例销毁之前调用。这一步,实例仍然完全可用,`this` 仍能获取到实例。
8、destroyed(销毁后) :实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
另外还有 `keep-alive` 独有的生命周期,分别为 `activated` 和 `deactivated` 。用 `keep-alive` 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 `deactivated` 钩子函数,命中缓存渲染后会执行 `activated` 钩子函数。
复制代码

22、组件之间的传值有几种方式

1、父传子
2、子传父
3、eventbus
4、ref/$refs
5、$parent/$children
6、$attrs/$listeners
7、依赖注入(provide/inject)
复制代码

23、Eventbus具体是怎么实现的

24、父组件到子组件更新的方式是什么样的

25、$nexttick 是干嘛的,你一般拿它做什么

26、Keepalive 是什么,里面有哪些钩子

27、插槽是什么 怎么使用的

28、Es6常见的语法你知道哪一些

29、自定义指令你是怎么用的

30、重绘和重排

31、常见的水平垂直方式有几种?

//利用绝对定位,先将元素的左上角通过 top:50%和 left:50%定位到页面的中心,然后再通过 translate 来调整元素的中心点到页面的中心。该方法需要考虑浏览器兼容问题。
.parent {
    position: relative;
}

.child {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
//利用绝对定位,设置四个方向的值都为 0,并将 margin 设置为 auto,由于宽高固定,因此对应方向实现平分,可以实现水平和垂直方向上的居中。该方法适用于盒子有宽高的情况:
.parent {
    position: relative;
}

.child {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
}
//利用绝对定位,先将元素的左上角通过 top:50%和 left:50%定位到页面的中心,然后再通过 margin 负值来调整元素的中心点到页面的中心。该方法适用于盒子宽高已知的情况
.parent {
    position: relative;
}

.child {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -50px;     /* 自身 height 的一半 */
    margin-left: -50px;    /* 自身 width 的一半 */
}
//使用 flex 布局,通过 align-items:center 和 justify-content:center 设置容器的垂直和水平方向上为居中对齐,然后它的子元素也可以实现垂直和水平的居中。该方法要**考虑兼容的问题**,该方法在移动端用的较多:
.parent {
    display: flex;
    justify-content:center;
    align-items:center;
}
//另外,如果父元素设置了flex布局,只需要给子元素加上`margin:auto;`就可以实现垂直居中布局
.parent{
    display:flex;
}
.child{
    margin: auto;
}
复制代码

32、标准盒模型和怪异盒模型

33、Flex常见的属性  flex:1代表什么

34、Rem你是怎么做适配的

35、媒体查询是什么

36、首屏性能优化你是怎么做的

37、怎么解决白屏问题

1、加loading
2、骨架屏

38、浏览器的性能监控你是怎么做的

戳右边链接blog.csdn.net/qq_29438877…

39、Diff算法是什么  :key = index 为什么不常用数组的下标作为index  加了它有什么好处

40、虚拟列表你是怎么实现的

41、说一下防抖和节流

42、哪些情况会导致内存泄漏

1、意外的全局变量:由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收
2、被遗忘的计时器或回调函数:设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
3、脱离 DOM 的引用:获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
4、闭包:不合理的使用闭包,从而导致某些变量一直被留在内存当中。
复制代码

43、Vue的父子组件生命周期钩子函数执行顺序?

<!-- 加载渲染过程 -->
    <!-- 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created ->
    子beforeMount -> 子mounted -> 父mounted -->
    <!-- 子组件更新过程 -->
    <!-- 父beforeUpdate -> 子beforeUpdate -> 子updaed -> 父updated -->
    <!-- 父组件跟新过程 -->
    <!-- 父beforeUpdate -> 父updated -->
    <!-- 销毁过程 -->
    <!-- 父beforeDestroy -> 子beforeDestroy -> 子destroyed ->父destroyed -->
复制代码

44、说一下常见的检测数据类型的几种方式?

typeof  其中数组、对象、null都会被判断为Object,其他判断都正确

instanceof 只能判断引用数据类型,不能判断基本数据类型

constructor 它有2个作用 一是判断数据的类型,二是对象实例通过constructor对象访问它的构造函数。需要注意的事情是如果创建一个对象来改变它的原型,constructor就不能来判断数据类型了

Object.prototype.toString.call()
复制代码

45、说一下data为什么是一个函数而不是一个对象?

JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。而在Vue中,我们更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当我们每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。

46、说一下slice splice split 的区别?

// slice(start,[end])
// slice(start,[end])方法:该方法是对数组进行部分截取,该方法返回一个新数组
// 参数start是截取的开始数组索引,end参数等于你要取的最后一个字符的位置值加上1(可选)。
// 包含了源函数从start到 end 所指定的元素,但是不包括end元素,比如a.slice(0,3);
// 如果出现负数就把负数与长度相加后再划分。
// slice中的负数的绝对值若大于数组长度就会显示所有数组
// 若参数只有一个,并且参数大于length,则为空。
// 如果结束位置小于起始位置,则返回空数组
// 返回的个数是end-start的个数
// 不会改变原数组
var arr = [1,2,3,4,5,6]
/*console.log(arr.slice(3))//[4,5,6] 从下标为0的到3,截取3之后的数
console.log(arr.slice(0,3))//[1,2,3] 从下标为0的地方截取到下标为3之前的数
console.log(arr.slice(0,-2))//[1,2,3,4]
console.log(arr.slice(-4,4))//[3,4]
console.log(arr.slice(-7))//[1,2,3,4,5,6]
console.log(arr.slice(-3,-3))// []
console.log(arr.slice(8))//[]*/
// 个人总结:slice的参数如果是正数就从左往右数,如果是负数的话就从右往左边数,
// 截取的数组与数的方向一致,如果是2个参数则截取的是数的交集,没有交集则返回空数组 
// ps:slice也可以切割字符串,用法和数组一样,但要注意空格也算字符

// splice(start,deletecount,item)
// start:起始位置
// deletecount:删除位数
// item:替换的item
// 返回值为被删除的字符串
// 如果有额外的参数,那么item会插入到被移除元素的位置上。
// splice:移除,splice方法从array中移除一个或多个数组,并用新的item替换它们。
//举一个简单的例子 
var a=['a','b','c']; 
var b=a.splice(1,1,'e','f'); 
 console.log(a) //['a', 'e', 'f', 'c']
 console.log(b) //['b']

 var a = [1, 2, 3, 4, 5, 6];
//console.log("被删除的为:",a.splice(1, 1, 8, 9)); //被删除的为:2
// console.log("a数组元素:",a); //1,8,9,3,4,5,6

// console.log("被删除的为:", a.splice(0, 2)); //被删除的为:1,2
// console.log("a数组元素:", a) //3,4,5,6
console.log("被删除的为:", a.splice(1, 0, 2, 2)) //插入 第二个数为0,表示删除0个  
console.log("a数组元素:", a) //1,2,2,2,3,4,5,6

// split(字符串)
// string.split(separator,limit):split方法把这个string分割成片段来创建一个字符串数组。
// 可选参数limit可以限制被分割的片段数量。
// separator参数可以是一个字符串或一个正则表达式。
// 如果separator是一个空字符,会返回一个单字符的数组,不会改变原数组。
var a="0123456";  
var b=a.split("",3);  
console.log(b);//b=["0","1","2"]
// 注意:String.split() 执行的操作与 Array.join 执行的操作是相反的。
复制代码

47、说一下怎么把类数组转换为数组?

//通过call调用数组的slice方法来实现转换
Array.prototype.slice.call(arrayLike)

//通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)

//通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)

//通过Array.from方法来实现转换
Array.from(arrayLike)
复制代码

48、说一下数组如何去重,你有几种方法?

let arr = [1,1,"1","1",true,true,"true",{},{},"{}",null,null,undefined,undefined]

// 方法1
let uniqueOne = Array.from(new Set(arr)) console.log(uniqueOne)

// 方法2
let uniqueTwo = arr => {
    let map = new Map(); //或者用空对象 let obj = {} 利用对象属性不能重复得特性
    let brr = []
    arr.forEach( item => {
        if(!map.has(item)) { //如果是对象得话就判断 !obj[item]
            map.set(item,true) //如果是对象得话就obj[item] =true 其他一样
            brr.push(item)
        }
    })
    return brr
}
console.log(uniqueTwo(arr))

//方法3
let uniqueThree = arr => {
    let brr = []
    arr.forEach(item => {
        // 使用indexOf 返回数组是否包含某个值 没有就返回-1 有就返回下标
        if(brr.indexOf(item) === -1) brr.push(item)
        // 或者使用includes 返回数组是否包含某个值 没有就返回false 有就返回true
        if(!brr.includes(item)) brr.push(item)
    })
    return brr
}
console.log(uniqueThree(arr))

//方法4
let uniqueFour = arr => {                                         
     // 使用 filter 返回符合条件的集合
    let brr = arr.filter((item,index) => {
        return arr.indexOf(item) === index
    })
    return brr
}
console.log(uniqueFour(arr))
复制代码

49、说一下怎么取出数组最多的一项?

// 我这里只是一个示例
const d = {};
let ary = ['赵', '钱', '孙', '孙', '李', '周', '李', '周', '周', '李'];
ary.forEach(k => !d[k] ? d[k] = 1 : d[k]++);
const result = Object.keys(d).sort((a, b) => d[b] - d[a]).filter((k, i, l) => d[k] === d[l[0]]);
console.log(result)
复制代码

50、说一下JSON.stringify有什么缺点?

1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;

好,本篇就到这里,有些题目没有给出答案,能力有限或者太多亦或许我相信大家可以自己百度解决,但列出就是重点,务必要搞懂,没准面试就会有。

第一篇:2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(一)

第三篇:2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(三)

致敬作者:villay
链接:juejin.cn/post/7073869980411887652
来源:稀土掘金

2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(一)

求职找工作的金三银四已经过去了快一半,收疫情和整个大房间的影响,目前工作的竞争压力非常大。尤其对于人数众多的初级工程师来说,竞争尤为激烈,这里总结了2022年以来来自一线的各位求职者遇到的实际的前端面试和笔试题目,而且附带案例和代码,希望有助于大家查缺补漏,明确方向,提高自身竞争力,让自己脱颖而出,顺利拿到offer。

因为内容比较多,而且会不定期更新追加,所以暂时会分为三篇,本文是第一篇,主要包含,vue中的双向数据绑定原理、语法糖、哈希、浅拷贝和深拷贝、以及常问常新的原型和原型链、闭包、Promise、New、Set\Map、localStorage,sessionStorage,cookies区别、Vuex等,废话不多说,直接上题目和答案。

1、什么是原型什么是原型链?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
<script>
    function Person () {

    }
    var person  = new Person();
    person.name = 'Kevin';
    console.log(person.name) // Kevin

    // prototype
    function Person () {

    }
    Person.prototype.name = 'Kevin';
    var person1 = new Person();
    var person2 = new Person();
    console.log(person1.name)// Kevin
    console.log(person2.name)// Kevin

    // __proto__
    function Person () {

    }
    var person = new Person();
    console.log(person.__proto__ === Person.prototype) // true

    //constructor
    function Person() {

    }
    console.log(Person === Person.prototype.constructor) // true

    //综上所述
    function Person () {

    }
    var person = new Person()
    console.log(person.__proto__ == Person.prototype) // true
    console.log(Person.prototype.constructor == Person) // true
    //顺便学习一下ES5得方法,可以获得对象得原型
    console.log(Object.getPrototypeOf(person) === Person.prototype) // true

    //实例与原型
    function Person () {

    }
    Person.prototype.name = 'Kevin';
    var person = new Person();
    person.name = 'Daisy';
    console.log(person.name) // Daisy
    delete person.name;
    console.log(person.name) // Kevin

    //原型得原型
    var obj = new Object();
    obj.name = 'Kevin',
    console.log(obj.name) //Kevin

     //原型链
     console.log(Object.prototype.__proto__ === null) //true
     // null 表示"没用对象" 即该处不应该有值

     // 补充
     function Person() {

     }
     var person = new Person()
     console.log(person.constructor === Person) // true
     //当获取person.constructor时,其实person中并没有constructor属性,当不能读取到constructor属性时,会从person的原型
     //也就是Person.prototype中读取时,正好原型中有该属性,所以
     person.constructor === Person.prototype.constructor

     //__proto__
     //其次是__proto__,绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于Person.prototype中,实际上,它
     // 是来自与Object.prototype,与其说是一个属性,不如说是一个getter/setter,当使用obj.__proto__时,可以理解成返回了
     // Object.getPrototypeOf(obj)
     总结:
     
     1、当一个对象查找属性和方法时会从自身查找,如果查找不到则会通过__proto__指向被实例化的构造函数的prototype

     2、隐式原型也是一个对象,是指向我们构造函数的原型

     3、除了最顶层的Object对象没有__proto_,其他所有的对象都有__proto__,这是隐式原型

     4、隐式原型__proto__的作用是让对象通过它来一直往上查找属性或方法,直到找到最顶层的Object的__proto__属性,它的值是null,这个查找的过程就是原型链



</script>
</html>
复制代码

2、箭头函数和普通函数有什么区别?

(1)箭头函数比普通函数更加简洁
    如果没有参数,就直接写一个空括号即可
    如果只有一个参数,可以省去参数括号
    如果有多个参数,用逗号分割
    如果函数体的返回值只有一句,可以省略大括号
    如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常用的就是调用一个函数:
    let fn = () => void doesNotReturn()
    
 (2) 箭头函数没有自己的this
 箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中的this的指向在它在定义时一家确定了,之后不会改变。
 
(3)箭头函数继承来的this指向永远不会改变

 (4) call()、apply()、bind()等方法不能改变箭头函数中的this指向 
 
 (5) 箭头函数不能作为构造函数使用
 
 (6) 箭头函数没有自己的arguments
 
 (7) 箭头函数没有prototype
 
 (8) 箭头函数不能用作Generator函数,不能使用yeild关键字
复制代码

3、New操作符做了什么事情?

1、首先创建了一个新对象
2、设置原型,将对象的原型设置为函数的prototype对象
3、让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
4、判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象

4、vue实现双向数据绑定原理是什么?

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script src="https://cdn.bootcss.com/vue/2.5.16/vue.js"></script>
    <!-- 引入vue文件 -->
    <div id="box">
      <new-input v-bind:name.sync="name"></new-input>
      {{name}}
      <!-- 小胡子语法 -->
      <input type="text" v-model="name" />
    </div>
    <script>
      Vue.component("new-input", {
        props: ["name"],
        data: function () {
          return {
            newName: this.name,
          };
        },
        template: `<label><input type="text" @keyup="changgeName"
        v-model="newName" /> 你的名字:</label>`,
        // 模板字符串
        methods: {
          changgeName: function () {
            this.$emit("update:name", this.newName);
          },
        },
        watch: {
          name: function (v) {
            this.newName = v;
          },
        },
        //    监听
      });

      new Vue({
        el: "#box",
        //挂载实例
        data: {
          name: "nick",
        },
        //赋初始值
      });
    </script>
  </body>
</html>

复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="text" v-mode="msg" />
    <p v-mode="msg"></p>
    <script>
      const data = {
        msg: "你好",
      };
      const input = document.querySelector("input");
      const p = document.querySelector("p");
      input.value = data.msg;
      p.innerHTML = data.msg;
      //视图变数据跟着变
      input.addEventListener("input", function () {
        data.msg = input.value;
      });
      //数据变视图变
      let temp = data.msg;
      Object.defineProperty(data, "msg", {
        get() {
          return temp;
        },
        set(value) {
          temp = value;
          //视图修改
          input.value = temp;
          p.innerHTML = temp;
        },
      });
      data.msg = "小李";
    </script>
  </body>
</html>

5、v-model语法糖是怎么实现的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- v-model 只是语法糖而已 -->
    <!-- v-model 在内部为不同的输入元素使用不同的property并抛出不同的事件 -->
    <!-- text和textarea 元素使用value property 和 input事件 -->
    <!-- checkbox 和radio使用checked  property 和 change事件-->
    <!-- select 字段将value 作为prop 并将change 作为事件 -->
    <!-- 注意:对于需要使用输入法(如中文、日文、韩文等)的语言,你将会发现v-model不会再输入法
    组合文字过程中得到更新 -->
    <!-- 再普通标签上 -->
    <input v-model="sth" />  //这一行等于下一行
    <input v-bind:value="sth" v-on:input="sth = $event.target.value" />
    <!-- 再组件上 -->
    <currency-input v-model="price"></currentcy-input>
        <!--上行代码是下行的语法糖
         <currency-input :value="price" @input="price = arguments[0]"></currency-input>
        --> 
        <!-- 子组件定义 -->
        Vue.component('currency-input', {
         template: `
          <span>
           <input
            ref="input"
            :value="value"
            @input="$emit('input', $event.target.value)"
           >
          </span>
         `,
         props: ['value'],
        })   
</body>
</html>

6、什么是闭包,闭包的作用是什么

当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数。
闭包作用:
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
复制代码

7、Promise是什么?

Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
  // 保存初始化状态
  var self = this;

  // 初始化状态
  this.state = PENDING;

  // 用于保存 resolve 或者 rejected 传入的值
  this.value = null;

  // 用于保存 resolve 的回调函数
  this.resolvedCallbacks = [];

  // 用于保存 reject 的回调函数
  this.rejectedCallbacks = [];

  // 状态转变为 resolved 方法
  function resolve(value) {
    // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
    if (value instanceof MyPromise) {
      return value.then(resolve, reject);
    }

    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变,
      if (self.state === PENDING) {
        // 修改状态
        self.state = RESOLVED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.resolvedCallbacks.forEach(callback => {
          callback(value);
        });
      }
    }, 0);
  }

  // 状态转变为 rejected 方法
  function reject(value) {
    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变
      if (self.state === PENDING) {
        // 修改状态
        self.state = REJECTED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.rejectedCallbacks.forEach(callback => {
          callback(value);
        });
      }
    }, 0);
  }

  // 将两个方法传入函数执行
  try {
    fn(resolve, reject);
  } catch (e) {
    // 遇到错误时,捕获错误,执行 reject 函数
    reject(e);
  }
}

MyPromise.prototype.then = function(onResolved, onRejected) {
  // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
  onResolved =
    typeof onResolved === "function"
      ? onResolved
      : function(value) {
          return value;
        };

  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : function(error) {
          throw error;
        };

  // 如果是等待状态,则将函数加入对应列表中
  if (this.state === PENDING) {
    this.resolvedCallbacks.push(onResolved);
    this.rejectedCallbacks.push(onRejected);
  }

  // 如果状态已经凝固,则直接执行对应状态的函数

  if (this.state === RESOLVED) {
    onResolved(this.value);
  }

  if (this.state === REJECTED) {
    onRejected(this.value);
  }
};
复制代码

8、Set 和 Map有什么区别?

1、Map是键值对,Set是值得集合,当然键和值可以是任何得值
2、Map可以通过get方法获取值,而set不能因为它只有值
3、都能通过迭代器进行for...of 遍历
4、Set的值是唯一的可以做数组去重,而Map由于没有格式限制,可以做数据存储
复制代码

9、map和foreach有什么区别

foreach()方法会针对每一个元素执行提供得函数,该方法没有返回值,是否会改变原数组取决与数组元素的类型是基本类型还是引用类型
map()方法不会改变原数组的值,返回一个新数组,新数组中的值为原数组调用函数处理之后的值:
复制代码

10、localStorage  sessionStorage  cookies 有什么区别?

localStorage:以键值对的方式存储 储存时间没有限制 永不生效 除非自己删除记录
sessionStorage:当页面关闭后被清理与其他相比不能同源窗口共享 是会话级别的存储方式
cookies 数据不能超过4k 同时因为每次http请求都会携带cookie 所有cookie只适合保存很小的数据 如会话标识
复制代码

11、Vuex有哪些基本属性?为什么 Vuex 的 mutation 中不能做异步操作?

有五种,分别是 State、 Getter、Mutation 、Action、 Module
1、state => 基本数据(数据源存放地)
2、getters => 从基本数据派生出来的数据
3、mutations => 提交更改数据的方法,同步
4、actions => 像一个装饰器,包裹mutations,使之可以异步。
5、modules => 模块化Vuex

1、Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
2、每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
复制代码

12、Loader和Plugin 有什么区别

Loader:直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到`loader`。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。 Plugin:直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

其他的下篇更新,收藏网址鸭。

第二篇:2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(二)

第三篇:2022年最新前端面试题,来自这几个月找工作面试和笔试前线,附问题和答案代码,初级前端工程师求职必备(三)

中美程序员对比有什么差别?谁加班多,谁福利好?谁厉害,谁的工资高?

程序开发语言基本都是国外发明的,但是我们在互联网应用上比国外要热闹一点。作为一个新兴的领域,程序员在国内外都是一个很普遍的职业,那我们和国外,尤其是中美程序员对比,有什么区别呢?

下面来自一位横跨太平洋工作的资深程序员的亲身经历分享,作为大家茶余饭后的谈资乐呵乐呵一下。

1.发量和年龄 

我国公司:

  • 我国就很明显了,程序员几乎成为了青春饭。员工平均年龄在30岁以下。35岁焦虑弥漫整个职场,虽然目前收到重视,但情况仍未好转。那么年纪大程序员的都去哪里了呢?有人转岗、有人转行、有人继续在开发岗位,极少数在管理层。希望社会对35的中年人宽容点吧
代表建议放开35岁限制,程序员能摆脱35岁困境吗?

美国公司:

  • 同事里20多到70多岁的都有,众数是三四十的中年人,大部分工作目标都是为了早日退休,攒够钱就随时办退休party。也有些纯粹因为热爱工作、热爱写代码选择不退休的。
  • 我们组的核心成员之一,是位72岁的老头,他每天4点多起床到公司写一会儿代码,等天全亮就戴上头盔去骑山地车锻炼,9点多回公司继续工作。对这老头印象深刻,是因为他逻辑清晰、思路锐利,他是code review小组的成员,经常在邮件里破口大骂其他人写的代码写得有多烂,被投诉,只好在邮件里道歉,过几天继续骂,在我工作的两年里一直循环。
  • 我的另一位资深同事,是位68岁的架构师,热爱工作,每天都乐呵呵的,对我这种新毕业生也很友好,有人问他什么时候退休,他回答说他死的那天。

还有一个被大家津津乐道的话题就是程序员的发量,据说技术行不行,就看头发多还是少?哈哈

程序员工作年龄增加,技术越来越好,发量越来越少
程序员发量逐渐减少

 2. 加班

美国公司:

  • 从没加过班,晚上发版除外(会默认第二天调休)。
  • 经常正开着会,时间到了5点半,产品打断领导说到点了他要回去喂狗(他是一个50岁的不婚族,养了一院子狗),然后就散会下班了。
  • 加班需要申请,有次我申请工作日晚上加班,没批准只好回家了。因为加班费会比较高,需要从项目预算走,领导控制预算不给批。
  • 偶尔周末去办公室取东西,几层停车场只有两三辆车。

我国公司:

  • 996是常事了,据说还是福宝。
  • 印象比较深的是我司之前有个清华本科+美国硕士的小伙子,每天7点半准时下班,结果试用期被辞退了,原因是工作态度不积极,据说后来还和公司打了官司,不知输赢。
加班加到走火入魔

3. 代码质量谁的高 

我国公司:

  • 国内互联网节奏会要快得多,讲究小步快跑,就几天的开发时间,不管三七二十一先上线再说,刚开始我都惊呆了。讲究先有再好,只要能上就先上。

美国公司:

  • 项目在前期花的时间是最多的,比如说需求分析、架构讨论、技术讨论。
  • 写代码会考虑得比较长远,比较有时间去考虑开发原则、维护成本,领导也会乐意去安排版本来解决技术债务。

 4.工作环境和公司文化

美国公司:

  • 老美的公司确实比较尊重员工,在员工关怀上做得比较好。我可以感受到,和领导职位不同,但是我们人格是平等的,彼此尊重。
  • 记得有一次发版前几天,组里程序员说他压力太大,领导给他假期让他放松调整,版本被延迟上线。
  • 美国有family first的文化。有个老印同事,家里老人身体不好,公司同意他回印度工作照顾家人,远程跨国工作。经常有同事因为要看孩子比赛请假。领导自己也会偶尔周五请假,因为要去和女儿一起参加学校的公益活动。
  • 对差异性接受度也比较高。同事有变性人、残疾人,大家相处得都很好。
中美办公环境对比
中美办公环境对比-夸张了

我国公司:

  • 领导高高在上,官威很大。请个假,和求他借钱似的,组长还提醒我让我请假原因不要写“旅游”不然可能会不给批假。
  • 记得有个需求,大家都认为不合理没必要,我去找领导沟通,刚提了一句还没展开,领导直接甩脸色“你是领导还是我是领导”。
  • 有个同事因为耿直,和领导不和,被各种排挤冷暴力,逼他自己辞职拒给赔偿金。
  • 开个线上事故复盘会,做root cause分析,就像要把人钉在耻辱柱一样,我不理解这对解决问题有什么帮助。

其实硬件环境上中美已经差别不大,我们甚至还略胜,但是软件和文化等方面我们还有进步的空间。

如何判断程序员技术水平-看头发多少

 4.工资

美国:

如果汇率不换算的话,国外的软件工程师薪资在10万美元~30万美元不等,与国内的薪资在数字上相差不大(考虑消费水平)。

AI工程师,月薪60k~140k。

软件工程师,月薪69k~183k。

数据科学家,月薪43k~131k。

前端工程师,月薪82k~122k。

我国公司:

国内的薪资水平对应的大公司薪资又是如何的呢?根据拉勾网的岗位薪资数据(截至2020年9月11日),字节、滴滴、大疆以及商汤的薪酬(月薪)大致如下:

字节跳动:

基础架构工程师:30k~60k;

软件工程师(iOS、android):30k~60k;

数据科学家:30k~60k;

前端工程师:15~50k;

滴滴:

AI工程师(自动驾驶):30k~60k,15薪;

软件开发工程师:30k~60k;

数据科学家:20k~100k;

前端工程师:30k~70k;

大疆:

机器学习算法工程师(智能驾驶方向):25k~50k;

前端工程师:12k~40k,部分岗位15薪;

后台开发工程师:15k~25k;

信息安全专家:40k~70k,15薪;

商汤:

前端架构师:35k~60k,15薪;

高级系统开发工程师:30k~70k;

前端开发工程师:15k~35k,15薪;

后端开发工程师:20k~40k,15薪;

事实上,如果汇率不换算的话,国外的软件工程师薪资在10万美元~30万美元不等,与国内的薪资在数字上相差不大(考虑消费水平)

而国外机器学习工程师相关的年薪,则在11万~21万美元之间,数字上甚至会比国内的普遍更低。所以整体上工资体验相差不大,但分具体的工种。

以上的对比,仅仅是一个大牛个人经历的对比,基本上符合大家认知中的国内外区别,但我们也在不断改变,希望短板可以尽快补齐,终有一天我们会和外国程序员面对面坐着喝咖啡或者喝茶。

当前对每个人最重要的是开心工作、保持健康,保住头发、护住绿码。

2022前端趋势展望和2021年前端开发总结

前端开发在过去的一年经历了怎样的发展?2022年前端开发又将出现什么新的技术、应用和趋势?

本文综合各位前端大牛的总结,涵盖:基础框架/工程化、语言、行业发展趋势、底层发展,涉及大前端、微前端、DevOps、5G时代下的前端、浏览器、Vue、React、Web3.0、HTML6.0等前端技术开发热议话题。

1. 基础框架/工程化

随着 jQuery.js 渐渐淡出人们的视野,前端开发框架成为了开发人员必不可少的工具,也成为大家最为关注的东西。

mv* 框架

  • React(Next.js)
  • Vue(nuxt.js)
  • Svelte
  • Angular。

React 即将发布18版本,vue3 成为vue默认版本,Svelte异军突起。

打包工具

  • 传统:Webpack, Rollup, Parcel, Esbuild
  • ESM相关:Snowpack, Vite;ESM的实现:在开发环境编译时,使用 Server 动态编译 + 浏览器的 ESM,基本上实现了“开发环境 0 编译”的功能。而生产环境编译时,则会调用其他编译工具来完成(如 Vite 使用 Rollup)。

语法规范

  • Babel
  • Prettier
  • ESLint

CSS

  • Tailwind CSS(原子类)

web3D

  • Three.js
  • Oasis Engine

跨端

  • React Native
  • Flutter
  • Weex
  • uni-app
  • taro

桌面端

  • Tauri(Webview + Rust/.Net/Go)
  • electron(Chromium + Nodejs)

微前端

  • qiankun
  • single-spa
  • micro-app

E2E 测试

  • cypress(node服务,与程序一起运行)
  • puppeteer(无头浏览器)

shell

  • zx

以下是stateOfJs对2016-2021各个框架的趋势统计:

stateOfJs对2016-2021各个框架的趋势统计

2. 语言

编程语言发展趋势
编程语言排行版
腾讯开发语言排名-
腾讯2020研发大数据报告

_

TIOBE 2022年2月编程语言排行榜

阿特伍德定律:任何可以用 JavaScript 来写的应用,最终都将用 JavaScript 来写。

随着前端应用大型化、复杂化,TypeScript 肯定会越来越普及。未来,TypeScript 是否能得到浏览器和 Node.js 原生支持呢?我们一起期待吧。

前端的同学如果有想学习其他语言的,有如下推荐:

  • Rust 是 JS 基础设施的未来 – Lee Robinson
  • 全栈 —— Go
  • AI —— Python
  • Flutter —— Dart

3. 行业趋势

3.1 前端智能化

  • 低代码(LowCode)其实就是早期的搭建系统、组件平台等(宜搭、微搭),这个概念2014年被著名的研究机构Forrester提出。低代码平台的门槛在逐步降低,从专业的技术人员向业务人员进行转变,中国低代码行业比较分散。
低代码(LowCode)
  • 其中,OutSystems、Mendix、微软Salesforce、ServiceNow 被评为行业领导者。Appian、Oracle 和 Pega 被评为挑战者。Creatio、Kintone、Newgen 和 Quickbase 被评为利基(niche)市场参与者。今年没有厂商被评为远见者。Gartner 预测:“到 2023 年,超过 70% 的企业将采用低代码(LCAP)作为他们发展战略的关键目标之一”。到 2025 年,整体 LCAP(低代码开发平台)市场规模将达到 290 亿美元,年复合增长率超过 20%;其中,LCAP 的细分市场预计将在 2020——2025 年之间,从 44.5 亿美元增长至 143.8 亿美元,复合年增长率为 26.4%。
  • 代码自动生成Sketch2Code,AI 将手绘稿子 转换为 html 代码。imgcook,将Sketch/PSD/图片 转换为 React、Vue、Flutter、小程序等代码。

3.2 大前端(泛前端)

从切图仔、写 HTML 模板的“石器时代”,到前后端分离、大前端的“工业时代”,再到现在跨端技术、低代码的“电气时代”。前端研发的职责一直在改变,同时前端研发需要掌握的技术也在迭代更新。- 字节前端

Serverless

Serverless 是一种基于云计算的简化方式,基本可以理解为 FaaS(函数即服务)+ BaaS(后端即服务),在 BaaS 层进行存储与计算,在 FaaS 层提供云函数。

在 Serverless 的赋能之下,前端工程师能够将页面交互、业务逻辑、数据处理等全部掌控在自己的手中,实现了真正全栈的可能。

全栈

“全栈开发者”是指“同时掌握前端、后端以及其他网站开发相关技能的开发者”。

一个“全栈开发者”可能会使用以下技能点:

前端:JavaScript、H5、CSS3、sass、less、React、Vue、webpack、jest。

后端:Nodejs/Deno、Go、Java、Spring、Gin、Kafka、Hadoop。

数据库:MySQL、mongoDB、redis、clickhouse。

运维:网络协议、CDN、Nginx、ZooKeeper、Docker、Kubernetes。

值得注意的是,一个优秀的工程师并不是以“栈”数取胜,而取决于你解决了什么层次的问题。

DevOps

DevOps(Development 和 Operations 的组合词)是一种重视“软件开发人员(Dev)”和“IT 运维技术人员(OPS)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。在开发、测试、部署、运维等多个领域进行了共建。

与 Kubernetes 相结合:Kubernetes 是一种开源容器编排系统,容器技术的日益普及是 DevOps 出现的因素之一。使用 Kubernetes DevOps,软件开发人员和运维团队可以快速实时地相互交换大量的应用程序,大大提高了生产力。

微前端

微服务架构:微服务架构可以将一个应用分成若干个更小的服务,这让整个开发过程具有很高的敏捷性和可拓展性。

常用的微前端框架包括 qiankun、single-spa、micro-app:

微前端框架排名和发展趋势

微前端我觉得它其实没有太多的趋势。首先微前端就不是一个大家都要用的。微前端沾了微服务的光,但是微服务是所有后端基本上都要往架构上迁, 微前端很明显不是这样的。它更多的是单页应用并有多框架隔离的需求,然后做出微前端这样一个技术方案。我觉得说实话,微前端就不该这么热,包括很多学生都会问我微前端,我反问你有没有看过微前端解决什么样的问题?如果非要往这上边靠的话,就相当于没有困难创造困难也要上,举个例子,公司一共有四五个前端,就非要用微前端架构,四五个人都可以用不同框架,这其实是没必要的。- 程劭非(winter)❞

我比较赞同老师说的,我认为一切技术都为于解决某个问题,关键在于我们有没有找准那个关键的问题,是否在解决这个问题。

小程序

根据winter老师的看法,小程序只是一个前端的技术实现方案,并无大的难点和技术创新,更重要的是看商业模式上的考量。

阿拉丁《2021 年度小程序互联网发展白皮书》

上图截选自:阿拉丁《2021 年度小程序互联网发展白皮书》

解决小程序的跨平台开发问题可以采用框架转换:uni-app(Vue)、taro(React)。

5G时代

5G 时代到来,5G将与超高清视频、VR、AR、消费级云计算、智能家居、智慧城市、车联网、物联网、智能制造等产生深度融合,这些都将为前端技术的发展带来新的增长和机遇。WebGL、WebGPU等技术也将迎来一波发展的机会。

  • Web 3D3D 类的 H5 小游戏、在线看房、电子商务、在线教育等,对于技术而言这无疑是一片沃土。随着 5G 技术发展,视频加载速度会非常快,简单的实时渲染会被视频直接替代。复杂的可以通过服务器渲染,将画面传回网页中,只要传输够快,手机的性能就不再是问题。相关的一些库:Three.jsOasis EngineBabylon.jsPlayCanvas.js
  • WebRTC (Web Real-Time Communications)传统的技术包括:XMLHttpRequest,WebSocket,未来:WebRTC 会在点对点私密传输、娱乐领域,元宇宙领域,低延迟领域大放异彩。

4. 底层演进

前端历年大事件

2021 JavaScript大事件 ↓

「JavaScript」

8.24:TypeScript 新官网上线

12.4:JavaScript 26 岁了

「Node.js」

2.2:npm 7.0 正式可用

3.29:Deno 公司成立

4.21:Node.js 16 发布

7.20:Node-RED 2.0 发布,低代码编程工具

9.20:Node.js 发布 Corepack,用于管理npm、yarn、pnpm、cnpm

10.19:Node.js 17 发布

「Vue」

8.3:Vue.js 被选作维基百科的前端框架

8.5:Vue 3.2 发布

11.24:Pinia 正式成为 vuejs 的一员

「React」

5.28:React 18 alpha 发布

10.5:React 全新文档发布

11.27:Next.js 12 发布

12.14:Create React App 5.0 发布

「打包工具」

2021.1.6 Snowpack 3.0 发布

2021.2.17 Vite 2.0 发布

2021.10.13 Parcel v2 发布

「其他」

2021.3.6 jQuery 3.6.0 发布

2021.3.17 Chrome V8 9.0 发布

2021.10.7 jQuery Mobile 弃用

2021.11.4 Angular v13 发布

浏览器

Chrome 一家独大,IE 浏览器将于 2022 年 6 月 15 日正式停用。

谷歌浏览器几乎独霸移动浏览器市场

HTML6.0

支持原生模式、没有 JavaScript 的单页应用程序、自由调整图像大小、专用库、微格式、自定义菜单、增强身份验证、集成摄像头。

WebAssembly

WebAssembly 简称 Wasm,是一种可在 Web 中运行的全新语言格式,同时兼具体积小、性能高、可移植性强等特点,在底层上类似 Web 中的 JavaScript,同时也是 W3C 承认的 Web 中的第 4 门语言。

在前端的游戏、音乐、视频等领域大放异彩,目前很多桌面软件也纷纷通过编译成 Wasm 的形式搬进了浏览器中。

2022 年 Wasm 功能将会不断完善,同时也会有越来越多的传统 PC 软件推出 Web 版本。

2022 Wasm

开源

首次被列入十四五规划,2021年,中国企业积极构建开源平台。根据GitHub统计,中国开发者已成为全球最大规模的开发者群体。

元宇宙

游戏、VR/AR、区块链数字资产等等概念的整合。

web 3.0

web1.0: 单向信息,只读;web 2.0的标志:User Generated Content(用户生成内容,例如微博、Facebook);web3.0: 人和网络以及网络与人的沟通。

5. 总结

在工业4.0的大背景下,随着人工智能、云计算、大数据、物联网、区块链等互联网潮流技术的不断推进,互联网行业走向工业化和智能化。全球疫情的常态化,越来越多的公司选择或者不得不居家办公(WFH),必然给前端行业带来更多的机会。

2023 年底全球软件开发人员达到 2770万,中国将占6%至8%,前端预计30万左右,而JavaScript 在全球目前约有 1400 万开发者。

2005左右才出现前端的岗位,变化非常快,目前还是处在发展期(好事儿),只有把握底层变化,不断思辨和学习,才能把焦虑控制在一定范围内。

另外说到“卷”,还是上进心导致的,对吧?真想躺的话,能躺的办法还是挺多的。如果想要进前端的领域,从学习的角度来讲,我推荐 hard way。也就是说,我们看起来最难的那条路反而是最简单的,那些看起来简单的路,它有可能反而是更绕。你想往山顶上走,肯定选择最陡峭的,你想走下坡路,肯定选择最远的那条路。这个道理是是我想今天传达给大家的,原因还是在于看到太多的同学们想走捷径,或者说想走简单的路,结果反而越走越远,最后绕回来的话反而消耗更大。- 程劭非(winter)❞

最后,小厂前端团队押宝什么?

押技术落地,押Vue3.0、TS、Three.js,押年轻人!

希望此文有助于大家的学习和提升,以更强的自己面对技术的迭代和外部不断增加的不确定性。

共勉!

来自:作者:大清,掘金社区

JavaScript将新增两个原始数据类型Record 和 Tuple特性、作用和使用用法

JavaScript又有动作了——将推出两个新的数据类型:Record 和 Tuple 。

JavaScript将新增两个原始数据类型Record和Tuple

这俩是啥呢?其实就是一个只读的 Object 和 Array,其实在其它语言中已经有类似的数据类型了,例如 Python 中也有 Tuple(元祖)这一类型,作用也是一个只读的数组(在Python里叫只读的列表),一起来了解一下,这个特性是一个第2阶段提案(即差不多稳了),想要提前体验的,文末也有 polyfill 的使用教程!

两个新的数据类型:Record 和 Tuple的写法

// Records
const myRecord = #{
name: ’01’,
age: 23
}

// Tuple
const myTuple = #[‘1’, ‘2’, ‘3’]

不用仔细看就能发现,其实就是在原先的对象和数组前面加了个 #

特性:

1 可读性——只读,注意是只读

const myRecord = #{
name: ’01’
}

const myTuple = #[‘1’, ‘2’]

myRecord[‘age’] = 23 // error
myTuple.push(‘3’) // error

这两个例子充分验证这两个新增的数据类型是只读的,不可写入。

非唯一性

在平时的开发中,数组与数组、对象与对象 都不适合直接用 === 进行比较判断,因为每个生成的对象在内存中的地址都不一样

const obj1 = { name: ’01’ }
const obj2 = { name: ’01’ }
const objIsSame = obj1 === obj2   // false

const arr1 = [1]
const arr2 = [1]
const arrIsSame = arr1 === arr2   // false

要想真正比较两个数组或对象是否相等(即我们想要的内容都一样),需要遍历递归去一一对比,而现在呢?Record和Tuple能否解决这一问题呢?

const record1 = #{ name: ’01’ }
const record2 = #{ name: ’01’ }
const recordIsSame = record1 === record2   // true

const tuple1 = #[1]
const tuple2 = #[1]
const tupleIsSame = tuple1 === tuple2   // true

可以看到,只要内部内容一致,即使是两个分别生成的Record或Tuple比较一下,也是相等的

可以看到,只要内部内容一致,即使是两个分别生成的Record或Tuple比较一下,也是相等的

普通对象和数组的转换

转换成普通对象和数组

我可以用对象 Record 和 Tuple 将普通的对象和数组转换

const myRecord = Record({ name: ’01’, age: 23 });   // #{ name: ’01’, age: 23 }const myTuple = Tuple([1, 2, 3, 4, 5]);   // #[1, 2, 3, 4, 5]

支持扩展运算符

我们也可以对Record和Tuple使用扩展运算符

const myTuple = #[1, 2, 3];const myRecord = #{ name: '01', age: 23 };const newRecord = #{ ...myRecord, money: 0 } // #{ name: '01', age: 23, money: 0 }const newTuple = #[ ...myTuple, 4, 5];   // #[1, 2, 3, 4, 5]

JSON方法扩展

现在不是有 JSON.parse 和 JSON.stringfy 两个方法嘛,据说草案中还提到一个不错的想法,那就是给 JSON 对象新增一个 parseImmutable 方法,功能应该就是直接将一个 Record字符串或Tuple字符串 解析成对应的Record和Tuple对象

提前体验

如果你想现在体验该功能,可以装一下babel的插件

# babel基本的库
yarn add @babel/cli @babel/core @babel/preset-env -D

# Record和Tuple Babel polyfill
yarn add @babel/plugin-proposal-record-and-tuple @bloomberg/record-tuple-polyfill -D

在目录下创建 .babelrc,内容如下:

{
    “presets”: [“@babel/preset-env”],
    “plugins”: [
        [
          “@babel/plugin-proposal-record-and-tuple”,
          {
            “importPolyfill”: true,
            “syntaxType”: “hash”
          }
        ]
      ]
}

再创建一个 index.js,内容如下:

const tuple1 = #[1,2,3]
const tuple2 = #[1,2,3]

const record1 = #{ name: ’01’ }
const record2 = #{ name: ’02’ }

console.log(tuple1 === tuple2, record1 === record2)

执行一下babel的命令编译一下

./node_modules/.bin/babel index.js –out-file compiled.js

输出得到的 compiled.js 文件内容如下:

“use strict”;

var _recordTuplePolyfill = require(“@bloomberg/record-tuple-polyfill”);

var tuple1 = (0, _recordTuplePolyfill.Tuple)(1, 2, 3);
var tuple2 = (0, _recordTuplePolyfill.Tuple)(1, 2, 3);
var record1 = (0, _recordTuplePolyfill.Record)({
  name: ’01’
});
var record2 = (0, _recordTuplePolyfill.Record)({
  name: ’02’
});
console.log(tuple1 === tuple2, record1 === record2);

最后执行 compiled.js 即可获得结果

node compiled.js
# Result: true false

@babel/plugin-proposal-record-and-tuple 更多用法见 Babel 官方文档

https://babeljs.io/docs/en/babel-plugin-proposal-record-and-tuple#docsNav

新增的两个数据类型的应用场景

了解了那么多的内容,印象最深刻的应该就是 只读 这个特性,那么基于这个特性,Record 和 Tuple 有哪些应用场景呢?

  • 用于保护一些数据,比如函数的返回值、对象内部的静态属性…
  • 既然具有只读的特性,即不可变对象,那应该也可以作为对象的 key 值吧?

怎么样,对JS即将新增的两个数据类型有比较全面的认识了吗?

《浏览器基本原理与实践》36点总结,让我重新认识浏览器

前言

浏览器是每一名前端开发日常工作打交道最多(之一)的,或者说前端工作的内容大部分都是基于浏览器环境来的,因为最终成果需要在浏览器运行和验证。

浏览器工作原理

但对大部分前端开发来说,浏览器就是一个最熟悉的陌生人,经常用,却没几个人说得清其运行原理。不过浏览器确实也很复杂,想想到现在我们“国产替代”都失败了。比如几个简单的问题:

比如js是怎样运行的呢?

精美样式页面是怎样渲染到电脑屏幕的呢?

在开放的互联网它又是怎样保证我们个人信息安全的呢?

带着打破砂锅问到底的态度,开始肝李兵老师的《浏览器基本原理与实践》,下面是研读本书后的36点总结,希望有益于大家。

1. Chrome 架构:仅仅打开了 1 个页面,为什么有 4 个进程

线程和进程区别
多线程可以并行处理任务,线程不能单独存在,它是由进程来启动和管理的。一个进程是一个程序的运行实例。

线程和进程的关系
1、进程中任意一线程执行出错,都会导致整个进程的崩溃。
2、线程之间共享进程中的数据。
3、当一个进程关闭后,操作系统会回收进程所占用的内存。
4、进程之间的内容相互隔离。

单进程浏览器
1、不稳定。单进程中的插件、渲染线程崩溃导致整个浏览器崩溃。
2、不流畅。脚本(死循环)或插件会使浏览器卡顿。
3、不安全。插件和脚本可以获取到操作系统任意资源。

多进程浏览器
1、解决不稳定。进程相互隔离,一个页面或者插件崩溃时,影响仅仅时当前插件或者页面,不会影响到其他页面。
2、解决不流畅。脚本阻塞当前页面渲染进程,不会影响到其他页面。
3、解决不安全。采用多进程架构使用沙箱。沙箱看成时操作系统给进程上来一把锁,沙箱的程序可以运行,但是不能在硬盘上写入任何数据,也不能在敏感位置读取任何数据。

多进程架构
分为 浏览器进程、渲染进程、GPU 进程、网络进程、插件进程。

缺点
1、资源占用高。
2、体系架构复杂。

面向服务架构
把原来的各种模块重构成独立的服务,每个服务都可以在独立的进程中运行,访问服务必须使用定义好的接口,通过 IPC 通讯,使得系统更内聚、松耦合、易维护和拓展。

2. TCP 协议:如何保证页面文件能被完整送达浏览器

  • IP 头是 IP 数据包开头的信息,包含 IP 版本、源 IP 地址、目标 IP 地址、生存时间等信息;
  • UDP 头中除了目的端口,还有源端口号等信息;
  • IP 负责把数据包送达目的主机;
  • UDP 负责把数据包送达具体应用;
  • 对于错误的数据包,UDP 不提供重发机制,只是丢弃当前的包,不能保证数据的可靠性,但是传输速度非常块;
  • TCP 头除了包含了目标端口和本机端口号外,还提供了用于排序的序列号,保证了数据完整地传输,它的连接可分为三个阶段:建立连接、传输数据和断开连接;

3. HTTP 请求流程:为什么很多站点第二次打开速度会很快

  • 浏览器中的 HTTP 请求从发起到结束一共经历如下八个阶段:构建请求、查找缓存、准备 IP 和端口、等待 TCP 队列、建立 TCP 连接、发起 HTTP 请求、服务器处理请求、服务器返回请求和断开连接;
  • 构建请求。浏览器构建请求行,构建好后,准备发起网络请求;
  • 查找缓存。在真正发起请求前浏览器会查询缓存中是否有请求资源副本,有则拦截请求,返回资源副本,否则进入网络请求;
  • 准备 IP 地址和端口。HTTP 网络请求需要和服务器建立 TCP 连接,而建立 TCP 连接需要准备 IP 地址和端口号,浏览器需要请求 DNS 返回域名对应的 IP,同时会缓存域名解析结果,供下次查询使用;
  • 等待 TCP 队列。Chrome 机制,同一个域名同时最多只能建立 6 个 TCP 连接;
  • 建立 TCP 连接。TCP 通过“三次握手”建立连接,传输数据,“四次挥手”断开连接;
  • 发送 HTTP 请求。建立 TCP 连接后,浏览器就可以和服务器进行 HTTP 数据传输了,首先会向服务器发送请求行,然后以请求头形式发送一些其他信息,如果是 POST 请求还会发送请求体;
  • 服务器处理请求。首先服务器会返回响应行,随后,服务器向浏览器发送响应头和响应体。通常服务器返回数据,就要关闭 TCP 连接,如果请求头或者响应头有 Connection:keep-alive TCP 保持打开状态;

4. 导航流程:从输入 URL 到页面展示这中间发生了什么

  • 用户输入 URL 并回车
  • 浏览器进程检查 URL,组装协议,构成完整 URL
  • 浏览器进程通过进程通信(IPC)把 URL 请求发送给网络进程
  • 网络进程接收到 URL 请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程
  • 如果没有,网络进程向 web 服务器发起 http 请求(网络请求),请求流程如下:
    • 进行 DNS 解析,获取服务器 IP 地址,端口
    • 利用 IP 地址和服务器建立 tcp 连接
    • 构建请求头信息
    • 发送请求头信息
    • 服务器响应后,网络进程接收响应头和响应信息,并解析响应内容
  • 网络进程解析响应流程:
    • 检查状态码,如果是 301/302,则需要重定向,从 Location 自动读取地址,重新进行第 4 步,如果是 200,则继续处理请求
    • 200 响应处理:检查响应类型 Content-Type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行后续渲染。如果是 html 则通知浏览器进程准备渲染进程进行渲染
  • 准备渲染进程
    • 浏览器进程检查当前 URL 是否和之前打开的渲染进程根域名是否相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程
  • 传输数据、更新状态
    • 渲染进程准备好后,浏览器向渲染进程发起“提交文档”的消息,渲染进程接收到消息和网络进程建立传输数据的“管道”
    • 渲染进程接收完数据后,向浏览器发送“确认提交”
    • 浏览器进程接收到确认消息后 engine 浏览器界面状态:安全、地址 URL、前进后退的历史状态、更新 web 页面

5. 渲染流程(上):HTML、CSS 和 JavaScript 是如何变成页面的

  • 浏览器不能直接理解 HTML 数据,需要将其转化为 DOM 树结构;
  • 生成 DOM 树后,根据 CSS 样式表,计算出 DOM 树所有节点样式;
  • 创建布局树:遍历 DOM 树所有可见节点,把这些节点加到布局中,不可见节点忽略,如 head 标签下所有内容,display: none 元素;

6. 渲染流程(下):HTML、CSS 和 JavaScript 是如何变成页面的

  • 分层:层叠上下文属性的元素(比如定位属性元素、透明属性元素、CSS 滤镜属性元素)提升为单独的一层,需要裁剪的地方(比如出现滚动条)也会被创建为图层;
  • 图层绘制:完成图层树构建后,渲染引擎会对图层树每一层进行绘制,把一个图层拆分成小的绘制指令,再把指令按照顺序组成一个带绘制列表;
  • 有些情况图层很大,一次绘制所有图层内容,开销太大,合成线程会将图层划分为图块(256×256 或者 512×512);
  • 合成线程将图块提交给栅格线程进行栅格化,将图块转换为位图。栅格化过程都会使用 GPU 加速,生成的位图保存周期 GPU 内存中;
  • 一旦所有图块都被栅格化,合成线程会生成一个绘制图块命令(DrawQuad),然会将命令提交给浏览器进程,viz 组件接收到该指令,将页面内容绘制到内存中,显示在屏幕上;
  • 重排:通过 JavaScript 或者 CSS 修改元素几何位置属性,会触发重新布局,解析后面一系列子阶段;重绘:不引起布局变换,直接进入绘制及其以后子阶段;合成:跳过布局和绘制阶段,执行的后续操作,发生在合成线程,非主线程;

7. 变量提升:javascript 代码是按顺序执行的吗

  • JavaScript 代码在执行之前需要先编译,在编译阶段,变量和函数会被存放到变量环境中,变量默认值会被设置为 undefined;
  • 在代码执行阶段,JavaScript 引擎会从变量环境中查找自定义的变量和函数;
  • 如果在编译阶段,窜爱两个相同的函数,那么最终放在变量环境中的是最后定义的那个,后定义的覆盖先定义的;

8. 调用栈:为什么 JavaScript 代码会出现栈溢出

  • 每调用一个函数,JavaScript 引擎会为其创建执行上下文压入调用栈,然后,JavaScript 引擎开始执行函数代码。
  • 如果一个函数 A 调用另外一个函数 B,那么 JavaScript 引擎会为 B 函数创建执行上下文,并将 B 函数的执行上下文压入栈顶。
  • 当前函数执行完毕后,JavaScript 引擎会将该函数的执行上下文弹出栈。
  • 当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。

9. 块级作用域:var 缺陷以及为什么要引入 let 和 const

  • let、const 申明的变量不会被提升。在 javascript 引擎编译后,会保存在词法环境中。
  • 块级作用域在代码执行时,将 let、const 变量存放在词法环境的一个单独的区域。词法环境内部维护一个小型的栈结构,作用域内部变量压入栈顶。作用域执行完,从栈顶弹出。

10. 作用域链和闭包:代码中出现相同的变量,JavaScript 引擎如何选择

  • 使用一个变量,JavaScript 引擎会在当前的执行上下文中查找变量,如果没有找到,会继续在 outer(执行环境指向外部执行上下文的引用)所指向的执行上下文中查找;
  • JavaScript 执行过程,作用域链是由词法作用域决定,而词法作用域是由代码中函数声明的位置决定;
  • 根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使外部函数已经执行结束了,但是内部函数引用外部函数的变量依旧保存在内存中,把这些变量的集合称为闭包;

11. this:从 JavaScript 执行上下文视角讲 this

当执行 new CreateObj 的时候,JavaScript 引擎做了四件事:

  • 首先创建一个控对象 tempObj;
  • 接着调用 CreateObj.call 方法,并将 tempObj 作为 call 方法的参数,这样当 createObj 的执行上下文创建时,它的 this 就指向 tempObj 对象;
  • 然后执行 CreateObj 函数,此时的 CreateObj 函数执行上下文中的 this 指向 tempObj 对象;
  • 最后返回 tempObj 对象。

this 的使用分为:

  • 当函数最为对象的方法调用时,函数中的 this 就是该对象;
  • 当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;
  • 嵌套函数中的 this 不会继承外层函数的 this 值;
  • 箭头函数没有自己的执行上下文,this 是外层函数的 this。

12. 栈空间和堆空间:数据是如何存储的

动态语言:在使用时需要检查数据类型的语言。
弱类型语言:支持隐式转换的语言。

JavaScript 中的 8 种数据类型,它们可以分为两大类——原始类型和引用类型。
原始类型数据存放在栈中,引用类型数据存放在堆中。堆中的数据是通过引用与变量关系联系起来的。

从内存视角了解闭包:词法扫描内部函数,引用了外部函数变量,堆空间创建一个“closure”对象,保存变量。

13. 垃圾回收:垃圾数据如何自动回收

  • 栈中数据回收:执行状态指针 ESP 在执行栈中移动,移过某执行上下文,就会被销毁;
  • 堆中数据回收:V8 引擎采用标记-清除算法;
  • V8 把堆分为两个区域——新生代和老生代,分别使用副、主垃圾回收器;
  • 副垃圾回收器负责新生代垃圾回收,小对象(1 ~ 8M)会被分配到该区域处理;
  • 新生代采用 scavenge 算法处理:将新生代空间分为两半,一半空闲,一半存对象,对对象区域做标记,存活对象复制排列到空闲区域,没有内存碎片,完成后,清理对象区域,角色反转;
  • 新生代区域两次垃圾回收还存活的对象晋升至老生代区域;
  • 主垃圾回收器负责老生区垃圾回收,大对象,存活时间长;
  • 新生代区域采用标记-清除算法回收垃圾:从根元素开始,递归,可到达的元素活动元素,否则是垃圾数据;
  • 为了不造成卡顿,标记过程被切分为一个个子标记,交替进行。

14. 编译器和解析器:V8 如何执行一段 JavaScript 代码的

  • 计算机语言可以分为两种:编译型和解释型语言。编译型语言经过编译器编译后保留机器能读懂的二进制文件,比如 C/C++,go 语言。解释型语言是在程序运行时通过解释器对程序进行动态解释和执行,比如 Python,JavaScript 语言。
  • 编译型语言的编译过程:编译器首先将代码进行词法分析、语法分析,生成抽象语法树(AST),然后优化代码,最后生成处理器能够理解的机器码;
  • 解释型语言解释过程:解释器会对代码进行词法分析、语法分析,并生产抽象语法树(AST),不过它会再基于抽象语法树生成字节码,最后根据字节码执行程序;
  • AST 的生成:第一阶段是分词(词法分析),将一行行源码拆解成一个个 token(语法上不可再分、最小单个字符)。第二阶段是解析(语法分析),将上一步生成的 token 数据,根据语法规则转为 AST,这一阶段会检查语法错误;
  • 字节码存在的意义:直接将 AST 转化为机器码,执行效率是非常高,但是消耗大量内存,从而先转化为字节码解决内存问题;
  • 解释器 ignition 在解释执行字节码,同时会手机代码信息,发现某一部分代码是热点代码(HotSpot),编译器把热点的字节码转化为机器码,并保存起来,下次使用;
  • 字节码配合解释器和编译器的计数实现称为即时编译(JIT)。

15. 消息队列和事件循环:页面是怎么活起来的

  • 每个渲染进程都有一个主线程,主线程会处理 DOM,计算样式,处理布局,JavaScript 任务以及各种输入事件;
  • 维护一个消息队列,新任务(比如 IO 线程)添加到消息队列尾部,主线程循环地从消息队列头部读取任务,执行任务;
  • 解决处理优先级高的任务:消息队列的中的任务称为宏任务,每个宏任务中都会包含一个微任务队列,在执行宏任务的过程中,如果 DOM 有变化,将该变化添加到微任务队列中;
  • 解决单个任务执行时长过久:JavaScript 通过回调功能来规避。

16. webapi:setTimeout 是怎么实现的

  • JavaScript 调用 setTimeout 设置回调函数的时候,渲染进程会创建一个回调任务,延时执行队列存放定时器任务;
  • 当定时器任务到期,就会从延时队列中取出并执行;
  • 如果当前任务执行时间过久,会影响延时到期定时器任务的执行;
  • 如果 setTimeout 存在嵌套调用(5 次以上),判断该函数方法被阻塞,那么系统会设置最短时间间隔为 4 秒;
  • 未激活的页面,setTimeout 执行最小间隔是 1000 毫秒,目的是为了降低加载损耗;
  • 延时执行时间最大值是 24.8 天,因为延时值是以 32 个 bit 存储的;
  • setTimeout 设置的回调函数中的 this 指向全局 window。

17. webpai:XMLHttpRequest 是怎么实现的

  • XMLHttpRequest onreadystatechange 处理流程:未初始化 -> OPENED -> HEADERS_RECEIVED -> LOADING -> DONE;
  • 渲染进程会将请求发送给网络进程,然后网络进程负责资源下载,等网络进程接收到数据后,利用 IPC 通知渲染进程;
  • 渲染进程接收到消息之后,会将 xhr 回调函数封装成任务并添加到消息队列中,等主线程循环系统执行到该任务的时候,会根据相关状态来调用回调函数。

18. 宏任务和微任务:不是所有的任务都是一个待遇

  • 消息队列中的任务为宏任务。渲染进程内部会维护多个消息队列,比如延时执行队列和普通消息队列,主线程采用 for 循环,不断地从这些任务队列中取出任务并执行;
  • 微任务是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前;
  • V8 在执行 javascript 脚本时,会为其创建一个全局执行上下文,同时会创建一个微任务队列;
  • 执行微任务过程中产生的微任务不会推迟到下个宏任务中执行,而是在当前宏任务中继续执行;

19. 使用 Promise 告别回调函数

  • 使用 Promise 解决了回调地狱问题,消灭嵌套和多次处理;
  • 模拟实现 Promise
function Bromise(executor) {  var _onResolve = null  this.then = function (onResolve) {    _onResolve = onResolve  }  function resolve(value) {    setTimeout(() => {      _onResolve(value)    }, 0)  }  executor(resolve, null)}

20. async await 使用同步方式写异步代码

  • 生成器函数是一个带星号函数,而且是可以暂停执行和回复执行的;
  • 生成器函数内部执行一段代码,遇到 yield 关键字,javascript 引擎返回关键字后面的内容给外部,并且暂停该函数的执行;
  • 外部函数可以同步 next 方法恢复函数的执行;
  • 协程是一种比线程更加轻量级的存在,协程可以看成是跑在线程上的任务,一个线程可以存在多个协程,但是同时只能执行一个协程,如果 A 协程启动 B 协程,A 为 B 的父协程;
  • 协程不被操作协同内核所管理,而完全由程序所控制,这样性能提升;
  • await xxx 会创建一个 Promise 对象,将 xxx 任务提交给微任务队列;
  • 暂停当前协程的执行,将主线程的控制权力转交给父协程执行,同时将 Promise 对象返回给父协程,继续执行父协程;
  • 父协程执行结束之前会检查微任务队列,微任务队列中有 resolve(xxx) 等待执行,触发 then 的回调函数;
  • 回调函数被激活后,会将主线程的控制权交给协程,继续执行后续语句,完成后将控制权还给父协程。

21. 页面性能分析:利用 chrome 做 web 性能分析

  • Chrome 开发者工具(简称 DevTools)是一组网页制作和调试的工具,内嵌于 Google Chrome 浏览器中。它一共包含了 10 个功能面板,包括了 Elements、Console、Sources、NetWork、Performance、Memory、Application、Security、Audits 和 Layers。

22. DOM 树:JavaScript 是如何影响 DOM 树构建的

  • HTML 解析器(HTMLParse)负责将 HTML 字节流转换为 DOM 结构;
  • HTML 解析器并不是等整个文档加载完成之后再解析,而是网络进程加载流多少数据,便解析多少数据;
  • 字节流转换成 DOM 三个阶段:1、字节流转换为 Token;2、维护一个 Token 栈,遇到 StartTag Token 入栈,遇到 EndTag Token 出栈;3、为每个 Token 创建一个 DOM 节点;
  • JavaScript 文件和 CSS 样式表文件都会阻塞 DOM 解析;

23. 渲染流水线:CSS 如何影响首次加载时的白屏时间?

  • DOM 构建结束之后,css 文件还未下载完成,渲染流水线空闲,因为下一步是合成布局树,合成布局树需要 CSSOM 和 DOM,这里需要等待 CSS 加载结束并解析成 CSSOM;
  • CSSOM 两个作用:提供给 JavaScript 操作样式表能力,为布局树的合成提供基础样式信息;
  • 在执行 JavaScript 脚本之前,如果页面中包含了外部 CSS 文件的引用,或者通过 style 标签内置了 CSS 内容,那么渲染引擎还需要将这些内容转化为 CSSOM,因为 JavaScript 有修改 CSSOM 的能力,所以在执行 JavaScript 之前,还需要依赖 CSSOM。也就是说 CSS 在部分情况下也会阻塞 DOM 的生成。

24. 分层和合成机制:为什么 CSS 动画比 JavaScript 高效

  • 显示器固定刷新频率是 60HZ,即每秒更新 60 张图片,图片来自显卡的前缓冲区;
  • 显卡的职责是合成新的图像,保存在后缓冲区,然后后缓冲区和前缓冲区互换,显卡更新频率和显示前刷新频率不一致,就会造成视觉上的卡顿;
  • 渲染流水线生成的每一副图片称为一帧,生成一帧的方式有重排、重绘和合成三种;
  • 重排会根据 CSSOM 和 DOM 计算布局树,重绘没有重新布局阶段;
  • 生成布局树之后,渲染引擎根据布局树特点转化为层树,每一层解析出绘制列表;
  • 栅格线程根据绘制列表中的指令生成图片,每一层对应一张图片,合成线程将这些图片合成一张图片,发送到后缓存区;
  • 合成线程会将每个图层分割成大小固定的图块,优先绘制靠近视口的图块;

25. 页面性能:如何系统优化页面

  • 加载阶段:减少关键资源个数,降低关键资源大小,降低关键资源的 RTT 次数;
  • 交互阶段:减少 JavaScript 脚本执行时间,避免强制同步布局:操作 DOM 的同时获取布局样式会引发,避免布局抖动:多次执行强制布局和抖动,合理利用 CSS 合成动画:标记 will-change,避免频繁的垃圾回收;
  • CSS 实现一些变形、渐变、动画等特效,这是由 CSS 触发的,并且是在合成线程中执行,这个过程称为合成,它不会触发重排或者重绘;

26. 虚拟 DOM:虚拟 DOM 和真实 DOM 有何不同

  • 当有数据更新时, React 会生产一个新的虚拟 DOM,然会拿新的虚拟 DOM 和之前的虚拟 DOM 进行比较,这个过程找出变化的节点,然后将变化的节点应用到 DOM 上;
  • 最开始的时候,比较两个 DOM 的过程是在一个递归函数里执行的,其核心算法是 reconciliation。通常情况,这个比较过程执行很快,不过虚拟 DOM 比较复杂时,执行比较函数可能占据主线程比较久的时间,这样会导致其他任务的等待,造成页面卡顿。React 团队重写了 reconciliation 算法,称为 Fiber reconciler,之前老的算法称为 Stack reconciler;

27. PWA:解决 web 应用哪些问题

  • PWA(Progressive Web App),渐进式 Web 应用。一个渐进式过渡方案,让普通站点过渡到 Web 应用,降低站点改造代价,逐渐支持新技术,而不是一步到位;
  • PWA 引入 ServiceWorker 来试着解决离线存储和消息推送问题,引入 mainfest.json 来解决一级入口问题;
  • 暗转了 ServiceWorker 模块之后,WebApp 请求资源时,会先通过 ServiceWorker,让它判断是返回 Serviceworker 缓存的资源还是重新去网络请求资源,一切的控制权交给 ServiceWorker 来处理;
  • 在目前的 Chrome 架构中,Service Worker 是运行在浏览器进程中的,因为浏览器进程生命周期是最长的,所以在浏览器的生命周期内,能够为所有的页面提供服务;

28. WebComponent:像搭积木一样构建 web 应用

  • CSS 的全局属性会阻碍组件化,DOM 也是阻碍组件化的一个因素,因为页面中只有一个 DOM,任何地方都可以直接读取和修改 DOM;
  • WebComponent 提供了对局部试图封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中;
  • template 创建模版,查找模版内容,创建影子 DOM,模版添加到影子 DOM 上;
  • 影子 DOM 可以隔离全局 CSS 和 DOM,但是 JavaScript 是不会被隔离的;

29. HTTP1:HTTP1 性能优化

  • HTTP/0.9 基于 TCP 协议,三次握手建立连接,发送一个 GET 请求行(没有请求头和请求体),服务器接收请求之后,读取对应 HTML 文件,数据以 ASCII 字符流返回,传输完成断开连接;
  • HTTP/1.0 增加请求头和响应头来进行协商,在发起请求时通过请求头告诉服务器它期待返回什么类型问题、什么形式压缩、什么语言以及文件编码。引入来状态吗,Cache 机制等;
  • HTTP/1.1 改进持久化连接,解决建立 TCP 连接、传输数据和断开连接带来的大量开销,支持在一个 TCP 连接上可以传输多个 HTTP 请求,目前浏览器对于一个域名同时允许建立 6 个 TCP 持久连接;
  • HTTP/1.1 引入 Chunk transfer 支持动态生成内容:服务器将数据分割成若干任意大小的数据块,每个数据块发送时附上上个数据块的长度,最后使用一个零长度的块作为发送数据完成的标志。在 HTTP/1.1 需要在响应头中设置完整的数据大小,如 Content-Length。

30. HTTP2:如何提升网络速度

  • HTTP/1.1 主要问题:TCP 慢启动;同时开启多条 TCP 连接,会竞争固定宽带;对头阻塞问题;
  • HTTP/2 在一个域名下只使用一个 TCP 长连接和消除对头阻塞问题;
  • 多路复用的实现:HTTP/2 添加了二进制分帧层,将发送或响应数据经过二进制分帧处理,转化为一个个带有请求 ID 编号的帧,服务器或者浏览器接收到响应帧后,根据相同 ID 帧合并为一条完整信息;
  • 设置请求优先级:发送请求可以设置请求优先级,服务器可以优先处理;
  • 服务器推送:请求一个 HTML 页面,服务器可以知道引用了哪些 JavaScript 和 CSS 文件,附带一起发送给浏览器;
  • 头部压缩:对请求头和响应头进行压缩;

31. HTTP3:甩掉 TCP、TCL 包袱,构建高效网络

  • 虽然 HTTP/2 解决了应用层面的对头阻塞问题,不过和 HTTP/1.1 一样,HTTP/2 依然是基于 TCP 协议,而 TCP 最初是为了单连接而设计;
  • TCP 可以看成是计算机之间的一个虚拟管道,数据从一端发送到另一端会被拆分为一个个按照顺序排列的数据包,如果在传输过程中,有一个数据因为网络故障或者其他原因丢失,那么整个连接会处于暂停状态,只有等到该数据重新传输;
  • 由于 TCP 协议僵化,也不可能使用新的协议,HTTP/3 选择了一个折衷的方法,基于现有的 UDP 协议,实现类似 TC 片多路复用,传输可靠等功能,称为 QULC 协议;
  • QULC 实现类似 TCP 流量控制,传输可靠功能;集成 TLS 加密功能;实现多路复用功能;

32. 同源策略:为什么 XMLHttpRequst 不能跨域请求

  • 协议、域名和端口号相同的 URL 是同源的;
  • 同源策略会隔离不同源的 DOM、页面数据和网络通信;
  • 页面可以引用第三方资源,不过暴露出诸如 XSS 问题,引入内容安全策略 CSP 限制;
  • 默认 XMLHttpRequest 和 Fetch 不能跨站请求资源,引入跨域资源共享(CORS)进行跨域访问控制;

33. 跨站脚本攻击 XSS:为什么 cookie 中有 httpOnly 属性

  • XSS 跨站脚本,往 HTML 文件中注入恶意代码,对用户实施攻击;
  • XSS 攻击主要有存储型 XSS 攻击、反射型 XSS 攻击和 DOM 的 XSS 攻击;
  • 阻止 XSS 攻击:服务器对脚本进行过滤或转码,利用 CSP 策略,使用 HttpOnly;

34. CSRF 攻击:陌生连接不要随便点

  • CSRF 跨站请求伪造,利用用户的登录状态,通过第三方站点攻击;
  • 避免 CSRF 攻击:利用 SameSite(三种模式:Strict、Lax、None) 让浏览器禁止第三方站点发起请求携带关键 Cookie;验证请求的来源站点,请求头中的 Referer 和 Origin 属性;利用 CSRF Token;

35. 沙盒:页面和系统之间的隔离墙

  • 浏览器被划分为浏览器内核和渲染内核两个核心模块,其中浏览器内核石油网络进程、浏览器主进程和 GPU 进程组成的,渲染内核就是渲染进程;
  • 浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,需要通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程;
  • 站点隔离(Site Isolation)将同一站点(包含相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行;
  • 实现站点隔离,就可以将恶意的 iframe 隔离在恶意进程内部,使得它无法继续访问其他 iframe 进程的内容,因此无法攻击其他站点;

36. HTTPS:让数据传输更安全

  • 在 TCP 和 HTTP 之间插入一个安全层,所有经过安全层的数据都会被加密或者解密;
  • 对称加密:浏览器发送加密套件列表和一个随机数 client-random,服务器会从加密套件中选取一个加密套件,然后生成一个随机数 service-random,返回给浏览器。这样浏览器和服务器都有相同 client-random 和 service-random,再用相同的方法将两者混合生成一个密钥 master secret,双方就可以进行数据加密传输了;
  • 对称加密缺点:client-random 和 service-random 的过程都是明文,黑客可以拿到协商的加密套件和双方随机数,生成密钥,数据可以被破解;
  • 非对称加密:浏览器发送加密套件列表给服务器,服务器选择一个加密套件,返回加密套件和公钥,浏览器用公钥加密数据,服务器用私钥解密;
  • 非对称加密缺点:加密效率太低,不能保证服务器发送给浏览器的数据安全,黑客可以获取公钥;
  • 对称加密结合非对称加密:浏览器发送对称加密套件列表、非对称加密列表和随机数 client-random 给服务器,服务器生成随机数 service-random,选择加密套件和公钥返回给浏览器,浏览器利用 client-random 和 service-random 计算出 pre-master,然后利用公钥给 pre-master 加密,向服务器发送加密后的数据,服务器用私钥解密出 pre-master 数据,结合 client-random 和 service-random 生成对称密钥,使用对称密钥传输加密数据;
  • 引入数字证书是为了证明“我就是我”,防止 DNS 被劫持,伪造服务器;
  • 证书的作用:一个是向浏览器证明服务器的身份,另一个是包含服务器公钥;
  • 数字签名过程:CA 使用 Hash 函数技术明文信息,得出信息摘要,然后 CA 使用私钥对信息摘要进行加密,加密后的秘文就是数字签名;
  • 验证数字签名:读取证书明文信息,使用相同 Hash 函数计算得到信息摘要 A,再利用 CA 的公钥解密得到 B,对比 A 和 B,如果一致,则确认证书合法;

web前端号整理,来源:wuwhs[segmentfault]