vue 相关问题
vue2和vue3的区别
生命周期
多根节点
Composition API
Vue2 是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。
Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。
响应式原理
Object.defineProperty
- 直接在一个对象上定义新的属性或修改现有的属性,并返回对象。
- 无法监听对象或数组新增、删除的元素。
- Vue2 相应解决方案:针对常用数组原型方法push、pop、shift、unshift、splice、sort、reverse进行了hack处理;提供Vue.set监听对象/数组新增属性。对象的新增/删除响应,还可以new个新对象,新增则合并新属性和旧对象;删除则将删除属性后的对象深拷贝给新对象。
Proxy
- Proxy 是 ES6 新特性,通过第2个参数 handler 拦截目标对象的行为。相较于 Object.defineProperty 提供语言全范围的响应能力,消除了局限性。
- 基本用法:创建对象的代理,从而实现基本操作的拦截和自定义操作。
- 局限性: (1)、对象/数组的新增、删除 (2)、监测 .length 修改 (3)、Map、Set、WeakMap、WeakSet 的支持
事件缓存
Vue3 的cacheHandler可在第一次渲染后缓存我们的事件。相比于 Vue2 无需每次渲染都传递一个新函数。加一个 click 事件。
Diff算法优化
搬运 Vue3 patchChildren 源码。结合上文与源码,patchFlag 帮助 diff 时区分静态节点,以及不同类型的动态节点。一定程度地减少节点本身及其属性的比对。
打包优化
Tree-shaking:模块打包 webpack、rollup 等中的概念。移除 JavaScript 上下文中未引用的代码。主要依赖于 import 和 export 语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。
TypeScript支持
Options API 与 Composition API
vue数据绑定
Vue是如何实现双向数据绑定的?
-
Vue双向数据绑定是通过数据劫持+发布订阅者模式来实现的。Vue采用的是MVVM架构,实现MVVM主要包含两个方面,一是数据变化更新视图,二是试图变化更新数据。 在实现过程上来说,主要有四个模块:
监听器Observer:执行劫持监听的所有属性,如果属性发生变化了,就通知订阅者Watcher看是否需要更新。
订阅者Watcher:可以受到属性的变化通知并执行相应的函数,从而更新视图。
消息订阅器Dep:因为订阅者有很多个,所以需要一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理。
解析器Compile:可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
-
vue 内部实现双向绑定过程:简单来说就是初始化 data 的时候会调用 observe 方法给 data 里的属性重写 get 方法和 set 方法,到渲染真实 dom 的时候,渲染 watcher 会去【 访问 】页面上使用的属性变量,给属性的 Dep 都加上渲染函数,每次修改数据时通知渲染 watcher 更新视图
Vue是如何实现响应式的?
Vue实现响应式的核心就是通过Object.defineProperty来劫持对象的get和set,在get中收集依赖,在set中分发依赖,其中Vue2是借助Object.defineProperty实现的,而Vue3则是借助Proxy实现的。
Vue2和Vue3响应式原理的不同之处
- Vue2使用的是Object.defineProperty()实现响应式原理,而Vue3使用的是Proxy()实现。
- 在面对对象嵌套的时候,Vue2和Vue3都需要进行递归,但是Vue2是对所有属性进行递归,而Vue3则是按需递归,如果没有使用到内部对象的属性,就不要递归,性能更好。
- Vue2中对象不存在的属性是不能被拦截的,但是Vue3可以。
Vue3为什么弃用了ObjectdefineProperty选择了Proxy
- Object.defineProperty无法监控到数组下标的变化,导致通过数组数组下标添加元素不能实时响应。
- Proxy不仅可以代理对象,还可以代理数组,还可以代理动态增加的属性。
- Object.defineProperty不能对ES6新产生的Map、Set这样的数据结构进行监听。
vue和react区别
组件化
- react和vue都推崇组件化,通过将页面拆分成一个一个小的可复用单元来提高代码的复用率和开发效率。 在开发时react和vue有相同的套路,比如都有父子组件传参,都有数据状态管理,都有前端路由等。
- React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中,即 all in js;
- Vue 推荐的做法是 template 的单文件组件格式(简单易懂,从传统前端转过来易于理解),即 html,css,JS 写在同一个文件(vue也支持JSX写法)
虚拟DOM
虚拟DOM的优点
- 减少 DOM 操作:虚拟 DOM 可以将多次 DOM 操作合并为一次操作
- 研发效率的问题:虚拟 DOM 的出现,为数据驱动视图这一思想提供了高度可用的载体,使得前端开发能够基于函数式 UI 的编程方式实现高效的声明式编程。
- 跨平台的问题:虚拟 DOM 是对真实渲染内容的一层抽象。同一套虚拟 DOM,可以对接不同平台的渲染逻辑,从而实现“一次编码,多端运行”
react和vue中虚拟DOM的相同点
- Vue与React都使用了 Virtual DOM + Diff算法, 不管是Vue的Template模板+options api 写法, 还是React的Class或者Function写法,最后都是生成render函数,而render函数执行返回VNode(虚拟DOM的数据结构,本质上是棵树)。
- 当每一次UI更新时,总会根据render重新生成最新的VNode,然后跟以前缓存起来老的VNode进行比对,再使用Diff算法(框架核心)去真正更新真实DOM(虚拟DOM是JS对象结构,同样在JS引擎中,而真实DOM在浏览器渲染引擎中,所以操作虚拟DOM比操作真实DOM开销要小的多)
react和vue中虚拟DOM的差别
- react 会自顶向下全diff。vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
- 在react中,当状态发生改变时,组件树就会自顶向下的全diff, 重新render页面, 重新生成新的虚拟dom tree, 新旧dom tree进行比较, 进行patch打补丁方式,局部更新dom。所以react为了避免父组件更新而引起不必要的子组件更新, 可以在shouldComponentUpdate做逻辑判断,减少没必要的render, 以及重新生成虚拟dom,做差量对比过程。
- 在vue中, 通过Object.defineProperty 把 data 属性全部转为 getter/setter。同时watcher实例对象会在组件渲染时,将属性记录为dep, 当dep 项中的 setter被调用时,通知watch重新计算,使得关联组件更新。
数据驱动视图
Vuejs的数据驱动是通过MVVM这种框架来实现的。MVVM框架主要包含3个部分:model、view和 viewModel
- Model:指的是数据部分,对应到前端就是javascript对象
- View:指的是视图部分,对应前端就是dom
- ViewModel:就是连接视图与数据的中间件
ViewModel是实现数据驱动视图的核心,当数据变化的时候,ViewModel能够监听到这种变化,并及时的通知view做出修改。同样的,当页面有事件触发时,ViewModel也能够监听到事件,并通知model进行响应。ViewModel就相当于一个观察者,监控着双方的动作,并及时通知对方进行相应的操作。
React通过setState实现数据驱动视图,通过setState来引发一次组件的更新过程从而实现页面的重新渲染(除非shouldComponentUpdate返回false)。
vuex
什么是Vuex?
vuex是一个公共的状态管理工具,用于管理一些公共的状态或用于一些毫不相关的组件之间的值传递
Vuex解决了什么问题?
- 多个组件依赖于同一状态时,对于多层嵌套的组件的传参将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
- 来自不同组件的行为需要变更同一状态。以往采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
五个核心
- state => 基本数据(数据源存放地)
- getters =>从基本数据派生出来的数据
- mutations => 提交更改数据的方法,同步
- actions =>像一个装饰器,包裹mutations,使之可以异步
- modules => 模玦化Vuex
辅助函数
- mapstate
- mapmutation
- mapaction
v-model
- v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
- 果 v-model 绑定的是响应式对象上某个不存在的属性,那么 vue 会悄悄地增加这个属性,并让它响应式。
- v-model 是双向绑定
- v-model 是单向数据流
子组件不能改变父组件传递给它的 prop 属性,推荐的做法是它抛出事件,通知父组件自行改变绑定的值。
数据向下,事件向上
vue对象添加属性怎么实现响应式更新
定义:使用到了数据,当这个数据改变后,视图跟着数据变化同步更新。
缺陷
- Vue2通过Object.defineProperty进行数据劫持,需要遍历对象为属性添加getter和setter,性能很差
- 在组件第一次挂载时,会使用某些响应式数据,因此会触发数据对应的getter方法,就可以在 getter 中进行依赖收集。在后续数据变化时,会触发setter 方法,就可以在 setter 中通知依赖进行更新
- 新增属性和删除属性监测不到。需要使用 s e t 、 set、 set、delete来实现数据的响应式
- 由于数组的元素可能有很多,循环遍历为每一个元素添加getter和setter性能太差,所以Vue2不对基本类型的数组元素进行响应式处理,而是重写数组相关的方法。
- 对于ES6中新产生的Map、Set等数据结构不支持
vue3 tree-shaking
- 在Vue2中,无论我们使用什么功能,它们最终都会出现在生产代码中。主要原因是Vue实例在项目中是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到
- 而Vue3源码引入tree shaking特性,将全局 API 进行分块。如果您不使用其某些功能,它们将不会包含在您的基础包中
- 编译阶段利用ES6 Module判断哪些模块已经加载
- 判断那些模块和变量未被使用或者引用,进而删除对应代码
作用
- 减少程序体积(更小)
- 减少程序执行时间(更快)
- 便于将来对程序架构进行优化(更友好)
vue-router
hash
hash: 兼容所有浏览器,包括不支持 HTML5 History Api 的浏览器,例http://www.abc.com/#/index,hash值为#/index, hash的改变会触发hashchange事件,通过监听hashchange事件来完成操作实现前端路由。hash值变化不会让浏览器向服务器请求。
history
history: 兼容能支持 HTML5 History Api 的浏览器,依赖HTML5 History API来实现前端路由。没有#,路由地址跟正常的url一样,但是初次访问或者刷新都会向服务器请求,如果没有请求到对应的资源就会返回404,所以路由地址匹配不到任何静态资源,则应该返回同一个index.html 页面,需要在nginx中配置。
路由守卫
- router.beforeEach:全局前置守卫。
- router.beforeResolve:全局解析守卫。
- router.afterEach:全局后置钩子。
- beforeEnter:进入路由之前(独享守卫)
vuex
- state用来存放共享变量的地方
- mutations用来存放修改state的方法。
- actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。常用来做一些异步操作
- getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值
axios
ajax缺点
- 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
- 基于原生的XHR开发,XHR本身的架构不清晰。
- JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
- 不符合关注分离(Separation of Concerns)的原则
- 配置和调用方式非常混乱,而且基于事件的异步模型不友好。
特点
- 基于 promise 的异步 ajax 请求库,支持promise所有的API
- 浏览器端/node 端都可以使用,浏览器中创建XMLHttpRequests
- 支持请求/响应拦截器
- 支持请求取消
- 可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据
- 批量发送多个请求
- 安全性更高,客户端支持防御 XSRF,就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。
请求拦截器,响应拦截器
- 请求拦截器用于在接口请求之前做的处理,比如为每个请求带上相应的参数(token,时间戳等)
- 返回拦截器用于在接口返回之后做的处理,比如对返回的状态进行判断(token是否过期)
data
- 每个组件都是 Vue 的实例。
- 组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一个会影响其他
- 组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份 data,就会造成一个变了全都会变的结果。
webpack
打包过程
- 初始化参数:从配置文件和 Shell语句中读取与合并参数,得出最终的参数
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
- 确定入口:根据配置中的 entry 找出所有的入口文件
- 编译模块:从入口文件出发,调用所有配置的Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译:在经过第金步使上oader 翻译完所有模块后,得到了每个模块被翻译后的最終内寮以及它们之间的依赖关系
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统,在以上过程中,webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用
babel
通过配置Babel加载器,Webpack会在构建过程中将JavaScript模块经过Babel转换为向后兼容的代码,以便在目标浏览器或环境中运行。这样可以让开发者使用最新的JavaScript语法和特性,同时保证代码在不同环境下的兼容性。
webpack 优化
- 因为webpack构建流程其中有个步骤是所有的模块进行编译处理,那么我们可以在以下几点上做优化处理,既然它要查找文件,那么我们就让它查找的范围缩小从而提升查找效率,可以用alias,extensions等配置缩小范围,
- 第二点减少需要解析的文件,使用noparse配置告诉webpack排除忽略指定文件,不对他们进行解析,
- 第三点避免重复编译第三方库,可以把第三方库单独打包到一个文件中,它不会跟着业务文件一起重新打包,也能提高webpack的构建速度,起到优化作用。
vite 和 webpack
构建速度
Webpack: Webpack的构建速度相对较慢,尤其在大型项目中,因为它需要分析整个依赖图,进行多次文件扫描和转译。
Vite: Vite以开发模式下的极速构建著称。它利用ES模块的特性,只构建正在编辑的文件,而不是整个项目。这使得它在开发环境下几乎是即时的。
开发模式
Webpack: Webpack通常使用热模块替换(HMR)来实现快速开发模式,但配置相对复杂。
Vite: Vite的开发模式非常轻量且快速,支持HMR,但无需额外配置,因为它默认支持。
配置复杂度
Webpack: Webpack的配置相对复杂,特别是在处理不同类型的资源和加载器时。
Vite: Vite鼓励零配置,使得项目起步非常简单,但同时也支持自定义配置,使其适用于复杂项目。
插件生态
Webpack: Webpack拥有庞大的插件生态系统,适用于各种不同的需求。
Vite: Vite也有相当数量的插件,但相对较小,因为它的开发模式和构建方式减少了对一些传统插件的需求。
编译方式
Webpack: Webpack拥有庞大的插件生态系统,适用于各种不同的需求。
Vite: Vite也有相当数量的插件,但相对较小,因为它的开发模式和构建方式减少了对一些传统插件的需求。
应用场景
Webpack: 适用于复杂的大型项目,特别是需要大量自定义配置和复杂构建管道的项目。
Vite: 更适用于小到中型项目,或者需要快速开发原型和小型应用的场景。
打包原理
Webpack: Webpack的打包原理是将所有资源打包成一个或多个bundle文件,通常是一个JavaScript文件。
Vite: Vite的打包原理是保持开发时的模块化结构,使用浏览器原生的导入机制,在生产环境中进行代码分割和优化
ES mode
- ES6 module是编译时加载,输出的是接口,CommonJS运行时加载,加载的是一个对象
- ES6模块是编译时加载的,这意味着模块的依赖关系在代码编译阶段就可以确定,并且在运行时保持不变。在ES6模块中,import语句会创建一个连接到被导出模块的实时引用,而不是一个简单的值拷贝。这样做的好处是可以进行静态分析和优化,使得编译器可以更好地理解模块之间的依赖关系,并进行相关的优化操作。
- 另一方面,CommonJS模块是运行时加载的,这意味着模块的依赖关系在代码执行过程中动态确定。在CommonJS模块系统中,require函数用于加载模块,返回的是被导出模块的一个拷贝(通常是一个对象)。因为是在运行时加载,所以无法在编译阶段对模块进行静态分析和优化。
差异
预先解析:ES6模块可以在编译阶段预先解析模块的依赖关系,使得浏览器或其他工具可以提前加载和缓存模块,从而实现更快的加载速度。而CommonJS模块需要在运行时动态解析依赖关系,无法进行预先解析。
静态分析:ES6模块的导入和导出是静态的,可以通过静态分析工具进行代码优化和检查。而CommonJS模块的导入和导出是动态的,无法进行静态分析。
循环依赖:由于ES6模块的编译时加载特性,它可以处理循环依赖(即模块之间相互引用)的情况。而CommonJS模块在处理循环依赖时会返回一个未完全加载的对象。
什么是bundle,什么是chunk,什么是module?
- bundle是由webpack打包出来的文件
- chunk是指webpack在进行模块的依赖分析的时候,代码分割出来的代码块
- module是开发中的单个模块。
什么是Loader?什么是Plugin?
- loader是使wenbpack拥有加载和解析非js文件的能力
- plugin 可以扩展webpack的功能,使得webpack更加灵活。可以在构建的过程中通过webpack的api改变输出的结果
区别
在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过Webpack 提供的 API 改变输出结果
对于 loader,实质是一个转换器,将A文件进行编译形成B文件,操作的是文件,比如将 A.scss 转变为 B.css ,单纯的文件转换过程
什么是Tree-shaking?CSS可以Tree-shaking吗?
- Tree-shaking是指在打包中去除那些引入了,但是在代码中没有被用到的那些死代码。
- 在webpack中Tree-shaking是通过uglifySPlugin来Tree-shaking JS
- Css需要使用Purify-CSS。
JS
基本数据类型
- Number
- String
- Boolean
- Undefined
- null
- symbol
原型链
数据循环
闭包
定义
一个作用域可以访问另外一个函数内部的局部变量 ,或者说一个函数(子函数)访问另一个函数(父函数)中的变量。 此时就会有闭包产生 ,那么这个变量所在的函数我们就称之为闭包函数。
优缺点:
闭包的主要作用: 延伸了变量的作用范围, 因为闭包函数中的局部变量不会等着闭包函数执行完就销毁, 因为还有别的函数要调用它 , 只有等着所有的函数都调用完了他才会销毁 闭包会造成内存泄漏,如何解决:用完之后手动释放
详解:
闭包不仅仅可以实现函数内部的作用域访问这个函数中的局部变量,还可以实现全局作用域或者是别的地方的作用域也可以访问到函数内部的局部变量 ,实现方法就是 return 了一个函数所以 return 函数也是我们实现闭包的一个主要原理, 因为返回的这个函数本身就是我们fn 函数内部的一个子函数 ,所以子函数是可以访问父函数里面的局部变量的, 所以返回完毕之后 ,外面的函数一调用, 就会回头调用返回的这个函数, 所以就可以拿到这个子函数对应的父函数里面的局部变量
节流和防抖
jwt
宏任务 微任务
定时器
异步
xhr
箭头函数和普通函数
箭头函数this指针
深拷贝浅拷贝
浅拷贝
当对某个数据(数组或对象)进行拷贝后,修改新数据(拷贝后的数据)里面第1层的数据是不会影响老数据(被拷贝的数据)的,但是如果还要第2层 或 更深层次的数据(复杂数据类型),它仍然是有关联的,如果修改了新数据,那么老数据也会被修改。
浅拷贝方式1:扩展运算符
浅拷贝方式2:对象合并Object.assign()
深拷贝
就是在拷贝数据(数组或对象)时,不管数据对象里面有多少层,是简单 还是 复杂数据类型,只要进行深拷贝后,和老数据(之前被拷贝的数据)就毫无关联,相互独立(重新开辟内存空间),互不影响!在修改新数据,对老数据毫无影响。
深拷贝方式1:JSON.parse(JSON.stringify( )) 序列化
深拷贝方式2:deepCopy() 自定义方法
ES6新特性
继承
- 原型继承
- 组合式继承
- 继承组合式继承
数组中的forEach 和map 的区别?
相同点
- 都是循环遍历数组中的每一项
- forEach 和map 方法里每次执行匿名函数都支持 3 个参数,参数分别是 item(当前每一项),index(索引值),arr(原数组)
- 匿名函数中的 this 都是指向 window 只能遍历数组 都不会改变原数组
不同点
map
- map 方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值
- map 方法不会对空数组进行检测,map 方法不会改变原始数组。
- 浏览器支持:chrome、Safari1.5+、opera 都支持,IE9+, 若 arr 为空数组,则 map 方法返回的也是一个空数组。
forEach 方法
- forEach 方法用来调用数组的每个元素,将元素传给回调函数
- forEach 对于空数组是不会调用回调函数的。forEach 返回的都是 undefined。
or in 和for of 的区别
- 推荐在循环对象属性的时候使用 for…in,在遍历数组的时候的时候使用 for…of
- for…in 循环出的是 key,for…of 循环出的是 value
- 注意,for…of 是 ES6新引入的特性。修复了 ES5 引入的 for…in 的不足
计网
TCP/UDP
网络层次
HTTP2 HTTP3
- HTTP/1.1:HTTP/1.1是最早的版本,它使用传统的文本格式进行通信。每个请求都需要建立一个新的TCP连接,并且在请求-响应模型中只能处理一个请求。这导致了延迟和性能瓶颈,特别是在处理大量小文件或并行请求时。
- HTTP/2:HTTP/2引入了多路复用(Multiplexing)的概念,允许在单个TCP连接上同时发送多个请求和响应。这样可以避免建立多个连接的开销,并显著提高性能。HTTP/2还支持头部压缩、服务器推送和优先级等功能,进一步减少了延迟和带宽消耗。
- HTTP/3:HTTP/3采用了QUIC(Quick UDP Internet Connections)作为传输协议,而不是依赖于TCP。QUIC基于UDP,具有更低的延迟和更好的拥塞控制机制,适用于移动网络和高丢包率环境。HTTP/3通过使用QUIC实现了快速而可靠的数据传输,并解决了TCP中的队头阻塞问题。此外,HTTP/3还支持多路复用、头部压缩和服务器推送等功能。
xss攻击 csrf攻击
get post 请求区别
- GET在浏览器回退不会再次请求,POST会再次提交请求
- GET请求会被浏览器主动缓存,POST不会,要手动设置
- GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会
- GET请求在URL中传送的参数是有长度限制的,而POST没有限制
- GET参数通进URL传递
- GET参数暴露在地址栏不安全,POST放在报文内部更安全
- GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作
- GET产生一个TCP数据包;POST产生两个TCP数据包
http 请求原理
- 客户端发起请求:客户端(如浏览器)向服务器发送HTTP请求。请求包括一个请求行、请求头和请求体。请求行指定请求方法(GET、POST等)、请求的URL和协议版本。
- 服务器响应请求:服务器接收到请求后,根据请求进行处理,并生成一个HTTP响应。响应包括一个状态行、响应头和响应体。状态行指定响应的状态码(例如200表示成功、404表示未找到等)和协议版本。
数据传输:客户端和服务器之间通过TCP/IP协议进行数据传输。HTTP使用TCP作为传输协议,通过建立TCP连接来进行可靠的数据传输。 - 请求-响应模式:HTTP采用了请求-响应模式,即每个请求都对应一个单独的响应。客户端发送请求后,服务器处理请求并返回响应给客户端。然后,客户端解析响应并进行相应的处理,如渲染页面或执行其他操作。
- 连接管理:HTTP可以使用持久连接(Keep-Alive)来减少连接的建立和关闭次数,提高性能。通过持久连接,多个请求可以共享同一个TCP连接,减少了建立和关闭连接的开销。
- 请求方法:HTTP定义了不同的请求方法,常见的有GET、POST、PUT、DELETE等。不同的请求方法用于执行不同的操作,例如获取资源、提交数据、更新资源等。
- 请求头和响应头:HTTP请求和响应中都包含头部信息,用于传递附加的元数据。头部信息包括键值对形式的字段,用于指定一些控制参数、身份验证、内容类型等。
- URL和URI:HTTP使用统一资源标识符(Uniform Resource Identifier)来标识要访问的资源。URL(Uniform Resource Locator)是URI的一种常见形式,它包含了资源的位置和访问方式。
ES6
数据类型
ES6新语法有哪些
项目
如何性能优化
项目如何打包
项目中遇到过的问题,如何解决
多端适配
首屏加载慢
问题
- 未剔除项目模板用到的冗余依赖,比如g2、quill、wangEditor、mock等
- 一些没用到的Ant-design组件由于全局注册也一并打包了进去
- 项目中只用到几个Ant-Design/icons,但却被全量引入
- moment和moment-timezone重复,且体积较大
- 打包策略不合理,导致chunk-vendor太大
- core-js体积较大
解决
排查并移除冗余依赖、静态资源
移除项目模板冗余依赖
将public的静态资源移入assets。静态资源应该放在assets下,public只会单纯的复制到dist,应该放置不经webpack处理的文件,比如不兼容webpack的库,需要指定文件名的文件等等
构建时压缩图片
使用image-webpack-loader
使用webP图片
优化路由懒加载
组件开发
项目难点
svg和canvas
渲染方式:
- SVG:SVG图形是基于矢量的,它使用浏览器内置的SVG解析器将图形转换为DOM元素,并通过CSS进行样式渲染。因此,SVG图形可以无损放大或缩小,并保持清晰度。
- Canvas:Canvas是基于位图的,它在画布上直接绘制像素。由于使用的是位图,所以当Canvas画布大小改变时,图形会被重新绘制,可能导致图形失真或模糊。
交互性:
- SVG:SVG图形是基于DOM的,每个图形元素都是一个独立的DOM节点,可以通过JavaScript操作和修改。因此,SVG图形可以具有交互性,例如添加事件监听器、修改属性等。
- Canvas:Canvas绘制的图形是一个静态位图,它没有内置的交互功能。如果需要实现交互性,需要手动处理鼠标事件或触摸事件,并根据事件进行相应的操作。
适用场景:
- SVG:由于SVG图形是矢量的,适合绘制复杂且需要缩放的图形,如地图、数据可视化图表等。同时,SVG也适合需要与DOM进行交互的场景。
- Canvas:Canvas适合绘制需要频繁更新的图形,如游戏画面、动画效果等。由于Canvas绘制的是位图,不适合用于需要无损缩放的情况。
CSS
BFC
BFC(Block formatting context)直译为"块级格式化上下文"。它是一个独立的渲染区域,
只有 Block-level box 参与,它规定了内部的 Block-level Box 如何布局,并且与这个区域外部毫不相干
布局规则
- 内部的 Box 会在垂直方向,一个接一个地放置
- Box 垂直方向的距离由 margin 决定。属于同一个 BFC 的两个相邻 Box 的 margin会发生重叠
- 每个元素的 margin box 的左边, 与包含块 border box 的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此
- BFC 的区域不会与 float box 重叠
- BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此
- 计算 BFC 的高度时,浮动元素也参与计算
哪些元素会生成 BFC:
- 根元素
- float 属性不为 none
- position 为 absolute 或 fixed
- display 为 inline-block, table-cell, table-caption, flex, inline-flex
- overflow 不为 visible
盒模型
行内元素
块级元素
元素居中
手撕代码
- 排序
- 节流
- 构造函数,箭头函数,构造实例的this分别指向谁
- 数组区间
- 快速排序
- 数组合并
浏览器
线程
如果一个浏览器同时打开两个tab页,其中一个tab页出现了报错,会影响另一个吗
在浏览器中同时打开两个标签页时,每个标签页都是相互独立的运行环境。因此,一个标签页中的报错通常不会直接影响到其他标签页。 当一个标签页发生错误时,通常会在该标签页的控制台(开发者工具)中显示错误信息,并且可能会导致该标签页的某些功能无法正常工作。其他标签页将继续运行,并且不会受到错误的直接影响。 然而,有一些情况下,错误可能会间接地影响其他标签页。例如,如果一个标签页中的 JavaScript 代码出现了内存泄漏或性能问题,它可能会导致整个浏览器变得缓慢或不稳定,从而影响到其他标签页的性能。 总体而言,浏览器的多标签页设计旨在实现隔离和安全性,以确保一个标签页的问题不会直接影响到其他标签页。然而,如果你遇到了特定的情况或浏览器行为异常,请参考浏览器的文档或联系浏览器厂商获取更详细的解释和支持。
重绘重排
- 重绘:当元素的一部分属性发生改变,如外观、背景、颜色等不会引起布局变化,只需要浏览器根据元素的新属性重新绘制,使元素呈现新的外观叫做重绘。
- 重排(回流):当 render 树中的一部分或者全部因为大小边距等问题发生改变而需要 DOM 树重新计算的过程
- 重绘不一定需要重排(比如颜色的改变),重排必然导致重绘(比如改变网页位置)
避免多次重排方法:
- 需要要对元素进行复杂的操作时,可以先隐藏(display:“none”),操作完成后再显示
- 需要创建多个 DOM 节点时,使用 DocumentFragment 创建完后一次性的加入 document
缓存 Layout 属性值,如:var left = elem.offsetLeft; 这样,多次使用 left 只产生一次回流 - 尽量避免用 table 布局(table 元素一旦触发回流就会导致 table 里所有的其它元素回流)
- 避免使用 css 表达式(expression),因为每次调用都会重新计算值(包括加载页面)
- 尽量使用 css 属性简写,如:用 border 代替 border-width, border-style, border-color
- 批量修改元素样式:elem.className 和 elem.style.cssText 代替 elem.style.xxx
XXS CSRF
XSS 恶意脚本攻击
- CSP(内容安全策略):通过设置CSP头部,限制页面中能够加载和执行的资源来源,从而减少XSS攻击的风险。
- HttpOnly标记:对于存储敏感信息的Cookie,使用HttpOnly标记,限制JavaScript访问,防止恶意脚本获取敏感数据。
CSRF 跨站请求伪造
- 验证来源(Referer):服务器端可以验证请求的Referer头部,确保请求来自合法的源,而不是恶意网站。
- 随机令牌(CSRF Token):在每个敏感操作中包含一个随机生成的令牌,并将该令牌与用户会话相关联。提交请求时,验证令牌的有效性,确保只接受合法的请求。
OAuth
- OAuth(开放授权)是一种用于授权第三方应用程序访问用户资源的开放标准。它被广泛应用于各种互联网服务,如社交媒体、云存储和API提供商等。
- 用户授权:OAuth的核心理念是用户授权。在OAuth流程中,用户明确地授予第三方应用程序访问其受保护资源的权限。这意味着用户可以选择性地授予或撤销对某些资源的访问权限,从而保护自己的隐私和安全。
- 令牌的有效期限:OAuth使用短期访问令牌(Access Token)来授权访问用户资源。这些令牌具有较短的有效期限,并且可以被撤销或刷新。通过限制令牌的有效期,可以降低被滥用的风险,并增加系统的安全性。
http1.0 和 http1.1 缓存策略的区别
http1
- HTTP/1.0的缓存策略相对简单,主要依赖于Expires头字段。服务器通过设置Expires字段来指定响应的过期时间,客户端可以根据该值来判断是否使用缓存。如果本地缓存中存在有效的资源副本(即未过期),则客户端可以直接使用缓存而无需向服务器发送请求。
- 然而,HTTP/1.0的缓存策略存在一些问题。由于Expires字段是由服务器提供的固定时间戳,如果服务器的时钟与客户端的时钟不同步,或者资源在服务器上提前失效,那么客户端可能会获取到过期的缓存。此外,HTTP/1.0没有提供一种机制来检查资源是否已经被修改,因此客户端只能按照过期时间来判断是否需要重新请求资源。
http1.1 - 为了解决HTTP/1.0的缓存问题,HTTP/1.1引入了更灵活的缓存机制。HTTP/1.1的缓存策略主要依赖于Cache-Control头字段。Cache-Control提供了多个指令,用于控制缓存的行为。
- HTTP/1.1还引入了Etag头字段,用于标识资源的唯一性。服务器可以生成一个唯一的Etag值,并在响应中返回给客户端。客户端在后续请求中可以通过If-None-Match头字段将Etag值发送给服务器,以检查资源是否已经被修改。如果Etag匹配,服务器可以返回一个特殊的状态码(304 Not Modified),告知客户端可以使用缓存副本。
浏览器渲染过程
- 构建DOM树(Document Object Model):当浏览器接收到HTML文档,它会解析HTML代码并构建DOM树。DOM树是一个表示文档结构的树形结构,其中每个HTML元素都表示为一个节点,它们之间通过父子关系连接。
- 构建CSSOM树(CSS Object Model):同时解析HTML时,浏览器还会解析CSS样式表,并构建CSSOM树。CSSOM树表示了文档中所有CSS规则和样式信息。
- 合并DOM树和CSSOM树,生成渲染树(Render Tree):将DOM树和CSSOM树合并成渲染树。渲染树只包含需要显示在屏幕上的可见元素,例如有尺寸和位置的元素。渲染树的构建过程中,一些元素可能会被隐藏、过滤或优化,以提高渲染性能。
- 布局(Layout):渲染树构建完成后,浏览器会确定每个渲染树节点在屏幕上的精确位置和大小,这个过程称为布局或回流(reflow)。浏览器计算出每个元素的盒模型(Box Model),包括边距、边框、填充和内容区域。
- 绘制(Painting):在布局完成后,浏览器将渲染树的节点转换为屏幕上的实际像素,这个过程称为绘制。浏览器会遍历渲染树,并根据每个节点的样式属性绘制相应的图形和文本。
- 合成与显示:最后,浏览器将绘制好的图像发送给GPU进行合成,并在屏幕上显示出来。GPU会对多个图层进行合成,以提高性能和动画效果。
JWT和sessionid
- 跨域支持:由于JWT是基于标准的JSON格式,因此易于在不同的域和服务之间进行传输。这使得JWT成为构建跨域认证和授权系统的理想选择。而Session ID通常使用基于Cookie的机制,并受到浏览器同源策略的限制。
- 扩展性和灵活性:JWT可以在令牌中包含自定义的声明(Claims),这使得开发人员能够根据应用程序的需求添加额外的信息。这些声明可以用于传递用户角色、权限、过期时间等信息。而Session ID通常只包含一个唯一的会话标识符。
- 安全性:JWT可以使用数字签名进行验证,确保令牌的完整性和真实性。这使得服务器能够验证JWT是否被篡改或伪造。而Session ID通常依赖于服务器端的会话存储和安全策略。