目录
- 一、CSS
- 二、JavaSscipt
- 1.JS由哪三部分组成?
- 2.JS有哪些内置对象?
- 3.操作数组的方法有哪些?
- 4.JS对数据类的检测方式有哪些?
- 5.说一下闭包,闭包有什么特点?
- 6.前端的内存泄漏怎么理解?
- 7.事件委托是什么?
- 8.基本数据类型和引用数据类型的区别?
- 9.说一下原型链。
- 10.new操作符具体做了什么?
- 11.JS是如何实现继承的?
- 12.JS的设计原理是什么?
- 13.JS中关于this指向的问题
- 14.script标签里的async和defer有什么区别?
- 15.setTimeout最小执行时间是多少?
- 16.ES6和ES5有什么区别?
- 17.ES6的新特性有哪些?
- 18.call,aply,bind三者有什么区别?
- 19.用递归的时候有没有遇到什么问题?
- 20.如何实现一个深拷贝?
- 21.说一下事件循环(微任务、宏任务...)
- 22.Ajax是什么?axios、Fetch API?
- 23.get和post有什么区别?
- 24.promise的内部原理是什么?它的优缺点是什么?
- 25.promise和async await的区别是什么?
- 26.浏览器的存储方式有哪些?
- 27.token存在sessionstorage还是loaclstorage?
- 28.token的登录流程。
- 29.页面渲染的过程是怎样的?
- 30.DOM树和渲染树有什么区别?
- 31.精灵图和base64的区别是什么?
- 32.svg格式了解多少?
- 33.了解过JWT吗?
- 34.npm的底层环境是什么?
- 35.HTTP协议?
- 36.说一下浏览器的缓存策略。
- 37.说一下什么是“同源策略”?
- 38.防抖和节流是什么?
- 39.解释一下什么是json?
- 40.当数据没有请求过来的时候,该怎么做?
- 41.有没有做过无感登录(无感刷新token)?
- 42.大文件上传是怎么做的?
- 三、HTML5CSS3
- 四、Vue
- 1.v-if和v-show的区别?常用的vue指令
- 2.如何理解MVVM的?
- 3.v-for中的key值的作用是什么?
- 4.说一下你对vue生命周期的理解。
- 5.在created和mounted去请求数据,有什么区别?
- 6.vue中的修饰符有哪些?
- 7.elementui是怎么做表单验证的?
- 8.vue如何进行组件通信?
- 9.keep-alive是什么?怎么使用?
- 10.axios是怎么做封装的?
- 11.vue路由时怎么传参的?
- 12.vue路由的hash模式和history模式有什么区别?
- 13.路由拦截是怎么实现的?
- 14.说一下vue的动态路由。
- 15.如何解决刷新后二次加载路由?
- 16.vuex刷新数据会丢失吗?怎么解决?
- 17.computed和watch的区别?
- 18.vuex在什么场景会去使用?属性有哪些?
- 19.vue的双向数据绑定原理是什么?
- 20.了解diff算法和虚拟DOM吗?
- 21.vue和jquery的区别是什么?
- 22.vuex的响应式处理。
- 23.vue中遍历全局的方法有哪些?
- 24.如何搭建脚手架?
- 25.如何封装一个组件?
- 26.封装一个可复用的组件,需要满足什么条件?
- 27.vue的过滤器怎么使用?
- 28.vue中如何做强制刷新?
- 29.vue3和vue2有哪些区别?
- 30.vue的性能优化怎么做?
- 31.白屏优化怎么去做?
- 31.首屏优化该如何去做?
- 32.vue3的性能为什么比vue2好?
- 33.vue3为什么使用proxy?
- 34.如何封装可复用的组件?
- 35.你是如何规划项目文件的?
- 36.是否使用过nuxt.js?
- 37.SEO如何优化?
- 38.插槽slot
- 39.ref和reactive有什么区别?
- 40. NextTick是什么
- 五、Echarts
- 六、Uni-APP
- 七、Weabpack
- 八、Git
一、CSS
1.说一下CSS的盒模型。
在HTML页面中的所有元素都可以看成是一个盒子
盒子的组成:内容content、内边距padding、边框border、外边距margin
盒模型的类型:
标准盒模型:margin + border + padding + content
IE盒模型:margin + content(border + padding)
控制盒模型的模式:box-sizing:content-box(默认值,标准盒模型)、border-box(IE盒模型);
2.CSS选择器的优先级?
CSS的特性:继承性、层叠性、优先级
优先级:写CSS样式的时候,会给同一个元素添加多个样式,此时谁的权重高就显示谁的样式
标签、类/伪类/属性、全局选择器、行内样式、id、!important
!important > 行内样式 > id > 类/伪类/属性 > 标签 > 全局选择器
3.隐藏元素的方法有哪些?
display:none; 元素在页面上消失,不占据空间
opacity:0; 设置了元素的透明度为0,元素不可见,占据空间位置
visibility:hidden; 让元素消失,占据空间位置,一种不可见的状态
position:absolute; clip-path
4.px和rem的区别是什么?
px是像素,显示器上给我们呈现画面的像素,每个像素的大小是一样,绝对单位长度
rem,相对单位,相对于html根节点的font-size的值,直接给html节点的font-size:62.5%;
1rem = 10px; (16px*62.5%=10px)
5.重绘重排有什么区别?
重排(回流):布局引擎会根据所有的样式计算出盒模型在页面上的位置和大小
重绘:计算好盒模型的位置、大小和其他一些属性之后,浏览器就会根据每个盒模型的特性进行绘制
浏览器的渲染机制:
对DOM的大小、位置进行修改后,浏览器需要重新计算元素的这些几何属性,就叫重排
对DOM的样式进行修改,比如color和background-color,浏览器不需要重新计算几何属性的时候,直接绘制了该元素的新样式,那么这里就只触发了重绘
浏览器先重排,再重绘,重排会导致重绘!
6.让一个元素水平垂直居中的方式有哪些?
- 定位+margin:这种方法依赖于手动计算(和偏移)元素的宽度和高度
/* 父容器样式 */
.parent {
position: relative; /* 设置相对定位 */
height: 100vh; /* 设置父容器的高度 */
}
/* 子元素样式 */
.child {
position: absolute; /* 子元素绝对定位 */
top: 50%; /* 置于容器的50%顶部位置 */
left: 50%; /* 置于容器的50%左侧位置 */
width: 50px; /* 宽度 */
height: 50px; /* 高度 */
margin-top: -25px; /* 向上偏移自身高度的一半 */
margin-left: -25px; /* 向左偏移自身宽度的一半 */
background-color: blue; /* 背景颜色 */
}
- 定位+transform:同上
/* 子元素样式 */
transform: translate(-50%, -50%); /* 使用transform平移自身的50% */
- flex布局
/* 父容器样式 */
.parent {
display: flex; /* 启用Flex布局 */
justify-content: center; /* 水平居中 */ 主轴方向居中
align-items: center; /* 垂直居中 */
height: 100vh; /* 设置父容器的高度,这里为视窗高度 */
}
- grid布局
- table布局
7.CSS的哪些属性哪些可以继承?哪些不可以继承?
CSS的三大特性:继承、层叠、优先级
子元素可以继承父类元素的样式:
1.字体的一些属性:font
2.文本的一些属性:line-height
3.元素的可见性:visibility:hidden
4.表格布局的属性:border-spacing
5.列表的属性:list-style
6.页面样式属性:page
7.声音的样式属性
8.有没有用过预处理器?
预处理语言增加了变量、函数、混入等强大的功能
SASS LESS
二、JavaSscipt
1.JS由哪三部分组成?
ECMAScript:JS的核心内容,描述了语言的基础语法,比如var,for,数据类型(数组、字符串),
文档对象模型(DOM):DOM把整个HTML页面规划为元素构成的文档
浏览器对象模型(BOM):对浏览器窗口进行访问和操作
2.JS有哪些内置对象?
String Boolean Number Array Object Function Math Date RegExp…
Math:abs() sqrt() max() min()
Data:new Data() getYear()
Array
String
concat() length slice() split()
3.操作数组的方法有哪些?
push() pop() sort() splice() unshift() shift() reverse() concat() join() map() filter() ervery() some() reduce() isArray() findIndex()
哪些方法会改变原数组?
push() pop() unshift() shift() sort() reverse() splice()
var list=[1,2,5,7,9,20]
list.sort() //排序
console.log(list)
var newList=list.filter(function(item){ //过滤器
if(item>=3){
return item
}
})
var str="banana"
var listStr=str.split("") //拆成单独的字母
var str2=list.join("") //还原回来
// slice 切片
var arr=list.slice(0,3) // 获取从索引0开始到索引2元素
var arr=list.slice(1,3) // 获取第2到3个元素
// splice 和 slice 相似
list.splice(0,3) // 从索引0开始删除3个元素
数组遍历的方法:
- while循环遍历
- for循环遍历
- for in 遍历(i为索引)
list=["a","b","c","d"]
for(var i in list){
console.log(list[i]);
}
- for of 遍历(i为值)
for(var i of list){
console.log(i);
}
- map方法遍历
list.map(function (value, index) {
console.log("第"+index+"个元素是:"+value)
})
4.JS对数据类的检测方式有哪些?
- typeof():虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'
虽然 null为object,但这只是JavaScript 存在的一个悠久 Bug,不代表null就是引用数据类型,并且null本身也不是对象
- instanceof constructor(): 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
object instanceof constructor //object为实例对象,constructor为构造函数
- Object.prototype.toString.call():调用该方法,统一返回格式“[object Xxx]”的字符串
Object.prototype.toString({}) // "[object Object]"
Object.prototype.toString.call({}) // 同上结果,加上call也ok
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(document) //"[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"
5.说一下闭包,闭包有什么特点?
什么是闭包?外部函数和内部函数,函数嵌套,返回值是内部函数,内部函数使用了外部函数的变量,因为闭包,外部函数的变量不会被销毁。
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存再内存中,不会被垃圾回收机制回收
缺点:闭包较多的时候,会消耗内存,导致页面的性能下降,在IE浏览器中才会导致内存泄漏
使用场景:防抖,节流,函数嵌套函数避免全局污染的时候
- 创建私有变量
- 延长变量的生命周期
function outerFun() {
//函数嵌套函数,内部函数就是闭包
let a = 10;
function innerFun() {
console.log(a);
}
return innerFun
}
let fun = outerFun()
fun()
6.前端的内存泄漏怎么理解?
JS里已经分配内存地址的对象,但是由于长时间没有释放或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃的情况。
垃圾回收机制
因素:一些为生命直接赋值的变量;一些未清空的定时器;过度的闭包;一些引用元素没有被清除。
7.事件委托是什么?
又叫事件代理,原理就是利用了事件冒泡的机制来实现,也就是说把子元素的事件绑定到了父元素的身上。如果子元素组织了事件冒泡,那么委托也就不成立
阻止事件冒泡:event.stopPropagation()
阻止事件的默认行为:event.preventDefault() 或者 return false
addEventListener(‘click’,函数名,true/false) 默认是false(事件冒泡),true(事件捕获)
好处:提高性能,减少事件的绑定,也就减少了内存的占用。
let ul = document.querySelector("ul");
ul.onclick = function (e) {
ul.removeChild(e.target);
};
document.onkeydown = function (e) { //键盘事件
let key = e.keyCode
console.log(key)
}
8.基本数据类型和引用数据类型的区别?
基本数据类型:String Number Boolean undefined null
基本数据类型保存在栈内存当中,保存的就是一个具体的值
引用数据类型(复杂数据类型):Object Function Array
保存在堆内存当中,声明一个引用类型的变量,它保存的是引用类型数据的地址
假如声明两个引用类型同时指向了一个地址的时候,修改其中一个那么另外一个也会改变
区别:
- 赋值的区别:原始类型赋值,引用类型赋的是引用。
- 比较的区别∶原始类型比较的是值,引用类型比较的是引用是否指向同一个对象。
- 原始数据类型检测:typeof(值),引用数据类型检测︰值instranceof类型
let nu = null
console.log(typeof (nu)); //打印出来的是object
console.log( null instanceof object ); //打印出来是false
9.说一下原型链。
原型就是一个普通对象,每个对象都可以使用自己原型对象上的属性和方法,它是为构造函数的实例共享属性和方法;当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾
所有实例中引用的原型都是同一个对象
使用prototype可以把方法挂在原型上,内存值保存一份
__proto__可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype)
- 构造函数Person存在原型对象Person.prototype
- 构造函数生成实例对象person,person的__proto__指向构造函数Person原型对象Person.prototype
- Person.prototype._proto_ 指向内置对象,因为原型对象 Person.prototype 本身是一个普通对象,而普通对象的构造函数都是Object,所有的构造器都是函数对象,函数对象都是 Function构造产生的,Object.proto === Function.prototype
- Object的原型对象也有__proto__属性指向null,null是原型链的顶端,Object.prototype.proto === null
//1.通过对象的_proto_获取
let cat = {
name: "喵喵",
};
cat._proto_.eat = function () {
console.log("吃鱼");
};
cat.eat();
//2.通过构造函数的prototype函数拿到原型
function Cat(name, age) {
this.name = name;
this.age = age;
}
let cat = new Cat("喵喵", 2);
Cat.prototype.eat = function () {
console.log("吃鱼");
};
cat.eat();
//3.通过类的prototype函数拿到原型,ES6的类
class Cat(name, age) {
this.name = name;
this.age = age;
}
let cat = new Cat("喵喵", 2);
Cat.prototype.eat = function () {
console.log("吃鱼");
};
cat.eat();
//4.原型有什么用? -通过原型扩展这个对象
let data = new Data();
console.log(data);
data.prototype.formate = function () {
let year = this.getFullYear; // this调取formate对象
let month = this.getMonth() + 1;
let data = this.getData();
return `${year}年 ${month}月 ${data}日`;
};
console.log(data.formate());
//继承 -例子:登录
class User{
constructor(username, password) {
this.username = username;
this.password = password;
}
login() {
console.log("登录")
}
}
class Admin extends User{
deletePerson() {
console.log("删除一个人")
}
constructor(username, password, email) { //创造自己独有的属性
super(username, password);
this.email = email;
}
}
let admin = new Admin()
admin.login()
//ES5继承:prototype
function User(username, password) {
this.username = username;
this.password = password;
this.login=function() {
console.log("登录")
}
}
function Admin() {
this.deletePerson() = function () {
console.log("删除一个人")
};
}
//原型链最上层会一直找到object,这是系统默认的,不需要自己指定
// Object.prototype.login = function () {
// console.log("Object原型上的登录方法");
// };
Admin.prototype = new User();
let admin = new Admin();
admin.login()
10.new操作符具体做了什么?
1.先创建一个空对象
2.把空对象和构造函数通过原型链进行链接
3.把构造函数的this绑定到新的空对象身上
4.根据构建函数返回的类型判断,如果是值类型,则返回对象,如果是引用类型,就要返回这个引用类型
11.JS是如何实现继承的?
1.原型链继承
2.借用构造函数继承
3.组合式继承
4.ES6的class类继承
12.JS的设计原理是什么?
JS引擎 运行上下文 调用栈 事件循环 回调
13.JS中关于this指向的问题
- 全局对象中的this指向:指向的是window
- 全局作用域或者普通函数中的this:指向全局window
- this永远指向最后调用它的那个对象:在不是箭头函数的情况下
- new 关键词改变了this的指向
- apply,call,bind:可以改变this指向,不是箭头函数
- 箭头函数中的this:它的指向在定义的时候就已经确定了
箭头函数它没有this,看外层是否有函数,有就是外层函数的this,没有就是window - 匿名函数中的this:永远指向了window,匿名函数的执行环境具有全局性,因此this指向window
14.script标签里的async和defer有什么区别?
当没有async和defer这两个属性的时候,浏览器会立刻加载并执行指定的脚本
有async:加载和渲染后面元素的过程将和script的加载和执行并行进行(异步)
有defer:加载和渲染后面元素的过程将和script的加载并行进行(异步),但是它的执行事件要等所有元素解析完成之后才会执行
15.setTimeout最小执行时间是多少?
HTML5规定的内容:
setTimeout最小执行时间是4ms
setInterval最小执行时间是10ms
16.ES6和ES5有什么区别?
JS的组成:ECMAScript BOM DOM
ES5:ECMAScript5,2009年ECMAScript的第五次修订,ECMAScript2009
ES6:ECMAScript6,2015年ECMAScript的第六次修订,ECMAScript2015,是JS的下一个版本标准
17.ES6的新特性有哪些?
- 新增块级作用域(let,const)
- 不存在变量提升
//变量提升,整个script标签下任何位置都能访问str
console.log(str) //定义前访问,打印出undefined
var str = "hello"
console.log(str) //定义后访问,打印出hello
- 存在暂时性死区的问题
- 块级作用域的内容
- 不能在同一个作用域内重复声明
- 新增了定义类的语法糖(class)
- 新增了一种基本数据类型(symbol)
- 新增了解构赋值:从数组或者对象中取值,然后给变量赋值
//数组的解构赋值
let [n, m] = [10, 20]
console.log(n)
console.log(m)
//变量的解构赋值
let { name, age } = { name: "xiaoming", age: 100 }
//利用结构赋值传参
let xm = { name: "xiaoming", age: 10 }
function getName({name}) {
return name
}
let reslut=getName(xm)
- 新增了函数参数的默认值
- 给数组新增了API
- 对象和数组新增了扩展运算符
- Promise
解决回调地狱的问题。
自身有all,reject,resolve,race方法
原型上有then,catch
把异步操作队列化
三种状态:pending初始状态,fulfilled操作成功,rejected操作失败
状态:pending -> fulfilled;pending -> rejected 一旦发生,状态就会凝固,不会再变
async await
同步代码做异步的操作,两者必须搭配使用
async表明函数内有异步操作,调用函数会返回promise
await是组成async的表达式,结果是取决于它等待的内容,如果是promise那就是promise的结果,如果是普通函数就进行链式调用
await后的promise如果是reject状态,那么整个async函数都会中断,后面的代码不执行 - 新增了模块化(import,export)
- 新增了set和map数据结构
set就是不重复
map的key的类型不受限制 - 新增了generator
- 新增了箭头函数
不能作为构造函数使用,不能用new
箭头函数就没有原型
箭头函数没有arguments
箭头函数不能用call,apply,bind去改变this的执行
this指向外层第一个函数的this - 模板字符串:可以换行
let year = "2023"
let month = "10"
let date = "31"
let reslut = year + "年" + month + "月" + date + "日"
let reslut2=`${year}年${month}月${date}日` //变量用${}
18.call,aply,bind三者有什么区别?
都是改变this指向和函数的调用,call和apply的功能类似,只是传参的方法不同
call方法传的是一个参数列表
apply传递的是一个数组
bind传参后不会立刻执行,会返回一个改变了this指向的函数,这个函数还是可以传参的,bind()()
call方法的性能要比apply好一些,所以call用的更多一点
19.用递归的时候有没有遇到什么问题?
如果一个函数内可以调用函数本身,那么这个就是递归函数
函数内部调用自己
特别注意:写递归必须要有退出条件return
20.如何实现一个深拷贝?
深拷贝就是完全拷贝一份新的对象,会在堆内存中开辟新的空间,拷贝的对象被修改后,原对象不受影响
主要针对的是引用数据类型
1.扩展运算符
2.JSON.parse(JSON.stringify())
3.利用递归函数实现
21.说一下事件循环(微任务、宏任务…)
JS是一个单线程的脚本语言
主线程 执行栈 任务队列 宏任务 微任务
线程先执行同步任务,然后才去执行任务队列里的任务,如果在执行宏任务之前有微任务,那么要先执行微任务
全部执行完之后等待主线程的调用,调用完之后再去任务队列中查看是否有异步任务,这样一个循环往复的过程就是事件循环!
消息队列中的任务称为宏任务,宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合。因为添加事件是由系统操作的,JavaScript 代码不能准确掌控任务要添加到队列中的位置,控制不了任务在消息队列中的位置,所以很难控制开始执行任务的时间。比如,通过 setTimeout 来设置两个回调任务,并让它们按照前后顺序来执行,中间也不要再插入其他的任务,因为如果这两个任务的中间插入了其他的任务,就很有可能会影响到第二个定时器的执行时间了。但实际情况是我们不能控制的,比如在调用 setTimeout 来设置回调任务的间隙,消息队列中就有可能被插入很多系统级的任务。
微任务就是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。每个宏任务都关联了一个微任务队列。当有实时性要求高的任务时,如果创建成宏任务,就会排在消息队列的最后面,需要等前面的所有宏任务执行完才能执行到它。如果创建成微任务,就加在当前宏任务关联的微任务队列后,执行时机就是当前宏任务执行完成前,实现了实时性的要求!!
产生微任务有两种方式:
- 第一种方式是使用 MutationObserver 监控某个 DOM 节点,然后再通过 JavaScript来修改这个节点,或者为这个节点添加、删除部分子节点,当 DOM 节点发生变化时,就会产生 DOM 变化记录的微任务。
- 第二种方式是使用 Promise,当调用 Promise.resolve() 或者 Promise.reject() 的时候,也会产生微任务
产生的微任务都会被 JavaScript 引擎按照顺序保存到微任务队列中。通常情况下,在当前宏任务中的 JavaScript 快执行完成时,也就在 JavaScript 引擎准备退出全局执行上下文并清空调用栈的时候,JavaScript 引擎会检查全局执行上下文中的微任务队列,然后按照顺序执行队列中的微任务。如果在执行微任务的过程中,产生了新的微任务,同样会将该微任务添加到微任务队列中,V8 引擎一直循环执行微任务队列中的任务,直到队列为空才算执行结束。也就是说在执行微任务过程中产生的新的微任务并不会推迟到下个宏任务中执行,而是在当前的宏任务中继续执行。
22.Ajax是什么?axios、Fetch API?
这三者都是用来发送网络请求
其中Ajax全称是:Asynchronous JavaScript And XML,翻译过来就是“异步的 Javascript 和 XML”,是创建交互式网页应用的网页开发技术,这是一种思想,在不重新加载整个网页的前提下,与服务器交换数据并更新部分内容,实现页面的局部刷新
库/框架 | 特点 | 缺点 |
---|---|---|
Ajax | 一种技术集成,主要利用XHR实现异步请求 | 易于使用 |
Fetch | 原生API,基于Promise,实现简洁异步请求 | 缺少取消请求 |
Axios | 一个封装库,基于XHR封装,较为方便使用 | 较为臃肿 |
- 原生Ajax:XMLHttpRequest 只是实现 Ajax 的一种方式
通过XmlHttpRequest对象向服务器发送异步请求,然后从服务器拿到数据,最后通过JS操作DOM更新页面
1.创建XmlHttpRequest对象 xmh
2.通过xmh对象里的open()方法和服务器建立连接
3.构建请求所需的数据,并通过xmh对象的send()发送给服务器
4.通过xmh对象的onreadystate chansge事件监听服务器和你的通信状态
5.接收并处理服务器响应的数据结果
6.把处理的数据更新到HTML页面上
// 原生ajax
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://wuyou.com/common/get?name=三体&age=18') //get只能传输纯文本
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
//XMLHttpRequest.DONE就是4
console.log(JSON.parse(xhr.responseText))
}
}
也可以将上面的进行封装
function ajax(url) {
上面的代码,只是把其中的网址作为参数url进行传递
xhr.send(null);
}
ajax('http://wuyou.com/common/get?name=三体&age=18')
xhr.open('POST', 'http://wuyou.com/common/post')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
//需要声明发送的数据类型,因为post可以发送各种类型
xhr.send('name=三体&age=18')
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
}
}
- Fetch API:代替原生ajax,基于Promise实现的,采用.then()链式调用的方法处理结果,这样在请求中再发送请求,就不用像原生的那样继续嵌套回调函数xhr.onreadystatechange,从而不会产生回调地狱的问题。默认不携带cookie。
// 3. Fetch API
fetch('http://wuyou.com/common/get?name=三体&age=18') //get方法
.then(res => {
if (res.ok) {
return res.json()
}
})
.then(data => {
console.log(data)
})
fetch('http://wuyou.com/common/post', { //post方法
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: '三体',
age: 18
})
})
.then(res => {
if (res.ok) {
return res.json()
}
})
.then(data => {
console.log(data)
})
- axios:是随着Vue兴起的基于Promise封装的网络请求库,是基于XHR进行二次封装,感觉就像是Fetch和XHR的混合
(async () => {
const ins = axios.create({
baseURL: 'http://wuyou.com/common' //给出基地址,方便下面进行拼接
})
ins.interceptors.request.use(config => {
console.log('发送了请求')
//请求拦截器,在发送请求时候拦截,修改请求配置或进行日志记录
return config //返回修改好的请求
})
ins.interceptors.response.use(res => { //响应拦截器
return res
})
const res1 = await ins.get('/get', {
params: {
name: '三体',
age: 18
}
})
const res2 = await ins.post('/post', {
name: '三体',
age: 18
})
console.log(res2.data)
})()
23.get和post有什么区别?
1.get一般是获取数据,post一般是提交数据
2.get参数会放在url上,所以安全性比较差,post是放在body中
3.get请求刷新服务器或退回是没有影响的,post请求退回时会重新提交数据
4.get请求时会被缓存,post请求不会被缓存
5.get请求会被保存在浏览器历史记录中,post不会
6.get请求只能进行url编码,post请求支持很多种
24.promise的内部原理是什么?它的优缺点是什么?
Promise对象,封装了一个异步操作并且还可以获取成功或失败的结果
Promise主要就是解决回调地狱的问题,之前如果异步任务比较多,同时他们之间有相互依赖的关系,就只能使用回调函数处理,这样就容易形成回调地狱,代码的可读性差,可维护性也很差
function getTea() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("green tea");
}, 2000);
})
}
//resolve传出来的值是then里面的形参
getTea().then((data) => {
console.log(data);
});
function getHotpot() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("hotpot");
}, 2000);
})
}
//先吃火锅再喝茶
getHotpot().then((data) => {
console.log(data);
return getTea(); //第二个then的参数是上一个then的返回值
}).then((data) => {
console.log(data);
});
async function getData() {
//await后面加一个promise对象就能直接拿到resolve的值
let hotpot = await getHotpot();
let tea = await getTea();
console.log(hotpot);
console.log(tea);
}
//异步的看起来更像同步的代码
有三种状态:pending初始状态 fulfilled成功状态 rejected失败状态
状态改变只会有两种情况,pending -> fulfilled; pending -> rejected 一旦发生,状态就会凝固,不会再变
首先就是我们无法取消promise,一旦创建它就会立即执行,不能中途取消
如果不设置回调,promise内部抛出的错位就无法反馈到外面
若当前处于pending状态时,无法得知目前在哪个阶段。
原理:
构造一个Promise实例,实例需要传递函数的参数,这个函数有两个形参,分别都是函数类型,一个是resolve一个是reject
promise上还有then方法,这个方法就是来指定状态改变时的确定操作,resolve是执行第一个函数,reject是执行第二个函数
Promise.all: 可以并行执行多个 Promise 实例,并等待所有的 Promise 实例都完成(或者至少一个失败)。
- 输入: 它接收一个 Promise 对象的数组作为参数。
- 输出:
- 成功情况:如果所有传入的 Promise 实例都成功解决,则Promise.all 返回一个新的 Promise,这个 Promise解决为一个数组,其中包含所有传入 Promise的解决值,按照它们原始数组的顺序。
- 失败情况:如果任何一个传入的 Promise 实例失败(即被拒绝),Promise.all 返回的 Promise 会立即失败,并且失败的原因是第一个失败 Promise 的原因。
- 错误处理:一旦任何一个传入的 Promise 失败,整个 Promise.all 调用会立刻结束并返回一个拒绝的 Promise。如果一个 Promise 在 Promise.all 调用中被拒绝,但是它自己的 catch 方法处理了这个拒绝,那么这个拒绝就不会传播到 Promise.all 的结果中。
25.promise和async await的区别是什么?
- 都是处理异步请求的方式
- promise是ES6,async await 是ES7的语法
- async await是基于promise实现的,他和promise都是非阻塞性的
优缺点:
1.promise是返回对象我们要用then,catch方法去处理和捕获异常,并且书写方式是链式,容易造成代码重叠,不好维护,async await 是通过tra catch进行捕获异常
2.async await最大的优点就是能让代码看起来像同步一样,只要遇到await就会立刻返回结果,然后再执行后面的操作
promise.then()的方式返回,会出现请求还没返回,就执行了后面的操作
26.浏览器的存储方式有哪些?
1.cookies
H5标准前的本地存储方式
兼容性好,请求头自带cookie
存储量小,资源浪费,使用麻烦(封装)
2.localstorage
H5加入的以键值对为标准的方式
操作方便,永久存储,兼容性较好
保存值的类型被限定,浏览器在隐私模式下不可读取,不能被爬虫
3.sessionstorage
当页面关闭后就会立刻清理,会话级别的存储方式
4.indexedDB
H5标准的存储方式,他是以键值对进行存储,可以快速读取,适合WEB场景
27.token存在sessionstorage还是loaclstorage?
token:验证身份的令牌,一般就是用户通过账号密码登录后,服务端把这些凭证通过加密等一系列操作后得到的字符串
1.存loaclstorage里,后期每次请求接口都需要把它当作一个字段传给后台
2.存cookie中,会自动发送,缺点就是不能跨域
如果存在localstorage中,容易被XSS攻击,但是如果做好了对应的措施,那么是利大于弊(存储量占一个很大优势)
如果存在cookie中会有CSRF攻击
28.token的登录流程。
- 客户端用账号密码请求登录
- 服务端收到请求后,需要去验证账号密码
- 验证成功之后,服务端会签发一个token,把这个token发送给客户端
- 客户端收到token后保存起来,可以放在cookie也可以是localstorage
- 客户端每次向服务端发送请求资源的时候,都需要携带这个token
- 服务端收到请求,接着去验证客户端里的token,验证成功才会返回客户端请求的数据
29.页面渲染的过程是怎样的?
DNS解析
建立TCP连接
发送HTTP请求
服务器处理请求
渲染页面
浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树
再把CSS解析成CSSOM
把DOM和CSSOM合并为渲染树
布局
把渲染树的每个节点渲染到屏幕上(绘制)
断开TCP连接
30.DOM树和渲染树有什么区别?
DOM树是和HTML标签一一对应的,包括head和隐藏元素
渲染树是不包含head和隐藏元素
31.精灵图和base64的区别是什么?
精灵图:把多张小图整合到一张大图上,利用定位的一些属性把小图显示在页面上,当访问页面可以减少请求,提高加载速度
base64:传输8Bit字节代码的编码方式,把原本二进制形式转为64个字符的单位,最后组成字符串
base64是会和html css一起下载到浏览器中,减少请求,减少跨域问题,但是一些低版本不支持,若base64体积比原图片大,不利于css的加载。
32.svg格式了解多少?
基于XML语法格式的图像格式,全称是可缩放矢量图,其他图像是基于像素的,SVG是属于对图像形状的描述,本质是文本文件,体积小,并且不管放大多少倍都不会失真
1.SVG可直接插入页面中,成为DOM一部分,然后用JS或CSS进行操作
2.SVG可作为文件被引入
4. SVG可以转为base64引入页面
33.了解过JWT吗?
JSON Web Token ,通过JSON形式作为在web应用中的令牌,可以在各方之间安全的把信息作为JSON对象传输
信息传输、授权
JWT的认证流程
- 前端把账号密码发送给后端的接口
- 后端核对账号密码成功后,把用户id等其他信息作为JWT 负载,把它和头部分别进行base64编码拼接后签名,形成一个JWT(token),这是一个字符串。
- 前端每次请求时都会把JWT放在HTTP请求头的Authorization字段内
- 后端检查是否存在,如果存在就验证JWT的有效性(签名是否正确,token是否过期)
- 验证通过后后端使用JWT中包含的用户信息进行其他的操作,并返回对应结果
简洁、包含性、因为Token是JSON加密的形式保存在客户端,所以JWT是跨语言的,原则上是任何web形式都支持。
34.npm的底层环境是什么?
全称是node package manager,node的包管理和分发工具,已经成为分发node模块的标准,是JS的运行环境
npm的组成:网站、注册表、命令行工具
35.HTTP协议?
非持久连接就是HTTP1.0
持久连接是HTTP1.1,它可以并发的发送请求,但是服务器只能按先后顺序去响应请求,这就会产生问题,比如拥塞等
HTTP2.0也是持久连接,但是可以并行相应。并且增加了主动推送的功能,比如希望获取三个资源分别是index.html,css以及js文件。我只使用了一次请求,CSS和JS文件是服务端推送给我的。在HTTP 1.1中,需要三个请求并获取三个响应才能完成。采用二进制格式而非文本格式。使用报头压缩,降低开销。
1.请求头信息:
Accept:浏览器告诉服务器所支持的数据类型
Host:浏览器告诉服务器我想访问服务器的哪台主机
Referer:浏览器告诉服务器我是从哪里来的(防盗链)
User-Agent:浏览器类型、版本信息
Date:浏览器告诉服务器我是什么时候访问的
Connection:连接方式
Cookie
X-Request-With:请求方式
2.响应头信息:
Location:这个就是告诉浏览器你要去找谁
Server:告诉浏览器服务器的类型
Content-Type:告诉浏览器返回的数据类型
Refresh:控制了的定时刷新
36.说一下浏览器的缓存策略。
强缓存(本地缓存)、协商缓存(弱缓存)
强缓:不发起请求,直接使用缓存里的内容,浏览器把JS,CSS,image等存到内存中,下次用户访问直接从内存中取,提高性能
协缓:需要向后台发请求,通过判断来决定是否使用协商缓存,如果请求内容没有变化,则返回304,浏览器就用缓存里的内容
强缓存的触发:
HTTP1.0:时间戳响应标头
HTTP1.1:Cache-Control响应标头
协商缓存触发:
HTTP1.0:请求头:if-modified-since 响应头:last-modified
HTTP1.1:请求头:if-none-match 响应头:Etag
37.说一下什么是“同源策略”?
http:// www. aaa.com:8080/index/vue.js
协议 子域名 主域名 端口号 资源
同源策略是浏览器的核心,如果没有这个策略就会遭受网络攻击,主要指的就是协议+域名+端口号三者一致,若其中一个不一样则不是同源,会产生跨域。
三个允许跨域加载资源的标签:img link script
跨域是可以发送请求,后端也会正常返回结果,只不过这个结果被浏览器拦截了!一定要注意跨域是浏览器的限制!
解决跨域的方法:
- CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输HTTP头组成,这些HTTP头决定浏览器是否阻止前端 JavaScript代码获取跨域请求的响应。CORS 实现起来非常方便,只需要增加一些 HTTP 头,让服务器能声明允许的访问来源。
- Proxy代理,也称网络代理,是一种特殊的网络服务,允许一个(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。
38.防抖和节流是什么?
防抖:避免事件重复触发,只要最后一次操作的触发
使用场景:1.频繁和服务端交互 2.输入框的自动保存事件
let inp = document.querySelector("input"); //找到一个标签为input的元素
inp.oninput =function(){
console.log(this.value)
}
如果每一次输入都是和后台进行一次数据交互,太影响性能了!
let inp = document.querySelector("input");
let t=null;
inp.oninput=function(){
if(t!==null){
clearTimeout(t);
}
t=setTimeout(() => {
console.log(this.value); //这是业务逻辑,和我其余代码(防抖代码)混到了一起
},500)
}
let inp = document.querySelector("input"); //找到一个标签为input的元素
//inp执行一个oninput事件,用防抖来约束这个事件,这个事件的执行函数是后面的function
inp.oninput = debounce(function () {
console.log(this.value);
//这里的this指向window,因为function就是下面的fn,如果直接是fn(),那么输出就是undefined,
//必须使用call或者其他改变this指向
}, 500);
//相当于一个闭包,返回值是内部函数
function debounce(fn, delay) {
let t = null; //内部函数使用了外部函数的变量,因为闭包,变量不会被销毁
return function () {
if (t !== null) {
clearTimeout(t);
}
t = setTimeout(() => {
console.log(this); //这里的this指向input
// fn() //window调用
fn.call(this); //this调用的,this是事件对象oninput
}, delay);
};
}
节流:把频繁触发的事件减少,每隔一段时间执行,控制高频事件执行次数
使用场景:scroll事件
window.onscroll = throttle(function () {
console.log("hello word"); //
}, 500);
function throttle(fn, delay) {
let flag = true;
return function () {
if (flag) {
setTimeout(() => {
fn.call(this);
flag = true;
}, delay);
}
flag = false;
};
}
实际应用: 实现页面的滚动回到顶部
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
button {
position: fixed;
right: 100px;
bottom: 100px;
display: none;
}
body {
height: 2000px;
}
</style>
</head>
<body>
<h1>hello world</h1>
<button>👆</button>
<script>
let btn = document.querySelector("button");
btn.onclick = function () {
window.scrollTo(0, 0);
};
let timer = null;
window.onscroll = function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
if (document.documentElement.scrollTop > 0) {
btn.style.display = "block";
} else {
btn.style.display = "none";
}
timer = null;
}, 500);
};
// 利用闭包,封装防抖函数
function debounce(fn) {
let timer = null;
function eventFun() {
if (timer != null) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn();
timer = null;
}, 500);
}
return eventFun;
}
window.onerror = debounce(() => {
if (document.documentElement.scrollTop > 0) {
btn.style.display = "block";
} else {
btn.style.display = "none";
}
});
// 利用闭包,封装节流函数
function throttle(fn) {
let mark = true;
return function () {
if (mark) {
setTimeout(() => {
fun();
mark = true;
}, 500);
}
};
}
</script>
</body>
</html>
39.解释一下什么是json?
JSON是一种纯字符串形式的数据,它本身不提供任何方法,适合在网络中进行传输
JSON数据存储在.json文件中,也可以把JSON数据以字符串的形式保存在数据库、Cookise中
JS提供了JSON.parse() JSON.stringify()
什么时候使用json:定义接口;序列化;生成token;配置文件package.json
40.当数据没有请求过来的时候,该怎么做?
可以在渲染数据的地方给一些默认的值
if判断语句
41.有没有做过无感登录(无感刷新token)?
1.在响应期中拦截,判断token返回过期后,调用刷新token的接口
2.后端返回过期时间,前端判断token的过期时间,去调用刷新token的接口
3.写定时器,定时刷新token接口
流程:
1.登录成功后保存token 和 refresh_token
2.在响应拦截器中对401状态码引入刷新token的api方法调用
3.替换保存本地新的token
4.把错误对象里的token替换
5.再次发送未完成的请求
6.如果refresh_token过期了,判断是否过期,过期了就清楚所有token重新登录
42.大文件上传是怎么做的?
分片上传:
1.把需要上传的文件按照一定的规则,分割成相同大小的数据块
2.初始化一个分片上传任务,返回本次分片上传的唯一标识
3.按照一定的规则把各个数据块上传
4.发送完成后,服务端会判断数据上传的完整性,如果完整,那么就会把数据库合并成原始文件
断点续传:
服务端返回,从哪里开始 浏览器自己处理
三、HTML5CSS3
1.语义化的理解。
在写HTML页面结构时所用的标签有意义
头部用head 主体用main 底部用foot…
怎么判断页面是否语义化了?
把CSS去掉,如果能够清晰的看出来页面结构,显示内容较为正常
为什么要选择语义化?
1.让HTML结构更加清晰明了
2.方便团队协作,利于开发
3.有利于爬虫和SEO
4.能够让浏览器更好的去解析代码
5.给用户带来良好的体验
2.H5C3有哪些新特性?
H5的新特性:
1.语义化的标签
2.新增音频视频
3.画布canvas
4.数据存储localstorage sessionstorage
5.增加了表单控件 email url search…
6.拖拽释放API
CSS3的新特性:
1.新增选择器:属性选择器、伪类选择器、伪元素选择器
2.增加了媒体查询
3.文字阴影
4.边框
5.盒子模型box-sizing
6.渐变
7.过度
8.自定义动画
9.背景的属性
10.2D和3D
3.rem是如何做适配的?
rem是相对长度,相对于根元素(html)的font-size属性来计算大小,通常来做移动端的适配
rem是根据根元素font-size计算值的倍数
比如html上的font-size:16px,给div设置宽为1.5rem,1.2rem = 16px*1.2 = 19.2px.
4.解决了哪些移动端的兼容问题?
1.当设置样式overflow:scroll/auto时,IOS上的滑动会卡顿:-webkit-overflow-scrolling:touch;
2.在安卓环境下placeholder文字设置行高时会偏上:input有placeholder属性的时候不要设置行高
3.移动端字体小于12px时异常显示:应该先把在整体放大一倍,然后再用transform进行缩小
4.ios下input按钮设置了disabled属性为true显示异常:
input[typy=button]{
opcity:1
}
5.安卓手机下取消语音输入按钮
input::-webkit-input-speech-button{
display:none
}
6.IOS下取消input输入框在输入英文首字母默认大写
7.禁用IOS和安卓用户选中文字:添加全局CSS样式:-webkit-user-select:none
8.禁止IOS弹出各种窗口:-webkit-touch-callout:none
9.禁止IOS识别长串数字为电话:添加meta属性
四、Vue
1.v-if和v-show的区别?常用的vue指令
都可以控制元素的显示和隐藏
1.v-show时控制元素的display值来让元素显示和隐藏;v-if显示隐藏时把DOM元素整个添加和删除
<div v-show="isVisible">Hello!</div>
<script setup>
import { ref } from 'vue';
const isVisible = ref(true);
</script>
2.v-if有一个局部编译/卸载的过程,切换这个过程中会适当的销毁和重建内部的事件监听和子组件;v-show只是简单的css切换
3.v-if才是真正的条件渲染;v-show从false变成true的时候不会触发组件的声明周期,v-if会触发声明周期
4.v-if的切换效率比较低 v-show的效率比较高
常用的vue指令
- v-model:用于在表单输入和应用状态之间创建双向数据绑定。
<input v-model="message" />
<script setup>
import { ref } from 'vue';
const message = ref('');
</script>
- v-for:用于基于源数据多次渲染元素或模板块。
<ul>
<li v-for="item in items" :key="item.id">{{ item.text }}</li>
</ul>
<script setup>
import { ref } from 'vue';
const items = ref([{ id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }]);
</script>
- v-if / v-else-if / v-else:用于条件渲染一块内容。这个块只会在指令的表达式返回 truthy 值时被渲染。
<template v-if="type === 'A'">
A
</template>
<template v-else-if="type === 'B'">
B
</template>
<template v-else>
Not A/B
</template>
<script setup>
import { ref } from 'vue';
const type = ref('A');
</script>
- v-on:用于监听 DOM 事件。
<button v-on:click="sayHello">Click me</button>
//简写形式: <button @click="sayHello">Click me</button>
<script setup>
const sayHello = () => alert('Hello!');
</script>
- v-bind:动态绑定一个或多个属性,或一个组件 prop 到表达式。
<div v-bind:class="{ active: isActive }"></div>
///简写形式:<div :class="{ active: isActive }"></div>
<script setup>
import { ref } from 'vue';
const isActive = ref(false);
</script>
- v-slot:用于定义插槽的内容。
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
//简写形式: <template #header>Header Content</template>
- v-pre:跳过这个元素和它的子元素的编译过程。可以用来优化更新性能。
<div v-pre>{{ this will not be compiled }}</div>
- v-once:只渲染元素和组件一次。渲染后,不再随数据变化重新渲染。
<span v-once>The job title will never change: {{ jobTitle }}</span>
<script setup>
import { ref } from 'vue';
const jobTitle = ref('Developer');
</script>
2.如何理解MVVM的?
是Model-View-ViewModel的缩写。前端开发的架构模式
M:模型,对应的就是data的数据
V:视图,用户界面,DOM
VM:视图模型:Vue的实例对象,连接View和Model的桥梁
核心是提供对View和ViewModel的双向数据绑定,当数据改变的时候,ViewModel能监听到数据的变化,自动更新视图,当用户操作视图的时候,ViewModel也可以监听到视图的变化,然后通知数据进行改动,这就实现了双向数据绑定
ViewModel通过双向绑定把View和Model连接起来,他们之间的同步是自动的,不需要认为干涉,所以我们只需要关注业务逻辑即可,不需要操作DOM,同时也不需要关注数据的状态问题,因为她是由MVVM统一管理
3.v-for中的key值的作用是什么?
key属性是DOM元素的唯一标识
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
作用:
1.提高虚拟DOM的更新
2.若不设置key,可能会触发一些bug
3.为了触发过度效果
在不使用key的情况,vue会进行这样的操作:一共发生了3次更新,1次插入操作
在使用key的情况:
比较A,A,相同类型的节点,进行patch,但数据相同,不发生dom操作
比较B,B,相同类型的节点,进行patch,但数据相同,不发生dom操作
比较C,F,不相同类型的节点
比较E、E,相同类型的节点,进行patch,但数据相同,不发生dom操作
比较D、D,相同类型的节点,进行patch,但数据相同,不发生dom操作
比较C、C,相同类型的节点,进行patch,但数据相同,不发生dom操作
循环结束,将F插入到C之前
一共发生了0次更新,1次插入操作
4.说一下你对vue生命周期的理解。
组件从创建到销毁的过程就是它的生命周期(重点是创建和挂载)
作用:有些效果想要在页面渲染时候就执行,就可以写在生命周期钩子函数里面(created和beforeMount)
创建
beforeCreat在这个阶段属性和方法都不能使用
created这里时实例创建完成之后,在这里完成了数据监测,可以使用数据,修改数据,不会触发updated,也不会更新视图
挂载
beforeMount完成了模板的编译,虚拟DOM也完成创建,即将渲染,修改数据,不会触发updated
Mounted把编译好的模板挂载到页面,这里可以发送异步请求也可以访问DOM节点
更新
beforeUpdate组件数据更新之前使用,数据是新的,页面上的数据时旧的,组件即将更新,准备渲染,可以改数据
updated
render重新做了渲染,这时数据和页面都是新的,避免在此更新数据
销毁
beforeDestroy实例销毁前,在这里实例还可以用,可以清楚定时器等等
destroyed组件已经被销毁了,全部都销毁
使用了keep-alive时多出两个周期:
activited组件激活时
deactivited组件被销毁时
5.在created和mounted去请求数据,有什么区别?
created:在渲染前调用,通常先初始化属性,然后做渲染
mounted:在模板渲染完成后,一般都是初始化页面后,在对元素节点进行操作
在这里请求数据可能会出现闪屏的问题,created里不会
一般用created比较多
请求的数据对DOM有影响,那么使用created
如果请求的数据对DOM无关,可以放在mounted
6.vue中的修饰符有哪些?
1.事件修饰符
.stop 组织冒泡
.prevent 组织默认行为
.capture 内部元素触发的事件先在次处理
self 只有在event.target是当前元素时触发
.once 事件只会触发一次
.passive 立即触发默认行为
.native 把当前元素作为原生标签看待
2.按键修饰符
.keyup 键盘抬起
.keydown 键盘按下
3.系统修饰符
.ctrl
.alt
.meta
4.鼠标修饰符
.left 鼠标左键
.right 鼠标右键
5.表单修饰符
.lazy 等输入完之后再显示
.trim 删除内容前后的空格
.number 输入是数字或转为数字
7.elementui是怎么做表单验证的?
1.在表单中加rules属性,然后再data里写校验规则
2.内部添加规则
3.自定义函数校验
8.vue如何进行组件通信?

