V-1

// 掌握好 2/8 原则
// vDOM 模版渲染
// 考察整体流程是否全面,热门技术是否有深度。

// 题目
  // 描述 组件渲染和更新的 过程
  // 描述 双向数据绑定 v-model 的实现原理

// Vue原理
  // 组件化
  // 响应式
  // vdom 和 diff
  // 模版编译
  // 组件渲染过程
  // 前端路由

// 如何理解 MVVM?
  // 组件化基础
    // asp jsp php 已经有组件化了
    // nodejs 也有组件化了~

    // 数据驱动视图 (MVVM setState)
      // 传统组件 - 只是静态渲染,更新还要依赖于操作DOM
      // 数据驱动视图 - React setState
      // 数据驱动视图 - Vue MVVM - 只需要修改数据 就可以把视图内容修改了~ 不再是频繁操作 DOM 了
        //  - View <-> ViewMdeol(Vue就是 提供 链接,中间并处理的一个能力 ) <-> Model
        // Model view viewModel

// Vue 响应式
  // 组件 data 的数据一旦变化,立刻触发视图的更新。
  // 实现 数据驱动视图的 第一步
    // Object.defineProperty
      Object.defineProperty(data, 'name', {
        get: function() {return name},
        set: function(newVal) {name = newVal}
      })
      // - 监听对象 监听数组
      // - 复杂对象 深度监听
        //  - 具体可看 observe-demo 文件夹~
        // Object.defineProperty 是不具备 监听数组的能力的 
      // - 缺点
        // 深度监听, 需要递归到底, 一次性计算量大
        // ⚠️ - 所以需要一次性 递归完吗? 可不可以 什么时候用到什么时候监听呢?
        // ⚠️ - 如果对 data 新增一个属性 就监听不到 所以需要有 Vue.set
        // ⚠️ - 如果对 data 删除一个属性 就监听不到 所以需要有 Vue.delete
        // ⚠️ - 无法原生 监听数组, 需要特殊处理。
    // Object.defineProperty 的一些缺点 (Vue3.0 启动 Proxy)
    // Proxy 有兼容性问题 而且无法 polyfill - ⚠️

// 虚拟DOM (Virtual DOM) 和 diff - 
  // vdom 是实现 vue 和 React 的重要基石
  // diff 算法是 vdom 中最核心,最关键的部分。
  // vdom 是一个热门话题 也是面试中的热门问题
  
    // DOM操作非常耗费性能 - ⚠️⚠️
    // 用 jQ 的时候可以自行操作 DOM 手动调整
    // Vue React 是数据驱动视图, 如何有效控制 DOM 操作?
  // vdom~
    // 有了一定的复杂度,想减少计算次数就比较难
    // 能不能吧计算 更多的转移为 Js 计算呢? 因为 Js 的执行速度很快~
    // vdom 用 Js 模拟 DOM 结构,计算出最小的变更,操作DOM~

    // 用 Js 模拟 DOM 结构 - 
      // HTML 可以说是 XML 的一个特别版本~
        

