前端面试实录,(回答均为个人见解)

面试题

js

1. 箭头函数

箭头函数和普通函数的区别是:

  1. 没有自己的this,它内部的this使用的是上层函数作用域或者全局的this
  2. 返回值有隐式的返回和显示的返回两者
  3. 没有arguments,需要使用…args的方式获取形参列表
  4. 没有prototype,所以不能使用new关键字。也不可以使用call等方法改变this的指向
let obj = {
	fn: () => {
		console.log(this)
	}
}
obj.fn() // window
let obj = {
	fn: () => 123 // 隐式的返回
}
console.log(obj.fn())
2. let 和 var 的区别

let和var都是js中用于声明变量的关键字,他们的区别在于:

  1. let 有块级作用域的概念,var只有函数作用域或者全局作用域的概念

    {
    	let a = 100
    	var b = 200
    }
    console.log(b)
    console.log(a)
    
  2. var有变量提升,在变量声明前使用,值为undefined。

  3. let 在同一作用域内,不可以重复声明相同名字的变量

3. new关键字在new的时候做了些什么

实现new关键字

// 模拟new关键字,创建构造函数

function Person(name,age) {
	this.name = name;
	this.age = age;
}

function createInstance(contrustor,...args) {
	// 1. 创建一个新的对象
	let newObj = {}
	// 2. 设置原型 将新对象的隐式原型,指向显示原型
	newObj.__proto = contrustor.prototype
	// 3. 改变构造函数的this指向
	let result = contrustor.apply(newObj,args)
	// 4. 如果是对象类型就返回,不是就返回新创建的对象
	let reObj = (typeof result === 'object' && result !== null) ? result : newObj
	return reObj
}

let person = createInstance(Person,'jjs',14)
console.log(person.name)

那么我们可以发现如果构造函数有返回值,且为对象时那么new表达式会返回这个对象,而不是新创建的对象。这允许构造函数在特定情况下覆盖默认的实例化行为。

也就是

// 模拟new关键字,创建构造函数

function Person(name,age) {
	this.name = name;
	this.age = age;
	return {
		a: 1
	}
}

function createInstance(contrustor,...args) {
	// 1. 创建一个新的对象
	let newObj = {}
	// 2. 设置原型 将新对象的隐式原型,指向显示原型
	newObj.__proto = contrustor.prototype
	// 3. 改变构造函数的this指向
	let result = contrustor.apply(newObj,args)
	// 4. 如果是对象类型就返回,不是就返回新创建的对象
	let reObj = (typeof result === 'object' && result !== null) ? result : newObj
	return reObj
}

let person = createInstance(Person,'jjs',14)
let person2 = new Person('jtt',18)
console.log(person.name,person.a)
console.log(person2.name,person2.a)

这种情况下,只读的到返回的对象

4. call,apply和bind的区别

他们的作用是,改变this的指向

区别是:

call: func.call(thisArg, arg1, arg2, …),第一个参数为要指定的this,以分散的形式将参数传入

上面那个例子,我们要使用call的话:

let result = contrustor.call(newObj,…args)

apply:方法调用一个函数,其具有一个指定的this值,以及以一个数组(或类数组对象)的形式提供的参数

bind: 形式和call相同 func.bind(thisArg, arg1, arg2, …),第一个参数为要指定的this,以分散的形式将参数传入

区别是:callapply都是直接调用 bind则是用于创建一个新的函数,这个新函数被bind调用时的this被永久绑定,之后无论这个新函数如何被调用,this都不会改变。

5. localStorage,sessionStorage cookie和session的区别
  1. 存储位置和生命周期

    localStorage,sessionStorage cookie存储在客户端,也就是浏览器上面。session存在服务器上面

    localStorage,持久化存储。sessionStorage,仅在当前会话页面有效,页面关闭sessionStorage就会被删除

    cookie 根据设置的过期时间而定,如果没有设置,那么浏览器关闭,cookie删除

    session 受到服务器的超时时间的影响

  2. 存储的内容大小

    localStorage,sessionStorage 存储空间较大,cookie存储空间较小一般为4kb,session不占用客户端内存,与服务端存储空间相关

