Vue面试八股文一
说一说MVVM、MVC 和 MVP 模式的区别是什么?
MVVM、MVC 和 MVP 是三种常见的软件架构模式,它们在前端开发中都有广泛的应用。下面是它们之间的主要区别:
- MVC(Model-View-Controller):
- 模型(Model):负责处理应用程序的数据和业务逻辑。
- 视图(View):负责展示数据给用户,通常是用户界面。
- 控制器(Controller):接收用户的输入,并将其转换为对模型的操作,然后更新视图。
- 在 MVC 模式中,视图和模型之间是通过控制器进行通信的,控制器起到了桥梁的作用。
- MVP(Model-View-Presenter):
- 模型(Model):与 MVC 中的模型相同,负责处理数据和业务逻辑。
- 视图(View):负责展示数据给用户,与 MVC 中的视图类似。
- presenter(Presenter):负责协调模型和视图之间的交互,将用户的输入转换为对模型的操作,并将模型的更新反映到视图上。
- 在 MVP 模式中,视图和模型之间没有直接的通信,它们通过 presenter 进行交互。
- MVVM(Model-View-ViewModel):
- 模型(Model):与 MVC 和 MVP 中的模型相同,负责处理数据和业务逻辑。
- 视图(View):负责展示数据给用户,与 MVC 和 MVP 中的视图类似。
- 视图模型(ViewModel):是 MVVM 模式的核心,它是一个数据绑定的抽象层,负责将模型的数据转换为视图可以理解的格式,并将视图的用户输入转换为对模型的操作。
- 在 MVVM 模式中,视图和模型之间通过数据绑定进行通信,视图模型起到了数据转换和协调的作用。
总结:
- VC更适用于传统的服务器渲染应用。
- MVP适用于Web应用和桌面应用,但逻辑较为复杂。
- MVVM 非常适合前端框架如Vue、React、Angular,能够更好地处理UI 绑定和状态管理。
扩展:
- MVC:最早提出的设计模式之一,广泛应用于Web开发,如传统的JavaEE、ASP.NET应用中。典型的例子是使用SpringMVC框架进行Java Web开发,View层通常使用JSP或Thymeleaf。
- MVP:在Android 开发中使用较多。在 Android 中,由于 Activity 和Fragment 既承担了逻辑又负责了 UI 渲染,使用 MVP 模式可
以通过Presenter来分离这些责任,使得代码更易维护和测试。 - MVVM:不仅在Vue中使用,而且在其他现代前端框架如Angular和React(虽然更偏向于Flux/Redux架构)中也有类似的实现。
MVVM使得开发者可以专注于业务逻辑和数据流,框架会处理具体的DOM操作和视图更新。Vue提供了简洁的双向数据绑定,Angular则使用了复杂的依赖注入和数据绑定,而 React 虽然采用单向数据流,但通过 Hooks、Context API 也能实现类似 MVVM 的效果。
vue-router的几种模式有什么区别?
vue-router一共有三种模式,分别是hash模式、history模式和abstract模式,其中,vue默认的路由模式是hash模式。
它们的区别在于:
hash模式:
在
hash
模式下,vue-router
使用 URL 的哈希部分(即#
后面的部分)来表示路由,当浏览器的 URL 发生变化时,hashchange
事件会被触发,vue-router
可以通过监听这个事件来更新页面的内容。优点:是兼容性好,所有浏览器都支持;缺点是 URL 中会出现
#
符号,不太美观。history模式:
在
history
模式下,vue-router
使用浏览器的历史记录 API 来管理路由。它通过pushState
和replaceState
方法来改变 URL,并监听popstate
事件来更新页面的内容。优点:是 URL 更加美观,没有
#
符号;缺点是兼容性不如hash
模式,需要服务器端进行相应的配置,以处理路由的跳转。abstract模式:
abstract模式一般用于nodejs服务端或者老旧浏览器(IE8、9)。在这种模式下,vue-router会放弃URL,改用JavaScript变量来维护路由队列。
什么是 Vue 指令? Vue 有哪些常用的指令?
- v-bind:用于绑定 HTML 元素的属性,例如
v-bind:src
可以绑定图片的src
属性。简写 ==> : - v-on:用于绑定事件处理程序,例如
v-on:click
可以绑定点击事件。简写 ==> @ - v-model:用于在表单元素中实现双向数据绑定,例如
v-model
可以绑定输入框的值。 - v-if:用于根据条件控制元素的显示和隐藏,例如
v-if="condition"
可以根据条件显示或隐藏元素。 - v-else:与
v-if
一起使用,用于在条件不成立时显示另一个元素。 - v-for:用于循环渲染列表,例如
v-for="item in items"
可以循环渲染一个列表。 - v-show:用于根据条件控制元素的显示和隐藏,与
v-if
不同的是,v-show
只是通过 CSS 的display
属性来控制元素的显示和隐藏,而不是真正地删除或添加元素。 - v-html:用于渲染 HTML 内容,例如
v-html="htmlContent"
可以渲染一段 HTML 内容。 - v-text:用于渲染文本内容,例如
v-text="textContent"
可以渲染一段文本内容。 - v-pre:用于跳过元素的编译过程,例如
v-pre
可以跳过一个元素的编译过程,直接显示原始的 HTML 内容。
vue实例挂载的过程中发生了什么?
- 创建 Vue 实例:首先,通过
new Vue()
创建一个 Vue 实例。在创建实例时,可以传入一个选项对象,用于配置 Vue 实例的各种属性和方法。 - 初始化数据:Vue 实例会对传入的选项对象进行处理,包括初始化数据、计算属性、方法、生命周期钩子等。
- 编译模板:如果 Vue 实例有模板选项,那么会对模板进行编译,将模板转换为渲染函数。
- 创建render函数 :无论是编译模板还是用户提供了render函数,Vue都需要一个render函数来生成虚拟DOM。
- 触发beforeMount钩子 :在挂载开始之前,相关的
beforeMount
生命周期钩子将被调用。 - 虚拟DOM的创建与渲染:Vue通过render函数生成虚拟DOM,并调用渲染器将虚拟DOM渲染为真实DOM。
- DOM替换或插入 :生成的真实DOM将替换挂载元素,或者插入到挂载元素中。
- 触发mounted钩子 :一旦完成DOM插入,
mounted
生命周期钩子将被调用,表明挂载过程结束。
整个挂载过程是Vue实例从开始创建到最终渲染完成的过程,它作为Vue的生命周期的一个部分,确保了组件按照既定的方式被正确编译和渲染到页面上。
说说你对Vue生命周期的理解
- 创建阶段:在这个阶段,Vue 会创建一个空的实例,并初始化一些数据和方法。这个阶段的钩子函数有
beforeCreate
和created
。 - 挂载阶段:在这个阶段,Vue 会将实例挂载到 DOM 元素上,并渲染视图。这个阶段的钩子函数有
beforeMount
和mounted
。 - 更新阶段:在这个阶段,当数据发生变化时,Vue 会更新视图。这个阶段的钩子函数有
beforeUpdate
和updated
。 - 销毁阶段:在这个阶段,Vue 会销毁实例,并释放相关的资源。这个阶段的钩子函数有
beforeDestroy
和destroyed
。
Vue 还提供了一些其他的钩子函数,例如 activated
和 deactivated
,用于在组件被激活或停用的时候执行一些操作。
Vue3中watch和watchEffect有什么区别
Vue 中,watch
和 watchEffect
是用来观察和响应 Vue 实例上数据变化的两种不同方式,它们有以下区别:
- 定义方式 :
watch
需要显式地指定一个或多个要观察的数据源,并且对其变化做出响应。watchEffect
不需要指定观察的数据源。它会自动追踪执行过程中所有响应性依赖,并在其中任何依赖变更时重新运行。
- 使用方式 :
watch
要求传入要观测的数据源,以及一个回调函数,当观测的数据发生变化时,回调函数就会被执行。watchEffect
只需要一个函数作为参数,该函数内部使用到的所有响应式状态都会被追踪。
- 返回值:
watch
的回调函数可以返回一个函数,用于在监听结束时执行一些清理操作watchEffect
的回调函数不能返回任何值。
总之,watch
提供了更多的控制能力,适合于需要更细粒度反应变化的场景,而 watchEffect
则适合于不需要精确控制或响应多个数据源变化的场景。
vue中为什么data属性是一个函数而不是一个对象?
因为在Vue组件中,每个组件实例都应该维护一份被返回对象的独立的拷贝,作为自己的状态。如果data是一个对象,则意味着所有的组件实例将共享同一个data对象,那么一个组件实例对data对象的修改,将影响到所有组件实例。
通过使用一个函数返回数据对象的方式,Vue在初始化组件实例时会调用data函数,从而保证每一个组件实例都可以获取到一份独立的数据副本,这样组件之间的数据就不会相互影响了。
所以,在Vue组件中定义data时要用一个函数来返回一个初始数据对象,这是Vue设计的一部分,以确保组件数据的独立性和可复用性。
Object.defineProperty和Proxy 的区别在哪里呢?
object.defineProperty
和 Proxy
都是 JavaScript 中的特性,它们提供了不同的方式来拦截和定义对象的行为。以下是两者的主要区别:
- 拦截能力 :
Object.defineProperty
只能拦截对属性的访问和设置操作,不能拦截其他操作,例如属性的删除、对象属性的遍历等。Proxy
能拦截多达 13 种不同的对象操作,包括属性的读取、设置、枚举、删除等,以及更多高级操作,如有对象原型的变更、函数调用等。
- 使用方式 :
Object.defineProperty
需要直接在一个已有的对象上操作定义属性。Proxy
在目标对象外部创建一个新的代理对象,不直接操作原始对象,而是通过这个代理对象来管理对原始对象的所有访问。
- 性能 :
- 对于 Vue.js 的使用情境而言,
Object.defineProperty
对于大量属性的对象可能会有性能问题,因为需要递归遍历对象的每个属性。 Proxy
可以更高效地处理嵌套对象和数组等情况,因为只有当访问到某个属性时,才会对该属性做代理处理。
- 对于 Vue.js 的使用情境而言,
- 灵活性和动态性 :
Object.defineProperty
不能监测到属性的添加和删除,一旦定义了 getter 和 setter,就无法添加新的响应式属性,除非再次调用Object.defineProperty
。Proxy
可以轻松地监测到对象结构的任何变化,包括属性的添加和删除。
Vue中的$nextTick有什么作用?
$nextTick
是 Vue.js 中的一个全局 API,它的作用是在下次 DOM 更新循环结束之后执行延迟回调。在 Vue.js 中,数据的更新是异步的,当你修改了数据后,DOM 不会立即更新,而是会在下一次事件循环中进行更新。如果你需要在 DOM 更新后执行一些操作,就可以使用 $nextTick
方法。
$nextTick
方法接受一个回调函数作为参数,这个回调函数会在 DOM 更新后执行。
简而言之:因为Vue 采用的是异步更新 DOM 的机制,统一在一个异步任务中去批量更新 DOM,$nextTick会在数据和视图都更新完毕时触发内部的回调函数
$nextTick的实现原理:
Vue 利用了JS中的事件循环机制来实现 $nextTick
,$nextTick
会使用 setTimeout
+ Promise.then()
来把回调函数添加到微任务队列里, 确保在数据变化引发的 DOM 更新操作后,再去执行 $nextTick
里的回调函数
说说你对keep-alive的理解是什么?
keep-alive
是 Vue.js 中的一个内置组件,它可以在组件切换时缓存组件的状态,避免组件被重新创建和销毁,从而提高性能和用户体验。
- 组件状态保持 :
keep-alive
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。这样,用户切换回缓存的组件时,组件状态将会保留,如滚动位置、用户输入等。 - 性能优化 :当组件在频繁渲染的情景下(例如切换标签页),使用
keep-alive
可以避免重新创建组件实例,节省性能开销。 - 生命周期钩子 :
keep-alive
会影响内部组件的生命周期钩子。被缓存的组件不会再触发destroyed
钩子,而是会触发deactivated
钩子;当组件再次激活时,会触发activated
钩子。 - 包含与排除 :
keep-alive
提供include
和exclude
属性,允许组件根据组件名被缓存或不被缓存。这提供了更细致的控制,可以缓存某些组件,而让其他组件每次都重新渲染。 - 最大缓存数 :通过设置
max
属性,可以指定keep-alive
能够缓存的最大组件实例数目。超过这个数目时,最久未访问的组件缓存将会被丢弃。 - 典型应用场景 :
keep-alive
常见于如标签页切换、路由切换中不需要每次都重新加载的场景,例如一个复杂的列表页,如果没有keep-alive
,每次切换回来时都需要重新发送请求获取数据并渲染。
你知道vue中key的原理吗?说说你对它的理解
在 Vue 中,key
是一个特殊的属性,它用于在虚拟 DOM Diff 算法中优化列表渲染的性能。key
的原理是给每个列表项一个唯一的标识,以便 Vue 能够准确地识别哪些列表项需要更新,哪些需要删除,哪些需要添加。
当 Vue 渲染一个列表时,它会根据 key
的值来判断列表项是否发生了变化。如果 key
的值没有变化,Vue 会认为列表项没有发生变化,从而复用之前的 DOM 元素,而不是重新创建一个新的 DOM 元素。这样可以避免不必要的 DOM 操作,提高渲染性能。
如果 key
的值发生了变化,Vue 会认为列表项发生了变化,从而重新创建一个新的 DOM 元素,并将其插入到正确的位置。这样可以确保列表项的顺序和位置是正确的。
需要注意的是,key
的值必须是唯一的,否则 Vue 无法准确地识别列表项的变化。通常情况下,我们可以使用列表项的 ID 或者其他唯一的标识作为 key
的值。
Vue常用的修饰符有哪些?有什么应用场景 ?
在 Vue 中,修饰符是一种特殊的指令后缀,用于对指令进行一些特殊的处理。Vue 常用的修饰符有以下几种:
事件修饰符:事件修饰符用于对事件进行一些特殊的处理,例如阻止事件冒泡、阻止默认行为、按键修饰符等。
.prevent
- 阻止默认事件修饰符
.stop
- 阻止事件冒泡修饰符
.capture
- 事件捕获模式修饰符
.once
- 单次事件修饰符
.native
-原生修饰符,用来监听组件根元素的原生事件的。(vue3中已移除)表单修饰符:表单修饰符用于对表单元素进行一些特殊的处理,
.lazy
修饰符可以将input
事件转换为change
事件
.number
-数转修饰符 可以将输入的值转换为数字类型v-model.number="'5'"
.trim
- 去空修饰符可以去除输入值的首尾空格模型修饰符:模型修饰符用于对
v-model
指令进行一些特殊的处理,例如.sync
修饰符可以实现父子组件之间的双向数据绑定。按键修饰符:按键修饰符用于对键盘事件进行一些特殊的处理,例如
.enter
修饰符可以监听回车键的按下。
这些修饰符可以在不同的应用场景中使用,例如:
- 阻止事件冒泡:当你在一个父元素中绑定了一个事件,同时在子元素中也绑定了一个相同的事件,如果你不希望子元素的事件冒泡到父元素中,可以使用
.stop
修饰符。 - 阻止默认行为:当你在一个链接或者表单中绑定了一个事件,如果你不希望链接或者表单的默认行为发生,可以使用
.prevent
修饰符。 - 按键修饰符:当你需要监听键盘事件时,可以使用按键修饰符来指定需要监听的按键,例如
.enter
修饰符可以监听回车键的按下。 - 表单修饰符:当你需要对表单元素进行一些特殊的处理时,可以使用表单修饰符,例如
.lazy
修饰符可以将input
事件转换为change
事件,.number
修饰符可以将输入的值转换为数字类型,.trim
修饰符可以去除输入值的首尾空格。 - 模型修饰符:当你需要实现父子组件之间的双向数据绑定时,可以使用
.sync
修饰符。
请说一说浏览器的同源策略和如何解决跨域?
浏览器的同源策略是一种安全机制。同源策略要求协议、域名和端口号都必须相同,否则就是跨域。请求能发出去, 服务器也能把数据响应回来, 但是浏览器会阻止数据给回页面
解决跨域:
CORS(跨域资源共享):
- CORS 需要服务器端进行配置,请求服务器可以在服务器端设置响应头Access-Control-Allow-Origin,允许特定的域名或者任意域名访问。
代理服务器💥💥
- 页面中请求先发给代理服务器, 代理服务器再把请求转发给接口服务器, 因为服务器与服务器之间没有跨域问题, 接口服务器能把响应的数据给到代理服务器, 因为代理服务器和项目运行的本地服务器是同源的, 所以浏览器不会触发同源保护
- 开发环境中使用代理:在Vue项目中,常使用vue.config.js配置文件中的devServer.proxy选项来设置代理。
- 生产环境中nginx配置反向代理:在生产环境中,可以在nginx服务器上配置反向代理来实现跨域。
JSONP(只支持GET请求):
- 主要利用
<script>
标签没有跨域限制的特性,通过动态创建<script>
来发送带有回调函数的GET请求。需要后端配合输出JSONP格式的响应。
- 主要利用
项目上线:
- 如果项目上线, 可以把网站项目和接口服务器部署在同一个源,就不存在跨域问题
- 如果项目上线, 网站和接口服务器不在一个源,
- 配置服务器端 CORS(跨域资源共享)
- 使用反向代理服务器(如 Nginx)
在vue项目中如何通过代理服务器解决跨域?
在Vue项目中,常使用vue.config.js配置文件中的devServer.proxy选项来设置代理。
通过这种方式,开发服务器会在后台将请求转发至指定的服务器地址,从而绕过浏览器的同源策略。
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: {
'/api': {
target: 'http://example.com', // 这里替换成实际要跨域请求的目标服务器地址
changeOrigin: true,
pathRewrite: {
'^/api': '' // 重写路径,将请求路径中的'/api'去掉,这样实际请求的就是目标服务器对应的真实路径
},
'/abc': {
// ...
}
}
}
}
})
在用axios发请求时, 就不需要配置基地址, 如果所有的接口请求中有相同字段, 比如: /api, 代理服务就可以用/api去匹配接口, 走代理服务器转发。
说一说vue中组件之间如何通信?
vue中组件的通信: 父子组件通信 / 兄弟组件通信 / 跨组件通信
父子组件通信( props和$emit )
父传子
父组件中通过
v-bind
指令将数据绑定到子组件的属性上子组件中通过在
props
选项中定义属性来接收从父组件传来的数据在props选项中定义的属性可以做类型, 非空, 可选, 自定义类型校验
子传父
子组件通过
$emit
方法触发自定义事件,并将数据作为参数传递给父组件在父组件中通过
v-on
指令监听子组件触发的事件,并在事件处理函数中获取子组件传递的数据
兄弟组件通信
- 使用事件总线(Event Bus):创建一个新的 Vue 实例作为事件总线,各个组件可以通过在这个事件总线上
$on
监听事件以及$emit
触发事件来实现相互之间的数据传递,相当于一个全局的事件中心。 - 注意点: vue3中不能直接用, 一般用
mitt
库
- 使用事件总线(Event Bus):创建一个新的 Vue 实例作为事件总线,各个组件可以通过在这个事件总线上
跨层级组件通信
- 通过
provide
和inject
:祖先组件通过provide
选项提供数据或方法,后代组件(无论层级多深)可以通过inject
选项来注入并使用祖先组件提供的内容,这样就跨越了中间的组件层级实现了通信 - 注意点: vue2中传递数据如果需要时响应式的则需要传递 对象 或 数组, vue3则使用ref函数或reactive函数即可
- 通过
使用 Vuex 或 Pinia(状态管理库)
Vue中的过滤器了解吗?过滤器的应用场景有哪些?(vue3已废弃)
在Vue中,过滤器(Filters)是一些可以用在模板表达式中的特殊函数,主要用于文本格式化。它们可以用在双花括号插值和v-bind表达式中,过滤器应该被添加在JavaScript表达式的尾部,由“管道”符号表示。
过滤器可以在模板中使用 |
符号来调用,例如:
<!-- 定义局部过滤器 -->
filters: {
formatDate(value) {
if (!value) return '';
// 这里使用了moment.js库来格式化时间
return moment(value).format('YYYY-MM-DD HH:mm:ss');
}
}
<!-- 定义全局过滤器 -->
Vue.filter('formatDate', function (value) {
if (!value) return '';
return moment(value).format('YYYY-MM-DD HH:mm:ss');
});
//然后在模板中,我们可以这样使用这个过滤器:
<span>{{ timestamp | formatDate }}</span>
// 在这个示例中,管道符 | 起到的作用是指明后面跟随的是一个过滤器。
// 在Vue中,管道符用于将数据传递给过滤器函数,并将其作为第一个参数。
// 过滤器函数可以接受额外的参数,如果需要的话,可以在过滤器名之后通过管道符继续传递。
// timestamp 是要处理的原始数据,管道符 | 告诉Vue接下来将使用名为 formatDate 的过滤器来处理这些数据。
// formatDate 是定义在组件的 filters 对象中的一个方法,它会接收 timestamp 作为输入参数,处理后返回格式化的日期字符串,这个字符串随后将展示在页面的 <span> 元素之内。
Vue中过滤器的应用场景包括:
- 日期、时间格式化:如上所示,可以将时间戳转换为更可读的日期格式。
- 文字截断:创建一个截短文字并添加省略号的过滤器,用于长文本的预览显示。
- 货币格式化:将数值转换显示为货币格式,如将1000转换成$1,000.00。
- 大小写转换:将文本的字母转换成全大写或全小写。
- 自定义文本格式:比如添加前缀、后缀,或者其他自定义文本操作。
谈谈你对slot的了解
在 Vue 中,slot
是一种用于在组件中插入内容的机制。它允许我们在父组件中定义一些内容,并将其插入到子组件的指定位置。
slot
可以分为以下几种类型:
- 匿名插槽:匿名插槽是最基本的插槽类型,它没有名称。在子组件中,可以使用
<slot>
元素来定义匿名插槽。在父组件中,可以直接在子组件标签内部插入内容,这些内容将被插入到子组件的匿名插槽中。 - 具名插槽:具名插槽是带有名称的插槽。
- 在子组件中,可以使用
<slot name="slotName">
元素来定义具名插槽。 - 在父组件中,可以使用
<template v-slot:slotName>
指令来指定要插入到具名插槽中的内容。
- 在子组件中,可以使用
- 作用域插槽:作用域插槽是一种特殊的插槽类型,它允许子组件向父组件传递数据。
- 在子组件中,可以使用
<slot name="slotName" :data="data">
元素来定义作用域插槽,并通过:data
属性向父组件传递数据。 - 在父组件中,可以使用
<template v-slot:slotName="slotProps">
指令来接收子组件传递的数据,并在插槽内容中使用这些数据。
- 在子组件中,可以使用
SSR了解吗?SSR解决了什么问题?有做过SSR吗?你是怎么做的?
SSR(Server-Side Rendering,服务器端渲染)是一种将应用程序的界面在服务器上进行预先渲染并以 HTML 形式发送到客户端的技术。与传统的客户端渲染(CSR)相比,SSR 在服务器端生成完整的 HTML 页面,然后将其发送到浏览器,以提供更好的性能和搜索引擎优化。
SSR 的优势包括:
- 首屏加载性能提升:客户端不需要等待所有的JavaScript都加载完毕才能呈现页面,服务器直接发送渲染后的HTML页面,减少了首屏加载时间。
- 搜索引擎优化(SEO):由于搜索引擎更容易抓取渲染后的HTML页面,SSR可以提升应用的可搜寻性。
- 减轻浏览器负担:由于部分页面渲染在服务器端完成,客户端所需处理的工作相比单页应用(SPA)减少了。
需要注意的是,SSR 可能会增加服务器负载和响应时间。因此,在选择是否使用 SSR 时,需要根据项目需求和复杂性来权衡利弊。
TS和JS的区别?
我有使用TypeScript的经验。TypeScript是JavaScript的一个超集,它在JavaScript的基础上增加了一些特有的特性,主要的区别包括:
类型系统:
- TypeScript引入了静态类型检查,可以在编写代码时检查类型错误。这可以大大减少运行时的错误,提升代码的质量和可维护性。
- 在JavaScript中,类型是动态的,并且不提供静态类型检查。
编译时错误检查:
- 由于TypeScript的类型系统,大多数类型错误会在编译阶段被捕捉到,而不是在运行时。这加快了错误的发现和修复速度。
- JavaScript中的错误大多数在运行时发现,这可能导致生产环境中的bug。
类型注解和类型推断:
TypeScript允许开发者为变量、函数参数和返回值等指定类型。如果没有指定类型,TypeScript也能够根据代码上下文自动推断出类型。
JavaScript不支持类型注解,它是一个纯动态类型语言。
接口和枚举:
- TypeScript支持接口(Interfaces)和枚举(Enums),这有助于构建更加严谨和可维护的代码结构。
- JavaScript的ES6之前并没有接口的概念,虽然ES6加入了Class,但是并没有Enum。
Generics(泛型):
- TypeScript支持泛型,这允许开发者编写可重用的组件,而这些组件能够支持多种类型的数据。
- JavaScript本身不支持泛型。