安卓面经(25/30)MVC、MVP、MVVM全解析

牛客高级系列专栏:

安卓(安卓系统开发也要掌握)

嵌入式

本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对常见安卓高频开发面试题的理解;

网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,承诺提供专栏内容免费技术答疑,直接咨询即可。助您提高安卓面试准备效率,为您面试保驾护航!

正文开始⬇

MVC、MVP、MVVM是安卓开发中使用较多的软件架构设计模式,特别是MVVM更是面试官喜欢提问的模式。面试官可能会问:

  1. 说说你对MVC、MVP、MVVM的理解,他们有什么区别和联系,如何演变的? ⭐⭐⭐⭐⭐
  2. MVVM的优点和缺点 ⭐⭐⭐⭐
  3. 为什么Activity旋转屏幕后ViewModel可以恢复数据 ⭐⭐
  4. ViewModel 的实例缓存到哪儿了 ⭐⭐
  5. 什么时候 ViewModel#onCleared() 会被调用 ⭐⭐

看完以下的解析,一定可以让面试官眼前一亮。

目录

  • 1、MVC
  • 2、MVP
  • 3、MVVM
  • 4、MVVM总结
    • 4.1 MVVM的优点和缺点
    • 4.2 ViewModel 的实例缓存到哪儿了
    • 4.3 为什么Activity旋转屏幕后ViewModel可以恢复数据
    • 4.4 什么时候 ViewModel#onCleared() 会被调用

1、MVC

如果需要设计一套人脸识别系统,用户输入自己姓名后,站在机器前面,机器就可以自动拍照并识别出用户的性别。程序员小帅不考虑软件架构,直接动手撸代码,最终功能虽然实现了,但是各种业务代码都写在同个文件里。过了一段时间,领导让小帅新增识别年龄的功能时,才发现代码非常臃肿,不利于二次开发。看过本文后,小帅重新设计了代码的架构:

  • View:界面相关,将计人脸识别系统UI界面相关代码抽离出来,对应于xml布局文件
  • Controller:控制相关,将和UI界面操作相关以及判断相关的业务逻辑也抽离出来,比如判断用户是否输入姓名了,没有输入则不会开始采集用户照片等,对应于Activity
  • Model:数据相关,具体的性别识别算法运算等较为复杂的运算,本地数据的维护等;

其中View和Model,一个单独负责界面,一个负责处理数据,比较好理解。而Controller就是接收View的指令,做一些逻辑判断或者调用Model做复杂运算后,再把结果反馈给View。通过如此分工,写页面,写业务逻辑,数据运算三者相互分离。

alt

然而,在实际的Android开发中,View层对应xml布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller,使得Activity变得臃肿。

而且如果不涉及复杂的运算,那么Controller处理完简单的逻辑判断后就直接返回给View。甚至View可以跳过Controller,直接和Model进行交互。这就造成,本来Controller本来就应该依赖View和Model,而现在View也依赖Controller和Model,耦合性还是太高了,于是我们希望View和Model之间要彻底断开联系。

2、MVP

在MVC的基础上,用Presenter代替Controller,此时MVP具体为:

  • View:不仅负责UI界面相关操作,还要负责用户交互,相当于Activity
  • Model:依然主要负责数据处理;
  • Presenter:负责View和Model之间的交互;

首先,明确下理解,在MVC中的View相当于布局文件,而在MVP中View相当于Activity;此时View和Model不再直接交互,而是使用Presenter作为二者交互的桥梁。Presenter里同时持有View和Model的引用,View也有Presenter的引用,但没有Model的引用。

当View层中某个UI界面发起某个数据操作时,会调用Presenter的引用,Presenter再调用Model层的引用请求数据。当Model层的数据处理完后会调用Presenter层的回调通知Presenter层数据加载情况,最后Presenter层再调用View层的接口将加载后的数据展示给用户看。Model层是不会直接和View层产生通讯的。

alt

如果想看看基于MVP写的代码,可以看看这个:MVPdemo

3、MVVM