6. js创建对象的几种方式
  1. 使用字面量的方式

    let obj = {}

  2. 使用Object.create() 静态方法以一个现有对象作为原型,创建一个新对象。

    需要一个参数,构造函数。相当于newObj.__porto = Fn.prototype

    var personProto = {  
        greet: function() {  
            console.log("Hello, my name is " + this.firstName + " " + this.lastName);  
        }  
    };  
      
    var person = Object.create(personProto);  
    person.firstName = "John";  
    person.lastName = "Doe";  
      
    person.greet(); // 输出: Hello, my name is John Doe
    
  3. 构造函数,使用new 关键字来创建对象

  4. 工厂函数,一个函数的返回值为对象,然后就可以通过调用这个函数来批量的产生对象。

    function createPerson(firstName, lastName) {  
        var obj = {};  
        obj.firstName = firstName;  
        obj.lastName = lastName;  
        obj.greet = function() {  
            console.log("Hello, my name is " + this.firstName + " " + this.lastName);  
        };  
        return obj;  
    }  
      
    var person = createPerson("John", "Doe");  
    person.greet(); // 输出: Hello, my name is John Doe
    
  5. 通过new Obejct的方式

  6. 通过class的方式

7.this的指向

有三种情况

  1. 作为对象的属性调用,如果不是箭头函数,那么this指向调用的那个对象
  2. 在函数当中:普通函数,非严格模式下是window或者golab,严格模式下是undefined
  3. 构造函数中,是实例对象

闭包

在什么时候产生,在什么时候死亡

闭包就是,内部函数引用到了外部函数的变量,导致外部函数空间不能被释放

在内部函数创建时产生,在内部函数销毁时闭包死亡

8. 继承
  1. 可以通过原型链继承

    Child._proto _= Parent.prototype

    Child.prototype.constructor = Child

    案例:

    这样的话,只能继承的到原型上面的方法,不能动态的去指定属性的值

    function Parent() {  
        this.name = 'Parent';  
    }  
      
    Parent.prototype.sayName = function() {  
        console.log(this.name);  
    };  
      
    function Child() {  
        this.age = 10;  
    }  
      
    // 继承Parent  
    Child.prototype = Object.create(Parent.prototype);  
    Child.prototype.constructor = Child;  
      
    var child = new Child();  
    child.sayName(); // 输出: Parent
    
  2. 构造函数继承

    通过new的方式

  3. 组合继承,将借用构造函数和原型链继承结合起来

    function Parent(name) {  
        this.name = name;  
    }  
      
    Parent.prototype.sayName = function() {  
        console.log(this.name);  
    };  
      
    function Child(name,age) {
    	Parent.call(this,name)
        this.age = age;  
    }  
      
    // 继承Parent  
    Child.prototype = new Parent()
    Child.prototype.constructor = Child;  
      
    let child = new Child("jjs",20); 
    let child2 = new Child("jtt",18);
    child2.sayName(); // 输出: Parent
    
    
9. 什么是事件循环机制

用于协调异步任务的顺序,传递信息以及处理用户交互。

由调用栈,执行队列,事件循环,三部分组成。

当主线程的同步代码执行完毕后,事件循环会开始工作。它首先检查微任务队列,如果有微任务,则依次执行微任务队列中的所有任务,直到微任务队列为空。然后,事件循环会从宏任务队列中取出第一个宏任务执行。每个宏任务执行完毕后,会再次清空微任务队列,并继续执行下一个宏任务,如此循环往复。

node的事件循环与浏览器上面的事件循环有什么区别

  1. 实现方式不同

    node.js : 主要是处理i/o操作和网络请求,基于Libuv库利用底层操作系统的多线程特性去实现。

    浏览器:主要处理,事件,用户的交互。单线程的,通过异步回调和触发事件来进行异步操作

  2. 宏任务和微任务的处理

    node.js : 宏任务包括整体的代码、setTimeout、setInterval、I/O操作等,微任务则包括Promise的回调和process.nextTick() 先执行宏任务,清空当前微任务队列。执行下一次tick

    浏览器:浏览器的宏任务同样包括setTimeout、setInterval、DOM操作、AJAX请求等,但微任务则包括Promise的回调、MutationObserver以及queueMicrotask等,先执行微任务再执行宏任务

10. Promise.all()、Promise.race()的区别

promise有三种状态

  1. Pending(等待中):初始状态,既不是成功,也不是失败状态。
  2. Fulfilled(已成功):意味着操作成功完成。
  3. Rejected(已失败):意味着操作失败。

基本用法:

