p3-m2

  • 本阶段围绕当下国内最主流的前端核心框架 Vue.js 展开,深入框架内部,通过解读源码或者手写实现的方式,剖析 Vue.js 框架的内部实现原理,让你做到知其所以然。同时我们还会介绍 Vue.js 的进阶用法、周边生态以及性能优化,让你轻松应对更加复杂的项目业务需求。

模块二 Vue.js 源码分析(响应式、虚拟 DOM、模板编译和组件化)

  • 本模块会带你深入分析 Vue.js 源码,包括:Vue.js 初始化开始、首次渲染的过程、响应式的依赖收集、Watcher 渲染视图、虚拟 DOM 和模板编译等。通过阅读源码了解底层实现,学习 Vue.js 处理问题的方式,最终解决你的面试难题。
任务一:Vue.js 基础回顾
  1. 课程目标
  • Vue.js 的静态成员和实例成员初始化过程
  • 首次渲染的过程
  • 数据响应式原理
  1. 准备工作-目录结构
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RUXnn0Mr-1626755610301)(./img/1626400047632.jpg)]
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lUQSKmCa-1626755610302)(./img/1626400344105.jpg)]
  1. 准备工作-调试
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tU8njL60-1626755610303)(./img/1626400520787.jpg)]
  1. 准备工作-Vue的不同构建版本
  • 完整版包含编译器,编译器有三千多行代码
  • vue-cli 创建的项目
    • 基于vue-cli创建的项目,也是运行时版本
    • vue inspect 可以查看当前生成的项目webpack配置
    • vue inspect > output.js >把前面的运行结果输出到后面的文件中去,所以单文件组件在运行的时候,也是不需要编译器的
    • 在打包的时候,会把单文件组件转换为对象,转换成对象的过程中,还会把模版转换成render函数
    • 问题:既然运行时版不带编译器,那么vue-cli生成的项目打包时,是谁把单文件组件中的template模版编译成render函数的?
      • vue-loader
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HYuL8Ahz-1626755610305)(./img/1626401117090.jpg)]
  1. 寻找入口文件
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IhzGHnNl-1626755610306)(./img/1626402472997.jpg)]
  1. 从入口开始

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I3mMaTtl-1626755610307)(./img/1626403526107.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zV3VvSby-1626755610308)(./img/1626404140804.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-31aNYNhi-1626755610309)(./img/1626404261175.jpg)]

  1. Vue初始化的过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bClQMW1Y-1626755610309)(./img/1626405023286.jpg)]

  1. Vue初始化-两个问题

  2. Vue初始化-静态成员

  3. Vue初始化-实例成员

  4. Vue初始化-实例成员-init

  5. Vue初始化-实例成员-initState

  6. 调试Vue初始化过程

  7. 首次渲染过程

  8. 首次渲染过程-总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r2ygLpS9-1626755610310)(./img/1626421617827.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9esIPSDz-1626755610311)(./img/1626421558838.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TXc4b1gJ-1626755610311)(./img/1626421451552.jpg)]

  1. 数据响应式原理-响应式处理入口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-70TU8dVf-1626755610312)(./img/1626484992612.jpg)]

  1. 数据响应式原理-Observer

  2. 数据响应式原理-defineReactive

  3. 数据响应式原理-依赖收集

  4. 数据响应式原理-依赖收集-调试

  5. 数据响应式原理-数组

  6. 数据响应式原理-数组练习

  7. 数据响应式原理-Watcher上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WjqtioTI-1626755610313)(./img/1626492470517.jpg)]

  1. 数据响应式原理-Watcher下

  2. 数据响应式原理-调试上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tmCRRTd8-1626755610313)(./img/1626493545750.jpg)]

  • 如果数组中有一个是对象,修改这个对象的为数字,会触发更新嘛? [2, 3, {a: 3}] > [2, 3, 4]
  1. 数据响应式原理-调试下

  2. 数据响应式原理-总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K2PmFSYa-1626755610314)(./img/1626501527376.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEDnAViz-1626755610315)(./img/1626501591811.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hSstCfBQ-1626755610316)(./img/1626501640418.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bLDRmIGo-1626755610316)(./img/1626501683897.jpg)]

  1. 动态添加一个响应式属性

  2. set-源码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sG0ceoZC-1626755610317)(./img/1626502111658.jpg)]

  1. set-调试

  2. delete

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ES5KhrBJ-1626755610317)(./img/1626503158203.jpg)]

  1. delete-源码

  2. watch-回顾

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sSD2oVM1-1626755610318)(./img/1626503582159.jpg)]

  1. 三种类型的 Watcher

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pBDtL6G2-1626755610319)(./img/1626504201779.jpg)]

  1. watch-源码
  • 没有静态方法,因为内部要用到Vue的实例
  1. nextTick-回顾
  • 使用场景:比如一个聊天窗,当用户发了新消息后,消息位于最下面,在用户更新消息的地方,调用 vm.$nextTick() 执行聊天窗下拉。(在本次虚拟dom更新后执行,还没有更新到页面上)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z3TTC5Il-1626755610319)(./img/1626505478550.jpg)]
  1. nextTick-源码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-12gCJp6B-1626755610320)(./img/1626505737168.jpg)]

  • setImmediate 比 setTimeout 性能好,setTimeout及时设置为0,也要等4毫秒
