HTML
h5语义化标签以及作用
header :头部
nav:导航
article:内容
section:块级
aside:侧边栏
footer:尾部
好处:
1.去掉或者丢失样式的时候能够让页面呈现出清晰结构
2.有利于SEO
3.方便其他设备解析
CSS
伪类和伪元素
伪元素和伪类的根据区别就是:前者是创建出了一个新元素,而后者是一个已存在但你不能直接看到的元素。
伪元素
::before ::after
- 默认是行内元素
- content必须添加,即便是空。否则伪元素不生效。
- 伪元素在网页中无法通过鼠标直接复制粘贴。
伪类
伪类用于定义元素的特殊状态
例如。它可以有以下作用
- 为元素设置鼠标悬停上时的样式
- 链接已点击和未点击时的样式
- 设置元素获得焦点的样式
定位
- 静态定位
- 相对定位
- 改变的位置是参照自己原来的位置
- 绝对定位(脱标)
- 先找已经定位的父级(一般是 相对定位),以这个父级为参照物
- 如果父级没有定位,那么以浏览器窗口为参照物。
- 固定定位(脱标)
- 改变位置参考浏览器窗口
- 具备行内块特点
css布局方式
- 静态布局
- Float 布局
- 绝对布局
- 自适应布局
- 流式布局(又别名 百分比布局 %)
- 左侧固定 右侧自适应
- 左右固定宽度 中间自适应
- 圣杯布局
- 双飞翼布局
- 响应式布局:媒体查询
- 弹性布局 (rem/em flex布局)
- rem/em
- flex布局
清除浮动
- 给父元素增加一个样式overflow:auto;(overflow表示溢出),这个样式的意思是对于超出边界的元素,父元素可以自动调整。
- 使用clear:left/right/both;(clear意思清除浮动),当前元素如果不想受到前面浮动元素影响时,增加clear。
那些方式可以脱离文档流
- 浮动
- 绝对定位
- 固定定位
垂直水平居中有哪些方法
- 绝对定位 负margin
- 绝对定位 margin auto
- 绝对定位 transform translate
- flex display:flex; justify-content: center; align-items: center
CSS动画
animation 的子属性有:
- animation-name:指定由 @keyframes 描述的关键帧名称。
- animation-duration:设置动画一个周期的时长。
- animation-delay:设置延时,即从元素加载完成之后到动画序列开始执行的这段时间。
- animation-direction:设置动画在每次运行完后是反向运行还是重新回到开始位置重复运行。
- animation-iteration-count:设置动画重复次数, 可以指定 infinite 无限次重复动画
- animation-play-state:允许暂停和恢复动画。
- animation-timing-function:设置动画速度, 即通过建立加速度曲线,设置动画在关键帧之间是如何变化。
- animation-fill-mode:指定动画执行前后如何为目标元素应用样式
- @keyframes 规则,当然,一个动画想要运行,还应该包括 @keyframes 规则,在内部设定动画关键帧
javascript
堆和栈的存储方式
复杂数据类型(Object、[数组、对象])创建的时候 开辟的 堆内存
基本数据类型(number、string等6种)创建的时候 开辟的 栈内容
什么是虚拟DOM
虚拟DOM其实就是用一个原生的JS对象去描述一个DOM节点,实际上它只是对真实 DOM 的一层抽象。最终可以通过一系列操作使这棵树映射到真实环境上。
相当于在js与DOM之间做了一个缓存,利用patch(diff算法)对比新旧虚拟DOM记录到一个对象中按需更新, 最后创建真实的DOM。
let和const的区别
let声明的变量可以改变,值和类型都可以改变;而const声明的常量不可以改变
const保证的是内存地址不能被修改,所以能够修改对象
用什么检测数组
- instanceof方法
- Array.isArray
- 数组构造函数,constructor属性来判断
- Object.prototype.toString.call()判断 结果是 ‘[object Array]’
如何判断为一个对象
- 使用Object.prototype.toString.call来判断;
- 使用“obj.constructor === Object”来判断;
- 使用“typeof obj === Object”来判断;
- 利用instanceof关键字来判断。
instanceof如何判断一个对象(流程)
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
var变量提升
var声明一个变量时,该变量会被提升到作用域的顶端,但是赋值的部分并不会被提升。
原理:
JS引擎的工作方式是 :
1、先解析代码,获取所有被声明的变量;
2、然后在运行。也就是专业来说是分为预处理和执行两个阶段。
代码语言:javascript复制 console.log(a); //undefined
var a = "9";
// 实际运行表示变量a已声明未赋值,在js引擎中的运行过程是:
var a;
console.log(a);
a = "9"
什么是原型和原型链
- 原型:每个函数都有 prototype 属性,该属性指向原型对象
- 原型链:每个对象都拥有一个原型对象,通过proto 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。
什么是作用域链
作用域分为全局作用域,局部作用域和块级作用域
在访问一个变量的时候,首先在当前作用域中寻找,如果找不到再从外层作用域寻找。这样一层一层查找,就形成了作用域链
闭包的理解
闭包 函数和声明该函数的词法环境的组合(两个嵌套关系的函数,内部函数可以访问外部函数定义的变量) 闭包的优点:1、形成私有空间,避免全局变量的污染 2、持久化内存,保存数据 闭包的缺点:1、持久化内存,导致内存泄露 解决内存泄漏:在退出函数之前,将使变量赋值为null;
闭包的使用场景
- 立即执行函数
- cache缓存
- setTimeout定时器
- 异步操作
- 方法的值return出去
es6-es10新增常用方法
代码语言:javascript复制es6:
1、let、const
2、解构赋值 let { a, b } = { a: 1, b: 2 }
3、箭头函数 ()=>{}
4、字符串模板 ``
5、扩展运算符 ...arr
6、数组方法:map、filter、some等等
7、类:class关键字
8、promise 主要用于异步计算
9、函数参数默认值 fn(a = 1) {}
10、对象属性简写 let a = 1; let obj = {a}
11、模块化:import--引入、exprot default--导出
es7:
1、includes()方法,用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。
es8:
1、async/await
es9:
1、Promise.finally() 允许你指定最终的逻辑
es10:
1、数组Array的flat()和flatmap()
flat:方法最基本的作用就是数组降维
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity 作为深度,展开任意深度的嵌套数组
arr3.flat(Infinity);
// [1, 2, 3, 4, 5, 6]
flatmap:方法首先使用映射函数映射(遍历)每个元素,然后将结果压缩成一个新数组
***8、let、const和var的区别
代码语言:javascript复制1、var声明变量存在提升(提升当前作用域最顶端),let和const是不存在变量提升的情况
2、var没有块级作用,let和const存在块级作用域
3、var允许重复声明,let和const在同一作用域不允许重复声明
4、var和let声明变量可以修改,const是常量不能改变
函数的防抖和节流
定义:
防抖: 就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。商品搜索 延时器
节流: 就是指连续触发事件但是在设定的一段时间内中只执行一次函数。(登录按钮)
防抖和节流的实现
代码语言:javascript复制 <body>
<input type="text" class="ipt" />
<script>
var timerId = null
document.querySelector('.ipt').onkeyup = function () {
// 防抖
if (timerId !== null) {
//延时器
clearTimeout(timerId)
}
timerId = setTimeout(() => {
console.log('我是防抖')
}, 1000)
}
document.querySelector('.ipt').onkeyup = function () {
// 节流
console.log(2)
if (timerId !== null) {
return
}
timerId = setTimeout(() => {
console.log('我是节流')
timerId = null
}, 1000)
}
</script>
</body>
js的运行机制是什么
js是单线程执行的,页面加载时,会自上而下执行主线程上的同步任务,当主线程代码执行完毕时,才开始执行在任务队列中的异步任务。具体如下 :
1.所有同步任务都在主线程上执行,形成一个执行栈。 2.主线程之外,还存在一个"任务队列(eventloop队列或者消息队列)“。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。 3.一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列”,看看里面有哪些事件。哪些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 4.主线程不断重复上面的第三步。
箭头函数和普通函数的区别
1、外形不同:箭头函数使用箭头定义,普通函数中没有。
2、 箭头函数全都是匿名函数:普通函数可以有匿名函数,也可以有具名函数
3、箭头函数不能用于构造函数:普通函数可以用于构造函数,以此创建对象实例。
4、箭头函数中 this 的指向不同:在普通函数中,this 总是指向调用它的对象,如果用作构造函数,它指向创建的对象实例。
5、箭头函数不具有 arguments 对象:每一个普通函数调用后都具有一个arguments 对象,用来存储实际传递的参数。但是箭头函数并没有此对象。
6、其他区别:箭头函数不具有 prototype 原型对象。
深拷贝浅拷贝
概念
浅拷贝: 拷贝对象的一层属性,如果对象里面还有对象,拷贝的是地址, 两者之间修改会有影响,适用于对象里面属性的值是简单数据类型的.
深拷贝: 拷贝对象的多层属性,如果对象里面还有对象,会继续拷贝,使用递归去实现.
方式
- 浅拷贝
Object.assign({}, obj)
展开运算符
- 深拷贝
- 递归
const obj = {
name: 'Tricia',
age: 26,
love: {
friuts : 'apple',
meat: 'beef'
}
}
function getObj(obj) {
const newObj = {}
for (let k in obj) {
// 如果对象的属性还是对象,那么就递归调用这个函数,如果不是,就赋值
newObj[k] = typeof obj[k] === 'object' ? getObj(obj[k]) : obj[k]
}
return newObj
}
var obj2 = getObj(obj)
console.log(obj2)
JSON.parse JSON.stringfy
存在一些问题:
- ⽇期类型的数据会直接变成字符串的形式,⽽不是对象的形式
- 正则类型的数据会变成空对象{}
- 函数会丢失
数组遍历方法
forEach map区别?
用forEach、map函数对引用类型的数组元素的属性值进行了修改,原数组也会跟着改变。 对操作数组进行深拷贝。用拷贝的对象调用数组处理方法,原数组就不会改变了
1、forEach:遍历开始以后无法停止,如果要遍历整个数组,那就使用这个方法;
2、map:根据当前数组映射出一个新的数组;
3、some:遍历整个数组,返回值true就停止循环(返回false继续循环)
返回值:如果数组中的有一项回调函数返回true,那么结果为true,否则为false;(或者这样理解:数组别遍历完,那么结果为false,否则为true)
4、every:与some相反,返回false就停止循环(返回true就继续循环)
5、filter:过滤数组,返回一个新的数组
6、reduce:实现数据的累加
图片懒加载 原生js实现
scrollTop
判断图片滚动的距离, innerHeight
求当前视口高度,offsetTop
得到图片相对于父元素的位置,然后 scrollTop clientH - elementTop - 图片自身的高度
得到元素当前的位置。
如果>=0
说明该元素在视口内,给元素的url赋值
let imgs = document.querySelectorAll('img')
console.log(imgs);
scrollFn()
// 监听滚动事件
window.onscroll = scrollFn
function scrollFn() {
let clientH = window.innerHeight // 可视区高度
let scrollTop = document.documentElement.scrollTop // 滚动的高度
console.log(clientH, scrollTop)
Array.from(imgs).forEach(item => {
let elementTop = item.offsetTop // 元素相对于父元素的位置
// console.log('eleTop', elementTop)
let count = scrollTop clientH - elementTop - item.height // 元素的位置
console.log('count',count)
if(count >= 0) {
// 如果大于等于0,说明在可视区内,给图片赋值
item.setAttribute('src', item.getAttribute('data-url'))
}
})
}
原生js怎么操作dom
- 创建元素:document.createElement()参数是创建元素的标签名
- 操作元素:
- appendChild(): 末尾添加一个节点,并且返回这个新增的节点。
- 、insertBefore(): 将节点插入到某个特定的位置
- replaceChild(): 替换节点
- removeChild(): 删除节点
- cloneNode(true/false): 克隆节点
- 元素选择
- querySelector()、querySelectorAll()
- getElementById()
js垃圾回收机制
js的内存分配和回收都是自动完成的,内存不使用时,会被垃圾及回收机制自动回收
回收机制有两种:
- 引用计数(IE)
- 看⼀个对象是否有指向它的引⽤,如果没有任何变量指向它, 说明对象已经不需要了, 该被释放
- 如果对象空间的引⽤计数, 引⽤为 0, 就需要释放 缺点: 引⽤计数存在 循环引⽤ 的问题, 造成了内存泄露
- 标记清除(主流浏览器)
- 回收策略: 将不再使⽤的对象 定义为 ⽆法到达的对象, ⽆法到达的对象要回收
- 从window出发, 定时扫描内存中的对象
- 凡是从根部能到达的对象, 都是还要使⽤的, 如果⽆法从根部出发, 触及到, 就会被标记为不再使⽤
js循环
性能: for > for-of > forEach > map > for-in
- 传统for循环
- forEach for… in… for…of… 1、for…of:是interator的语法糖,它能够遍历出interator所有可迭代的所有数据类型和数据结构,Set、Map、字符串、数组,但不能遍历对象 2、forEach:只能遍历数组使用,不能用作其他也能迭代对象 3、for…in:是唯一一个可以迭代对象的一种语法结构,当然,也可以迭代数组、字符串
- map: 创建一个新数组,新数组的结果是原数组中的每个元素都调用一次提供的函数后的返回值。
js排序
- sort方法⭐:注意,默认是按照字符编码从小到大的顺序排序
- 冒泡排序
- 比较所有相邻元素,如果第一个比第二个大,则交换它们
- 一轮下来保证可以找到一个数是最大的
- 执行n-1轮,就可以完成排序
- 选择排序
- 找到数组中的最小值,选中它并将其放置在第一位
- 接着找到第二个最小值,选中它并将其放置到第二位
- 执行n-1轮,就可以完成排序
- 插入排序
- 从第二个数开始往前比
- 比它大就往后排
- 以此类推进行到最后一个数
- 快排
- 分区: 从数组中任意选择一个基准,所有比基准小的元素放到基准前面,比基准大的元素放到基准的后面
- 递归:递归地对基准前后的子数组进行分区
Vue
vuex执行流程
- 如果是同步的情况 直接在页面中 commit mutation,mutation 中 修改 state 即可
- 如果是异步的情况需要
- 先在页面中dispatch action
- 其次在action中发送请求,进行操作
- 然后action中commit mutation
- 最后修改 state
vuex里的数据,刷新为什么会丢失,怎么解决
因为JS的数据都是保存在浏览器的堆栈内存⾥⾯的,当⻚⾯刷新时,⻚⾯会重新加载vue实例,vuex⾥⾯的数据就会被重新赋值。
解决:存入本地缓存
vue的传值方式
父组件向子组件传值
父组件通过属性的方式向子组件传值,子组件通过props来接受。
子组件接受的父组件的值分为引用数据类型和普通数据类型两种。
基于vue的单向数据流原则,组件之间的数据是单向流通的,子组件不允许直接对父组件的值进行修改,所以要避免直接修改父组件传过来的值得情况。
子组件向父组件传值
子组件绑定一事件,并通过$emit来触发这个事件
兄弟组件传值
- 通过eventbus进行兄弟组件通讯,
on 监听回调,回调函数接收所有触发事件时传入的参数
- 使用vuex ,pinia
- vuex使用数据:
$store.state.模块名.属性名
- pinia使用数据:
直接模块名.属性名
- vuex使用数据:
多层父子组件通信
provide/inject
简单来说就是在父组件中通过provide来提供变量,然后在子组件中通过inject来注入变量,不管组件层级有多深,在父组件生效的生命周期内,这个变量就一直有效。
注意: provide和inject的绑定不是响应式的 解决办法: Vue.observable 优化响应式 provide
组件封装
用vue开发的所有项目,都是采用组件化的思想开发的。一般我在搭建项目的时候,会创建一个views目录和一个comment目录,views目录中放页面级的组件,comment中放公共组件
首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性低等问题。
我们一般用脚手架开发项目,每个 .vue
单文件就是一个组件。在另一组件import
导入,并在components
中注册(install
函数注册组件),子组件需要数据,在props
中接受。而子组件修改好数据后采用$emit
方法将数据传递给父组件。
封装的组件举例
- 数字框组件
- 表格组件
- 对话框组件
- 树形组件
- 消息提示组件
插槽
- 什么是插槽 1.1 插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。 1.2 插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制
- 插槽使用 2.1 默认插槽 在子组件中写入slot,slot所在的位置就是父组件要显示的内容 2.2 具名插槽 在子组件中定义了三个slot标签,其中有两个分别添加了name属性header和footer 在父组件中使用template并写入对应的slot名字来指定该内容在子组件中现实的位置 2.3 作用域插槽 在子组件的slot标签上绑定需要的值 在父组件上使用slot-scope=“user”来接收子组件传过来的值
Keep-alive
keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM
keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
对应两个钩子函数 activated和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated
vue响应式原理
Vue2响应式原理:
Vue 的响应式原理是核心是通过 ES5 的Object.defindeProperty 进行数据劫持,然后利用 get 和 set 方法进行获取和设置,data 中声明的属性都被添加到了get和set中,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发,重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
Vue3响应式原理:
通过Proxy(代理): 拦截对data任意属性的任意操作, 包括属性值的增删改查
通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
Vue的运行机制(new Vue做了什么)
- new Vue之后,Vue会调用init函数进行初始化,会初始化生命周期,事件,props、methods、 data、 computed 与 watch 等。
- 初始化之后会调用$mount挂载组件
- 然后进行编译 首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。 Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。 编译的最后一步是将优化后的AST树转换为可执行的代码。
Vue2和Vue3的区别
- vue2和vue3响应式不同:
- Vue2使用的是通过defineProperty对对象的已有属性值的读取和修改进行劫持监视/拦截
- Vue3使用的是proxy
- Vue2采用的是options API, Vue3采用的是Composition API
- Composition API几乎是函数,会有更好的类型推断。
- 在逻辑组织和逻辑复用方面,Composition API是将相同功能的代码写在一起,便于复用。优于Options API
- Composition API中见不到this的使用,减少了this指向不明的情况
- Vue3支持碎片, 就是说在组件可以拥有多个根节点。Vue2只能有一个根节点。
- Vue2我们把数据放在了data函数中,数据以函数返回值的形式定义,Vue3中我们使用的是新的setup()方法,此方法在组件初始化时触发。
- Vue2和Vue3的生命周期钩子函数也是不同的 Vue2--------------vue3 beforeCreate -> setup() created -> setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted activated -> onActivated deactivated -> onDeactivated
Vue3为什么用proxy来代替defineProperty
defineProperty:
- 检测不到对象属性的添加和删除
- 数组API方法无法监听到
- 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题
Vue3生命周期钩子函数
setup() : 开始创建组件之前,在beforeCreate 和 created 之前执行,创建的是data 和 method
- onBeforeMount() : 组件挂载到节点上之前执行的函数;
- onMounted() : 组件挂载完成后执行的函数;
- onBeforeUpdate(): 组件更新之前执行的函数;
- onUpdated(): 组件更新完成之后执行的函数;
- onBeforeUnmount(): 组件卸载之前执行的函数;
- onUnmounted(): 组件卸载完成后执行的函数;
- onActivated(): 被包含在 中的组件,会多出两个生命周期钩子函数,被激活时执行;
- onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;
- onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。
vue Router有哪些模式
- hash 模式:后面的 hash 值的变化,浏览器既不会向服务器发出请求,浏览器也不会刷新,每次 hash 值的变化会触发 hashchange 事件。
- history 模式:利用了 HTML5 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
v-model原理
一方面model层通过defineProperty来劫持每个属性,一旦监听到变化通过相关的页面元素更新。
另一方面通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值。
vue的父组件和子组件的生命周期钩子函数执行顺序
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
1)加载渲染过程
代码语言:javascript复制 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
2)子组件更新过程
代码语言:javascript复制 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
3)父组件更新过程
代码语言:javascript复制 父 beforeUpdate -> 父 updated
4)销毁过程
代码语言:javascript复制父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
computed和watch的区别是什么
computed:
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- computed需要对数据进行修改时,需要get set两个方法,数据变化 ,调用set方法
- computed擅长处理的场景:一个数据受多个数据的影响(购物车计算总价)
watch:
- 不支持缓存,数据变化会直接触发相应的操作
- 支持异步,监听的函数接收2个参数,第一个参数是最新的值,第二个参数是输入之前的值
- immediate: 组件加载立即触发回调函数执行
- deep: true的意思就是深入监听,任何修改obj里面任何一个属性都会触发这个监听器里的 handler方法来处理逻辑
- watch擅长处理的场景:一个数据影响多个数据,例如搜索框
路由传参的方式和区别
params
和query
- params用的是name,传递的参数不会在地址栏中显示。但是刷新页面会丢失(结合本地存储)
- query用的是path,传递的参数会在地址栏中显示。刷新页面不会丢失(常用)
params刷新页面,路由信息丢失
配合localStorage sessionStorage实现刷新页面后数据不丢失.
v-for可以遍历的数据类型
- 数组
- 对象数组
- 对象
- 迭代数字
v-for为什么要加key
作用:
代码语言:javascript复制 1.key的作用主要是为了高效的更新虚拟DOM,提高渲染性能。
2.key属性可以避免数据混乱的情况出现。
原理:diff算法
代码语言:javascript复制 1.vue实现了一套虚拟DOM,使我们可以不直接操作DOM元素只操作数据,就可以重新渲染页面,而隐藏在背后的原理是高效的Diff算法
2.当页面数据发生变化时,Diff算法只会比较同一层级的节点;
3.如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点后面的子节点;
如果节点类型相同,则会重新设置该节点属性,从而实现节点更新
4.使用key给每个节点做一个唯一标识,Diff算法就可以正确失败此节点,"就地更新"找到正确的位置插入新的节点。
v-for的key为什么不推荐index
key的作用主要是为了高效的更新虚拟DOM。
如果key绑定的是index的话,index是会变化的。当插入数组的时候,原来的下标是2,现在可能变成3了,就达不到一一对应的关系,就提高不了性能,所以key一定要绑定的是唯一性的。
如何将解决跨域问题
- cors
- jsonp
- 设置代理服务器(前端) vue.config.js中⭐
同步异步
异步发展历程
- 回调函数
- 解决了同步问题
- 但是产生了回调地狱,不能用try catch捕获,不能return
- 回调地狱的根本问题
- 缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符;
- 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转);
- 嵌套函数过多的多话,很难处理错误
- Promise
- 就是为了解决回调地狱存在的
- 实现了链式调用,每次.then后都是一个全新的Promise, 我们在then中return,return的结果会被Promise.resolve包裹
- 优点:解决了回调地狱
- 缺点:无法取消Promise,错误需要通过回调函数来捕获;
- Async/Await
- 优点:代码清晰,不用像Promise写了一大堆then链,处理了回调地狱的问题;
- 缺点:await将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用await会导致性能上的降低。
promise如何解决回调地狱
promise对应的有一个.then方法,可以将内部成功或者失败的结果给传出来
- 在这里我们首先调用了getdata函数,因为getdata函数内部返回的是一个promise对象,那么就应该有.then方法,.then使用的话内部会有一个函数。函数的形参就是上面promise返回的成功和失败的结果.
- 在我们的.then方法里面我们又返回了使用数据的函数,因为使用数据函数内部封装的也是一个promise对象,所有也会对应有一个promise对应的方法,接着我们再用一个函数形参去接受返回的结果
function getdata() {
return new Promise((resove, reject) => {
setTimeout(() => {
resove("获取数据");
}, 2000);
});
}
function setdata() {
return new Promise((resove, reject) => {
setTimeout(() => {
resove("使用数据");
}, 1000);
});
}
getdata()
.then(function (value) {
console.log(value);
return setdata();
})
.then(function (value) {
console.log(value);
});
//promise对应的有一个then方法,可以将内部成功或者失败的结果给传出来
//1 在这里我们首先调用了getdata函数,因为getdata函数内部返回的是一个promise对象,那么就应该有.then方法,.then使用的话内部会有一个函数函数的形参就是上面promise返回的成功和失败的结果.
//在我们的.then方法里面我们又返回了使用数据的函数,因为使用数据函数内部封装的也是一个promise对象,所有也会对应有一个promise对应的方法,接着我们再用一个函数形参去接受返回的结果
//这样就解决了我们遇到的回调地狱问题,但是写多了,还是会发现一些问题.
promise的.then的rejected函数和.catch区别
- 第一种 catch 方法可以捕获到 catch 之前整条 promise 链路上所有抛出的异常。
- 第二种 then 方法的第二个参数捕获的异常依赖于上一个 Promise 对象的执行结果。
axios
是通过promise实现对ajax技术的一种封装,
AJAX与axios区别
- axios是一个基于Promise的HTTP库,而ajax是对原生XHR的封装;
- ajax技术实现了局部数据的刷新,而axios实现了对ajax的封装。
如何封装axios
- 设置接口请求前缀:根据开发、测试、生产环境的不同,前缀需要加以区分
if (process.env.NODE_ENV === 'development') {
axios.defaults.baseURL = 'http://dev.xxx.com'
} else if (process.env.NODE_ENV === 'production') {
axios.defaults.baseURL = 'http://prod.xxx.com'
}
- 设置请求头与超时时间
- 封装请求拦截器,给每个请求添加token
- 封装响应拦截器,统一做消息提示处理。
typescript
对TS的理解
- 是什么? ts是js的超集,支持ES6语法,支持面向对象的编程概念,如类,接口,继承,泛型等 它是一种静态类型的检查语言,提供了类型注解,在代码编译阶段就能检查出数据类型的错误
- 特性?
ts的主要特性:
- 类型批注和编译时类型检查 :在编译时批注变量类型
- 类型推断:ts 中没有批注变量类型会自动推断变量的类型
- 类型擦除:在编译过程中批注的内容和接口会在运行时利用工具擦除
- 接口:ts 中用接口来定义对象类型
- 枚举:用于取值被限定在一定范围内的场景
- Mixin:可以接受任意类型的值
- 泛型编程:写代码时使用一些以后才指定的类型
- 名字空间:名字只在该区域内有效,其他区域可重复使用该名字而不冲突
- 元组:元组合并了不同类型的对象,相当于一个可以装不同类型数据的数组
用TS实现的比较复杂的类型
ts常见复杂类型有object、数组、元组、枚举、普通对象
- 数组:使用[]定义,并明确指定数组元素的类型 let arrayOfNumber: number[] = [1, 2, 3];
- 元组类型:最重要的特性是可以限制数组元素的个数和类型,特别适合用来实现多值返回。
TS中type和interface的区别
相同点:
都可以描述一个对象或者函数 不同点: type 可以声明基本类型别名,联合类型,元组等类型
type 语句中还可以使用 typeof 获取实例的 类型进行赋值
interface 能够声明合并
TS泛型
泛型允许我们在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型 在typescript
中,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性
浏览器相关
地址栏输入url敲下回车发生了什么?
- 浏览器查找当前 URL 是否存在缓存,并⽐较缓存是否过期
- DNS 解析 URL 对应的 IP
- 根据 IP 建⽴ TCP 连接(三次握⼿)
- 发送 http 请求
- 服务器处理请求,浏览器接受 HTTP 响应
- 浏览器解析并渲染⻚⾯
- 关闭 TCP 连接(四次握⼿)
浏览器从请求数据到渲染在页面的过程
- 解析html生成DOM
- 解析css并构建CSSOM
- 执行javascript
- 合并DOM和CSSOM来构造渲染树
- 计算布局
- 渲染
如何禁止回退
history.pushState,可以添加浏览器的历史记录,你在进入页面的时候先pushState一个新页面(#hash),然后监听popstate事件。
页面修改了history,会触发popstate事件。触发时检测到url地址为刚进页面时的地址,就再pushState那个#hash。
浏览历史记录中会始终留下一个页面,后退按钮失效。
浏览器兼容性问题
- 不同浏览器的默认样式不同,可以使用
Normalize.css
解决。Normalize.css
是一个可以定制的CSS文件,它让不同的浏览器在渲染网页元素的时候形式更统一。Normalize.css
只是一个很小的css文件,但它在磨人的HTML元素样式上提供了跨浏览器的高度一致性。 相比于传统的CSS reset
,Normalize.css
是一种现代的、为HTML5准备的优质替代方案。总之,Normalize.css
是一种CSS reset
的替代方案。 - 不同浏览器的标签默认的
margin
和padding
不同- 使用CSS里
*{margin:0;padding:0;}
解决。 - 引入
reset.css
样式重置
- 使用CSS里
- css3新属性,加
浏览器前缀
兼容早期浏览器 - css
hack
解决浏览器兼容性
background-color:yellow0; 0 是留给ie8的
background-color:pink; ie7定了;
_background-color:orange; _专门留给ie6;
资源文件的加载顺序
先加载index.html,查询依赖的css文件、js文件,再加载js import的文件
优化
瀑布流优化
由于瀑布流在下拉很久后会页面上的数据和dom越来越多,以至内存暴增、应用卡顿或闪退,所以需要对已经划过去的dom节点做释放,方法有两种:
- 已经划过去的dom节点清空内部,保留外壳 1.1 每加载一个dom记录高度到它的style上 1.2 记录当前位置(index):除了它的上下5条,其它保留高度,隐藏内容 1.3 页面滚动时计算index是否滚上去,是的话index ;是否滚下去,是的话index–
- 在顶部添加一个占位dom,已经划过去的dom节点清空,高度累加到占位dom上 2.1 高度数组(hList),每加载一个dom记录高度到hList 2.2 记录当前位置(index):除了它的上下5条,其它高度累加到占位dom,隐藏自己 2.3 页面滚动时计算index是否滚上去,是的话index ,hList加上滚上去的dom高度 2.4 在底部再加添加一个占位dom,用来处理向下滚
白屏优化
- SSR 方式 1、SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端。 2、SSR有着更好的SEO、并且首屏加载速度更快等优点。不过它也有一些缺点,比如我们的开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境。还有就是服务器的压力比较大。
- 骨架屏
- 文件和数据缓存、减少请求
- 懒加载
CDN优化
将文件的静态资源移存到不同区域的CDN节点服务器上,当用户访问网络的时候,会去距离用户最近的CDN服务器上获取,避免网络拥塞,提高访问速度。
具体原理
1.用 户 输 入 要 访 问 的url
,浏 览 器 通 过 域名解析(本地DNS系统和CDN专用DNS服务器)得到CDN负载均衡设备的IP地址。
2.浏览器向CDN负载均衡发出访问,CDN负载均衡根据用户IP地址及URL,判断距离、有无内容、负载情况后返回用户所属区域的最佳cdn缓存服务器IP。
3.用户向cdn缓存服务器发起请求,服务器响应用户请求。
4.如果这台缓存服务器上没有用户想要的内容,再由缓存服务器向源服务器请求。
5.缓存服务器从源服务器得到内容后,一方面在本地进行缓存,另一方面将获取的数据返回给客户端
图片的优化方法
- 图片过多,进行
懒加载
- 大量小图片,
css精灵图
- 将小图片压缩成
base64
格式来节约请求 - 图片过大,借助第三方软件进行压缩
canvas
来代替图片
网络角度谈优化
- 尽量避免重定向
- DNS预解析
- http缓存
- 减少http请求次数
- 减少请求头大小,合理管理使用
cookie
和域名 - 减少请求的响应体大小
- 使用
CDN
- 合理使用本地缓存
- 占位图使用
- 利用
service workers
进行资源的离线缓存,用户再次访问时,可以利用离线缓存迅速打开应用。 - 利用浏览器的并发数限制
- http2.0
- header压缩 HTTP1.1每一次通信都会携带一组头部,用于描述这次通信的的资源、浏览器属性、cookie等。 而在HTTP2下: HTTP/2在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新;每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。
- 多路复用 http1.1下,想要并发多个请求,必须使用多个TCP连接,并且浏览器为了控制资源,对单个域名还有6个左右的连接数限制,超过限制数量的就需要pending。 http2.0下,多路复用的这个特性使得性能极大提升: 同个域名只需要占用一个 TCP 连接,消除了因多个 TCP 连接而带来的延时和内存消耗。单个连接上可以并行交错的请求和响应,之间互不干扰。
html优化
- 添加
link预加载标签
来实现预先加载 - html结构尽可能少,最好不超过6层
- 尽可能的使用
H5元素
来创建语义化的结构
CSS优化
- css压缩:将写好的css进行打包,可以减少体积
- 使用link引入css文件
- 合理设计布局,注意样式复用
- 合理选择选择器
其他
移动端适配
- rem适配
- 安装
postcss-pxtorem
用于将px单位转化为rem - 安装
amfe-flexible
用于设置 rem 基准值 根据设置屏幕的宽度去调整rem的值(html标签上font-size的大小) 它的默认计算方式是屏幕宽度的1/10,默认值是37.5- amfe-flesabile 监听视口大小的宽度来改变根标签字体大小。核心的功能是
document.body.clientWidth
- amfe-flesabile 监听视口大小的宽度来改变根标签字体大小。核心的功能是
步骤:
- 新建配置文件
postcss.config.js
配置postcss
插件 - 1 rem = 当前网页的宽度px / 10 网页宽度 375
em rem
rem
是基于html
定义的字体大小而决定,而em
则根据使用它的元素的大小决定。
现有的em
后有rem
, 因为em
是相对于父级元素的,使用起来比较麻烦。
大量数据本地存储
存储在indexDB
IndexedDB
就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB
允许储存大量数据,提供查找接口,还能建立索引。
步骤:
- 创建/打开数据库。
- 在数据库中创建一个对象仓库(object store)。
- 启动一个事务,并发送一个请求来执行一些数据库操作,像增加或提取数据等。
- 通过监听正确类型的 DOM 事件以等待操作完成。
- 在操作结果上进行一些操作(可以在 request 对象中找到)
后端一次性传了10w条数据,前端该如何处理
- 分页:
- 将当前页数和每页的条数发给后端,请求数据
- 后端一次性将大量数据发回,首先我们做一个加载渲染的loading,然后使用懒加载。
- 采用延迟加载的策略
- 根据用户的滚动位置动态渲染数据。要获取用户的滚动位置,我们可以在列表末尾添加一个空节点空白。每当视口出现空白时,就意味着用户已经滚动到网页底部,这意味着我们需要继续渲染数据。
异常处理
异常分类:
出错、呆滞、损坏、假死、崩溃
异常原因:
- JS 语法错误、代码异常
- 请求错误
- 静态资源加载异常
- Promise异常
- Iframe异常
- 跨域Script error
- 页面崩溃和卡顿
异常捕获:⭐
try...catch...
: 只能捕获到同步运行时错误,对语法和异步错误却无能为力,捕获不到。 2.window.onerror
: 当 JS 运行时错误发生时,window
会触发一个ErrorEvent
接口的error
事件,并执行window.onerror()
。- 不同域名下的
js
报错不能被 全局的window.onerror
监听到,我们需要给相关的 js 文件上加上Access-Control-Allow-Origin:*
的response header
,并且引用相关的js
文件时加上crossorigin
属性
- 不同域名下的
window.addEventListener
: 资源加载失败,加载资源的元素会触发Event
接口中的error
事件,并执行该元素上的onerror()
处理函数。这些error
事件不会向上冒泡到window
,不过能被单一的window.addEventListener
捕获Promise
的catch
处理抛出的异常axios
中,错误请求放到请求拦截器中vue
中的errorHandler
图片断网之后还存在
将图片转为base64存在localStorage中。
get和post的区别
1、url可见性:
代码语言:javascript复制get,参数url可见;
post,url参数不可见
2、数据传输上:
代码语言:javascript复制get,通过拼接url进行传递参数;
post,通过请求体传输参数
3、缓存性:
代码语言:javascript复制get请求是可以缓存的
post请求不可以缓存
4、后退页面的反应
代码语言:javascript复制get请求页面后退时,不产生影响
post请求页面后退时,会重新提交请求
5、传输数据的大小
代码语言:javascript复制get一般传输数据大小不超过2k-4k(根据浏览器不同,限制不一样,但相差不大)
post请求传输数据的大小根据php.ini 配置文件设定,也可以无限大。
6、安全性
代码语言:javascript复制原则上post肯定要比get安全,毕竟传输参数时url不可见
http缓存
分为两种:强缓存和协商缓存
- 强缓存:不会向服务器发送请求,直接从缓存中获取资源。可以在chrome控制台中的Network选项中看到请求返回200的状态码。并且size显示from disk cache或from memory cache两种
- 协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中获取资源。
TCP和UDP的区别
1、udp是无连接的,tcp是面向连接的;
2、udp是不可靠传输,tcp是可靠传输;
3、udp是面向报文传输,tcp是面向字节流传输
http版本
HTTP1.0:
- 浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接
HTTP1.1:
- 引入了持久连接,即TCP连接默认不关闭,可以被多个请求复用
- 在同一个TCP连接里面,客户端可以同时发送多个请求
- 虽然允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的,服务器只有处理完一个请求,才会接着处理下一个请求。如果前面的处理特别慢,后面就会有许多请求排队等着
- 新增了一些请求方法
- 新增了一些请求头和响应头
HTTP2.0:
- 采用二进制格式而非文本格式
- 完全多路复用,而非有序并阻塞的、只需一个连接即可实现并行
- 使用报头压缩,降低开销
- 服务器推送
左边宽度是固定,右是自动铺满,能用到什么布局?
方法一:flex布局,左侧flex-grow: 0, flex-shrink: 0,flex-basis: 固定宽度数值;右侧flex-grow: 1, flex-shrink: 1,flex-basis: 0px(或者不设置);
方法二:float margin, 左侧元素float:left;给一个固定宽度,右侧元素margin-left: 左侧元素的宽度;
方法三:float float,左侧元素float:left;给一个固定宽度,右侧元素float:right;宽度设为calc(100% - 左侧元素的宽度);
方法四:父级元素的display: table, 左侧元素display:table-cell且给一个固定宽度,右侧元素display: table-cell,不设置宽度即可;
方法五:absolute margin, 左侧元素设置一个绝对定位定位在左侧且给一个固定宽度, 右侧元素,充满空间且给一个margin-left为左侧元素的宽度