let promise = new Promise(function(resolve, reject) {  
  // 异步操作  
  if (/* 异步操作成功 */) {  
    resolve(value); // 将Promise的状态从"pending"变为"fulfilled",并将异步操作的结果,作为参数传递出去  
  } else {  
    reject(error); // 将Promise的状态从"pending"变为"rejected",并将错误作为参数传递出去  
  }  
});  
  
// 处理Promise的结果  
promise.then(function(value) {  
  // 成功时调用  
  console.log(value);  
}).catch(function(error) {  
  // 失败时调用  
  console.log(error);  
});

Promise.race方法接受多个promise,返回最快成功的一个结果

Promise.all 方法接受一个数组,里面的promise都成功则为成功,有一个失败就都不成功

11. 怎么判断一个变量的类型为null
  1. 使用====

    let value = null;

    if(value === null) {console.log(“为null”)}

  2. 使用原型

    let value = null;

    if(Obejct.prototype.toString.call(value) == [object null]) {console.log(“为null”)}

  3. 使用es6

    let value = null;

    if(Object.is(value)) {console.log(“为null”)}

    使用typeof可以确定哪些变量的类型

    undefined,number,string,Boolean,

12.实现图片懒加载

使用IntersectionObserver这个api就可以轻松的实现它

官方文档:IntersectionObserver.root - Web API | MDN (mozilla.org)

提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root)。

实例属性(配置项):

root 属性用来获取当前 intersectionObserver 实例的根元素。

rootMargin 属性 可以理解为,懒加载的预加载,比如图片的懒加载,不是等图片元素进入可视区域之后,才去加载,而是检测到,离进入可视区域,在一定范围内部的时候,就提前加载(叫做预加载吧)。

threshold: 1.0, 阈值为 1.0 意味着目标元素完全出现在 root 选项指定的元素中可见时,回调函数将会被执行。

案例:

let options = {
	root:null, // 设置为null,顶层视口
	rootMargin: '0px', // 距离视口多少时显示
	threshold: 1.0 // 标元素完全出现在 root 选项指定的元素中可见
}
function callback(entries) {
	for (let entrie of entries) {
		if(entrie.isIntersecting) {
			let img = entrie.target // 将dom元素存放到了target属性上面
			img.src = "./img/test.gif"
		}
	}
}

let ob = new IntersectionObserver(callback,options)

let data = document.querySelectorAll('img')
data.forEach(item => {
	ob.observe(item)
})

自定义指令的形式

<script setup generic="T" lang="ts">
import {useRouter} from "vue-router";
import {Directive} from "vue";
import defaultImg from "../assets/image/vue.svg";

const router = useRouter()

const vLazy: Directive =(el, binding) => {
  let ob = new IntersectionObserver((entries) => {
    for (const entry of entries) {
      if(entry.isIntersecting) {
        let imgDom:HTMLImageElement = entry.target as HTMLImageElement
        import(binding.value).then(res => {
          imgDom.src = res.default
          ob.unobserve(el)
        })
      }
    }
  },{
    threshold: 1
  })
  ob.observe(el)
}
</script>

<template>
<div>
  <h1>我是b组件</h1>
  <button @click="router.back()">返回</button>
  <hr>
  <img :src="defaultImg" v-lazy="'../assets/1.png'">
  <img :src="defaultImg" v-lazy="'../assets/2.png'">
  <img :src="defaultImg" v-lazy="'../assets/3.png'">
</div>
</template>

<style scoped>
img {
  width: 200px;
  display: block;
}
</style>

13 深浅拷贝
  1. 浅拷贝,只复制了引用,没有复制值

    let arr = [1,2,3]
    let arr2 = arr
    arr[1] = 10
    console.log(arr2)
    let obj = {
        name: 'jjs'
    }
    let obj2 = Object.assign(obj)
    
    
  2. 深拷贝是复制真正的值

    // 使用JSON.parse,JSON.stringify()
    let obj = {
        a: 1,
        b: 3
    }
    let obj1 = JSON.parse(JSON.stringify(obj))
    // 使用递归的方式
    function deepClone(obj) {
        let newObj = obj instanceof Array ? [] : {}
        for (let key in obj) {
            if(obj[key] && typeof obj[key] === 'object') {
                newObj[key] = deepClone(obj[key])
            }else {
                newObj[key] = obj[key]
            }
        }
        return newObj
    }
    let obj2 = deepClone(obj)
    
    
