• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

《vuejs设计和实现》笔记

武飞扬头像
红豆泥豆红
帮助1

第一篇:框架设计概览

权衡
  • 命令式:关注过程,eg:jQuery
  • 声明式:关注结果
  • 运行时:手写 render 函数的树形结构数据对象
  • 运行时编译:把html字符编译成树形结构数据对象,再调用render渲染,eg:Vue。优点在于可以分析用户提供的内容。
  • 编译时:把 html 直接编译成命令式代码,eg:Svelte。灵活性不足。
核心要素
  • 开发体验:
    • 友好的警告信息warn()
    • 直观的输出内容initCustomFormatter(),可以通过修改浏览器 devtool 设置让输出更友好
  • 框架体积:
    • 仅开发环境提供警告: __DEV__结合 Rollupjs 插件配置
    • /*#__PURE__*/foo()告诉 rollupjs,foo()的调用不会产生副作用,可进行 tree shaking
  • 构建产物:为不同的环境输出不同的包
    • <script> IIFE立即执行函数
    • <script type='module'> ESM
      • vue.runtime.esm-bundler.js
      • vue.esm-browser.js
  • 特性开关:利用 rollup 预定义常量插件来实现,__VUE_OPTIONS_API__
  • 错误处理:为用户提供统一的错误处理接口:registerErrorHandler() callWithErrorHandling()
  • TS类型支持
设计思路
  • 声明式地描述 UI(使用模板或手写渲染函数使用虚拟DOM来描述 UI)
    • 虚拟 DOM:用 js 对象来描述真实的 DOM 结构
  • 渲染器:把虚拟 DOM 创建成真实的 DOM。
    • h()函数:用于创建 vnodes,返回对象,辅助虚拟DOM创建
    • render()渲染函数,创建虚拟 DOM 树
    • renderer() 渲染器函数:创建元素、添加属性和事件、处理 children……
  • 组件:是一组DOM元素的封装,可用函数/对象代表组件,返回要渲染的内容 p58
    • renderer 函数增加对组件的支持,当tag为组件(函数/对象)时……
  • 编译器:将模板<template>编译成渲染函数render(),并添加到<script>组件对象上。 运行时渲染器调用渲染函数,遍历返回的虚拟 DOM 树,并基于它创建实际的 DOM 节点。

学新通

第二篇:响应系统

情景
  • 有一个数据对象obj和若干个依赖该数据对象的副作用函数effect(){...obj...}
  • 希望数据对象发生变化的时候,副作用函数自动重新执行一遍
思路
  • proxy代理,(调用副作用函数时)get存储副作用函数track(),(修改数据对象时)set重新执行副作用函数trigger()
  • 存储副作用函数到桶里bucket——怎么存?用什么数据结构存储?bucket = weakmap map set
    • 如果副作用函数为匿名,怎么存储?——手动注册,保存到全局变量
    • 修改对象中的一个属性时怎么不触发没读取该属性的副作用函数?(此时用 set 作为桶的数据结构)——修改桶的数据结构,让每个属性对应其副作用函数。
    • weakmap 不影响垃圾回收,优化内存 学新通
  • 分支切换时,不会删掉切换前属性存下来的遗留副作用函数,怎么办?——p78 分支副作用函数执行时先移出,执行完再重新移入,确保保存的副作用函数是和当前分支属性关联的。
    • 要移出得先收集副作用函数的所有关联依赖集合——收集副作用函数时,新增 deps 属性保存依赖
    • cleanup()移除依赖
  • 避免无限循环,新建数据effectsToRun
代码实现

html 部分

<h1 id="text1">Hello</h1>
<button id="btn">click to change obj</button>

主要流程

const data = { key1: "value1", key2: "value2" }; //响应式源对象
const bucket = new WeakMap(); //存储副作用函数的桶
let activeEffect; //用一个全局变量存储被注册的副作用函数

//代理 data
const obj = new Proxy(data, {
    //拦截读取属性
    get(target, key) {
        track(target, key); //将副作用函数添加到存储副作用函数的桶中
        return target[key]; //继续读取属性值
    },
    //拦截修改属性
    set(target, key, value) {
        target[key] = value; //继续修改属性
        trigger(target, key); //把副作用函数从桶里取出并执行
    },
});

//副作用函数
effect(() => {
    setTimeout(() => {
        console.log("正在执行副作用函数");
        const text1 = document.getElementById("text1");
        text1.textContent = obj.key1; //用到了外部数据对象
    }, 1000);
});
  
//修改obj,看是否副作用函数是否再次执行
setTimeout(() => {
    let btn = document.getElementById("btn");
    btn.addEventListener("click", () => {
        obj.key1 = "World"; //修改 obj
        console.log("修改了obj");
    });
}, 1000);

函数封装

//注册副作用函数
function effect(fn) {
    //设置当前副作用函数
    function effectFn() {
        cleanup(effectFn); //调用 cleanup 函数完成清除工作
        activeEffect = effectFn; //将effectFn设置为当前副作用函数
        fn(); //执行副作用函数
    }
    effectFn.deps = []; //用来存储所有与该副作用函数相关联的依赖集合
    effectFn();
}

//在每次副作用函数执行时,将副作用函数从依赖集合中移除
function cleanup(effectFn) {
    for (let i = 0; i < effectFn.deps.length; i  ) {
        const deps = effectFn.deps[i];
        deps.delete(effectFn);
    }
    effectFn.deps.length = 0;
}

//把副作用函数存储到桶里
function track(target, key) {
    //如果当前没有副作用函数就算了
    if (!activeEffect) return;
    //如果当前有副作用函数,则存储到桶的weakmap>map>set里
    let depsMap = bucket.get(target);
    if (!depsMap) {
        bucket.set(target, (depsMap = new Map())); //初始化map
    }
    let deps = depsMap.get(key);
    if (!deps) {
        depsMap.set(key, (deps = new Set())); //初始化set
    }
    deps.add(activeEffect); //将当前激活的副作用函数添加到桶里
    //deps 就是一个与当前副作用函数存在联系的依赖集合
    //将其添加到 activeEffect.deps 数组中
    activeEffect.deps.push(deps);
}

//把副作用函数从桶里取出并执行
function trigger(target, key) {
    const depsMap = bucket.get(target);
    //如果桶里没有副作用函数就算了
    if (!depsMap) return;
    //如果桶里有副作用函数,则都取出来执行
    const effects = depsMap.get(key);
    //为了避免无限循环,执行时需要额外操作
    // effects && effects.forEach((fn) => fn());
    const effectsToRun = new Set(effects); 
    effectsToRun.forEach((effectFn) => effectFn());
}

www.bilibili.com/video/BV17G…

reactive proxy 只能代理对象

非原始值(引用数据类型)响应方案
原始值(基本数据类型)响应方案

第三篇:渲染器

设计

挂载与更新

(简单、双端、快速)Diff算法

第四篇:组件化

实现原理

异步组件

函数式组件

内建组件和模块

第五篇:编译器

编译器核心技术

解析器

编译优化

第六篇:服务端渲染

同构渲染

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbagec
系列文章
更多 icon
同类精品
更多 icon
继续加载