javaScript相关:
1.数据基本类型和引用类型
- 基本类型(就是值类型,存储在栈区,先进后出):
string、boolean、number、null、undefined、symbol(符号,es6新增,symbol值为原始类型,所以Symbol函 数不能使用new调用)、bigInt - 引用类型(就是地址类型,存储在堆,先进先出):
object、function、Array、date、regExp
2.判断数据类型的方法(四种)
typeof(最常用,最低级,只能判断基本数据类型)instanceof(一般用来判断A是否是B的实例,表达式:A instanceof B ;返回值true或者false)constructorObject.prototype.tostring.call()
3.let、var、const有什么区别
let定义的变量,不能在定义之前访问变量,会导致报错,不存在变量提升;不能重新定义var定义的变量,可以在声明之前使用const用来定义常量,且不能修改,如果定义的是对象,可修改对象内部的数据
4.原型和原型链
- 原型:原型就是原型对象,普通对象(不是通过
new Function创建的对象)没有原型对象,只有函数对象才有原型对象。
所有的函数都有prototype属性(原型),所有的对象都有_proto_属性。 - 原型链:当我们查找一个对象的属性时,首先在当前对象查找,如果该对象内不存在这个属性,会去它的
_proto_属性所指向的父对象中去 查找,一直到顶层null,这个查找链就是原型链。
***不懂构造函数,原型,原型链的看向这里,讲解的非常清楚,易懂 详情见: juejin.cn/post/708117…
5.闭包
- 闭包就是有权访问另一个函数中的变量的函数
- 用途:
1.能够访问函数定义时所在的词法作用域(阻止其被回收)
2.私有化变量
3.模拟块级作用域
4.创建模块 - 缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏
function a(){
var b = 1
function c(){
console.log(b)
}
c()
}
a()
//什么时候用到return--外部如果想要使用闭包的变量,则需要return
function a(){
let a = 10
function fn(){
console.log(a)
}
return fn
}
const fun = a()
1.闭包一定有return吗?不是
2.闭包一定会有内存泄漏吗?不是