14. script标签加什么属性可以变为异步加载

​ 可以使用async和defer两个属性

​ 不同的是:

​ 使用defer属性的脚本会等到整个文档被解析完成后,再按照它们在文档中出现的顺序执行

​ 使用async脚本将独立于页面的其他部分异步下载,并且一旦下载完成,浏览器就会暂停解析HTML文档来执行这个脚本不会按照它们在页面上出现的顺序执行

css

1. 如何快速实现居中

(1)给父元素添加display: flex;,目标元素: margin: auto;

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			*{
				padding: 0;
				margin: 0;
			}
			.container {
				width: 100vw;
				height: 100vh;
				display: flex;
			}
			.content {
				width: 200px;
				height: 200px;
				background-color: aquamarine;
				margin: auto;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<div class="content">
				
			</div>
		</div>
	</body>
</html>

​ (2) 给父元素 display: flex;justify-content: center;align-items: center;

.container {
	width: 100vw;
	height: 100vh;
	display: flex;
	justify-content: center;
	align-items: center;
}
.content {
	width: 200px;
	height: 200px;
	background-color: aquamarine;
}

​ (3)利用position进行定位

*{
	padding: 0;
	margin: 0;
}
.container {
	width: 100vw;
	height: 100vh;
	position: relative;
}
.content {
	width: 200px;
	height: 200px;
	background-color: aquamarine;
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%,-50%);
}

2. margin和padding有什么不同

​ margin 是☞,边框与外部元素之间的距离,即元素与其他元素之间的距离,不会影响到元素内部

​ padding 是☞, 边框与内容区的一个距离,它作用于元素本身,影响元素内部的空间

​ 怪异盒子模型:

​ 在怪异盒子模型中,高度和宽度是指的总高度和总宽度。如果设置了高宽为100,再设置边框为10px,padding为20px,那么内容区就只剩70px

​ 开启怪异盒模型: box-sizing: border-box;

.content {
	width: 200px;
	height: 200px;
	background-color: aquamarine;
	box-sizing: border-box;
	padding: 20px;
	border: 3px #bfa solid;
}

3. vw和百分比的区别

​ vw是相对于,视口的宽度进行计算,百分比则是相对于父元素的宽度进行计算

4. 如何让字体变的更小

​ (1)transform: scale(.8);

​ (2)利用rem,em来进行让字体相对于元素变换

​ rem: 相对于根元素进行计算

​ em: 相对于父元素进行计算

*{
	padding: 0;
	margin: 0;
}
html {
	font-size: 20px;
}
.container {
	width: 100vw;
	height: 100vh;
	position: relative;
}
.content {
	width: 200px;
	height: 200px;
	box-sizing: border-box;
	padding: 20px;
	border: 3px #bfa solid;
	font-size: 30px;
}
.content>:nth-child(1) {
	font-size: .5rem;
}
.content>:nth-child(2) {
	font-size: .5em
}

5.回流与重绘

​ 回流就是:改变了dom的几何结构,浏览器需要对页面的位置分布重新进行计算并进行绘制

​ 导致回流的原因:页面第一次渲染,元素尺寸发生变化,字体大小发生变化,删除或者新增dom

​ 重绘就是:改变了dom的样式而没有改变dom的几何结构,比如:背景颜色等,那么浏览器就会为该元素重新绘制

​ 导致重绘的原因:visibility属性被修改,样式发生变化

visibility

​ 作用:控制元素是否可见,有三个值:visible,可见,hidden:不可见但依然占据空间,collapse:不可见也不占据空间

visibility和display的区别

​ 使用display:none;之后元素并不可见也不占用空间,如果想要不可见但依然占据空间还是要使用visibility:hidden;

​ 减少回流与重绘:

​ 合并dom操作,可以搞一个异步的队列,将多次dom操作统一执行

6.overflow

​ 用于指定内容超出大小后,浏览器如何去处理溢出的内容。

​ 他一共有四个属性:分别是hidden,auto,scroll,visible,他们分别的功能是:

​ visible: 默认值,不管溢出。设置和没设置一样

​ hidden:直接裁掉溢出的部分,那么如果是下面还有内容的话,也会一起被裁掉

​ auto: 如果内容溢出,则浏览器会显示滚动条以便查看其余的内容。如果内容没有溢出,则不显示滚动条

​ scroll: 不管有没有溢出,都会显示滚动条。没有溢出的话滚动条是失活的。

