-
如何减少重排和重绘
- 读写分离
- 样式集中改变
- dom离线
- 父元素先设置display:none, 在子元素上进行css修改,然后再把display设置回来
- documentFrament创建一个碎片,在上面进行完成操作后在加入到dom中
- 使用absolute或者fixed使得脱离文档流,成为body元素的另一个子元素
-
BFC,如何触发BFC?
BFC是块级元素布局的区域,也是浮动元素和块级元素交互的区域。
触发BFC的条件:
- body根元素
- float设置成none以外的
- display为inline-block、flex、flow-root(没有任何副作用的方式)
- overfloat设置为visible以外的值
- 绝对定位的元素
-
伪类和伪元素
-
伪类是元素在某个特定状态的时候,添加他的样式
link hover active visited checked focus first-child first-of-type last-child last-of-type nth-child nth-of-type
-
伪元素是添加不在文档树的元素,添加他的样式
after before first-letter first-line selection
-
-
position
- static
- absolute
- fixed
- relative
- sticky
-
display
- none
- inline
- inline-block
- block
- flex
- grid
- flow-root
-
display:none 和 visible: hidden的区别
- render树上没有display:none的元素;二有
- 会引发回流,进行重新渲染;只会引发重绘
- 不是继承属性,子元素会消失;是继承属性,子元素可以设置显示
-
选择器的优先级和权重值
!important 无限大 内联1000 id选择器100 class选择器、属性选择器、伪类10 标签选择器、伪元素1 通配符0
-
介绍flex布局
flex布局分为主轴和副轴,flex容器的子元素在主轴上依次布局,默认是主轴方向是row、不换行。
flex容器:
flex-warp:是否换行
flex-direction:flex主轴方向
flex-flow:两者的结合
justify-content:主轴的对齐方式(flex-start, flex-end,center,space-between,space-around)
align-items: 副轴的对齐方式(flex-start,flext-end,center,stretch)
align-content:多个轴线的对齐方式。(flex-start,flex-end,center)
flex具体项目:
order:顺序
flex-grow:多余空间如何分配
flex-shrink:缩小的比例
flex-basis:主轴方向初始大小
flex:flex-grow flex-shrink flex-basis
align-self:更改自己的对齐方式
-
@import 和 link的区别
- 归属问题:link属于html,@import属于CSS
- 加载问题:link可以和html同时加载,@import需要等页面全部加载完毕之后,再被加载
-
async和defer的区别和联系
联系:都是异步加载js文件的
区别:
- async可能在任何时刻运行,defer在页面加载完后,
- async修饰的多个js文件执行顺序不确定,defer的执行顺序是确定的
-
CSS常用的优化策略
-
文件压缩
-
除去多余的css
-
内联关键的css
-
异步加载非关键的css
link中ref设置为preload,as设置为style
-
减少使用@import,因为会阻止css异步加载
-
减少重绘和重排
-
-
Css3新特性
- 圆角,阴影
- 文字特效,渐变
- 旋转,缩放,定位,倾斜
- rgba, ::selection
- 媒体查询
-
css预处理器的作用
提供css的复用机制,减少冗余代码,提高css的可维护性
-
link rel和as
rel:
- preload文加载完之后去加载,有较高的优先级
- prefetch 缓存数据,优先级较低,竟可能的多使用,但是会费用户的流量
- preconnect 预先建立tcp连接,(tls连接)浏览器不一定执行
- dns-prefetch预先解析一个域名,浏览器不一定执行
as:
- style
- script
- font
- fetch
-
CSS动画的优化
- 尽可能多的使用硬件能力,比如3d变形
- 尽可能让元素脱离文档流
- 减少重排和重绘
-
移动端设配的方案
- @media
- rem
- px+viewport
- vh/vw
-
基本类型和引用类型的区别
- 基本类型存储在栈中,引用类型存储在堆中
- 基本类型按值访问,引用类型按地址访问
-
Map 和WeakMap的区别
weakmap的键是对象,map可以是任何类型的数据,weakmap是弱引用,只要对对象的引用删除,就会被垃圾回收机制随时清理,weakmap没有clear(),也不能遍历
-
深拷贝和浅拷贝
-
浅拷贝
function isArray(val){ return Object.prototype.toString.call(val).includes("Array"); } function shallowCopy(src){ let target = isArray(src) ? []: {}; for(let key in src){ if(src.hasOwnProperty(key)){ target[key] = src[key]; } } return target; }
-
深拷贝
function deepClone(src){ return JSON.parse(JSON.stringify(src)); }
-
-
拥有symbol.iterator()数据类型有哪些
- String
- Array
- Map
- Set
- arguments
-
typeof可以判断哪些类型
- Number、String、Boolean、Bigint、Undefined、Object、Function,Symbol
-
判断的时候的类型转换
- boolean转换成数字
- 只有一个string,另一个为数字,那么string转换为数字
- 只有一个是对象,调用valueof的方法,如果不是基本类型,调用toString()转换成字符串,都是对象,比较地址
- 同时定义null == undefined,null和undefined和其他类型比较总是false,NaN也总是false
-
运算时候的类型转换
- 有string,那么转换成string
- 有数字
- 另一个是基本类型,转数字
- 引用类型,转string
-
有符号整数的最大数
Math.pow(2, 53)-1
-
new的具体过程
- 创建一个对象
- 绑定this
- 执行构造函数
- 返回对象
-
继承的方法
-
原型链继承,
-
借用构造函数,无法继承函数和父类原型
-
组合式继承,两次调用构造函数
-
原型继承
-
寄生式继承,函数无法公用
-
寄生组合式继承
function Parent(name){ this.name = name; } function Child(age,name){ Parent.call(this,name); this.age = age; } let temp = Object.create(Parent.prototype); temp.constructor = Child; Child.prototype = temp;
-
class extends
-
-
闭包
函数可以记住并访问所在的词法作用域,在此法作用域外执行的时候,产生了闭包
优点:
- 避免污染全局变量
- 模块化
- 防止内存泄漏
-
js的垃圾回收机制
- 对于栈来说,通过改变esp来进行销毁内存的
- 对于堆来说,V8把内存分为新生代和老生代,对于新生代,采用副垃圾回收器,将内存分为空闲区和对象区,每次遍历对象区,如果有被引用,就将其复制到空闲区,接着翻转两个区域就可以了,元素经历两次赋值,就将其放到新生代,对于老生代,采用从根元素标记,将能到达的元素称为活动元素,不能到达的就标记为垃圾元素,为了避免内存碎片,将存活的对象都向一端移动
-
throttle debounce
function throttle(fn, delay){ let time; return function(){ if(time) return; time = setTimeout(()=>{fn.apply(this, arguments)}, delay); } } function debounce(fn, delay){ let time; return function(){ if(time){ clearTimeout(time); } time = setTimeout(()=>{ fn.apply(this, arguments); }, delay) } }
-
事件循环
js是单线程的,先执行同步任务,在执行异步队列中的任务,异步任务分为宏任务和微任务。所有异步任务有了结果之后就在自己的相应的队列后面添加事件,当同步任务执行完之后,会检查当前还有没有微任务,有的话执行微任务,没有的话执行宏任务,一直循环即可。
常见的宏任务: setTimeout、setIntervel,UI渲染、script代码、IO操作
微任务:promise、mutationObserver
-
并发限制下载
function LimitedTask(){ this.task = []; this.maxSize = 10; setTimeout(run,0); } LimitedTask.prototype.run(){ if(this.task.length === 0){ return; } let num = Math.min(this.maxSize, this.task.length); for(let i = 0 ; i < num; ++i){ this.maxSize--; let task = this.task.shift(); task().then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }).finally(()=>{ this.maxSize++; this.run(); }) } } LimitedTask.prototype.push(task){ this.task.push(task); }
-
函数柯里化
function curry(fn){ return function curryFun(...args1){ if(args1.length >= fn.length){ fn.apply(this, args1); }else{ return function(...args2){ return curryFun.apply(this,args1.concat(args2)); } } } }
-
浏览器不同页面的通信
- BroadcastChannel,onmessage函数获得传递的值,通过postMessage函数传递参数
- indexDB
- localStorage
-
import 和require的区别
require:
- 所有代码在模块作用域,不会污染全局
- 模块可以加载多次,但是结果被缓存,要重新加载需要删除缓存
- 加载顺序就是代码出现的顺序
import:
- import是静态加载的
- import读入的变量会提升且是只读的
- import是静态执行的,singleton的模式
区别:
-
commonjs的模块输出的是值得拷贝,es6输出的是值得引用
-
commonjs是运行是加载,es6是编译时输出接口
- doctype
- 严格模式,采用w3c规定的标准解析文档
- 混杂模式,用浏览器自己的方式解析文档
- html5的新特性
- 语义化标签
- svg canvas
- video、audio
- web worker:创建一个线程来运行计算密集型或者高延迟的js代码,主要脚本需同源,不能获取window,document对象,与主线程可以通过postMessage来通信
- web storage
- webSocket,通过事件驱动来接收相应,无需要轮询
- 如何优化seo
- 设置好title description keyword标签
- 图片设置alt标签
- 提高服务器相应速度
- 减少iframe的使用
- 对于SPA,采用服务器渲染的方式
-
web worker
- js是单线程的,这意味着同一时间只能执行一个代码,但是html5在浏览器的层面上,提供了运行js的线程,使得脚本可以脱离主线程单独运行。
-
用户登录的几种方式
-
cookie和session的方式
缺点: 存放sessionid,导致服务器压力过大,sessionid存放在cookie中,无法避免csrf攻击
-
token
- 服务器端不用存放token
- token可以放在任何地方,提高了安全性
常用的token是JWT
JWT由三个部分组成:header、payload、signature,header保存加密算法,payload保存时间,signature是签名,由header中的加密算法对header和payload进行base64的转义再和服务器私钥加密组成的。当服务器要验证的时候,可以通过header、payload和自己的私钥加密,查看签名是否一致,来验证token是否被修改,以及通过payload来判断是否登录失效
-
-
判断用户机型
navigator.userAgent
-
禁用了cookie可以访问session嘛
- 追加到url中
- 在数据中包含sessionid
- 在http请求头中包含sessionid的字段
-
get和post的区别
- get将参数放在url中,并且有数量的限制,参数必须是ASCII字符,会被浏览器主动cache
- post将参数放在request的body中,没有长度的限制,可以是任何类型,不会主动cache
-
chrome浏览器的进程
- 浏览器进程
- 渲染进程
- GPU进程
- 网络进程
- 插件进程
-
Vue的双向绑定
采用数据劫持和订阅发布者模式,整体来说有4个核心类,observer、dep、watcher、compiler,在vue2中,observer采用Object.defineProperty,对data中的数据绑定getter和setter,进行数据劫持,这个绑定需要判断数据的类型,如果是object那么需要递归绑定,如果是数组,那么需要劫持push等多个函数。同时每一个属性都有一个dep来保存所有watcher订阅,在每一次get,通过判断dep.target来向dep中添加watcher,set的时候调用dep的notify实现所有watcher的更新。在compiler中,对模板进行数据的绑定,同时在绑定每个指令或者{{}}这样的数据的时候,创建一个watcher,设置dep.target为自己,调用get来添加到相应的dep中,这样就可以实现数据的双向绑定了。vue2采用object.defineProperty有个缺点就是无法绑定新添加的数据,或者通过下标访问数据。所以在vue3中,将其替换成了proxy。
-
Vue computed 和 watch的原理
- computed数据通过对data中的数据监听,来修改自己的dirty位,当自己的dirty位为true,就调用自己的getter函数,更新value属性,否则就直接从value中取值
- watched只是在属性的dep后面增加了一个watcher,当数据更新的时候调用这个更新函数
-
Vue nextTick的原理
先判断浏览器是否支持promise,当支持promise的时候,在异步队列中添加执行nextTickHandle的promise,这样就可以实现在更新dom之后执行自己的nextTickhandle了,当promise不被支持的时候,就采用setTimeout,但是这样会有不必要的渲染,造成性能一定的浪费。
-
子组件为何不能修改父组件的props,警告如何发布的?
- 在vue中,数据是单向流动的,这样有利于数据观测,在definereactive中,set函数中,判断是否在生产环境,如果在生产环境中,那么判断数据源是不是updatingChildren,是的话,就发出警告。
-
Vue diff算法
-
只进行同层比较
-
在旧的和新的vnode的两侧各有一个指针,每一次判断这四个指针指向的vnode是不是同一个vnode,这里就有5种情况,执行相应的操作,指导新的或者旧的指针起始指针越过末尾指针,那么就结束。结束后判断新旧vnode的长度,不一样长,就做相应的删除和插入即可。
5种情况的详细描述:
- 新的vnode和旧的vnode起始指针一样,那么直接patch,起始指针都加加
- 新的和就得vnode的末尾指针一样,那么在新末尾节点处patch,指针都减减
- 新的vnode的起始指针和旧的vnode的末尾指针一样,那么创建的元素放在旧vnode的起始指针处,vnode起始指针加加,旧vnode末尾指针减减
- 旧vnode的起始指针和新vnode的末尾指针一样,那么在新vnode末尾指针处添加vnode,
- 根据旧vnode的key值,以index为value的hash表中去寻找当前新vnode的下标,找到就patch同时将这个创建的dom移动到旧vnodeindex对饮dom的前面,否则就创建添加,新vnode加加
-
-
spa的理解和优缺点?
spa就是在web初始化页面的时候加载html、css、js,在用户交互的过程中不会引发页面的跳转或者重新加载,通过路由的方式,实现html的替换,从而和用户交互
优点:
- 不需要对页面重新加载或者跳转,速度快
- 减少了服务器的压力
- 实现前后端分离
缺点:
- 实现前后跳转,需要自己进行堆栈管理
- 首页加载慢
- 不利于seo
-
vue的生命周期
- beforeCreate:创建了vue实例,但是不能访问data method computed、watch
- created: 创建了vue实例,可以访问data methods computed watch,并且这个时候要访问dom,需要放在nexttick中
- beforemount: 编译template成render函数
- mounted:已经挂载到dom和渲染
- beforeUpdate: 在更新之前
- updated:完成dom的重新渲染和打补丁
- beforeDestroy: 在组件销毁之前,可以访问所有属性
- Destroyed: 组件销毁之后
- actived: keep-alive激活的时候调用
- deactived: keep-live 停用的时候调用
-
vue keep-alive
keep-alive是抽象组件,他并不会作为节点渲染在界面上。他用来缓存组件,有3个属性,include设置白名单,exclude设置黑名单,max设置最大存储的个数,当缓存的个数超过最大个数,那么就采取LRU的机制,替换组件
-
路由模式
- hash:location上的hash会返回USVString,包括url、#后面的标识符。标识符有以下特点,#后面的标识符不会传送给服务器,标识符改变,浏览器只会跳转到相应的位置,页面不会重新加载,修改标识符也会在浏览器中增加一个记录,点击回退可以返回上个记录。
- history:通过pushState,replaceState来修改url,触发popState事件来加载页面的,需要服务器配合
- abstract模式
-
axios
- 支持浏览器和node.js
- 支持promise风格的封装,
- 支持拦截请求和响应
- 取消请求
- 防止CSRF
- 自动转换json数据
-
vue-router中router view如何渲染
通过Vue.observable在router实例上创建一个保存当前路由的监控对象current,当地址变化的时候,修改监控对象,router-view中的组件监听current对象的变化后,获取用户注册相应的component,通过h函数渲染成vnode,实现视图更新
-
v-if 和v-show的区别
- v-if通过控制dom节点是否存在来元素的显隐的,v-show通过控制display是否是none来控制元素显隐的
- v-if默认是惰性的,只有当v-if是真的时候,才会进行编译,v-show在任何时候都会编译缓存的
- v-if切换消耗高,v-show首次渲染消耗高
-
vue-router路由解析的过程
- 导航激活
- 在失活的组件中调用beforeRouteLeave
- 在全局调用beforeEach
- 在更新的组件中,调用beforeRouteUpdate
- 在路由配置中,调用beforeEnter
- 解析组件
- 在激活的组件中调用beforeRouterEnter
- 调用全局的beforeResolve
- 导航被确认
- 调用全局的afterEach
- 更新dom
- 调用激活组件中beforeRouteEnter中的next,创建的组件作为他的参数
-
template转化成render函数的过程
- template将模板字符串转化成element AST
- 将AST转化成静态节点标记,用于虚拟dom的渲染优化
- 将AST转化成render函数的字符串
-
有哪些loader
css-loader、style-loader、eslint-loader、image-loader、file-loader、url-loader、vue-loader
-
有哪些plugin
html-webpack-plugin、servicework-webpack-plugin、minicssExtractPlugin
-
loader和plugin的区别
loader:本质是一个函数,webpack只认识js、json文件,通过loader可以将各种格式的文件转换成webpack可以理解的文件。
plugin:本质上,他是一个js的对象,apply方法会被webpack compiler调用,webpack在不同的生命周期中会广播出许多事件,plugin通过监听这些事件,改变最终的输出结果
-
webpack的构建流程
- 初始化参数。从配置文件和shell命令中获取合并参数
- 开始编译。通过上步获得的compiler对象,加载所有插件,执行对象的run函数开始编译
- 确定入口。通过entry确定所有的入口文件
- 编译模块。从入口文件出发,用loader对所有的模块进行编译,并且对模块中的模块进行编译,直到所有模块都编译结束
- 完成模块编译。所有模块都已经编译成最终文件和依赖关系,
- 输出资源。根据入口文件和模块的依赖关系,组成一个个由多个模块组成的chunk,再把chunk组成最终的单独文件加入到输出列表中,这步是最后修改文件的机会
- 输出完成,将文件输出到文件系统中
-
webpack开发中,用过哪些可以提高效率的插件
- webpack-dashboard展示相关的打包信息
- SMP,分析loader和plugin在打包过程中的耗时。
-
source map怎么用
webpack 最终生成的代码人是无法读的,通过source map将最终的代码映射会源代码。
通过nginx为白名单设置.map文件
-
文件监听的原理
可以通过后面加上--watch或者在配置文件中添加watch:true的方式启动文件监听
原理:
通过文件轮询的方式访问文件的最后修改时间,如果发生了变化,在aggregateTimeout时间后告诉监听者,进行文件更新
-
webpack HMR的原理
webpack在编译期间为每一个需要热更新的entry添加了热更新代码,实际上是与服务器端通信的eventSource,在第一次打开页面的时候,页面和服务器端建立websocket,并且服务器向客服端推送hash值,当监听到文件变化的时候,服务器推送hash值,客服端收到hash值后,对比变化,通过ajax向服务器请求文件列表和hash值,之后通过jsonp请求获得chunk的增量更新,之后通过HotModulePlugin提供的api实现浏览器的无刷新更新。像vue-loader也是通过这个api的。
-
文件指纹
hash:根据整个文档来设置hash的,只要有一个文件被改动了,那么所有文件都会被重命名
chunkhash:他根据不同entry进行依赖文件更新,生成的chunk来生成对应hash值。
Contenthash:文件内容不变,hash值不变。