meta标签可以用来定义页面的元信息,用于描述页面的内容、关键词、编码格式、刷新等。常见的meta标签有定义关键词的keywords,定义页面描述的description,定义页面编码的charset,定义页面自动刷新间隔的refresh等。
script标签引入的JavaScript脚本应该放在页面的闭合标签之前。因为浏览器解析HTML是从上到下顺序解析的,如果把script标签放在头部,JavaScript脚本将在页面DOM渲染之前执行,此时可能会访问到未准备好的DOM,导致出错。而将script标签放在之前,可以确保先解析页面的DOM元素,再执行脚本,访问到的DOM内容是准备好的。
盒模型(BoxModel)用来设定一个元素在网页布局中如何显示,盒模型组成包含内容(content)、内边距(padding)、边框(border)和外边距(margin)。通过盒模型可以精确控制一个元素占用的空间和位置。
BFC指块格式化上下文(BlockFormattingContext),它具有如下特点:
利用BFC可以解决浮动元素带来的问题,也可以避免margin重叠等布局问题。
冒泡和捕获描述了事件传播的两个阶段:
可以通过addEventListener的第三个参数配置true或者false,来指定只使用事件捕获或事件冒泡阶段:
//只使用捕获阶段ele.addEventListener(eventName,fn,true)//只使用冒泡阶段ele.addEventListener(eventName,fn,false)你了解shadowdom吗它在什么场景下会使用ShadowDOM可以将一个DOMsubtree封装到一个组件内部,实现组件的封装与隔离。组件内部的DOM改变只会影响组件内部,不会影响页面其他部分,有利于实现组件的封装。
常见的使用场景:
利用WebComponents可以封装独立的组件逻辑,实现可复用、可组合的组件系统,对组件化开发很有意义。
HTML5进行了大规模升级,新增的功能很多,主要包括:
MutationObserver是一个WebAPI,可以监听DOM变动事件。当DOM对象树发生任何变动时,MutationObserver会得到通知。这种机制取代了以前的MutationEvents功能。
使用MutationObserver主要有以下优势:
所以MutationObserver常用于需要对DOM变动作出响应的场景,如动画效果等。
实现资源预加载常用的技术主要有两种:
使用标签,设置rel="preload"可以让浏览器提前加载该资源,语法如:
实现网页水印的方式有服务端生成和前端生成两种,对于前端生成水印主要技术步骤是:
这样可以实现一个页面可见但不影响交互的水印效果。
实现页面截图主要通过HTMl5canvas元素来完成,常用的方式是:
如果要完美兼容各种浏览器(如老版本IOS不支持toBlob),也可以发送DOM给后端服务生成图片再返回前端。
常用的移动端适配方案包括:
综合使用这些方式可以使页面在不同尺寸屏幕上效果良好。
CSS3对CSS进行了大规模升级,主要新增的功能包括:
以及其他各种视觉、动画等方面的增强。使CSS具有了更强的呈现能力。
CSS的引入方式主要有:
优先级从高到低依次是:行内样式>内嵌样式>外链样式。
避免过多使用style的原因:
所以推荐使用外链的独立CSS文件进行样式管理。
作用域指的是一个变量存在的范围。在JS中主要有全局作用域和函数作用域。
闭包(Closure)是可以访问另一个函数内部变量的函数。闭包可以让一个函数保持对原始定义作用域的引用,所以使得这个函数可以继续访问定义函数的变量。
产生闭包的条件是:
然后内嵌函数就拥有了外层函数作用域的访问权,形成了闭包。
副作用指函数执行过程对函数外部可观察的状态产生的影响,如修改全局变量、修改参数、打印日志等都是副作用。
纯函数是无副作用的函数,它具有以下特点:
纯函数可以避免外部状态被修改,更易于测试和理解。
在JavaScript中,双等号"=="是宽松相等运算符,它会在比较之前尝试转换数据类型。具体来说,它遵循以下规则:1.如果比较的是相同数据类型,则直接比较值大小。2.如果比较的是不同数据类型,则先尝试转换类型再比较:-如果有一个操作数是Boolean,则将其转换为Number再比较。true转换为1,false转换为0。-如果一个操作数是String,另一个是Number,则将String转换为Number再比较。-如果一个操作数是Object,另一个是primitivetype(string/number/boolean),则Object调用valueOf()/toString()方法转成primitivetype再比较。3.对于非Number或者非String的对象,它们被转换成primitivevalues通常结果为NaN,导致比较结果为false。但Date对象是一个例外。所有对于双等号的比较来说,有的人如果理解不到位,会对这样的问题产生疑惑:'0'==0为true,'true'==true为false;这是因为有的人简单的认为双等号在内容相同的情况下也一定成立,但是这个想法是错误的,一定要及时纠正!EventLoop的理解是什么JavaScript采用事件循环模型执行代码。EventLoop负责监听调用栈和任务队列,按照规则判断是否将任务队列的任务添加到调用栈中执行。
每个事件循环都有一个任务队列,可以分为宏任务和微任务。常见的宏任务包括setTimeout等,微任务包括Promise回调等。调用栈执行完毕后会先执行当前微任务队列的所有任务,然后按规则读取宏任务并执行。
这种机制决定了JavaScript代码的执行顺序,需要理解EventLoop的运作,才能更好地编写JavaScript程序。
常用的判断一个对象是否为数组的方法有:
使用Array.isArray(obj)判断
使用Object.prototype.toString.call(obj)获取对象类型判断:
ES6及之后JavaScript版本新增的主要特性包括:
区别主要在于WeakSet和WeakMap的引用为弱引用,如果没有其他引用链会释放对象,可以用来防止内存泄漏。后两者只能使用对象作为键名也是为了利用这个特点。
void0主要的作用是获取undefined的值。在全局作用域中undefined是可以被重写的,为了获取确定为undefined的值,使用void0是一个好习惯。
例如,下面的赋值可以改变undefined的值:
varundefined='xxx'使用void0可以避免被覆盖,从而安全地得到undefined。
函数二义性指一个函数既可以被当作普通函数直接调用,也可以当作构造函数通过new关键字执行。这会引起一些语义上的困扰。
可以通过下面两种方式避免函数二义性:
ES6新增class只能作为构造函数,不能直接调用
在函数内部判断执行方式,阻止对函数的误用
functionfn(){if(new.target){//当前被new调用}else{//被直接调用,报错thrownewError('必须使用new调用')}}如何同时发起多个异步任务Promise有哪些并发方式发起多个异步任务的常用Promise方法主要有:
它们可以以并发的方式发起多个异步任务,然后统一处理结果。根据需要选择不同的并发方法。
Symbol是ES6新增的一种基本数据类型,表示独一无二的值。Symbol的特点包括:
通过Symbol可以生成一个独一无二的标识符,常用于定义对象的私有属性等场景。
可迭代协议(IterableProtocol)是ES6规定的一种约定,使得一个对象可以定义或定制它的迭代行为,也就是可以被for-of循环遍历。
具体来说,一个可迭代对象必须实现@@iterator方法,该方法返回一个带有next()方法的迭代器对象。next()方法每次返回一个包含value和done属性的对象,这样就可以不断获取序列中的下一个值。
只要对象实现了这个协议,即可被for-of、展开语法、解构等特性调用。内置类型如Array、String都实现了可迭代协议。
Array实现了可迭代协议,有默认的@@iterator方法,所以是可迭代的。
Object没有实现@@iterator方法,所以不是可迭代对象。
可以通过为Object定义@@iterator方法使其可迭代:
Object.prototype[Symbol.iterator]=function(){letkeys=Object.keys(this)letindex=0return{next:()=>{return{value:this[keys[index++]],done:index>keys.length}}}}这样就可以使用for-of遍历对象了。
可以通过两个方式实现对象解构赋值:
letobj={a:1,b:2,c:3}obj[Symbol.iterator]=function(){//返回[a,b]迭代器}const[a,b]=obj使用Object.entries和数组解构const{a,b}=Object.entries({a:1,b:2,c:3})[0]两种方式都可以实现从对象中获取指定属性作为解构目标。
实现大文件上传的方法:
利用File对象的slice方法,分片切割文件
计算切片大小,一般为几MB到几十MB
在前端通过循环切片,生成多个FileBlob对象
将Blob通过FormDataAJAX上传
后端收到切片后写入目标文件
所有切片上传完成,合并切片得到完整文件
这样通过分片上传可以支持大文件上传,不会消耗用户大量带宽和内存。
通过visibilitychange和pagehide等事件判断页面状态
需要处理页面切换、浏览器回退等情况。综合几种事件可以计算出准确的停留时长。
零宽字符是不占用可见宽度的Unicode字符,常见的包括零宽连词符、零宽ESPACE等。
零宽字符的常见用途包括:
它可以在不占用显示空间的情况下在文本中插入信息,适合各种标记、打点等用途。
WebComponents是一种通过原生JS实现可复用组件逻辑的技术规范,主要包含:
常见的资源懒加载实现方式:
监听浏览器资源加载进度常用的方法:
这样可以监听页面资源总体加载情况,以及每个资源的加载进度。
设计模式的七大原则:
这些原则是编写优秀面向对象程序的指导思想。
这个视项目而定,常见的设计模式包括:
要根据实际场景选择合适的设计模式。
依赖注入(DI)是一种设计模式,其核心思想是:
依赖注入的目的是提高模块的解耦性,通过依赖注入容器生成对象及其依赖并注入,而非对象内部直接建立依赖。
在Vue、Angular等框架中都有依赖注入的实现,用于构建服务等。
发布订阅模式和观察者模式的主要区别是:
发布订阅模式通过事件机制解耦,而观察者模式通常是同步的直接调用。
此外,观察者模式通常一对多订阅,而发布订阅模式可以一对多也可以一对一。
在webpack中,loader和plugin的区别在于:
例如,loader可以转换JSX,TS,而plugin可以打包优化、资源管理、环境变量注入等。
loader转换单个文件,而plugin更强大。
JavaScript模块化发展历程经历了以下阶段:
发展目标是实现可重用、松耦合的模块化开发与复用。
webpack处理import的关键步骤:
这样webpack实现了CommonJS到浏览器端的功能映射,通过import加载模块资源。
webpack实现动态导入的方法主要有两种:
import(/*webpackChunkName:"chunk-name"*/'./module')在optimization选项中配置splitChunks,实现代码分割:splitChunks:{chunks:'async'}webpack会将动态导入的模块单独打包为一个chunk。
require和import的主要区别有:
因此import适合在浏览器环境下使用,可以进行TreeShaking等优化。
webpack动态加载js模块的方式:
import(/*webpackChunkName:"module"*/'./module')借助第三方库如loadable-componentsimportloadablefrom'@loadable/component'constOtherComponent=loadable(()=>import('./OtherComponent'))利用ES6的动态import特性constimportComponent=async()=>{const{default:comp}=awaitimport('./OtherComponent');//Usecomp};它可以实现按需异步加载js代码,避免初始化加载全部模块代码。
webpack动态导入的常见使用场景:
动态导入可以按需加载代码、优化性能,主要用于对性能和包体积优化。
React常见的面试题包括:
需要重点掌握虚拟DOM、组件化、性能优化等方面。
HOC(Higher-OrderComponent)是React的高阶组件,参数为组件,返回值为新组件的函数。
它可以像容器一样包裹组件,用于抽象和重用组件逻辑。
常用场景:
HOC是一个reuse组件逻辑的方式,相当于组件的容器。
React中class组件和函数组件不同之处主要有:
React推荐使用函数组件代替class组件。
ReactHooks的优势主要有:
Hooks解决了类组件的痛点,是函数式编程思想在React的实现。
在React中,key的主要作用有:
key要有稳定、唯一、预测性强,从而提升渲染性能。
class组件的生命周期包含挂载卸载等多个阶段,主要有:
初始化阶段:constructor>getDerivedStateFromProps>render>componentDidMount
更新阶段:getDerivedStateFromProps>shouldComponentUpdate>render>getSnapshotBeforeUpdate>componentDidUpdate
卸载阶段:componentWillUnmount
错误处理:getDerivedStateFromError>componentDidCatch
函数组件通过Hooks可以模拟生命周期,常见的有:
挂载:useEffect(fn,[])等同于componentDidMount
更新:useEffect(fn)等同于componentDidUpdate
卸载:useEffect(fn,()=>fn)
错误处理:useErrorHandler
React的数据流动默认是单向的,常用的状态管理方式有:
Context:适用于简单的状态共享
Redux:集中存储共享状态,配合react-redux
MobX:利用观察者模式实现响应式状态
Recoil:Facebook推出的状态管理工具
Jotai:轻量的原子化状态管理
XState:基于状态机的库
根据具体需求选择合适的状态管理方案。
Redux的工作流程主要包含:
Store用来存储状态数据
Action用于描述事件
Reducer根据Action更新State
dispatch(action)触发Reducer并更新State
Subscription由Store.subscribe实现订阅
View通过订阅获取State并显示
Redux通过单一数据流形式构建可预测的状态管理。
Vue2与Vue3的主要区别包括:
采用Proxy代替defineProperty实现响应式
重写虚拟DOM,提升性能
CompositionAPI代替OptionsAPI
支持Fragment、Teleport、Suspense
源码采用TypeScript重构
移除keyCode作为v-on的修饰符
...
Vue3对框架进行了全面升级,性能和功能更优。
Vue中的computed实现了懒执行的缓存机制:
第一次访问时会计算值并缓存
如果依赖未改变,多次访问直接返回缓存值
依赖改变时才会重新计算
这样可以避免每次获取都进行高开销的计算,优化性能。
Vue中的data是一个对象,定义为函数返回的原因有几点:
组件可能被复用,每个实例需要各自的数据副本,避免共享造成冲突
data如果以对象形式定义,那么会在多个组件实例间共享这个对象
使用函数可以使每个组件实例获得data独立的数据副本
实现了数据的私有化,每个实例可以维护一份被返回对象的独立拷贝
这对实现组件数据的私有化非常重要。
Vue组件间常用的通信方式有:
props和自定义事件:父子组件通信
provide/inject:父组件向后代注入数据
EventBus:非父子组件间通过事件总线通信
Vuex:通过vuexmodule共享状态
mitt/rxjs:通过第三方pubsub库信号通信
根据场景选择合适的通信方式。
Vue2通过Object.defineProperty()实现响应式,不能检测到数组变化和新添加的属性。
Vue3使用Proxy代替,直接代理对象实现响应式。原理有:
Proxy可以检测数组变化和新增属性,但需要有原生支持。
Vue的diff主要分为三步:
patch:对比新旧VNode
patchVnode:处理VNode数据变化
updateChildren:比对子节点,使用key进行高效比较
优化方案:
这样可显著提高比较性能。
常见触发回流的CSS属性:
触发重绘的属性:
了解区分可以避免不必要的回流影响性能。
document中的常见触发回流的操作:
需要注意避免频繁读取引起不必要的重排。
v-if和v-show的区别在于:
v-if是条件渲染,切换时销毁重建节点
v-show是cssdisplay切换,只切换显示隐藏
v-if有更高的切换消耗,v-show有初始渲染消耗
v-if适合运营条件不大可能改变的场景
v-show适合频繁切换的场景
Vue生命周期函数的常见应用场景包括:
beforeCreate:定义数据观察者或初始化非响应式变量
created:异步请求数据,依赖DOM的操作
mounted:访问DOM元素,绑定事件
beforeUpdate:更新之前访问现有的DOM
updated:DOM更新后执行依赖DOM的操作
beforeUnmount:解绑事件等清理任务
errorCaptured:出错时的事件捕获处理
根据需要在不同生命周期执行所需逻辑。
Vue组件中常用的选项包括:
data:定义组件状态
methods:存放方法的哈希表
computed:计算属性
watch:监听数据变化
props:定义组件数据接口
components:局部注册的子组件
mixins:混入代码复用
directives:自定义指令
等等。这些选项定义了组件的各种逻辑。
Vue组件name属性的必写场景主要有:
在使用keep-alive缓存组件时,需要name区分缓存key
在DevTools调试时标识组件名称
在递归组件中,name可标识组件位置
和配合使用
vue-router的路由异步组件也需要name
主要是需要区分同类型组件,或与其他特性配合的时候需要使用name。
v-model的实现原理:
默认绑定input事件监听输入值
根据value更新data中的属性值
对应prop进行数据初始化和回显
一般配合value属性与输入值双向绑定
实现了数据和视图的双向绑定同步。
Vue2中的.sync修饰符的工作方式:
绑定一个更新父组件值的自定义事件
事件处理函数中调用组件用来更新绑定值的方法
监听自定义事件以更新父组件对应的绑定
这样实现了子向父的双向绑定,但是只适用于组件的prop。
Vue3的v-model变化:
nextTick的实现原理主要是:
这样可以批量更新DOM,优化重排重绘。
一个简单的nextTick实现:
functionnextTick(callback){//将回调推迟到下一个事件循环setTimeout(callback,0);}利用setTimeout将回调推迟到下一个事件循环,实现和nextTick类似的效果。
Vue3中的Composable是组合式API,意思是:
常用的Composable包括:
开发者也可以自定义Composable。它提升了代码的可重用性。
在Vue的v-for列表渲染中,key的作用主要有:
所以key要保证稳定、可预测、唯一,这对优化渲染性能很重要。
Vue中的函数组件指没有管理任何状态,仅负责接收prop并返回输出的组件。
与普通组件相比,函数组件:
应优先使用,可提高渲染性能。
JSX常用于React和Vue中作为语法糖,主要使用场景:
Vue中的jsx实现原理:
Vue选项API或组合API都可以配合JSX使用。
createElement主要参数包括:
返回创建的虚拟节点VNode,用于描述真实DOM。
exportdefault{inheritAttrs:false,props:[],emits:[],created(){console.log(this.$attrs);//获得透传属性console.log(this.$listeners);//获得透传方法}}这样可以方便地进行透传,实现组件的封装和复用。
Vue3中的setup实现原理:
创建一个新的Proxy代理对象作为响应式上下文
将props解构传入上下文,统计依赖
执行setup函数,返回函数或对象
将setup返回值挂载到render上下文
effect自动收集依赖,trigger触发响应
setup为组件提供了统一的响应式编程模型。
SameSite是一个Cookie的属性,用来提供第三方Cookie的跨站限制,主要作用是防止CSRF攻击。
它有两种模式:
Chrome最近在SameSite上有更新,开发者需要注意跨域请求所依赖的Cookie设置。
限制请求资源个数主要出于以下考虑:
所以对资源请求数量进行了限制。
HTTP/2协议使用了多路复用机制,在一个TCP连接上可以并行交错的传输多份请求和响应数据。
这样就不存在明显的请求数限制,并发请求和资源下载更加高效。
常见的浏览器存储方式包括:
Cookie:存储少量数据,每次请求都会发送到服务器
LocalStorage:键值对存储,存储在客户端,容量较大,不会自动发送到服务器
SessionStorage:与LocalStorage类似,但数据只存在于当前会话,窗口或标签关闭则清空
IndexedDB:键值对存储,可以存储结构化克隆对象,大容量存储
CacheAPI:缓存请求或其他数据,作用域为窗口或者服务工作器
WebSQL:基于SQL的关系型数据库,已不推荐使用
根据需要选择合适的浏览器存储方式。
session、cookie和sessionStorage的区别主要在:
作用域:cookie可跨域名,其他两者当前源限制
存储大小:cookie4kb左右,sessionStorage约5MB,localStorage更大
请求发送:cookie会自动发送,后两者仅在客户端
用途:cookie常用于存储用户信息,localStorage用于持久保存数据
API访问:cookie直接document.cookie,后两者直接localStorage对象
根据实际需要选择合适的浏览器存储方式。
常用的页面关闭事件监听方式:
window.onbeforeunload,页面关闭前执行
window.onunload,页面完全卸载时
document.addEventListener('visibilitychange',fn)监听visibilityState
或者结合pagehide等事件判断页面状态。
需要注意浏览器兼容性问题,跨浏览器支持比较复杂。
浏览器跨页面签数据共享的实现方式:
localStorage或IndexedDB存储数据,不同页签可共享
BroadcastChannelAPI,用于页面间通信
ServiceWorker也可以用于数据共享
父级域名设置cookie,页面可共享读取
LocationHash也可进行有限数据共享
可以根据需要选择合适的跨页签数据共享方案。
Chrome性能面板常看的关键指标:
FPS:动画或滚动时每个帧的耗时,越高越好
JSPROFILE:hotpath等JS执行分析
这些指标可以分析页面的渲染、加载、资源、内存等性能。
页面卡顿的常见原因:
图片未压缩,资源体积太大
CSS/JS文件体积过大
重复DOM操作引起频繁重排重绘
内存泄漏导致频繁GC
频繁网络请求阻塞页面
解决方案:
代码优化,减少DOM操作
图片懒加载
避免大体积资源导致主线程阻塞
合理使用webworker分离线程
预编译、缓存等手段
需要定位具体原因进行有针对性优化。
页面滚动卡顿的原因:
重绘重排频繁导致计算过慢
渲染区域过多节点,需要大量计算样式
触发频繁的GPU合成
解决方式:
optimize-css-assets-webpack-plugin压缩css
shouldComponentUpdate优化渲染
减少不必要的重排重绘
避免过深DOM树
使用will-change提示浏览器提前合成层
主要还是需要定位优化重绘重排和页面合成。
常用的前端单元测试框架包括:
Jest:Facebook出品,适合React应用
Mocha:简单灵活,搭配Chai使用
Jasmine:语法简单清晰的BDD框架
Karma:运行测试用例的TestRunner
Cypress:端到端测试框架
puppeteer:HeadlessChrome测试
可以根据具体项目需求选择合适的框架。
前端常见的安全问题包括:
XSS攻击:注入恶意脚本,盗取信息
CSRF攻击:跨站请求伪造攻击
点击劫持:修改链接地址欺骗用户
暴露信息:如抓包获得敏感信息
中间人攻击:插入非法代理截获用户信息
需要对用户输入输出进行校验,token验证身份,HTTPS传输,代码混淆加密等来保证安全。
SSR即服务器端渲染,是一种使网站内容在服务器端渲染成静态HTML字符串,再发送给客户端的技术。
其工作流程是:
服务器接受请求,交给对应的路由处理
路由根据当前URL返回需要渲染的组件
在服务器使用VDOM生成组件静态HTML字符串
将HTML发送给客户端,客户端接收并显示
SSR可以有效改善SEO并加快首屏加载速度。
SSR和SSG的主要区别有:
SSR是请求时服务器渲染,SSG是预构建静态页面
SSR适合频繁更新的数据,SSG适合静态或不经常更改的数据
SSR有更好的首屏性能,SSG对服务器压力小
SSG更简单,可以部署在CDN上,而SSR需要对应服务器
根据应用场景选择合适的静态站点生成方案。
常用的SSR框架包括:
Next.js:React的SSR框架
Nuxt.js:基于Vue的服务端渲染框架
AngularUniversal:Angular的服务端渲染实现
RubyonRails:服务端语言实现的SSR
Laravel:PHP框架整合了SSR能力
可以根据具体使用的前端框架选择对应的SSR解决方案。
Nuxt3和Nextjs在SSR实现上的主要区别有:
Next直接输出HTML字符串,Nuxt通过vue-server-renderer节点流生成