Jason Lee
首页
  • 学习笔记

    • 常用资源
    • html&css
    • webpack
    • vue2.x
    • vue3
    • typescript入门
    • 工程化实践
  • css
  • javascript
  • es6常问
  • 手写代码
  • 错误监控
  • webpack
  • vue框架
  • 浏览器相关
  • 计算机网络
  • 数据结构和算法
  • 学习笔记
工具
首页
  • 学习笔记

    • 常用资源
    • html&css
    • webpack
    • vue2.x
    • vue3
    • typescript入门
    • 工程化实践
  • css
  • javascript
  • es6常问
  • 手写代码
  • 错误监控
  • webpack
  • vue框架
  • 浏览器相关
  • 计算机网络
  • 数据结构和算法
  • 学习笔记
工具
  • css
  • javascript
  • 错误监控
  • ES6常问
  • 手写代码
  • webpack
  • vue框架
    • Vue 的理解,优缺点
    • MVVM
    • 为什么说 Vue 是一个渐进式框架?
    • vue 组件通讯方式
    • Vuex 的 Mutation 和 Action 的区别吗
    • v-if 和 v-for 优先级
    • vue 生命周期
    • Vue 底层实现原理
    • computed 与 watch
    • key 的作用是什么?
    • v-model 中的实现原理
    • 为什么 Vue 采用异步渲染
    • nextTick 的实现原理是什么?
    • 路由原理 history 和 hash 两种路由方式的特点
    • 路由懒加载的方式
    • keep-alive 的实现
    • 异步组件
    • 什么是虚拟 DOM?
    • diff 算法
    • vue 性能优化
    • 参考资料
  • 浏览器相关
  • 计算机网络
  • 数据结构和算法
  • 设计模式
  • interview
jason lee
2021-09-06
目录

vue框架

# Vue 的理解,优缺点

Vue 是一个构建数据驱动的渐进性框架,它的目标是通过 API 实现响应数据绑定和视图更新

优点:渐进式,组件化,轻量级,虚拟 dom,响应式,单页面路由,数据与视图分开

缺点:单页面不利于 seo,不支持 IE8 以下,首屏加载时间长

# MVVM

全称: Model-View-ViewModel , Model 表示数据模型层。 view 表示视图层, ViewModel 是 View 和 Model 层的桥梁,数据绑定到 viewModel 层并自动渲染到页面中,视图变化通知 viewModel 层更新数据。

# 为什么说 Vue 是一个渐进式框架?

渐进式:通俗点讲就是,你想用啥你就用啥,咱也不强求你。你想用 component 就用,不用也行,你想用 vuex 就用,不用也可以

# vue 组件通讯方式

  • props 和$emit 父组件向子组件传递数据是通过 prop 传递的,子组件传递数据给父组件是通过$emit 触发事件来做到的、
  • $parent,$children 获取当前组件的父组件和当前组件的子组件
  • $attrs 和$listeners A->B->C。解决深层组件间的通信, Vue 2.4 开始提供了$attrs 和$listeners 来解决这个问题
  • 父组件中通过 provide 来提供变量,然后在子组件中通过 inject 来注入变量。
  • $refs 获取组件实例
  • envetBus 兄弟组件数据传递
  • vuex 状态管理

# Vuex 的 Mutation 和 Action 的区别吗

Vuex 是一个专为 Vue 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。

Vuex 主要包括以下几个核心模块:

  1. State:定义了应用的状态数据

  2. Getter:在 store 中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来, 且只有当它的依赖值发生了改变才会被重新计算

  3. Mutation:是唯一更改 store 中状态的方法,且必须是同步函数

  4. Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作 5. Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中

# v-if 和 v-for 优先级

1、v-for 优先于 v-if 被解析

2、如果同时出现,每次渲染都会先执行循环在判断条件,无论如何循环都不可避免,浪费了性能

3、要避免出现这种情况,在外层嵌套 template,在这一层进行 v-if 判断,然后在内部进行 v-for 循环

# vue 生命周期

Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载DOM-渲染、更新-渲染、卸载等一系列的过程,每个过程对应着生命周期钩子,就是说在达到某一阶段或条件时去触发的函数,目的就是为了完成一些动作或者事件

  • create阶段:vue 实例被创建

beforeCreate: 创建前,此时 data 和 methods 中的数据都还没有初始化

created: 创建完毕,data 中有值,未挂载

-mount阶段: vue 实例被挂载到真实 DOM 节点

beforeMount:可以发起服务端请求,去数据

mounted: 此时可以操作 DOM

  • update阶段:当 vue 实例里面的 data 数据变化时,触发组件的重新渲染

beforeUpdate :更新前

