• 从源码角度浅析Vue常见知识点


      平时碰到一个问题,我们通常都需要从以下几点分析:

      1、先说这个点的明确定义,或者是特性;

      2、再说具体的应用场景;

      3、说说自己的看法、观点;

      4、可以稍微举一反三,说说同类特性,或者类似的框架,更好的方案。

      简短的概括下常考题:

    一、基础点

    1、对 SPA 单⻚⾯的理解,优缺点是什么?

    2、new Vue() 发生了什么?

    3、Vue.use是干什么的?原理是什么?

    4、请说一下响应式数据的理解?

    5、Vue如何检测数组变化?

    6、Vue.set 方法是如何实现的?

    7、Vue中模板编译原理?

    8、Proxy 与 Object.defineProperty 优劣对比

    9、Vue3.x响应式数据原理

    二、生命周期

    1、Vue的生命周期方法有哪些?一般在哪一步发起请求及原因

    2、生命周期钩子是如何实现的?

    3、Vue 的父组件和子组件生命周期钩子执行顺序

    三、组件通信

    1、Vue中的组件的data 为什么是一个函数?

    2、Vue 组件间通信有哪几种方式?

    3、组件中写 name选项有哪些好处及作用?

    4、keep-alive平时在哪里使用?原理是?

    5、Vue.minxin的使用场景和原理?

    四、路由

    1、Vue-router有几种钩子函数?具体是什么及执行流程是怎样的?

    2、vue-router 两种模式的区别?

    五、属性作用与对比

    1、nextTick在哪里使用?原理是?

    2、Vue 为什么需要虚拟DOM?  虚拟DOM的优劣如何?

    3、Vue中key的作用和工作原理,说说你对它的理解

    4、Vue 中的diff原理

    5、v-if 与 v-for的优先级

    6、v-if 与 v-show的区别  

    7、computed 和 watch 的区别和运用的场景?

    8、如何理解自定义指令?

    六、性能优化

    1、编码阶段

    2、用户体验:

    3、SEO优化

    4、打包优化

    一、基础点

    1、对 SPA 单⻚⾯的理解,优缺点是什么?

      SPA( single-page application )仅在 Web ⻚⾯初始化时加载相应的 HTML、JavaScript 和 CSS。⼀旦⻚⾯加载完成,SPA 不会因为⽤户的操作⽽进⾏⻚⾯的重新加载或跳转;取⽽代之的是利⽤路由机制实现 HTML 内容的变换,UI 与⽤户的交互,避免⻚⾯的重新加载。

      优点:

    1)⽤户体验好、快,内容的改变不需要重新加载整个⻚⾯,避免了不必要的跳转和重复渲染;

    2)SPA 相对对服务器压⼒⼩;

    3)前后端职责分离,架构清晰,前端进⾏交互逻辑,后端负责数据处理;

      缺点:

    1)⾸屏(初次)加载慢:为实现单⻚ Web 应⽤功能及显示效果,需要在加载⻚⾯的时候将JavaScript、CSS 统⼀加载,部分⻚⾯按需加载;

    2)不利于 SEO:由于所有的内容都在⼀个⻚⾯中动态替换显示,所以在 SEO 上其有着天然的弱势。

    2、new Vue() 发生了什么?

    1)结论:new Vue()是创建Vue实例,它内部执行了根实例的初始化过程。

    2)具体包括以下操作:

    • 选项合并

    • $children,$refs,$slots,$createElement等实例属性的方法初始化

    • 自定义事件处理

    • 数据响应式处理

    • 生命周期钩子调用 (beforecreate created)

    • 可能的挂载

    3)总结:new Vue()创建了根实例并准备好数据和方法,未来执行挂载时,此过程还会递归的应用于它的子组件上,最终形成一个有紧密关系的组件实例树。

      源码地址:src/core/instance/init.js

    3、Vue.use是干什么的?原理是什么?

      vue.use 是用来使用插件的,我们可以在插件中扩展全局组件、指令、原型方法等。

    1、检查插件是否注册,若已注册,则直接跳出;

    2、处理入参,将第一个参数之后的参数归集,并在首部塞入 this 上下文;

    3、执行注册方法,调用定义好的 install 方法,传入处理的参数,若没有 install 方法并且插件本身为 function 则直接进行注册;

    1) 插件不能重复的加载

    install 方法的第一个参数是vue的构造函数,其他参数是Vue.set中除了第一个参数的其他参数; 代码:args.unshift(this)

    2) 调用插件的install 方法 代码:typeof plugin.install === 'function'

    3) 插件本身是一个函数,直接让函数执行。 代码:plugin.apply(null, args)

    4) 缓存插件。  代码:installedPlugins.push(plugin)

      源码地址:src/core/global-api/use.js

    4、请说一下响应式数据的理解?

      根据数据类型来做不同处理,数组和对象类型当值变化时如何劫持。

    1) 对象内部通过defineReactive方法,使用 Object.defineProperty() 监听数据属性的 get 来进行数据依赖收集,再通过 set 来完成数据更新的派发;

    2) 数组则通过重写数组方法来实现的。扩展它的 7 个变更⽅法,通过监听这些方法可以做到依赖收集和派发更新;( push/pop/shift/unshift/splice/reverse/sort )

      这里还有一些相关知识点 (比如多层对象是通过递归来实现劫持,而且在vue3中已改用使用 proxy 来实现响应式数据)

    补充知识:

    1)内部依赖收集是怎么做到的:

      每个属性都拥有自己的dep属性,存放他所依赖的 watcher,当属性变化后会通知自己对应的 watcher去更新。

    2)响应式流程:

      1、defineReactive  把数据定义成响应式的;

      2、给属性增加一个 dep,用来收集对应的那些watcher;

      3、等数据变化进行更新

      dep.depend()  // get 取值:进行依赖收集

      dep.notify() // set 设置时:通知视图更新

      这里可以引出性能优化相关的内容:

    1)对象层级过深,性能就会差。

    2)不需要响应数据的内容不要放在data中。

    3)object.freeze()  可以冻结数据。

      源码地址:src/core/observer/index.js  158

    5、Vue如何检测数组变化?

      数组考虑性能原因没有用defineProperty对数组的每一项进行拦截,而是选择重写数组方法以进行重写。当数组调用到这 7 个方法的时候,执行 ob.dep.notify() 进行派发通知 Watcher 更新;(重写数组方法:push/pop/shift/unshift/splice/reverse/sort)

      补充知识回答:

      在Vue中修改数组的索引和长度是无法监控到的。需要通过以下7种变异方法修改数组才会触发数组对应的wacther进行更新。数组中如果是对象数据类型也会进行递归劫持。

      说明:那如果想要改索引更新数据怎么办?

      可以通过Vue.set()来进行处理,核心内部用的是 splice 方法。

    // 取出原型方法;
    const arrayProto = Array.prototype  
    // 拷贝原型方法;
    export const arrayMethods = Object.create(arrayProto)  
    // 重写数组方法;
    def(arrayMethods, method, function mutator (...args) { }
    ob.dep.notify()  // 调用方法时更新视图;

      源码地址:src/core/observer/array.js  15

    6、Vue.set 方法是如何实现的?

      为什么$set可以触发更新,我们给对象和数组本身都增加了dep属性,当给对象新增不存在的属性则触发对象依赖的watcher去更新,当修改数组索引时我们调用数组本身的splice方法去更新数组。

      补充知识回答:

      官方定义 Vue.set(object, key, value) 

    1)如果是数组,调用重写的splice方法 (这样可以更新视图 )

    代码:target.splice(key, 1, val)

    2)如果不是响应式的也不需要将其定义成响应式属性。

    3)如果是对象,将属性定义成响应式的  defineReactive(ob.value, key, val)

      通知视图更新  ob.dep.notify()

      源码地址:src/core/observer/index.js 202

    7、Vue中模板编译原理?

      如何将template转换成render函数(这里要注意的是我们在开发时尽量不要使用template,因为将template转化成render方法需要在运行时进行编译操作会有性能损耗,同时引用带有complier包的vue体积也会变大),默认.vue文件中的 template 处理是通过 vue-loader 来进行处理的并不是通过运行时的编译。

    1) 将 template 模板转换成 ast 语法树 - parserHTML

    2) 对静态语法做静态标记 - markUp

    3) 重新生成代码 - codeGen

      补充知识回答:

      模板引擎的实现原理就是new Function + with来进行实现的。

      vue-loader中处理template属性主要靠的是 vue-template-compiler

      vue-loader

    // template => ast => codegen => with+function 实现生成render方法 
    let {ast, render } = VueTemplateCompiler.compile(`<div>{{aaa}}</div>`)
    console.log(ast, render)
    
    // 模板引擎的实现原理 with + new Function
    console.log(new Function(render).tostring())
    // render方法执行完毕后生成的是虚拟 dom
    // with(this){return _c('div',[_s(aaa)])}
    // 代码生成

      源码设置:

     const ast = parse(template.trim(), options) // 将代码解析成ast语法树
      if (options.optimize !== false) {
        optimize(ast, options) // 优化代码 标记静态点 标记树
      }
      const code = generate(ast, options) // 生成代码 

      源码地址:src/compiler/index.js

    8、Proxy 与 Object.defineProperty 优劣对比

      Proxy 的优势如下:

    1)Proxy 可以直接监听对象而非属性;

    2)Proxy 可以直接监听数组的变化;

    3)Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等,这是 Object.defineProperty 不具备的;

    4)Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;

    5)Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;

      Object.defineProperty 的优势如下:

    1)兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

    9、Vue3.x响应式数据原理

      Vue3.x改用Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。

    1)Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?

      判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。

    2)监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?

      我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。

  • 相关阅读:
    php 三级连动及 php+ajax的调试方法
    ajax传值 乱码问题
    ajax传值给php
    php连接mssql pdo
    语法正确的情况下报错的原因
    Mina传递对象
    Mina小例子
    基于MINA构建简单高性能的NIO应用
    点与不规则图形关系判断
    Postman----基础使用篇(没有接口文档的情况下如何着手做接口测试)
  • 原文地址:https://www.cnblogs.com/goloving/p/13996850.html
Copyright © 2020-2023  润新知