任务二:Vue.js 源码剖析-虚拟 DOM
  1. 课程回顾
  • 虚拟DOM库 - Snabbdom
  • Vue.js 响应式原理模拟实现
  • Vue.js 源码剖析 - 响应式原理
  1. 虚拟 DOM 概念回顾
  • 什么是虚拟DOM
    • 虚拟DOM (Virtual DOM) 是使用JavaScript对象描述真实DOM
    • Vue.js 中的虚拟DOM借鉴Snabbdom,并添加了Vue.js的特性。例如:指令和组件机制
  • 为什么要使用虚拟DOM
    • 避免直接操作DOM,提高开发效率
    • 作为一个中间层可以跨平台
    • 虚拟DOM不一定可以提高性能
      • 首次渲染的时候增加开销
      • 复杂视图情况下提升渲染性能
  1. 代码演示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXFvehkK-1626755610320)(./img/1626509569572.jpg)]

  • VNode 的核心属性: tag data children text elm key
  1. 整体过程分析

  2. createElement-上

  3. createElement-下

  4. update

  5. patch 函数的初始化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kS7aYchc-1626755610321)(./img/1626510981105.jpg)]

  1. patch

  2. createElm

  3. patchVnode

  4. updateChildren

  5. 没有设置key的情况

  6. 设置key的情况