6.作用域和作用域链
- 作用域:规定变量的可使用范围
- 作用域链:当我们查找一个变量的时候,首先我们会从局部作用域到全局作用域依次查找,这个查找链就是作用域链
7.什么是继承,继承方式
- 继承:子类可以使用父类的所有功能,并且对这些功能进行扩展。继承的过程,就是从一般到特殊的过程。
- 继承方式:
- 原型链继承
- 构造继承
- 实例继承
- 拷贝继承
- 组合继承
- 寄生组合继承
- 原型链继承
8.深拷贝和浅拷贝方法
-
深拷贝:仅仅是复制了引用,彼此之间的操作会互相影响
JSON.parse(JSON.stringify()) -
浅拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响
1.Object.assign()
2.Array.prototype.concat()
3.Array.prototype.slice()
详解见:https://blog.csdn.net/cc18868876837/article/details/114918262
9.防抖节流函数
- 防抖函数:触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,
则重新计算时间。
应用场景:
1.input输入自动补全事件
2.频繁的操作点赞或取消点赞之类的
3.浏览器窗口大小改变后,只需窗口调整完成后,再执行resize事件中的代码,防止重复渲染
function debounce(fn,timing){
let timer;
return function(){
clearTimeout(timer);
timer=setTimeout(()=>{
fn();
},)
}
}
- 节流函数:高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行效率。
应用场景:
1.提交表单
2.高频的监听事件
10.箭头函数和普通函数
- 箭头函数:箭头函数省去了
function关键字,函数的参数放在=>前面的括号中,函数体跟在=>后的花括号中。代码如下:
let fun = (name) => {
return `Hello ${name} `;
};
- 普通函数,代码如下:
let fun = function (name) {
return `Hello ${name} `;
};
- 区别:
(1)箭头函数比普通函数更加简洁,清晰,便捷
(2)箭头函数没有prototype(原型),所以箭头函数本身没有this。代码如下:
// 箭头函数
let a = () => {};
console.log(a.prototype); // undefined
// 普通函数
function a() {};
console.log(a.prototype); // {constructor:f}
(3)箭头函数不会创建自己的this,只会继承最外层的第一个普通函数的this,如果外层没有普通函数,则this指向window(全局对象),所以箭头函数中this的指向在它被定义的时候就已经确定了,之后永远不会改变
(4)call/apply/bind无法改变箭头函数中this的指向,他们只能动态修改函数执行时this的指向。代码如下:
var num1 = 10;
let fun = () => {
console.log(this.num1)
};
fun(); // 10
fun.call({ num1: 20 }); // 10
fun.apply({ num1: 20 }); // 10
fun.bind({ num1: 20 })(); // 10
(5)箭头函数不能作为构造函数使用,因为箭头函数没有自己的this,它的this是继承了外层执行环境中的this,且this指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能定义成构造函数,否则用new调用时会报错
(6)箭头函数不能绑定arguments,箭头函数处于全局作用域中,则没有arguments
可以用rest参数...代替arguments对象,来访问箭头函数的参数列表。代码如下:
let fn3 = (a,...arr) => {
console.log(a,arr) //1, [2,3,4,5,6] }` `
fn3(1,2,3,4,5,6)
*当要求动态上下文的时候,就不能够使用箭头函数。也就是this的固定化,代码如下:
1、在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象
2、不能够用作构造函数,这就是说,不能够使用new命令,否则就会抛出一个错误
3、不能够使用arguments对象
4、不能使用yield命令
//案例1
class Cat {
constructor(){
this.type = 'cat'
}
says(say){
setTimeout(function(){
console.log(this.type + 'says' + say)
},1000)
}
}
var cat = new Cat()
cat.says('hello') //undefined says hello
//案例2
class Cat {
constructor(){
this.type = 'cat'
}
says(say){
setTimeout(()=>{
console.log(this.type + 'says' + say)
},1000)
}
}
var cat = new Cat()
cat.says('hello') //cat says hello
### 注意:
①. rest参数只能作为函数的最后一个参数
②. 函数的length属性,不包括rest参数
rest参数与arguments的区别: ①. 箭头函数和普通函数都可以使用rest参数,而arguments只能普通函数使用
②. 接受参数rest比arguments更加灵活
③.rest参数是一个真正的数组,而arguments是一个类数组对象,不能直接使用数组方法
(7)箭头函数不能重复函数参数名称。代码如下:
function fn(name,name) {
console.log('fn2:',name)
}
let fn2 = (name,name) => {
console.log('fn',name)
}
fn('nan','jiu') // 'jiu'
fn2('nan','jiu') // 报错
(8)箭头函数不支持new.target,new.target是ES6新引入的属性,普通函数如果通过new调用,new.target会返回该函数的引用。代码如下:
//箭头函数
let a = () => {
console.log(new.target); // 报错:new.target 不允许在这里使用
};
a();
//普通函数
new bb();
function bb() {
let a = () => {
console.log(new.target); // 指向函数bb:function bb(){...} };
a();
}
- 箭头函数不适用场景:箭头函数的
this意外指向和代码的可读性。 官网标明如下图,所以箭头函数不要随便用。因为箭头函数能做的,普通函数都可以实现。:

11.this指向问题,更改this指向的三个方法
bind:是数组,可以分为多次传入;apply、call:是一次性传入;apply/call用途完全一样,传参不一样,apply是数组,call是参数列表。代码如下:
var name = "小吴"
var obj = {
name: "李四"
}
function show() {
console.log(this.name)
}
show() // 小吴
show.call(obj) // 李四
show.bind(obj)() //李四
12.new关键字
new 关键字用来创建一个类(模拟类)的实例对象。 实例化对象之后, 也就继承了类的属性和方法。
详解见:https://www.jianshu.com/p/4bbf0c582e97
13.null 和 undefined
- 相同:
1. 在if语句中null和undefined都会转为false两者用相等比较也是相等
2.Undefined和Null都是基本数据类型,这两个基本数据类型分别都只有一个值,就是undefined和null - 不同:
1.undefined代表的含义是未定义
2.null代表的含义是空对象。也作为对象原型链的终点;null主要用于赋值给一些可能会返回对象的变量,作为初始化
14.遍历数组的方法,map不写return会产生什么后果;map,filter和forEach有什么不一样
map用箭头函数可以不用return
var a=[{a:'123',b:'111'},{a:true}]
b=a.filter(item=>item.a==true)
bb=a.filter(item=> {return item.a==true})
c=a.filter(function(item){return item.a=='123'})
console.log(b)//0: {a: true}
console.log(bb)//{ {a: true}
console.log(c)//{a: "123", b: "111"}
var a=[2,4]
var d=a.map(item=>item*2)
console.log(d)//[2,4]
都是三个参数:
- 第一个表示数组的子项
- 第二个表示数组的索引(index)
- 第三表示原数组本身
- 区别:map和filter返回的数组都不会替换原数组,可以用新的变量名接受这个新生成的数组;filter 的 return 可以设置筛选条件用于数组数据的筛选。
15.数组遍历如何终止循环
(1)break - 中止当前循环,switch语句或label 语句,并把程序控制流转到紧接着被中止语句后面的语句;
(2)continue - 终止当前循环或标记循环的当前迭代中的语句执行,并在下一次迭代时继续执行循环;
(3)return - 终止函数的执行,并返回一个指定的值给函数调用者;
let arr = ['a', 'b', 'c', 'd']
for (let i = 0; i < arr.length; i++) {
console.log('for:', arr[i])
if (i === 2) {
return
}
}
// 输出 a b
for (let i = 0; i < arr.length; i++) {
console.log('for:', arr[i])
if (i === 2) {
break
}
}
// 输出 a b
for (let i = 0; i < arr.length; i++) {
console.log('for:', arr[i])
if (i === 2) {
continue
}
}
// 输出 a b d
16.遍历对象的方法,for in和for of的区别;for in遍历对象有什么问题?
- 区别:
- for in可以遍历数组和对象,会把原型上的属性和属性值都遍历下来,遍历的是键名;for of只能遍历数组,只会遍历数组中的每一项,不是遍历的键名。
- for in遍历对象:
- 对象无法直接被for循环遍历。
- 对象的遍历一般使用的是for in 循环,不过for in 循环会把原型上的属性和方法给遍历出来。
- for of只能遍历iterable类型的数据,不能遍历对象。
- 如果只想遍历对象自身的属性和方法,可以使用Object.hasOwnProperty()方法进行判断。
- Object.keys方法,也是一种获取对象的属性的方式,这种方式不会把原型上面的属性和方法的键名获取。
17.详细说明前端登陆加密的过程
- md5
- AES
// npm i crypto-js --save下载先,然后引包
import CryptoJS from "crypto-js";
export default {
//随机生成指定数量的16进制key
generatekey(num) {
let library =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let key = "";
for (var i = 0; i < num; i++) {
let randomPoz = Math.floor(Math.random() * library.length);
key += library.substring(randomPoz, randomPoz + 1);
}
return key;
},
//加密
encrypt(word, keyStr) {
keyStr = keyStr ? keyStr : "123456{saltZX}.."; //判断是否存在key,不存在就用定义好的key
var key = CryptoJS.enc.Utf8.parse(keyStr);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
},
//解密
decrypt(word, keyStr) {
keyStr = keyStr ? keyStr : "abcdsxyzhkj12345";
var key = CryptoJS.enc.Utf8.parse(keyStr);
var decrypt = CryptoJS.AES.decrypt(word, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}
};
然后在需要的页面引入AES.js文件:
import AES from "../../src/uitls/AES";
18.宏任务和微任务

19.异步有哪些方法?异步解决那些问题?
- 方法:
- (1) 回调函数、事件监听、定时器
- (2) Promise
- (3) Web Workder
- (4) async/await
- 解决问题:
- (1)执行一些需要等待的任务且不影响同步任务的进行
- (2)解决了复杂业务场景中回调函数累加之后产生的回调地狱等问题
- (3)由于 js 是单线程的,随着多核 CPU 的出现单线程无法充分使用计算机性能,因此可以使用 Web Workder 来开辟另一个线程,来分配一些任务给 Web Workder 来运行
- (4)使得代码在形式上更接近于同步代码,非常的简洁。async/await 是非阻塞的,但类似 Promise.all 这样的并行方法 async/await 暂时是无法实现的
- 由于 js 是单线程的,防止代码阻塞,我们把代码任务分为:同步和异步
- 同步代码给js引擎执行,一部代码交给宿主环境
- 同步代码放入执行栈中,异步代码等待时机成熟送入任务队列排队
- 执行栈执行完毕,回去任务队列看是否有异步任务,有就送到执行栈执行,反复循环查看执行,这个过程是事件循环(
eventloop),如下图:
20. Class的讲解
- class语法相对原型、构造函数、继承更接近传统语法,它的写法能够让对象原型的写法更加清晰、面向对象编程的语法更加通俗,代码如下:
class Cat {
constructor () {
this.type = 'cat'
}
says(say) {
console.log(this.type + 'says' + say)
}
}
let cat = new Cat()
cat.says('hello') // cat says hello
class Dog extends Cat {
constructor() {
super()//在ES6中,子类的构造函数必须含有super函数,super表示的是调用父类的构造函数,虽然是父类的构造函数,但是this指向的却是dog
this.type = 'dog'
}
}
let dog = new Dog()
dog.says('hello') // dog says hello
结论:contructor内部定义的方法和属性是实例对象自己的,不能通过extends 进行继承。
vue相关:
1.vue生命周期
Vue 一共有 8 个生命阶段,分别是创建前、创建后、加载前、加载后、更新前、更新后、销毁前和销毁后,每个阶段对应了一个生命周期的钩子函数
(1)beforeCreate 钩子函数,在实例初始化之后,在数据监听和事件配置之前触发。因此在 这个事件中我们是获取不到 data 数据的。
初始化inInject 注入响应式的一个挂载
初始state处理(props,method,data,computed,watch)
初始化provide
(2)created 钩子函数,在实例创建完成后触发,此时可以访问 data、methods 等属性。但 这个时候组件还没有被挂载到页面中去,所以这个时候访问不到 $el 属性。一般我们可以在这 个函数中进行一些页面初始化的工作,比如通过 ajax 请求数据来对页面进行初始化。
(3)beforeMount 钩子函数,在组件被挂载到页面之前触发。在 beforeMount 之前,会找 到对应的 template,并编译成 render 函数。
(4)mounted 钩子函数,在组件挂载到页面之后触发。此时可以通过 DOM API 获取到页面中 的 DOM 元素。
(5)beforeUpdate 钩子函数,在响应式数据更新时触发,发生在虚拟 DOM 重新渲染和打补 丁之前,这个时候我们可以对可能会被移除的元素做一些操作,比如移除事件监听器。
(6)updated 钩子函数,虚拟 DOM 重新渲染和打补丁之后调用。
(7)beforeDestroy 钩子函数,在实例销毁之前调用。一般在这一步我们可以销毁定时器、 解绑全局事件等。
(8)destroyed 钩子函数,在实例销毁之后调用,调用后,Vue 实例中的所有东西都会解除 绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
当我们使用 keep-alive 的时候,还有两个钩子函数,分别是 activated 和 deactivated 。 用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
2.vue双向数据绑定原理
- 当一个
Vue实例创建时,Vue会遍历data选项的属性,用Object.defineProperty将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
3.vue中为什么要用key
(1) 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。
(2)而使用key时,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。
- 有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误
4.computed和watch
computed:使用时,函数里面的所有的变量都会被监听,里面某一个变动,便会将整个函数执行一遍。
是计算值,具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不是再次计
应用:简化template里面的计算和处理props或$emit的传值;如:购物车商品结算watch:只是监听某一个值,若是监听的值里面也有很多变量,也会全部监听
是观察动作,无缓存性,页面重新渲染时值不变化也会执行
应用:监听props,$emit或本组件的值执行异步操作;如:搜索框
5.nextTick
this.$nextTick()将回调延迟到下次DOM更新循环之后执行。在修改数据之后立即使用它,然后等待DOM更新。它跟全局方法Vue.nextTick一样,不同的是回调的this自动绑定到调用它的实例上。
//实例1
<template>
<section>
<div ref="hello">
<h1>Hello World ~</h1>
</div>
<el-button type="danger" @click="get">点击</el-button>
</section>
</template>
<script>
export default {
methods: {
get() {
}
},
mounted() {
console.log(333);
console.log(this.$refs['hello']);
this.$nextTick(() => {
console.log(444);
console.log(this.$refs['hello']);
});
},
created() {
console.log(111);
console.log(this.$refs['hello']);//undefined
this.$nextTick(() => {
console.log(222);
console.log(this.$refs['hello']);
});
}
}
</script>
✿ 可以根据打印的顺序看到,在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作并无作用,而在created()里使用this.$nextTick()可以等待dom生成以后再来获取dom对象
//实例2
<template>
<section>
<h1 ref="hello">{{ value }}</h1>
<el-button type="danger" @click="get">点击</el-button>
</section>
</template>
<script>
export default {
data() {
return {
value: 'Hello World ~'
};
},
methods: {
get() {
this.value = '你好啊';
console.log(this.$refs['hello'].innerText);
this.$nextTick(() => {
console.log(this.$refs['hello'].innerText);
});
}
},
mounted() {
},
created() {
}
}
</script>
✿ 根据上面的例子可以看出,在方法里直接打印的话, 由于dom元素还没有更新, 因此打印出来的还是未改变之前的值,而通过this.$nextTick()获取到的值为dom更新之后的值
✿✿ 综上所述,this.$nextTick()在页面交互,尤其是从后台获取数据后重新生成dom对象之后的操作有很大的优势
6.vue组件之间通讯方式有哪些,组件之间如何获取方法;自己封装实现通信如何传参
- 1.
props/$emit:父传子:props;子传父:$emit - 2
.ref/refs:可以直接调用组件的方法或访问数据 - 3.
vuex - 4.
evenBus - 5.
$children/$parent:子实例可以用this.$parent访问父实例,子实例被推向父实例的$children数组中 - 6.
localStorage/sessionStorage - 7.
$attrs/$listeners - 8.
provide/inject
✿传参是两个参数,第一个是事件名,第二个为传递的参数。
7.vuex
vuex是一个专门为vue.js应用程序开发的状态管理模式。Vuex解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题- 属性:
(1)state属性:基本数据
(2)getters属性:从state中派生出的数据
(3)mutation属性:更新store中数据的唯一途径,其接收一个以state为第一参数的回调函数
(4)action属性:提交mutation以更改state,其中可以包含异步操作
(5)module属性:用于将store分割成不同的模块
8.在开发过程中,我们时常会遇到这样一种情况:当vue的data里边声明或者已经赋值过的对象或者数组(数组里边的值是对象)时,向对象中添加新的属性,如果更新此属性的值,是不会更新视图的,这是为啥
- 根据官方文档定义:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。
当你把一个普通的
JavaScript对象传入 Vue 实例作为data选项,Vue 将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter。
受现代 JavaScript的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
- 解决方案:
this.$set()
9.v-if和v-show的区别
v-if:不满足条件时,不渲染domv-show:隐藏dom,只是简单的控制display属性,多次切换,不能用于权限操作
10.v-for和v-if可以一起使用吗?为什么?
- 不建议一起使用,因为
v-for优先级比v-if高,会造成性能方面的浪费(每次渲染都会先循环再进行条件判断),所以我们应该先用v-if判断,再循环。代码如下:
<template v-if="isShow">
<p v-for="item in items">
</p>
</template>
11.怎么理解vue的单向数据流
-
指数据一般从父组件传到子组件,子组件没有权利直接修改父组件传来的数据,即子组件从
props中直接获取的数据,只能请求父组件修改数据再传给子组件。父级属性值的更新会下行流动到子组件中。
这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。每次父级组件发生更新时,子组件中所有的prop都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变prop。如果你这样做了,Vue会在浏览器的控制台中发出警告。所以,在子组件中直接用
v-model绑定父组件传过来的数据是不合理的,如果希望修改父组件传给子组件的值:
(1)在子组件data中创建一个变量获取props中的值,再改变这个data中的值。
(2)子组件使用$emit发出一个事件,让父组件接收去修改这个值。
12.前端路由是如何实现的;路由原理history和hash两种路由方式,我们通常用哪种,分别有什么特点和区别;hash和history模式是如何实现页面不刷新跳转的
- history模式(美观,但是刷新会出现 404 需要后端进行配置):
利用了HTML5中新增的pushState()和replaceState()方法。这两个方法应用于浏览器的历史记录栈,在当前已有的back、forward、go的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的URL,但浏览器不会立即向后端发送请求。
history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或<a>标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便。 - hash 模式(兼容性好但是不美观)
(1) 后面的hash值的变化,浏览器既不会向服务器发出请求,浏览器也不会刷新,每次hash值的变化会触发hashchange事件。
(2) 可以为hash的改变添加监听事件。
通过 hashchange 事件监听 URL 的变化,改变 URL 的方式只有这几种:通过浏览器前进后退改变 URL、通过<a>标签改变 URL、通过window.location改变URL,这几种情况改变 URL 都会触发 hashchange 事件。
13.vue页面闪烁问题怎么解决
v-cloak:v-cloak指令和css规则如[v-cloak]{display:none}一起用时,这个指令可以隐藏未编译的Mustache标签直到实例准备完毕。v-cloak指令可以像css选择器一样绑定一套css样式然后这套css会一直生效到实例编译结束。
// <div> 不会显示,直到编译结束。
[v-cloak]{
display:none;
}
<div v-cloak>
{{ message }}
</div>
v-text:vue中我们会将数据包在两个大括号中,然后放到HTML里,但是在vue内部,所有的双括号会被编译成textNode的一个v-text指令。
而使用v-text的好处就是永远更好的性能,更重要的是可以避免FOUC(Flash of Uncompiled Content) ,也就是上面遇到的问题。
<span v-text="message"></span>
<!-- same as -->
<span>{{message}}</span>
14.vue跳转页面的方法
// 标签跳转
<router-link :to="{ name: 'user', params: { username: 'erina' }}">User</router-link>
<router-link :to="{ name:' editDetail', query: {page:1,code:1111}}"></router-link>
// JS 跳转
this.$router.push({
name:"editDetail",
query:{
page:1,
code:1111
}
})
// 优势:参数不显示 劣势:页面刷新参数丢失
this.$router.push({
name:"editDetail",
params:{
page:1,
code:1111
}
})
//冒号的形式传参 优势:页面刷新后参数不会丢失 劣势:需要手动配置
const editDetail = (data)=>{
root.$router.push({
path:`/editDetail/${data.id}/${data.title}`
})
}
// 接受参数
this.$route.query.page
this.$route.params.page
15.vue 3.0和2.0的绑定原理对比看一下
16. 父向子传值,用属性绑定(v-bind)
1. 子向父传值,用事件绑定(v-on)
2. 不相干(兄弟)组件绑定,用EventBus(接收方emit),小范围数据共享
3. 组件全局数据共享,或状态(vuex)(1.易于开发和维护;2.提高开发效率;3.数据响应式,实时同步;)
- 格式配置
1. 新建一个配置文件.prettierrc
2. 文件里面内容:
{
“semi”:false,
“singleQuote”:true
}
3. 会出现一个配置弹框,选择Prettier即可
架构师知识点
1. 本地存储
localStorage.setItem(“obj”,JSON.stringify(obj))---存储对象转字符串
var res = localStorage.getItem(“obj”)---获取存储字符串
console.log(JSON.parse(res))---存储字符串转对象
2. map/filter
///////////////////////////////////////map
var arr = [1,2,3,4]
var newArr = arr.map(function(v){
return v + 1; //[2,3,4,5]
})
///////////////////////////////////////filter
var arr = [
{name:”tom”},
{name:”tim”}
{name:”pom”}
]
//找出带有t的对象
var newArr = arr.filter(function(v){
return v.name.indexOf(“t”) > -1;
})
console.log(newArr)
3. 解构/合并对象
4. Promise和Async await
var promise = new Promise(function(resolve,reject){
$ajax({
Url:””,
Success:function(res){
If(res >= 60){
1111
}else{
2222
}
}
})
})
Promise.then(function(){
Console.log(“1111”)
}).catch(function(){
Console.log(“2222”)
})
5. session和Token
15.route的区别,
$router:是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,它包含了所有的路由,包含了很多对象(如history对象)和属性,任何页面都可以调用其push()、replace()、go()等方法。$route:是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取当前路由的name、params、path、query等属性
16.keep-alive
- 生命周期:
(1)activated:页面第一次进入的时候,钩子触发的顺序created-mounted-activated
(2)deactivated:页面退出会触发deactivated,当再次前进或后退时触发activated - 属性:
(1)include:表示只有某些属性的组件会被缓存,其他组件不会被缓存
(2)exclude:表示除了某些组件不会被缓存,其他组件都会被缓存,如下代码:
<keep-alive include="name1,name2">//name1,name2是组件的名字,不是路由的名字;表示name属性为name1,name2的组件会被缓存
<router-view></router-view>
</keep-alive>
<keep-alive exclude="name3">//表示除了name属性为name3的组件不会被缓存,其他组件都会被缓存
<router-view></router-view>
</keep-alive>
- 作用:实现组件缓存,节约性能
17.ES6
- 新增
symbol类型 表示独一无二的值,用来定义独一无二的对象属性名; const/let都是用来声明变量,不可重复声明,具有块级作用域。存在暂时性死区,也就是不存在变量提升。(const一般用于声明常量);- 变量的解构赋值(包含数组、对象、字符串、数字及布尔值,函数参数),剩余运算符(...rest);
- 模板字符串(
${data}); - 扩展运算符(
...)函数、数组、对象方法等;代码如下:
// 数组
let arr1 = [1,2,3,4]
let arr2 = [2,3,4,5]
console.log(...arr1)
console.log(...arr1,...arr2) //使用扩展运算符将arr1和arr2放入同一个数组中
// 类数组对象
let obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
}
let objC = Array.from(obj)
console.log(foo(...objC))
// 函数
function sum(...num) { // 使用扩展运算符 将实参传进来
return num.reduce((one, two)=>{
return one + two
})
}
console.log(sum(1,2,3,4,5))
// 构造字面量对象使用的语法
let person = {name:'张三', age: 28}
// console.log(...person) 直接报错,必须在赋值并用花括号
let person2 = {...person} //克隆对象
console.log(person2) // {name: "张三", age: 28}
let person3 = {...person, name: '李四',add:'女'} // 克隆并修改
console.log(person3) //{name: "李四", age: 28,add: "女"}
console.log(person) // {name: "张三", age: 28}
// 赋值
const [a, ...b] = [1, 2, 3, 4, 5]
console.log(a) // 1
console.log(b) // [2, 3, 4, 5]
✿✿注意: 扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。如下:

- 箭头函数;
Set和Map数据结构;
(1)Set:它类似于数组,但是成员的值都是唯一的,没有重复的值。
(2)Map:它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串--值”的对应,Map结构提供了“值--值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。
✿ 属性及操作方法见:https://www.cnblogs.com/mengxiangji/p/10740615.htmlProxy(拦截器)/Reflect(反射);Proxy相当于去修改设置对象的属性行为,而Reflect则是获取对象的这些行为Promise;代码如下:
///////////////// 基本用法
function2(){
// 你的逻辑代码
return Promise.resolve(/* 这里是需要返回的数据*/)
}
function3(){
// 你的逻辑代码
return Promise.resolve(/* 这里是需要返回的数据*/)
}
// 调用
function1(){
this.function2().then(val => {
this.function3();
});
}
///////////////// 高阶用法
init1(){
return new Promise((resolve, reject) => {
let data={
dateStr:this.time
};
api.get('url', null).then( res => {
//自己的操作
resolve()
}).catch(err => {
reject()
});
});
};
init2(){
return new Promise((resolve, reject) => {
let data={
dateStr:this.time
};
api.get('url', null).then( res => {
//自己的操作
resolve()
}).catch(err => {
reject()
});
});
};
//调用
Promise.all([this.init1(),this.init2()]).then(() => {
//两个都调成功以后执行的操作
//主要是loading问题
}).catch(err => {
// 抛出错误信息
});
async函数;同Generator函数一样,async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。Class;两种写法代码如下:
////////// 1
// 函数名和实例化构造名相同且大写(非强制,但这么写有助于区分构造函数和普通函数)
function Person(name,age) {
this.name = name;
this.age=age;
}
Person.prototype.say = function(){
return "我的名字叫" + this.name+"今年"+this.age+"岁了";
}
var obj=new Person("王五",38);//通过构造函数创建对象,必须使用new 运算符
console.log(obj.say());//我的名字叫王五今年38岁了
///////// 2
class Person{//定义了一个名字为Person的类
constructor(name,age){//constructor是一个构造方法,用来接收参数
this.name = name;//this代表的是实例对象
this.age=age;
}
say(){//这是一个类的方法,注意千万不要加上function
return "我的名字叫" + this.name+"今年"+this.age+"岁了";
}
}
var obj=new Person("王五",38);
console.log(obj.say());//我的名字叫王五今年38岁了
Module语法(import/export)。
18.你做过哪些vue的性能优化
# 编码阶段:
v-for和v-if不要同时使用;避免带来性能浪费(每次渲染先循环再判断),先用v-if,再用v-for或者通过计算属性computed提前过滤不需要显示的(filter)- 列表使用唯一标识
key;提升效率,否则列表改变时重新渲染,用key只会渲染已修改的数据 - 使用
v-show复用dom; - 子组件分割,避免每次重新渲染;
- 使用路由懒加载、异步组件;缓解首屏加载缓慢,异步按需加载
- 防抖节流;避免造成浏览器卡死
- 需要使用
v-for给每项元素绑定事件的时候,使用事件委托(事件代理);利用冒泡,委托他们的父级代为执行事件,这样就减少与dom的操作次数,提高性能 - 多数情况下,使用
v-if替代v-show;只执行需要的部分,但也要根据情况而定,频繁的切换就用v-show,避免出现停顿 - 第三方模块按需导入;
- 图片懒加载;减少首次加载数量,减少服务器压力,避免卡顿;加载慢的时候,提前给这张图片一个占位图片,不至于重叠一起或空白,让用户体验更好
- 长列表滚动到可视区域动态加载;避免卡顿、数据渲染慢的问题;具体操作见:https://www.cnblogs.com/mfyngu/p/13675004.html # SEO优化:
- 预渲染;预渲染能与服务端渲染一样提高
SEO优化,但前者比后者需要更少的配置,实现成本低。 - 服务器渲染
SSR;服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息;也更有利于首屏渲染 # 打包优化: - 压缩代码
- 使用
cdn加载第三方模块 - 图片压缩合并;压缩地址:https://tinypng.com/
- 多线程打包
happypack;安装:npm i happypack;代码如下:
// 在配置文件webpack.config.js文件中,以前的加载js、css文件的使用方法如下:
module:{
rules:[
{test:/.js$/,
use:{
loader:'babel-loader',
options:{
presets:[
//解析ES6和react语法
'@babel/preset-env',
'@babel/preset-react'
]
}
}
}
},
{
test:/.css$/,
use:['style-loader','css-loader']
}
]
}
// 使用happypack后如下:
module:{
rules:[
{test:/.js$/,
use:'happypack/loader?id=js'
},
{
test:/.css$/,
use:'happypack/loader?id=css'
}
]
},
plugins: [
new happypack({
id:'css',
use:['style-loader','css-loader']
}),
new happypack({
id:'js',
use:[{
loader:'babel-loader',
options:{
presets:[
'@babel/preset-env',
'@babel/preset-react'
]
}
}]
})
}
19.Vue如何获取DOM元素
- 首先先为标签元素设置
ref属性,然后通过this.$refs.属性值获取。代码如下:
<div ref="test"></div>
const dom = this.$refs.test
20.对babel和polyfill的理解,常用loader和plugin是做什么的?
babel:babel解决语法层面的问题,用于将ES6+的高级语法转为ES5。babel polyfill:如果要解决API层面的问题,需要使用垫片。
比如常见的:babel-polyfill:通过向全局对象和内置对象的prototype上添加方法来实现的。所以这会造成全局空间污染。babel-runtime:更像是一种按需加载的实现,在哪里需要使用 Promise,需要‘import Promise from 'babel-runtime/core-js/promise’,但是不方便,需要每个文件都import。babel-plugin-transform-runtime:装了babel-plugin-transform-runtime就不需要装babel-runtime了,因为前者依赖后者。
‘babel-plugin-transform-runtime’优点:
-
- 不会污染全局变量
-
- 多次使用只会打包一次
-
- 依赖统一按需引入,无重复引入,无多余引入
-
- 避免 babel 编译的工具函数在每个模块里重复出现,减小库和工具包的体积
loaders:webpack 可以使用 loader 来预处理文件,处理非 js 类型的模块,这允许你打包除 JavaScript 之外的任何静态资源。plugins:主要是扩展webpack本身的一些功能。插件可以运行在webpack的不同阶段(钩子 / 生命周期)。
css相关:
1.css3新属性有哪些,它们会带来什么影响
新属性:
(1) CSS3边框:
border-radius:CSS3圆角边框。box-shadow:CSS3边框阴影。 在 CSS3 中,box-shadow用于向方框添加阴影。box-shadow:10px 10px 5px #888888;border-image:CSS3边框图片。
(2)CSS3背景:
background-size:属性规定背景图片的尺寸。background-origin:属性规定背景图片的定位区域。background-clip:规定背景的绘制区域。
(3)CSS3文字效果:
hanging-punctuation:规定标点字符是否位于线框之外。punctuation-trim:规定是否对标点字符进行修剪。text-align-last:设置如何对齐最后一行或紧挨着强制换行符之前的行。text-emphasis:向元素的文本应用重点标记以及重点标记的前景色。text-justify:规定当text-align设置为 "justify" 时所使用的对齐方法。text-outline:规定文本的轮廓。text-overflow:规定当文本溢出包含元素时发生的事情。text-shadow:向文本添加阴影。text-wrap:规定文本的换行规则。word-break:规定非中日韩文本的换行规则。word-wrap:允许对长的不可分割的单词进行分割并换行到下一行。
(4)CSS3 转换:
transform向元素应用 2D 或 3D 转换。transform-origin允许你改变被转换元素的位置。transform-style规定被嵌套元素如何在 3D 空间中显示。perspective规定 3D 元素的透视效果。perspective-origin规定 3D 元素的底部位置。backface-visibility定义元素在不面对屏幕时是否可见。
(5)CSS3 过渡:当元素从一种样式变换为另一种样式时为元素添加效果。
transition:简写属性,用于在一个属性中设置四个过渡属性。transition-property:规定应用过渡的 CSS 属性的名称。transition-duration:定义过渡效果花费的时间。默认是 0。transition-timing-function:规定过渡效果的时间曲线。默认是 "ease"。transition-delay:规定过渡效果何时开始。默认是 0。
(6)CSS3动画: 通过 CSS3,我们能够创建动画,这可以在许多网页中取代动画图片、Flash 动画以及 JavaScript。
@keyframes:规定动画。animation:所有动画属性的简写属性,除了 animation-play-state 属性。animation-name:规定 @keyframes 动画的名称。animation-duration:规定动画完成一个周期所花费的秒或毫秒。默认是 0。animation-timing-function:规定动画的速度曲线。默认是 "ease"。animation-delay:规定动画何时开始。默认是 0。animation-iteration-count:规定动画被播放的次数。默认是 1。animation-direction:规定动画是否在下一周期逆向地播放。默认是 "normal"。animation-play-state:规定动画是否正在运行或暂停。默认是 "running"。animation-fill-mode:规定对象动画时间之外的状态。
(7)CSS3多列:
column-count:指定元素应该被分割的列数。column-fill:指定如何填充列column-gap:指定列与列之间的间隙column-rule: 所有 column-rule-* 属性的简写column-rule-color:指定两列间边框的颜色column-rule-style:指定两列间边框的样式column-rule-width:指定两列间边框的厚度column-span:指定元素要跨越多少列column-width:指定列的宽度columns:设置column-width和column-count的简写
(8)CSS3用户界面:
resize:属性规定是否可由用户调整元素尺寸。box-sizing:属性允许您以确切的方式定义适应某个区域的具体内容。outline-offset:属性对轮廓进行偏移,并在超出边框边缘的位置绘制轮廓。appearance:允许您使一个元素的外观像一个标准的用户界面元素。icon:为创作者提供了将元素设置为图标等价物的能力。nav-down:指定在何处使用箭头向下导航键时进行导航。nav-index:指定一个元素的Tab的顺序。nav-left:指定在何处使用左侧的箭头导航键进行导航。nav-right:指定在何处使用右侧的箭头导航键进行导航。nav-up:指定在何处使用箭头向上导航键时进行导航。
影响:
(1) 性能提升;更小巧、更快速 支持自定义渲染器
(2) API变动;模板语法99%保持不变,原生支持基于class的组件,并且无需借助任何编译及各种stage阶段的特性,在设计时也考虑TypeScript的类型推断特性,重写虚拟DOM可以期待更多的编译时提示来减少运行时的开销,优化插槽生成可以单独渲染父组件和子组件,静态树提升降低渲染成本,基于Proxy的观察者机制节省内存开销
(3) 不兼容IE11;检测机制更加全面、精准、高效,更具可调试式的响应跟踪
2.垂直水平居中实现方法(不定宽高,有很多种,就写三种,前两种常用)
<div class='box'>
<div class='child'></div>
</div>
第一种(绝对定位+transform):
.box{
position:relative;
}
.child{
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%)
}
第二种(flex):
.box{
display:flex;
justify-content:center;
align-items:center;
}
第三种(gird+margin):
.box{
display:grid;
}
.child{
justify-self:center;
align-self:center;
}
第四种
.box{
font-size:0;
text-align:center;
&::bofore{
content:'';
display:inline-block;
width:0;
height:100%;
vertical-align:middle;
}
}
.child{
display:inline-block;
vertical-align:middle;
}
3.盒子模型属性
- 一个盒子是由四个部分组成:
content、margin、padding、border
4.怎么理解语义化
详解见:https://www.cnblogs.com/xueandsi/p/5965733.html
5.cookies、sessionStorage、localStorage的区别是什么?
cookies:请求时会携带;数据大小不能超过4k;需要设置有效期,过期后cookie就会销毁sessionStorage:保存本地,请求时不会携带;数据大小支持5M左右;页面关闭后就会被销毁localStorage:保存本地,请求时不会携带;数据大小支持5M左右;除非被手动清理,否则永久存在
6.怎么理解回流和重绘?
- 回流:当
DOM的变化影响了元素的几何信息(dom对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。
触发:
(1)添加或者删除可见的dom元素
(2)元素尺寸改变,如:margin,padding,border,height,width改变 - 重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。
触发:
(1)改变元素的color、background、box-shadow等属性 ✿ 区别:回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流,当页面布局和几何属性改变时就需要回流
7.什么是BFC,有什么作用?bfc触发的条件
BFC即块级格式化上下文,他是一个独立的渲染区域,让处于BFC内部的元素和外部的元素相互隔离,使内外元素的定位不会相互影响- 作用:在页面上有一个独立隔离容器,容器内的元素和容器外的元素布局不会相互影响。
- 常用触发条件:
1.浮动元素不为none;
2.position不为static;
3.display:inline-block;display:table-cell;display:table-caption;
4.overflow 计算值(Computed)不为visible的块元素
5.display:inline-flex; - 应用:
1.清除内部浮动;
2.分属于不同的BFC时,可以防止margin重叠;
3.自适应多栏布局
8.请说说你对标签语义化的理解
- 一个语义元素能够清楚的描述其意义给浏览器和开发者,即使在去掉或丢失样式的时候,也能够让页面呈现出清晰的结构
- 有利于
SEO优化,让页面和搜索引擎建立良好的沟通,爬虫依赖于标签来确定上下文和各个关键词的权重,有助于爬虫抓取更多的有效信息 - 方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备等),并以具有意义的方式来渲染网页
- 便于团队开发和维护,语义化更具有可读性,遵循
W3C标准的团队都遵循语义化标准,可以减少差异化
9.http和https的区别
TTP的URL以http://开头,而HTTPS的URL以https://开头TP是不安全的,而HTTPS是安全的HTTP标准端口是80,而HTTPS的标准端口是443- 在
OSI网络模型中,HTTP工作于应用层,而HTTPS的安全传输机制工作在传输层 HTTP无法加密,而HTTPS对传输的数据进行加密HTTP无需证书,而HTTPS需要CA机构wosign的颁发的SSL证书
10.get和post的区别
GET在浏览器回退不会再次请求,POST会再次提交请求GET请求会被浏览器主动缓存,POST不会,要手动设置GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会GET请求在URL中传送的参数是有长度限制的,而POST没有限制GET参数通过URL传递,POST放在Request body中GET参数暴露在地址栏不安全,POST放在报文内部更安全GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作GET产生一个TCP数据包;POST产生两个TCP数据包
11.浏览器常见的兼容性问题?
1、不同浏览器margin和padding不同
2、ie6中,父级元素浮动以后,内部元素内容撑不开宽度
3、标签嵌套不规范,如p和h1-h6里面嵌套div
4、ie6小于19px,会当成19px处理,也就元素宽高小于19px的bug
5、图片3像素问题
6、IE8下给图片添加超链接时,图片会有蓝色边框
7、鼠标滑过时,不显示小手
12.flex
- 左右定宽,中间自适应:
中间flex = 1, 宽用百分比,左右固定宽,父元素display:flex。
// HTML代码:
<div class="container">
<div class="left"></div>
<div class="mid"></div>
<div class="right"></div>
</div>
// CSS代码:
.container {
display:flex;
}
.mid{
flex:1;
width:100%;
height: 100px;
background: yellow;
}
.left{
width:100px;
height: 100px;
background: red;
}
.right{
width:100px;
height: 100px;
background: red;
}
- 等分布局:
父元素display:flex,子元素flex:1
// HTML代码:
<div class="container">
<div class="left"></div>
<div class="mid"></div>
<div class="right"></div>
</div>
// CSS代码:
.container {
display:flex;
}
.mid{
flex:1;
height: 100px;
background: yellow;
}
.left{
flex:1;
height: 100px;
background: red;
}
.right{
flex:1;
height: 100px;
background: red;
}
- 垂直居中排列:
父dom里使用display: flex和align-items: center
// HTML代码:
<div class="container">
<div class="left"></div>
<div class="mid"></div>
<div class="right"></div>
</div>
// CSS代码:
.container {
height: 300px;
width: 300px;
display: flex;
align-items: center;
}
.mid{
height: 80px;
width: 50px;
background: yellow;
}
.left{
width: 50px;
height: 100px;
background: red;
}
.right{
width: 50px;
height: 180px;
background: red;
}
- 水平居中排列:父
dom里使用display: flex和align-items: center,再加一个flex-direction: column - 水平垂直居中:
// HTML代码:
<div class="container">
<div class="left"></div>
</div>
// CSS代码:
.container {
height: 300px;
width: 300px;
display: flex;
background: red;
align-items: center;
justify-content: center;
}
还有更多的,详解见:https://www.runoob.com/w3cnote/flex-grammar.html
13.响应式布局有哪些实现方式
1.百分比布局,百分比是相对于父元素;但是无法对字体,边框等比例缩放
2.flex 弹性盒子布局 display:flex
3.rem布局:当前页面中元素的 rem 单位的样式值都是针对于html 元素的font-size 的值进行动态计算的,所以有两种方法可以达到适配不同屏幕:第一种利用媒体查询,在不同分辨率下给 html 的 font-size 赋值。第二种利用 js 动态计算赋值
css3媒体查询@media screen and(max-width: 750px){};但IE6、7、8不支持媒体查询
5.vw+vh:比如font-size: 12px,PSD文件宽度375,转换公式12 * 100 / 375,则样式改为font-size: 3.2vw
6.使用一些框架(bootstrap,element ui,vant)
14.HTML全局属性
- 全局属性是可与所有 HTML 元素一起使用的属性。具体如下图:

14.css的布局方案 和浏览器的加载渲染机制
优化相关
1.简述前端性能优化
-
<1>页面内容方面
- 通过文件合并、css 雪碧图、使用 base64 等方式来减少 HTTP 请求数, 避免过多的请求造成等待的情况;
- 通过 DNS 缓存等机制来减少 DNS 的查询次数;
- 通过设置缓存策略,对常用不变的资源进行缓存;
- 通过延迟加载的方式,来减少页面首屏加载时需要请求的资源,延迟加载 的资源当用户需要访问时,再去请求加载;
- 通过用户行为,对某些资源使用预加载的方式,来提高用户需要访问资源 时的响应速度;
-
<2>服务器方面
- 使用 CDN 服务,来提高用户对于资源请求时的响应速度;
- 服务器端自用 Gzip、Deflate 等方式对于传输的资源进行压缩,减少传 输文件的体积;
- 尽可能减小 cookie 的大小,并且通过将静态资源分配到其他域名下,来 避免对静态资源请求时携带不必要的 cookie;
2.什么是 webp?
WebP 是谷歌开发的一种新图片格式,它是支持有损和无损两种压缩方式的 使用直接色的点阵图。使用 webp 格式的最大优点是是,在相同质量的文件下, 它拥有更小的文件体积。因此它非常适合于网络图片的传输,因为图片体积的减 少,意味着请求时间的减少,这样会提高用户的体验。这是谷歌开发的一种新的 图片格式。
3.通过创建 Image 对象,将其 src 属性设置为 webp 格式的图片,然后在
onload 事件中获取图片的宽高,如果能够获取,则说明浏览器支持 webp 格式 图片。如果不能获取或者触发了 onerror 函数,那么就说明浏览器不支持 webp 格式的图片。