vdom

  • a
{ tag: 'div', // 目标元素~ props: { // 属性 样式 事件啥的~ 子元素 id: '1', className: '2', }, children: [ { tag: 'p' // text文字也是 子节点 children: 'vdom' }, { tag: 'ul', props: { style: 'font-size: 20px'; }, children: [ { tag: 'li', children: 'a' } ] } ] } // 通过 snabbdom 学习 dom - (Vue 参考它的实现的vdom 和 diff) // - diff 算法 / diff算法能在日常使用 vue React 中体现出来 (例如 key) // 两棵树做 diff, 如这里的 vdom diff~ // - 树的 diff算法的 事件复杂度 (O^3) - 1. 遍历tree1 2. 遍历tree2 3.排序 // 如果有 1000 个节点, 要计算1亿次 算法不可用。 // - 优化时间复杂度到 O(n) // - 只比较统一层级, 不跨级比较 // - tag 不相同,则直接删掉重建, 不再深度比较 // - tag 和 key,两者都相同, 则认为是相同节点, 不再深度比较。 // snabbdom 源码解读~ // [cbs 其实就是 callbacks] [cbs.pre 就是执行 pre-hook~] [hook 其实就是 类似生命周期] // h函数 - 返回的是一个 vnode(vnode 返回的是一个对象) // patch函数解析 // 第一个参数不是 vnode~ 那就创建一个 空的vnode 关联到这个 DOM 元素 // 相同的 vnode~ (判断相同vnode的条件 - key 和 tag 都相等 就是相同的) // - 都不传 key 那 undefined === undefined // true 都不传递 key 是不再循环体内的 // 不同的 vnode~ (如果不相同 那就 对比同一层级 然后进行删掉 销毁 重建~) // - patchVnode // 1 - 执行 prepatch hook~ // 2 - 设置 vnode.elem (将旧的vnode对应的 elem 元素 对应的 赋值给 新的vnode 这样才可以知道 之后更新或者替换哪个 DOM元素) // 3 - 获取 旧的children 和 新的children // 4 - 判断 新的 text 等于 undefined (那就意味新的 children 一般有值) ~ // 4.1 - 也是对比新旧 (- 新的有旧的没就赋值 - 旧的有新的没就删除 - 新旧都有就更新(updateChildren)) // 5 - 4否则 然后做处理 // 6 - 在判断 新的text 和 旧的text 对比~ (然后对比之后 不一样的话执行 删掉销毁重建(设置新的text)啥的) // 更新节点操作 - updateChildren // - 定义了 oldStartIdx oldEndIdx newStartIdx newEndIdx // 然后 开始累加 结尾递减 在一边对比~ (往中间 碰头~) // 然后新旧 开始和开始做对比 else 结尾和结尾做对比 else 开始和结尾 else 结尾和开始 (交叉对比) 这个只是 snabbdom 自己的对比~ // 如果命中了就会走 patchVnode 函数 然后有子节点的话 再递归对比 子节点 // 然后 else 以上四个都没有命中 // - 拿新节点的 key 能否对应上 oldChildren 中的某个节点的key (这个就是 我拿新的一个节点去找 旧的当中任意一个 去查看对比) // - 没有对应上 - 直接重建(createElem) 就是插入 insertBefore()~ // - 对应上了的话 查看 key 和 sel(就是tag) 看看是否相等 - 相等就是 patchVnode 不想等就是 - 直接重建(createElem) 就是插入 insertBefore()~ // ⚠️ - 不使用key 和 使用key的对比 - 例如只是 顺序改变了 // 如果不用key 就判断不一样直接删除了~ // 如果使用key 那就只是改变一下顺序 // ⚠️ - 所以 key 不能使用随机数或者index 要和遍历Item有关的唯一标示的val 做key // vdom和diff算法总结 - vdom核心改变很重要 h vnode patch diff key 等 // vdom 存在的价格更加重要 - 数据驱动视图 控制DOM操作 - // 模版编译~ // 模版是 vue 开发中最常用的部分, 他不是 html, 它有指令,插值,Js表达式,能实现判断,循环啥的。 // html 是标签语言, 只有 Js 才能实现判断,循环(图灵完备的语言) // 因此 模版一定是转换为 某种Js代码 即编译模版~ // 具体查看 ./vue-template-compiler-demo 文件夹 // 组件渲染和更新过程 // vue template complier 将模版编译成 render 函数 // 执行 render 函数生成 vnode (vnode 在渲染到 DOM上~) // 基于 vnode 在执行 patch 和 diff // 监听属性变化(有监听属性 和触发更新视图的方法) 生成一个新的属性 生成一个新的vnode 然后再对应的渲染到DOM上 // 如果使用 webpack vue-loader, 会在 开发环境下 编译模版~( 如果在运行时编译就比较慢⚠️) // - // Js 的 with 语法~ 不常用 // 使用 with,能改变 {} 内自由变量的查找方式。 // 将 {} 内自由变量, 当作 obj 的属性来查找。 // ⚠️ - with 要慎用, 他打破了作用域规则, 易读性变差~ const obj = { a: 1 }; with(obj) { console.log(a); // 1 console.log(b); // 报错 } console.log(obj.b); // 这样会打印 undefined // Vue 组件中使用 render 代替 template - Vue.component('heading', { // template: 'xxx', 第一种方式 template 执行 render: function(createElement) { // 第二种方式, rander 函数执行 return createElement( 'h' + this.level, // 这里的 h 就是简单的字符串拼接就是 h1 h2啥的 // 第二个参数可以是属性 [ // 第三个参数 就是 子元素 createElement('a', { attrs: { name: 'headerId', href: '#' + 'headerId' } }, 'this is a tag') ] ) } }) // 在有些复杂情况下, 不能用 template,就考虑使用 render~ // React 一直都在用 render(没有模版) 和这里一样 // 模版到 render 再到vnode 再到渲染和更新 - // 组件渲染和更新过程 - - 考察对流程了解的全面程度 // - 一个组件渲染到页面,修改data触发更新(数据驱动视图) // 知识点 // - zs - 响应式: 监听 data属性, getter setter 包括数组 // - zs - 模版编译: 模版到 render 函数,再到 vnode // - zs - vdom: `patch(elem, vnode)`(把 vnode 渲染到空的 elem 上) 和 `patch(vnode, newVnode)` (新的vnode 去更新 旧的vnode) // 初次渲染过程 // 解析模版为 render 函数 (这个vue-loader 使用webpack 的话 在编译或者打包的时候已经完成了) // 触发响应式 监听 data 属性的 getter setter // with(this){return _c('p',[_v(_s(message))])} // 就获取到了 this.message ⚠️⚠️⚠️⚠️ //

{{ meaage }}

// data() { // return { // message: 'hello', // 会触发get // city: '北京' // 不会触发get 因为模版没有用到 和视图没有关系 // } // } // 执行 render 函数, 生成vnode, patch(elem, vnode) // 更新过程 // 修改 data 触发 setter (此前 getter 中已被监听) // 重新执行 render 函数, 生成 newVnode // patch(vnode, newVnode) - 对比差异 更新 // render 会生成 vnode~ // render 过程中触发 data (getter setter) // 然后中间会监听 然后触发依赖 收集依赖 // 异步渲染 - // 回顾 $nextTick ($nextTick 会等待 DOM 渲染完再回调) // 汇总 data 的修改, 一次性 更新 视图 (多次 data 只更新一次~) // 减少 DOM 操作次数,提高性能 // Vue-router - - 前端路由的原理 // hash - window.onhashchage // hash - #后面的部分 - 通过监听 hash的变化 来触发路由的变化 // hash 变化会触发网页跳转 (即浏览器的前进 后退) // hash 变化不会刷新页面 SPA必需的特点 (router-view 前端自己控制) // hash 永远不会提交到 server 端 (完全属于前端的东西) // window.onhashchage = (event) => { // event.oldURL - 变化前的url // event.newURL - 新的url // location.hash - 获取hash值 // } // // 页面初次加载,获取 hash // document.addEventListener('DOMContentLoaded', () => { // console.log('hash:', location.hash); // }) // // hash的变化包括 - Js修改URL - 手动修改url的hash(如果修改其他 可能会触发页面刷新) - 浏览器的前进,后退 // // Js修改 url // btn.onclick(() => { // location.href = '#/user'; // }) // history (H5) // 用 URL 规范的路由, 但跳转的时候不刷新页面(是 SPA 必需的) // histoty.pushState // window.onpopstate // 正常页面浏览 // - https://gighub.com/yyy - https://gighub.com/yyy/aaa - https://gighub.com/yyy/aaa/bbb 这三种都会刷新页面 // 改造成 H5-history 模式 // - https://gighub.com/yyy(第一次访问刷新页面) - https://gighub.com/yyy/aaa - https://gighub.com/yyy/aaa/bbb 后两次都前端跳转,不刷新页面  // document.addEventListener('DOMContentLoaded', () => { // console.log('path:', location.pathname); // 页面初次加载 获取path // }) // // 打开一个新的路由 【⚠️注意】用pushState方式, 浏览器不会刷新页面 // btn.onclick(() => { // const state = { name: 'page1' }; // console.log('切换路由到 page1'); // todo code this // histoty.pushState(state, '', 'page1'); // 重要 // }) // // 监听浏览器的前进 后退 // window.onpopstate = (event) => { // 重要 // // state 很有可能就是上面定义的 state(只要定义了 state) // // location.pathname 可以监听到 path // console.log('onpopstate', event.state, location.pathname); // } // // 需要 server 端配合~ 无论访问什么样的路由 都回去返回一个 index.html~ // 无论前端怎么搞 后端只返回 前端的一个主文件即可~ // to B系统推荐 hash 简单易用 对url规范不敏感。 - // to C系统 可以考虑 H5 history, 但需要服务端支持 - 如果需要做 搜索引擎优化的话 就需要考虑 H5 history - // 总结~

你可能感兴趣的:(V-1)