任务三:Vue.js 源码分析(响应式、虚拟 DOM、模板编译和组件化)
  1. 模板编译介绍
  • Vue2.x使用NNode描述视图以及各种交互,用户自己编写VNode比较复杂
  • 用户只需要编写类似HTML的代码 - Vue.js模版,通过编译器将模版转换为返回VNode的render函数
  • .vue 文件会被webpack 在构建的过程中转换成render函数
    • 编译分为运行时编译和打包构建时编译,运行时编译必须是Vue完整版,打包时编译webpack可以借助vue-loader
  1. 体验模板编译的结果-上

  2. 体验模板编译的结果-下

  3. Vue Template Explorer

  • template-explorer.vuejs.org 可以把模版转换成 render函数。vue2
    • 模版中html 标签内部尽量不要有空白内容(空格和换行),因为在 render函数中也会对应的产生空格和换行,占用内存空间,影响性能
  • vue-next-template-explorer.netlify.app vue3
    • vue3 编译后已经去除了多余的空白
  1. 模板编译的入口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qruP7POL-1626755610322)(./img/1626531399907.jpg)]

  1. 模板编译过程-compileToFunctions

  2. 模板编译过程-compile

  3. 模板编译过程-baseCompile-AST

  • 什么是抽象语法树
    • 抽象语法树简称AST(ABstract Syntax Tree)
    • 使用对象的形式描述树形的代码结构
    • 此处的抽象语法树是用来描述树形结构的HTML字符串
  • 为什么要使用抽象语法树
    • 模版字符串转换成AST后,可以通过AST对模版做优化处理
    • 标记模版中的静态内容,在patch的时候直接跳过静态内容
    • 在patch的过程中静态内容不需要对比和重新渲染
  • astexplorer.net 查看模版编译后的语法树
  1. 模板编译过程-baseCompile-parse

  2. 模板编译过程-baseCompile-optimize

  3. 模板编译过程-generate-上

  4. 模板编译过程-generate-下

  5. 模板编译过程-调试

  6. 模板编译过程-总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AKUboAmK-1626755610322)(./img/1626575626048.jpg)]

  1. 组件化回顾
  • 一个Vue组件就是一个拥有预定义选项的一个Vue实例
  • 一个组件可以组成页面上一个功能完备的区域,组件可以包含脚本、样式、模版
  1. 组件注册

  2. Vue.extend

  3. 调试组件注册过程

  4. 组件的创建过程

  5. 组件的 patch 过程

  • 总结

    1. 打包过程
    • “dev”: “rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev”
      • -w 监控 -c 使用配置文件 scripts/config.js
    • 通过 scripts/config.js 找到 src/platforms/web/entry-runtime-with-compiler.js’
    1. 四个导出Vue的文件
    • src/instance/index 给原型对象添加成员方法
      • Vue 的构造函数,此处不用class 的原因是因为方便后续给 Vue 实例混入实例成员
        • 测试环境下,如果不是通过new Vue 的方式调用构造函数,报错
        • 调用_init 方法
      • initMixin(Vue) 给Vue的prototype添加 _init 方法,在_init中完成实例(vm)的初始化
      • stateMixin(Vue) 注册 vm 的 d a t a / data/ data/props/ s e t / set/ set/delete/$watch
      • eventMixin(Vue) 初始化事件相关方法, o n , on, on,once, o f f , off, off,emit
      • lifecycleMinxins(Vue)初始化生命周期相关混入方法 _update/ f o r c e U p d a t e / forceUpdate/ forceUpdate/destroy
      • renderMixin(Vue) $nextTick/_render 安装运行时便利助手 Vue.prototype._b/_d…
    • src/core/index
      • initGlobalAPI(Vue) 给Vue添加静态成员方法
        • Vue.set/delete/nextTick
        • Vue.observable 让一个对象变成响应式对象
        • Vue.options 初始化 Vue.options 对象,并给其扩展
        • extend(Vue.options.components, builtInComponents) 设置keep-alive 组件到Vue.options.component上
        • initUse(Vue) 注册 Vue.use() 用来注册插件
        • initMixin(Vue) 注册 Vue.minxin() 实现混入
        • initExtend(Vue) Vue.extend() 基于传入的options返回一个组件的构造函数
        • initAssetRegisters(Vue) 注册 Vue.directive()/Vue.component()/Vue.filter()
          • Vue.component() 实现本质上是调用Vue.extend(),只是多加了一个全局注册的步骤,挂载在了this.options[‘components’]上
    • src/platforms/web/runtime/index
      • extend(Vue.options.directives, platformDirectives) 加载平台内置的指令和组件
      • Vue.prototype.patch 给 Vue 原型对象挂载__patch__ 方法
      • Vue.prototype. m o u n t 给 V u e 原 型 对 象 上 添 加 mount 给Vue原型对象上添加 mountVuemount方法
    • src/platforms/web/entry-runtime-with-compiler.js
      • 重写 Vue.prototype.$mount 方法
        • 主要判断是否有render函数
        • 如果没有通过compileToFunctions 把template 模版编译成render函数
      • Vue.compile = compileToFunctions 导出把template 转换为 render 的函数
    1. Vue 首次渲染的过程
    2. 先进行Vue的初始化
    3. 给原型对象添加成员方法
    4. 给 Vue 添加静态成员方法
    5. 加载平台内置指令和组件,给原型添加 新旧虚拟DOM对比的方法_patch_,给原型加上 $mount 方法
    6. 重写 $mount 在其中加入编译器
    7. 执行 _init 方法
    8. 合并参数options
    9. vm 生命周期相关变量初始化
    10. 调用 vm.$mount 方法进行挂载
      1. 如果有render函数,直接进行下一步,没有则找template 编译成render 函数
      2. 通过 _render 函数把 render函数转换为虚拟DOM
      3. 通过 _update 函数 中调用 patch 对比 DOM(el) 和 newVNode 区别,更新DOM 完成渲染
    • 执行Vue构造函数,调用 _init 方法。
      • vm._uid 标记uid=0
      • vm._isVue = true 标记是 Vue 实例
      • 合并实例和构造函数上的 options
      • initLifecycle(vm) vm 的生命周期相关变量初始化 c h i l d r e n / children/ children/parent/ r o o t / root/ root/refs
      • initEvents(vm) vm 的事件监听初始化,父组件绑定在当前组件上的事件
      • initRender(vm) vm 的编译render初始化
        • vm._c 对编译生成的 render 进行渲染的方法
        • vm.$createElement 手写 render 函数进行渲染的方法
        • 给 vm. a t t r s , v m . attrs, vm. attrsvm.listener 添加监控
      • callHook(vm, ‘beforeCreate’) 生命钩子回调
      • initInjections(vm) 把 inject 的成员注入到 vm上
      • initState(vm) 初始化vm的 _props/methods/_data/computed/watch
      • initProvide(vm) 初始化 provide
      • callHook(vm, ‘created’) 触发created 生命钩子函数
      • vm. m o u n t ( v m . mount(vm. mount(vm.options.el) 调用$mount挂载
        • el = el && query(el) 获取el对象
        • 判断是否有 render函数,如果没有rander函数,则判断tepmlate是否存在,存在则把template转换为模版
        • mount.call(this, el) 调用 mount 方法,渲染 DOM
          • 获取 el 对象
          • 执行 mountComponent(this, el) 函数
            • callHook(vm, ‘beforeMount’) 执行 beforeMount 函数
            • new Watcher 创建观察者
              • 执行 get() > 调用 updateComponent
              • pushTarget(this) 和 popTarget() 给 Dep.target 赋值和去掉值
            • 执行 _render
              • render.call(vm._renderProxy, vm.$createElement)
                • vm.$createElement
                  • createElement
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值