vue hooks
umi3 next.js egg.js Nuxt.js
函数声明:function aa(a,b){return a+b}
函数表达式: var aa = function(a,b){return a+b}
Promise 的状态一旦从 pending(进行中)变为 fulfilled(已成功)或 rejected(已失败)之后,就不能再改变了。这也是 Promise 的特性之一,它保证了一旦状态发生改变,就不会再变化。
se6新增
1、let const
2、加入了导出功能
//导出main.js
export let name = 'zjz'
export function talk(arg){
return arg
}
//导入
import {name,talk} from './main'
//或者
let tool = {
name:'zjz',
talk(){}
}
export default tool
//使用
tool.name
3、解构
let arr = [1,2,3]
let [a,b,c] = arr
4、扩展运算符
let arr = [1,2,3]
let arr1 = [566, ...arr]
5、函数参数赋值(即设置默认值)
6、对象属性值的简写
7、async await
async function talk(params){
let res = await getList(params)
if(res){}
}
8、includes 是否包含
let arr = ['zjz','lyl','zxx']
if(arr.includes('lyl')){ //包含返回true
}
9、指数操作符
2的十次方 2**10 = 1024
10、对象的操作
let obj = {
a:1,
b:2,
c:3
}
Object.keys(obj) //[a,b,c]
Object.values(obj) //[1,2,3]
Object.entries(obj) //[[a,1],[b,2],[c,3]]
11、null 传导运算符 ?.
12、null 判断运算符 ??
let aa = response.data ?? '400' //左侧不为null 和undefind 时取左侧,否则取右侧
let bb = response?.data?.cont //当?有值时,可以取到后面
13、模板字符串
会改变原数组的方法有
splice()、push()、pop()、shift()、unshift()、reverse()、sort()。
splice
arr.splice(val,val,val)
第一个参数表示从哪开始
第二个参数表示删除几位
第三个参数表示替换的
会返回被删除的元素集合
会改变原数组
reduce
数组累加器
1. reduce是一个对数组累积操作的方法,使用时要加上 return 返回累积操作的数据。这样 prev 才能获取上一次执行的结果,否则是 undefined;
2. 空数组执行 reduce 操作且不提供初始值时reduce会报错
let arr = [1,2,3,4]
let sum = arr.reduce((prev,curr,index,arr)=>{
return prev+curr
},5) //初始值,可选
let sum = arr.reduce((x,y)=>x+y)
let mul = arr.reduce((x,y)=>x*y)
vue中数据更新视图不更新的原因
1、Vue 无法检测实例被创建时不存在于 data 中的 变量
由于 Vue 会在初始化实例时对 data中的数据执行 getter/setter 转化,所以 变量必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
2、vue也不能检测到data中对象的动态添加和删除
this.$set(this.obj, 'id', 002)
3、数组的时候,不能通过索引直接修改或者赋值,也不能修改数组的长度
this.$set(this.items, 4, 'd)
4、异步获取接口数据,DOM数据不发现变化
原因:Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的Promise.then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn, 0)代替。
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
vm.$el.style.color = 'red' // 文字颜色变成红色
})
5、循环嵌套层级太深,视图不更新
当嵌套太深时,页面也可以不更新,此时可以让页面强制刷新
this.$forceUpdate() (不建议用)
6、 路由参数变化时,页面不更新(数据不更新)
拓展一个因为路由参数变化,而导致页面不更新的问题,页面不更新本质上就是数据没有更新。
原因:路由视图组件引用了相同组件时,当路由参会变化时,会导致该组件无法更新,也就是我们常说中的页面无法更新的问题。
1)通过watch监听$route的变化。
2)给<router-view>绑定key属性,这样 Vue 就会认为这是不同的<router-view>。
宏任务微任务
宏任务: setTimeout、setInterval
微任务: Promise.then
在 JavaScript 的事件循环模型中,当执行栈中的同步任务执行完毕后,事件循环会首先检查微任务队列是否为空。如果不为空,它会优先执行微任务队列中的所有任务,直至微任务队列为空。这一过程被称为微任务检查点(microtask checkpoint)。
一旦微任务队列被清空,事件循环会继续检查宏任务队列,从中取出一个宏任务并执行它的回调函数。在执行完这个宏任务的所有同步代码后,事件循环又会回到微任务检查点,如此往复,形成一个持续不断的事件循环过程。
**Ts泛型 **
This 更改指向
5、说一下防抖和节流。怎么实现?
防抖和节流是用于控制函数执行频率的方法,防抖是在一定时间内只执行最后一次操作,节流是在一定时间内只执行一次操作。可以通过setTimeout和时间戳等方式实现。
For in for of区别
for in适合遍历对象,for of适合遍历数组
for in 可遍历数组和对象,对象遍历key,数组遍历下标 ,会遍历原型上的方法
for in适合遍历对象,for of适合遍历数组。for in遍历的是数组的索引,对象的属性,以及原型链上的属性。
let list = [ { name: '花花' }, { name: '草草' } ]
for(let i in list) {
console.log(i)
}
for of 只能遍历数组,输出的是每一项的值, 不会把原型上的属性或方法遍历出来
apply,call,bind 的区别
其实用于指定函数内部的this指向的问题,这三个方法都差不多,只是存在形式上的差别。读者可以将以上的例子用三种方法尝试用三种方法实现。
a:第一个参数都是指定函数内部中this的指向(函数执行时所在的作用域),然后根据指定的作用域,调用该函数。
b:都可以在函数调用时传递参数。call,bind方法需要直接传入,而apply方法需要以数组的形式传入。
c:call,apply方法是在调用之后立即执行函数,而bind方法没有立即执行,需要将函数再执行一遍。有点闭包的味道。
d:改变this对象的指向问题不仅有call,apply,bind方法,也可以使用that变量来固定this的指向。
apply方法:
-
apply方法的第一个参数是函数的上下文对象(this的值)。- 第二个参数是一个数组,包含了传递给函数的参数。
例如:
function greet(name) {
console.log('Hello, ' + name);
}
var obj = { name: 'John' };
greet.apply(obj, ['Doe']); // Hello, Doe
- 在这个例子中,
greet函数被调用,this的值为obj,参数是'Doe'。
call方法:
-
call方法的第一个参数也是函数的上下文对象。- 接下来的参数是以逗号分隔的,直接传递给函数的各个参数。
function greet(name) {
console.log('Hello, ' + name);
}
var obj = { name: 'John' };
greet.call(obj, 'Doe'); // Hello, Doe
- 在这个例子中,
greet函数被调用,this的值为obj,参数是'Doe'。
bind方法:
-
bind方法与apply和call类似,第一个参数是函数的上下文对象。bind方法会返回一个新的函数,这个新函数的this值被永久设定为绑定的上下文对象。bind方法的其他参数会被记住,但是在新函数被调用时才会作为参数传入
function greet(name) {
console.log('Hello, ' + name);
}
var obj = { name: 'John' };
var boundGreet = greet.bind(obj, 'Doe');
boundGreet(); // Hello, Doe
- 在这个例子中,
boundGreet是新创建的函数,它的this值被永久绑定为obj,并且它记住了参数'Doe'。当boundGreet被调用时,会打印Hello, Doe。
综上所述,apply、call和bind方法都是用来改变函数执行时的上下文环境的,但apply和call是立即调用函数,而bind是返回一个新函数。在使用这些方法时,需要注意参数的格式和顺序,以确保函数按照预期的方式被调用。
使用场景
1.改变函数执行上下文
2.传递参数
function sum(a, b) {
return a + b;
}
console.log(sum.apply(null, [2, 3])); // 输出:5
3、 函数复用
const obj1 = {
value: 42,
getValue: function() {
return this.value;
}
};
const obj2 = {
value: 100
};
console.log(obj1.getValue.call(obj2)); // 输出:100
4、使用Object.prototype.toString.call(xx)判断对象类型:
Object.prototype.toString 是 JavaScript 所有对象都具有的方法,它返回一个表示当前对象类型的字符串,格式为 "[object 类型]"。通过使用 call 方法,我们可以改变 this 的指向,从而让 toString 方法作用于传入的参数 xx。
console.log(Object.prototype.toString.call(obj)); // 输出: "[object Object]" console.log(Object.prototype.toString.call(arr)); // 输出: "[object Array]" console.log(Object.prototype.toString.call(str)); // 输出: "[object String]" console.log(Object.prototype.toString.call(num)); // 输出: "[object Number]"
5、 Math.max.call求数组的最大值:.call 方法接受两个参数:第一个参数是用来设置 this 值的对象,我们这里传入 null,因为 Math.max 不需要依赖于特定的对象;第二个参数是展开操作符 ... 来将数组中的元素作为单独的参数传递给 Math.max 方法。
const numbers = [10, 5, 20, 15];
const maxNumber = Math.max.call(null, ...numbers);
console.log(maxNumber); // 输出: 20
-6、浏览器渲染机制
浏览器的渲染机制涉及到从接收 HTML、CSS 和 JavaScript 到最终呈现页面的整个过程。以下是浏览器渲染页面的基本流程:
- HTML 解析: 当浏览器接收到 HTML 文件时,它会从上到下逐行解析文档,构建 DOM(文档对象模型)树。DOM 树表示了文档的结构和内容。
- CSS 解析: 浏览器在解析 HTML 的同时,也会解析外部和嵌入的 CSS 样式表,构建 CSSOM(CSS 对象模型)树。CSSOM 树表示了文档的样式信息。
- 合并 DOM 和 CSSOM: 浏览器将 DOM 树和 CSSOM 树合并成一个渲染树(Render Tree)。渲染树只包括需要显示的元素和其样式信息,不包括不可见的元素(例如
display: none的元素)。 - 布局: 浏览器根据渲染树中每个节点的信息,计算它们在页面上的位置和大小。这个过程被称为布局(或回流),浏览器确定每个元素在视口中的确切位置。
- 绘制: 浏览器使用计算好的位置和大小信息,将每个元素绘制到屏幕上,形成用户最终看到的页面。
- 重绘与重排: 当页面发生变化时,浏览器会对修改的部分进行重绘或重排。重绘指的是更新元素的外观而不影响布局,而重排指的是布局发生改变,需要重新计算元素的位置和大小。
JavaScript 的执行也会对渲染过程产生影响。当浏览器遇到 <script> 标签或 JavaScript 文件时,它会暂停 HTML 解析,执行 JavaScript 代码,然后继续解析和渲染。
优化前端性能的关键在于减少布局抖动、减小重排、重绘的区域、减少资源加载时间等方面,以提高页面的渲染速度和流畅度。
5、WebSocket
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它提供了在 Web 应用程序中进行实时数据传输的能力,客户端和服务器之间可以进行双向通信
// 创建 WebSocket 连接
const ws = new WebSocket('ws://localhost:8080');
// 监听连接打开事件
ws.onopen = function() {
console.log('已连接到服务器');
// 发送消息
ws.send('这是客户端发送的消息');
};
// 监听消息事件
ws.onmessage = function(event) {
console.log('收到服务器消息:', event.data);
};
// 监听错误事件
ws.onerror = function(event) {
console.error('WebSocket 错误:', event);
};
// 监听连接关闭事件
ws.onclose = function(event) {
console.log('连接已关闭');
};
-4vue的导航守卫有哪些?
1、全局前置守卫
2、路由独享的守卫
3、组件内的守卫
1、全局前置守卫
- beforeEach(to, from, next):全局前置守卫,会在路由切换开始时触发。
- beforeResolve(to, from, next):在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后触发。
- afterEach(to, from):导航成功完成后触发,不接受 next 函数。
2、路由独享守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// 路由独享的前置守卫逻辑
}
}
]
});
3、组件内守卫
beforeRouteEnter(to, from, next):在路由进入该组件前触发。
beforeRouteUpdate(to, from, next):在当前路由改变但是该组件被复用时触发。
beforeRouteLeave(to, from, next):在离开当前组件对应路由前触发。
-3、点击刷新按钮或者按 F5、按 Ctrl+F5 (强制刷新)、地址栏回车有什么区别?
- 点击刷新按钮或者按 F5
浏览器直接认为本地的缓存文件过期,但是请求时会带上 If-Modifed-Since,If-None-Match,这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200。 - 用户按 Ctrl+F5(强制刷新)
浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前从来没有请求过,返回结果是 200 - 地址栏回车
浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容
-2、bfc话术
- BFC(Block formatting context)直译为“块级格式化上下文”
- BFC是一个独立的容器,容器内子元素不会影响容器外的元素,反之亦是如此。
- 盒子从顶端开始垂直的一个一个的排列,盒子之间垂直的间距是由margin决定的。
- 在同一个BFC中,两个相邻的块级盒子的垂直外边距会发生重叠。
- BFC区域不会和float box发生重叠。
- BFC能够识别并包含浮动元素,当计算其区域的高度时,浮动元素也可以参与计算了。
- 计算BFC的高度时,浮动元素也参与计算。两个box之间的距离为100px,发生了
margin重叠(塌陷),解决这个问题,只需要把其中一个box变为BFC元素即可
-1、js事件循环
JavaScript语言最大的特点就是单线程,即同一时间只能做一件事情,做完一件事情之后才能去做另一件事情。
JavaScript的事件循环机制 :
单线程就意味着所有任务需要排队,前一个任务结束才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等待。设计者意识到了这一点,于是将所有的任务分成了两种:
同步任务(synchronous):
同步任务指的是:在主线程上排队执行的任务,只有前一个任务执行完毕才能执行后一个任务。所有的同步任务都在主线程上执行,形成了一个执行栈。执行栈的执行顺序是后进先出。
异步任务(asynchronous):
异步任务指的是:不进入主线程,而是进入“任务队列”的任务。只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
事件循环过程:
同步任务和异步任务分别进行不同的场所,同步任务进入主线程,异步任务进入事件表(Event Table)并且注册函数。
当指定的事情完成之后(例如接受到了Promise对象的返回结果resolve/reject),事件表(Event Table)会将这个函数移入到任务队列(Event Queue)。
主线程内的任务执行完毕之后,回去任务队列(Event Queue)读取对应的函数,移入到主线程执行。
上述过程不断重复,这就是事件循环(Event Loop)
1、vue生命周期
- 创建阶段(Initialization):
-
beforeCreate:在实例初始化之后,数据观测和事件配置之前被调用。created:在实例创建完成后被立即调用。在这一步,实例已完成以下配置:数据观测、属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
- 挂载阶段(Mounting):
-
beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted:el 被新创建的 vm.el替换,并挂载到实例上去之后调用该钩子函数。如果根实例挂载到了一个文档内的元素上,当mounted被调用时
-
- el替换,并挂载到实例上去之后调用该钩子函数。如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.el 也在 document 内。
- 更新阶段(Updating):
-
beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁后调用。
- 销毁阶段(Destroying):
-
beforeDestroy:在实例销毁之前调用。这一步,实例仍然完全可用。destroyed:在实例销毁之后调用。这时,所有绑定的事件和子实例都已经被销毁。
- 错误处理阶段(Error Handling):
-
errorCaptured:errorCaptured 钩子函数可以捕获子孙组件触发的任何异常,类似于 JavaScript 的 try/catch 块。
vue2 -------> vue3
beforeCreate --------> setup(()=>{})
created --------> setup(()=>{})
beforeMount --------> onBeforeMount(()=>{})
mounted --------> onMounted(()=>{})
beforeUpdate --------> onBeforeUpdate(()=>{})
updated --------> onUpdated(()=>{}) kl'//‘ ’
beforeDestroy --------> onBeforeUnmount(()=>{})
destroyed --------> onUnmounted(()=>{})
activated --------> onActivated(()=>{})
deactivated --------> onDeactivated(()=>{})
errorCaptured --------> onErrorCaptured(()=>{})
2、数据类型,类型判断,null跟NaN的区别
基本数据类型:string number bolean null undefind
引用数据类型:object array (function)
let arr = [1, 2, 3]; // 定义一个数组
let func = function() {}; // 定义一个函数
console.log(arr instanceof Array); // 输出 true
console.log(func instanceof Function); // 输出 true
console.log(Object.prototype.toString.call(arr)); // 输出 "[object Array]"
console.log(Object.prototype.toString.call(func)); // 输出 "[object Function]"
- typeof 操作符
-
- 优点:typeof 是最基本的类型检测方法,可用于检测 undefined、boolean、number、string、symbol、function 和 object 类型。
- 缺点:对于数组、null 和其他对象类型,返回的结果并不总是准确,例如 typeof null 返回 "object",而 typeof [] 也返回 "object"。只能准确判断除 null 以外的基本数据类型;对于引用数据类型,typeof 的判断结果都是 object,函数返回 function;
- instanceof 操作符
-
- 优点:可以用来检测一个对象是否是某个类的实例。
- 缺点:对于基本数据类型(如字符串、数字等)和原始类型值(如 null、undefined 等),无法准确判断类型。
- constructor 是查看自己的构造函数;
constructor可以改变,会导致判断不准确;
(5).constructor === Number // true
"text".constructor === String // true
true.constructor === Boolean // true
({}).constructor === Object // true
console.log({} instanceof Object) // true
// Uncaught TypeError: Cannot read property 'constructor' of undefined
undefined.constructor === undefined // 报错
null.constructor === null // 报错
- Object.prototype.toString.call() 方法
-
- 优点:对于所有数据类型都能够进行准确的类型判断,包括基本数据类型和引用数据类型。
- 缺点:语法稍显繁琐,需要使用 Object.prototype.toString.call(value) 的形式,比较复杂。
null表示一个空值或者没有值,常用于初始化变量或者清空一个变量。如果一个变量被赋值为null,它表示这个变量没有任何值,即该变量不指向任何对象或值。
3、新增数据类型
1)set集合 :是Object里面的一种, set与数组不同在于set中不可以有重复数据,常用语去重操作
2)Map类型:可以允许任何类型作为对象的键,弥补了object只能使用字符串作为键的问题
3)symbol : 用于表示独一无二的标识符,有一个特性—每次创建一个Symbol值都是不一样的。
symbol是程序创建并且可以用作属性键的值,并且它能避免命名冲突的风险。
创建的方式也有点奇怪,只能通过调用全局函数Symbol()来完成。
let firstSymbol = Symbol();
4、vue中的通信方式
1、props $emit
2、this.$ref.parent / children
3、bus 总线
- 挂载全局 Vue.prototype.$bus = this
- 数据发送方 this.$bus. emit (‘hello’, this.data)
- 数据接收 mounted 中 this.$bus. on ('hello', 当前函数)
在接收方的beforeDestroy中销毁 this.$bus. off ('hello')
5、vueX
状态管理模式
- 集中式存储管理应用的所有组件的状态
state是状态数据
可以通过this.$store.state来直接获取状态,也可以利用vuex提供的mapState辅助函数将state映射到计算属性(computed)中去。
computed:{
// 利用mapState辅助函数的简写形式,代替以上的语句,但本质也是生成计算属性
...mapState('options',['title','singer']),
},
mutations
更改 Vuex 的 store 中的修改状态的唯一方法是提交 mutation。
actions
Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。
getters
有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数,也就是对状态在加工
modules
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const options ={
namespaced: true,
// 相应组件中的动作,组件中可以通过通过dispatch发送,
// actions 通过commit发送动作告知mutations
actions:{
changeSing(context,value){
setTimeout(()=>{
context.commit('editTitle',value);
},3000)
},
changeSinger(context,value){
setTimeout(()=>{
context.commit('editSinger',value);
},3000)
}
},
// 操作state中的数据
mutations :{
editTitle(state,value){
state.title = value;
},
editSinger(state,value){
state.singer = value;
}
},
// 管理store中的数据
state :{
title:'Daylight',
singer:'TaylorSwift'
},
// 依赖并加工state中的数据,state中的数据也会相应改变
getters :{
otherSong(state){
return state.title = 'Delicate'
}
}
}
const store = new Vuex.Store({
modules:{
options
}
})
export default store;
5、local
6、provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量
// 父组件
import { provide } from 'vue'
provide('message', 'test')
// 任意层级的子组件
import { inject } from 'vue'
const value = inject('message', 'default value')
export default {
// 父组件通过provide将自己的数据以对象形式传出去
provide(){
return {
parentValue:"我是父组件的值啊"
}
}
};
// B.vue
export default {
// inject:["parentValue"], // 使用一个注入的值作为数据入口:
inject:{
// 使用一个默认值使其变成可选项
parentValue: { // 健名
from: 'parentValue', // 来源
default: 'parentValue' // 默认值
}
}
}
6、组件中 data 为什么是一个函数?
为了组件中数据的作用域
因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响, 如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
7、Vue 的父组件和子组件生命周期钩子函数执行顺序?
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
加载渲染过程 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
子组件更新过程 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父组件更新过程 父 beforeUpdate -> 父 updated
销毁过程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
8、computed 和 watch 的区别和运用的场景?
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
9、vue中 v-for循环中用index 作为key值的问题
zhuanlan.zhihu.com/p/644692963
如果有 删除、增加、排序这样的功能,不要把 index 作为 key。
操作时,删除,排序都会有问题
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
10、vuex数据页面刷新丢失
监听刷新
存储本地 sessionStrogge local
开始的时候是否触发:
updated 钩子函数只有在组件完成初次渲染后,当数据发生变化导致组件重新渲染时才会触发。因此,在组件初次渲染时,updated 钩子函数不会被调用。
11、输入url之后经历了什么?
一、浏览器查找输入域名的IP地址(拿到 IP)
1、查找浏览器缓存(浏览器一般会缓存DNS记录一段时间,一般为2-30分钟)。
2、查找系统缓存(即hosts文件,有没有对应的IP)
3、以上都没有的话,就会经过DNS域名服务器进行域名解析
二、建立TCP连接(三次握手)
1、你在家么有外卖
2、我在家,你来吧
3、好的,我这就去
三、发送Http请求
四、服务器处理请求
五、返回响应结果
六、关闭TCP连接
七、浏览器解析html
八、浏览器布局渲染
12、Object.assign
Object.assign()== 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
简单来说,就是Object.assign()是对象的静态方法,可以用来复制对象的可枚举属性到目标对象,利用这个特性可以实现对象属性的合并。
var target={name:'guxin',age:18}
var source1={state:'signle',age:22}
var source2={mood:'happy',age:25}
var result=Object.assign(target,source1,source2)
console.log(target)
- 注意事项:
1、Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象,继承属性和不可枚举属性是不能拷贝的。
2、==针对深拷贝,需要使用其他办法==,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
3、== 目标对象自身也会改变 result=Object.assign(target,source1,source2)target也会和result 结果一样==
4、异常会打断后续拷贝任务
13、$.extend()
$.extend() 是 jQuery 中一个非常有用的方法,用于将一个或多个对象的内容合并到目标对象中。
$.extend(target, [object1], [objectN]);
$.extend(true, target ,defaults, options) 可用于深拷贝。
- true:是否深度拷贝,不加为false,浅拷贝,加了深拷贝
- {}:将合并结果保存到新对象,这样原对象将不会发生改变
- a:第一个合并的对象
- b:第二个合并的对象
var target = {
name: "John",
age: 25
};
var source = {
age: 30,
city: "New York"
};
$.extend(target, source);
console.log(target); // 输出:{name: "John", age: 30, city: "New York"}
14、flat()
语法:
var newArray = arr.flat([depth]) depth 可选 指定要提取嵌套数组的结构深度(降维深度),默认值为 1。
eg:
- 扁平化嵌套数组
const newArr = [1, 2, 3, ['a', 'b', 'c', ['Aa']]].flat(2)//[1,2,3,"a","b","c","Aa]
const newArr2 = [1, 2, 3, ['a', 'b', 'c', ['Aa']]].flat(2)//[1, 2, 3, ['a', 'b', 'c', ['Aa']]].flat(1)
- Infinity扁平化任意深度
const newArr2 = [1, 2, 3, ['a', 'b', 'c', ['Aa']]].flat(Infinity)//[1, 2, 3, ['a', 'b', 'c', ['Aa']]].flat(1)
- 扁平化与空项
const newArr = [1, 2, 3, , 6, 8].flat() // [1, 2, 3, 6, 8]
15、flatMap()
** flatMap是数组的一个新方法,它会遍历原数组的每一个元素, 并且会为每个元素都执行一次传入的回调函数,最终把所有元素执行回调函数返回的结果压缩成一个新数组,flatMap会返回一个新的数组,不会改变原数组的元素。**
16、vue自定义指令 和vue 过滤器 (vuie3废弃)
<template>
<h3>{{str | strChange('加油 China !')}}</h3>
<input v-focus>
</template>
<script>
export default {
data (){
return {str:'hello world'};
},
filters:{
//第一个参数是|之前的,第二个参数是过滤器携带的
strChange(value,res) {
console.log('value值:',value,'res值:',res)
return value+' ! '+res
}
}
directives: {
focus: {
// 当被绑定的元素插入到 DOM 中时...
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
}
}
</script>
17、js中的 async 和 defer 有什么区别
1)没有这个两个属性时,浏览器遇到script时,文档解析停止,其他线程现在下载并执行js脚本,执行完后接续解析文档
2)添加async 时,文档解析不会停止,其他线程下载js脚本,脚本下载完成后开始执行脚本,执行脚本时文档停止解析,直到脚本解析完成,再继续解析 (异步下载,并立即执行)
3)添加defer时,文档的解析不会停止,其他线程下载脚本,待文档解析完成之后,脚本才会执行。 (异步下载,待文档解析完成后,执行)
18、async awite
Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。
Async声明一个异步函数,Await 等待一个异步方法执行完毕
错误处理
在async函数里,无论是Promise reject的数据还是逻辑报错,都会被默默吞掉,所以最好把await放入try{}catch{}中,catch能够捕捉到Promise对象rejected的数据或者抛出的异常
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模拟出错,返回error
});
}
async function asyncPrint(ms) {
try {
console.log('start');
await timeout(ms); //这里返回了错误
console.log('end'); //所以这句代码不会被执行了
} catch(err) {
console.log(err); //这里捕捉到错误error
}
}
asyncPrint(1000);
19、原型,原型链
// 访问一个对象的属性时, 如果从这个对象里找不到, 就从obj.__proto__里找, 再找不到就继续从obj.proto.__proto__里找, 最终会到达Object.prototype
// js中每个对象都有一个_proto_属性指向这个对象构造函数的prototype对象,这个prototype对象也有一个_proto_属性,因此跟链条一样子子孙孙无穷尽也~当然他们的根是null
// 当试图查找一个对象的某个属性时,不仅仅会在对象上查找,还会查找对象的原型,以及对象原型上的原型,层层向上,直到查找到第一个名称相同的属性或者到达一个空的原型,这个就是javascript的原型链机制。其中每一层的原型所构成的这条查找链就是原型链。
// 既然 Object.prototype.proto === null, 那么访问对象的不可达属性时,为什么是undefined 而不是报错 Cannot read property 'name' of null呢?
// 你访问的对象是存在的,在访问属性的时候,如果对象本身不存在这个属性,会往上找原型链上存不存在。最后不管找不着得到,只是属性不存在,而非访问了null的一个属性,所以不会报错。
//更改原型
// Object.setPrototypeOf(friend, dog);
// console.log(friend.getGreeting()); // "Wang"
// console.log(Object.getPrototypeOf(friend) === dog); /
20、proto prototype constructor
function Foo() {...}; //构造函数
let f1 = new Foo(); //实例化对象
①__proto__和constructor属性是对象所独有的;
② prototype属性是函数所独有的。
但是由于JS中函数也是一种对象,所以函数也拥有__proto__和constructor属性,这点是致使我们产生困惑的很大原因之一。
对象的construtor指向的是对象的构造函数
对象的 __proto__指向的是构造函数的原型对象 也就是Foo的prototype
21、闭包
两个函数 内函数使用了外函数的变量,外函数反回了内函数的引用,就形成了一个闭包
函数 A 内部return一个函数 B,函数 B 可以访问到函数 A 中的a变量,那么函数 B 就是闭包。
能访问父函数的变量并且能立即执行或者被直接return出来的函数称为 闭包。
- 作用:闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁
function f1(){
n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
内存泄漏
指的是在程序运行时,分配的内存空间没有被正确释放,导致内存无法再次使用的情况。这种情况下,程序会持续占用内存资源,最终导致系统性能下降甚至崩溃。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //The Window
内存泄漏(函数中获取一个dom,给这个dom绑定事件的时候会导致内存泄漏)
function showId() {
var btn = document.getElementById('btn');
btn.onclick = function () {
alert(btn.id);
}
// ... 其他逻辑 ...
// 当不再需要调用 showId 函数时,解除事件绑定
btn.onclick = null;
}
22、class
/ 类声明
class Person {}
// 类表达式
const TestPerson = class {}
函数声明和类声明之间的一个重要区别在于,函数声明会提升,类声明不会。需要先声明类,然后再访问它,否则就会出现ReferenceError
函数受函数作用域限制,而类受块作用域限制
class Person {
constructor (x, y) {
this.text = new Number(1); //每个对象会实例化一遍,对象之间不共享
this.x = x
this.y = y
this.getText = () => {console.log(this.text)}
}
toString () {
console.log(`${this.x}, ${this.y}`)
}
}
getter 与 setter
在 class 内部可以使用 get 与 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class Person {
constructor (test) {
this.test = test || '默认值'
}
get prop () {
return this.test
}
set prop (value) {
console.log(`setter prop value: ${value}`)
this.test = value
}
}
const p = new Person('1')
p.prop // 1
p.prop = '2' // setter prop value: 2
p.prop // 2
set函数和get函数是设置在属性的 Descriptor 对象上的,可以通过 Object.getOwnPrototyDescriptor 来获取指定属性的指定描述对象。
const descriptor = Object.getOwnPropertyDescriptor(Person.prototype, 'prop')
'get' in descriptor // true
'set' in descriptor // true
23、vue双向绑定原理
vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,通过getter和setter对数据进行监视,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
实现一个监听者Oberver来劫持并监听所有的属性,一旦有属性发生变化就通知订阅者
实现一个订阅者watcher来接受属性变化的通知并执行相应的方法,从而更新视图
实现一个解析器compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相对应的订阅者。
Observe实现数据劫持,递归给对象属性,绑定setter和getter函数,属性改变时,通知订阅者;
Compile解析模板,把模板中变量换成数据,绑定更新函数,添加订阅者,收到通知就执行更新函数;
Watcher作为Observe和Compile中间的桥梁,订阅Observe属性变化的消息,触发Compile更新函数;
- 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
- 实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
- 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
//创建vue类
class Vue {
//执行初始化
constructor(obj_instance) {
this.$data = obj_instance.data;
//调用Observe - 对data中的每个数据进行数据劫持
//对data中的每一项进行响应式处理
Observer(this.$data);
//解析模板
Compile(obj_instance.el, this);
}
}
//数据劫持 - 监听实例中的数据
function Observer(data_instance) {
//递归出口
if (!data_instance || typeof data_instance !== 'object') return;
//创建订阅者实例
const dependency = new Dependency()
//object.keys以数组形式返回对象中的属性
// console.log(Object.keys(data_instance));
//遍历属性属性,通过obj.defineProperty来进行数据监视
Object.keys(data_instance).forEach((key) => {
let value = data_instance[key];
//递归将 子属性的值进行数据劫持
Observer(value);
//三个参数,(对象, 监视的属性, 回调)
Object.defineProperty(data_instance, key, {
//可以枚举 属性描述符可以改变
enumerable: true,
configurable: true,
//通通过getter 和 setter函数进行数据监视
get() {
//访问属性时候 调用getter函数 返回return 值
console.log(`访问了属性:${key} -> 值为${value}`);
// console.log(Dependency.temp);
//将订阅者实例添加到订阅者数组中
Dependency.temp && dependency.addSub(Dependency.temp)
return value;
},
//修改的新属性值
set(newValue) {
console.log(`将属性:${key}的值${value} 修改为->${newValue}`);
value = newValue;
Observer(newValue);
dependency.notify()
},
});
});
}
//HTML模板解析 - {{}}替换dom
function Compile(element, vm) {
//获取id为app的dom元素 绑定到vm.$el上
vm.$el = document.querySelector(element);
// console.log(vm.$el);
//创建文档碎片节点 临时存储数据的改变 避免过频繁地操作dom 文档片段存储在于内存中不在dom中,元素的改变不会引起页面的回流
const fragment = document.createDocumentFragment();
//循环将vm.$el中的dom元素 插入到fragment文档碎片中
let child;
while ((child = vm.$el.firstChild)) {
//使用fragment.append会将原先dom删除
fragment.append(child);
}
// console.log(fragment);
// console.log(fragment.childNodes);
//要将{{}}替换 所以节点类型为 1 和 3为h3
fragment_compile(fragment);
//替换文档碎片内容
function fragment_compile(node) {
//正则匹配 {{ 属性 }}
const pattern = /{{\s*(\S+)s*}}/;
//如果节点为文本节点
if (node.nodeType === 3) {
const temp = node.nodeValue
//输出正则验证过后 去除换行符等一些不需要的元素 返回的数组 "{{ name }}" "name" 需要索引为1的值 不需要{{}}
const result_regex = pattern.exec(node.nodeValue);
// console.log(result_regex);
if (result_regex) {
// console.log(vm.$data[result_regex[1]]);
const arr = result_regex[1].split('.');
//reduce迭代累加器 遍历arr数组 total[current] 不断地迭代的链式获取最终的值 ,reduce两个参数 , 第一个参数是个回调函数,第二参数vm.$data是初始值,total的初始值
const value = arr.reduce(
(total, current) => total[current],
vm.$data
);
//将 {{name}} {{more.age}} 替换成value
node.nodeValue = temp.replace(pattern, value);
//文档碎片替换的时候添加创建订阅者
new Watcher(vm, result_regex[1], newValue => {
//wacther的回调函数 会将文档碎片中的nodevalue更新为我们修改的newValue
node.nodeValue = temp.replace(pattern, newValue);
});
}
return;
}
//找v-model属性的元素 更改其nodeValue
if(node.nodeType === 1 && node.nodeName === 'INPUT'){
const attr = Array.from(node.attributes)
attr.forEach(item => {
if(item.nodeName === 'v-model'){
console.log(item.nodeValue);
//修改nodeValue
const value = item.nodeValue.split('.').reduce((total, current) => total[current], vm.$data)
// console.log(value);
node.value = value
//创建watcher实例
new Watcher(vm, item.nodeValue, newValue => {
node.value = newValue
})
//触发input事件来通过视图修改数据
node.addEventListener('input', e => {
const arr1 = item.nodeValue.split('.')
// console.log(arr1);
const arr2 = arr1.slice(0, arr1.length - 1)
const final = arr2.reduce((total, current) => total[current], vm.$data)
// console.log(final);
final[arr1[arr1.length - 1]] = e.target.value
})
}
})
}
//递归遍历
node.childNodes.forEach((child) => fragment_compile(child));
}
//将文档碎片 fragment渲染到el中
vm.$el.appendChild(fragment);
}
//依赖 --收集和通知订阅者
class Dependency {
constructor() {
//收集订阅者
this.subscribers = [];
}
//添加订阅者
addSub(sub) {
this.subscribers.push(sub);
}
//通知订阅者
notify() {
//遍历订阅者 让订阅者触发自己的update函数
this.subscribers.forEach((sub) => sub.update());
}
}
//订阅者
class Watcher {
//三个参数
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback;
//临时属性 --触发getter
//因为想要将watcher实例添加到依赖的数组中
Dependency.temp = this;
//触发getter时候 将订阅者实例添加到订阅者数组中
key.split('.').reduce((total, current) => total[current], vm.$data )
//避免多次重复添加到订阅者数组中
Dependency.temp = null
}
//更新函数
update() {
//获取属性值
const value = this.key.split('.').reduce((total, current) => total[current], this.vm.$data )
this.callback(value);
}
}
订阅者-发布者模式
- 当数据修改时需要通知订阅者,触发自己的update更新函数来更新视图
会产生一定的性能损耗,若定义的变量只是静态显示,不需要为响应式,可以在data之外定义变量,并通过$options获取。
在data外面定义的属性和方法通过$options可以获取和调用
name: "CSDN",
age: 12,
testMethod() {
console.log("shq5785");
},
created() {
console.log(this.$options.name); // CSDN
console.log(this.$options.age); //12
this.$options.testMethod(); // shq5785
}
24、proxy
语法:
const target = {};
const handler = {};
const proxy = new Proxy(target, handler);
拦截器函数
handler 对象中的属性可以用来拦截对象的底层操作。它包括以下拦截器函数:
- get(target, property, receiver):拦截属性的读取操作。
- set(target, property, value, receiver):拦截属性的赋值操作。
- deleteProperty(target, property):拦截属性的删除操作。
- apply(target, thisArg, argumentsList):拦截函数的调用操作。
- construct(target, argumentsList, newTarget):拦截类的构造函数调用操作。
const data = { name: "Tom", age: 18 };
const handler = {
get(target, property) {
console.log(`Get ${property}`);
return target[property];
},
set(target, property, value) {
console.log(`Set ${property} to ${value}`);
target[property] = value;
},
};
const proxy = new Proxy(data, handler);
proxy.name = "Jerry"; // 输出 "Set name to Jerry"
console.log(proxy.age); // 输出 "Get age" 和 "18"
vue2.0 用 Object.defineProperty 作为响应式原理的实现,但是会有它的局限性,比如 无法监听数组基于下标的修改,不支持 SEV 等缺陷 ,所以改用了proxy解决了这些问题,这也意味着vue3.0将放弃对低版本浏览器的兼容(兼容版本ie11以上)。
25、vu3 vue2 区别
- 使用了 Proxy 对象来代替了 Object.defineProperty,从而提升了性能。
Object.defineProperty()无法扩展属性和删除属性,也不能对新添加的属性进行响应化,同时在大规模数据变化时,这种方法也会带来性能问题。
Vue2使⽤的是Object.defineProperty()进⾏数据劫持,结合发布订阅的⽅式实现。
Vue3使⽤的是Proxy代理,使⽤ref或者reactive将数据转化为响应式数据
- Vue 3 引入了一个全新的 API,称为 Composition API(组合式 API),它可以让我们更加灵活地组织组件代码,使用逻辑复用和代码共享等高级特性,提高代码可读性和可维护性。
Vue2使⽤的是选项类型API(Options API),Vue3使⽤的是组合式API(Composition API)
- 生命周期钩子函数不同
- 支持多根节点
<template>
<header>...</header>
<main>...</main>
<footer>...</footer>
</template>
5、移除了一些api 比如 过滤器 v-once等
6、vue3 引用组件不用 在componcent 中注册,可以直接使用
真实的项目中踩过坑,若想在 setup 中调用异步请求,需在 setup 前加async关键字。这时,会受到警告async setup() is used without a suspense boundary。
解决方案:在父页面调用当前组件外包裹一层Suspense组件。
26、new 一个对象都做了什么
- 创建一个空的简单JavaScript对象。
- 将这个新对象的__proto__属性设置为构造函数的原型对象。这样,新对象就可以访问构造函数原型对象上的属性和方法。
- 将构造函数的this关键字绑定到新对象上。这样,在构造函数内部,this关键字就指向了这个新对象。
- 如果构造函数没有返回其它对象,则返回这个新对象
27、pinia
使用:
import { defineStore } from 'pinia'
import dt from '@dt/dt'
const key_house = 'house_info'
export const houseStore = defineStore('house', {
state: () => {
return {
info: dt.session.get(key_house)
}
},
getters: {
name: (state) => {
return state.info?.houseName
},
id: (state) => {
return state.info?.id ?? 0
}
},
actions: {
choice(item) {
dt.session.set(key_house, item)
this.info = item
}
}
})
defineStore 接收两个参数
第一个参数就是模块的名称,必须是唯一的,多个模块不能重名,Pinia 会把所有的模块都挂载到根容器上
第二个参数是一个对象,里面的选项和 Vuex 差不多
使用:
<template>
<div>{{ user_store.count }}</div>
</template>
<script lang="ts" setup>
import { userStore } from '../store'
const user_store = userStore()
user_store.count
</script>
getter使用方法
getters: {
// 方法一,接收一个可选参数 state
myCount(state){
console.log('调用了') // 页面中使用了三次,这里只会执行一次,然后缓存起来了
return state.count + 1
},
// 方法二,不传参数,使用 this
// 但是必须指定函数返回值的类型,否则类型推导不出来
myCount(): number{
return this.count + 1
}
}
Pinia持久化
安装组件持久化 pinia-persistedstate-plugin
修改state数据的5种方法
import {userStore} from '../store'
const Test = userStore()
// 1直接修改
Test.count = 1
// 2 使用$patch进行批量修改
Test.$patch({
count:2,
name:'lisi'
})
// 3使用$patch 中函数进行修改
Test.$patch((state)=>{
state.count = 3
state.name = 'wangwu'
})
// 4 使用$state 进行修改,缺点是必须是覆盖整个state
Test.$state={
count:2,
name:'lisi'
}
// 5是用action进行修改
// addCont 是action中的函数,也可以接受参数
Test.addCont(1)
28、TS
1、number 类型表示数值类型。
let num: number = 10;
2、string 类型表示字符串类型、
let str: string = "Hello";
3、boolean 类型表示布尔类型。
let bool: boolean = true;
4、null 类型表示空值类型。
let n: null = null;
5、undefined 类型表示未定义类型。
let u: undefined = undefined;
6、void 类型表示没有返回值的函数类型。
JS中的有些函数只有功能没有返回值,此时使用void进行返回值注解,明确表示函数没有函数值
- 在JS中如果没有返回值,默认返回的是undefined
- 在TS中 void和undefined不是一回事:
- a.undefined在TS中是一种明确的简单类型,如果指定返回值为undefined,那返回值必须是undefined类型
****b.void表示没有返回值
function sayHello(): void {
console.log("Hello");
}
7、any 类型表示任意类型,可以对其赋予任何值。
let anyType: any = "Any value can be assigned here";
两种隐式 any 类型的情况
- 声明变量不提供类型也不提供默认值
- 函数参数不加类型
8、never 类型表示永远不会出现的值的类型。
function throwError(): never {
throw new Error("This function never returns");
}
9、Array 类型表示数组类型。
let arr: number[] = [1, 2, 3];
let arr: Array<number> = [1, 2, 3];
联合类型
let arr: Array<number> = [1, 2, 3];
类型注解重复的问题: (number | string)[] 这个类型出现了多次, 怎么优化?
类型别名
定义: type 别名 = 类型
别名可以是任意的合法字符串,一般首字母大写
type MyArr = (number | string) []
const arr1:MyArr = [1, '1']
const arr2:MyArr = ['a', 'b']
10、Tuple 类型表示元组类型。 是一个固定长度且元素类型确定的数组。
let tuple: [string, number] = ["hello", 42];
11、Enum 类型表示枚举类型。可以为一组有名字的常量赋予一个更友好的名字。
enum Color {
Red,
Green,
Blue,
}
let color: Color = Color.Red;
12、Interface 类型表示接口类型。用于描述对象的形状,包括属性名、属性类型、方法等。
用interface来描述对象数据的类型(常用于给对象的属性和方法添加类型约束)
一旦注解接口类型之后对象的属性和方法类型都需要满足要求,属性不能多也不能少
interface Person {
name: string;
age: number;
}
let person: Person = {
name: "Alice",
age: 25,
};
可选设置 概念: 通过 ? 对属性进行可选标注,赋值的时候该属性可以缺失,如果有值必须保证类型满足要求。
interface goodItem {
id: number;
name: string;
price: number;
imgUrl?: string; // 可选的
}
const goodList: goodItem[] = [
{id:1, name: '手机', price: 2999, imgUrl: 'http://w.g1.png'},
{id:1, name: '毛巾', price: 9} // 这里也是正确的
]
接口的继承
概念:接口的很多属性是可以进行类型复用的,使用 extends 实现接口继承,实现类型复用。
/ 正常的商品
interface goodItem {
id: number;
name: string;
price: number;
imgUrl?: string;
}
// 打折的商品:正常商品 + newPrice + effectDate
interface goodItemDiscount {
id: number;
name: string;
price: number;
imgUrl?: string;
newPrice: number;
effectDate: Date
}
interface goodItemDiscount extends goodItem {
newPrice: number;
effectDate: Date
}
13、Class 类型表示类类型。用于定义类的结构和实例方法。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
walk() {
console.log(`${this.name} is walking`);
}
}
let animal: Animal = new Animal("Dog");
animal.walk();
14、Function 类型表示函数类型。包括参数类型和返回值类型等信息。
函数类型是指给函数添加类型注解,本质上就是给函数的参数和返回值添加类型约束
function add(x: number, y: number): number {
return x + y;
}
let result: number = add(3, 4);
type AddFn = (a: number, b: number )=> number
const add1:AddFn = (a, b) => {
return a + b
}
可选参数 表示可传可不传 ,, “可选参数一定要在参数列表的末尾”
function add2(x: number, y?: number): number {
if(y){
return x + y;
}else{
return x
}
}
let result: number = add(3, 4);
- 函数参数注解类型之后不但限制了参数的类型还限制了参数为必填
- 函数返回值注解类型之后限制了该函数内部return出去的值必须满足类型要求
type注解对象类型
注解对象
概念:在TS中对于对象数据的类型注解,除了使用interface之外还可以使用类型别名来进行注解,作用相似
type Person = {
name:string,
age:number
}
const p:Person = {
name:'zjz',
age:18
}
type + 交叉类型模拟继承
类型别名配合交叉类型(&)可以模拟继承,同样可以实现类型复用
父接口
type Father = {
name:string,
age:18
}
子接口继承
type Son = Father & {
height:number,
weight:number
}
const p:Son = {
name:'zjz',
age:18,
height:177,
weight:70
}
interface 对比 type
相同点
- 都能描述对象类型
- 都能实现继承,interface使用extends, type配合交叉类型
不同点
- type除了能描述对象还可以用来自定义其他类型
- 同名的interface会合并(属性取并集,不能出现类型冲突)
interface goods = {
name:string
}
interface goods = {
price:number
}
const g:goods = {
name:'篮球',
price:400
}
- 同名的type会报错
字面量类型
let str1 = 'abc'
const str2 = 'abc'
通过 TS 类型推论机制,可以得到答案:
变量 str1 的类型为:string
变量 str2 的类型为:‘abc’
解释:
str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string
str2 是一个常量(const),它的值不能变化只能是 ‘abc’,所以,它的类型为:‘abc’
注意:此处的 ‘abc’,就是一个字面量类型,也就是说某个特定的字符串也可以作为 TS 中的类型,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用
类型断言
有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言(as)来指定更具体的类型
应用场景:把一个大类型 缩小 为更加具体的类型
const alink1:HTMLAnchorElement
const alink1 = document.getElementById('link') as HTMLAnchorElement
tips:
HTMLElement 是表示 HTML 元素的类型。它是 TypeScript 标准库中定义的一个接口,用于描述任意 HTML 元素的通用属性和方法。
const element: HTMLElement = document.getElementById("my-element");
element.innerHTML = "Hello, world!";
注意,HTMLElement 只描述了 HTML 元素的通用属性和方法,而没有描述特定类型的 HTML 元素的属性和方法。
相比之下,HTMLAnchorElement 是 HTMLElement 的子类,用于描述 <a> 标签元素的属性和方法。例如:
const link: HTMLAnchorElement = document.createElement("a");
link.href = "https://www.example.com";
link.textContent = "Click me!";
HTMLAnchorElement 在继承 HTMLElement 的基础上,还增加了一些与 <a> 标签相关的属性和方法,例如 href、target、download 等等。
需要注意的是,在处理 DOM 元素时,我们通常无需显式地指定元素的类型,TypeScript 会自动推断出元素的类型(例如根据元素的 tagName 属性)。而当我们需要操作元素的特定属性或方法时,可以使用相应的类型来获得更好的类型检查和代码提示。
发请求拿数据的过程中有一种典型的场景:我们提前知道了后端返回的数据的类型,也定义了个变量用来保存数据,把他的初值为{}。但是,下面的代码会报错
type Res ={
total:number,
list:object[]
}
const result:Res = {} //因为缺少属性会报错
const result:Res = {} as Res //这样写就好了
泛型
后端接口给前端返回数据时,一般会遵循一定的规律,例如:都有code, msg, data三个属性,而不同的接口返回的具体数据保存在data中,它的格式是不同的。
// 请求得到用户信息
const res1 = {code: 200, msg: 'success', data: {name: 'jack', age: 18}}
// 请求得到商品信息
const res2 = {code: 200, msg: 'success', data: [{id: 1001, goodsName: '衬衣'}}]
//定义两个 接口会有大量的代码重复
interface Res1 {
code: number,
msg: string,
data: {
name: string,
age: number
}
}
interface Res2 {
code: number,
msg: string,
data: {
goodsName: string,
id: number
}[]
}
const res1: Res1 = {}//
const res2: Res2 = {}//
//加上泛型
interface Res1<T> {
code: number,
msg: string,
data: T
}
const res1: Res1<{name: string, age: number}> = { code:10,msg:'123',data:{name: '小花',age:18} }
const res2: Res1<{goodsName: string, id: number}[]> = { code:10,msg:'123',data:[{goodsName: '小花',id:18}] }
泛型(Generics)是指在定义接口、函数等类型的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性**,** 使用泛型可以复用类型并且让类型更加灵活。
string number boolean null undefind
array 元组 function class emum
interface void any never
29、vite webpack
webpack
5个核心概念
- entry (入口) webpack从哪个文件开始
- output (出口) webpack打包完的文件输出到哪里去,如何命名等
- loader (加载器) webpack本身只能处理js,json等资源,其他资源需要借助loader,webpack才能解析
- plugins(插件) 扩展webpack的功能
- mode(模式)
主要有两种模式
开发模式:development
生产模式:production
Webpack提供的基本功能有哪些
目前,Webpack支持如下一些常见的功能(具体视版本而定):
-
- 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
- 文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
- 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
- 自动刷新:监听本地源代码的变化、自动构建、刷新浏览器
- 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
- 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
webpack打包流程
1、从入口(entry) 开始,递归转换入口文件所依赖的module
2、每找到一个module,就根据对应的loader去转换这个module
3、然后,再对当前module依赖的所有module进行转换,如果子module还有依赖的话,再转换,直至没有依赖
4、其次,根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk
5、最后,Webpack会把所有Chunk转换成文件输出,在整个流程中Webpack会在恰当的时机执行plugin里定义的扩展插件。
loader
Webpack默认只支持识别JS和JSON
Loader用于在Webpack构建过程中对模块的源代码进行转换。它们可以将不同类型的文件(如JavaScript、CSS、图片等)转换为模块,或者在加载模块时对其进行预处理。Loader通常作为规则配置的一部分,在Webpack配置文件中进行配置。
- 编译转换:例如,Babel Loader可以将ES6/ES7的代码转换为ES5以供旧版本浏览器使用。
- 文件处理:例如,File Loader可以将文件复制到输出目录,并返回文件路径作为模块引入的结果。
- 样式处理:例如,CSS Loader可以解析CSS文件,并将其转换为JS模块,然后通过Style Loader将其注入到页面中。
less-loader : 用于将 less 编译成 css
sass-loader:
css-loader: 用于将 css 以 CommonJs 语法打包到 JS 中 配合 style-loader共同使用style-loader: 用于动态创建 style 标签,负责把包含 css 内容的 js 代码挂载到页面的 style 标签当中 但是这样子打包出来的文件 css 样式和结构糅杂在一起 可以用 mini-css-extract-plugin 插件来将 css 提取为单独的文件
postcss-loader:
用于补充css样式各种浏览器内核前缀,用于处理css兼容问题,需要和postcss、postcss-preset-env配合使用。
备注1:使用的规则为:[“css-loader”,“postcss-loader”,“less-loader”]
备注2:需要在package.json中配置browserslist属性指定具体的兼容规则
备注3:browserslist是一个单独的库,被广泛用在各种涉及浏览器/移动端的兼容性支持工具中
babel-loader:
将Es6+ 语法转换为Es5语法(只能转换简单的es6+语法)
babel-loader 这是使babel和webpack协同工作的模块
@bable/core 这是babel编译器核心模块
@babel/preset-env 这是babel官方推荐的预置器,可根据用户的环境自动添加所需的插件和补丁来编译Es6代码;
备注1:直接使用只能处理简单的语法,Promise等无法处理。
备注2:借助polyfill完成高级es6语法的转换,缺点:所有都转换,无法按需转换,生成的js体积大。
备注3:生成的js体积大使用core-js配合polyfill完成按需转换。
ts-loader: 用于配置项目 typescript
html-loader: 将 HTML 导出为字符串。当编译器需要时,将压缩 HTML 字符串。
file-loader: 用于处理文件类型资源 常用于打包 jpg、png 等图片
url-loader: 将文件作为 data URI 内联到 bundle 中
raw-loader: 将文件导入为字符串
eslint-loader: 用于检查代码是否符合规范,是否存在语法错误
module.exports = {
// ...
module: {
rules: [
{
test: /.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsxSuffixTo: [/.vue$/],
}
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.vue'],
},
};
test表示需要处理的文件类型,以正则表达式形式给出loader表示使用的 loaderexclude表示排除某些文件或文件夹options表示 loader 的配置选项,此处是给.vue文件添加.tsx后缀名
Plugin
plugin用于扩展Webpack的功能,执行一些特定的任务,例如打包优化、资源管理、环境变量注入等。它们可以在整个构建过程中监听Webpack的事件,并在特定时机执行自定义的操作。Plugin是通过在Webpack配置文件中实例化并添加到plugins字段中来进行配置。
- 优化:例如,UglifyJsPlugin可以压缩和混淆JS代码,提高页面加载速度。
- 资源管理:例如,HtmlWebpackPlugin可以根据模板生成HTML文件,并自动将打包后的资源注入到HTML中。
- 环境变量注入:例如,DefinePlugin可以在编译过程中定义全局的变量,用于配置开发环境和生产环境的不同行为。
html-webpack-plugin: 自动打包创建html文件(或者指定 html 模板),并且引入外部资源如 js css等
mini-css-extract-plugin: 将 CSS 提取为独立的文件的插件,对每个包含 css 的 js 文件都会创建一个 CSS 文件,支持按需加载 css 和 sourceMap。
css-minimizer-webpack-plugin: 用于压缩css,减小 css 打包后的体积
eslint-webpack-plugin: 该插件使用 eslint 来查找和修复 JavaScript 代码中的问题。校验 js 格式
clean-webpack-plugin: 自动删除已经打包过的文件 这样每次重新打包的时候就不用手动删除文件夹了
copy-webpack-plugin: 用于打包静态文件
stylelint-webpack-plugin: 校验 css 格式
Webpack 性能优化措施
- 1有哪些方式可以减少
Webpack的打包时间? - 有哪些方式可以让
Webpack打出来的包更小?
优化 Loader
对于 Loader 来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,然后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。抽象语法树,简称AST
1、 优化Loader,首先可以优化 Loader 文件搜索范围
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /.js$/,
loader: 'babel-loader',
// 只在 src 文件夹下查找
include: [resolve('src')],
// 不会去查找的路径
exclude: /node_modules/
}
]
}
}
2、DllPlugin
DllPlugin 可以将特定的类库提前打包然后引入。
// webpack.conf.js
module.exports = {
// ...省略其他配置
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
// manifest 就是之前打包出来的 json 文件
manifest: require('./dist/vendor-manifest.json'),
})
]
}
3、代码压缩 ****通过配置实现比如删除 console.log 这类代码的功能。
4、 resolve.alias :可以通过别名的方式来映射一个路径,能让 Webpack 更快找到路径。
减少 Webpack 打包后的文件体积
1、按需加载
- vite服务启动更快
30、网络攻击
1. SQL 注入(SQL Injection):
- 防范手段:
-
- 使用参数化查询或预编译语句来执行 SQL 查询,而不是拼接字符串。
- 对用户输入进行严格的验证和过滤,尤其是在构造 SQL 查询语句时。
- 最小化数据库用户的权限,避免使用超级用户权限执行查询操作。
2. 跨站脚本攻击(XSS):
- 防范手段:
-
- 对用户输入进行适当的转义,确保不会被当作 HTML 或 JavaScript 代码执行。
- 使用 Content Security Policy(CSP)来限制页面可以加载的资源,并减少 XSS 攻击的可能性。
- 使用 HttpOnly 标记来设置 cookie,防止 JavaScript 访问敏感信息。
3. 跨站请求伪造(CSRF):
- 防范手段:
-
- 在关键操作(如修改数据、提交表单)中使用随机生成的 CSRF 令牌,并验证这个令牌的有效性。
- 确保使用 POST 请求而不是 GET 请求执行敏感操作。
- 限制敏感操作的访问权限,例如需要登录或者使用多因素认证。
4. 点击劫持(Clickjacking):
- 防范手段:
-
- 使用 X-Frame-Options 头部来防止网页被嵌入到
<frame>、<iframe>或<object>中。 - 使用 JavaScript 来检测页面是否被嵌入到框架中,并采取相应的防御措施。
- 使用 X-Frame-Options 头部来防止网页被嵌入到
5. 其他安全问题:
- 文件上传漏洞: 对上传的文件进行严格的类型和大小限制,并在服务端对上传的文件进行合法性检查。
- 会话管理: 使用安全的会话管理机制,包括合适的会话过期时间、会话固定攻击的防范等。
-
CSRF攻击
CSRF即Cross-site request forgery(跨站请求伪造)
- 第一步,用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
- 第二步,在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
- 第三步, 用户未退出网站A之前,在同一浏览器中,打开一个新的tab页访问网站B;
- 第四步, 网站B接收到用户请求后,返回一些攻击性代码,通过这个代码,在用户不知情的情况下携带Cookie信息,向网站A发出请求,比如说这个请求是获取用户信息的。
- 第五步,网站A根据cookie,会认为是用户C发送的该请求,从而导致该请求被执行,进行给用户C造成个人信息或财产的损失。
防御:
1.验证 HTTP Referer 字段:由于是从其它网站发送的请求,所以可以通过验证请求头的Origin、Referer来验证发送请求的站点。(验证请求头)
2.设置http-only:true,禁止cookie被浏览器通过js访问到
3.验证Token:浏览器发送请求时,携带token,因为token存放在sessionStorage中,不会被其它网站所窃取到。
-
XSS攻击
XSS即Cross Site Scripting(跨站脚本攻击),攻击者通过各种方式将恶意代码注入到用户的页面中,这样就可以通过脚本进行一些操作。
例如:在评论区植入JS代码,使得页面被植入广告
XSS攻击有哪些类型
1.存储型XSS攻击:也叫持久型XSS,主要将恶意代码提交存储在服务器端,当目标用户访问该页面获取数据时,恶意代码会从服务器返回到浏览器做正常的HTML和JS解析执行,这样XSS攻击就发生了。
存储型 XSS 一般出现在网站留言、评论等交互处。
2.反射型XSS攻击:一般是攻击者通过特定手法(如电子邮件),诱使用户去访问一个包含恶意代码的 URL,当用户点击后,恶意代码会直接在用户的浏览器执行。
反射型XSS通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端 Cookies 。
3.DOM型XSS攻击:指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。
DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞。
防御:
1.针对存储型XSS攻击,对输入内容的特定字符进行编码
例如对表示 html标记的 < > 等符号进行编码
2.针对反射型XSS攻击,在 cookie中设置 httpOnly:
可以防止客户端通过document.cookie读取 cookie,此 HTTP头由服务端设置。
3.针对反射型XSS攻击,在输出 URL参数之前,要先对URL进行 URLEncode操作
4.开启白名单,阻止白名单以外的资源的加载和运行
5.后端接口也需要对关键字符进行过滤。
- 标签过滤,如
- 编码,对字符< 、>、&、" 、' 、+、/等进行转义。
- cookie防盗,将cookie设置为http-only,js脚本将无法读取到cookie信息。
- 纯前端渲染,明确innerText、setAttribute、style,将代码与数据分隔开。
- 避免不可信的数据拼接到字符串中传递给这些API,如DOM中的内联事件监听器,location、onclick、onload、onmouseover等,标签的href属性,JavaScript的eval()、setTimeout()、setInterval()等,都能把字符串作为代码运行。
3、点击挟持
ClickJacking点击劫持当访问某网站时,利用CSS将攻击者实际想让你点击的页面进行透明化隐藏,然后在页面后显示 一些东西诱导让你点击,点击后则会在用户毫不知情的情况下做了某些操作,这就是点击劫持ClickJacking。案例:当我们点击弹窗右上角的"X"想关闭弹窗事,跳转到其他页面。
iframe覆盖第三方网站通过iframe内嵌某一个网站,并且将iframe设置为透明不可见,将其覆盖在其他经过伪装的DOM上,伪装的可点击DOM(按钮等)与实际内嵌网站的可点击DOM位置相同,当用户点击伪装的DOM时,实际上点击的是iframe中内嵌的网页的DOM从而触发请求操作。
网络劫持有哪几种,如何防范?
DNS 劫持(输⼊京东被强制跳转到淘宝这就属于 DNS 劫持)
由于涉嫌违法,已经被监管起来,现在很少会有 DNS劫持HTTP 劫持: (访问⾕歌但是⼀直有贪玩蓝⽉的⼴告),由于 http明⽂传输,运营商会修改你的 http 响应内容(即加⼴告)
http 劫持依然⾮常盛⾏,最有效的办法就是全站 HTTPS,将HTTP 加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
token为什么就比cookie安全
- cookie是什么
登陆后,后端生成一个sessionid放在cookie中返回给客户端,并且服务端一直记录着这个sessionid,客户端以后每次请求都会自动带上这个sessionid,服务端通过这个sessionid来验证身份之类的操作。所以别人拿到了cookie等于拿到了sessionid,就可以完全替代你。 - token是什么
登陆后,后端会返回一个token给客户端,客户端将这个token存储起来,然后每次客户端请求都需要开发者手动将token放在header中带过去,服务端每次只需要对这个token进行验证就能使用token中的信息来进行下一步操作了。 - 对于xss攻击来说,cookie和token没有什么区别。但是对于csrf来说就有区别
1、对于xss攻击,不管是token还是cookie,都能被拿到,没有什么区别。
2、对于csrf来说就有区别,例子如下:
cookie:在浏览器中,所有的cookie,可以在任意一个标签页中被访问(没有设置http-only为true的情况下)(不论是否跨域)
token:存放在sessionStorage中,仅在浏览器的当前窗口中可以被访问到
31、git
32、跨域
所谓的同源是指,域名、协议、端口均为相同。
www.baidu.com:8080
协议 域名 端口号
Cookie、LocalStorage 和 IndexDB 无法读取DOM 和 JS 对象无法获取Ajax请求发送不出去
opener.document 就是访问父窗口的文档对象。但需要注意的是,跨域访问受到同源策略的限制,如果父窗口和子窗口不属于同一域名、协议、端口,则无法访问父窗口的内容。
-
JSONP(JSON with Padding):
JSONP 是一种利用 <script> 标签可以跨域加载资源的特性来实现跨域请求的方法。服务端返回的数据需要包裹在一个函数调用中,客户端通过动态创建 <script> 标签来加载数据,从而绕过同源策略的限制。jsonp跨域其实也是JavaScript设计模式中的一种代理模式。在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器允许的,所以我们可以通过这个“犯罪漏洞”来进行跨域。一般,我们可以动态的创建script标签,再去请求一个带参网址来实现跨域通信
优势:轻量,兼容性高,不需要XMLHttpRequest
缺点:
- 只支持get,不支持post
- 存在注入恶意脚本的风险
- 接口异常无法监听
-
CORS(Cross-Origin Resource Sharing):
CORS 是一种现代浏览器机制,通过在服务端设置响应头来实现跨域资源共享。服务端需要设置 Access-Control-Allow-Origin 头部字段,允许指定的域名访问资源。可以设置为具体的域名或通配符 *,表示允许所有域名访问。
所有都可以访问
指定可以访问
优点:操作简单、支持所有http请求类型(get、post、put)等
缺点:IE8以下浏览器不支持
-
代理服务器:
可以设置一个代理服务器,将客户端的请求转发到目标服务器,并将响应返回给客户端。由于代理服务器与目标服务器之间的请求是同源的,因此不会受到浏览器的同源策略限制。
本地开发:
部署线上
- postMessage
- WebSocket:WebSocket 是一种基于 TCP 的协议,可以实现双向通信。WebSocket 连接不受同源策略的限制,可以直接与目标服务器通信。
33、HTTPS和HTTP的区别
HTTP是超文本传输协议,它是一种用于传输数据的基本协议,但它不提供任何数据加密或身份验证功能。这意味着HTTP传输的数据可以被窃听或篡改,因此不适合传输敏感数据,如信用卡号码、密码等。
而HTTPS是基于HTTP的安全协议,通过在HTTP之上添加SSL/TLS加密来保证数据的安全性。HTTPS通过使用数字证书对Web服务器进行身份验证,并使用公钥加密算法来加密数据传输,以确保数据在传输过程中不被窃听或篡改。这使得HTTPS非常适合传输敏感数据,如信用卡号码、密码等。
34、kee-alive
kee-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染 。也就是所谓的组件缓存。
例如当列表页面进行了大量的筛选条件,点进去详情页面时,再回到列表页面需要保存上次的筛选
基本用法
<keep-alive>
<component />
</keep-alive>
被keep-alive包含的组件不会被再次初始化,也就意味着不会重走生命周期函数
但是有时候是希望我们缓存的组件可以能够再次进行渲染,这时Vue为我们解决了这个问题
被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
activated当keep-alive包含的组件再次渲染的时候触发deactivated当keep-alive包含的组件销毁的时候触发
include (包含的组件缓存生效) 缓存谁
exclude (排除的组件不缓存,优先级大于include ) 不缓存谁
// 只缓存组件name为a或者b的组件
<keep-alive include="a,b">
<component />
</keep-alive>
// 组件name为c的组件不缓存(可以保留它的状态或避免重新渲染)
<keep-alive exclude="c">
<component />
</keep-alive>
//同时使用include,exclude,那么exclude优先于include, 下面的例子只缓存a组件
<keep-alive include="a,b" exclude="b">
<component />
</keep-alive>
// 如果缓存的组件超过了max设定的值5,那么将删除第一个缓存的组件
< keep-alive exclude="c" max="5">
<component />
</keep-alive>
//只有路径匹配到的 name 为 a 组件会被缓存 配合router 使用
<keep-alive include="a">
<router-view></router-view>
</keep-alive>
// routes 配置
export default [
{
path: '/',
name: 'home',
component: Home,
meta: {
keepAlive: true // 需要被缓存
}
},{
path: '/',
name: 'home1',
component: Home1,
meta: {
keepAlive: false// 不需要被缓存
}
},
]
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 这里是会被缓存的视图组件,比如 Home! -->
</router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive">
<!-- 这里是不会被缓存的视图组件,比如 Profile! -->
</router-view>
35、router和route
$router是全局路由的实例 对象上的方法有push(),go(),replace()
$route 是表示当前的路由信息,包含了当前url解析到的信息,包含路径参数
属性有:path,params,query,hash
36、vue3面试题
toRef()
函数基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。
toRef() 函数一次仅能设置一个数据,接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性。
toRefs()
利用toRefs可以将一个响应式 reactive 对象的所有原始属性转换为响应式的ref属性。
普通解构会让数据失去响应式,用toRefs解构
const state = reactive({
title: '标题',
name: 'wff'
})
const { title } = toRefs(state)
toRaw()
(将响应式对象转化为普通对象)
用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
isRef()
isRef 顾名思义它是用来判断某个值是否是 ref,注意:它判断不了这个值是不是 reactive(可以使用 isReactive 判断)
unref()
其实它是一个语法糖
val = isRef(val) ? val.value : val;
shallowRef
与 ref 不同的是只有.value 是响应式的,再深层的属性则不具备响应式
triggerRef
它可以让浅层的 ref 即 shallowRef 深层属性发生改变的时候强制触发更改
const shallowObj = shallowRef({
a: 1
})
const addCount = () => {
shallowObj.value.a++
//加入triggerRef强制触发更改
triggerRef(shallowObj)
}
customRef
Vue 3.0与Vue 2.x相比有哪些新功能?
响应式系统 的重构,使用proxy替换Object.defineProperty属性
defineProperty API 的局限性最大原因是它只能针对单例属性做监听。
Vue2.x中的响应式实现正是基于defineProperty中的descriptor,对 data 中的属性做了遍历 + 递归,为每个属性设置了 getter、setter。 这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因,在Vue中使用下标的方式直接修改属性的值或者添加一个预先不存在的对象属性是无法做到setter监听的,这是defineProperty的局限性。 b. Proxy API的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作, 这就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。
- Proxy可直接监听 对象
添加/删除属性; - Proxy直接监听 数组的变化
- Proxy监听的目标是对象本身,不需要像Object.defineProperty那样遍历每个属性,有一定的性能提升
新增组合式API (Composition API),更好的逻辑重用和代码组织
vue2 如何升级到 vue3
- children被vue3移除,使用children 的需要替换为 $ref。
- filters 被移除,更改为 computed 。
- $destory 被移除,需要删除掉。
- 插槽的新变化。
- Vuex 使用发生改变。
- vue-router 使用发生改变等等。
v-if 和 v-for 的优先级哪个高?
在 vue2 中 v-for 的优先级更高,但是在 vue3 中优先级改变了。v-if 的优先级更高。
在 vue2 中 v-for 和 v-if 同时出现时,可放在一个标签内 在 vue3 中这样写会报错,就是因为 v-if 的优先级更高,写到一个标签渲染的情况下会导致报错
setup() {
const count = ref(0)
function increment() {
// 在 JavaScript 中需要 .value
count.value++
}
// 不要忘记同时暴露 increment 函数
return {
count,
increment
}
setup() {
const count = ref(0)
function increment() {
// 在 JavaScript 中需要 .value
count.value++
}
// 不要忘记同时暴露 increment 函数
return {
count,
increment
}
scrtpt setup 是 vue3 的语法糖,简化了组合式 API 的写法,并且运行性能更好。使用 script setup 语法糖的特点:
- 属性和方法无需返回,可以直接使用。
- 引入组件的时候,会自动注册,无需通过 components 手动注册。
- 使用 defineProps 接收父组件传递的值。
- useAttrs 获取属性,useSlots 获取插槽,defineEmits 获取自定义事件。
- 默认不会对外暴露任何属性,如果有需要可使用 defineExpose 。
使用
ref:
常用:ref()、isRef()、unRef()、toRef()、toRefs()、shallowRef()
注:toRef()、toRefs()用在解构时仍希望数据为响应式时使用
reactive:
常用:reactive()、isReactive()、shallowReactive()、isProxy()、toRaw() readonly
computed:
//写法1:接受一个getter函数,并根据getter的返回值返回一个不可变的响应式ref对象
const a = computed(()=>{})
//写法2:接受一个具有get、set函数的对象,用来创建可写的ref对象
const b = computed({
get: () =>{},
set: (newValue) => {},
})
watch:
const obj = reactive( {a:{b:1}} )
const e = ref('e')
const f = ref('f')
watch( obj, (newValue,oldValue) => {})
//监听对象里的某个属性,必须用getter返回,
即必须写成()=>obj.a.b的形式(直接写成obj.a.b会报错)
watch( ()=>obj.a.b, (newValue,oldValue) => {})
//深拷贝过后,监听的newValue,oldValue才会是前后值不一样,
否则newValue,oldValue打印值一样
watch( ()=>_.cloneDeep(obj), (newValue,oldValue) => {})
//vue3新增的写法,可同时监听多个ref数据,写成数组形式
watch( [e,f], (newValue,oldValue) => {})
const stop = watch(obj,()=>{})
stop() //停止监听
watchEffect:
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数(写在watchEffect里的数据会被收集为其依赖,当这些依赖改变时才会触发watchEffect)
不需要指定要监听的数据,而是会自动追踪函数中使用的响应式数据,并在这些数据发生变化时重新执行回调函数。这种自动追踪的特性可以简化代码,并提高应用的性能。
watchEffect(()=>{
console.log(state.value)
})
const stop = watchEffect(() =>{},{flush:'post'}) //对写在回调函数里的所有数据监听
stop() //停止监听
Teleport / ˈtelɪpɔːrt / 是什么?它的作用是什么?
指定该组件渲染到父组件之外的其它 DOM 节点下
eg:假如有非常多的弹窗,那么如何管理它们的 z-index 呢,也就是同时弹窗时的层级关系,如果每个弹窗都在各自的父组件中,那么我们是没法控制的,所有有必要把它们都拧出来,放在同一个父元素下面,这样就可以方便的设置层级关系了。
<Teleport to="body" :disabled="true">
<p>我是被 teleport 包裹的元素</p>
<p>{{ message }}</p>
</Teleport>
<Teleport to="#customDom">
<p>我是被 teleport 包裹的一号元素</p>
</Teleport>
<Teleport to="#customDom">
<p>我是被 teleport 包裹的二号元素</p>
</Teleport>
- to 允许接收值:
期望接收一个 CSS 选择器字符串或者一个真实的 DOM 节点。
- disabled 属性接收一个 Boolean 值,true 代表不允许传送,false 代表传送)
- 多个 < Teleport > 组件可以将其内容挂载在同一个目标元素上,而顺序就是简单的顺次追加,后挂载的将排在目标元素下更后面的位置上。
Vue3中的Suspense / səˈspens /
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
37、diff 算法详解
38、axios二次封装,环境变量的配置、api的解耦
39、vue-router详解
40、uni-app复习
uview : uni-app的ui框架
: 类似div 一些h5的标签只有在h5里面能识别,在小程序中不可识别
: 类似span,还有文字是否可选,是否解码等属性
滚动
swiper:轮播
有默认宽高
跳转首页
跳转:
uni.navigateTo(OBJECT)
保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面。
//在起始页面跳转到test.vue页面并传递参数
uni.navigateTo({
url: 'test?id=1&name=uniapp'
});
// 在test.vue页面接受参数
export default {
onLoad: function (option) { //option为object类型,上个页面传递的参数
console.log(option.id); //打印出上个页面传递的参数。
console.log(option.name); //打印出上个页面传递的参数。
}
}
- 页面跳转路径有层级限制,不能无限制跳转新页面
- 跳转到 tabBar 页面只能使用 switchTab 跳转
- 路由API的目标页面必须是在pages.json里注册的vue页面。如果想打开web url,在App平台可以使用 plus.runtime.openURL或web-view组件;H5平台使用 window.open;小程序平台使用web-view组件(url需在小程序的联网白名单中)。在hello uni-app中有个组件ulink.vue已对多端进行封装,可参考。
uni.redirectTo(OBJECT)
关闭当前页面,跳转到应用内的某个页面。
需要跳转的应用内非 tabBar 的页面的路径,路径后可以带参数
uni.reLaunch(OBJECT)
关闭所有页面,打开到应用内的某个页面。
需要跳转的应用内页面路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2',如果跳转的页面路径是 tabBar 页面则不能带参数
uni.switchTab(OBJECT)
跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
uni.navigateBack(OBJECT)
关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。
// 在C页面内 navigateBack,将返回A页面
uni.navigateBack({
delta: 2
});
生命周期
应用生命周期
在App.vue中定义
// 应用初始化完成触发,全局只触发一次,可做登录判断...
onLaunch
// 应用启动的时候,或者从后台进入前台就会触发
onShow
// 应用从前台进入后台就会触发
onHide
页面生命周期
page 在页面中都可以定义
// 监听页面加载,在开始加载的时候触发,元素还未开始渲染 -- 页面只执行一次 tab页面不加载
onLoad
// 监听页面显示,每次页面出现的时候,就会触发这个钩子。
onShow
// 监听页面渲染完成, 如果页面渲染速度快(元素太少),会在页面翻页动画完成前就触发了 -- 页面只执行一次
onReady
// 监听页面隐藏,每次页面隐藏的时候,就会触发这个钩子。 页面跳转会触发
onHide
// 监听页面卸载 重定向跳转页面会触发
onUnload
执行顺序
组件没有onLoad 和onShow 有onReady
// 进入应用
App Launch
App Show
component beforeCreate
component created
page onLoad
page onShow
component mounted
page onReady
// 应用后台
App Hide
page onHide
// 应用关闭
page onUnload
component destory
// 后台重新进入
App Show
page onLoad
page onShow
- onInit:页面初始化时调用
- onLoad:页面加载时调用
- onShow:页面显示时调用
- onReady:页面初次渲染完成时调用
- onHide:页面隐藏时调用
- onUnload:页面卸载时调用
- onPullDownRefresh:用户下拉刷新时调用
- onReachBottom:页面上拉触底时调用
- onResize:页面尺寸改变时调用
- onTabItemTap:当前是tab页时,点击tab时调用
执行顺序
1.beforeCreate
2.created
3.onLoad
4.onShow
5.beforeMount
6.onReady
7.mounted
8.beforeUpdate
9.updated
onInit
监听页面初始化,其参数同 onLoad 参数,为上个页面传递的数据,参数类型为 Object(用于页面传参),触发时机早于 onLoad
onLoad 页面级的 vue中的 create 属于组件级
onLoad比较适合的操作是:接受上页的参数,联网取数据,更新data。
监听页面加载,该钩子被调用时,响应式数据、计算属性、方法、侦听器、props、slots 已设置完成,其参数为上个页面传递的数据,参数类型为 Object(用于页面传参),参考示例。
onShow
监听页面显示,页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面
onReady
监听页面初次渲染完成,此时组件已挂载完成,DOM 树($el)已可用,注意如果渲染速度快,会在页面进入动画完成前触发
onHide
监听页面隐藏
onUnload
监听页面卸载
onResize
监听窗口尺寸变化
onPullDownRefresh
监听用户下拉动作,一般用于下拉刷新
onReachBottom
页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据。
onTabItemTap
点击 tab 时触发,参数为Object
onShareAppMessage
用户点击右上角分享
注意点:
1、小程序中不识别this.$route ,不能它来获取传值参数(h5中可以实现)
使用
onload(option){
console.log(option) 来获取参数
}
组件配置不用引入直接使用
"easycom": {
"uni-(.*)": "@/pages/components/uni-$1/uni-$1.vue"
},
41、uni-app 面试题
uniApp中如何进行数据缓存?
异步:
- uni.setStorage(OBJECT)
uni.setStorage({
key: 'storage_key',
data: 'hello',
success: function () {
console.log('success');
}
});
- uni.getStorage(OBJECT)
uni.getStorage({
key: 'storage_key',
success: function (res) {
console.log(res.data);
}
});
同步:
- uni.setStorageSync(KEY,DATA)
uni.setStorageSync('storage_key', 'hello');
- uni.setStorageSync(KEY,DATA)
uni.setStorageSync('storage_key', 'hello');
uniApp中如何实现下拉刷新和上拉加载更多?
可以使用uni.onPullDownRefresh方法实现下拉刷新,使用uni.onReachBottom方法实现上拉加载更多
// 在页面的onPullDownRefresh方法中实现下拉刷新
onPullDownRefresh() {
// 执行刷新操作
console.log('下拉刷新');
// 刷新完成后调用uni.stopPullDownRefresh()方法停止刷新
uni.stopPullDownRefresh();
}
// 在页面的onReachBottom方法中实现上拉加载更多
onReachBottom() {
// 执行加载更多操作
console.log('上拉加载更多');
}
uniApp中如何获取用户地理位置信息?
可以使用uni.getLocation方法获取用户的地理位置信息。
uni.getLocation({
success: (res) => {
console.log(res.latitude, res.longitude);
},
fail: (err) => {
console.error(err);
}
});
uniApp中如何进行微信支付?
可以使用uni.requestPayment方法进行微信支付,通过设置支付参数来实现支付功能。
uni.requestPayment({
provider: 'wxpay',
timeStamp: '1234567890',
nonceStr: 'abcdefg',
package: 'prepay_id=1234567890',
signType: 'MD5',
paySign: 'abcdefg',
success: (res) => {
console.log(res);
},
fail: (err) => {
console.error(err);
}
});
- uniApp中如何进行图片的懒加载?
答案:可以使用uni.lazyLoadImage组件实现图片的懒加载,将图片的src属性设置为需要加载的图片地址。
42、vue3项目搭建
43、Mixin
是 Vue.js 中用来复用组件选项的一种方式,可以包含组件的任意选项,比如 data、methods、computed 等,然后将这些选项合并到组件自身的选项中。
// myMixin.js
export const myMixin = {
data() {
return {
message: 'Hello from Mixin!'
};
},
methods: {
greet() {
alert(this.message);
}
}
};
//组件内
import { myMixin } from './myMixin.js';
export default {
mixins: [myMixin],
created() {
this.greet(); // 调用Mixin中的方法
}
};
uni-app 搭建
顺丰面试总结:
1.微前端拆分后的问题
1.script引入三方
2.权限配置相关的
3.webpack配置跨域问题
4.webpack热更新的实现原理
5.webpack loader 的使用
6.项目优化
7.使用CDN的好处
8.http2.0
9.http缓存
10.computed 和 watch的区别
11.index作为key会导致什么问题
12.vuex数据持久化
13.react context跟props的区别
14.hooks的副作用
15.useEffect
16.react hooks
中通一面:
2.自定义指令?生命周期
3.update什么时候触发,开始的时候会不会触发,父组件发生变化会不会触发
4.vue父子组件之间通信,(props/attrs/parent/children)
5.高阶组件
6.如何封装一个组件? 实现将代码放在body里面或者外面
7.盒模型
中通二面:
1.网络攻击,xss和csrf,处理现在有一个编辑器提交的代码怎么防攻击。
2.工程化的理解
3.项目权限的处理
4.watch和computer的区别
5.http 缓存,vue项目打包,正在线的项目允许会不会报错,怎么处理,怎么处理360的缓存
6.性能优化
7.速排(算法)
8.vue3的新特性
9.webpack的loader和plugin
酷家乐:
1面:
1.介绍项目
2.乾坤的选择跟碰到的问题,处理通信
3.变量提升,let, 暂时性死区
4.promise,peomise.all ,promise.race
5.开发移动端碰到的问题,怎么处理,单位的选择
6.跨域的方式
7.从URL输入到页面展现到底发生什么
1.DNS解析,浏览器不识别地址通过IP去查找的,DNS缓存(浏览器缓存,系统缓存,路由器缓存, IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存)DNS prefetch
2.TCP三次握手
3.发送http请求
4.处理请求返回的http报文
5.解析渲染页面:dom树->css 树->render Dom->绘制->渲染
6.四次挥手
7.http http2
http :超文本连接
http2.0的特点 a、HTTP/2采用二进制格式而非文本格式 b、HTTP/2是完全多路复用的,而非有序并阻塞的——只需 一个HTTP连接就可以实现多个请求响应 c、使用报头压缩,HTTP/2降低了开销 d、HTTP/2让服务器可以将响应主动“推送”到客户端缓存中