Vue和React框架内容

99 阅读13分钟

说说Vue和React的区别?

初级水准

区别:

  • Vue API比React API多
  • Vue双向绑定,修改数据会自动更新视图,而React需要手动setState
  • Vue template结构表现分离,React采用JSX结构表现融合,html/css都可以写在js中
  • 都可以通过props传递数据给子组件,但是Vue要对props进行声明,其中Vue2中使用props:{},Vue3中使用defineProps;React不用声明可以直接解构出来使用
  • Vue可以使用插槽,React是万物皆可props
  • Vue2利用mixin,React可以使用自定义hook或者高阶函数实现
  • Vue的fragment,hook只有在Vue3中才有,Vue还有丰富的指令v-for,v-bind,v-model,过滤器

相同点:

  • 都支持服务端渲染
  • 都有虚拟DOM
  • 都是数据驱动
  • 都有组件化开发
  • 响应式
  • 组件通信方式
  • 都有状态管理Vuex,Redux

中级水准

区别:

  • React的社区很丰富,Vue生态没那么丰富,而且每次改动都挺多内容要看
  • 组件化:Vue2组件说白了就是挂满一堆东西的Vue核心类,每个script到处一个options的纯实例对象,而且Vue的插件也都是挂载在Vue实例上的;而React直接定义render函数生成vnode,而且React类组件都是继承于React.Component类,它的this指向我们自定义的类,对开发者是透明的
  • hook:React hook是根据调用顺序来确定下一次重新渲染时的state来源的,所以不能在循环/条件判断/嵌套函数中使用,而且必须在函数最顶层调用hook; Vue3 hook是基于响应式实现的,它是声明在setup中的,一次组件实例化只调用一次setup,而React每次重新渲染都要重新调用

中上级水准

响应式区别:

  • Vue2响应式的特点就是依赖收集,数据可变,自动派发更新,初始化时通过Object.defineProperty递归劫持data所有属性添加setter/getter,触发getter时进行依赖收集,修改时触发setter自动派发更新,找到引用组件重新渲染
  • Vue3响应式使用原生Proxy重构了响应式,一是Proxy不存在Vue2响应式存在的缺陷,二是性能更好,不仅支持更多的数据结构,而且不再单一递归劫持对象,而是代理第一层对象本身。运行时才递归,用到才会代理,用effect副作用来代替Vue2中的watcher,用一个依赖管理中心trackMap统一管理Vue中的Dep,这样不需要维护特别多的依赖关系,性能上取得很大进步
  • 相比Vue的自动化,React则是基于状态,单向数据流,数据不可变,需要手动setState来更新,而且数据改变时会以组件根为目录,默认全部重新渲染整个组件树,只能用额外的useMemo,useCallback,pureComponent,shouldComponentUpdate等方法来进行控制,更新粒度比较粗

Diff算法区别

  • Vue2是同层比较新老vnode,新的不存在老的存在就删除,新的存在老的不存在就创建,子节点采用双指针头尾两端对比的方式,全量diff,然后移动节点时通过splice进行数组操作
  • Vue3是采用Map数据结构以及动静结合的方式,在编译阶段提前标记静态节点,Diff过程中直接跳过有静态标记的节点,并且子节点对比会使用一个source数组来记录节点位置以及最长递增子序列算法优化了对比的过程,快速diff,需要处理的边际条件会更少
  • React是递归同层比较,标识差异点保存到Diff队列,得到patch树,再统一操作批量更新DOM。Diff总共就是移动,删除,增加三个操作,如果结构发生变化就直接卸载重新创建,如果没有就将节点在新的集合中的位置和老集合中的lastIndex进行比较是否需要移动,如果遍历过程中发现新集合没有,但老集合有就删除

高级水准

  1. 突出核心思想和设计理念 React一开始的定位就是UI开发的新思路,就是要改变开发者,制定规则,让开发者按照规则来开发 Vue是尽可能降低前端开发的门槛,来适应不同的开发者来的。

  2. 数据管理方式 虽然都是数据驱动,但是Vue是响应式的,React是手动setState

