1,vue的双向绑定原理是什么?里面的关键点在哪里?
1,数据劫持
vue通过Object.defineProperty()方法劫持数据对象属性上的getter和setter,从而实现数据的监听和更新
2,观察者模式
vue采用观察者模式实现对数据的监听和更新,当数据发生变化时,watcher会收到通知,并触发相应的更新操作。
3,模板编译
vue的模板编译过程会将模板转换为渲染函数,该函数将数据和dom绑定,生成虚拟dom,通过diff算法使vue能更高效的更新视图。
4,发布-订阅者模式
vue使用发布-订阅者模式实现组件之间的通信,当数据变化时,vue会通过事件订阅和派发的方式实现数据的同步更新。
关键点
1,响应式数据:vue的数据是响应式的,当数据发生变化的时候,视图也会同步更新
2,Virtual DOM:vue使用Virtual DOM来优化视图更新,通过对比虚拟dom来差异化更新视图。
3,数据监听:vue通过数据劫持观察者订阅者模式实现数据的监听和更新
4,单项数据流:vue中的数据流是单项的,父传子通过props,子组件通过emit。
5,computed 和 watch:Vue 提供了 computed 属性和 watch 方法来处理复杂的数据侦听和计算属性
2,实现水平垂直居中的方式
1,flex布局
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
2,css定位(绝对定位)
.container {
position: relative;
}
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
3,表格布局
.container {
display: table;
width: 100%;
height: 100vh; /* 可根据需要设置容器高度 */
}
.centered {
display: table-cell;
text-align: center;
vertical-align: middle;
}
4,Grid布局
.container {
display: grid;
place-items: center;
height: 100vh; /* 可根据需要设置容器高度 */
}
5,其他居中方式
text-align: center;水平居中
line-height:xx 垂直居中
3,常用的伪类有那些
:hover - 当鼠标悬停在元素上时应用样式。
:active - 当元素被激活(鼠标点击)时应用样式。
:focus - 当元素获取焦点时应用样式。
:first-child - 选择父元素下的第一个子元素。
:last-child - 选择父元素下的最后一个子元素。
:nth-child(n) - 选择父元素下第n个子元素。
:nth-last-child(n) - 选择父元素下倒数第n个子元素。
:nth-of-type(n) - 选择父元素下特定类型的第n个子元素。
:nth-last-of-type(n) - 选择父元素下特定类型的倒数第n个子元素。
:not(selector) - 排除满足特定选择器的元素。
:checked - 选择被选中的表单元素,如复选框或单选按钮。
:disabled - 选择被禁用的表单元素。
:required - 选择需要填写的表单元素。
:valid - 选择通过验证的表单元素。
:invalid - 选择未通过验4,证的表单元素。
4,移动端如何适配不同屏幕尺寸
viewprot Meta标签
<meta name="viewport" content="width=device-width, initial-scale=1">
相对单位
em,rem,百分比等
媒体查询
@media screen and (max-width: 768px) {
/* 在小屏幕上应用的样式 */
}
@media screen and (min-width: 769px) and (max-width: 1024px) {
/* 在中等屏幕上应用的样式 */
}
@media screen and (min-width: 1025px) {
/* 在大屏幕上应用的样式 */
}
弹性布局(flex,grid布局)
min-width/max-width等
5,有哪几种本地存储方式,他们的区别是什么
-
Cookie:
- 特点:Cookie是一种小型文本文件,存储在用户的计算机上。每个Cookie的大小通常受到限制(约4KB),并且随着每次HTTP请求发送到服务器。
- 使用场景:主要用于存储会话标识、用户偏好设置,以及跟踪用户行为等。由于每次请求都会发送到服务器,所以适用于需要跨页面和跨会话保持数据的情况。
-
Web Storage:
-
LocalStorage:
- 特点:LocalStorage是一种持久性的本地存储方式,数据保存在浏览器中,不会随每次请求发送到服务器。
- 使用场景:适合用于持久性的本地存储,如用户设置、表单数据等。
-
SessionStorage:
- 特点:SessionStorage也是本地存储方式,但数据在会话期间有效,关闭标签页或浏览器后数据将被清除。
- 使用场景:适合用于会话期间需要暂时保存数据的情况。
-
-
IndexedDB:
- 特点:IndexedDB是一个基于JavaScript的面向对象的数据库,允许存储大量结构化数据,并支持事务操作和索引。
- 使用场景:适合用于存储大量结构化数据,如存储离线数据、缓存等,提供更灵活的数据检索和操作功能。
JS有哪些数据类型,判断数据类型的方法有哪些?
1,基本数据类型:
Sting:字符串
Number:数字(整数和浮点数)
Boolean:布尔值(true和false)
Null:表示空的特殊关键字(需要注意的是null使用typeof返回的是object,但是他并不是对象类型,这是js设计初的一个错误,可以使用instanceof的方法 null === instanceof Object 返回false。在二进制中代表0,但是不是一个有效的指针,指针处是空。所以会有object的说法。)
Undefined:表示未定义的值
Symbol:唯一切不可变的值,通常用作对象的键。
2,Object:对象
function:函数
array:数组
Date:日期对象
判断数据类型方法
1,typeof操作符,最常用的方法
console.log(typeof "Hello"); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object"
console.log(typeof {key: "value"}); // "object"
console.log(typeof function(){}); // "function"
2,instanceof操作符:
instanceof可以检测对象的类型,也可以用来检测原型链上的对象(拓展一下:原型链简单来说就是继承对象原型形成的一跳链结构,比如对象B继承了对象A,访问一个B的一个方法,如果B没有就会去A找,A没有就会向上层找,原型链的顶端是Object.prototype。这么一个寻找下来的路线就是原型链)
let arr = [];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true
3,Object.prototype.toString.call()方法:
这个方法可以精确的判断数据类型
console.log(Object.prototype.toString.call("Hello")); // "[object String]"
console.log(Object.prototype.toString.call(42)); // "[object Number]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call({key: "value"})); // "[object Object]"
console.log(Object.prototype.toString.call(function(){})); // "[object Function]"
4,使用instanceof和构造函数
可以通过对比实例和构造函数来判断数据类型。
说一下ES6的新特性有哪些
1,let和const声明:引入了let和const关键字用于声明变量,let声明的变量具有块级作用域,而const声明的变量是常量。
2,箭头函数:使用箭头函数语法=>可以简化函数的定义,同时具有词法作用域绑定和更简洁的语法
3,模板字符串:引入了模板字符串,使用反引号`` 可以方便地插入变量和多行文本
4,结构赋值:可以通过解构赋值语法从数组或对象中提取值并赋给变量,提高了代码的简洁性。
5,默认参数值:函数参数可以设置默认值,简化了函数调用时的代码。
6,展开运算符和剩余参数:引入展开运算符...和剩余参数...rest,用于简化操作数组和函数参数的语法。
7,类和继承:引入类的概念和class
let,const和var三者的区别
-
var:
var
是 ES5 中引入的变量声明关键字。- 变量声明的作用域是函数作用域或全局作用域,而不是块级作用域。
- 使用
var
声明的变量可以在声明之前被访问,这就是所谓的变量提升(hoisting)。 - 可以重复声明同名变量,不会报错。
- 可以修改和重新赋值已声明的变量。
javascript复制代码
var x = 10; var x = 20; // 合法,可以重复声明
-
let:
let
是 ES6 中引入的块级作用域变量声明关键字。- 变量声明的作用域是块级作用域,例如
{ }
内部。 - 在同一个作用域内,不允许重复声明同名变量。
- 不存在变量提升,必须在声明后使用。
- 可以修改和重新赋值已声明的变量。
javascript复制代码
let y = 10; //
let y = 20; // 报错,不允许重复声明 y = 20; // 合法,可以重新赋值
-
const:
const
也是 ES6 中引入的关键字,用于声明常量。- 声明的常量必须立即初始化,并且不能再重新赋值。
- 作用域同样是块级作用域,不允许重复声明。
- 对于引用类型(如对象和数组),
const
只是保证变量指向的内存地址不变,但对象内部的值可以改变。
javascript复制代码
const z = 10; //
z = 20; // 报错,常量不可重新赋值
数组去重的办法
使用set:利用 Set 数据结构的特性,可以很容易地去除数组中的重复元素
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]
使用filter方法:使用filter方法结合indexOf来判断当前下标和首位匹配下标是否相同,如果不同说明重复。
const arr = [1, 2, 2, 3, 4, 4, 5];
const newArr = arr.filter((item,index)=>{
arr.indexOf(item) === index
})
使用 reduce 方法:利用 reduce 方法和 includes 方法来去除重复元素。
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = arr.reduce((result, item) => {
if (!result.includes(item)) {
result.push(item);
}
return result;
}, []);
说一下深拷贝和浅拷贝,并自己实现一个深拷贝
1,浅拷贝
浅拷贝只是讲对象或者数组的第一层复制到新的数组或者对象里,但是对于嵌套的只是复制了引用,而不是复制其内容。
浅拷贝只是复制对象或者数组本身,不会递归复制其内部的引用
2,深拷贝
深拷贝是完全复制一个对象或者数组,包括嵌套的对象或者数组,以及他们的属性
深拷贝的对象是完全独立的,修改数值不会影响原数组或者对象
可以使用递归的方式实现深拷贝(我其他文章里有详细介绍深拷贝的实现方法,可以参考)
function deepCopy(){
if(obj ===null || typeof obj!=='object'){
return obj;
}
let copy = Array.isArray(obj)?[]:{};
for(let key in obj){
if(obj.hasOwnProperty(key)){
copy[key] = deepCopy(obj[key])
}
}
return copy
}
vue的声明周期有哪些,每个阶段做了什么
1,beforCreate
在实例初始化之后,数据观测和事件配置之前被调用
此时组件实例化还未完成,data和methods无法访问,所以this.属性或者方法是undefined
2,crate
实例已经创建完成,可以访问data和methods
通常在这里进行数据初始化,异步请求等
3,beforMount
在挂载开始之前被调用
此时模板已经编译完成,但还未将模板渲染到页面上
4,mounted
在挂载完成后被调用
组件已经显示在页面上,可以访问dom元素
通常在这里进行dom操作,数据请求,定时器等操作
5,beforUpdate
数据更新时被调用,发生在虚拟dom重新渲染和打补丁之前
可以在数据更新前后进行比较或者其他操作
6,updated
数据更新后调用
组件dom已更新,可以进行页面操作
通常不能在这里修改数据,可能导致无限循环
7,beforDestroy
实例销毁前调用
可以在这里进行清理工作,如清楚定时器,解绑事件等
8,destroyed
实例销毁后
组件实例已经销毁,无法访问其数据和方法
可以在这里进行最终的清理操作
组件通讯方式有哪些
父子传值使用props
通过父组件向传递数据,子组件通过props接受父组件传递的数据。
//父组件
<template>
<ChildComponent :message="parentMessage" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
data() {
return {
parentMessage: 'Hello from parent',
};
},
components: {
ChildComponent,
},
};
</script>
//子组件
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: ['message'],
};
</script>
子传父使用emit(通过事件调用传值给父组件)
//子组件
<template>
<button @click="sendMessage">Send Message</button>
</template>
<script>
export default {
methods: {
sendMessage() {
this.$emit('message-sent', 'Hello from child');
},
},
};
</script>
//父组件
<template>
<ChildComponent @message-sent="handleMessage" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
methods: {
handleMessage(message) {
console.log(message); // 输出:Hello from child
},
},
components: {
ChildComponent,
},
};
</script>
vuex多组件共享状态
//stroe
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
},
});
//组件
<template>
<div>
<div>{{ $store.state.count }}</div>
<button @click="$store.commit('increment')">Increment</button>
</div>
</template>
<script>
export default {
};
</script>
事件总栈bus(可父传子,也可以子传父,也可以隔代传输)
// EventBus.js 创建时间总栈JS文件
import Vue from 'vue';
const bus = new Vue();
export default bus;
//父组件引入时间总栈,使用emit方式发送数据
<template>
<div>
<input v-model="message" type="text">
<button @click="sendMessage">Send Message to Child</button>
</div>
</template>
<script>
import EventBus from './EventBus';
export default {
data() {
return {
message: '',
};
},
methods: {
sendMessage() {
EventBus.$emit('message-from-parent', this.message);
},
},
};
</script>
//子组件引入事件总栈,使用on接受数据
<template>
<div>{{ receivedMessage }}</div>
</template>
<script>
import EventBus from './EventBus';
export default {
data() {
return {
receivedMessage: '',
};
},
created() {
EventBus.$on('message-from-parent', message => {
this.receivedMessage = message;
});
},
};
</script>
vuex有几个属性及作用
vuex有五个属性,state,getter,mutations,actions,modules。
state是储存应用程序需要共享数据的地方,类似组件中的date,不过是全局可用的,我们可以理解为是应用程序的数据中心,存储着所有组件共享的数据。
getter是对state进行过滤和计算后的属性,类似组件中的计算属性,但是全局可访问。
mutations是用来修改state的唯一途径,每个mutation都有一个字符串类型的事件和一个回调函数,用于同步的修改stae
actions用于处理异步操作,可以包含任意异步操作,api请求等。他提交mutations来修改state。
modules允许将vuex分割成小模块,每个小模块都有自己的state,getter,mutations,actions。
vue的监听属性和计算属性有什么区别
监听属性
监听属性用来监听数据变化,当数据发生改变的时候执行特定的操作,当需要在数据变化时执行一步或者复杂逻辑时使用。
语法
watch: {
propertyName(newVal, oldVal) {
// 处理数据变化时的操作
}
}
计算属性
计算属性是根据其他数据计算新的数据,当依赖的数据发生改变时自动重新计算。计算属性是根据依赖进行缓存的,只有相关依赖属性发生改变时才会重新计算,当需要基于现有数据计算得到新数据时使用,提高性能简化模板逻辑
语法
computed: {
computedProperty() {
// 根据其他数据计算新数据的逻辑
return this.dataProperty + 10;
}
}
区别总结:
监听属性适用于需要在数据变化时执行额外逻辑的场景,不常用于简单的计算数据。
计算属性适用于基于已有数据计算新数据的场景,具有缓存机制,只有相关依赖发生改变时重新计算,用于简化模板逻辑和提高性能。
说一下防抖和节流,怎么实现?
防抖:
防抖的基本思想是当时间持续出发时,只有等事件停止后一段时间才会执行相应操作,即延时执行函数,常用于输入搜索框,滚动事件等频繁触发的场景中。
function debounce(func, delay) {
let timer;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
// 使用防抖函数处理频繁触发的事件
const debouncedFunction = debounce(() => {
// 执行需要防抖的操作
console.log('Debounced Function Executed');
}, 300);
// 触发事件
debouncedFunction();
节流:
节流的作用是限制函数的执行频率,确保函数在一定时间内只执行一次,当函数被调用后,会等待一段时间后才执行,然后再次等待。
function throttle(func, delay) {
let canRun = true;
return function() {
if (!canRun) return;
canRun = false;
setTimeout(() => {
func.apply(this, arguments);
canRun = true;
}, delay);
};
}
// 使用节流函数处理频繁触发的事件
const throttledFunction = throttle(() => {
// 执行需要节流的操作
console.log('Throttled Function Executed');
}, 300);
// 触发事件
throttledFunction();
总结
防抖是延缓执行,适用于处理高频触发的事件,当事件停止一段时间后才执行操作。
节流是降低触发频率,确保函数在一定时间间隔内会执行一次。
vue的导航首页有哪一些?
1,全局前置守卫,beforEach,在路由切换开始时触发,适用于全局权限检查或者修改导航。
2,全局解析守卫,beforResolve,在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后触发,适用于处理异步路由组件
3,全局后置钩子,afterEach,在导航完成后触发,适用于页面加载完成后的逻辑
4,路由独享的守卫,beforEach,在单个路由配置中定义的守卫,适用于特定路由权限检查
5,组件内的守卫,在组件内部定义的 beforeRouteEnter、beforeRouteUpdate 和 beforeRouteLeave,用于处理组件内部的路由变化逻辑。
你的登录拦截怎么实现的
可以通过路由守卫来实现
1,设置路由配置:在路由配置中,为需要登录的路由设置 meta: { requiresAuth: true }
const routes = [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
},
// 其他路由配置
];
2,编写导航守卫:在 Vue Router 中定义全局前置守卫,检查用户是否已登录,如果未登录且访问需要登录的页面,则重定向到登录页面。
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// 需要登录权限
if (!isLoggedIn()) {
// 未登录,重定向到登录页面
next({
path: '/login',
query: { redirect: to.fullPath }
});
} else {
// 已登录,继续导航
next();
}
} else {
// 不需要登录权限,直接导航
next();
}
});
// 检查用户是否已登录的函数
function isLoggedIn() {
// 这里可以根据具体的登录逻辑来判断用户是否已登录
// 返回 true 表示已登录,返回 false 表示未登录
return true; // 假设始终返回已登录状态,实际应根据具体情况修改
}
3,登录页面跳转:在登录页面中登录成功后,重定向到之前访问需要登录权限的页面或其他页面。
// 登录成功后的跳转
router.push(to.query.redirect || '/');
闭包是什么,怎么实现?
闭包是一个函数内部能够访问其外部作用域的现象。
假设有一个外部函数 outerFunction 内部定义了一个内部函数 innerFunction,并且 innerFunction 使用了 outerFunction 中的变量。当 outerFunction 执行完毕后,我们仍然可以调用 innerFunction,并且 innerFunction 可以访问并操作 outerFunction 中的变量,这就是闭包的表现。例子中的innerFunction就是闭包的
function outerFunction() {
let outerVar = 'I am from outerFunction';
function innerFunction() {
console.log(outerVar); // 内部函数访问外部函数的变量
}
return innerFunction; // 返回内部函数
}
const closure = outerFunction(); // 调用外部函数,返回内部函数
closure(); // 调用内部函数,输出 "I am from outerFunction"
vue2.0和vue3.0的区别(这个比较复杂,请自行百度,如果要想好好回答或者在前端走的更久建议好好学习vue2和3。就不在这叨叨了)
vue的一些常用指令有哪些
Vue 中常用的指令有很多,以下是一些常见的 Vue 指令:
- v-if:根据表达式的真假条件,控制元素的显示与隐藏。
- v-show:根据表达式的真假条件,控制元素的显示与隐藏,通过修改元素的 CSS
display
属性。 - v-for:基于数据源循环渲染元素列表。
- v-bind(简写为
:
):动态绑定元素的属性,如:src="url"
。 - v-on(简写为
@
):绑定事件监听器,如@click="handleClick"
。 - v-model:实现表单元素与 Vue 实例数据的双向绑定。
- v-text:更新元素的
textContent
。 - v-html:更新元素的
innerHTML
。 - v-cloak:防止页面闪现,用于隐藏未编译的 Mustache 标签直到实例准备完全就绪。
- v-pre:跳过该元素和子元素的编译过程,用于显示原始 Mustache 标签。
除了上述常用的指令外,还有一些 Vue 的指令用于处理特定的场景或功能,比如:
- v-once:只渲染元素和组件一次。
- v-slot:用于具名插槽的分发内容。
- v-once:只渲染元素和组件一次。
- v-pre:跳过该元素和子元素的编译过程。
- v-once:只渲染元素和组件一次。
- v-pre:跳过该元素和子元素的编译过程。
v-if和v-show有什么区别
v-if元素的存在与否是通过dom的添加和移除来实现的,
v-show是通过dom的隐藏来实现的
区别是v-show不管是true还是false,该元素都是存在在dom上的,只是现实和不显示,v-if则是有和无。v-if适用于频繁切换的情况下使用,v-show适用于频繁切换样式的情况下使用
v-for为什么要加一个key
在 Vue 中使用 v-for 进行列表渲染时,为每个被渲染的元素添加一个唯一的 key 是非常重要的。这是因为在 Vue 中通过 key 来识别每个节点的身份,帮助 Vue 识别哪些元素是新增的、被修改的或被删除的,从而提高性能并避免出现一些意想不到的问题。
主要原因包括:
性能优化:
带有 key 的元素可以更快地被识别,减少不必要的 DOM 操作。Vue 通过比较新旧节点的 key 来判断节点是否需要更新,从而提高渲染效率。
避免重复渲染:
如果没有为 v-for 渲染的每个元素提供唯一的 key,在列表中进行增删操作时,可能会导致 Vue 出现混乱,重排元素,甚至出现意外的渲染结果。
维护组件状态:
在使用 v-for 渲染动态组件时,key 可以帮助 Vue 保持组件的内部状态,避免在重新排序时导致组件状态丢失。
因此,为 v-for 渲染的每个元素添加一个唯一的 key 是一个良好的实践,可以提高性能并确保应用在处理列表时的正确性。通常可以使用 v-bind:key 来指定元素的 key 值,确保每个元素都具有唯一标识。
你是如何封装一个组件的?
-
创建组件文件:在您的项目中创建一个新的 Vue 组件文件,通常以
.vue
结尾。 -
定义组件:在组件文件中,定义您的 Vue 组件。一个基本的 Vue 组件通常包括模板、脚本和样式部分。例如:
<template> <div> <h2>{{ title }}</h2> <p>{{ content }}</p> </div> </template> <script> export default { data() { return { title: 'Welcome to My Component', content: 'This is a simple Vue component.' }; } }; </script> <style> /* 可选的样式 */ </style>
- 注册组件:在需要使用该组件的地方,通过
import
引入组件,并将其注册为全局或局部组件。如果是全局注册,可以在main.js
或根组件中注册。如果是局部注册,可以在父组件中注册。// 全局注册 import YourComponent from './YourComponent.vue'; Vue.component('your-component', YourComponent); // 局部注册 import YourComponent from './YourComponent.vue'; export default { components: { YourComponent } };
- 使用组件:在模板中使用封装的组件。
<template>
<div>
<your-component></your-component>
</div>
</template>
说一下在项目遇到的难点,如何解决(每个人遇到的不一样,这里大家自己总结下)
url到浏览器的一个过程有哪些步骤
-
解析域名:首先,浏览器会解析输入的 URL 中的域名部分,比如 www.example.com。它会将这个域名解析成对应的 IP 地址,这个过程称为 DNS 解析。
-
建立连接:浏览器通过解析得到的 IP 地址找到目标服务器,然后与服务器建立一个 TCP 连接,这个连接是可靠的,确保数据的可靠传输。
-
发送请求:一旦建立了连接,浏览器会向服务器发送一个 HTTP 请求,这个请求包含了要访问的资源的信息,比如页面文件、图片、样式表等。
-
服务器处理请求:服务器收到请求后会根据请求的资源路径和类型进行处理,可能需要调用后端程序生成动态内容。
-
返回响应:服务器处理完成后,会将请求的资源作为 HTTP 响应返回给浏览器,响应中包含了资源的数据以及状态码等信息。
-
浏览器渲染:浏览器收到响应后,会根据响应中的数据解析 HTML、CSS 和 JavaScript,然后将页面渲染出来,并呈现给用户。
-
显示页面:最终,浏览器会将解析和渲染后的页面显示给用户,用户就可以看到呈现出来的页面内容了。
如何实现小程序的request封装和拦截
- 封装请求模块:创建一个独立的请求模块,例如
utils/request.js
,在该模块中定义通用的请求方法供应用调用// utils/request.js const request = (url, data = {}, method = 'GET') => { return new Promise((resolve, reject) => { wx.request({ url: url, data: data, method: method, success: (res) => { resolve(res.data); }, fail: (error) => { reject(error); } }); }); } export default request;
- 拦截请求:在封装的请求模块中可以实现拦截功能,例如在请求发送前或收到响应后做一些处理。
// utils/request.js const request = (url, data = {}, method = 'GET') => { // 在请求发送前进行拦截处理 console.log('发送请求:', url); return new Promise((resolve, reject) => { wx.request({ url: url, data: data, method: method, success: (res) => { // 在收到响应后进行拦截处理 console.log('请求成功:', res); resolve(res.data); }, fail: (error) => { console.error('请求失败:', error); reject(error); } }); }); } export default request;
- 使用封装的请求模块:在小程序的页面或组件中引入封装的请求模块,并调用请求方法来发送请求。
// pages/index/index.js import request from '../../utils/request.js'; Page({ onl oad: function () { request('https://api.example.com/data') .then((data) => { console.log('请求成功:', data); }) .catch((error) => { console.error('请求失败:', error); }); } });
在vue项目中,不适用框架,怎么封装,比如请求封装
创建一个请求模块:创建一个独立的请求模块,例如utils/request.js
,在该模块中定义通用的请求方法供应用调用。
// utils/request.js
const request = (url, options = {}) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(options.method || 'GET', url);
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => {
reject(xhr.statusText);
};
xhr.send();
});
}
export default request;
什么事Js原型,原型链是什么
在 JavaScript 中,每个对象都有一个 __proto__
属性,用于指向该对象的原型对象。访问对象的属性时,JavaScript引擎会沿着 __proto__
链向上查找属性。
例如,如果有对象A,它的原型是B,B的原型是C,那么当你访问A的一个属性时,JavaScript会先查找A自身是否有该属性,如果没有,就会在B中查找,如果还没有就会在C中查找,依次类推,直到找到属性或者查找到原型链的末端。
闭包的原理可以做哪些,你做过哪些
-
保护变量:闭包可以用于隐藏变量,防止被外部访问和修改,实现私有性。
-
模块化:通过闭包可以创建模块化的代码结构,将相关的函数和变量封装在闭包中,避免全局命名冲突。
-
实现函数式编程:闭包是函数式编程的重要概念,可以用于创建高阶函数、柯里化等函数式编程模式。
-
延迟执行:通过闭包可以实现延迟执行功能,例如在定时器中使用闭包来保存变量状态。
-
事件处理:闭包可以用于事件处理,保存事件处理函数的上下文和状态。
-
解决回调地狱:通过闭包可以优雅地解决回调地狱问题,更好地管理和传递异步回调函数。
-
缓存:闭包可以用于实现缓存,将计算结果缓存在闭包内部,避免重复计算。
-
实现私有变量和方法:通过闭包可以创建具有私有变量和私有方法的对象,保护数据安全性。
-
作为参数传递:闭包可以作为参数传递给其他函数,实现更灵活的函数组合和功能扩展。
function createCounter() {
let count = 0; // 变量 count 在闭包中被保护
function increment() {
count++;
console.log('Current count: ' + count);
}
function decrement() {
count--;
console.log('Current count: ' + count);
}
return {
increment,
decrement
};
}
const counter = createCounter();
counter.increment(); // 输出 "Current count: 1"
counter.increment(); // 输出 "Current count: 2"
counter.decrement(); // 输出 "Current count: 1"
作用域是什么
想象一下作用域就像是房子的房间,每个房间里有不同的物品。在程序中,就像是在不同的房间里,这些房间就是不同的作用域,而变量就是放在这些房间里的物品。只有在同一个房间里的物品才能被看到和使用,同样,在同一个作用域内定义的变量才能被访问到。
-
全局作用域(Global Scope): 就像是整栋房子,里面的物品都可以被任何人使用。
-
局部作用域(Local Scope): 就像是房子里的某个房间,里面的物品只能在这个房间内被使用,其他房间的人看不到。
操作数组的方式有哪些
-
访问元素: 通过索引访问数组中的元素,例如
arr[0]
获取第一个元素。 -
追加元素: 使用
push()
方法向数组末尾添加一个元素。 -
删除元素:
- 使用
pop()
方法从数组末尾删除一个元素。 - 使用
shift()
方法从数组开头删除一个元素。 - 使用
splice()
方法根据索引删除一个或多个元素。
- 使用
-
插入元素: 使用
splice()
方法在指定位置插入一个或多个元素。 -
连接数组: 使用
concat()
方法连接两个或多个数组。 -
切片数组: 使用
slice()
方法从原数组中截取一部分元素,返回一个新数组。 -
查找元素:
- 使用
indexOf()
方法查找指定元素的索引。 - 使用
find()
方法根据条件查找第一个满足条件的元素。 - 使用
filter()
方法根据条件筛选出满足条件的所有元素。
- 使用
-
遍历数组:
- 使用
for
循环遍历数组。 - 使用
forEach()
方法遍历数组并对每个元素执行指定操作。 - 使用
map()
方法遍历数组并对每个元素执行指定操作,返回一个新数组。
- 使用
-
数组操作:
- 使用
join()
方法将数组元素连接成一个字符串。 - 使用
reverse()
方法反转数组元素的顺序。 - 使用
sort()
方法对数组元素进行排序。 - 使用
includes()
方法检查数组是否包含指定元素。
- 使用
keep-alive是什么?有哪几个生命周期
在前端开发中,keep-alive 是 Vue.js 中的一个内置组件,用于缓存组件的状态或避免组件的重复渲染。keep-alive 组件可以将包裹的组件缓存起来,当组件被切换隐藏时,并不会被销毁,而是保留在内存中,下次需要显示时会直接从缓存中取出,避免重新创建和销毁组件,提高性能。
-
activated: 当缓存的组件被激活时调用,例如切换到该组件时。
-
deactivated: 当缓存的组件被停用时调用,例如切换到其他组件时。
<template>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</template>
<script>
export default {
data() {
return {
currentComponent: 'ComponentA'
};
},
activated() {
console.log('Component activated');
},
deactivated() {
console.log('Component deactivated');
}
};
</script>
判断一个变量是否是数组,有哪些办法
isArray
const arr = [1, 2, 3];
console.log(Array.isArray(arr)); // 输出 true
instanceof
const arr = [1, 2, 3];
console.log(arr instanceof Array); // 输出 true
Object.prototype.toString.call()
const arr = [1, 2, 3];
console.log(Object.prototype.toString.call(arr) === '[object Array]'); // 输出 true
判断一个变量是否是变量有哪些办法
typeof
instanceof
object.prototype.toString.call()
对象/数组的常用方法有哪些
对象的常用方法:
-
Object.keys(obj):
- 返回一个包含给定对象的所有属性键的数组。
-
Object.values(obj):
- 返回一个包含给定对象的所有属性值的数组。
-
Object.entries(obj):
- 返回一个包含给定对象的所有属性键值对的数组。
-
Object.assign(target, ...sources):
- 将一个或多个源对象的所有可枚举属性复制到目标对象中,并返回目标对象。
-
Object.hasOwnProperty(prop):
- 判断一个对象是否包含指定属性,返回一个布尔值。
数组的常用方法:
-
push(element):
- 在数组的末尾添加一个或多个元素,并返回新数组的长度。
-
pop():
- 删除数组的最后一个元素,并返回该元素的值。
-
shift():
- 删除数组的第一个元素,并返回该元素的值。
-
unshift(element):
- 在数组的开头添加一个或多个元素,并返回新数组的长度。
-
splice(start, deleteCount, ...items):
- 从指定位置开始删除/插入元素,可以删除任意数量的元素,并返回被删除的元素。
-
slice(start, end):
- 返回数组的一部分,不会改变原数组,而是返回一个新的数组。
-
forEach(callback):
- 遍历数组的每个元素,并对每个元素执行回调函数。
-
map(callback):
- 对数组的每个元素执行回调函数,并返回一个新数组。
-
filter(callback):
- 根据指定条件过滤数组的元素,并返回满足条件的新数组。
-
reduce(callback, initialValue):
- 对数组中的每个元素执行回调函数(从左到右),将结果汇总为单个值。
Set和Mpa分别是什么
Set(集合):
Set
是一种类似数组的数据结构,它存储唯一值,即集合中的值都是唯一的,不允许重复。Set
中的值是有序的,可以按照插入的顺序迭代。Set
实例的方法主要包括:add(value)
(添加一个值)、delete(value)
(删除一个值)、has(value)
(判断是否包含某个值)、clear()
(清空集合)等
const mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(1); // 这个值不会被重复添加
console.log(mySet); // 输出 Set(2) { 1, 2 }
Map(映射):
Map
是一种键值对的集合,其中每个键都是唯一的,可以是任意类型的值。Map
中的键值对是有序的,可以按照插入的顺序迭代。Map
实例的方法主要包括:set(key, value)
(设置键值对)、get(key)
(获取键对应的值)、delete(key)
(删除键值对)、has(key)
(判断是否包含某个键)、clear()
(清空映射)等。
const myMap = new Map();
myMap.set('name', 'Alice');
myMap.set('age', 30);
console.log(myMap.get('name')); // 输出 "Alice"
console.log(myMap); // 输出 Map(2) { 'name' => 'Alice', 'age' => 30 }
主要区别:
Set
是一种值的集合,存储唯一值;Map
是键值对的集合,存储键值对。
Promise通常会解决三种问题
1,链式回调
// 异步操作1,返回一个Promise对象
function asyncOperation1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Async Operation 1 completed");
resolve("Result from Async Operation 1");
}, 2000);
});
}
// 异步操作2,返回一个Promise对象
function asyncOperation2(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Async Operation 2 completed");
resolve(`Result from Async Operation 2 with data: ${data}`);
}, 1500);
});
}
// 异步操作3,返回一个Promise对象
function asyncOperation3(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Async Operation 3 completed");
resolve(`Result from Async Operation 3 with data: ${data}`);
}, 1000);
});
}
// 使用Promise链式调用异步操作
asyncOperation1()
.then((data) => {
return asyncOperation2(data);
})
.then((data) => {
return asyncOperation3(data);
})
.then((finalResult) => {
console.log("Final Result:", finalResult);
})
.catch((error) => {
console.error("Error:", error);
});
2,同时发起几个请求,谁先有结果就拿谁的
// 模拟异步请求1
function asyncRequest1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Result from Async Request 1");
}, 2000);
});
}
// 模拟异步请求2
function asyncRequest2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Result from Async Request 2");
}, 1500);
});
}
// 模拟异步请求3
function asyncRequest3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Result from Async Request 3");
}, 1000);
});
}
// 同时发起多个异步请求,谁先有结果就用谁的
Promise.race([asyncRequest1(), asyncRequest2(), asyncRequest3()])
.then((result) => {
console.log("First result:", result);
})
.catch((error) => {
console.error("Error:", error);
});
3,发起多个请求,等到所有的请求后再做下一步处理
// 模拟异步请求1
function asyncRequest1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Result from Async Request 1");
}, 2000);
});
}
// 模拟异步请求2
function asyncRequest2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Result from Async Request 2");
}, 1500);
});
}
// 模拟异步请求3
function asyncRequest3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Result from Async Request 3");
}, 1000);
});
}
// 发起多个异步请求,并等待它们全部完成后进行下一步处理
Promise.all([asyncRequest1(), asyncRequest2(), asyncRequest3()])
.then((results) => {
console.log("All results:", results);
// 下一步处理,可以在这里对所有结果进行操作
})
.catch((error) => {
console.error("Error:", error);
});
如何改变函数a的上线文
使用 call()
call()
方法接受一个参数作为函数执行时的上下文,并可以接受多个参数传递给函数。
function A() {
console.log(this.name);
}
const obj1 = { name: "Alice" };
const obj2 = { name: "Bob" };
A.call(obj1); // 输出 "Alice"
A.call(obj2); // 输出 "Bob"
使用 apply()
apply()
方法与 call()
类似,但是它接受一个数组作为参数传递给函数。
function A() {
console.log(this.name);
}
const obj = { name: "Alice" };
A.apply(obj); // 输出 "Alice"
使用 bind()
bind()
方法会创建一个新函数,指定函数执行时的上下文,但不会立即执行该函数。
function A() {
console.log(this.name);
}
const obj = { name: "Alice" };
const boundFunction = A.bind(obj);
boundFunction(); // 输出 "Alice"
Evenbus是什么(答案见上面vue组件传值--时间总栈)
vue中父子组件声明周期执行顺序是怎样的
先父后子
父组件和子组件生命周期执行顺序:
-
父组件:
beforeCreate
created
beforeMount
mounted
-
子组件:
beforeCreate
created
beforeMount
mounted
更新过程中的执行顺序:
-
父组件更新:
beforeUpdate
updated
-
子组件更新:
beforeUpdate
updated
销毁过程中的执行顺序:
-
销毁子组件:
beforeDestroy
destroyed
-
销毁父组件:
beforeDestroy
destroyed
mixins有几个生命周期
在 Vue.js 中,Mixins 是一种可以复用组件选项的特性。当一个组件使用了 Mixin,Mixin 中定义的选项会被合并到组件本身的选项中。Mixin 可以包含生命周期钩子函数,影响到组件的生命周期。一个 Mixin 可以包含以下生命周期钩子函数:
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
一个 Mixin 可以包含以上这些生命周期钩子函数中的任意一个或多个。当一个组件使用了 Mixin,并且该 Mixin 包含了生命周期钩子函数,这些生命周期钩子函数会按照特定规则与组件自身的生命周期钩子函数合并执行。
const myMixin = {
created() {
console.log('Mixin created');
},
mounted() {
console.log('Mixin mounted');
},
beforeDestroy() {
console.log('Mixin beforeDestroy');
}
};
export default myMixin;
import myMixin from './myMixin';
export default {
mixins: [myMixin],
created() {
console.log('Component created');
},
mounted() {
console.log('Component mounted');
},
beforeDestroy() {
console.log('Component beforeDestroy');
}
}
弹性布局,一行两列,一列固定宽度,如何实现
使用flex布局
//HTML
<div class="container">
<div class="fixed-column">固定宽度列</div>
<div class="flexible-column">占据剩余空间列</div>
</div>
//css
.container {
display: flex;
}
.fixed-column {
width: 200px; /* 固定宽度 */
background-color: lightblue;
}
.flexible-column {
flex: 1; /* 占据剩余空间 */
background-color: lightgreen;
}
flex:1包含了哪三种属性
在 Flexbox 布局中,flex: 1;
是一个快捷属性,它实际上包含了以下三个属性:
flex-grow
flex-shrink
flex-basis
详细解释:
flex-grow
:指定项目的放大比例,默认为 0,表示不放大。当父容器有剩余空间时,项目会根据flex-grow
的值进行放大。设置为1
表示项目会占据剩余空间的比例。flex-shrink
:指定项目的缩小比例,默认为 1,表示项目可以缩小。当父容器空间不足时,项目会根据flex-shrink
的值进行缩小。通常情况下,我们不需要特别设置flex-shrink
的值。flex-basis
:指定项目在主轴方向上的初始大小。通常可以设置为固定的像素值或百分比。如果未指定flex-basis
,则根据项目的内容自动设置大小。
所以,flex: 1;
等同于以下的设置:
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
这意味着项目会根据父容器的剩余空间等比例放大,可以缩小,并且初始大小为 0%,以便占据剩余空间。这种设置通常用于实现弹性布局中让项目占据剩余空间的效果
标签:面试题,BOOS,console,log,直聘,元素,数组,组件,const From: https://blog.csdn.net/LoveSQ0625/article/details/136702055