updated:更新后

-destroy阶段:vue 实例被销毁

beforeDestroy:实例被销毁前,此时可以手动销毁一些方法

destroyed:销毁后

# Vue 底层实现原理

vue 实例化的时候会创建一个 observe 实例,通过 Object.defineProperty 对 data 设置 get,set。 初始化编译的时候会触发 get 方法进行依赖收集,将观察者 watcher 对象添加到订阅者 dep 中。数据改变的时候会触发 set方法,通知 dep 中的 watcher 执行 update 方法, 将 watcher 添加到事件队列 queue 中,执行 nextTick(queue)。

Observer(数据监听器) Observer 的核心是通过 Object.defineProprtty()来监听数据的变动,这个函数内部可以定义 setter 和 getter,每当数据发生变化,就会触发 setter。这时候 Observer 就要通知订阅者,订阅者就是 Watcher

Watcher(订阅者) Watcher 订阅者作为 Observer 和 Compile 之间通信的桥梁,主要做的事情是:

  1. 在自身实例化时往属性订阅器(dep)里面添加自己
  2. 自身必须有一个 update()方法
  3. 待属性变动 dep.notice()通知时,能调用自身的 update()方法,并触发 Compile 中绑定的回调

Compile(指令解析器) Compile 主要做的事情是解析模板指令,将模板中变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加鉴定数据的订阅者,一旦数据有变动,收到通知,更新试图

# computed 与 watch

计算属性本质上是 computed watcher,而侦听属性本质上是 user watcher。

就应用场景而言,计算属性适合用在模板渲染中,某个值是依赖了其它的响应式对象甚至是计算属性计算而来;而侦听属性适用于观测某个值的变化去完成一段复杂的业务逻辑。

使用场景 computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能。watch:当一条数据影响多条数据的时候使用,例:搜索数据

# key 的作用是什么?

  • Key 的作用: 主要用来在虚拟 DOM 的 diff 算法中,在新旧节点的对比时辨别 vnode ,key可以精准的判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少了dom的操作量。常见的列子是结合 v-for 来进行列表渲染,或者用于强制替换元素/组件。
  • 设置 Key 的好处:
    • (1)数据更新时,可以尽可能的减少 DOM 操作;
    • (2)列表渲染时,可以提高列表渲染的效率,提高页面的性能;

# v-model 中的实现原理

v-model 可以看成是 value+input 方法的语法糖(组件)。原生的 v-model ,会根据标签的不同生成不同的事件与属性。解析一个指令来。

# 为什么 Vue 采用异步渲染

如果采用同步更新的话,vue 观察到数据改变就进行一次计算、渲染,那么就会重复渲染。对属性进行多次操作的情况,我们并不关心中间的过程发生了什么,只需要知道最后的结果。

# nextTick 的实现原理是什么?

  • 简单的概括,nextTick 只做了两件事情:

    -将回调函数 cb 包装处理为箭头函数添加到事件队列中 -事件队列异步执行(执行的优先顺序为 promise.then => MutationObserver => setImmediate => setTimeout)

# 路由原理 history 和 hash 两种路由方式的特点

  • hash 模式

    • 实现原理:location.hash 的值实际就是 URL 中#后面的东西 它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

    • 可以为 hash 的改变添加监听事件 window.addEventListener("hashchange", funcRef, false);

    • 特点:每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录,实现前端路由“更新视图但不重新请求页面”的功能了

  • history 模式

    • 实现原理:利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。 这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。
    • 特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。

# 路由懒加载的方式

  • Vue 异步组件技术

  • 动态 import 语法

  • require.ensure()

// 异步组件
{
  path: '/Demo',
  name: 'Demo',
  //打包后,每个组件单独生成一个chunk文件
  component: reslove => require(['../views/Demo'], resolve)
}
//动态import 默认将每个组件,单独打包成一个js文件
const Demo = () => import('../views/Demo')
// require.ensure
const Demo = r => require.ensure([], () => r(require('../views/Demo')), 'Demo')

# keep-alive 的实现

  • 作用:实现组件缓存,保持这些组件的状态,以避免反复渲染导致的性能问题。 需要缓存组件 频繁切换,不需要重复渲染

  • 场景:tabs 标签页 后台导航,vue 性能优化

  • 原理:Vue.js 内部将 DOM 节点抽象成了一个个的 VNode 节点,keep-alive 组件的缓存也是基于 VNode 节点的而不是直接存储 DOM 结构。它将满足条件(pruneCache 与 pruneCache)的组件在 cache 对象中缓存起来,在需要重新渲染的时候再将 vnode 节点从 cache 对象中取出并渲染。