- 父子关系的组件数据传递选择 props 与 $emit进行传递,也可选择ref
- 兄弟关系的组件数据传递
- 祖先与后代组件数据传递可选择attrs与listeners或者Provide与 Inject
- 复杂关系的组件数据传递可以通过vuex存放共享的变量
- 父传子
- Props:父组件使用自定义属性,然后子组件使用props
<!-- ParentComponent.vue -->
<template>
<ChildComponent :user="user" />
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const user = ref({ name: 'Alice', age: 30 });
</script>
<!-- ChildComponent.vue -->
<template>
<div>{{ user.name }} - {{ user.age }}</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
user: Object
});
</script>
- ref:这意味着父组件对子组件的每一个属性和方法都有完全的访问权。这使得在父组件和子组件之间创建紧密耦合的实现细节变得很容易,当然也因此,应该只在绝对需要时才使用组件引用。
首先,创建一个子组件,通过 defineExpose 宏显式暴露:
<!-- ChildComponent.vue -->
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
a,
b
})
</script>
然后,在父组件中使用 ref 来获取子组件的引用,并调用其方法或访问其数据:
<!-- ParentComponent.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value 是 <Child /> 组件的实例
})
</script>
<template>
<Child ref="child" />
</template>
- 子传父
$emit子组件绑定自定义事件,第二个参数为传递的数值。触发执行后,传给父组件,父组件需要用事件监听来接收参数
<!-- ChildComponent.vue -->
<template>
<button @click="sendData">Send Data</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['updateData']);
const sendData = () => {
emit('updateData', 'New Data');
};
</script>
<!-- ParentComponent.vue -->
<template>
<ChildComponent @updateData="handleUpdate" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const handleUpdate = (data) => {
console.log(data); // Outputs: 'New Data'
};
</script>
3.子孙传递
在 Composition API 中,provide 函数用于定义可以被任何子孙组件注入的依赖。provide 接受两个参数:第一个是一个唯一的标识符(通常是一个字符串或Symbol),第二个是要提供的值。这个值可以是任何类型的数据,包括响应式的或非响应式的数据。
<script setup>
import { provide, reactive } from 'vue';
// 创建一个响应式对象
const userInfo = reactive({
name: 'Alice',
age: 25
});
// 提供这个响应式对象
provide('userInfo', userInfo);
</script>
inject 函数用于在组件内部接收由父组件或祖先组件提供的依赖。inject 也接受两个参数:第一个是 provide 中使用的标识符,第二个是一个可选的默认值,如果没有找到提供的数据,则使用这个默认值。
<script setup>
import { inject } from 'vue';
// 注入 userInfo,如果未找到,使用默认值
const userInfo = inject('userInfo', { name: 'Default', age: 0 });
console.log(userInfo.name); // 如果父组件或祖先组件有提供,将显示 'Alice'
</script>
4.vuex传值:相当于一个用来存储共享变量的容器
- state用来存放共享变量的地方
- getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值
- mutations用来存放修改state的方法。
- actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。常用来做一些异步操作
9.keep-alive是什么?怎么使用?
Vue的一个内置组件,包裹组件的时候,会缓存不活跃的组件实例,并不是销毁他们
作用:把组件切换的状态保存在内存里,防止重复渲染DOM节点,减少加载时间和性能消耗,提高用户体验
<template>
<keep-alive :include="['ComponentA', 'ComponentB']">
//组件提供了 include 和 exclude 属性,允许你指定哪些组件应该被缓存或排除,这可以通过组件的名称来设置
<component :is="currentComponent" /> //动态组件
</keep-alive>
</template>
<script setup>
import { ref } from 'vue';
const currentComponent = ref('ComponentA');
</script>
“currentComponent” 可以根据用户操作变化指向不同的组件。当切换组件时, 确保当前非活动的组件实例被缓存起来,而不是销毁。
10.axios是怎么做封装的?
下载 创建实例 接着封装请求响应拦截器 抛出 最后封装接口
11.vue路由时怎么传参的?
params传参
this.
r
o
u
t
e
r
.
p
u
s
h
(
n
a
m
e
:
′
i
n
d
e
x
′
,
p
a
r
a
m
s
:
i
d
:
i
t
e
m
.
i
d
)
t
h
i
s
.
router.push({name:'index',params:{id:item.id}}) this.
router.push(name:′index′,params:id:item.id)this.route.params.id
路由属性传参
this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …({name:'/index/{item.id}‘})
路由配置 { path:’/index:id’ }
query传参(可以解决页面刷新参数丢失的问题)
this.$router.push({
name:‘index’,
query:{id:item.id}
})
12.vue路由的hash模式和history模式有什么区别?
1.hash的路由地址上有#号,history模式没有
2.在做回车刷新的时候,hash模式会加载对应页面,history会报错404
3.hash模式支持低版本浏览器,history不支持,因为是H5新增的API
4.hash不会重新加载页面,单页面应用必备
5.history有历史记录,H5新增了pushState和replaceState()去修改历史记录,并不会立刻发送请求
6.history需要后台配置
13.路由拦截是怎么实现的?
路由拦截 axios拦截
需要在路由配置中添加一个字段,它是用于判断路由是否需要拦截
{
name:‘index’,
path:‘/index’,
component:Index,
meta:{
requirtAuth:true
}
}
router.beforeEach((to,from,next) => {
if(to.meta.requirtAuth){
if( store.satte.token ){
next()
}else{
}
}
})
14.说一下vue的动态路由。
要在路由配置里设置meta属性,扩展权限相关的字段,在路由导航守卫里通过判断这个权限标识,实现路由的动态增加和跳转
根据用户登录的账号,返回用户角色
前端再根据角色,跟路由表的meta.role进行匹配
把匹配搭配的路由形成可访问的路由
15.如何解决刷新后二次加载路由?
1.window.location.reload()
2.matcher
const router = createRouter()
export function resetRouter(){
const newRouter = creatRouter()
router.matcher = newRouter.matcher
}
16.vuex刷新数据会丢失吗?怎么解决?
vuex肯定会重新获取数据,页面也会丢失数据
1.把数据直接保存在浏览器缓存里(cookie localstorage sessionstorage)
2.页面刷新的时候,再次请求数据,达到可以动态更新的方法
监听浏览器的刷新书简,在刷新前把数据保存到sessionstorage里,刷新后请求数据,请求到了用vuex,如果没有那就用sessionstorage里的数据
17.computed和watch的区别?
计算属性常被用来创建依赖于应用状态但需要某种形式计算的数据,通常是响应式的,意味着当依赖的数据变化时,计算属性的值会自动更新。
<script setup>
import { ref, computed, watch, watchEffect } from 'vue';
// 创建响应式的引用
const firstName = ref('Jane');
const lastName = ref('Doe');
// 创建计算属性
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
// 观察fullName计算属性的变化
watch(fullName, (newName, oldName) => {
console.log(`Name changed from ${oldName} to ${newName}`);
// 在此可以执行一些如API调用或复杂逻辑的操作
});
// 使用watchEffect自动跟踪并作出反应
watchEffect(() => {
console.log(`Current full name is: ${fullName.value}`);
// 这里可以执行基于fullName变化的逻辑,例如自动保存、更新UI等
//首先会立即执行保存fullName.value,当fullName.value发生变化时,会再次执行这个函数
});
</script>
<template>
<div>
Full Name: {{ fullName }}
</div>
</template>
1.computed是计算属性,watch是监听,监听的是data中数据的变化
2.computed是支持缓存,依赖的属性值发生变化,计算属性才会重新计算,否则用缓存;watch不支持缓存
3.computed不支持异步,watch是可以异步操作
4.computed是第一次加载就监听,watch是不监听
5.computed函数中必须有return watch不用
18.vuex在什么场景会去使用?属性有哪些?
state 存储变量
getters state的计算属性
mutations 提交更新数据的方法
actions 和mutations差不多,他是提交mutations来修改数据,可以包括异步操作
modules 模块化vuex
使用场景:用户的个人信息、购物车模块、订单模块
19.vue的双向数据绑定原理是什么?
就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新,用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定。
我们都知道 Vue 是数据双向绑定的框架,双向绑定由三个重要部分构成
- 数据层(Model):应用的数据及业务逻辑
- 视图层(View):应用的展示效果,各类UI组件
- 业务逻辑层(ViewModel):框架封装的核心,它负责将数据与视图关联起来。它的主要职责就是,数据变化后更新视图,视图变化后更新数据。
而上面的这个分层的架构方案,可以用一个专业术语进行称呼:MVVM这里的控制层的核心功能便是 “数据双向绑定” 。
vue2中采用 defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式。而vue3采用proxy重写了响应式系统,因为proxy可以对整个对象进行监听,所以不需要深度遍历。Proxy的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性了。Proxy直接可以劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式目的
通过数据劫持和发布订阅者模式来实现,同时利用Object.defineProperty()劫持各个属性的setter和getter,
在数据发生改变的时候发布消息给订阅者,触发对应的监听回调渲染视图,也就是说数据和视图时同步的,数据发生改变,视图跟着发生改变,视图改变,数据也会发生改变。
第一步:需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter
第二步:compile模板解析指令,把模板中的变量替换成数据,然后初始化渲染视图,同时把每个指令对应的节点绑定上更新函数,添加订阅者,如果数据变化,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间的通信桥梁,
作用:
1.在自身实例化的时候忘订阅器内添加自己
2.自身要有一个update()方法
3.等待属性变动时,调用自身的update方法,触发compile这种的回调
第四步:MVVM作为数据绑定的入口,整合了observer、compile和watcher三者,通过observer来监听自己的数据变化,通过compile解析模板指令,最后利用watcher把observer和compile联系起来,最终达到数据更新视图更新,视图更新数据更新的效果
20.了解diff算法和虚拟DOM吗?
虚拟DOM的产生原因:
对于一些复杂的页面或者目前使用非常多的单页应用来说,其 DOM 结构是非常复杂的,而且还需要不断地去修改 DOM 树,每次操作 DOM 渲染引擎都需要进行重排、重绘或者合成等操作,这些操作都是非常耗时的,这就带来了真正的性能问题。所以需要有一种方式来减少 JavaScript 对 DOM 的操作,这时候虚拟 DOM 就上场了。
虚拟DOM,描述元素和元素之间的关系,创建一个JS对象
如果组件内有响应的数据,数据发生改变的时候,render函数会生成一个新的虚拟DOM,这个新的虚拟DOM会和旧的虚拟DOM进行比对,找到需要修改的虚拟DOM内容,变化被应用到虚拟 DOM 上时,虚拟 DOM 并不急着去渲染页面,而仅仅是调整虚拟 DOM 的内部状态,这样操作虚拟 DOM 的代价就变得非常轻了。
在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。
createElement 创建 VNode 的过程,每个 VNode 有 children,children 每个元素也是一个VNode,这样就形成了一个虚拟树结构,用于描述真实的DOM树结构
diff算法就是虚拟DOM的比对时用的,返回一个patch对象,这个对象的作用就是存储两个节点不同的地方,最后用patch里记录的信息进行更新真实DOM
其有两个特点:
-
比较只会在同层级进行, 不会跨层级比较
-
在diff比较的过程中,循环从两边向中间比较
21.vue和jquery的区别是什么?
1.原理不同:vue就是数据绑定;jq是先获取dom再处理
2.着重点不同:vue是数据驱动,jq是着重于页面
3.操作不同
4.未来发展不同
22.vuex的响应式处理。
vuex是vue的状态管理工具
vue中可以直接触发methods中的方法,vuex是不可以的。为了处理异步,当触发事件的时候,会通过dispatch来访问actions中的方法,actions中的commit会触发mutations中的方法从而修改state里的值,通过getter把数据更新到视图
Vue.use(vuex),调用install方法,通过applyMixin(vue)在任意组件内执行this.$store就可以访问到store对象。
vuex的state是响应式的,借助的就是vue的data,把state存到vue实例组件的data中
23.vue中遍历全局的方法有哪些?
1.普通遍历,对象.forEach()
arr.forEach(function(item,index,arr){
console.log(item,index)
})
2.对元素统一操作 对象.map()
var newarr = arr.map(function(item){
return item+1
})
3.查找符合条件的元素 对象.filter()
arr.filter(function(item){
if(item > 2){
return false
}else{
return true
}
})
4.查询符合条件的元素,返回索引 对象.findindex()
arr.finindex(function(item){
if(item>1){
return true
}else{
return false
}
})
对象.evening() 遇到不符合的对象会停止
对象.some() 找到符合条件的元素就停止
24.如何搭建脚手架?
下载:node cnpm webpack vue-cli
创建项目 1.找到对应的文件,然后利用node指令创建(cmd)
2.vue init webpack xxxx
3.回车项目描述
4.作者回车
5.选择vue build
6.回车
7.输入n
8.不按照yarn
9.输入npm run dev
25.如何封装一个组件?
1.使用Vue.extend()创建一个组件
2.使用Vue.components()方法注册组件
3.如果子组件需要数据,可以在props中接收定义
4.子组件修改好数据,要把数据传递给父组件,可以用emit()方法
原则:
把功能拆开
尽量让组件原子化,一个组件做一件事情
容器组件管数据,展示组件管视图
26.封装一个可复用的组件,需要满足什么条件?
1.低耦合,组件之间的依赖越小越好
2.最好从父级传入信息,不要在公共组件中请求数据
3.传入的数据要进行校验
4.处理事件的方法写在父组件中
27.vue的过滤器怎么使用?
vue的特性,用来对文本进行格式化处理
使用它的两个地方,一个是插值表达式,一个是v-bind
分类:
1.全局过滤器
Vue.filter(‘add’,function(v){
return v < 10 ? ‘0’ + v : v
})
2.本地过滤器:和methods同级
filter:{
add:function(v){
return v < 10 ? ‘0’ + v : v
}
}
28.vue中如何做强制刷新?
1.localtion.reload()
2.this.$router.go(0)
3.provide和inject
29.vue3和vue2有哪些区别?
相同功能的代码编写在一块,而不像options API那样,各个功能的代码混成一块
1.双向数据绑定的原理不同
2.是否支持碎片
3.API不同
4.定义数据变量方法不同
5.生命周期的不同
6.传值不同
7.指令和插槽不同
8.main.js不同
30.vue的性能优化怎么做?
1.编码优化
不要把所有数据都放在data中
v-for时给每个元素绑定事件用事件代理
keep-alive缓存组件
尽可能拆分组件,提高复用性、维护性
key值要保证唯一
合理使用路由懒加载,异步组件
数据持久化存储的使用尽量用防抖、节流优化
2.加载优化
按需加载
内容懒加载
图片懒加载
3.用户体验
骨架屏
4.SEO优化
预渲染
服务端渲染ssr
5.打包优化
CDN形式加载第三方模块
多线程打包
抽离公共文件
6.缓存和压缩
客户端缓存、服务端缓存
服务端Gzip压缩
31.白屏优化怎么去做?
白屏时间是指浏览器从响应用户输入网址地址,到浏览器开始显示内容的时间。
DNS解析耗时+服务端耗时+网络传输耗时
- DNS预解析:让浏览器在用户点击链接之前预先解析链接中包含的域名,以提前获取该域名对应的 IP 地址。对那些确实会在页面导航中用到的域名进行预解析,过度使用可能会增加网络负载,因为浏览器会在后台解析大量的域名。
<link rel="dns-prefetch" href="//example.com"> //解析 example.com 的域名
- 使用服务端渲染(SSR):服务端拿出数据来,直接填充到html或者说DOM元素中,然后把填充好数据的DOM直接返回给前端。
如果服务端一开始不做任何操作,前端加载完页面,通过元素加载完成触发一个事件,请求服务端的数据,将请求回来的数据填充到DOM(这里是浏览器操作),就是客户端渲染或者浏览器端渲染。所以在整个页面加载完后再请求数据,两次请求,有阻塞关系的,所以速度稍微慢了点。
服务端是搜索引擎友好的,因为直接爬虫就能拿到数据,而不是拿到数据请求的js代码,更容易被搜索引擎抓取 - 使用 HTTP2:HTTP2 相比于 HTTP1,解析速度更快;支持多路复用,多个请求可以共用一个 TCP 连接;提供了首部压缩功能;支持服务器推送,服务器可以在发送 HTML 页面时,主动推送其他资源,而不用等到浏览器解析到相应位置发请求再响应。
31.首屏优化该如何去做?
首屏时间是指浏览器从响应用户输入网络地址,到首屏内容渲染完成的时间。
- 使用路由懒加载
把不同路由对应的组件分割成不同的代码块,等路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加
const routes = [
{
path: '/about',
name: 'About',
component: () => import('./views/About.vue') // 懒加载About组件
},
];
// 创建路由实例
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
- 非首屏组件使用异步组件:是一种延迟加载组件的方式,即只有在需要使用该组件时才会进行加载。
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue') //返回一个promise
)
可以看浏览器里面的network中的请求数,或者在打包的时候可以发现异步组件是单独打包的。
-
首屏不重要的组件延迟加载
-
静态资源放在CDN上
-
减少首屏上JS、CSS等资源文件的大小
-
尽量减少DOM的数量和层级
-
使用精灵图请求
-
做一些loading
-
开启Gzip压缩
-
图片懒加载
<body>
<!-- 使用占位符代替图片,避免页面跳动 -->
<div class="placeholder"></div>
<!-- 图片懒加载 -->
<img class="lazy" data-src="image.jpg" alt="Lazy Loaded Image">
<!-- 可以添加更多的图片 -->
<script>
// 页面加载完成后执行以下操作
document.addEventListener("DOMContentLoaded", function() {
// 获取所有带有 'lazy' 类的图片元素
var lazyImages = document.querySelectorAll('.lazy');
// 图片懒加载函数
var lazyLoad = function() {
// 对于每个懒加载图片
lazyImages.forEach(function(img) {
// 检查图片是否进入了可见区域
if (img.getBoundingClientRect().top < window.innerHeight && img.dataset.src) {
// 如果图片进入了可见区域且 data-src 属性存在,则加载图片
img.src = img.dataset.src; // 将 data-src 的值赋给 src 属性
img.removeAttribute('data-src'); // 移除 data-src 属性,避免重复加载
}
});
};
// 页面加载时执行一次图片懒加载
lazyLoad();
// 监听滚动事件,当页面滚动时执行图片懒加载
window.addEventListener('scroll', lazyLoad);
});
</script>
</body>
32.vue3的性能为什么比vue2好?
1.diff算法的优化
2.静态提升
3.事件侦听缓存
33.vue3为什么使用proxy?
1.proxy可以代理整个对象,defineproperty只代理对象上的某个属性
2.proxy对代理对象的监听更加丰富
3.proxy代理对象会生成新的对象,不会修改被代理对象本身
4.proxy补兼容ie浏览器
34.如何封装可复用的组件?
组件的理解:
可以重复使用的vue实例,独一无二的组件名称
可以抽离单独的公共模块
提高代码的复用率
分为定义属性、定义事件、slote插槽的使用
- 定义属性:在 Vue 3 中,定义组件的属性(也称为props)是组件接收外部数据的基础。Props 是组件的公共接口的一部分,使得组件可以在不同的上下文中复用,同时保持内部数据的独立和封装性。
作用:一旦定义了 props,你可以在组件的任何地方(如计算属性、方法或模板)直接使用它们。Vue 的响应性系统会自动跟踪 props 的变化,当传入的 prop 值改变时,组件将重新渲染。
<template>
<div>
<h1>{{ title }}</h1>
<p>Likes: {{ likes }}</p>
</div>
</template>
<script setup>
const props = defineProps({
title: String,
likes: Number
});
</script>
- 定义事件:通过事件,子组件可以将信息传递给父组件,或者触发在父组件中定义的行为。这个机制持了组件间的解耦和数据流的单向性
<template>
<button @click="handleClick">Click me</button>
</template>
<script setup>
const emit = defineEmits(['update', 'click']);
function handleClick() {
emit('click'); // 通知父组件按钮被点击
emit('update', 'new value'); // 可以传递数据给父组件
}
</script>
<!-- ParentComponent.vue -->
<template>
<ChildComponent @update="handleUpdate" @click="handleClick" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
function handleUpdate(data) {
console.log('Received data:', data);
}
function handleClick() {
console.log('Button was clicked');
}
</script>
35.你是如何规划项目文件的?
public
图标、index.html、img
src
api
assets
components
按分类再次划分子目录
plugins
router
static
styles
utils
views
App.vue
main.js
package.json
vue.config.js
36.是否使用过nuxt.js?
是基于vue的应用框架,关注的是渲染,可以开发服务端渲染应用的配置
SSR:服务端渲染
好处:
SSR生成的是有内容的HTML页面,有利于搜索引擎的搜索
优化了首屏加载时间
SEO:优化搜索引擎
SPA的应用不利于搜索引擎SEO的操作
37.SEO如何优化?
1.SSR
2.预渲染 prerender-spa-plugin
38.插槽slot
使用场景:通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理,如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情,通过slot插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用,比如布局组件、表格列、下拉选、弹框显示内容等
- 默认插槽
- 具名插槽
<div class="container">
<header>
<slot name="header"></slot> //具名插槽
</header>
<main>
<slot></slot> //默认插槽
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
//使用方法:
<BaseLayout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</BaseLayout>
- 作用域插槽
在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。可以像对组件传递 props 那样,向一个插槽的出口上传递参数。
<!-- <MyComponent> 的模板 -->
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
<!-- 第一种使用方法 -->
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
<!-- 第二种使用方法:解构 -->
<MyComponent v-slot="{ text, count }">
{{ text }} {{ count }}
</MyComponent>
39.ref和reactive有什么区别?
- ref可以同时处理基本数据类型和对象,使用ref时,需要通过.value属性来访问或修改其内部值 ,在模板中使用时无需使用.value(Vue 模板会自动解引用)。
- reactive只能处理对象和数组,直接操作对象的属性,不需要.value。更深层次的响应式转换,即嵌套对象自动成为响应式。但不处理嵌套对象的根更换,如果你替换了reactive对象的整个属性值(例如,将一个嵌套对象整体替换为另一个对象),新对象不会自动变为响应式,除非它也是通过reactive创建的。这需要额外注意,以避免丢失响应性。
这是因为二者响应式数据实现的方式不同:
- ref是通过一个中间对象RefImpl持有数据,并通过重写它的set和get方法实现数据劫持的,本质上依旧是通过Object.defineProperty ,对RefImpl的value属性进行劫持。
- reactive则是通过Proxy进行劫持的。Proxy无法对基本数据类型进行操作,进而导致reactive在面对基本数据类型时的束手无策。
40. NextTick是什么
等待下一次 DOM 更新刷新的工具方法。当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。
nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。
<template>
<button id="counter" @click="increment">{{ count }}</button>
</template>
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
async function increment() {
count.value++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
</script>
五、Echarts
1.echarts有用过吗?常用的组件有哪些?
title标题组件 show text link
toolbox工具栏 导出图片 数据视图 切换 缩放 show orient feature
tooltip tigger 触发类型
markPoint标注点
markLine图标的标线
六、Uni-APP
1.uni-app有没有做过分包?
优化小程序的下载和启动速度
小程序启动默认下载主包并启动页面,当用户进入分包时,才会下载对应的分包,下载完进行展示
七、Weabpack
1.webpack打包和不打包的区别?
1.运行效率
2.对基础的支持不够
2.webpack是怎么打包的,babel是做什么的?
webpack会把js css image看作一个模块,用import/require引入
找到入口文件,通过入口文件找到关联的依赖文件,把他们打包到一起
把bundle文件,拆分成多个小的文件,异步按需加载所需要的文件
如果一个被多个文件引用,打包时只会生成一个文件
如果引用的文件没有调用,不会打包,如果引入的变量和方法没有调用也不会打包
对于多个入口文件,加入引入了相同的代码,可以用插件把他抽离到公共文件中
八、Git
1.git如何合并、拉取代码?
拉取代码 git pull ‘仓库地址’
查看状态 git sattus
提交到本地缓存区 git add .
提交本地仓库 git commit -m ‘修改描述’
提交到远程仓库 git push ‘仓库地址’ master
创建分支 git branch -b xxx
合并分支 git merge ‘合并分支的名字’
2.git如何解决冲突问题?
1.两个分支中修改了同一个文件
2.两个分支中修改了同一个文件的名字
1.解决:当前分支上,直接修改代码 add commit
2.解决:在本地当前分支上,修改冲突代码 add commit push
九、HR
1.你的离职原因是什么?
疫情 社保 薪资问题 个人发展 技术提升 家庭因素
2.工作到现在,项目中遇到最难的问题是什么?怎么解决的?
1.不要回答,没有问题
2.不要说一些常见的简单的问题,比如:数据请求不过来、渲染页面时出现了问题、跳转路由不会…
首先应该时自行去查找资料寻求解决办法,然后再去请教同时或者组长
3.你的优势在哪里?
1.尽量不要暴露自己的缺点
2.不要过度美化自己
4.如何协同工作?
1.开发前会开个会议,最后形成一个开发文档
2.利用工具保证项目的正常进度,规范化