主要体现在以下几个方面:

  • 比如Vue是对数据进行劫持/代理,它对监测数据的变化更加准确,动了多少数据就触发多少更新,更新粒度很小,而React推崇的是函数式,它是没办法感知数据变化的,不知道什么时候应该刷新,而且就算是手动setState触发更新,它也不知道哪些组件需要刷新,而是渲染整个DOM,这样会导致性能不好,所以后面只能通过不断的新增方法来避免不必要的刷新。当然Vue也不是那么完美,它实现精准刷新也是有代价的,就是需要给每个组件都配置监视器,管理依赖收集和派发更新,这同样是有性能损耗的。对比下两个框架的版本迭代可以发现,React迭代是增加了一个个避免刷新的钩子函数或者API还有采用Fiber的架构来做时间分片也是来优化渲染的性能。而Vue2/Vue3每个版本核心都是围绕响应式来优化的
  • 正是这种设计上的区别,也直接影响了hooks的实现,React hook底层是基于链表实现的,每次组件被render的时候都会按顺序执行所有hooks,而且正因为底层是链表,每个hook的next是指向下一个hook的,所以我们写代码时候不能在不同的hooks调用里使用条件判断/函数嵌套之类的,这里会导致执行顺序不对,从而出错。而Vue是直接对数据代理观察,唯一困扰点就是需要返回一个包装对象,所以需要用.value获取。因为在js中基础数据类型只有值,没有引用,或者说只存在栈里,使用完就回收了,无法追踪到后续的变化,自然也就做不到数据的代理和拦截了。
  • 再说说编译优化的问题,Vue能做到数据劫持,再到Vue3的动静结合的Diff思想得益于它的模板语法实现了静态编译。就是能做到预编译优化,可以静态分析,在解析模板时能根据解析到的不同的标签,文本等分别执行对应的回调函数来构造AST,而React虽然JSX语法更加灵活,重新渲染时候就是一堆递归调用React.createElement,无法从模板层面进行静态分析,也无法做到双向绑定,即使是后来的Fiber,也是因为伤害已经造成了,所以通过时间分片的优化手法来弥补一些伤害,因为它已经无法在编译阶段进行优化了。

项目选型?怎么考虑?

  • 从加载速度,运行时性能来说,两个框架是没有质的区别的。Vue在更新时性能的优化方面需要的心智负担可能会更少点,尤其是Vue3,而React如果不注意,容易导致一些组件无用的Diff。
  • 如果公司主要用的Vue技术栈的话:Vue性能会更有优势,特别是Vue3更加灵活,有很好的扩展性,同时有更快的渲染速度和更小的打包体积。
  • 如果公司主要用的React技术栈的话:要是一些不大的系统就用Vue,因为开发难度低,开发效率也高,而且打包体积更小。但是像一些中后台系统或者一些大的项目,多人协作开发的还是用React更好,因为它的函数式编程更加灵活且具有可扩展性,丰富的生态圈和工具链,后期也方便迭代与维护,还使用原生APP,所以会更加倾向使用React

Vue的渲染流程

  1. 每个Vue组件都需要产出一份虚拟DOM,也就是组件的render函数的返回值,render函数可以手写,也可以通过template传递模板字符串,由Vue内部来编译成渲染函数,平常写的Vue单文件最后都会被编译成普通的Vue组件对象
  2. render函数会作为副作用函数执行,也就是当模板中使用到了响应式数据,那么响应式数据和属性会与render函数关联起来,当响应式数据被修改后,就能找到依赖它的render函数,就可以通知依赖的组件进行更新
  3. 有了虚拟DOM之后,Vue内部的渲染器就能将它渲染成真实的DOM,如果是更新的情况,也就是存在两个虚拟DOM,Vue会通过比较,必要时使用Diff算法进行高效的更新真实DOMDOM 总结下来,就是实现一个渲染器,就能将虚拟DOM渲染成真实DOM,并且能高效的根据新旧虚拟DOM对比完成更新,再实现一个编译器,能够将模板编译成渲染函数,最后再基于Proxy实现一个响应式系统就是简易版的Vue3了。

v-show和v-if的区别

  • 控制手段不同:v-show隐藏是为该元素添加display: none,dom元素依旧存在。v-if显示隐藏是将dom元素整个添加或删除
  • 编译过程不同:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css的切换
  • 编译条件不同:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假的时候,并不操作,直到为真的时候才渲染。 = 性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗。

