1.10w数据渲染
1.数据分页+setTimeout
2.requestAnimationFrame减少重排代替setTimeout
3.requestAnimationFrame+document.creatDocumentFragment 先插入faragment之后一次插入,减少操纵dom的次数
2.原型链与继承
[[Prototype]]等同于_proto_(原型链)
只有函数才有原型
这个狗屎js的原型原型链思路其实跟java差不多
都是一个根类 一层层的继承细分下来的
原型就是那个顶级类
3.async与await串行与并发
能够让异步代码看起来像同步代码,方便控制顺序,线程休眠
async await无法处理并发
promise.all()可以处理并发
4.Vue面试题
1.虚拟dom是什么 ---js对象 -> 下一步创建真实dom document.createElement() ->三要素 target目标元素(必须),attr属性,children子节点
1.1用来做什么 -> 将直接操纵dom(影响性能)
1.2如何提升vue的渲染效率的 -> js对象比较(执行效率上的问题) diff算法对比不同处
2.Vue data()为什么是一个函数? ->闭包,用到了数据的私密性
2.2 延长变量的生命周期
3.Vue父子组件生命周期加载执行顺序
Vue2生命周期 创建前后 挂载前后 更新前后 销毁前后
父beforeCreate->父created->父beforeMounted->子beforeCreate->子created->子beforeMounted->子moounted->父mounted
在父组件在被挂载到根组件之前必须保证子组件先挂载到父组件上
4.promise概念与应用
构造函数立即执行(同步执行),构造函数只执行一次
.then()异步执行,可多次执行
手写promise函数
1/基础框架
function myPromise(){
let self = this
self.status = 'pending' //状态
self.value = null //成功状态初始化
self.reason = null //失败状态初始化
self.onFulfilledCallbacks = []
self.onRejectedCallbacks = []
//成功回调
function resolve(value){
//状态处理 等待=>成功/失败(状态不可逆转)
if(self.status === 'pending'){
self.value = value
self.status = 'fulfilled'
//状态改变,依次取出
self.onFulfilledCallbacks.forEach( (item)=> item(value))
}
}
//失败回调
function reject(reason){
if(self.status === 'pending'){
self.reason = reason
self.status = 'rejected'
self.onFulfilledCallbacks.forEach( (item)=> item(reason))
}
}
//导出
try{
excutor(resolve,reject)
}catch(err){
reject(err)
}
}
2/.then方法
myPromsie.prototype.then = function(onFulfilled,onRejected){
onFullfilled = typeof onFulfilled === 'function' ? onFulfilled : function(data) {resolve(data)}
onRejected = typeof onRejected === 'function' ? onRejected : function(err){throw err}
//发布订阅模式(.then执行时状态还未改变)
if(this.status === 'pending'){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
V-model原理
类似构造函数getset方法?+订阅发布至相关组件
<input id="username"/>
<p id="iName"></p>
Object.definedProperty(obj,"username",{
get: function(){
console.log("取值")
},
set: function(val){
console.log("设置值")
document.getElementById("iName").innerText = val
}
})
//通过监听keyup时间赋值给obj.username
document.getElemnetById("username").addEventListener("keyup",function(){
obj.username = event.target.value
})
data()为什么是函数
闭包 -> 保证每个组件都有自己的私有作用域,确保各组件不会相互干扰
纯对象 let obj = {}会有干扰
v-if 重新渲染
v-show display none (不能用于权限操作)
diff算法中的patch()方法
虚拟dom生成的三个要素 1.目标元素 2.
1.初始化patch(container,vnode)
2.更新update(vnode,newVnode)
$nextTick()
dom更新之后延迟回调,让修改后的data值渲染到dom更新之后
单页与多页
单页SPA: 只有一个主页面的应用
组件 -> 页面片段
跳转 -> 刷新局部资源
场景 -> PC端
优点: 体验好,快,改变内容不用加载整个页面,前后端分离
缺点: 不利于优化,初次加载比较慢,页面复杂度高
多页: 整页刷新
v-if与v-for
v-for优先级更高
Vue-router与location.href有什么区别
locaton.href: 简单方便,刷新页面(跳外链)
Vue-router: 实现按需加载,减少dom消耗(底层封装原生history)
5.JS相关
ES6 var let const
全局变量不会被回收
//声明提升
console.log(num)//undefined
var num = 123
运用结构的方法将两个值互换
let a = 1
let b = 2
[a,b] = [b,a]
去重
let item = [...new Set(arr)]
闭包 -> 方法里返回一个方法
-> 延长变量的生命周期,创建私有环境
-> 都有独立的词法作用域
-> 面向对象 数据的隐藏和封装
-> 闭包会常驻内存 慎用闭包
防抖与节流
防抖(debounce)是指在一定的时间间隔内,将多次触发变成一次触发。 例如,当用户输入时,只有在停止输入一段时间后才执行相应的函数。(按钮点击)
demo.addEventListen('input',antiShake(demo,2000))
function antiShake(fn,wait){
let timeOut= null
return args => {
if(timeOut) clearTimeout(timeOut)
timeOut = setTimeout(fn,wait)
}
}
function demo(){
console.log('test')
}
节流(throttle)是指不管触发多少次事件,都只在固定的时间间隔执行事件处理函数。例如,当用户滚动页面时,每隔一秒钟执行一次滚动事件的回调函数。 (提交表单,高频监听事件,上拉刷新等)
// 第一个参数是需要进行防抖处理的函数,第二个参数是延迟时间,默认为1秒钟 function debounce(fn, delay = 1000) {
// 实现防抖函数的核心是使用setTimeout
// time变量用于保存setTimeout返回的Id
let time = null function _debounce() {
// 如果time不为0,也就是说有定时器存在,将该定时器清除
if (time !== null) {
clearTimeout(time)
}
time = setTimeout(() => { fn() }, delay) }
// 防抖函数会返回另一个函数,该函数才是真正被调用的函数
return _debounce }
防抖和节流的区别是:防抖是延迟执行,节流是间隔执行。
forearch与map
map和foreach都是数组的方法,用于遍历数组中的每个元素。
map会返回一个新的数组,其中每个元素是原数组中对应元素经过回调函数处理后的结果。而foreach不会返回任何值,只是执行回调函数。
map和foreach都可以接受三个参数:当前元素、当前索引和原数组。但是map可以链式调用,而foreach不能。
map和foreach在性能方面没有明显的差异,但是for循环通常比它们快一些。
js递归求和
function add(num1,num2){
let num = num1 + num2
if(num2 + 1 > 100){
return num
}else{
return add(num,num2+1)
}
}
传值与传址
基本数据类型和引用数据类型(对象,数组,函数)
判断数据类型
1. typeof 判断基本数据类型
2. instenceof
3. Object.prototype.toString.call(data)
数组常用方法
1. join -> array.join("符号") 把数组转为字符串,不改变原数组
2. split -> string.split("符号") 以字符为标准分割字符串,返回新数组
3. push -> array.push(参数) 把参数添加到数组尾部,改变数组,返回最新的数组长度为数字
4. pop -> 删除尾部一个元素,改变原数组,返回删除的那个元素
5. unshift -> 把参数添加到数组头部,改变原数组,返回最新的数组长度为数字
6. shift -> 删除数组头部一个元素,改变原数组,返回删除的元素
7. reverse -> 反转数据,更改原数组
8. sort -> 排序 数字 <0升序 >0降序
9. contact -> 数组拼接 arr1.contact(arr2)不改变原数组,返回拼接后的新数组
10. splice -> 截取/删除数组 arr1("开始下标","截取的长度","填充替换的数组")
11. slice -> 截取/删除数组,不改变原数组
ref三种用法
1.加在普通元素上,this.ref.name获取到dom元素
2.加在子元素上 this.ref.name获取组件砬,可以使用组件的所有方法
3.如何利用v-for和ref获取一组数组或者dom节点
第1,2种情况
this.$refs 获取了所有设置ref属性的元素(如有重名那么会被覆盖)如果是普通的元素那返回的是一个普通的dom,如果ref放在[vue]组件里,那么返回的是一个vue对象,这个对象包含了这个组件的各种信息
第3种情况
组件设置了ref属性,使用this.$refs.G1
6.原型prototype与原型链_proto_([[prototype]])
原型 -> 函数特有
原型链 -> 都有
简述Vue2 ,3的区别
在数据的双向绑定,即响应式原理上,vue2利用的是object.defineprotoy对数据进行劫持(get/set),结合发布订阅模式的方法实现.但defineProperty只能监听某个属性,不能对全对象进行监听
Vue3使用了es6中的proxy和reflect API对数据代理,通过reactive,函数给每一个对象都包一层proxy,通过proxy监听属性的变化,从而实现对数据的监控
vue2中的new出的实例对象,挂载着所有属性和方法,无论实际用没用到都会运行一遍,消耗内存
Vu3可以使用ESModule按需引入,减少内存消耗,减少加载时间
vue2单组件文件要求一个根元素
vue2选项式api vue3组合式api
vue3 setupapi是围绕beforeCreate和created生命周期钩子运行的
作用域插槽
父
<current-user>
<tempalte v-slot:default="slotProps">
{{slotProps.user.age}}
</template>
</current-user>
import CurrentUser from '/CurrentUser'
子
<slot :user="user">
{{user.name}}
</slot>
data(){
return{
user:{}
}
}
webpack魔法代码
components:{
Test:() => import(/* webpackChunkName:"test"*/ './component/Test)
}
structuredClone深拷贝(函数无法拷贝)
浅拷贝和深拷贝的本质区别
浅拷贝只复制对象的引用,而不复制对象本身。这意味着浅拷贝的对象和原始对象共享相同的内存地址,如果修改其中一个,另一个也会受到影响
深拷贝则完全复制对象及其嵌套的子对象。这意味着深拷贝的对象和原始对象是完全独立的,它们不共享相同的内存地址,如果修改其中一个,另一个不会受到影响
computed和watch的区别
computed是计算属性,watch是监听data中的数据变化
computed支持缓存,依赖的属性值发生变化,计算属性才会重新计算,否则用缓存,wtach不支持缓存
computed不支持异步,watch支持
computed是第一次加载就监听,watch是不监听
computed函数中必须要有return,watch不用
vuex中的数据会丢失吗?怎么解决?
1.保存到浏览器缓存中 cookie localstorage sessionstorage
2.页面刷新再次请求数据,动态更新获取数据
监听浏览器的刷新事件,在刷新前把数据保存到sessionstorage里,刷新后请求数据,请求到数据用vuex,如果没有就用sessionstorege里的数据
如何解决刷新后二次加载路由
1.window.location.reload() (有闪屏的情况)
2.matcher 新老路由比对
conster router = createRouter()
export function resetRouter(){
const newRouter = createTouter()
router.matcher = newRouter.matcher
}
vue动态路由
要在路由配置里设置meat属性,扩展权限相关的字段,在路由导航守卫里通过判断这个权限标识,实现路由的动态增加和跳转
根据用户登录的账号,返回用户角色
前端再根据角色,跟路由表的meta.role进行匹配
把匹配到的路由形成可访问的路由
路由拦截怎么实现的
路由拦截,axios拦截 -> 需要在路由配置中添加自定义字段,用于判断是否需要拦截
{
name:'index',
path:'/index'
component:Index
meta:{
requirtAuth:true
}
}
router.beforeEach((to,from,next)=>{
if(to.meta.requirtAuth){
if(store.state.token){
next()
}else{
}
}
})
vue路由是怎么传参的
params传参
this.$router.push({name:'index',params:{id:item.id}})
this.$router.params.id
路由属性传参
this.$router.push({name:'/index/${item.id}'})
路由配置
query传参(可以解决页面刷新参数丢失的问题)
this.$router.push({
name:'index',
query:{id:item.id}
})
vue路由的hash模式和history模式有什么区别
1.hash路由地址上有#,在 url 中的 # 之后对应的是 hash 值, 其原理是通过hashChange() 事件监听hash值的变化, 根据路由表对应的hash值来判断加载对应的路由加载对应的组件
2.在访问二级页面的时候,做刷新操作,会出现404错误,
3.hash模式支持低版本浏览器,history不支持因为是h5新增api
4.hash不会重新加载页面,单页面应用必备
5.history有历史记录,h5新增了pushState和replaceState()去修改历史记录,并不会立刻发送请求
6.history需要后台配置
Vue路由有两种模式:hash模式和history模式。其中,hash模式是在地址栏URL上有#,用 window.location.hash 读取。而history模式没有#,是个正常的url,适合推广宣传。
在使用hash模式时,浏览器不会向服务器发送请求,而是在本地直接通过hash值的改变来实现前端路由。而history模式则是通过HTML5的History API来完成的,可以在不刷新页面的情况下改变URL。
总的来说,两种模式的区别在于URL的形式不同,以及history模式可以通过HTML5的History API来完成URL的改变,而hash模式则是通过hash值的改变来实现前端路由
hash模式的优缺点
优点:只需要前端配置路由表,不需要后期参与
兼容性好
hash值改变不会向后端发送请求,万千属于前端路由
缺点:hash值前面需要加#,不符合url规范,也不美观
当 URL 改变时,页面不会重新加载。hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器`只会滚动到相应位置,不会重新加载网页`,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,`HTTP请求中也不会不包括#`;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说`Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据`
history模式的优缺点
优点:符合url地址规范, 不需要#, 使用起来比较美观
缺点:在用户手动输入地址或`刷新页面时会发起url请求`, 后端需要配置index.html页面用户匹配不到静态资源的情况, 否则会出现404错误
兼容性比较差, 是利用了 HTML5 History对象中新增的 pushState() 和 replaceState() 方法,需要特定浏览器的支持.
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。pushState()方法可以改变URL地址且不会发送请求,replaceState()方法可以读取历史记录栈,还可以对浏览器记录进行修改。 这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
axios是怎么做封装的
引入,创建实例,封装请求响应拦截器,抛出,封装接口
keep-alive的用法
还可使用v-bind+正则,动态判断,exclude排除缓存组件
<keep-alive include = "test1,test2">
<router-view/>
</kee-alive>
在router中缓存部分页面
在routes中配置meat:{keepAlive:true},在router-view中通过v-if="$router.meta.keepAlive"判断
vue如何进行组件通信
1.父传子 props,$ref(引用信息会注册在父组件的$refs对象上)
2.子传父 $emit,父组件用事件监听接收参数
3.兄弟组件 eventBus全局时间总线,用on和emit传输数据
4.vuex
element-ui是怎么做表单验证的
1.在表单中添加rules属性,然后在data里写校验规则
<el-form :rules="rules">
<el-form-item prop="code">
<el-input v-mode="ruleForm.code"></el-input>
</el-form-item>
</el-form>
rules:{
code:[
{requied:true,message:"test",trigger:'blur'}
]
}
2.内部添加规则
<el-form-item rules="[{required:true},{validator:yanz,trgger:'blur'}]" ></el-form-item>
3.自定义函数校验
data自定义规则(可以在data或者method中定义)
data(){
var rules = (rule,value,callback) =>{
//rule 对应使用rules自定验证的对象
//value 对应使用rules自定义验证的数值
const r = /^+?[1-9][0-9]*$///正整数
if(value)
}
return:{
rule;{
number:[
{
required:true
}
]
}
}
}
vue中的修饰符有哪些
1.事件修饰符
.stop 阻止冒泡
.prevent 阻止默认行为
.capture 内部元素触发的时间先在此处理
.self 只有在event.target是当前元素时触发
.once 事件只会触发一次
.passive 立即触发默认行为
.native 把当前元素作为原生标签看待
2.按键修饰符
.keyup .keydown
3.系统修饰符
.ctrl .clt .meta
4.鼠标修饰符
.left .right .middle
5.表单修饰符
.lazy 等输入完之后显示
.trim 删除前后空格
.number 输入数字或转为数字
在created和mounted请求数据有什么区别
created:在渲染调用前,通常先初始化属性,然后做渲染
mounted:在模板渲染完成后,一般都是初始化页面后,在对元素节点进行操作,在这里请求数据可能闪屏,ceated里不会
请求的数据对dom有影响,使用created
vue生命周期
beforeCreated 属性和方法都不能使用
created 实例创建完成,在这里完成了数据监测,可以使用数据,修改数据,不会触发update,也不会更新视图
beforeMounted 完成了模板的编译,虚拟DOM创建完成,即将渲染,修改数据,不会触发updated
Mounted 把编译好的模板挂载到页面,可以发送异步请求也可以访问dom节点
beforeUpadte 组件数据更新之前使用,数据是新的,页面上的数据是旧的,组件即将更新,准备渲染,可以修改数据
updated render重新渲染,数据和页面都是新的,避免在此更新数据
beforeDestoryed 实例销毁前,实例还可以使用,清除定时器等
destoryed 销毁
keepalive activited deactived
解决跨域的方法?
1.CORS(跨域资源共享):这是一种标准化的跨域解决方案,它允许服务器通过设置响应头来指定哪些源可以访问其资源。浏览器会在发送请求时携带一个Origin头,服务器根据这个头来判断是否允许跨域
2.JSONP(JSON with Padding) :这是一种利用<script>标签的src属性不受同源策略限制的特点来实现跨域请求的技术。它需要服务器返回一个回调函数名和数据的拼接字符串,浏览器会执行这个回调函数并获取数据
3.代理跨域:这是一种通过在同源服务器上设置一个代理服务器来转发请求和响应的方法。它可以使用nginx、node.js、Apache等工具来实现
Vuex的原理
Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
vuex中的module是做什么的
Vuex中的module是用来将store分割成不同的模块,每个模块拥有自己的state、getters、mutation、action等这样可以让代码更清晰,更容易维护module可以嵌套子模块,形成一个树状结构module默认是注册在全局命名空间的,但也可以设置命名空间来避免命名冲突
data和computed的区别
data是简单的数据,可以直接赋值和修改,而computed是计算属性,依赖于data或其他computed,不能直接修改
data是在Vue实例创建时初始化的,而computed是在第一次访问时计算的,并且会缓存结果,只有当依赖的数据变化时才会重新计算
data可以用在任何地方,而computed只能用在模板或其他computed中
created具体有什么作用,举例说明
Vue中的created是一个生命周期钩子函数,它在实例被创建之后被调用,此时data和methods已经初始化,但是页面DOM节点还没有生成,(Vue中页面DOM节点是在mounted生命周期时生成的,这个阶段表示实例已经挂载到对应的元素上,可以操作DOM元素)created的作用是可以在这个阶段进行一些数据的预处理,比如发送请求获取数据,举例说明:
var vm = new Vue ({
data : {
a : 1,
b : 2
},
created : function () {
// `this` 指向 vm 实例
console.log ('a is: ' + this.a) // -> "a is: 1"
// 发送请求获取数据
this.$http.get('/api/data').then(res => {
this.b = res.data;
})
}
})
vue3和vue2的区别
Vue3支持多个根标签,Vue2不支持
Vue3使用createApp()创建实例,Vue2使用new Vue()
Vue3使用Proxy实现响应式数据,Vue2使用Object.defineProperty
Vue3引入了Composition API,提供了更灵活的组合逻辑
Vue3优化了虚拟DOM和模板编译,提高了渲染性能
Vue2使用的是选项式API,它是一种基于对象的API,让我们在组件中定义data、methods、computed、watch等选项。选项式API的优点是简单易用,但缺点是难以管理复杂的逻辑Vue3仍然支持选项式API,但也提供了组合式API作为一个更灵活的选择
组合式api
Vue3的组合式API是一种新的编写和组织组件的方式,它可以让我们在setup()函数中按照逻辑关注点对部分代码进行分组,然后提取逻辑到可复用的函数中
组合式API主要包括以下几个部分:
- [响应式API:例如ref()和reactive(),用来创建响应式数据]
- [生命周期钩子:例如onMounted()和onUpdated(),用来在不同阶段执行逻辑]
- [计算属性和侦听器:例如computed()和watch(),用来处理数据变化]
- [提供和注入:例如provide()和inject(),用来实现跨组件通信]
- [其他辅助函数:例如getCurrentInstance()和toRefs(),用来获取当前实例或转换响应式对象]
选项式API和组合式API的区别主要有以下几点:
选项式API是基于对象的,组合式API是基于函数的
选项式API把逻辑分散在不同的选项中,组合式API把逻辑按照关注点分组
选项式API适合简单的场景,组合式API适合复杂和复用的场景
选项式API对TS支持不够好,组合式API对TS支持更友好
Vue3如何优化虚拟DOM和模板编译,提高渲染性能
重写了虚拟DOM的实现,使用更轻量的对象结构,减少了内存占用和属性访问的开销
引入了静态标记(Static Tree Hoisting)和静态属性提升(Static Props Hoisting)的优化技术,把不变的部分提前编译成常量,避免每次渲染时都重新创建
引入了基于块(Block Tree)的代码生成方式,把模板按照动态和静态部分分割成多个块,只对动态部分进行diff算法
引入了事件侦听器缓存(Event Listener Caching),把事件侦听器函数缓存起来,避免每次渲染时都重新绑定
vue3和vue2响应式的区别
Vue3和Vue2的响应式原理有很大的区别。Vue2使用Object.defineProperty()对对象的属性进行拦截,但是不能检测到新增或删除的属性
Vue3使用Proxy代理对整个对象进行拦截,可以检测到任意属性的变化
vue的模板编译过程
Vue的模板编译过程是指把模板转换成渲染函数的过程。渲染函数是用来生成组件的最终HTML的函数。这个过程分为三个步骤:
1.把模板转换成AST语法树(拼接字符串),AST语法树是用来描述模板结构的对象
2.把AST语法树通过new Function + with语法包装成Render函数,Render函数可以生成虚拟节点,虚拟节点是用来描述真实DOM的对象
3.把虚拟节点挂载到DOM树上,生成真实的DOM
Vue也提供了APIs,让我们可以跳过模板编译的步骤,直接编写渲染函数。渲染函数比模板更灵活,因为我们可以用JavaScript的全部能力来操作虚拟节点
在模板编译过程中,编译器可以把一些属性,比如src URLs,转换成require调用,让目标资源可以被webpack处理。例如,<img src="./foo.png">会尝试在文件系统中找到./foo.png,并把它作为一个依赖包含在bundle中
编译模板过程中为什么要做静态分析和动态分析
编译模板过程中要做静态分析和动态分析的原因是为了提高代码的安全性和性能。静态分析和动态分析是两种互补的技术,它们可以在不同的阶段发现不同类型的问题。
静态分析是指在代码运行之前对其进行检查,它可以解析数据,提取模式,属性和特征,并标记异常。静态分析可以发现语法错误,逻辑错误,风格问题,潜在的漏洞等。
动态分析是指在代码运行时对其进行检查,它可以观察代码的行为,输出,内存占用等,并记录异常。动态分析可以发现运行时错误,性能问题,内存泄漏等。
通过结合静态分析和动态分析,我们可以更全面地测试和优化我们的代码,在部署之前消除潜在的风险。
vue2 diff算法
Vue2的diff算法是一种用来比较两棵虚拟DOM树的变化的算法,它可以高效地更新真实DOM,提高渲染性能。
Vue2的diff算法的基本思想是:
1.只比较同级节点,不跨层级比较
2.从两端向中间遍历节点,找出相同或不同的节点
3.如果节点类型或键值不同,则直接替换或删除该节点
4.如果节点类型相同,则继续比较其子节点
5.如果子节点是文本,则直接更新文本内容
6.如果子节点是数组,则递归调用diff算法
Vue2的diff算法主要涉及三个函数:
1.patch():负责创建和更新真实DOM
2.patchVnode():负责比较两个虚拟节点,并调用updateChildren()处理其子节点
3.updateChildren():负责比较两个虚拟子节点数组,并根据四种情况进行处理:更新、新增、删除、移动
Vue2的diff算法的时间复杂度是O(n),其中n是虚拟DOM树中的最大层级数。
vue3 diff算法
Vue3的diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有O(n) 。它的核心是参考了Snabbdom,通过比较新旧虚拟DOM(即patch过程),找出最小变化的地方并转换为DOM操作。Vue3对diff算法进行了优化,引入了基于块(Block Tree)的代码生成方式,把模板按照动态和静态部分分割成多个块,只对动态部分进行diff算法
Vue2和Vue3的diff算法有以下几点区别:
Vue2的diff算法是全量的对比,每个节点不论写死的还是动态的都会一层一层比较,这就浪费了大部分时间在对比静态节点上。
Vue3的diff算法是基于块(Block Tree)的对比,把模板按照动态和静态部分分割成多个块,只对动态部分进行diff算法
Vue2的diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。
Vue3的diff算法采用了去头尾的最长递增子序列(LIS)算法,通过寻找新旧children中最长不需要移动的子序列,减少了移动节点所需操作
react和vue diff算法的区别
react的diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。
vue2的diff算法也采用了双端比较的算法,但是vue3的diff算法采用了去头尾的最长递增子序列(LIS)算法,通过寻找新旧children中最长不需要移动的子序列,减少了移动节点所需操作。
react和vue都使用了静态标记(Static Tree Hoisting)和静态属性提升(Static Props Hoisting)等优化技术,把不变的部分提前编译成常量,避免每次渲染时都重新创建。
但是vue还引入了基于块(Block Tree)的代码生成方式,把模板按照动态和静态部分分割成多个块,只对动态部分进行diff算法。
flex布局,具体参数的意义
flex-direction: 定义了容器内元素的排列方向,可以是水平或垂直。
flex-wrap: 定义了容器内元素是否换行,以及换行的方式。
flex-flow: 是flex-direction和flex-wrap的缩写形式1。
justify-content: 定义了容器内元素在主轴上的对齐方式,可以是居中、两端对齐、平均分配等。
align-items: 定义了容器内元素在交叉轴上的对齐方式,可以是居中、顶部对齐、底部对齐等。
align-content: 定义了容器内多行元素在交叉轴上的对齐方式,类似于justify-content。
flex: 是flex-grow、flex-shrink和flex-basis的缩写形式,用来控制容器内单个元素的大小和比例23。flex-grow: 定义了元素在有多余空间时的放大比例,越大则占据更多空间23。
flex-shrink: 定义了元素在空间不足时的缩小比例,越大则缩小得更多。
flex-basis: 定义了元素在没有放大或缩小时的初始大小,可以是固定值或auto。
项目中做了哪些性能优化
js -> 全局变量影响性能,命名污染,节流,防抖,减少if else,提前return不满的逻辑,按需引入,懒加载
vue -> 子组件拆分,将复杂耗时计算方在子组件中处理,局requestAnimationFrame部变量,v-if,v-show,路由切换时使用keep-alive,使用requestAnimationFrame分批分批执行大数据量的计算
减少HTTP请求次数,例如合并JS和CSS文件,使用雪碧图,使用字体图标等
减少DNS查询次数,例如使用CDN,预解析DNS等
避免页面跳转,例如使用SPA框架,使用History API等
缓存AJAX请求,例如使用localStorage或sessionStorage存储数据等
延迟加载或懒加载资源,例如图片、视频、音频等
使用AMP HTML技术提高移动端页面的渲染速度
优化项目构建速度和打包体积,例如使用webpack或rollup等工具进行代码分割、压缩、去除无用代码等
优化页面渲染性能和交互流畅度,例如使用requestAnimationFrame、debounce、throttle等技术避免重绘和回流等
http1和http2的区别,http3?
http1短链接,http1.1长连接三次握手,1.1管道机制,同一个tcp中客户端可以同时发送多个请求,1.1的缺陷浏览器同时发出AB请求,服务器还是按照顺序先回应A再回应B(为了避免这个问题,我可以减少请求数,合并js,css和同时多开持久链接,多域名->同一域名最多有6个tcp连接)
http1.x串行,2并行,二进制分帧
http2专注于性能,最大目标只用一个tcp连接
http2核心升级点:在二进制协议(1.x为文本协议),一个tcp连接多路复用,头压缩(字典),服务器推送
http2缺点:依然需要顺序响应,一旦阻塞所有请求都会阻塞
http3基于UDP的“QUIC”协议,实际上就是在udp的基础上重写了tcp的功能,但又比tcp更加智能,更高效的实现了tcp的核心功能,不需要三次握手,udp是无序的解决了队头阻塞问题
UDP怎么保证连接的可靠性(tcp是问答式,每个请求都有确认保证数据包的可到达)
http数据包中添加一个packet header,当udp报文丢失后,通过packet header中的packet number实现报文的重传
packet number是每个报文独一无二的序号,基于它可以实现丢失报文的精准重发
没有了tcp的三次握手和四次挥手,怎么建立连接
使用dh密钥交换算法,客户端向服务器请求,服务器传回“令牌”,客户传发送数据和“令牌”
cookie和storage的认识和区别
cookie(4kb)
时效性可以自己设置
前端的使用-> cookie键值对,document.cookie = "username=YY"只能一条条分开写,删除document.cookie="username=YY;过去的时间"
服务器端使用->在response中 res.cookie('username','YY'{
maxAge:1000*60 //过期时间,单位是毫秒
})
res.send('测试cookie')
localStorage(5MB)
长期存在
localStorage.setItem('username','YY')
localStorage.removeItem('username')
sessionStorage(5MB)
有效性在一个会话中,关掉窗口清除
sessionStorage.setItem('username','YY')
sessionStorage.removeItem('username')
http状态码
1xx:消息,属于提示信息,是协议处理中的一种中间状态,一般禁止服务器向客户端发送此类状态码(options预请求类似)
2xx:成功
3xx:重定向,需要客户端进一步采取操作才能完成请求
301:永久移动 302: 临时重定向 303:必须临时重定向且必须使用get方式 304:重定向至浏览器本身
4xx:请求错误
401:协议格式出错(没有传token),可能是此ip地址被禁止访问该资源与403类似 403:表示没有权限,服务器拒绝访问请求 404:找不到系统资源
5xx:服务器错误
502:网关故障 503:服务不可用,服务器短时间过载 504:网关超时
http请求格式,响应头里面有哪些参数
HTTP请求是客户端向服务器发送的消息,包括一个请求行,一些请求头部,一个空行和一个可选的请求体。HTTP响应是服务器向客户端返回的消息,包括一个状态行,一些响应头部,一个空行和一个可选的响应体
响应头里面有哪些参数,主要取决于服务器的实现和配置。不过,有一些常见的参数是标准化或者广泛使用的,比如:
Content-Type: 表示响应体的数据类型和字符编码
Content-Length: 表示响应体的长度(字节)
Date: 表示服务器发送响应的时间
Server: 表示服务器的名称或者版本
Set-Cookie: 表示服务器设置或者更新客户端的cookie
Location: 表示重定向到另一个URL
Cache-Control: 表示缓存策略
nginx
事件循环,微任务,宏循环
事件循环是指在浏览器或 Node.js 中,执行 JavaScript 代码时,将代码分为两类:同步和异步。同步代码会在主线程上执行,而异步代码会在事件循环中执行。事件循环是一个无限循环,它会不断地从异步队列中取出任务并执行,直到队列为空。事件循环中的任务分为两类:宏任务和微任务。宏任务包括 setTimeout、setInterval、setImmediate、I/O、UI 事件等,而微任务包括 Promise、process.nextTick 等。在事件循环中,微任务的优先级高于宏任务,因此当微任务和宏任务同时存在时,微任务会先执行。
微任务:当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。 (Promise.then,process.nextTick 等等)。 宏任务中的事件放在 callback queue 中,由事件触发线程维护;微任务的事件放在微任务队列中,由 js 引擎线程维护。1
宏任务和微任务的执行顺序是:先执行主线程同步的任务,发现异步之后,放入到异步队列中。然后执行当前微任务队列中的所有任务,再执行当前宏任务队列中的所有任务。
webpack常见配置
webpack 常见配置选项包括 entry、output、module、plugins 等。其中,entry 和 output 是 webpack 的基本配置,entry 指定 webpack 的入口文件,output 指定 webpack 的输出文件。module 配置项用于配置 webpack 如何处理不同类型的模块,plugins 配置项用于配置 webpack 的插件。
浏览器缓存协商缓存和强制缓存
浏览器每次发送请求,都会在浏览器缓存中查找该请求的结果及缓存标识
浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器中
强制缓存(三种情况:不存在强制缓存,存在但已失效,存在且有效)
协商缓存(两种情况:协商成功304对照资源一致,协商失败200对照资源不一致重新返回,)
强制缓存优先于协商缓存,若强制缓存(expires和cache-control)生效则强制使用缓存,若不生效则进行协商缓存
协商缓存由服务器决定是否使用缓存
flexible.js 移动端自适应方案 flex + rem + flexiable.js + less
vw就是视口的宽度,vw 是个相对单位。不管在什么屏幕下, 我们把屏幕分为平均的 100等份。
js中的迭代器
JavaScript 迭代器是在 ES6 中引入的,它们用于循环一系列值,通常是某种集合。根据定义,迭代器必须实现一个`next()`函数,该函数以`{ value, done }` 的形式返回对象,其中`value`是迭代序列中的下一个值,并且`done`是一个布尔值,确定序列是否已被消费完。
在 JavaScript 中,迭代器是一种设计模式,用于在容器对象上遍历,无需关心容器对象的内存分配的实现细节。在 JavaScript 中,迭代器是一个特殊对象,这个迭代器对象有一个 next() 方法,每次调用都会返回一个对象,语法为“next:function () {return {value,done}}”。
在 JavaScript 中,最常见的迭代器是 Array 迭代器,它只是按顺序返回关联数组中的每个值。虽然很容易想象所有迭代器都可以表示为数组,但事实并非如此。数组必须完整分配,但迭代器仅在必要时使用,因此可以表示无限大小的序列,例如 0 和无穷大之间的整数范围。
除了 Array 迭代器,JavaScript 还有其他类型的迭代器,例如 Map 迭代器、Set 迭代器、String 迭代器、TypedArray 迭代器、NodeList 迭代器等。这些迭代器都有一个 next() 方法,每次调用都会返回一个对象,语法为“next:function () {return {value,done}}”.
for in和for of的区别
在 JavaScript 中,for…in 循环只遍历可枚举属性(包括它的原型链上的可枚举属性),而 for…of 循环则是 ES6 中引入的一种新的遍历语法,它只遍历可迭代对象(包括数组、Map、Set、String、arguments 对象等)的元素值,而不包括对象的属性。
简单来说,for…in 循环遍历的是对象的属性名和原型中的方法和属性,而 for…of 循环遍历的是数组的元素值。
如何减少重绘
在JavaScript 中,减少重绘的方法有很多,以下是一些常见的方法:
1. 避免触发同步布局事件,例如读取 box 的一个 offsetWidth 等属性值,会强制浏览器刷新队列,先回流重排,在计算结果。因此可以将 offsetWidth 保存在变量中。
2. 使用 CSS3 硬件加速,可以让 transform、opacity、filters 这些动画不会引起回流重绘。
3. 使用 requestAnimationFrame() 方法,可以将多个重绘操作合并成一个,从而减少重绘次数。
4. 使用虚拟 DOM,可以将多个 DOM 操作合并成一个,从而减少重绘次数。
5. 使用 canvas 绘图,可以将多个 DOM 元素合并成一个,从而减少重绘次数。
二叉树diff算法的简单实现
// 定义一个节点类
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
// 定义一个diff函数,接收两棵二叉树作为参数
function diff(tree1, tree2) {
// 如果两棵树都为空,返回空数组
if (!tree1 && !tree2) return [];
// 如果其中一棵树为空,返回另一棵树的所有节点值
if (!tree1 || !tree2) return [tree1 ? tree1.value : tree2.value];
// 如果两棵树的节点值不同,返回两个节点值
if (tree1.value !== tree2.value) return [tree1.value, tree2.value];
// 否则递归比较左右子树,并合并结果
return [...diff(tree1.left, tree2.left), ...diff(tree1.right, tree2.right)];
}
dom太多如何优化
减少dom数量,使用伪元素,阴影等样式实现的内容尽量不使用dom实现
按需加载,减少不必要的渲染
使用合适的选择器,缓存dom节点,使用事件委托等方式减少dom操作
使用文档碎片(document fragment)或虚拟dom(virtual dom)批量更新dom
使用requestAnimationFrame或setTimeout延迟渲染部分
图片懒加载
图片懒加载是一种网页性能优化的方式,能够提升用户体验,节约网络资源,避免页面卡顿。图片懒加载的原理是:在图片没有进入可视区域时,先不给<img>的src赋值,而是用一个占位符或者data-xxx属性保存图片的路径;当图片进入可视区域时,再将data-xxx的值赋给src,这样浏览器就会按需请求图片资源。图片懒加载的实现方法有以下几种:
使用原生js监听scroll事件和getBoundingClientRect方法判断图片是否进入可视区域
使用Intersection Observer API监听元素是否与可视区域相交
使用vue自定义指令v-lazy实现懒加载
http与socket的区别
http和socket是两个不同的概念,不能直接比较。http是一种应用层的协议,基于TCP/IP协议实现,主要用于客户端和服务器之间的请求和响应模式。socket是一种编程接口,用于应用层和传输层之间的通信,可以使用TCP或UDP协议。http和socket的区别有以下几点:
http是无状态的,每次请求都是独立的,服务器不会保存客户端的信息;socket是有状态的,可以保持连接,实现双向通信
http是基于文本的,传输的数据格式固定;socket是基于二进制的,传输的数据格式灵活
http需要客户端主动发起请求,服务器才能回复数据;socket可以由服务器主动向客户端推送数据
vue如何给一个普通变量加上响应式
在初始化实例前声明根级响应式属性,哪怕只是一个空值
使用Vue.set(object, propertyName, value)方法向嵌套对象添加响应式属性
使用ref()函数创建一个可写的计算属性,并通过.value访问或修改它的值
浏览器循环机制是什么
浏览器循环机制是指浏览器如何处理JavaScript的同步任务和异步任务的一种机制。浏览器有一个主线程,一个事件队列,一个微任务队列和一个渲染线程。
主线程负责执行同步任务,形成一个执行栈;事件队列负责存放异步任务的回调函数,等待主线程空闲时执行;微任务队列负责存放Promise等产生的微任务,优先于事件队列执行;渲染线程负责更新页面的显示
浏览器循环机制的大致过程如下:
主线程从上到下执行同步代码,遇到异步代码则将其回调函数放入事件队列或微任务队列;
主线程执行完同步代码后,检查微任务队列是否有待执行的微任务,如果有则依次执行直到清空;
主线程检查是否需要渲染页面,如果需要则通知渲染线程进行渲染;
主线程从事件队列中取出一个回调函数并执行,然后重复上述步骤;
如果事件队列为空,则等待新的异步任务到来。
判断一个代码是同步还是异步,有以下几种方法:
根据代码的执行顺序,如果代码按照书写的顺序依次执行,那么它是同步的;如果代码需要等待某个操作完成后才能执行,那么它是异步的
根据代码是否有回调函数,如果代码有回调函数,并且回调函数在主线程空闲时才执行,那么它是异步的;如果代码没有回调函数,或者回调函数和主线程一起执行,那么它是同步的
根据测试工具如Jest等,如果测试工具需要使用done参数或者返回一个Promise对象来等待异步操作完成,那么它是异步的
number类型储存形式
number类型是JavaScript中用于存储数值的数据类型,所有数字都是以64位浮点数形式储存,即便整数也是如此。number类型可以表示的范围是-2^53 ~ 2^53 - 1,超出这个范围的数字会被转换为Infinity或-Infinity。number类型还有一些特殊的值,如NaN(Not a Number),表示非法的数值运算结果
js中的基础数据类型有六种:
undefined, null, number, boolean, string 和 symbol
js中将异步任务转换为同步任务,有以下几种方法:
使用async和await关键字,让主线程等待异步函数返回结果
使用Promise对象,让主线程使用then或catch方法处理异步函数的结果或错误
使用Generator函数和yield关键字,让主线程暂停和恢复异步函数的执行
小数计算有误差怎么办 0.1+0.2 != 0.3?
这是一个常见的JavaScript问题,原因是js使用的双精度浮点数,在计算机内部存储数据的编码会出现误差,导致0.1+0.2=0.3000000000000000412。和0.3相比较结果为false。如果你想得到精确的结果,你可以使用一些库或方法来解决这个问题
将小数先转换成整数,再相加之后转回小数1。例如,0.1+0.2可以写成(0.110+0.210)/10。
使用Number对象的toFixed方法,指定运算结果的小数点后的位数1。例如,0.1+0.2可以写成parseFloat((0.1+0.2).toFixed(1))。
使用一些库或函数来处理浮点数的运算。例如,math.js或decimal.js等。
使用整数转换的方法,你可以这样写:
let x = (0.1 * 10 + 0.2 * 10) / 10;
console.log(x === 0.3); // true
1
使用toFixed方法,你可以这样写:
let x = parseFloat((0.1 + 0.2).toFixed(1));
console.log(x === 0.3); // true
1
使用math.js库,你可以这样写:
let x = math.add(0.1, 0.2);
console.log(x === 0.3); // true
event loop
event loop是JavaScript的运行时模型,它负责执行代码,收集和处理事件,以及执行队列中的子任务。event loop是一种不断运行的过程,它监视回调队列和调用栈。如果调用栈不为空,event loop就等待它为空,然后将回调队列中的下一个函数放到调用栈中执行。如果回调队列为空,就什么都不做。event loop可以让JavaScript在单线程下实现异步操作
例如,当一个外部脚本<script src="…">加载时,任务是执行它。当用户移动鼠标时,任务是分发mousemove事件并执行处理函数。这些任务会被放入回调队列中等待执行。当调用栈为空时,event loop会从回调队列中取出最早的任务放入调用栈中执行。这样就实现了JavaScript的异步操作。
函数箭头特性
箭头函数可以使用一个或多个参数。如果使用两个或多个参数,必须用括号括起来。
箭头函数没有自己的this,arguments,super或new.target,而是继承了外层作用域的这些值。
箭头函数不能用作构造函数或对象方法。
箭头函数可以简化数组方法的代码,例如map,filter,reduce等。
箭头函数可以用来创建对象字面量,但需要用括号包裹。
箭头函数和普通函数有以下区别
语法上,箭头函数更简洁,可以省略括号和return关键字。
this值上,箭头函数没有自己的this,而是继承了外层作用域的this,而普通函数的this取决于调用方式。
arguments对象上,箭头函数没有自己的arguments对象,而是继承了外层作用域的arguments对象,而普通函数有自己的arguments对象。
构造性上,箭头函数不能用作构造函数或对象方法,也不能使用new关键字调用,而普通函数可以。
命名性上,箭头函数总是匿名的,不能被命名,而普通函数可以是命名的或匿名的。
position的值有哪些?分别说一下
static:默认值。元素按照正常的文档流进行定位。不受top, bottom, left和right属性的影响
relative:元素相对于其正常位置进行定位,可以使用top, bottom, left和right属性来调整位置。
fixed:元素相对于浏览器窗口进行定位,不随页面滚动而移动。可以使用top, bottom, left和right属性来调整位置
absolute:元素相对于最近的已定位祖先元素(如果有的话)或者初始包含块进行绝对定位,可以使用top, bottom, left和right属性来调整位置。
sticky:元素在正常文档流中表现为相对定位,但当页面滚动超出指定偏移量时表现为固定定位。需要指定一个偏移量(top, bottom, left或者right)
vue和react区别
Vue和React都使用组件化的开发模式,支持虚拟DOM和响应式数据绑定。
Vue默认使用HTML模板,但也可以选择使用JSX。React则只使用JSX来构建视图层。
Vue是一个完整的框架,提供了路由、状态管理等功能。React是一个库,需要配合其他工具或库来实现完整的功能。
Vue有更灵活和定制化的模板语法,可以方便地添加指令、过滤器等特性。React则更倾向于使用纯JavaScript来实现逻辑和渲染。
get和post的区别
GET把参数包含在URL中,POST通过request body传递参数。
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark(收藏),而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求对数据长度有限制(因为URL长度有限),而POST没有。
GET一般用于获取资源,不会修改服务器的数据;POST一般用于发送修改请求,进行数据的修改。
localstorage超过限制大小怎么办
1. 划分域名。各域名下的存储空间由各业务组统一规划使用
2. 跨页面传数据:考虑单页应用、优先采用 url 传数据
3. 最后的兜底方案:清掉别人的存储
4. IndexedDB
5. localforage.js
webpack打包时做了什么事
Webpack 是一个 JavaScript 应用程序的静态模块打包器,它可以将多个 JavaScript 文件打包成一个或多个 bundle,以及处理其他类型的文件如 CSS、图片等。
在打包过程中,Webpack 主要完成以下任务:
1. 解析入口文件:Webpack 会根据配置文件中指定的入口文件,从该文件开始分析整个应用程序的依赖关系。它会解析每个模块的代码,找出每个模块所依赖的其他模块,以及被哪些模块所依赖。
2. 加载和转换模块:Webpack 支持加载多种类型的文件,例如 JavaScript、CSS、图片等。当它遇到一个依赖的模块时,它会尝试加载并解析该模块的代码,并通过适当的 loader 对代码进行转换。
3. 模块打包:在解析和转换每个模块后,Webpack 会将所有模块打包到一个或多个 bundle 中。每个 bundle 包含一组关联的模块,这些模块会在运行时被加载和执行。
4. 代码分割:Webpack 可以将应用程序代码分割成多个 chunk,以便按需加载。这有助于减少初始加载时间并提高应用程序的性能。
5. 压缩和优化:Webpack 可以自动进行代码压缩和优化,以减小文件的大小,并提高应用程序的性能。
6. 输出文件:最后,Webpack 将打包好的文件输出到指定的目录中,以便在浏览器或其他环境中使用。
总的来说,Webpack 主要完成了代码解析、模块打包、代码分割、压缩和输出等任务,从而将多个模块组合成一个或多个 bundle,以便在浏览器或其他环境中使用。
为什么vue3对ts的兼容更友好
Vue3 对 TypeScript 的兼容性更友好是因为 Vue3 在设计和开发过程中,充分考虑了 TypeScript 的特性和使用场景。具体来说,以下是几个方面的改进:
1. 改进了类型推导:Vue3 在设计和实现时考虑到了 TypeScript 的类型系统,使用了更多的类型注解和类型推导,以便在开发过程中提供更好的类型检查和代码提示。
2. 改进了 API 设计:Vue3 在设计 API 时,充分考虑了 TypeScript 的类型系统,使用了更多的泛型和接口,以便在使用时提供更好的类型检查和代码提示。
3. 改进了组件选项声明:Vue3 改进了组件选项声明,使其更易于与 TypeScript 配合使用。例如,Vue3 在组件选项中增加了 `setup()` 函数,它的参数和返回值可以被 TypeScript 推导和检查。
4. 改进了编译器:Vue3 改进了编译器,使其更加智能化,能够检查和修复更多的类型错误和语法错误,以提供更好的开发体验。
综上所述,Vue3 对 TypeScript 的兼容性更友好是因为在设计和实现时,充分考虑了 TypeScript 的特性和使用场景,从而提供了更好的类型检查、代码提示和开发体验。
TypeScript 和 JavaScript 有以下区别:
类型系统:TypeScript 引入了静态类型系统,这意味着在开发过程中可以在编译时捕获许多类型错误。而 JavaScript 是一种弱类型语言,没有静态类型检查。这也是 TypeScript 最大的特点之一。
语言特性:TypeScript 支持许多 JavaScript 不支持的语言特性,例如枚举、接口、泛型等。这些特性可以帮助开发人员编写更健壮、可维护的代码。
工具支持:TypeScript 提供了更好的开发工具支持,例如强大的代码补全、智能提示、错误检查等。这些功能可以提高开发效率和代码质量。
编译方式:TypeScript 编译成 JavaScript 执行。编写 TypeScript 代码时需要使用 TypeScript 编辑器或者 TypeScript 编译器将 TypeScript 代码编译成 JavaScript 代码,然后在浏览器或者 Node.js 等 JavaScript 运行环境中执行。而 JavaScript 直接可以在浏览器或者 Node.js 等 JavaScript 运行环境中执行。
vue模板解析原理
Vue首先会通过正则表达式对模板进行词法分析,将模板字符串转换为一个数组,数组中包含了各种类型的标记(如开始标签、结束标签、文本、注释等)。
然后Vue会根据数组中的标记构建一个抽象语法树(AST),AST是一种用对象表示的树形结构,能够描述模板中的元素、属性、指令、表达式等信息。
接着Vue会对AST进行优化,标记出静态节点和动态节点,以便在后续的更新过程中跳过静态节点,提高性能。
最后Vue会将AST转换为渲染函数,渲染函数是一段JavaScript代码,能够根据数据生成虚拟DOM,并返回给Vue实例。
vue中nexttick的原理
Vue.js中的nextTick()方法是用于在DOM更新后执行回调的异步方法。Vue.js是异步执行DOM更新的,一旦观察到数据变化,Vue.js就会开启一个队列,然后把在同一个事件循环(event loop)中观察到数据变化的watcher推送进这个队列。nextTick()方法就是在这个队列中添加一个回调函数,以便在DOM更新后执行。nextTick()方法的实现原理是使用了JavaScript的Event Loop机制
设计模式
JavaScript设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式可重用代码、让代码更容易被他人理解、保证代码可靠性,设计模式使代码编制真正工程化。JavaScript中常见的设计模式有单例模式、工厂模式、适配器模式、装饰者模式、观察者模式、代理模式等
观察者模式(Observer Pattern):这种模式用于在对象之间建立一对多的依赖关系。当一个对象的状态发生改变时,所有依赖它的对象都会收到通知并自动更新。这种模式常用于事件处理程序和UI编程。
工厂模式(Factory Pattern):工厂模式用于创建对象,而不需要显式地指定它们的类。这种模式在对象创建方面非常灵活,可以通过简单地修改工厂方法的代码,轻松地创建新类型的对象。
// 定义汽车接口
interface Car {
drive(): void;
}
// 定义汽车工厂类
class CarFactory {
createCar(type: string): Car {
switch (type) {
case 'SUV':
return new SUV();
case 'Sedan':
return new Sedan();
default:
throw new Error('Invalid car type.');
}
}
}
// 定义SUV类
class SUV implements Car {
drive() {
console.log('Driving SUV.');
}
}
// 定义Sedan类
class Sedan implements Car {
drive() {
console.log('Driving Sedan.');
}
}
// 使用汽车工厂类创建汽车对象
const carFactory = new CarFactory();
const suv = carFactory.createCar('SUV');
const sedan = carFactory.createCar('Sedan');
suv.drive(); // Driving SUV.
sedan.drive(); // Driving Sedan.
在这个例子中,我们使用工厂模式来创建汽车对象。首先,我们定义了一个汽车接口或抽象类,然后定义了一个汽车工厂类,该类负责创建汽车对象。最后,我们使用汽车工厂类来创建汽车对象
单例模式:通过一个全局对象只创建一个实例来实现共享资源的访问。例如,一个全局的日志对象可以使用单例模式,确保只有一个实例记录日志信息。
装饰者模式:通过动态地将对象添加到已有对象中,来为对象添加新的功能。例如,可以使用装饰者模式为一个文本框对象添加验证器、输入限制等功能。
// 定义汽车接口
interface Car {
drive(): void;
}
// 定义基础的汽车类
class BasicCar implements Car {
drive() {
console.log('Driving basic car.');
}
}
// 定义装饰器类
class CarDecorator implements Car {
protected car: Car;
constructor(car: Car) {
this.car = car;
}
drive() {
this.car.drive();
}
}
// 定义加速装饰器类
class SpeedDecorator extends CarDecorator {
drive() {
super.drive();
console.log('Driving fast.');
}
}
// 定义导航装饰器类
class NavigationDecorator extends CarDecorator {
drive() {
super.drive();
console.log('Navigating.');
}
}
// 使用装饰器类扩展汽车对象的功能
const basicCar = new BasicCar();
const fastCar = new SpeedDecorator(basicCar);
const fastCarWithNavigation = new NavigationDecorator(fastCar);
fastCarWithNavigation.drive(); // Driving basic car. Driving fast. Navigating.
在这个例子中,我们使用装饰者模式来扩展汽车对象的功能。首先,我们定义了一个汽车接口或抽象类,然后定义了一个基础的汽车类,该类实现汽车接口或抽象类。接下来,我们定义了一个装饰器类,该类实现汽车接口或抽象类,并包含一个指向汽车接口或抽象类的引用。最后,我们使用装饰器类来扩展汽车对象的功能
策略模式:将一系列算法封装成对象,使得它们可以相互替换。例如,可以使用策略模式来处理不同的支付方式,每种支付方式对应一个支付算法对象。
https为什么安全
1. 对称加密DES,AES,高效,不安全与非对称加密RSA,复杂安全性高
2. SSL服务器证书
证书验证+非对称加密传输密钥+对称加密传输数据
抓包工具为什么要导入证书
判断证书的合法性?
web安全解决方法(sxx,csrf,sql注入(后端))
xss跨站脚本攻击,在web界面中插入js代码,当用户浏览页面时js代码执行实现攻击,利用用户对站点得到信任
反射型:又叫非持久性xss,攻击方式只有一次,如果没有恶意链接,就不构成攻击
存储型:又称持久型跨站点脚本,一般发生在xss攻击代码存储在网站数据库中,当一个页面被用户打开时执行
防御方式:核心手段->将危险的标识进行转义
X-XSS-Protection响应头是IE,Chrome和safari的一个功能,当检测到xss,页面将停止加载页面,0禁止,1允许
前端转义库js-xss
CSRF,跨站请求伪造,盗用身份发送恶意请求,利用站点对已经身份认证的信任
csrf防御手段:1.检查http referer字段,后端通过这个字段判断请求是否合法,简单,不安全,referer字段可篡改
2.添加检验token,要求浏览器不保存在cookie中,服务机发放token
3.使用验证码
深色模式
滤镜,filter:invert(1)+图片再次取反,1深色0浅色
延迟加载js
script标签加上defer属性
defer:js脚本可以延迟到文档完全被解析和显示之后执行
如何异步加载js
script标签加上async属性
async:立即执行脚本,但不妨碍其他页面的操作,简单粗暴,谁先加载完了谁执行
params和query传参的区别
在Vue Router中,params传参和query传参都是用于路由传参的方式。params传参是通过路由的形式把参数传递过去,不会显示在地址栏中,但是不能刷新。params只能配合name使用,如果提供了path,params会失效。而query传参则是把参数放在url后面,可以刷新,但是参数会显示在地址栏中。query传参可以通过$route.query或者this.$route.query来获取参数。
两个数组的并集、差集、交集
1.直接用filter,contact计算
//indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。如果要检索的字符串值没有出现,则该方法返回 -1。
var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
//交集
var intersect = a.filter(function(v){ return b.indexOf(v) > -1 })
//差集
var minus = a.filter(function(v){ return b.indexOf(v) == -1 })
//补集
var complement = a.filter(function(v){ return !(b.indexOf(v) > -1) })
.concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}))
//并集
var unionSet = a.concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}));
2.1ES6
var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
var sa = new Set(a);
var sb = new Set(b);
// 交集
let intersect = a.filter(x => sb.has(x));
// 差集
let minus = a.filter(x => !sb.has(x));
// 补集
let complement = [...a.filter(x => !sb.has(x)), ...b.filter(x => !sa.has(x))];
// 并集
let unionSet = Array.from(new Set([...a, ...b]));
2.2借助includes和箭头函数
var a = [1,2,3,4,5];
var b = [2,4,6,8,10];
//交集
var intersect = a.filter(v => b.includes(v));
//差集
var minus = a.filter(v => !b.includes(v));
//补集
var complement = a.filter(v => !b.includes(v)).concat(b.filter(v => !a.includes(v)))
//并集
var unionSet = a.concat(b.filter(v => !a.includes(v)));
3.使用jQuery
var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
// 交集
let intersect = $(a).filter(b).toArray();
// 差集
let minus = $(a).not(b).toArray();
// 补集
let complement = $(a).not(b).toArray().concat($(b).not(a).toArray());
// 并集
let unionSet = $.unique(a.concat(b));
常用的数组遍历方式
for 循环
forEach(item, index, arr) 方法 用于调用数组的每个元素,并将元素传递给回调函数
forEach 循环对于空数组是不会执行回调函数的。
forEach 循环接受三个参数,[第一参数]为数组中的每一项,[第二参数]为数组的下标,[第三个参数]为你要遍历的数组本身。第二和第三参数都是可选的。
forEach 循环本身不支持 continue 和 break语句的
如果想实现continue效果,可以使用 return。
如果要实现break效果,建议使用 every 和 some 循环。(这个后面会讲到)
map(item,index,arr) 方法 返回一个经过调用函数处理后的新的数组
map 循环不会对空数组进行检测 。
map 循环必须 return 。
map 循环不会修改原数组。
map 循环接受三个参数,[第一参数]为数组中的每一项,[第二参数]为数组的下标,[第三个参数]为你要遍历的数组本身。第二和第三参数都是可选的。
map 循环会针对每一项都进行循环,如果跳过则会返回 undefined
filter() 方法 循环返回一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
filter 循环不会对空数组进行检测。
filter 循环不会改变原数组。
filter 循环接受三个参数,`[第一参数]为数组中的每一项,[第二参数]为数组的下标,[第三个参数]为你要遍历的数组本身`。第二和第三参数都是可选的。
reduce() 方法
some() 方法 循环查找数组中任意符合条件的元素并返回boolean值,当数组中有任意元素符合条件就返回 true 否则返回 fasle
some 循环会依次执行数组的每一个元素。
如果有一个元素满足条件,则返回 true,且剩余的元素不会在执行检测 即 循环结束。
some 循环不会对空数组进行检测
some 循环不会改变原数组
some 循环接受三个参数,[第一参数]为数组中的每一项,[第二参数]为数组的下标,[第三个参数]为你要遍历的数组本身。第二和第三参数都是可选的。
every() 方法
for…in 循环 for…in是为遍历对象属性而构建的,for…in 循环可以拿到数组的 key 值。针对上面的arr数组,拿到的也是key值
for…of 循环 for of循环是ES6的新增方法,针对for in循环的bug,for of循环就不会有问题 如果使用for…of循环,既想得到key 也想得到 value ,可以利用`arr.entries()`
forEach怎么跳出循环 在浏览器中输入地址后,按下回车到整个页面展示,中间的过程大致如下:
1.浏览器解析域名得到服务器 IP 地址。如果存在缓存,则直接从缓存中获取,否则会开启一个 DNS 域名解析器,首先访问顶级域名服务器,将对应的 IP 发给客户端;然后访问根域名解析器,将对应的 IP 发给客户端;最后访问目标域名服务器,将对应的 IP 发给客户端。
2.浏览器向服务器发送 HTTP 请求。
3.服务器接收到请求后,处理请求并返回 HTTP 响应。
4.浏览器接收到响应后,开始渲染页面。
map和reduce的区别
map 作用是生成一个新数组,遍历原数组,将每个元素拿出来做一些变换然后放入到新的数组中
reduce 可以将数组中的元素通过回调函数最终转换为一个值
参数一:为回调函数接受四个参数,分别为accumulator累计值、currentValue当前元素、currentIndex当前索引、array原数组
参数二:作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素
let arr = [1, 2, 3]
let sum = arr.reduce((acc, current) => acc + current, 0) // 1 + 2 + 3
console.log(sum) // 6
计算每个元素出现的次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
// allNames 初始值为{}
if (name in allNames) { // 当前元素是allNames对象的属性表示遍历过 次数+1
allNames[name]++;
}
else { // 当前元素不存在 设置为allNames对象的属性 第一次遍历:1
allNames[name] = 1;
}
return allNames; // 返回统计的对象 当做累计器
}, {});
console.log(countedNames); // {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
数组去重
let arr = [1, 2, 3, 4, 4, 1, 2, 5]
let newArr = arr.reduce((pre, cur) => {
// pre初始值为[]
if (!pre.includes(cur)) { // 当前元素不在数组中就加进去
pre.push(cur);
}
return pre;
}, [])
console.log(newArr);// [1, 2, 3, 4, 5]
将二维数组转化为一维
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
// 合并数组
( acc, cur ) => acc.concat(cur),
[]
);
console.log(flattened); // (6) [0, 1, 2, 3, 4, 5]
求对象里的属性和
var result = [
{
subject: 'math',
score: 10
},
{
subject: 'chinese',
score: 20
},
{
subject: 'english',
score: 30
}
];
var sum = result.reduce(function(prev, cur) {
return cur.score + prev;
}, 0);
console.log(sum) //60
promise的状态和解决的问题
它有三种状态:`pending`(未决定)、`fulfilled`(已兑现)和`rejected`(已拒绝)。一个 Promise 必然处于以下几种状态之一:待定(pending) :初始状态,既没有被兑现,也没有被拒绝。 已兑现(fulfilled) :意味着操作成功完成。 已拒绝(rejected) :意味着操作失败。 待定状态的 Promise 对象要么会通过一个值 被兑现 ,要么会通过一个原因(错误) 被拒绝 。 当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。 如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序也同样会被调用,因此在完成异步操作和绑定处理方法之间不存在竞态条件
http和https的区别
1.HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
2.使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。
3.HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
4.http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
5.HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
6.HTTP和HTTPS的传输不一样,所以他们对应的端口也不一样,一个是80,一个是443。
promise的方法,为什么Promise解决了回调地狱问题
romise 构造函数的入参通常为一个执行器函数(executor function),该函数接收两个参数:resolve 和 reject 函数。
从回调地狱的多层嵌套方面回答
Promise通过链式调用的方式,将多个异步操作串联起来,使得代码更加简洁易懂当一个异步操作完成时,它会返回一个Promise对象,这个对象可以通过then方法来指定回调函数。这样就可以避免回调函数嵌套的问题,使得代码更加清晰
垃圾回收机制
JavaScript中的垃圾回收机制(Garbage Collection)是自动化的内存管理过程,由JavaScript引擎负责。垃圾回收机制的主要目的是帮助开发人员管理内存,以防止内存泄漏和其他内存问题。
JavaScript中的垃圾回收器通常使用标记清除算法(Mark and Sweep)来标记和清除不再使用的对象。该算法使用根集作为起点,遍历对象图来标记所有可达对象,然后清除所有未被标记的对象。
在JavaScript中,根集包括全局对象、正在执行的函数的局部变量和参数、正在执行的函数的调用栈以及JavaScript代码中其他一些活动对象。垃圾回收器定期遍历根集并标记与其相关的所有可达对象,然后清除所有未被标记的对象。
在JavaScript中,垃圾回收器通过以下两种方式来释放不再使用的对象的内存:
1. 标记清除算法:垃圾回收器定期遍历内存中的所有对象,并标记所有可达对象,然后清除未被标记的对象。
2. 引用计数算法:在引用计数算法中,垃圾回收器维护每个对象的引用计数器。当对象被引用时,计数器加1,当对象不再被引用时,计数器减1。当计数器减到0时,垃圾回收器就可以清除该对象。
需要注意的是,JavaScript的垃圾回收机制是自动的,开发人员无法精确控制垃圾回收的时机和过程。因此,在编写JavaScript代码时,应该避免创建大量临时对象和循环引用的情况,以帮助垃圾回收器更快地释放内存。
vuex为什么要设计成actions那些个,而不是直接允许用户修改
前端项目中有过哪些优化的经历?(节流防抖,条件渲染,回流重绘,网络请求)
1. 减少HTTP请求数量:合并和压缩JavaScript和CSS文件,使用CSS Sprites合并图片,减少页面中的外部资源数量,以减少页面的加载时间。
2. 使用CDN:使用CDN可以加速静态资源的加载速度,从而提高页面的性能。
3. 图片优化:使用适当的图片格式,压缩图片大小以减少页面的下载时间。同时,使用懒加载技术可以提高页面的加载速度。
4. 前端缓存:使用浏览器缓存和CDN缓存等前端缓存技术可以减少重复的请求和数据传输,从而提高页面的性能。
5. 代码优化:尽可能减少页面的DOM操作和JavaScript的执行时间,避免页面渲染阻塞和JavaScript代码阻塞等问题,使用异步请求和异步加载技术等可以提高页面的性能。
6. 移动端适配:对于移动端页面,需要进行适配和优化,使用响应式布局或者移动端专用的布局技术,避免页面在移动设备上显示不全或者排版混乱等问题。
7. 后端接口优化:优化后端接口的响应速度和性能,减少数据传输量和响应时间,可以大大提高前端页面的性能。
判断两个版本号大小,如1.2.1>1.2.0
js隐式转换
在Vue Router中,params传参和query传参都是用于路由传参的方式。
params传参是通过路由的形式把参数传递过去,不会显示在地址栏中,但是不能刷新。params只能配合name使用,如果提供了path,params会失效。而query传参则是把参数放在url后面,可以刷新,但是参数会显示在地址栏中。query传参可以通过$route.query或者this.$route.query来获取参数。
promise和settimeout的异步有什么区别
我们了解了`setTimeout`和`Promise`调用的都是异步任务,这一点是它们共同之处,也即都是通过任务队列进行管理/调度。那么它们有什么区别吗?下文继续介绍。JavaScript通过任务队列管理所有异步任务,而任务队列还可以细分为MacroTask Queue和MicoTask Queue两类。
MacroTask Queue
MacroTask Queue(宏任务队列)主要包括`setTimeout`, `setInterval`, `setImmediate`, `requestAnimationFrame`, `UI rendeing`, NodeJS中的`I/O等。
MicroTask Queue
MicroTask Queue(微任务队列)主要包括两类:
1.独立回调microTask:如Promise,其成功/失败回调函数相互独立
2.复合回调microTask:如 `Object.observe`, `MutationObserver` 和NodeJs中的 `process.nextTick` ,不同状态回调在同一函数体
JavaScript将异步任务分为MacroTask和MicroTask,那么它们区别何在呢?
1.依次执行同步代码直至执行完毕
2.检查MacroTask 队列,若有触发的异步任务,则取第一个并调用其事件处理函数,然后跳至第三步,若没有需处理的异步任务,则直接跳至第三步
3.检查MicroTask队列,然后执行所有已触发的异步任务,依次执行事件处理函数,直至执行完毕,然后跳至第二步,若没有需处理的异步任务中,则直接返回第二步,依次执行后续步骤
4.最后返回第二步,继续检查MacroTask队列,依次执行后续步骤
5.如此往复,若所有异步任务处理完成,则结束
大白话讲,脚本同步执行一遍,然后按顺序看看所有异步,macrotask(settimeout)队列第一个执行一下。然后看有microtask队列有没有promise,没有,那就执行第二个settimeout,再次检查一下有没有promise,有,那就执行,执行完再检查有没有macrotask(settimeout),有就执行,执行完了继续找promise,没有的话就继续找macrotask
Promise和setTimeout都是异步操作,但是它们的本质不同。Promise属于异步微任务(microtask),而setTimeout则是异步宏任务(macrotask)。在JS的编译与执行过程中,当进行到异步操作时,会优先处理微任务,直到所有微任务与同步程序处理完毕才会开始处理宏任务。因此,Promise的执行顺序优先于setTimeout,另外,Promise的返回值可以被后续的then方法获取到,而setTimeout的返回值是一个新的task。根据HTML标准,一个task执行完后,UI会重渲染,所以micro task更新完数据后再渲染dom的操作要比setTimout的性能要好
http怎么做静态资源缓存
HTTP缓存机制主要分为强制缓存和协商缓存两种方式。强制缓存是通过设置HTTP响应头来实现的,可以让浏览器直接从本地缓存中读取资源,而不需要再向服务器发起请求。常用的响应头有Expires和Cache-Control,其中Cache-Control的优先级更高。协商缓存则是通过比较客户端和服务器端的资源是否一致来判断是否需要重新请求资源。常用的响应头有Last-Modified和ETag
reactnative
ts类型推导
TypeScript的类型推断是指在代码中不需要显式地指定变量的类型,而是让 TypeScript 根据变量的值自动推断出变量的类型。TypeScript会根据变量的值来判断变量的类型,如果变量的值是字符串,那么TypeScript就会把这个变量的类型设置为字符串。这样可以减少代码中的重复性,提高代码的可读性和可维护性。