.content {
	width: 200px;
	height: 200px;
	box-sizing: border-box;
	padding: 20px;
	border: 3px #bfa solid;
	font-size: 30px;
	overflow: auto;
}
.content>:nth-child(1) {
	font-size: .5rem;
	height: 300px;
}
.content>:nth-child(2) {
	font-size: .5em
}
/**
	这里出现了元素溢出,然后就会出现滚动条,下拉就可以看到,第二个子元素的内容
*/

7.position

​ 它用于控制元素再页面上的定位方式

​ 有五个属性值,分别是static,relative,absolute,fixed,sticky他们的功能是

​ static:静态定位,默认值,按正常文档流排序

​ relative:相对定位,相对于其在普通流中的父元素进行定位,可以使用left,top,bottom,right进行调位置

​ absolute: 绝对定位, 相对于最近的开启了position且值不为static的元素进行定位。会脱离文档流

​ fixed:固定定位,相对于浏览器的窗口进行定位,无论页面如何滑动,该元素都会保持在相同的位置

*{
	padding: 0;
	margin: 0;
}
html {
	font-size: 20px;
}
.container {
	width: 100%;
	height: 100%;
	position: relative;
}
.content {
	width: 200px;
	height: 200px;
	box-sizing: border-box;
	padding: 20px;
	border: 3px #bfa solid;
	font-size: 30px;
	position: fixed;
	top: 0;
}
.content>:nth-child(1) {
	font-size: .5rem;
}
.content>:nth-child(2) {
	font-size: .5em
}
.longer {
	height: 1300px;
	background-color: whitesmoke;
}

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" href="./style.css">
	</head>
	<body>
		<div class="container">
			<div class="content">
				<p>我是相对于html的字体</p>
				<p>我是相对于父元素字体</p>
			</div>
			<div class="longer"></div>
		</div>
	</body>
</html>

​ sticky: 粘滞定位,是一种特殊的定位方式,它结合了相对定位和固定定位的特点,粘性定位的元素在滚动到一定位置之前表现为相对定位,即元素按照正常文档流进行排列;当页面滚动到特定位置时,元素会“粘”在指定位置,表现为固定定位

修改一下即可看到效果:

position: sticky;
top: 0px;

8. float

​ 浮动布局,允许一个元素脱离文档流,向左或者向右浮动

​ 主要有三个属性值:none,left ,right

​ 三个属性值的作用是设置不浮动向右或者向左浮动

*{
	padding: 0;
	margin: 0;
}
html {
	font-size: 20px;
}
.container {
	border: 2px solid skyblue;
	padding: 5px;
}
.content {
	width: 200px;
	height: 200px;
	background-color: aquamarine;
	float: left;
}


<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" href="./style.css">
	</head>
	<body>
		<div class="container">
			<div class="content">
				
			</div>
		</div>
	</body>
</html>

这样的话,就开启了浮动,但是也带了一个问题,就是高度塌陷

所以就需要清除浮动所带来的影响,三种解决,1. 使用伪类。2. 父元素一起浮动,3. 开启bfc

什么是bfc

​ bfc是一个独立的渲染环境,在计算高度时,也会计算浮动元素的高度,所以就解决了高度塌陷的问题

​ 在同一个bfc的元素外边距会重叠,可以触发不同的bfc去计算,解决外边距重叠的问题

​ 主要作用就是解决上下外边距重叠和清楚浮动所带来的影响

*{
	padding: 0;
	margin: 0;
}
html {
	font-size: 20px;
}
.container {
	border: 2px solid skyblue;
	overflow: hidden; /* 触发bfc */
	padding: 5px;
}
.content {
	width: 200px;
	height: 200px;
	background-color: aquamarine;
	float: left;
}


vue

1. 什么是单向数据流

在 Vue 中,数据流主要遵循一个父到子的方向。父组件通过 props 向子组件传递数据,而子组件不能直接修改通过 props 接收的数据。如果子组件需要基于接收到的数据做一些修改,它应该通过事件(如自定义事件)来通知父组件进行更改,然后由父组件来决定是否更新数据

案例:

<script setup lang="ts">
import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";
import {ref} from "vue";
const list = ref([1,2,3,4,5])
const handleClick = (name:string,...args) => {
  console.log(name,"父------>")
  console.log(args)
}
</script>
 