如果你是做安卓系统开发,那么你可能会接触到MVC或者MVP,而不需要用到MVVM。但如果你是安卓应用开发,那么一定要懂得MVVM。

在MVP中,Presenter更新View也需要很多烦人的代码,有没有什么方法,告诉View去监听某个数据,该数据发生变化时,View自动跟着变化呢?当然是有的,ViewModel随之诞生。ViewModel代替了Presenter,MVP变成了MVVM:Model-View-ViewModel。

  • Model:和MVP中的Model一样,还是用来处理数据的;
  • View:还是用来更新和处理UI界面以及和用户交互,相当于Activity。和MVP里的View的区别在于,View界面的更新从由Presenter驱动,变成了自动监听数据,随着数据变化而自动更新;
  • ViewModel:Model和View的中介,处理逻辑中转任务的媒介。一个View可以绑定多个不同的ViewModel,一个ViewModel也可以被多个View同时绑定。

那么,MVVM是如何做到View的自动更新呢?在此之前,需要掌握几个新概念:

  • LifeCycle:LifeCycle是Google官方提供的管理生命周期事件的方法,可以更加方便地监听声明周期变化事件;
  • LiveData:这是一种用来持有数据的对象,可以设置多个观察者监听LiveData对象,当数据发生改变时,会通知处于active状态(Started和Resume状态)的观察者,并执行设置好的动作。举个例子,我们可以提前设置ActivityA,ActivityB,FragmentA,这三者分别都去监听一个变量名为studentName的LiveDataA对象,也就是在这几个类里,都有下面几行代码。当studentName数值发生改变时,就设置tv_name控件的text改为studentName的新值。那么,当studentName发生改变时,假设ActivityA和FragmentA刚好处于active状态,则ActivityA和FragmentA就会执行tv_name.text = it,自动的修改控件的文本内容。而ActivityB不处于active状态,则不会有任何操作。
myViewModel.studentName.observe(this) {
    tv_name.text = it
    }
  • DataBinding:这是Googel发布的一个用来支持MVVM模式的框架。主要用来降低布局和逻辑的耦合度。以前都是在xml布局文件写好控件后,在Activity通过findViewById()方法绑定控件后,对控件做操作。而随着DataBinding的产生,我们可以直接在xml布局文件里直接将控件和数据绑定在一起,当数据改变时,控件自动更新。

结合LiveData+DataBinding举个例子增进理解,以下例子参考于MVVMDemo ,读者可以下载该开源项目代码实际运行看看:

首先看看布局文件:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    >

    <data>
        <!-- 1 -->
        <variable
            name="image"
            type="com.zw.mvvmdemo.bean.ImageUrlBean.UrlBean"/>
    </data>


    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/myImageView"
            <!-- 2 -->
            imageUrl="@{image.BASE_IMAGE_ADDRESS_URL + image.url}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="match_parent"
            android:layout_height="200dp" />
    </android.support.constraint.ConstraintLayout>

</layout>

首先看[注释1],可以创建<data>变量:

<variable
    name="image"
    type="com.zw.mvvmdemo.bean.ImageUrlBean.UrlBean"/>

在variable标签中:

  • name:需要绑定数据的名称,名称可自定义;
  • type:需要绑定数据的类型;

所以,我们创建了一个名为“image”的<data>变量,并在[注释2]中使用,赋值给了另一个叫imageUrl的属性。imageUrl属性是我们自定义的一个属性,是通过DataBinding的注解BindingAdapter来声明的。

public class GetBingImageAdapter {

    @BindingAdapter("imageUrl")
    public static void setImage(ImageView iv, String url) { //3
        Glide.with(iv)
                .load(url)
                .into(iv);
    }
}

所以当[注释1]名为“image”的变量发生改变时,[注释2]名为“myImageView”的控件中的imageUrl属性就会随着改变,并调用[注释3]的回调函数setImage()方法,最终执行Glide这个第三方图片加载框架来刷新图片。

