目录
46:clientWidth offsetWidth, scrollWidth区别
47事件event.pageX ,event.pageY,clientX,clienX
63 webpack 中的插件plugin与加载器loader的区别
8. 什么是 Bom? Bom 是浏览器对象。有哪些常用的 Bom 属性呢?
Doctype 作用?严格模式与混杂模式如何区分?它们有何意义?
4. sessionStore有哪些方法(3个)localStorage也具有这些方法
5. 使用axios的get请求请求/list.php 请求参数是 {name:"mumu",age:18}
1、当使用 Promise 嵌套时,内部 Promise 中的错误不会自动冒泡到外部 Promise
仅供参考
1、js的数据类型
(1)基础数据类型、值类型
数据类型 | typeof值 | 举例 |
String字符串 | string | “abc” |
Number数组 | number | 123,-12.5 |
Boolean布尔 | boolean | true,false |
undefined未定义 | undefined | undefined |
Symbol符号 | symbol | symbol |
null 空 | object | null |
(2)复杂数据类型,引用数据类型
数据类型 | typeof | 举例 |
Array数组 | object | |
Object对象 | object | {} |
function函数class类 | function | function(){} |
Map图 | object | new Map([[]]) |
Set集合 | object | new Set([]) |
weakMap, weakSet | object | |
Date,Math, | object |
2、值类型与引用类型的区别
值类型 key和value都存储在栈中。
引用类型栈中存内存地址,真正值存储在堆中。
// 值类型
var a = 15;
var b = a;
b = 20;
// b与相互不影响
// 引用类型
var a = {age:15}
var b = a; // 把a的内存地址赋给b,a与b的值都指向同一堆中的内存地址
b.age= 18;
// 这个时候a.age 的值也是18
一个引用类型堆中的值可以被多个变量引用,修改其中一个会影响到其它。
3、什么是闭包,闭包的应用场景,闭包的缺点
闭包就是函数嵌套函数,函数作为参数被传入,作为返回值被返回。
闭包作用:
- 形成局部作用域,
- 在函数外部可以访问函数内部的局部变量
闭包的缺点:
被闭包引用的变量不会被js垃圾回收机制销毁,会常驻内存,使用不当容易造成内存崩溃
let num = 15;
function test() {
let num2 = 9;
console.log(num); //15
function handsNum() {
return num2;
}
return handsNum;
}
let result = test();
console.log(result());//9
4、什么是原型,什么是原型链
简述:
每个构造函数(class)都有一个显示的原型prototype
每个实例实例对象都有个隐私原型__proto__
实例的隐式原型__proto__等于其构造函数的显示原型protype
当查找一个对象的属性或方法时先在实例上查找,找不这沿着__proto__向上逐级查找
我们把__proto__的__proto__形成的链条关系成为原型链
作用:1 原型链实现了js继承,2.原型可以给构造函数创建的实例添加公用方法
详解 :
在 JavaScript 中,每个函数都有一个特殊的属性叫做
prototype
,它是一个对象。当使用new
操作符创建一个实例时,该实例会继承其构造函数的原型,即该函数的prototype
属性。原型对象是一个普通的对象,它拥有自己的属性和方法。当调用一个对象的属性或方法时,JavaScript 引擎会先在该对象本身查找,如果找不到,就会沿着原型链向上查找,直到找到该属性或方法为止。
原型链就是由多个原型对象连接而成的链状结构。一个对象的原型指向它构造函数的原型,构造函数的原型也可能指向另一个原型对象,这样就形成了原型链。当在一个对象上查找属性或方法时,JavaScript 引擎会沿着整条原型链依次查找,直到找到该属性或方法为止。
需要注意的是,Object.prototype 是所有对象的最顶层原型。它指向 null,因此沿着原型链最终会到达 null,这就是 JavaScript 的原型链终点。
5、JS如何实现继承
- class使用extends关键字实现继承
- 通过原型链实现继承
function B(){
A.call(this) //实现构造函继承
}
B.protoytpe = Object.create(A.prototype) //实现原型继承
B.prototype.constructor= B //修正构造函数
6、Call,apply,bind的区别
在JavaScript中,call
、apply
和bind
是Function
对象自带的三个方法,这三个方法的主要作用是改变函数中的this
指向
相同点:
- 都是用来改变函数的this对象的指向的;
- 第一个参数都是this要指向的对象,也就是想指定的上下文
- apply 、 call 、bind 三者都可以利用后续参数传参
不同点:
- 指向的区别:三者的第一个参数都是this的指向,但是在使用时,call和apply只是临时改变一次this的属性,并立即执行;而bind方法在使用时会返回一个绑定this指向(永久改变)的新函数,调用时才会执行。
- 传参的区别:call方法接受的是一个参数列表,apply方法接受到是一个包含多个参数的数组或类数组。bind方法接受的也是一个参数列表,但可以多次传参。而call和apply都是一次性传入参数。
apply参数用的是数组形式
bind
是返回对应函数,便于稍后调用;apply
、call
则是立即调用 。
7、new关键字做了什么?
1、创建一个空对象,开辟内存空间,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
call执行构造函数并传入空对象作为this
3、指定空对象的构造函数
function A(){}
var obj = {};
a.call(obj);
obj.prototype.constructor = A
8、深拷贝
深拷贝是指,拷贝对象的具体内容,深拷贝在计算机中开辟一块新的内存地址用于存放复制的对象。源数据改变不会影响复制的数据
转字符串
var obj2 = JSON.parse(JSON.stringify(obj1))
递归
function deeCopy(obj){
if(typeof obj==="object"&&obj!==null){
var result;
if(obj.instanceof Array){
result = [];
for(var i=0;i<obj.length;i++){
result[i] = deepCopy(obj[i])
}
}else{
result = {};
for(var k in obj){
result[k] = deepCopy(obj[k])
}
}
return result;
}else {
return obj;
}
}
9、浅拷贝
浅拷贝只复制一层对象的属性,并不会进行递归复制,而在浅拷贝之后,源对象与目标对象的引用已经不是同一块内存空间。
- Object.assign(A,B)
- for in遍历拷贝
- {...A}对象扩展
//对象的浅拷贝
let obj1 = {name: '张三', age: 22};
let obj2 = Object.assign({}, obj1);
obj2.age = 23;
console.log(obj1.age); // 22
console.log(obj2.age); // 23
//数组的浅拷贝
let arr1 = [1, 2, [3, 4]];
let arr2 = arr1.slice();
arr2[2][0] = 5;
console.log(arr1[2][0]); // 5
console.log(arr2[2][0]); // 5
深拷贝和浅拷贝的区别
浅拷贝只是复制了对象本身及其第一层属性的值,而深拷贝则是递归地复制了对象及其所有嵌套的子对象。
在浅拷贝时,原对象和新对象中的某些属性可能会共享同一个值;在深拷贝时,原对象和新对象完全独立。因此,在修改新对象中的某些属性时,不会对原对象造成影响。
浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,
使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。
浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。
10. js检测对象类型
- typeof :可以使用
typeof
操作符来检测 JavaScript 中的基本数据类型,引用类型除function都返回 objecttypeof 123; // "number" typeof "hello"; // "string" typeof true; // "boolean" typeof undefined; // "undefined" typeof null; // "object"
- instanceof :使用
instanceof
运算符可以判断对象是否是Object
类型,或者数组是否是Array
类型,是某个函数的实例,在原型链有该构造函返回turelet obj = {name: '张三', age: 22}; console.log(obj instanceof Object); // true let arr = [1, 2, 3]; console.log(arr instanceof Array); // true
- 最准确:Object.prototype.toString.call()
使用
Object.prototype.toString.call()
方法可以返回对象的内部[[Class]]
属性,从而判断其类型,更加准确可靠。let obj = {name: '张三', age: 22}; console.log(Object.prototype.toString.call(obj)); // "[object Object]" let arr = [1, 2, 3]; console.log(Object.prototype.toString.call(arr)); // "[object Array]"
11. 事件流
冒泡流:事件由最具体的元素响应然后组件冒泡到最不具体的元素(html)
捕获流:从最不具体的元素捕获事件
开启捕获 addEventListenter第三个参数 true
阻止事件冒泡:e.stopPropagation()
12. 事件代理
$(父元素).on(事件名,子选择器,响应函数)
把事件注册到多个元素共有的父元素上,通过事件的冒泡机制 响应事件
作用:动态添加的元素也可以响应事件
事件代理(Event Delegation),又称为事件委托,是一种常用的 JavaScript 事件绑定技巧。即把需要在子元素上触发的事件(如
click
、keydown
等)委托给其父元素,在父元素上进行监听,并通过事件冒泡机制来处理子元素的事件响应。事件代理的优点是:
- 减少对每个子元素的事件绑定,有效地提高了性能。
- 动态添加的子元素,也能够自动地继承父元素的事件响应。
事件代理的实现方式是利用事件冒泡机制,将需要绑定事件的子元素的事件委托给共同的父元素,例如下面的代码:
<div id="parent">
<p>子元素1</p>
<p>子元素2</p>
<p>子元素3</p>
<p>子元素4</p>
</div>
如果我们需要在每个子元素上绑定 click
事件,则可以使用事件代理,将 click
事件绑定到父元素上,如下所示:
let parent = document.querySelector('#parent');
parent.addEventListener('click', function(event) {
let target = event.target; // 获取当前点击的子元素
if (target.tagName === 'P') { // 只处理子元素 p 的点击事件
console.log(target.innerText);
}
});
上述代码中,通过在父元素 parent
上绑定 click
事件,并通过 event.target
属性获取当前点击的子元素。然后根据目标元素的标签名 tagName
来判断是否为需要处理的子元素,而其他的子元素则不做任何处理。
总之,事件代理是一种常用的性能优化技巧,可以减少 DOM 操作,提高了 JavaScript 的执行效率。
13. 数组去重
- set去重
var arr2 = [...new Set(arr1)]
- filter过滤
var arr2 = arr1.filter((item,index)=>arr1.indexOf(item)===inmmdex)
- indexOf
//利用数组的 indexOf 方法对数组进行遍历,并通过判断元素是否已经存在于新数组中来达到去重的目的,代码如下
const arr = [1, 2, 3, 1, 2];
const uniqueArr = [];
arr.forEach(item => {
if (uniqueArr.indexOf(item) === -1) {
uniqueArr.push(item);
}
});
console.log(uniqueArr); // [1, 2, 3]
- includes
//类似于 indexOf 方法,也可以使用 includes 方法来判断元素是否已经存在于新数组中,代码如下
const arr = [1, 2, 3, 1, 2];
const uniqueArr = [];
arr.forEach(item => {
if (!uniqueArr.includes(item)) {
uniqueArr.push(item);
}
});
console.log(uniqueArr); // [1, 2, 3]
14. 异步和同步
- 同步是按顺序执行,单线程,会阻塞代码
- 异步多线程执行,非阻塞式执行代码
异步方法:回调函数,Promise,订阅发布模式,事件响应,aync和awiat
详解:
//同步函数是指在调用该函数时,程序会一直等待该函数完成后再继续往下执行。这种函数通常可以立即返回结果,因为程序会一直等待它完成所有的任务再继续运行。同步函数的执行顺序是可控的、有序的,但是会阻塞主线程的执行,可能造成页面无响应的情况。 //异步函数则是在调用函数时不会阻塞主线程,而是立即返回一个 promise 或回调函数。在异步函数内部,程序会开启一个新的线程或者在事件循环中注册一个事件处理函数来执行相应的任务。当任务完成后,该线程或事件处理函数将会通知主线程将结果返回给相应的回调函数或 promise 的 resolve 函数中。由于异步函数的执行顺序是不可控的、无序的,所以它不会阻塞主线程的执行,页面依然可以响应用户交互。 //在实际编程中,我们经常需要使用异步函数来处理一些 I/O 操作或网络请求,以保证程序的性能和用户体验。常见的异步函数包括 setTimeout、Promise、async/await 等。同时,当我们需要按照一定的顺序执行多个异步任务时,通常需要使用 Promise.all 或 async/await 等方式来确保异步任务执行的结果是有序的。 //与异步相对应,同步操作可以保证程序的执行顺序,但也容易导致阻塞和性能问题。例如,在页面加载时,如果使用同步的方式获取资源,会导致页面长时间无响应,影响用户体验。因此,在实际编程中,我们需要根据具体情况选择合适的同步和异步操作,以达到最好的性能和用户体验。
15. 手写ajax
ajax核心是通过XMLHttpRequest(xhr) 与服务器异步交换数据,实现前端刷新更新视图
var xhr = new XMLHttpRequest();
// 创建一个xhr对象
xhr.open("get","url地址")
//打开连接get方式
xhr.send(data)
// 发送数据到服务器
xhr.onreadystatechange = function(){
// 监听xhr的变化
if(xhr.reaystate===4){ //状态4成功
if(xhr.status===200){ //响应码200
// xhr.responseText 获取的数据
}
}
}
15.1、Promise
Promise 实现异步操作,解决回调函数层级过多形成的回调地狱问题
一个 Promise 实例代表了一个异步操作被封装后返回的结果。Promise 有三种状态:
pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。当 Promise 的状态从pending
转移到fulfilled
或rejected
时,将会执行相应的回调函数。
//利用 Promise 可以更方便地处理异步操作的结果,而不需要使用回调函数或事件监听等方式。例如,我们可以使用 Promise 封装一个网络请求,取得数据并在页面上渲染,如下所示:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// 使用取得的数据渲染页面
})
.catch(error => {
// 处理错误
});
Promise resolve与reject状态发送改变不能更改
var p = new Promise((resolve,reject)=>{
var num = Math.random();
if(num>0.5){
setTimeout(()=>resolve(num),2000)
}else{
reject(num)
}
})
p.then(res=>{
// 成功 得到resolve的值
})
.catch(err=>{
// 拒绝 得到reject的信息
})
Promise.all() 多个promise多完成才返回结果
Promise.race() 多个promise执行返回最快的一个
你在哪些地方用到promise
- 定义api请求接口
- 自定义jsonp用到
- 弹框插件等待用户确定则resolve
- 封装actions 用sync装饰 await实现异步
(等待,网络,等待用户确定的)(异步操作都会用到promise)
16. get与post区别
get 获取数据
post 新增,修改,删除
put 修改
delete 删除
get 有缓存的,可以收藏书签,方向,数据量2k
post 没有缓存,理论上没有大小限制
get
- get形式请求数据,参数会以明文的形式拼接在url地址的后面
- 请求的长度受到url地址格式的限制
- get请求可以被缓存,请求数据的效率上比post高
- get请求可被收藏为书签
- get方式安全性相对来说比较低
post
- post形式请求数据,参数会打包到请求对象中进行传递,不会显示在url地址中
- 一般来说请求长度不受限制
- post请求不能被缓存
- post请求不可被收藏为书签
- 安全性相对get来说较高,所以请求的数据涉及到安全性的情况下,使用Post方式
17. 什么是RESTful
RESTful是接口的设计风格
每一个URI地址都是一个资源
使用get获取,post新增,put修改,delete方法删除
18. 如何实现跨越
- jsonp
- 后端响应头允许
- 代理
无论哪种跨越都需要后端支持
19. jsonp原理
利用了script标签src没有同源限制。
后端返回的是方法名+()+数据格式 fun(data)
前端提前与定义这个方法就能获取数据
JSONP 最大的优点就是兼容性好,在老旧浏览器中也可以很好地实现跨域请求。但同时它也存在一些缺点,比如只支持 GET 请求、不安全(因为服务器可以注入任意代码执行)、不支持终止请求等。同时,随着跨域资源共享(CORS)的出现,JSONP 已经逐渐不被推荐使用。
20.书写jsonp
function jsonp(url,data){
return new Promise((resolve,reject)=>{
var funName = “callback”+Date.now()+Math.random();
var script = document.createElement("script");
src = url+"callback="+funName;
document.body.appendChild(script);
script.src = src;
window[funName]=function(data){
resolve(data);
document.body.removeChild(script)
}
script.onerror = function(err){reject(err)}
})
}
20. 什么是MVVM(前后端分离)
M:model 模式存储数据
V:view 显示内容(html)
VM viewModel视图模块 连接视图与model模型 ( )
当model数据发生变化时候通过VM可以监听变化更新视图
当view视图发变化时候通过VM可以监听变化自动更新model数据
20 .什么是MVC(前后不分离代码)
M 模型
v 视图
c 控制器
后端软件的设计思维:把视图V和模型M通过C控制实现分类
mvc(有利于SEO)
前端写好视图 交给后端 后端经过编程 生成 html 发送给浏览器(客户端)
mvvm(有利于开发))
后端写好接口,前端请求接口数据+定义视图 ,在浏览器(客户端)渲染html
21. Vue2 响应式原理,(双向绑定的原理)
简述:通过Object.defineProperty劫持对象的getter与setter
通过订阅与发布者模式结合
watcher观察者来连接视图与数据
当数据发生变化时候通知说要订阅该数据的订阅者更新
详解:Vue 2.x 中的响应式原理主要是利用了 Object.defineProperty 方法。Vue 在初始化数据时,会遍历每一个属性并使用 Object.defineProperty 方法将其转化为 getter/setter,并通过闭包引用相关依赖,在数据发生变化时通知依赖进行更新。
在 Vue 组件渲染过程中,组件会通过 Watcher 实例监听数据的变化,并在数据变化时重新调用组件渲染函数进行更新。Watcher 实例会收集当前渲染函数中用到的所有数据属性上的依赖,并在数据变化时触发这些依赖的更新。
具体来说,每个 Watcher 实例都会有一个对应的 Dep 实例,用于收集该 Watcher 所涉及到的所有数据属性的依赖。在初始化 Watcher 实例时,该实例会执行一次求值操作,以收集起始渲染函数中用到的所有数据属性的依赖,这些依赖会被 addDep 方法依次添加到 Watcher 的 Dep 实例中去。当数据变化时,会遍历该属性的所有依赖并触发它们的更新函数,从而重新渲染组件。
此外,Vue 还提供了 $watch 方法,可以让开发者手动监听数据的变化,而不需要在模板中使用。
22.Vue3响应式原理
简述:vue3响应原理:ES6新增的proxy代理实现的
Vue 3.x 的响应式原理相比于 Vue 2.x 做了一些重要的更新和优化,其中最重要的更新是采用了 Proxy 对象来代替 Object.defineProperty 方法。Proxy 对象是 ES6 新增的特性,可以通过拦截对象的各种操作(如读取属性、写入属性、调用方法等)来实现对该对象的监听和控制。
详解:Vue 3.x 中的响应式原理主要有以下几个部分组成:
- Reactive(响应式数据对象)
在 Vue 3.x 中,由于使用了 Proxy 对象,导致 Vue 将“可响应的数据对象”称为 Reactive(响应式数据对象),而不是像 Vue 2.x 中的“响应式对象”,Reactive 对象包含了被代理对象的所有属性,并在内部使用一个 Map 对象来存储每个属性的依赖关系。当属性发生变化时,依赖该属性的所有副作用会被触发。
- Refs(引用数据类型的响应式处理)
Refs 是 Vue 3.x 中的一个新特性,用于处理引用类型的响应式数据。在 Vue 2.x 中,只有基本数据类型和响应式对象才能被监听,而引用类型的数据则需要手动调用 $set 或 Vue.set 方法进行监听。而在 Vue 3.x 中,使用 Refs 可以直接监听到引用类型的数据。Refs 会返回一个带有 value 属性的对象,该 value 属性是一个 Reactive 对象,它可以被正常地使用和监听。
- Computed(计算属性)
Computed 是 Vue 3.x 中的一个特性,用于处理需根据依赖而自动更新的数据。与 Vue 2.x 不同,Vue 3.x 中的计算属性是 Lazy(惰性)的,在取值时才会计算,而且只要计算前所依赖的数据没有发生变化,就会利用缓存直接返回之前的计算结果,从而提高性能。Computed 的本质是一个响应式数据对象,当它所依赖的数据属性发生变化时,会通知相关副作用进行更新。
- Watch(手动监听数据变化)
Watch 是 Vue 3.x 中的一个特性,用于手动监听数据变化并执行相应的回调函数。与 Vue 2.x 不同,Vue 3.x 中的 Watch 可以直接在组件外部进行定义,无需将其绑定到模板中的变量上。Watch 可以监听到多个数据变化,当任何一个数据发生变化时,都会触发回调函数的执行。同时,Vue 3.x 中的 Watch 是支持异步执行的,可以设置 immediate 和 deep 两个参数来控制,从而更加灵活和高效。
vue3 与Vue2的区别
- 响应式原理不同:Object.defineProperty和Proxy
- 启动方式不同:
//vue2 new Vue({ store, router, render:h=>h(App) }).$mount("#app")
// vue3 createApp(App).use(store).use(router).mount("#app")
3.全局挂载方法
// vue2 Vue.prototype.$http = axios; // vue3 app.config.globalProperties.$http = axios;
4.Vue3 增加了setup 组合式api
23. v-if与v-show的区别
都可以隐藏节点
v-show通过css方式隐藏
v-if 直接移除dom节点
频繁切换显示与隐藏用v-show反之用v-if
注意事项:
由于 v-if 可以完全销毁并重建元素,所以它对于一些需要在显示或隐藏时执行副作用(例如计算属性、监听事件等)的场景非常有用。而 v-show 则不会销毁元素,因此不适合这些场景。
24. computed与watch区别
简述:computed有缓存,watch没有
computed从现有数据计算出新的数据,watch是监听数据变化执行handler回调函数
computed 多对一,watch一对多
详情:computed 和 watch 都是 Vue.js 中用于监听数据变化的常用方式,但它们在实现方式和使用场景上有所不同。
- 实现方式不同:
computed 是一种基于响应式依赖进行缓存的计算属性。它会根据一个或多个响应式数据(data 中的属性、props 中的属性、甚至其他 computed 属性)来动态计算得到一个值,并将该值缓存起来。只有当计算属性所依赖的响应式数据发生变化时,computed 才会重新计算,否则就直接从缓存中获取上一次的计算结果。因此,computed 适合用于需要根据其他数据计算得到的值,并且该值的结果会被反复调用或渲染的情况。
watch 则是一种基于数据监听的方式,用于监听指定的数据是否发生变化,然后执行相应的回调函数。watch 可以监听单个数据的变化,也可以同时监听多个数据的变化,还可以设置 deep 参数来深度监听对象或数组的变化。根据监听的数据类型和参数设置的不同,watch 可以用于执行异步操作、请求数据、更新组件等各种场景。
- 使用场景不同:
由于 computed 只有在所依赖的数据发生变化时才会重新计算,因此它可以避免不必要的计算,提高应用的性能。computed 适合用于根据其他数据计算得到复杂值的情况,例如过滤、排序、格式化、分页等。如果某个值需要被多个组件调用或渲染,而且它的计算规则是确定不变的,那么可以使用 computed 来处理。
watch 则更适合处理需要在数据变化时执行一些异步操作或副作用的情况。例如监听用户输入框中的内容变化,然后实时请求后端接口进行搜索并显示搜索结果。由于 watch 能够监听到所有数据的变化,因此可以比 computed 更加灵活和自由地处理数据变化的场景。
25 .生命周期
vue2:
- beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性。
- created:实例已经在内存中创建 OK,此时 data 和 methods 等属性已经创建 OK,但还没有开始编译模板。
- beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中。
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示给用户看了。
- beforeUpdate:响应式数据发生变化,试图更新渲染重新计算虚拟 DOM。
- updated:虚拟 DOM 重新渲染视图之后,会执行此函数,当然这个时候数据可能还是不一样的,因为 updated 只是表示前后两次数据渲染完毕而已。
- beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyed:Vue 实例销毁后执行的回调函数。在这一步,对于所有的东西,Vue 实例指示的所有东西都已经解除了绑定,事件监听器也已经被移除了。
vue3:
1、setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
2、onBeforeMount() : 组件挂载到节点上之前执行的函数;
3、onMounted() : 组件挂载完成后执行的函数;
4、onBeforeUpdate(): 组件更新之前执行的函数;
5、onUpdated(): 组件更新完成之后执行的函数;
6、onBeforeUnmount(): 组件卸载之前执行的函数;
7、onUnmounted(): 组件卸载完成后执行的函数;
option选项api(vue2) :创建前后,挂载前后,更新前后,销毁前后
setup中(vue3):没有创建前后
-------------------------------------------------------------------------------------
created(vue2) 有this,发起ajax请求,监听事件,定时器
setup(vue3) 没有this ,发起ajax请求,监听事件,定时器(setup里面封装了beforeCreate,created周期)
------------------------------------------------------------------------------------
mounted 可以操做dom
---------------------------------------------------------------------------------------
beforeDestroyed(vue2) ||onUnmount(vue3) 移除定时器,移除事件监听
26.导航守卫
简述:
组件内部,路由独享,全局守卫
实现权限管理:
在路由配置定义meta权限
通过全局beforeEach实现路由守卫
beforeEach3个参数 to要去的页面,from从哪个页面来,next下一操作
详解:
导航守卫是 Vue Router 中提供的一种路由钩子,用于在路由切换过程中拦截导航或者修改跳转行为。
在 Vue Router 中,有三种导航守卫:
- 全局导航守卫:主要用来拦截所有路由跳转,包括进入该应用、跳转到某个路由、离开某个路由以及在该应用内部跳转。
- 路由独享守卫:可以在路由配置上直接定义 beforeEnter 守卫。它和全局前置守卫的方法类似,但又和它有所区别,它只会在当前路由的跳转中起作用。
- 组件内的守卫:这些守卫只会在组件激活时调用,而且它们都是属于某个组件的。
具体来说,导航守卫主要包括以下四个函数:
beforeEach(to, from, next)
:全局前置守卫。当一个导航触发时,全局前置守卫按照创建顺序调用。可以用next()
调用下一个钩子,也可以用next(false)
或next('/')
中断当前导航并进行跳转。beforeResolve(to, from, next)
:路由独享守卫。和全局前置守卫类似,但是只在当前路由匹配时调用。afterEach(to, from)
:全局后置钩子,当一个导航完成时调用。可以在此更新页面的 title 等信息。beforeRouteEnter(to, from, next)
和beforeRouteUpdate(to, from, next)
:组件内的守卫。这些守卫中可以访问组件实例this
,但在守卫被调用时,实例尚未创建。因此,在这些守卫中不能访问组件的数据和方法。可以用next()
异步获取组件实例,并进行回调,或者用next(false)
或next(error)
中断导航。Vue Router 的导航守卫功能非常强大,可以用于登录验证、权限控制等需要在路由切换过程中进行拦截或者修改跳转行为的应用场景。
27 . $router,和$route区别
$route是当前页面路由信息,params,query,path,fullpath
$router是整个路由实例 通常带方法push,replace, back, forward ,go
总的来说,$router 是用来操作路由的工具,而 $route 是用来获取路由信息的工具。在 Vue.js 应用中,我们通常需要使用这两个对象来完成路由跳转、页面渲染等操作。
28 . 路由传参,子路由
简述:
路由参数params进行传参 查询参数query进行传参 hash传参 meta传参
子路由:在路由配置添加children
动态添加路由 ,router有个AddRoutes方法
详解:
路由传参:
路由参数进行传参需要在定义路由时指定动态路由参数:
const routes = [ { path: '/user/:id', component: User, }, ];
这里的
:id
就是动态路由参数,可以在组件中通过$route.params.id
来获取该参数的值。查询参数进行传参,则需要在跳转路由时通过
query
传递参数:<router-link :to="{ path: '/user', query: { id: 123 } }">User</router-link>
上述代码会跳转到路径为
/user?id=123
的路由,并且可以在组件中通过$route.query.id
来获取该参数的值。
子路由:
此外,在 Vue.js 中还可以使用子路由来实现嵌套路由的功能,即将多个页面组织在一起,形成父子级关系。在定义路由时,可以使用
children
字段来定义子路由const router = new VueRouter({ routes: [ { path: '/', component: Home, children: [ { path: 'user', component: User, }, { path: 'product', component: Product, }, ], }, ], });
上述代码中,
Home
组件是父路由组件,User
和Product
组件是子路由组件。访问路径为/user
或/product
时会渲染对应的组件,并将其渲染到Home
组件中。
动态添加路由:
1、获取当前的路由实例,可以使用以下代码来获取:
const router = new VueRouter(); // 实例化路由
2、使用
router.addRoutes()
方法动态添加新的路由配置。该方法传入一个包含路由配置对象的数组,例如:router.addRoutes([ { path: '/new-route', component: NewRoute, }, ]);
3、使用
router.push()
或者router.replace()
方法进行路由跳转,例如:// 跳转到新的动态路由 router.push('/new-route');
通过上述步骤,就能够实现在运行时动态添加路由。
需要注意的是,当使用
addRoutes()
方法添加新的路由配置后,新的路由配置只能够通过push()
或者replace()
方法进行跳转。如果直接访问新增的路由路径,会导致 404 错误。
29. 你封装过哪些组件(哪些插件)?
日期格式,防抖节流,瀑布流,弹框,提示,翻译。
设计好组件的data(尽量不使用data)
定义好props(多使用)
设计好方法
设计插槽和查找作用域
注意组件的传参
哪些工具
解析时间,格式时间,获取查询参数
清空数组,url转换(qs)html转文本
对象 合并,常见dom操作,防抖,节流
深拷贝
封装组件的步骤:
- 分析组件的功能和数据结构,确定组件需要接受哪些参数(props),以及输出哪些事件(emit)。
- 编写组件的模板和样式。
- 定义组件的 JavaScript 逻辑,包括组件的属性和方法,以及钩子函数和生命周期函数等。
- 验证组件的正确性,并进行必要的修正和优化。
- 将组件注册到 Vue.js 中,可以使用
Vue.component()
方法进行全局注册,也可以在单文件组件中使用export default
进行局部注册。- 在需要使用该组件处引入组件,可以使用
import
或者在<script>
中使用require
。需要注意的是,在封装组件时,应该遵循单一职责原则,即为每个组件定义唯一的职责(或功能)。同时,应该保持组件的可复用性和可维护性,以提高开发效率和代码质量。
30. vuex 哪些组件
存储数据的state
修改数据唯一方式 mutations
异步操作的Actions
模块:module
映射方法:getters
31. 什么是vuex
vuex是vue的全局状态管理器,当state更新,引用state组件的视图会响应式更新。
本质上就是一个没有template的vue文件
通过vuex可以更好集中管理数据,多个组件共享数据
32. vuex和全局变量区别
都可以实现多个组件全局共享数据
vuex的数据是响应式,会自动更新视图
vuex的修改必须是mutations,更加利于调试
vuex还支持异步操作,方便vuex插件便于调试
33. vuex mutations和action区别
mutations用来改变数据(唯一方式)
actions执行异常操作
34. 哪些数据你都存储在vuex
当多个组件都需要使用的数据通常都存储在vuex里面
app信息: 菜单栏信息,设备,运行模式
用户相关:token ,用户信息,权限信息,第三方登录信息
用户访问历史,操作日志
设置信息:主题,自定义菜单,标签
权限相关:路由,固定路由,菜单路由,
同时通过actions执行请求,mutations更新
35 axios封装哪些常用功能
简述:
基础配置:baseURL,timeout,headers
拦截响应与请求:加载提示,token和其他请求头,错误响应操作,添加或移除权限
详解:
- 封装通用的网络请求方法
可以封装一个通用的网络请求方法,该方法可以接受请求 URL、请求类型、请求参数等参数,并返回 Promise 对象。例如,以下是一个简单的 axios 请求方法的封装示例:
import axios from 'axios'; export function request(url, method, data) { return new Promise((resolve, reject) => { axios({ url, method, data, }) .then(response => { resolve(response.data); }) .catch(error => { reject(error); }); }); }
- 统一处理请求错误
可以通过拦截 axios 请求的
response
,统一处理请求错误。例如,以下是一个在响应出错时统一弹出错误提示的示例:axios.interceptors.response.use( response => { return response; }, error => { alert(error.message || 'Oops! Something went wrong.'); return Promise.reject(error); } );
- 封装请求取消功能
为了避免在网络较慢时重复发送请求,我们可以封装请求取消的功能。例如,以下是一个可以在旧请求未完成时,取消新请求的示例:
const CancelToken = axios.CancelToken; let cancel; axios .get('https://api.example.com/some/url', { cancelToken: new CancelToken(function executor(c) { // 执行器函数接收一个取消函数作为参数 cancel = c; }), }) .catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 处理错误 } }); //取消请求 cancel();
- 添加请求头信息
可以封装一个函数来添加请求头信息,该函数接受需要添加的请求头参数,并在每个请求中添加对应的请求头信息。例如,以下是一个在请求时添加 token 请求头的示例:
import axios from 'axios'; const token = 'YOUR_TOKEN_HERE'; export function request(url, method, data) { return new Promise((resolve, reject) => { axios({ url, method, data, headers: { Authorization: `Bearer ${token}`, }, }) .then(response => { resolve(response.data); }) .catch(error => { reject(error); }); }); }
通过对 axios 进行常用功能的封装,可以使我们的代码更加简洁、可读、易维护,从而提高开发效率和代码质量。
36. 组件传参
父传子 props,Props 是父组件向子组件传递数据的最基本方式,父组件通过在子组件中定义 props 参数,再将需要传递的值作为属性传入子组件中,从而实现组件之间的通信。子组件可以通过
this.props
来访问父组件传递过来的参数子传父$emit 事件
非父子关系:
ref获取引用
依赖注入:provide inject
vuex
vue2(选项options):event Bus传参;$parent父对象,$Chilren子对象 $root根节点
37 nextTick作用(全局方法)
简述:数据更新,等待视图更新执行的回调函数(dom操作常用)
详解:
nextTick
是 Vue.js 的全局方法之一,它的作用是在事件循环结束后执行延迟回调函数。也就是说,当我们需要在下一次 DOM 更新完成后执行某些操作时,可以使用nextTick
方法。在 Vue.js 中,数据的更新是异步执行的,Vue 会在下一个事件循环中更新 DOM,并且在更新完成后触发
nextTick
回调函数。这样做的好处是可以避免不必要的 DOM 操作和重复计算,从而提高渲染效率,同时也保证了更新后的 DOM 可以被立即获取到。例如,以下是一个使用
nextTick
方法实现在视图更新后获取 DOM 元素的示例:// 在 template 中定义一个 ref <template> <div ref="myDiv">{{ message }}</div> </template> // 在组件内部使用 nextTick 获取 DOM 元素 mounted() { this.$nextTick(() => { const myDiv = this.$refs.myDiv; console.log(myDiv.textContent); // 输出当前 message 的值 }); }
需要注意的是,由于
nextTick
是在下一个事件循环中执行的,因此如果在同一个 tick 中多次调用nextTick
,那么回调函数的执行顺序是不确定的。如果需要按照顺序执行多个回调函数,可以使用 Promise 或者 async/await 来实现。//以下是通过 Promise 实现按顺序执行多个回调函数的示例: function firstTask() { return new Promise(resolve => { Vue.nextTick(() => { console.log('first task'); resolve(); }); }); } function secondTask() { return new Promise(resolve => { Vue.nextTick(() => { console.log('second task'); resolve(); }); }); } firstTask().then(() => { return secondTask(); }).then(() => { console.log('all tasks are done'); });
总之,
nextTick
方法是 Vue.js 中非常有用的一个方法,它可以保证数据更新后 DOM 已经更新,从而便于我们进行后续的操作。
38. vue2 有没有遇到数据更新视图不更新?
简述:
vue2 通过数组下标更新数据,视图不会更新
Vue.set(数据名,下标,数值)强制视图更新
this.$set(数组名,key,value)
vue2 数组的双向绑定监听是通过重写数组的原型方法 pop,push ..
详解:
通常情况下,Vue.js 会检测到数据的变化并及时更新视图,但是在以下情况下,可能会出现数据更新但视图不更新的情况:
- 直接替换数组元素或对象属性的方式更新数据,例如
this.myArray[0] = newValue
或this.myObject[prop] = newValue
,这种方式不会触发 Vue.js 的响应式机制。- 更新嵌套数据时,需要使用深度监听(deep watch)来确保数据的变化被检测到。
- 在使用自定义组件时,如果忘记对父组件传递的属性进行双向绑定(v-model),则无法将子组件中改变的值同步到父组件中。
针对这些情况,我们可以采取如下措施来解决数据更新但视图不更新的问题:
- 对于数组或对象的更新,应该使用 Vue.js 提供的方法进行更新,例如
this.$set(this.myArray, 0, newValue)
或this.$set(this.myObject, prop, newValue)
,这种方式能够触发 Vue.js 的响应式机制。- 对于嵌套数据,可以使用
watch
对其进行深度监听,或者使用Vue.set
方法强制触发 Vue.js 的响应式机制。- 在使用自定义组件时,应该对父组件传递的属性进行双向绑定(v-model),确保子组件中改变的值能够同步到父组件中。
39. 在vue-for循环中为什么时候key
key为了让vue虚拟dom节点查找与更新更加优化(优化dom的渲染)
具体来说,
key
属性可以帮助 Vue.js 识别出每个元素与之前的元素的差异,从而避免不必要的 DOM 操作和重复计算,提高渲染效率,同时也保证了更新后的 DOM 可以被立即获取到。vdom 优化diff算法:
- 如果key一致就不向下查找
- 如果tag不一致,子节点不查找
- 只在同级做比较
n3次方优化到n1次方
40. 什么是虚拟dom,优点
虚拟dom就是用js对象的形式去模拟dom节点
<ul> <li><a href="baidu.com">百度</a></li> <li>纯文本</li> </ul>
{ tag:"ul", children:[ {tag:"li",children:[{tag:"a",props:{href:"baidu.com"},chilren:["百度"]}]}, {tag:"li",children:["纯文本"]} ] }
操作dom 重绘,回流,消耗页面性能
虚拟dom,操作js比操作dom要快,通过diff算法查找之前的vdom与现状vdom最小区别,通过patch更新到dom视图中
虚拟dom可以映射html,虚拟dom可以转换为ios的组件,转换为android组件(react-native)
前端通过编写js实现原生ios/Android 的效果(实现跨平台开发)
41 箭头函数与普通函数的区别?
简述:
- 箭头函数this指向函数的上一层作用域的this
- 箭头函数不能作为构造函数,没有constructor
详解:
箭头函数与普通函数(function)在语法和作用上有着一些区别:
- 1、语法:
普通函数的语法类似于:
function functionName(parameters) { // 要执行的代码 }
箭头函数使用
=>
符号来定义,基本语法如下:(参数) => { // 要执行的代码 }
当只有一个参数时,可以省略括号:
parameter => { // 要执行的代码 }
当函数体只有一条语句时,可以省略花括号和
return
关键字:parameter => statement
- 2、this的指向:
在箭头函数之前,每个新函数都需要根据它是如何被调用来定义 this 值,而箭头函数的 this 值是由它所在的作用域决定的,与普通函数有很大的区别。箭头函数中的 this 值指向了函数定义时所在的作用域中的 this,而不是调用箭头函数时所在的作用域中的 this,这样就避免了普通函数中 this 指向不明的问题。
- 3、arguments 对象的处理:
在箭头函数中,无法访问
arguments
对象,需要使用 rest parameters 来处理函数的参数。总体来说,箭头函数简化了函数的语法,并且解决了普通函数在 this 值指向上的问题,但对于一些特殊的应用场景(如需要访问 arguments 对象时)则需要使用普通函数。
42. this指向问题
函数的this在函数执行的确定的,在 JavaScript 中,this 指向当前执行代码的上下文对象。由于 JavaScript 的灵活性和动态性质,this 的指向可能会受到调用方式、函数定义方式、作用域等多种因素的影响。理解 this 的指向问题对于写出高质量的 JavaScript 代码至关重要。
- 构造函数 new 函数名() this执行的 new出来对象的实例
- 箭头函数的this上一层作用域的this
- 方法中的 this 指向的是调用它的对象
- 事件响应函数的this指向调用者
- setTimout setInterval 里面this指向window,全局作用域、普通函数以及定时器中的 this 指向全局对象 window。
- call,apply,bind 响应的函数this指向第一个参数
43. 变量提升
t = 10
function fn(test) {
if(8>10){var t=30}
alert (t)
}
fn(t)
t = 10;
function tn(test){
var t = undefined;
if(8>10){t=30}
alert(t)
}
在js var 声明的变量会被提升到当前作用域的最前面 赋值为undefined
所有的函数也会被默认提升到当前作用域的最前面:函数可以先调用后声明
43. let 与var、const的区别
简述:
let 、const不会变量提升,var声明的变量会变量提升
let、const 不能重复声明 var可重复声明
const 声明必须赋值,值类型的值不能修改
详解:
var
声明的变量作用域是函数级别作用域,而let
和const
声明的变量作用域是块级别作用域。块级作用域指的是由一对花括号{}
包裹起来的代码块。
var
声明的变量在作用域内部和外部都可以访问到,而let
和const
声明的变量只能在块级作用域内部访问。
var
声明的变量会发生变量提升,let
和const
声明的变量不会进行变量提升。变量提升指的是将变量声明提升到作用域顶部的过程。
const
声明的变量必须进行初始化,并且不能重新赋值,而var
和let
声明的变量可以先声明再赋值,并且可以被重新赋值。
const
声明的变量是常量,它的值不能被修改,而var
和let
声明的变量是可变的。总之,相比于
var
关键字,let
和const
更加安全、合理、规范,并且能够减少代码中的问题所以建议尽量使用let
和const
。
let a = 10;
x let a = 25;
var b = 10;
√ var b = 20;
44 小程序
小程序单位:rpx 750rpx宽度等于一屏的宽度
更新数据 this.setData()
全局数据 globalData:{xxx:"yyy"}
var app = getApp()
app.globalData.xxx
限制:页面栈5层,tab栏2-5,源文件包2M总20M,setData 1M 本地存储1次1M最大100M
跳转:navigateBack 移除一层 页面栈 navigate 添加一个层页面栈 redirect 替换一层栈,switchTab请空页面栈
分包与与加载:app.json 配置一下
npm: npm init 初始化 ,a'z'z'z'z'z'z'z'z'z'z'z'z'z'z'z'z'z'zazzzzzzzzzzzzzzzzzz i安装 , 构造npm
生命周期:小程序生命周期,页面的生命周,组件的生命周期
小程序:启动onLaunch 前台onShow 后台 onHide 错误 onError
页面:加载onLoad(options)onReady加载完毕 onShow显示 onHide后台运行 分享,下拉,滚动,触底,底部栏切换
组件的:attach 挂载 detach 卸载 move移动 。。。。
登录流程:wx.login 获取code 发给后端 后端通过appid appscecret 找微信服务器换取 sessionid与openid
获取用户信息:必须是用户才能使用api getUserInfo getUserProfile
云开发:了解没有用过
自定义底部栏,顶部导航。 配置
45:获取浏览器宽高
在 JavaScript 中,我们可以使用
window.innerWidth
和window.innerHeight
属性来获取浏览器窗口的宽度和高度,这两个属性返回的是可视区域的大小,并且不包括浏览器工具栏、滚动条等非页面区域的尺寸。let width = window.innerWidth; let height = window.innerHeight; console.log(`浏览器宽度:${width}, 浏览器高度:${height}`);
此外,
document.documentElement.clientWidth
和document.documentElement.clientHeight
属性也可以获取到浏览器窗口的宽度和高度,它们返回的是文档元素<html>
的大小,也就是网页内容区域的大小。let width = document.documentElement.clientWidth; let height = document.documentElement.clientHeight; console.log(`浏览器宽度:${width}, 浏览器高度:${height}`); //在某些情况下,上述方法可能无法准确获取到浏览器窗口的大小,例如当浏览器处于全屏状态或者缩放状态时。如果需要获取更加精确的窗口大小信息,可以考虑使用第三方库或者通过其他方式实现。
46:clientWidth offsetWidth, scrollWidth区别
- clientWidth:
clientWidth
属性是指元素内部的可视区域的宽度,不包括滚动条和任何边框(padding)。- offsetWith :
offsetWidth
属性指元素在屏幕上占据的总宽度,包括元素自身的宽度(border)、元素的内边距(padding)和垂直滚动条的宽度(如果存在的话)- scrollwidth :内容+滚动区域的宽:
scrollWidth
属性指元素内容的实际宽度,包括被隐藏的部分,如果元素的内容没有超出它自身的范围,那么 scrollWidth 等于 clientWidth,否则 scrollWidth 大于 clientWidth
47 scrolleft offsetLeft
scrollLeft
和offsetLeft
都是用于获取元素在水平方向上的偏移量,但是它们的含义和用法有所不同。
- scrollLeft 滚动条滚动的距离
scrollLeft
是一个元素的属性,用于获取或设置元素左侧隐藏内容的像素大小。当一个元素拥有横向滚动条时,这个属性就会派上用场。通过设置或读取这个属性,可以控制元素内部横向滚动条所在位置的偏移量,以此来展示隐藏的内容。对于从左到右的语言,scrollLeft
的值越大,表明内容被滚动得越远,而对于从右到左的语言,scrollLeft
的值越小则意味着内容滚动得越远
- offsetLeft 当前元素与父元素(position非static)距离
offsetLeft
是另一个元素属性,用于获取元素相对于其父级元素(offsetParent)左侧边缘的像素距离。换句话说,offsetLeft
属性返回一个元素相对于其父级元素的左侧距离,其中包括元素自身的左外边框(border)、左内边距(padding)和垂直滚动条的宽度(如果存在的话)。
47事件event.pageX ,event.pageY,clientX,clienX
事件点击的位置
具体来说:
event.pageX
返回鼠标事件的横向(水平)坐标值,相对于文档左边缘。event.pageY
返回鼠标事件的纵向(垂直)坐标值,相对于文档顶部。event.clientX
返回鼠标事件的横向(水平)坐标值,相对于当前窗口(视口)的左边缘。event.clientY
返回鼠标事件的纵向(垂直)坐标值,相对于当前窗口(视口)的上边缘。需要注意的是,
event.pageX
和event.pageY
的坐标值相对于整个文档而言,而event.clientX
和event.clientY
的坐标值相对于当前窗口视口而言。此外,浏览器窗口的滚动会影响event.clientX
和event.clientY
的值,而不会影响event.pageX
和event.pageY
的值。
48 手机端事件
touchstart
:手指触摸屏幕时触发。touchmove
:手指在屏幕上滑动时持续触发。touchend
:手指离开屏幕时触发。touchcancel
:当系统停止跟踪触摸时触发。事件参数 event.currrentTouches[0]
49 获取元素的边界
1、getBoundingClientRect()
方法:这个方法返回一个 DOMRect 对象,该对象包含了当前元素的边界信息,包括元素的 top、right、bottom 和 left 属性以及其高度、宽度等。可以通过以下代码获取元素的边界:2、使用 offset 相关属性:元素的 offset 相关属性包括const element = document.getElementById('my-element'); const rect = element.getBoundingClientRect(); console.log(rect);
offsetTop
、offsetLeft
、offsetWidth
和offsetHeight
,它们可以用来获取元素的相对位置和大小信息。例如:3、使用 client 相关属性:元素的 client 相关属性包括const element = document.getElementById('my-element'); const x = element.offsetLeft; const y = element.offsetTop; const w = element.offsetWidth; const h = element.offsetHeight; console.log(x, y, w, h);
clientTop
、clientLeft
、clientWidth
和clientHeight
,它们可以用来获取元素的客户区域大小信息。例如:需要注意的是,const element = document.getElementById('my-element'); const w = element.clientWidth; const h = element.clientHeight; console.log(w, h);
getBoundingClientRect()
方法比较灵活,可以对非常复杂的元素进行边界计算,而使用 offset 和 client 相关属性则需要注意边界的定义和计算方式。可以做dom操作,距离计算(监听滚动,图片懒加载,瀑布流)
50 JQuery相关
jq源码阅读过:读过,没有全懂
无new操作怎么实现
var vm = new Vue() //通过new关键字实现实例化
$("div")
jquery的工厂函数与原型链重新指向
function jQuery (selector){
return new JQuery.prototype.init(selector)
}
JQuery.fn = jQuery.prototype;
JQuery.fn.init = function(selector){
return xxxx;
}
jQuery.fn.init.prototype = JQuery.prototype;
window.$=JQuery;
js对象转jq
用$函数包裹 $(js对象)
jq转js
下标获取,get方法获取
$(selector)[0]
$(selector).get(0)
window.onload 与 jQuery.ready() || $() 区别
onload 后面覆盖前面;$(function(){}) 可以执行多重
onload等于页面加载才执行;入口函数dom加载就执行
页面渲染过程
下载html 生成dom树
下载css生成css树
css树与dom树合并为渲染树
开始渲染页面,遇到js会有阻塞,
JQuery.常用方法
show hide fadeIn fadeOut toggle slideIn slideDown显示与隐藏
css() html() attr() props()text()val 属性和值
addClass toggleClass hasClass removeClass()类
animation() stop() 动画停止动画
$ajax,$.get $,post() .load() ajax方式
find chilren parent parents next sibings 查找节点
51.html5标签有哪些
article aisde section header footer main hgroup figure video audio
52. 行内标签 ,块标签,行内块
行内 a span i u code b strong sub sup em del
块 div,p,h1-h6,ul li dl dt dd ol
行内块 img iframe
表格:table thead tbody th tr td
表单 input textarea button
53.行内与块区别
行内元素:行内元素只占据它对应标签所包含内容的大小,不会产生独占一行的效果。例如
span
、a
、em
等都是行内元素,可以使用CSS设置宽高,但是对于宽高属性不起作用,可以设margin块级元素:块级元素通常独占一行,具有明显的宽度和高度属性,并且可以容纳其他块级元素和行内元素。例如
div
、p
、h1
等都是块级元素 , 不可以设margin行内块级元素:行内块级元素具有同时拥有行内元素和块级元素的特点。它可以设置宽高属性,并且可以容纳其他行内元素。例如
img
、input
、button
等都是行内块级元素。可以设置margin都可以设置paddding
行内元素浮动自动化为块
54. 如何写个三角形
宽高为0 三边透明,一边显示
.san{
width:0;
hegiht:0;
display:block;
border:10px solid transparent;"
border-left-color:red;
}
55. css哪些属性可以继承
字体相关属性:如
font-family
、font-size
、font-style
、font-weight
等。文本相关属性:如
text-align
、text-indent
、text-decoration
、line-height
等。颜色相关属性:如
color
、background-color
、border-color
等。列表相关属性:如
list-style-type
、list-style-image
、list-style-position
等。表格相关属性:如
border-collapse
、border-spacing
、caption-side
等。需要注意的是,并不是所有属性都可以继承,如
margin
、padding
、border
和定位属性(如position
、top
、left
等)等都不能被继承。同时也有一些属性可以通过设置inherit
来显式地让其子元素继承父元素的属性值。
a不继承,h标签页不继承粗细
56. css如何垂直居中
行内 :文字一行,高等于行高
行内块:一行,高等于行高,veticle-aligin:middle
块:
flex 弹性布局 aligin-items:center;
display:table-cell
定位的方式
使用 display 和 table-cell 属性,将父元素设置为 display: table;,子元素设置为 display: table-cell; vertical-align: middle;,这样就可以实现垂直居中了。[1]
使用 flex 布局,将父元素设置为 display: flex;,并设置 justify-content: center; align-items:center;,这样即可实现水平和垂直居中。[1]
利用 transform 和 translate 属性,将子元素相对于自身的高度偏移 50%,然后在使用相反数偏移自身一半的高度就可以实现垂直居中。[2]
使用 calc 函数,结合 absolute 和负外边距实现。首先给父元素相对定位,然后使用 top 和 bottom 同时设置为 0,再使用 calc 计算子元素的高度并赋值给 height,最后使用 margin 和负外边距实现垂直居中。[1]
css如何垂直水平居中
使用 display 和 table-cell 属性,将父元素设置为 display: table;,子元素设置为 display: table-cell; vertical-align: middle; text-align: center;,这样即可实现水平和垂直居中。[1]
使用绝对定位 position,将子元素的左上角分别调整至居中位置,此时需要将 margin 设置为负宽高的一半。我们可以这样设置子元素的样式:position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); 即可实现水平和垂直居中。[2]
使用 flex 布局,将父元素设置为 display: flex;,并设置 justify-content: center; align-items:center;,这样即可实现水平和垂直居中。[1]
使用 grid 布局,将父元素设置为 display: grid;,并使用 justify-content: center; align-items:center; 实现水平和垂直居中。[3]
57. css权重
简述:
选择器 * 0.5 标签 1 类10 id 100 import 最高
行内>内嵌>外链(离元素节点越近权限越高)
详解 :
常见的选择器类型(按优先级从低到高):
- 元素选择器和伪元素选择器:1
- 类选择器、属性选择器、伪类选择器:10
- id选择器:100
- 内联样式:1000
div p .my-class { color: red; } //权重60,因为它包含了一个元素选择器(1),一个类选择器(10),以及一个后代选择器(1)。另外,当多个选择器都适用于同一个元素时,CSS权重可以帮助浏览器确定应该使用哪个样式规则。
58. 0.5px线手机端
height:1px;
transform:scaleY(0.5)
59 BFC
块级格式上下文:BFC是CSS中一种渲染模式,用于决定块级元素如何放置和布局。BFC的形成需要满足一定条件,包括浮动元素、块级盒子等
解决:父元素高度坍塌,margin加倍问题,实现自适应宽布局(bfc之间不会重叠)
当一个元素生成了BFC,其内部的元素会按照一定规则进行布局,包括浮动元素、清除浮动、块级格式化上下文相关的属性等。例如,当一个元素形成BFC时,它的所有子元素垂直方向上会排列,并且块级盒子在垂直方向上的间距不会有重叠。同时,一个元素形成的BFC也会影响其兄弟元素的布局。
常见的创建BFC的方式包括:
- 浮动(float)元素:float属性值不为none的元素
- 绝对定位元素:position属性值为absolute或fixed的元素
- display属性值为inline-block、table-cell、table-caption和flex的元素
- overflow属性值不为visible的元素
60 px pt rem em rpx
px(Pixel): 像素,是计算机屏幕上最小的点,是固定的长度单位。在Web开发中,通常使用px作为CSS样式表中文本、图片等大小的单位。px大小不会因设备屏幕大小而改变,这也导致有时候无法在不同终端上保持元素的一定比例关系。
pt ios单位 通常 2倍屏(iphone 5,6,7,8) 1pt = 2px 3倍屏1pt = 3px(iphonex,iphone6 plus)
em:相对长度单位,是相对于当前元素设定的字体大小来计算的。例如,如果像素大小是16px,那么1em就是16px。em的大小会随着字体大小的变化而变化。
rem(Root EM): 相对于根元素(<html>)设定的字体大小来计算的相对长度单位,与em类似,但是它的大小不会随着父元素的大小变化而改变。rem受到浏览器默认字体大小的影响,因此在CSS样式表中,一般会将<html>的font-size设置为一个固定值。
rpx(Responsive pixel): 是一种相对于屏幕宽度的单位,在微信小程序中使用。屏幕宽度750rpx,相当于CSS样式表中的100%。在开发微信小程序时,rpx可以用来实现屏幕适配。
vh 100vh = 1个屏幕的高
vw 100vw = 1个屏幕的宽
61 清除浮动
解决的是父组件高度坍塌的问题
- 在父元素中定义 overflow 属性为 hidden 或 auto,开启bfc
- 设置高度
- 最后一子元素 宽100%,清除both
.clearfix:after{
content:"";
display:block;
clear:both;
visibility:hidden;
}
// visibility隐藏不会继承 opacity可以被继承 都占用原来的位置
//display:none 不占用原来的位置
4. 额外标签法:即在浮动元素下添加一个空的块级元素并设置 clear 属性,从而达到清除浮动的目的。例如:
<div class="parent">
<div class="float-left">浮动元素</div>
<div style="clear: both;"></div>
</div>
62 定位区别
- relative 相对定位 正常文档流 相对于自身的位置
- absolute 绝对订定位 脱离文档流 相对于开启了position非static最近的父辈元素 ,
- fixed 固定定位 脱离文档流 相对于浏览器可是窗口(不随滚动条滚动)
63 webpack 中的插件plugin与加载器loader的区别
简述:
loader用来处理非js文件的,插件在webpack运行生命周期中进行压缩,清理等工作
详解 :
loader用于处理文件的加载和转换。它将源文件作为输入,对源文件进行编译、转换等一系列操作,并将其转化成Webpack能够处理的模块。常见的Loader有babel-loader(用于将ES6转换成ES5)、style-loader 和 css-loader(用于处理CSS文件)等。Loader的主要目的是让Webpack能够处理不同类型的文件,以便于在编译时打包这些文件。
而Plugin则更多的用于完成其他的自动化任务,如代码压缩、文件合并、复制等等。Plugin可以在Webpack运行的各个阶段注入钩子(hook),以实现对Webpack生命周期的精细控制。Plugin也可以访问到Webpack的输出结果,从而可以对输出文件再做一些处理。常见的Plugin有uglifyjs-webpack-plugin(用于压缩JavaScript代码)、html-webpack-plugin(用于生成HTML文件)、clean-webpack-plugin(用于清理目录)等等。
总之,loader和plugin都是Webpack构建过程中必不可少的组件,它们分别负责不同的功能。loader负责把各种文件转换成模块,plugin则负责完成其他各种广泛的任务。了解这两个概念对于掌握Webpack的工作方式以及实现复杂的自动化构建任务都是非常有帮助的
64 webpack有哪些优化方式
区分生产模式和开发模式
压缩优化css,压缩优化js,压缩图片,base64小文件减少请求
使用hash命名控制二次缓存
异步加载,懒加载,按需加载,摇树treeshake
Webpack是一个强大的打包工具,但是由于项目越来越庞大,Webpack的打包时间也会越来越长。为了提升Webpack的性能,我们可以采取以下一些优化方式:
使用多进程/多实例构建:使用 webpack-parallel-uglify-plugin、thread-loader 等插件可以开启多进程/多实例构建,加速打包时间。
使用Tree Shaking:Tree Shaking 是一个有助于减少代码体积的技术,可以去除 JavaScript 中没有使用过的代码。在Webpack中,Webpack 2+ 的版本默认开启了 Tree Shaking 功能。通过使用 ES2015 模块语法以及配置 UglifyJsPlugin,可以轻松地让Webpack进行Tree Shaking。
对常用库进行CDN加载:对于常用的库如 Vue、React等,在页面上直接引用其 CDN 而不是通过打包编译减少打包时间。
避免过多的模块打包:Webpack打包时针对每个模块都会进行一定的处理,因此如果模块过多,将会影响打包的速度。可以通过合并模块、移除冗余模块等措施来减少模块数量,从而提高打包速度。
使用DllPlugin和DllReferencePlugin预编译资源:DllPlugin和DllReferencePlugin这两个插件可以将第三方库和应用代码分开打包,从而加快构建速度。
使用缓存:在使用Webpack的过程中,可以使用缓存来避免重复编译,进而提升构建效率。可以使用 hard-source-webpack-plugin、cache-loader 等插件实现缓存功能。
合理使用Loader:Loader是Webpack中常用的优化工具之一,但是过多的Loader也会导致性能问题。因此,在使用Loader时应该尽量减少不必要的复杂度,避免 Loader 之间的冲突和重复。
64 webpack 切换环境,区分环境,跨平台传递参数
cross-env 插件跨平台传递参数,通过参数区分环境,切换环境。process.env这个参数
(axios的baseURL也是通过pross.evn区分环境的)
65不用vue脚手架自己能搭建项目吗
能,原理是用webpack 搭建,写配置文件
vue-loader 处理.vue
vue-template-compiler 编译模板
66 webpack核心概念
入口:entery 从那开始进入打包 index.js/main.js
出口:文件输入的位置 output
plugin:插件 ,在webpack运行生命周期过程中进行打包清理,压缩等额外操作
loader:加载器处理非.js 文件的
mode:模式production development
tip:vue.config.js 其实就是webpack.config.js的vue版本(大部分可通用)
7 + 前端面试
get 与post 请求
get缓存,post不缓存
get由于浏览器url现在只能2k,post没有理论上没有限制
get收藏书签与分享
get常用于向服务器获取数据,post常用于修改,增加删除数据
闭包
函数嵌套函数,函数作为参数被传入,函数作为返回值被返回
在函数外部修改函数内部的局部变量
创建局部作用域,避免变量的全局污染
如何解决异步
回调函数(出现回调地狱)
Promise
sync/await
生成器generator (函数前加个* yeild next)
订阅发布模式
如何实现继承
- extend 关键字 类实现继承
- 原型链继承
function Parent(){}
function Son(){
// 继承构造函数
Parent.call(this,参数)
}
// 继承原型
Son.prototype = Object.create(Parent.protype)
// 修正构造函数
Son.prototype.constructor= Son
事件流
冒泡流,先最具体元素到最不具体
捕获流,反之
addEventListener(事件类型,回调函数,是否捕获)
如何事件先冒泡后捕获
同一事件同时监听冒泡和捕获,分别对应处理函数
事件委托,代理
事件监听不直接放在触发的元素上面,而监听其共有的父元素上
通过事件冒泡和事件对象(event.target)判断具体元素
好处:可以对动态添加的子元素添加事件
图片懒加载与预加载
图片懒加载当用户不需要的时候先不加载图片
图片预加载:当网络有空闲先提前把可能需要的图片缓存到内存中
mouseover与mouserenter区别
over移入元或者子对象都会触发
enter移入本身才会触发
js的new操作符做了哪些事情
- 创建空的对象
- 通过call apply执行函数 并把空对象作为this传入
- 把空对象原型原型构造函数的prototype
提问:
1. 说一下 http 和 https
https 加密,http明文; https端口443 http端口80
2. tcp 三次握手,一句话概括
C代表客户端
S代表服务端
第一次握手:S 只可以确认 自己可以接受 C 发送的报文段
第 二次握手:C 可以确认 S 收到了自己发送的报文段,并且可以确认 自己可以接受 S 发 送的报文段
第三次握手:S 可以确认 C 收到了自己发送的报文段
3. ? TCP 和 UDP 的区别
1.)TCP 是面向连接的,udp 是无连接的即发送数据前不需要先建立链接。
2)TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失, 不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付。
3)TCP 只能是 1 对 1 的,UDP 支持 1 对 1,1 对多。
4. webscoket
http 协议不支持持久性连接。 webscoket 是持续的长链接
webScoket常用于聊天,消息主动推送
5. HTTP 请求的方式,HEAD 方式与OPTIONS
HEAD 只发送请求头,不发送请求体
options:允许客户端查看服务器的性能,比如说服务器支持的请求方式等等。
6. 一个图片 url 访问后直接下载怎样实现?
1. 域名解析ip
2. 发送请求头
3. 服务器应答响应头和响应体(图片内容)
4. 客户端渲染图片
7. web Quality(无障碍)
能够被残障人士使用的网站才能称得上一个易用的(易访问的)网站。
如:设置img的alt属性 图片提示(通过语音也知道图片是什么)
8. 什么是 Bom? Bom 是浏览器对象。有哪些常用的 Bom 属性呢?
location 地址信息
location.href 地址
location.reload() 刷新
location.replace() 跳转
history 历史记录
history.go() 跳转
history.back() 后退
history.foward()前进
window.navigator.userAgent 浏览器的标识
10. 说一下 http2.0
提升访问速度;允许多路复用;二进制分帧(更快乐,同时容量更大了,可分割更小了)
iframe 是什么?有什么缺点?
行内框架,在网页中可以嵌入另外一个网页
缺点:阻塞onload加载事件,不利seo(搜索引擎优化)(你的网站在百度可以被人更快搜索到)
解决方法:使用js动态添加iframe 设置src
Doctype 作用?严格模式与混杂模式如何区分?它们有何意义?
Doctype 告诉浏览器你什么模式渲染页面;严格按浏览器最新标准;混杂兼容渲染
Cookie 如何防范 XSS 攻击
xss攻击用户使用非法javascript与网站交互实现非法的目的
后端:httponly 防止js访问cookie ,secure让https才发生cookie
Cookie 和 session 的区别
cookie 浏览器与服务器交互的重要手段;cookie存储在浏览器端一小段数据
http请求与响应都会携带cookie;cookie可以被后端设置;也可以被前端设置
session是存储在服务器的当前用户的唯一标识符(安全)
通常以cookie形式存储sessionid 告诉前端当前用户的状态
一句话概括 RESTFUL
就是用 URL 定位资源,用 HTTP 描述操作
是前后端交互风格开发方式,URL 定位资源,http请求时候用put添加 delete删除 post修改 get获取 等方法与服务器交互
什么是ajax?
全称:异步的javascript和xml
核心:xhr:XMLHttpRequest
作用:无刷新更新页面的数据(视图)
原理: 客户端 通过xhr与服务器交换数据,客户端用javascript来操作dom渲染更新数据
什么是浏览器同源策略?
https://pan.baidu.com:8080/list?name=mumu&age=18#good
https 协议
pan子域名
baidu.com 域名
:8080 端口号
/list 路径 ?name=mumu&age=18 查询参数 #good 哈希值
同源策略(安全策略):客户端向服务器请求数据的时候,要求客户端请求的html与服务器请求的地址要
协议一致,域名一致,子域名一致,端口号一致。
什么是跨域,跨域的常见方式有哪些?
跨域是绕过浏览器的同源策略(跨域名)向服务器请求数据
- CORS 服务器响应头允许:Access-Control-Allow-Origin: *
- jsonp技术(天气案例)
- 代理请求
jsonp的原理是?
利用的script的src属性没有同源策略的限制
Vue中路由相关的组件是?
<router-view> 存放路由页面
<router-link> 切换路由
路由配置
{ path:"/about" ,
name:"about",
component:About
}
路由传参
{path:"/produce/:id",name:"produce",component:Produce}
{$route.params.id}
路由方法
$router.go
.back()
.forward前进
.push() 添加
replace()替换
-
$router 有哪些方法 (5个)
go(num)跳转
back()返回
forward()前进
push()添加历史记录(跳转)
replace()替换
2. 写一个produce带参数路由配置
{ name:"produce",path:"/produce/:id",component:Produce }
在页面中获取参数$route.params.id
3. 导航组件内部守卫
进入前,更新前,离开前
beforeRouteEnter(to,from,next)
beforeRouteUpdate()
beforeRouteLeave()
4. sessionStore有哪些方法(3个)localStorage也具有这些方法
setItem(key,value) 设置数据
getItem(key) 获取key数据
removeItem(key) 移除key数据
clear()清空
5. 使用axios的get请求请求/list.php 请求参数是 {name:"mumu",age:18}
并在控制台输出请求内容(2种get方法)
axios.get("list.php?name=mumu&age=18").then(res=>'').catch(err=>'')
axios.get("list.php",{params:{name:"mumu",age:18}}).then().catch()
axios({url:"/list.php",method:"get",params:{name:"mumu",age:18}}).then().catch()
axios({url:"/list.php?name=mumu&age=18",method:'get'}).then().catch()
6. 使用axios的post请求请求/login.php 请求参数是 {name:"mumu",age:18}
并在控制台输出请求内容(2种post方法)
axios.post("/login.php","name=mumu&age=18",{headers:{'Content-Type':'application/x-www-form-urlencoded'}}).then().catch()
根据接口要求
axios.post("/login.php",{name:"mumu",age:18},{headers:{'Content-Type':'application/json'}}).then().catch()
post默认就是json格式可以省略
axios.post("/login.php",{name:"mumu",age:18}).then().catch()
axios({
url:"login.php",
method:"post",
data:"name=mumu&age=18"},
headers:{"Content-Type":"application/x-www-form-urlencoded"})
.then().catch()
7. 什么是浏览器的同源策略?
客户端向服务器请求数据,
t'r要求客户端的网址与服务器是同源
协议,端口,子域名,域名一致
8. 跨域的方式有哪些?
jsonp
CORS 响应头允许(后端允许)
服务器代理
9. jsonp的原理有哪些?(script)
利用的script的src属性没有同源策略的限制
返回的数据调用函数+数据参数形式 fn({name:"mumu",age:"18"})
function fn( data){ console.log(data,'后端返回的数据data')}
10. 如何全局挂载axios
import axios from ‘axios’
axios.default.baseURl="/" 默认请求域名
axios.default.timeout=3000 请求超时配置
axios.dafault.headers.post["content-type"]="application/x-www-form-urlencoded"
const app = createApp(App)
app.config.globalProperties.$http = axios;
++面试遇到的问题:
1、当使用 Promise 嵌套时,内部 Promise 中的错误不会自动冒泡到外部 Promise
在 Promise 嵌套的情况下,内部 Promise 中的错误不会自动冒泡到外部 Promise。如果内部 Promise 中出现了错误,并且没有被 catch 捕获,那么外部 Promise 是无法感知到这个错误的。
为了让外部 Promise 能够感知内部 Promise 中的错误,你可以使用链式调用和 catch 方法来处理错误,确保错误能够正确地冒泡到外部
,举例来说,在下面的代码中,如果第二个 Promise 发生错误,它会被内部 catch 块捕获并通过 reject() 方法抛出。因此,外部 Promise 的 .catch() 方法会捕获这个错误:
function someAsyncFunction() {
return Promise.resolve()
.then(() => {
// 执行一些异步操作
})
.then(() => {
// 一个嵌套的 Promise,可能发生错误
return Promise.resolve()
.then(() => {
// 执行一些异步操作
})
.catch(error => {
// 在内部 Promise 中捕获错误并抛出
throw new Error('Something went wrong');
});
})
.then(() => {
// 继续执行其他异步操作
})
.catch(error => {
// 捕获内部 Promise 中的错误
console.error(error);
});
}
2、希望一个同步操作在异步操作完成后才执行
如果你希望一个同步操作在异步操作完成后才执行,可以将同步操作放在异步操作的回调函数中。具体来说,可以在异步操作中使用 Promise 来实现这一点,让同步操作作为 Promise 的 resolve 回调函数执行。
举个例子,在下面的代码中,我们使用一个异步操作 simulateAsyncOperation() 来模拟一个耗时的操作,并返回一个 Promise。当异步操作完成后,我们可以在其 resolve 回调函数中执行同步操作 logMessage():
function simulateAsyncOperation() {
return new Promise(resolve => {
setTimeout(() => {
resolve('Async operation completed');
}, 1000);
});
}
async function doSomething() {
const result = await simulateAsyncOperation();
console.log(result); // 'Async operation completed'
logMessage(); // 在异步操作完成后执行同步操作
}
function logMessage() {
console.log('Message logged');
}
doSomething();
3、前端怎么区分角色权限
1.动态路由 该方式的思路是:前端通过接口获取用户的角色信息,并根据角色信息生成对应的路由表。在路由跳转时,前端会根据当前用户的角色信息判断是否有访问该路由的权限。如果没有权限,则直接跳转到 401 页面或者弹出提示信息。这种方式需要后端配合提供角色信息接口。
2.按钮级别控制 该方式的思路是:前端通过接口获取用户的角色信息,并根据角色信息生成对应的按钮展示。在用户操作时,前端会根据当前用户的角色信息判断是否具有该按钮的权限。如果没有权限,则将该按钮禁用或者隐藏。这种方式需要后端配合提供角色信息接口。
3.前端路由拦截 该方式的思路是:前端在每个需要控制权限的路由组件中添加路由守卫,例如beforeEach() 或 afterEach();在路由跳转时,前端会根据当前用户信息判断是否具有访问该路由的权限。如果没有权限,则直接跳转到 401 页面或者弹出提示信息。这种方式需要在前端实现权限判断逻辑
4、vue2项目怎么升级成为vue3
将 Vue 2 项目升级为 Vue 3 项目,需要以下几个步骤:
1.升级 Vue CLI。Vue CLI 是用于为 Vue 项目提供开发环境的工具,我们需要确保使用最新版本的 Vue CLI
npm install -g @vue/cli
2.创建新的 Vue 3 项目。可以通过 Vue CLI 的 UI 界面或者命令行工具来创建新的 Vue 3 项目
vue create my-project
3.将旧项目中的代码逐一迁移到新项目中。Vue 3 的一些 API 和生命周期钩子和 Vue 2 是不同的,需要对已有的代码进行修改。具体的改动可参考 Vue 官方文档中关于迁移指南的说明:
- Vue 2 -> Vue 3 迁移指南:Vue.js - The Progressive JavaScript Framework | Vue.js
- Composition API 迁移指南: https://composition-api.vuejs.org/migration/
4.根据情况是否升级 Vue 插件。一些 Vue 插件不支持 Vue 3,需要根据具体的情况判断是否需要升级或替换。
综上所述,将 Vue 2 项目升级为 Vue 3 项目需要进行较大的代码改动,需要认真评估并计划好迁移工作,确保在升级过程中不会出现重大问题。
5、v-model有什么优点
v-model 是 Vue.js 中的一个指令,是表单元素(如 input、textarea、select)和数据的双向绑定。v-model 在开发中有以下优点:
方便快捷:使用 v-model 指令可以快速实现双向绑定,无需手动监听输入框的输入和输出。
减少重复代码:v-model 在底层封装了 input 或者 textarea 的长相和事件监听,使得开发者不需要为每个表单元素手动绑定事件和更新数据。
数据驱动视图:Vue.js 框架采用 MVVM 架构,即数据变化驱动视图更新,v-model 实现了数据和视图的双向绑定,不但可以很方便地更新数据,同时也能让数据的变化自动反映到页面上。
支持自定义组件:使用 .sync 修饰符,可支持自定义组件双向绑定数据。
总之,v-model 可以提高开发效率,减少重复代码,简化开发难度,体现出 Vue.js 的数据驱动视图的思想,非常适合在开发表单相关功能时使用。
v-bind是单向数据的绑定,v-model是双向数据的绑定
5.1 .sync修饰符
.sync 修饰符在 Vue.js 中用于父子组件之间双向绑定数据。它是一个语法糖,可以简化代码量。
在 Vue.js 2.0 版本之前,.sync 修饰符是 Vue.js 的一项特性,但在 2.0 版本中被移除了。不过,在实际应用中,.sync 修饰符还是有其适用之处。比如,在开发可复用的组件库时,.sync 修饰符可以方便地对父子组件之间的数据进行双向绑定。
使用 .sync 修饰符,我们可以在子组件中通过 $emit('update:XXX', value) 触发父组件中 v-bind 指令所绑定的变量(XXX)进行更新。同时,父组件也可以在子组件中通过参数传递的方式更新子组件中的数据,这样就实现了父子组件之间的双向绑定。
但需要注意的是,.sync 修饰符不推荐在复杂的组件层级中使用,因为这可能会造成不必要的混乱和困难。
6.watch的深度监听
Vue.js 中的
watch
选项用于在数据变化时执行异步或同步函数。默认情况下,watch
选项是浅层监听的,意思是如果你在监听一个对象的属性,它不会检测对象内嵌对象的变化。但是我们有时候需要对对象进行深度监听,这时可以使用选项中的deep
选项。
例如,我们有一个对象 person
,该对象含有一个嵌套的子对象 info
,我们想要监听 info
内部属性的变化,可以使用 deep
选项实现深度监听:
watch: {
'person.info': {
handler: function(newVal, oldVal) {
console.log('person.info changed');
},
deep: true
}
}
当 person.info
对象中的属性有变化时,将触发 handler
函数,并打印出相应的信息。
需要注意的是,使用 deep
选项进行深度监听可能会带来性能上的开销,因此应该根据具体情况进行取舍。
7、父组件生命周期的执行顺序
当父子组件在加载的时候,它们的执行顺序如下:
- 父组件
beforeCreate
- 父组件
created
- 父组件
beforeMount
- 子组件
beforeCreate
- 子组件
created
- 子组件
beforeMount
- 子组件
mounted
- 父组件
mounted
父组件在 mounted
钩子函数中发起 HTTP 请求来获取数据,并将结果赋值给本地的 data
属性。这意味着,在父组件的 mounted
钩子函数之后,子组件才进行挂载,这可能与我们预期的不同。
因此,如果需要在父组件中获取数据并将其传递给子组件,可以考虑将数据请求和处理逻辑放在父组件的 created
钩子函数内,这样可以保证在子组件挂载之前就已经完成了数据的准备工作。
总之,了解父子组件生命周期的执行顺序是非常重要的,可以帮助我们更好地理解 Vue.js 应用的工作原理,并避免一些常见但难以排查的问题。
8、vue3定时器在哪销毁
在 Vue 3 中,可以使用 onUnmounted
钩子函数来清除定时器。这个钩子函数会在组件卸载时自动调用。
9、常见状态码
HTTP 状态码是定义在 HTTP 协议中表示特定状态的数字代码。常见的状态码包括:
- 1xx(信息性状态码):服务器已经接收到请求,正在处理中。
- 2xx(成功状态码):表示请求被成功接收、理解和处理。
- 200 OK:请求已成功,返回对应的实体内容。
- 201 Created:表示请求已经被成功处理,并且在服务器上创建了一个新的资源。
- 204 No Content:表示请求已经被成功处理,但是服务器没有返回任何实体内容。
- 3xx (重定向状态码):表示需要客户端进行额外操作才能完成请求。
- 301 Moved Permanently:请求的资源已被永久移动到新 URI。
- 302 Found:请求的资源已被暂时移动到新的 URI。
- 304 Not Modified:请求的资源未修改,可以使用缓存副本。
- 4xx(客户端错误状态码):表示客户端发送的请求有错误。
- 400 Bad Request:请求语法错误或无法被服务器所理解。
- 401 Unauthorized:需要授权认证才能获得访问权限。
- 404 Not Found:未能找到请求的资源。
- 5xx(服务器错误状态码):表示服务器在处理请求的过程中发生了错误。
- 500 Internal Server Error:服务器内部错误,无法完成请求。
- 503 Service Unavailable:服务器暂时无法处理请求,通常是由于过载或维护等原因造成。
10、==和===的区别
==
和 ===
都是 JavaScript 中用于比较两个值的运算符
==
运算符会进行类型转换后再比较两个值是否相等,如果两个值不是相同的类型,它们将被转换为相同的类型进行比较。例如:console.log(1 == '1'); // true,'1' 会被转换为数字 1 进行比较 console.log(true == 1); // true,true 会被转换为数字 1 进行比较 console.log(undefined == null); // true,它们被认为是相等的空值
===
运算符不进行类型转换,只在两个值完全相等时返回true
。两个值必须是相同的类型,并且值相等,才能返回true
。例如:console.log(1 === '1'); // false,它们的类型不同 console.log(true === 1); // false,它们的类型不同 console.log(undefined === null); // false,它们的类型不同
11、宏任务和微任务
宏任务(macro-task)和微任务(micro-task)是 JavaScript 中两种不同的任务队列。
宏任务可以理解为当前执行栈中没有可执行的代码时,需要执行的任务,比如
setTimeout、setInterval
等异步任务都属于宏任务。浏览器每轮事件循环只会从宏任务队列中取出一个任务执行,执行完后再去检查是否有新的宏任务需要执行。微任务则是指当当前宏任务执行完毕后,在下一个宏任务开始之前需要执行的任务,比如
Promise.then、MutationObserver
的回调等都属于微任务。每个宏任务在执行时,会创建一个微任务队列,并在当前宏任务执行完成后把微任务队列中的所有任务依次执行完毕,直到微任务队列为空或者异常被抛出。
举个例子:
console.log('start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('end');
//输出结果为
//start
//end
//Promise
//setTimeout
可以看到,
setTimeout
回调函数是在Promise.then
回调函数之后执行的,这就是因为setTimeout
是宏任务,而Promise.then
是微任务,在同一轮事件循环中,微任务优先级高于宏任务。
总结: 宏任务和微任务都是异步任务,它们的执行顺序以及如何触发和运行都是有一定规则的。了解这些规则有助于我们更好地理解和处理异步代码,并且避免出现不必要的问题。
12、从输入 URL 到页面加载的全过程
从输入 URL 到页面加载的全过程可以大致分为以下几个步骤:
1、DNS 解析
浏览器会先根据输入的域名解析出对应的 IP 地址,以便后续信息交互。DNS 解析的流程包括递归查询、迭代查询和负载均衡等,具体可以参考 [1]。
2、建立 TCP 连接
浏览器使用 HTTP 或 HTTPS 协议与服务器建立 TCP 连接,使用三次握手保证连接的可靠性。TCP 的三次握手包括 SYN、SYN-ACK 和 ACK 三个阶段,具体可以参考 [2]。
3、发送 HTTP 请求
在 TCP 连接建立之后,浏览器向服务器发送 HTTP 请求,包括请求头、请求方法、请求参数等信息。
4、服务器处理请求并返回 HTTP 响应
当服务器收到请求之后,会根据请求信息进行一系列的处理,然后将处理结果打包成 HTTP 响应发回给浏览器。
5、浏览器解析页面并渲染
浏览器接收到 HTTP 响应后,开始解析页面。具体过程大致为:根据 Content-Type 判断响应类型;HTML 解析成 DOM 树,CSS 解析成样式规则;渲染引擎将 DOM 树和样式规则共同转换成可视化的页面,然后进行页面布局和绘制等操作,最终呈现在浏览器界面中。
6、关闭 TCP 连接
当页面加载完毕之后,浏览器会关闭与服务器的 TCP 连接,释放网络和系统资源。
总结: 从输入 URL 到页面加载完成,涉及 DNS 解析、建立 TCP 连接、HTTP 请求、服务器响应、页面解析渲染等多个过程。对这些过程的深入理解可以帮助我们优化网站性能,提高用户体验。
13、vuex数据持久化
Vuex 是 Vue.js 应用程序开发的一种状态管理模式和库,可以方便地管理应用程序中的数据。Vuex 的数据存储是内存中的,当浏览器页面刷新或关闭后,所有保存在 Vuex 中的数据都会消失。为了避免这种情况,需要对 Vuex 数据进行持久化处理。
对于 Vuex 数据的持久化,可以考虑使用浏览器提供的 LocalStorage 或者 SessionStorage。LocalStorage 和 SessionStorage 都是 HTML5 新增的 Web 存储 API,可以将数据存储在客户端本地,以键值对的形式进行访问。
具体实现可参考以下步骤:
- 在 Vuex 中注册一个插件,在插件中添加一个方法来将 state 中的数据保存到 LocalStorage 中。
const myPlugin = store => { // 在每次 mutation 之后调用 store.subscribe((mutation, state) => { // 持久化 state 到 local storage window.localStorage.setItem('my-state', JSON.stringify(state)); }); }; const store = new Vuex.Store({ plugins: [myPlugin], state: { count: 0 }, mutations: { increment(state) { state.count++; } } });
- 在 Vuex 的初始化时,将 LocalStorage 中的数据恢复到 state。
const store = new Vuex.Store({ plugins: [myPlugin], state: { // 从 LocalStorage 中获取数据并进行 JSON 解析 count: JSON.parse(window.localStorage.getItem('my-state') || '{}').count || 0 }, mutations: { increment(state) { state.count++; } } });
或者,可以使用 Vuex 插件
vuex-persistedstate
来实现数据持久化。该插件可以自动将 state 中的数据保存在 LocalStorage 中,并在初始化时从 LocalStorage 中恢复数据。具体使用方法可参考以下代码:
-
import createPersistedState from 'vuex-persistedstate' const store = new Vuex.Store({ plugins: [ createPersistedState({ key: 'my-state' // 存储在 LocalStorage 中的键名 }) ], state: { count: 0 }, mutations: { increment(state) { state.count++; } } });
14、js事件循环机制
JavaScript 是一门单线程语言,也就是说在同一时间只能执行一个任务。但是,当我们编写代码时,使用了异步操作(如定时器、Ajax 请求等),这些异步操作会被挂起并在将来的某个时间点恢复,从而实现了非阻塞的异步执行。
JavaScript 异步执行的核心机制就是事件循环(Event Loop)。事件循环是指 JavaScript 实现异步的一种机制,它是一个执行模型,用于协调事件和输入 I/O 等异步操作。浏览器会维护一个事件队列(Event Queue),用于存储所有异步事件和回调函数。当事件循环的规定条件被满足时,就会根据优先级从事件队列中取出一个事件或回调函数,并将其放入执行栈中执行,然后再执行下一个周期的事件循环。
事件循环大致分为以下几个阶段:
- 执行同步任务:从上到下依次执行同步任务。
- 执行微任务(micro-task):当执行栈中所有同步任务执行完成后,会立即处理所有微任务,包括 Promise.then 和 MutationObserver 回调等。
- 执行宏任务(macro-task):如果有多个宏任务,优先处理优先级高的宏任务,常见的宏任务包括 setTimeout、setInterval、setImmediate 和 requestAnimationFrame 等。
- 执行其他操作:例如 I/O 操作、UI 渲染等。
注意:事件循环在执行时,会不断地从任务队列中取出一个任务进行执行。当取出的任务是宏任务时,任务执行完毕后会立即清空微任务队列再检查是否有新的宏任务需要执行。当取出的任务是微任务时,会立即把所有微任务队列中的任务执行完毕后再去检查是否有新的宏任务需要执行。
举个例子:
javascript
console.log('start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('end');
//输出
//start
//end
//Promise
//setTimeout
//因为 Promise.then 回调函数是微任务,所以会先被执行。而 setTimeout 回调函数是宏任务,要等待当前事件循环结束后才能执行。
15、数组去重
- 利用 Set 数据结构
Set 是 ES6 中新增的数据结构,它可以接收一个数组作为参数,并且会自动去除重复的元素。我们只需要将原始数组转化为 Set 对象,再将 Set 对象转化回数组即可完成数组去重。
const arr = [1, 2, 3, 4, 5, 1, 2]; const uniqueArr = [...new Set(arr)]; console.log(uniqueArr); // [1, 2, 3, 4, 5]
- 利用 forEach 和 includes 方法
我们可以使用 forEach 遍历原始数组,使用 includes 判断新数组中是否已经包含当前元素,如果不包含则加入新数组中。
const arr = [1, 2, 3, 4, 5, 1, 2]; const uniqueArr = []; arr.forEach((item) => { if (!uniqueArr.includes(item)) { uniqueArr.push(item); } }); console.log(uniqueArr); // [1, 2, 3, 4, 5]
- 利用 filter 和 indexOf 方法
我们可以使用 filter 方法对原始数组进行过滤,再利用 indexOf 判断元素是否是第一次出现,如果是则加入新数组中。
const arr = [1, 2, 3, 4, 5, 1, 2]; const uniqueArr = arr.filter((item, index) => { return arr.indexOf(item) === index; }); console.log(uniqueArr); // [1, 2, 3, 4, 5]
- 利用 map 方法和对象存储不重复值
我们可以使用 map 方法过滤出不重复的元素,并且使用对象存储不重复的值。
const arr = [1, 2, 3, 4, 5, 1, 2]; const obj = {}; const uniqueArr = arr.map((item) => { return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true); }); console.log(uniqueArr); // [1, 2, 3, 4, 5]
- 利用 reduce 方法和 includes 方法
我们可以使用 reduce 方法对原始数组进行迭代,再用 includes 方法判断新数组中是否已经包含当前元素,如果不包含则加入新数组中。
const arr = [1, 2, 3, 4, 5, 1, 2]; const uniqueArr = arr.reduce((pre, cur) => { if (!pre.includes(cur)) { return pre.concat(cur); } else { return pre; } }, []); console.log(uniqueArr); // [1, 2, 3, 4, 5]
- 利用 ES6 的 Map 数据结构
Map 是一种键值对结构,它可以用于存储各种类型的值(包括对象)。我们可以利用 Map 对象的结构特性,对元素进行去重。
const arr = [1, 2, 3, 4, 5, 1, 2]; const map = new Map(); const uniqueArr = []; arr.forEach((item) => { if (!map.has(item)) { map.set(item, true); uniqueArr.push(item); } }); console.log(uniqueArr); // [1, 2, 3, 4, 5]
- 利用递归和 indexOf 方法
我们可以利用递归和 indexOf 方法对原始数组进行遍历,依次判断元素出现的索引位置是否等于当前索引位置,如果是则加入新数组中。
const arr = [1, 2, 3, 4, 5, 1, 2]; function unique(arr) { const len = arr.length; return arr.filter(function (item, index, arr) { return arr.indexOf(item) === index; }); } console.log(unique(arr)); // [1, 2, 3, 4, 5]
16、 数组扁平化
数组扁平化指将多维的数组转化为一维数组,可以从下面几种方法进行处理:
- 利用递归
利用递归的方式对多维数组进行遍历,如果遇到元素不是数组,则直接存入结果数组中,否则递归调用函数进行处理。
function flatten(arr) { let res = []; arr.forEach((item) => { if (Array.isArray(item)) { res = res.concat(flatten(item)); } else { res.push(item); } }); return res; } const arr = [1, 2, [3, 4, [5, 6]]]; console.log(flatten(arr)); // [1, 2, 3, 4, 5, 6]
- 利用 reduce 方法
利用 reduce 方法对多维数组进行迭代,通过判断元素是否是数组来决定是否进行递归处理。
function flatten(arr) { return arr.reduce((pre, cur) => { return pre.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []); } const arr = [1, 2, [3, 4, [5, 6]]]; console.log(flatten(arr)); // [1, 2, 3, 4, 5, 6]
- 利用 while 循环和 shift 方法
利用 while 循环和 shift 方法对多维数组进行处理,每次从数组的第一个元素开始取出,如果是数组则将其扁平化后再放入数组中,否则直接加入结果数组中。
function flatten(arr) { let res = []; while (arr.length) { const item = arr.shift(); if (Array.isArray(item)) { arr = item.concat(arr); } else { res.push(item); } } return res; } const arr = [1, 2, [3, 4, [5, 6]]]; console.log(flatten(arr)); // [1, 2, 3, 4, 5, 6]
- 利用 ES6 的 flat 方法
ES6 提供了 Array.prototype.flat 方法,可以直接对多维数组进行扁平化操作,通过传入参数表示扁平化的层数。
const arr = [1, 2, [3, 4, [5, 6]]]; console.log(arr.flat(2)); // [1, 2, 3, 4, 5, 6]
17、set和map的区别
set 和 map 都是 ES6 中新增的数据结构,两者虽然在实际应用中有一些相似之处,但在本质上是有区别的。
1、基本概念和用法的不同
Set 是一个无序、唯一的集合,其值可以是任何类型(包括基本类型和引用类型),可以进行添加、删除和查询操作,常用于数组元素去重、交集、并集等场景。
Map 是一个键值对的集合,其中每个元素都由一个键和一个值组成,键和值可以是任何类型(包括基本类型和引用类型),可以进行添加、删除和查询操作,常用于存储一些描述性的信息,如对象属性、配置项等。
2、存储结构的不同
Set 的存储结构类似于数组,但由于其值的唯一性,其内部实现使用了类似于哈希表的方式。
Map 的存储结构类似于对象,但其键不限于字符串类型,也可以是任意其他类型。
3、适用场景的不同
Set 适用于需要存储一些唯一的元素集合,且需要进行一些特定操作的情况下,如数组去重、交集、并集等。
Map 适用于存储一些特定的键值对,在需要根据键来快速查找、更新或删除值的情况下,如存储对象属性、配置项等。
综上所述,Set 和 Map 在本质上是不同的,应根据实际需求选择合适的数据结构。
18、vue自定义过滤器
在 Vue 中,可以通过自定义过滤器来对数据进行格式化处理。以下是一个使用 Vue 自定义过滤器的示例:
<!-- 在模板中使用过滤器 -->
<p>{{ message | capitalize }}</p>
// 定义 capitalize 过滤器
Vue.filter('capitalize', function(value) {
if (!value) return '';
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
});
这段代码会将 message 中的字符串首字母大写后输出到页面中。
在上述代码中,我们使用了 Vue.filter 方法来定义过滤器。该方法接受两个参数:过滤器名称和过滤器函数。过滤器函数接受一个值作为输入,并返回处理后的值。
在模板中使用过滤器时,在需要过滤的值后面加上管道符(|),然后跟上过滤器名称即可。
当页面渲染时,Vue 将会自动调用过滤器函数并将需要过滤的值作为参数传入,然后输出过滤器函数的返回值。
19、v-if和v-for的优先级
vue2中v-for的优先级高于v-if
vue3中v-if的优先级高于v-for