<template>
 
 <BaseRefAndReactive title="这是标题" :list="list" @on-click="handleClick"></BaseRefAndReactive>
</template>
 
<style scoped>
 
</style>

<script setup lang="ts">
withDefaults(defineProps<{
  title: string,
  list: number[]
}>(), {
  title: 'default title',
  list: () => [1,2,3]
})
let emit = defineEmits(["on-click"])
const handleClick = () => {
    emit("on-click", "我是子组件1",123,"as",{name:"张三"})
}
</script>
 
<template>
  <div>
    <h1>{{ title }}</h1>
    <ul>
      <li v-for="(item,index) in list" :key="index">{{ item }}</li>
    </ul>
    <button @click="handleClick">传值</button>
  </div>
</template>
 
<style scoped>
 
</style>

2. vue router如何进行传参

提供了两种方式进行传参

  1. query

    查询参数是通过URL的查询字符串来传递的,它们会被附加在URL的?后面,并以&分隔。这种方式适合非敏感数据的传递,如搜索词等。

    query必须要是一个对象

    import {useRouter} from 'vue-router'
    const router = useRouter()
    router.push({ path: '/foo', query: { userId: 123 }})  
    // 结果的URL将会是 /foo?userId=123
    
    

    接受参数

    import {useRoute} from 'vue-router'
    const route = useRoute()
    created() {  
      // 访问查询参数  
      console.log(route.query.userId) // 输出:123  
    }
    
    
  2. 动态路由匹配

    当路由路径中包含动态片段时,如/user/:id,可以通过params来传递参数,但注意,这种方式与query不同,它不会在URL中直接显示参数。它通常与路由的嵌套或命名视图一起使用,并通过name来指定路由,然后使用params来传递参数。

    使用params必须使用name的形式,使用path的形式不可以

    import {useRouter} from 'vue-router'
    import {ref} from 'vue'
    const userID = ref(1)
    function toDetails (item:Data) {
      router.push({
        name: "B",
        params: {
          id: userID
        }
      })
    }
    
    

    获取:

    import {useRoute} from 'vue-router'
    const route = useRoute()
    created() {  
      // 访问查询参数  
      console.log(route.params.userID) // 输出:1  
    }
    
    
  3. 两者的区别

    query 传参配置的是 path,而 params 传参配置的是name,在 params中配置 path 无效

    query 在路由配置不需要设置参数,而 params 必须设置

    query 传递的参数会显示在地址栏中

    params传参刷新会无效,但是 query 会保存传递过来的值,刷新不变 ;

3. vue-router hash模式和history模式的区别
  1. url的区别

    hash模式:http://localhost:3000/#/router

    会包含一个#,#后面是路由的路径

    history模式:http://localhost:3000/router

    不包含#,端口号后面既是路由的路径

  2. 实现的方式不同

    hash模式:原理是 HTML5 的 window.location.hash 属性,这个属性值的改变(即 URL 中 # 后面的部分)不会导致页面重新加载

    history模式:利用了 HTML5 History API 来实现 URL 的跳转而不重新加载页面

4. vuex

Vuex是一个状态管理模式和库,用于集中式存储管理Vue.js应用中的所有组件的状态

有五个核心api

  1. state,是Vuex中的基本数据对象,用于存储应用的状态,它类似于组件的data属性
  2. getters:类似于组件的计算属性,可以利用state中的值,计算出新的值
  3. mutations:用于修改state里面的数据,必须是同步的函数,通过commit方法调用mutations
  4. actions:,可以执行异步操作(如API调用),并在操作完成后通过commit方法调用mutations来修改stat
  5. Modules:Vuex允许我们将store分割成模块(modules)。每个模块拥有自己的state、mutations、actions和getters等
5. public文件夹下的文件和src下的assert文件夹下的文件有什么区别

​ public文件夹下的文件在打包的时候不会被打包,直接放在根目录下,可以直接去访问,assert文件夹里的文件会随着src文件夹一起打包。

typescript

1. type和interface的区别
  1. 使用场景的不同

    • Type Aliases (type) 通常用于简单的类型定义,如字面量类型、联合类型、元组等。
    • Interfaces (interface) 更适合定义对象的形状,尤其是当对象包含方法时。同时,由于支持继承,它更适合构建复杂、可扩展的类型系统。
  2. 声明合并

    interface,同名的话,会将两个同名的interface进行合并而type却不可以

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值