那么就剩下最后一个问题,[注释1]名为“image”的变量什么时候会发生改变呢?我们可以在Activity中监听从网络获取到的图片的url地址,当url地址改变时,修改“image”变量的值:

 mViewModel.getImageUrl().observe(this, new Observer<UrlData<ImageUrlBean.UrlBean>>() {
            @Override
            public void onChanged(@Nullable UrlData<ImageUrlBean.UrlBean> data) {
                mDialog.dismiss();
                if(data.getErrorMsg() != null) {
                    Toast.makeText(BingImageActivity.this, data.getErrorMsg(),
                            Toast.LENGTH_LONG).show();
                }
               
                binding.setImage(data.getData()); //4
            }
        });

其中getImageUrl()返回的变量的定义是:

    private MutableLiveData<UrlData<ImageUrlBean.UrlBean>> mData;

没错,就是一个LiveData对象。最后通过[注释4]来修改“image”变量的值。有一个知识点要注意,如果“image”的名字改为“myImageValue”,则[注释4]的函数名应该同步改为setMyImageValue()。

具体的项目源码有兴趣的同学自行下载研究:MVVMDemo 。讲到这里,大家应该就明白所谓的View能自动更新,其实是因为我们提前设置好了监听,也设置好了监听的对象数值改变后,View需要完成什么动作。

4、MVVM总结

4.1 MVVM的优点和缺点

总的来说,MVVM还是一个非常优秀的模式,主要体现在:

  • 数据和布局双向绑定,我们只需要关心数据数值的改变,而不再需要操心findViewById和setText()等繁复的代码的工作;
  • 一个View可以绑定多个ViewModel,一个ViewModel可以供多个View绑定,提高复用性和灵活性;
  • 自带生命周期管理,不用担心因为生命周期而出现bug;

当然,MVVM也有缺点:

  • View和ViewModel独立,如果出现bug定位问题较难;
  • Binding类的生成可能需要多次Rebuild的操作,若Binding类过多,则编译工作会耗时很大;
  • 需要提前设置好数据监听,如果数值多,那就要设置很多监听;
  • 如果ViewModel层数据未改变,但却强制要View层改变UI控件时,就比较麻烦;

4.2 ViewModel 的实例缓存到哪儿了

如果我们修改了系统语言再回到App,或者旋转了屏幕,都会发现APP中当前Activity重新创建了,但是ViewModel里面的LiveDa

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Android高频面试题全解析 文章被收录于专栏

#提供免费售后答疑!!花一杯奶茶的钱获得安卓面试答疑服务,稳赚不赔# Android发展已经很多年,安卓资料网上千千万,本专栏免费提供专栏内容技术答疑!!私聊当天必回。在阅读过程或者其他安卓学习过程有疑问,都非常欢迎私聊交流。

全部评论
谢谢大佬分享
点赞 回复 分享
发布于 2023-03-05 13:06 湖南
一直没搞清MVC这几个,正好学习学习
点赞 回复 分享
发布于 2023-03-05 12:54 江西

相关推荐