解决SPA首页加载的方法

  1. 资源加载优化
    • 减小资源大小
      • 代码压缩:terser,uglifyjs
      • 开启Gzip压缩
      • 图片压缩
      • 静态资源本地缓存
      • 减小入口文件体积
      • 代码拆分:splitChunks
      • TreeShaking
    • 减少http请求次数
      • http强缓存:cache-control,expires
      • 本地存储localStorage
      • 合并请求:利用雪碧图
    • 提高http响应速度
      • CDN
      • Http协商缓存
      • DNS Prefetch
      • Http2
    • 优化资源加载时机
      • 懒加载
      • 按需加载
      • 预加载
  2. 渲染优化
    • 优化html代码
      • js放在body底部
      • css外联放在顶部
      • 减少DOM的数量
    • 优化js,css代码
      • 减少对DOM的操作
      • 降低css的选择器复杂度
    • 优化动画效果
      • 使用requestAnimationFrame
      • 使用transform和opacity实现动画

为什么data属性是一个函数而不是一个对象?

  • 根实例对象data可以是对象也可以是函数,不会产生数据污染情况
  • 组件实例对象data必须是函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。采用函数的形式,initData会将其作为工厂函数返回全新的data对象

Vue中给对象添加新属性界面不刷新?

  • Vue.set
  • Object.assign
  • $forceUpdate

Vue中的mixin

mixin提供了复用功能

同名会合并data,methods,components,directives

slot 插槽

  • 默认插槽 slot
  • 具名插槽 slot name=“header“
  • 作用域插槽 slot name=”header“ template v-slot:header=”header“ 就可以使用里面的数据了

自定义指令

directive

  • 表单防止重复提交 使用了节流函数 v-throttle
  • 一键copy功能 v-copy
  • 权限校验 v-permission

二次封装axios

  • 设置接口请求前缀:根据开发,测试,生产环境的不同,前缀需要加以区分
  • 请求头:post设置Content-Type
  • 状态码:根据返回的状态码执行不同的业务
  • 请求方法:根据get,post进行一个再次封装,使用起来会更加方便
  • 请求拦截器:发送请求前判断是否存在token
  • 响应拦截器:接收到响应后先做一层操作,比如根据状态码判断登录状态,授权

Vue怎么做权限管理

  • 权限控制是什么

    • 路由方面,用户登陆后只能看到自己有权访问的导航菜单,也只能访问自己有权访问的路由地址
    • 视图方面,用户只能看到自己有权浏览的内容和有权操作的控件
    • 最后加上请求控制,越权请求将在前端被拦截
  • 接口权限:jwt的形式,在请求时,,头部携带token

  • 路由权限:

    • 初始化时挂载全部路由,并在路由上标记相应的权限信息,每次路由跳转前做校验,beforeRouteEnter

      • 加载所有路由,如果路由很多,而用户并不是所有的路由都有权限访问,对性能会影响
      • 全局路由守卫,每次路由跳转都要做权限判断
      • 菜单信息写死在前端,要改个显示文字或权限信息需要重新编译
      • 菜单和路由耦合在一起
    • 初始化时先挂载不需要权限控制的路由,登陆后,获取用户的权限信息,筛选有权限访问的路由,在全局路由守卫里调用addRoutes添加路由

      • 全局路由守卫中,每次路由跳转都需要做判断
      • 菜单信息写死在前端,要修改显示文字或权限信息需要重新编译
      • 菜单和路由耦合在一起
  • 菜单权限

    • 方案一:前端定义路由,菜单由后端返回
      • 菜单需要和路由一一对应
      • 全局路由守卫中,每次路由跳转都要做判断
    • 方案二:菜单和路由都是后端返回
      • 全局路由守卫中,每次路由跳转都要做判
      • 前后端配合度要求高
  • 按钮权限

    • v-if做判断,每个页面都获取用户权限role和路由表中的meta.btnPermissions,然后做判断
    • 自定义指令实现

Vue项目中如何实现跨域

跨域的本质是浏览器基于同源策略的一种安全手段

  1. CORS:以koa框架为例,直接设置Access-Control-Allow-Origin响应头
  2. Proxy:
    1. vue.config.js中新增devServer:proxyxxxx,最终发布上线时如果web和接口服务器不在一起仍会跨域
    2. 通过服务端实现代理请求转发
    3. nginx实现代理 location /api { proxy_pass http://127.0.0.1:3000

Vue项目本地开发完成后部署到服务器后报404错误?

  1. nginx中的location 没有相关配置

  2. 为什么hash模式下不会存在问题?

    • hash虽然在url中,但不会被包括在HTTP请求中,对服务端完全没有影响,因此改变hash值不会重新加载页面
  3. 解决方案

    • 将任意页面重定向到index.html,把路由交由前端处理

    • 在Vue里面覆盖所有的路由情况,然后给出一个404页面