# 异步组件

Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。 优点:节省打包出的结果,异步组件分开打包,采用 jsonp 的方式进行加载,有效解决文件过大的问题。

一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:

Vue.component("async-webpack-example", function(resolve) {
  // 这个特殊的 `require` 语法将会告诉 webpack
  // 自动将你的构建代码切割成多个包,这些包
  // 会通过 Ajax 请求加载
  require(["./my-async-component"], resolve);
});

使用局部注册的时候,可以直接提供一个返回 Promise 的函数:

new Vue({
  // ...
  components: {
    "my-component": () => import("./my-async-component"),
  },
});

# 什么是虚拟 DOM?

虚拟 DOM 是一个对象,一个用来表示真实 DOM 的对象

<ul id="list">
  <li class="item">111</li>
  <li class="item">222</li>
  <li class="item">333</li>
</ul>;
let oldVDOM = {
  // 旧虚拟DOM
  tagName: "ul", // 标签名
  props: {
    // 标签属性
    id: "list",
  },
  children: [
    // 标签子节点
    {
      tagName: "li",
      props: { class: "item" },
      children: ["111"],
    },
    {
      tagName: "li",
      props: { class: "item" },
      children: ["222"],
    },
    {
      tagName: "li",
      props: { class: "item" },
      children: ["333"],
    },
  ],
};

# diff 算法

Diff 算法是一种对比算法。对比两者是旧虚拟DOM和新虚拟DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实 DOM,进而提高效率。

  • Diff 算法的原理: -Diff同层对比: 新旧虚拟 DOM 对比的时候,Diff 算法比较只会在同层级进行, 不会跨层级比较。 所以 Diff 算法是:深度优先算法。 时间复杂度:O(n) -Diff对比流程: 当数据改变时,会触发 setter,并且通过 Dep.notify 去通知所有订阅者 Watcher,订阅者们就会调用 patch 方法,给真实 DOM 打补丁,更新相应的视图。

    • patch方法:方法作用就是,对比当前同层的虚拟节点是否为同一种类型的标签

      • 是:继续执行 patchVnode 方法进行深层比对
      • 否:没必要比对了,直接整个节点替换成新虚拟节点
    • sameVnode方法: patch 关键的一步就是 sameVnode 方法判断是否为同一类型节点, 怎么才算是同一类型节点?

      function sameVnode(oldVnode, newVnode) {
        return (
          oldVnode.key === newVnode.key && // key值是否一样
          oldVnode.tagName === newVnode.tagName && // 标签名是否一样
          oldVnode.isComment === newVnode.isComment && // 是否都为注释节点
          isDef(oldVnode.data) === isDef(newVnode.data) && // 是否都定义了data
          sameInputType(oldVnode, newVnode) // 当标签为input时,type必须是否相同
        );
      }
      
    • patchVnode方法:

      • 找到对应的真实DOM,称为el
      • 判断newVnode和oldVnode是否指向同一个对象,如果是,那么直接return
      • 如果他们都有文本节点并且不相等,那么将el的文本节点设置为newVnode的文本节点。
      • 如果oldVnode有子节点而newVnode没有,则删除el的子节点
      • 如果oldVnode没有子节点而newVnode有,则将newVnode的子节点真实化之后添加到el
      • 如果两者都有子节点,则执行updateChildren函数比较子节点,这一步很重要
    • updateChildren方法: 也是一个对比方法,也叫首尾指针法,对比子节点是否一样

    • patch(vnode, newVnode) ,数据改变二次渲染,对比新旧 VNode 是否相同节点然后更新 DOM

    • createElm(vnode, insertedVnodeQueue),先执行用户的 init 钩子函数,然后把 vnode 转换成真实 DOM(此时没有渲染到页面),最后返回新创建的 DOM

    • updateChildren(elm, oldCh, ch, insertedVnodeQueue), 如果 VNode 有子节点,并且与旧 VNode 子节点不相同则执行 updateChildren(),比较子节点的差异并更新到 DOM

# vue 性能优化

  • 路由懒加载
  • keep-alive缓存页面
  • 使用v-show复用DOM
  • v-for遍历避免同时使用v-if
  • 事件销毁(定时器)
  • 图片懒加载
  • 第三方插件按需导入
  • 无状态组件标记为函数式组件
  • SSR

# 参考资料

https://vue-js.com/learn-vue/ (opens new window) Vue 源码系列-Vue 中文社区

https://ustbhuangyi.github.io/vue-analysis/ (opens new window) Vue.js 技术揭秘

webpack
浏览器相关

← webpack 浏览器相关→

Theme by Vdoing | Copyright © 2019-2022 jason lee | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式