05-16 23:53
已编辑
东华大学 前端工程师
一面(2025/04/22)1.&nbsp;本科学的大数据,为什么选前端2.&nbsp;OJ&nbsp;项目&nbsp;24&nbsp;年&nbsp;6&nbsp;月结束,那这中间做了什么。回答搞横向+小论文,然后最近搞泡泡图项目,问横向是跟什么有关3.&nbsp;做项目之前怎么学习的4.&nbsp;301&nbsp;302&nbsp;304&nbsp;分别是什么5.&nbsp;css&nbsp;选择器6.&nbsp;Vue3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;生命周期(这里竟然忘了具体的名字了!说着说着给自己绕晕了,然后答的&nbsp;create&nbsp;有两个、mount&nbsp;有两个、update&nbsp;有两个、unmount&nbsp;有两个。。。)&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;onMounted&nbsp;主要做什么&nbsp;&nbsp;&nbsp;&nbsp;3.&nbsp;onUnmounted&nbsp;呢&nbsp;&nbsp;&nbsp;&nbsp;4.&nbsp;Diff&nbsp;算法7.&nbsp;项目中有没有用过防抖节流等函数,说到了&nbsp;OJ&nbsp;里防抖的使用&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;那防抖怎么实现的,答得&nbsp;setTimeout&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;那&nbsp;setTimeout&nbsp;会产生什么影响,不知道&nbsp;&nbsp;&nbsp;&nbsp;3.&nbsp;那问你另外一个问题,JS&nbsp;事件循环&nbsp;&nbsp;&nbsp;&nbsp;4.&nbsp;结合事件循环,再考虑第二个问题呢&nbsp;&nbsp;&nbsp;&nbsp;5.&nbsp;提示了时间可能并不准确(可以看看别人的实现是怎么避免这个影响的)8.&nbsp;了解打包工具&nbsp;Webpack&nbsp;和&nbsp;Vite&nbsp;吗,优缺点呢9.&nbsp;重构&nbsp;OJ&nbsp;时有遵循一些什么原则或者规范,母鸡,猜了模块化和单一职责原则(不是说的名字,而是说的方法跟这两个原则比较类似)10.&nbsp;Git&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;会用&nbsp;Git&nbsp;吗,流程是怎么样的&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;如何撤销提交(面试官夸我说面了这么多校招生是唯一一个知道这个的)&nbsp;&nbsp;&nbsp;&nbsp;3.&nbsp;PR&nbsp;之后如何撤销提交,这个不会,面试官也说这题超纲了11.&nbsp;有用过&nbsp;Cursor、Windsurf&nbsp;吗,我回答的&nbsp;Trae,&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;使用体验,与大模型有什么不一样的&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;有没有提前做一些配置比如告诉模型我这个项目是做什么的(我直接拿来就用了,没有这个过程)12.&nbsp;写一个&nbsp;Prompt,题目是实现一个搜索框13.&nbsp;手撕:最长递增子序列14.&nbsp;反问:技术栈、工作时间面试官长得好看,人还特别好,对我特别有耐心,面试过程中更像是聊天,会总结你说的话,也会给你提示和建议。最后手撕题遇到了一个之前秒做的题目,以为两分钟写完,结果&nbsp;debug&nbsp;了十分钟,不过面试官很耐心地一直等我写完,最后终于是写出来了。(ಥ_ಥ)
美团一面2155人在聊 查看24道真题和解析
点赞 评论 收藏
分享
腾讯二面921人在聊 查看20道真题和解析
点赞 评论 收藏
分享
一面1.介绍一下项目2.有做权限吗3.登陆之后的权限,比如说我能操作哪些文章,不能操作哪些文章(刚刚说的是如何保存登陆态)查看文章的时候最终需要拉取一个接口去查看文章,如果我已经获取了这个接口并且不断用Postman伪造发请求,如何处理这种情况?先回答在请求头里带上Token,面试官说如果通过抓包拿到了Token那怎么办呢,回答在axios请求拦截器里面去对同一来源的请求做预防,如果两次请求发送的时间太短暂或者频率太快就拦截这个请求。4.目前技术栈5.localStorage和cookie有什么区别?Cookie&nbsp;的本职工作并非本地存储,而是“维持状态”。&nbsp;因为HTTP协议是无状态的,HTTP协议自身不对请求和响应之间的通信状态进行保存Cookie指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。&nbsp;cookie是服务端生成,客户端进行维护和存储。我们可以把Cookie&nbsp;理解为一个存储在浏览器里的一个小小的文本文件,它附着在&nbsp;HTTP&nbsp;请求上,在浏览器和服务器之间“飞来飞去”。它可以携带用户信息,当服务器检查&nbsp;Cookie&nbsp;的时候,便可以获取到客户端的状态。通过cookie,可以让服务器知道请求是来源哪个客户端,就可以进行客户端状态的维护,比如登陆后刷新,请求头就会携带登陆时response&nbsp;header中的set-cookie,Web服务器接到请求时也能读出cookie的值,根据cookie值的内容就可以判断和恢复一些用户的信息状态。我们可以在响应头中的set-cookie中获取cookie,也可以直接使用js的document.cookie来设置cookiecookie的缺陷:1不够大&nbsp;每个cookie大小限制在4KB左右,cookie只能存储少量的信息,但这里的4KB是指一个cookie的键值对的值的大小,不是所有的一个域名下所有cookie的总大小&nbsp;2同一个网站每次请求都会携带cookie&nbsp;会带来性能浪费,增加不必要的开销cookie存储在浏览器和服务器LocalStorage保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。大小为5M左右仅在客户端使用,不和服务端进行通信除非手动删除,否则会永久存储在浏览器本地,相对更安全一般来说cookie存储与服务器进行交互的少量数据比如token(会话标识session_id信息)等,而localstorage存储大量的、仅前端使用的持久化数据(缓存的用户信息)。不能在&nbsp;localStorage&nbsp;中存储敏感信息(如密码、身份证号),因为它容易被&nbsp;XSS&nbsp;攻击访问到XSS:跨站脚本攻击:在其他浏览器中注入恶意的可执行的脚本代码,从而达到恶意攻击的目的。比方说一个用户评论功能,输入“&lt;p&gt;用户评论:&lt;script&gt;alert('你被攻击了')&lt;/script&gt;&lt;/p&gt;”其他人浏览这条评论时就会看到弹窗,这说明脚本被执行了,那么攻击者就可以做更危险的事情比如获取Cookie,操作或者删除DOM等等反射型(构造恶意URL,用户点击链接会发送请求,服务器会把请求的脚本作为数据的一部分返回浏览器,浏览器随机解析了这段恶意代码)DOM型(如例子,构造特定的输入引起前端更新,使用闭合标签修改DOM解构)存储型(将恶意代码提交到网站的数据库中,访问网站时,恶意代码从数据库中被读入执行)防范措施:前后端在处理用户输入的内容时,都需要保持谨慎,对其中的特殊字符进行过滤和转义。使用安全的编程方式,譬如避免使用危险的&nbsp;innerHTML&nbsp;去设置&nbsp;HTML&nbsp;内容,而是使用&nbsp;textContent。HttpOnly&nbsp;是一个&nbsp;Cookie&nbsp;的标志,表示该&nbsp;Cookie&nbsp;只能通过&nbsp;HTTP&nbsp;请求发送,而无法通过&nbsp;JavaScript&nbsp;访问。这有助于防止&nbsp;跨站脚本攻击(XSS)本质上Cookie用于通信,而LocalStorage用于存储6.服务器返回token存储在localStorage里,那这个token是不是会一直存在?登陆之后,用户就一直保持登陆状态?Token在后端一般会设置他的过期时间7.平时如何去学习?怎么去做这个项目的?8.追问:你说你刚才说看别人的项目源码,你看过哪个项目?开源项目的一些源码呀?9.Pinia是干嘛的10.使用全局变量和Pinia有什么区别?Pinia的几个重点:Store全局状态管理、响应式更新、Composition&nbsp;API、支持模块化、支持devtools使用全局变量的缺点:缺乏响应式、不能支持模块化、无法使用调试工具11.Pinia的数据存储在哪里?内存,一旦刷新页面,内存被清空,pinia的数据也会丢失。想要实现持久化存储,需要使用持久化插件使用watch监听store数据的变化,把数据存储在LocalStorage里,以后每次登陆网站都使用这个LocalStorage存储的数据。12.TCP和UDP的区别?TCP是可靠的,需要通过连接(三次握手),有流量控制和拥塞控制,传输速度较慢,传输单位是字节流(连续、无边界的数据传输方式,像水流一样不分段)UDP是不可靠的,不需要通过连接,没有流量控制和拥塞控制,传输速度较快,传输单位是数据报(独立、封包形式的数据传输方式,每一段都是一个独立的单元)流量控制:流量控制的核心目的是确保数据的发送速率不超过接收方的处理能力,防止接收方的缓冲区溢出。具体原理如下:1.接收方窗口大小:接收方为发送方提供一个&nbsp;接收窗口大小(Window&nbsp;Size),表示它可以处理的最大数据量。这个值会动态变化,接收方会不断告诉发送方它当前的接收能力。2.滑动窗口机制:TCP协议采用&nbsp;滑动窗口机制,发送方根据接收方的窗口大小来决定发送数据的数量。窗口大小可以根据接收方的缓冲区大小动态调整。当接收方的缓冲区可用空间减少时,它会通过窗口大小减小发送方可以发送的数据量。3.发送方控制:发送方根据接收方提供的窗口信息调整数据的发送速率。如果接收方的接收窗口变小,发送方就会减慢发送速率,避免数据丢失拥塞控制:拥塞控制的核心目的是避免网络拥塞,防止整个网络的性能因数据过载而下降。TCP协议采用了多种机制来动态调整发送方的发送速率。具体原理如下:1.慢启动:TCP连接刚建立时,发送方的拥塞窗口(Congestion&nbsp;Window,CWND)很小。随着每次成功的确认(ACK)到达,窗口逐渐增大。这个过程称为&nbsp;慢启动。慢启动的目标是渐进地增加发送速率,避免网络立即出现拥塞。2.拥塞避免:一旦窗口增大到一定程度(慢开始门限),TCP会进入&nbsp;拥塞避免&nbsp;阶段。在这个阶段,拥塞窗口的增大变得更加缓慢(每收到一个确认,窗口增大一个固定值)。这个阶段是为了避免突然增大的窗口造成网络过载。3.快速重传和快速恢复:•快速重传:当发送方收到三个相同的确认号(即出现丢包情况),它会立即重发丢失的数据包,而不是等待超时。•快速恢复:当发生丢包后,拥塞窗口的大小会被减少,并进入&nbsp;快速恢复&nbsp;阶段,继续发送数据,但在一个较小的窗口下,直到网络恢复正常13.两个JS题第一个是实现异步相加,第二个是实现一个QQman类,满足异步输出14.https和http有什么区别? http是明文传输,https在http基础上使用了TLS加密15.讲一讲TLS16.讲讲对称加密和非对称加密对称加密:发送方和接收方用同样的规则对数据进行加密,也可以使用同样的钥匙对数据进行解密,缺点就在于如果第三方知道了加密的规则,他就很容易进行破解传输数据。(AES算法)非对称加密:公钥:可以共享给任何人的加密措施,一般用于加密。私钥:只有拥有者持有的加密措施,一般用于解密。使用对方的公钥加密数据,这样传输的内容只有对方使用自己的私钥才能解密,就算是发送方也无法解密,这就保证了加密的有效性。RSA算法非对称加密更加安全,但是速度太慢,不适合传输大型文件,一般用于交换密钥或者身份认证。对称加密速度更快,可以用来传输大量数据。17.对称加密和非对称加密分别用于什么?18.为什么不全部使用非对称加密让传输更安全?19.做电商平台有遇到什么难点吗20.说说遇到的难点以及是怎么解决的(说了全局数据共享,使用Pinia代替了ref)21.如果一个页面它不展示,但是用了那个&nbsp;Pinia&nbsp;里面的一个数据,数据更改的时候,看不见的那个页面的&nbsp;demo&nbsp;会不会更改?讲了一下响应式更新&nbsp;defineProperty&nbsp;proxy&nbsp;watcher&nbsp;update22.了解过vue2吗?vue2和vue3你觉得有哪些不同讲了一下组合式API和选项式API23.追问:讲讲Composition&nbsp;API和Options&nbsp;API的区别相同功能的逻辑可写在一起,复用性更强,更支持TS如何解释复用性更强?vue2相较于vue3的话vue2是没有ref这个响应式变量的声明的。他的响应式是通过data()、computed、watch等这些API定义在组件的内部来实现的,而不是通过组合函数复用。在vue3里组合式APi可以将相似的逻辑封装成独立的组合函数并导出,其他组件再导入这个函数就可以直接使用(优点:响应式、自动绑定生命周期钩子)24.了解TS吗?不是很了解,说说泛型25.后台怎么解决的我说用的Mock.js,因为不会后端,也没有和别人一起开发这个项目。面完无缝接二面,字数超了,新帖发。
查看24道真题和解析
点赞 评论 收藏
分享
评论
2
19
分享

创作者周榜

更多
牛客网
牛客企业服务