├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── README_EN.md ├── jest.config.js ├── package.json ├── packages ├── compiler-core │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── codegen.spec.ts.snap │ │ ├── codegen.spec.ts │ │ ├── parse.spec.ts │ │ └── transform.spec.ts │ ├── package.json │ └── src │ │ ├── ast.ts │ │ ├── codegen.ts │ │ ├── compile.ts │ │ ├── index.ts │ │ ├── parse.ts │ │ ├── runtimeHelpers.ts │ │ ├── transform.ts │ │ ├── transforms │ │ ├── transformElement.ts │ │ ├── transformExpression.ts │ │ └── transformText.ts │ │ └── utils.ts ├── reactivity │ ├── __tests__ │ │ ├── computed.spec.ts │ │ ├── dep.spec.ts │ │ ├── effect.spec.ts │ │ ├── reactive.spec.ts │ │ ├── readonly.spec.ts │ │ ├── ref.spec.ts │ │ └── shallowReadonly.spec.ts │ ├── package.json │ └── src │ │ ├── baseHandlers.ts │ │ ├── computed.ts │ │ ├── dep.ts │ │ ├── effect.ts │ │ ├── index.ts │ │ ├── reactive.ts │ │ └── ref.ts ├── runtime-core │ ├── __tests__ │ │ ├── apiWatch.spec.ts │ │ ├── componentEmits.spec.ts │ │ ├── rendererComponent.spec.ts │ │ └── rendererElement.spec.ts │ ├── package.json │ └── src │ │ ├── .pnpm-debug.log │ │ ├── apiInject.ts │ │ ├── apiWatch.ts │ │ ├── component.ts │ │ ├── componentEmits.ts │ │ ├── componentProps.ts │ │ ├── componentPublicInstance.ts │ │ ├── componentRenderUtils.ts │ │ ├── componentSlots.ts │ │ ├── createApp.ts │ │ ├── h.ts │ │ ├── helpers │ │ └── renderSlot.ts │ │ ├── index.ts │ │ ├── renderer.ts │ │ ├── scheduler.ts │ │ └── vnode.ts ├── runtime-dom │ ├── package.json │ └── src │ │ └── index.ts ├── runtime-test │ └── src │ │ ├── index.ts │ │ ├── nodeOps.ts │ │ ├── patchProp.ts │ │ └── serialize.ts ├── shared │ ├── package.json │ └── src │ │ ├── index.ts │ │ ├── shapeFlags.ts │ │ └── toDisplayString.ts └── vue │ ├── .DS_Store │ ├── dist │ ├── mini-vue.cjs.js │ ├── mini-vue.cjs.js.map │ ├── mini-vue.esm-bundler.js │ └── mini-vue.esm-bundler.js.map │ ├── example │ ├── apiInject │ │ ├── App.js │ │ └── index.html │ ├── compiler-base │ │ ├── App.js │ │ └── index.html │ ├── componentEmit.js │ │ ├── App.js │ │ ├── Child.js │ │ └── index.html │ ├── componentProxy │ │ ├── App.js │ │ ├── Child.js │ │ └── index.html │ ├── componentUpdate │ │ ├── App.js │ │ ├── Child.js │ │ └── index.html │ ├── createTextVnode │ │ ├── App.js │ │ └── index.html │ ├── customRenderer │ │ ├── App.js │ │ ├── game.js │ │ ├── index.html │ │ ├── main.js │ │ └── renderer.js │ ├── getCurrentInstance │ │ ├── App.js │ │ └── index.html │ ├── helloWorld │ │ ├── App.js │ │ ├── index.html │ │ └── main.js │ ├── nextTicker │ │ ├── App.js │ │ ├── NextTicker.js │ │ ├── index.html │ │ └── main.js │ ├── patchChildren │ │ ├── App.js │ │ ├── PatchChildren.js │ │ ├── index.html │ │ └── main.js │ ├── renderComponent │ │ ├── App.js │ │ ├── Child.js │ │ └── index.html │ ├── setupStateRenderComponent │ │ ├── App.js │ │ └── index.html │ └── slotsComponent │ │ ├── App.js │ │ ├── Child.js │ │ └── index.html │ ├── index.js │ ├── package.json │ └── src │ └── index.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── rollup.config.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "name": "vscode-jest-tests", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 12 | "args": ["--runInBand", "--watchAll=false"], 13 | "cwd": "${workspaceFolder}", 14 | "console": "integratedTerminal", 15 | "internalConsoleOptions": "neverOpen", 16 | "disableOptimisticBPs": true, 17 | "windows": { 18 | "program": "${workspaceFolder}/node_modules/jest/bin/jest" 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 zenoslin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, libribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [CN](README.md) / [EN](README_EN.md) 2 | 3 | ## mini-vue [![github](https://img.shields.io/badge/%E5%82%AC%E5%AD%A6%E7%A4%BE-mini--vue-blue)](https://github.com/cuixiaorui/mini-vue) 4 | 5 | 实现最简 vue3 模型,用于深入学习 vue3, 让你更轻松的理解 vue3 的核心逻辑 6 | 7 | ## Usage 8 | 9 | [B 站](https://www.bilibili.com/video/BV1Zy4y1J73E) 提供了视频讲解使用方式 10 | 11 | ## Why 12 | 13 | 当我们需要深入学习 vue3 时,我们就需要看源码来学习,但是像这种工业级别的库,源码中有很多逻辑是用于处理边缘情况或者是兼容处理逻辑,是不利于我们学习的。 14 | 15 | 我们应该关注于核心逻辑,而这个库的目的就是把 vue3 源码中最核心的逻辑剥离出来,只留下核心逻辑,以供大家学习。 16 | 17 | ## How 18 | 19 | 基于 vue3 的功能点,一点一点的拆分出来。 20 | 21 | 代码命名会保持和源码中的一致,方便大家通过命名去源码中查找逻辑。 22 | 23 | ### Tasking 24 | 25 | #### runtime-core 26 | 27 | - [x] 支持组件类型 28 | - [x] 支持 element 类型 29 | - [x] 初始化 props 30 | - [x] setup 可获取 props 和 context 31 | - [x] 支持 component emit 32 | - [x] 支持 proxy 33 | - [x] 可以在 render 函数中获取 setup 返回的对象 34 | - [x] nextTick 的实现 35 | - [x] 支持 getCurrentInstance 36 | - [x] 支持 provide/inject 37 | - [x] 支持最基础的 slots 38 | - [x] 支持 Text 类型节点 39 | - [x] 支持 $el api 40 | - [x] 支持 watchEffect 41 | 42 | 43 | #### reactivity 44 | 45 | 目标是用自己的 reactivity 支持现有的 demo 运行 46 | 47 | - [x] reactive 的实现 48 | - [x] ref 的实现 49 | - [x] readonly 的实现 50 | - [x] computed 的实现 51 | - [x] track 依赖收集 52 | - [x] trigger 触发依赖 53 | - [x] 支持 isReactive 54 | - [x] 支持嵌套 reactive 55 | - [x] 支持 toRaw 56 | - [x] 支持 effect.scheduler 57 | - [x] 支持 effect.stop 58 | - [x] 支持 isReadonly 59 | - [x] 支持 isProxy 60 | - [x] 支持 shallowReadonly 61 | - [x] 支持 proxyRefs 62 | 63 | ### compiler-core 64 | - [x] 解析插值 65 | - [x] 解析 element 66 | - [x] 解析 text 67 | 68 | ### runtime-dom 69 | - [x] 支持 custom renderer 70 | 71 | ### runtime-test 72 | - [x] 支持测试 runtime-core 的逻辑 73 | 74 | ### infrastructure 75 | - [x] support monorepo with pnpm 76 | ### build 77 | 78 | ```shell 79 | pnpm build 80 | ``` 81 | 82 | ### example 83 | 84 | 通过 server 的方式打开 packages/vue/example/\* 下的 index.html 即可 85 | 86 | >  推荐使用 [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) 87 | 88 | ### 初始化 89 | 90 | #### 流程图 91 | ![初始化流程图](https://user-images.githubusercontent.com/12064746/138114565-3e0eecbb-7fd0-4203-bf36-5e5fd8003ce0.png) 92 | 93 | > 可加 vx:cuixr1314 获取所有脑图(备注:github mini-vue 领取脑图) 94 | #### 关键函数调用图 95 | 96 | 97 | ![关键函数调用图2](https://images-1252602850.cos.ap-beijing.myqcloud.com/20220927170658.png) 98 | 99 | > 可以基于函数名快速搜索到源码内容 100 | 101 | ### update 102 | 103 | #### 流程图 104 | 105 | ![image](https://user-images.githubusercontent.com/12064746/138115157-1f4fb8a2-7e60-412d-96de-12e68eb0288c.png) 106 | 107 | #### 关键函数调用图 108 | 109 | ![image](https://user-images.githubusercontent.com/12064746/138114969-9139e4af-b2df-41b2-a5d9-069d8b41903c.png) 110 | 111 | 112 | > 可以基于函数名快速搜索到源码内容 113 | 114 | 115 | 116 | ### 从零到一实现一遍 117 | 118 | 自从有了 mini-vue 之后 很多同学都问我 能不能带着他从零到一敲一遍 119 | 120 | 因为对于源码的学习来讲 看在多遍也不如自己写一遍 121 | 122 | 为此我把 mini-vue 做成了一套视频课 从零到一带着大家实现一遍 不跳过任何一行代码 123 | 124 | 当然除了功能上的实现还有编程思想融入到了课程内 125 | 126 | 比如 TDD、小步走、重构手法、TPP 127 | 128 | > TDD 测试驱动开发 影响了我整个技术生涯 可以说在我认识到 TDD 之后 技术才有了质的飞跃 129 | 130 | 课程目录如下: 131 | 132 | 1. vue3 源码结构的介绍 133 | 2. reactivity 的核心流程 134 | 3. runtime-core 初始化的核心流程 135 | 4. runtime-core 更新的核心流程 136 | 5. setup 环境 -> 集成 jest 做单元测试 & 集成 test 137 | 6. 实现 effect 返回 runner 138 | 7. 实现 effect 的 scheduler 功能 139 | 8. 实现 effect 的 stop 功能 140 | 9. 实现 readonly 功能 141 | 10. 实现 isReactive 和 isReadonly 142 | 11. 优化 stop 功能 143 | 12. 实现 reactive 和 readonly 嵌套对象转换功能 144 | 13. 实现 shallowReadonly 功能 145 | 14. 实现 isProxy 功能 146 | 15. 实现 isProxy 功能 147 | 16. 实现 ref 功能 148 | 17. 实现 isRef 和 unRef 功能 149 | 18. 实现 proxyR 功能 150 | 19. 实现 computed 计算属性功能 151 | 20. 实现初始化 component 主流程 152 | 21. 实现 rollup 打包 153 | 22. 实现初始化 element 主流程 154 | 23. 实现组件代理对象 155 | 24. 实现 shapeFlags 156 | 25. 实现注册事件功能 157 | 26. 实现组件 props 功能 158 | 27. 实现组件 emit 功能 159 | 28. 实现组件 slots 功能 160 | 29. 实现 Fragment 和 Text 类型节点 161 | 30. 实现 getCurrentInstance 162 | 31. 实现依赖注入功能 provide/inject 163 | 32. 实现自定义渲染器 custom renderer 164 | 33. 更新 element 流程搭建 165 | 34. 更新 element 的props 166 | 35. 更新 element 的 children 167 | 36. 双端对比 diff 算法1 168 | 37. 双端对比 diff 算法2 - key 的作用 169 | 38. 双端对比 diff 算法3 - 最长子序列的作用 170 | 39. 学习尤大解决 bug 的处理方式 171 | 40. 实现组件更新功能 172 | 41. 实现 nextTick 功能 173 | 42. 编译模块概述 174 | 43. 实现解析插值功能 175 | 44. 实现解析 element 标签 176 | 45. 实现解析 text 功能 177 | 46. 实现解析三种联合类型 template 178 | 47. parse 的实现原理&有限状态机 179 | 48. 实现 transform 功能 180 | 49. 实现代码生成 string 类型 181 | 50. 实现代码生成插值类型 182 | 51. 实现代码生成三种联合类型 183 | 52. 实现编译 template 成 render 函数 184 | 53. 实现 monorepo & 使用 vitest 替换 jest 185 | 186 | 课程内部包含了 vue3 的三大核心模块:reactivity、runtime 以及 compiler 模块 187 | 188 | 等你自己手写一遍之后 在去看 vue3 源码或者再去看分析解析 vue3 源码的书籍时你会有不同的体验 189 | 190 | 除此之外 还录制了课程介绍以及课程试听课 191 | - [课程介绍](https://www.bilibili.com/video/BV16Z4y1r7Wp) 192 | - [试听课](https://www.bilibili.com/video/BV1R341177P7) 193 | - [购买链接](https://cua.h5.xeknow.com/s/xDWLc) 194 | 195 | > 可以直接购买 也可以加我 wx: cuixr1314 来咨询这门课是否合适你 196 | 197 | 除了课程内容以外 还有专门的社群来答疑大家在学习上的问题 😊 198 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | [EN](README.md) / [CN](README.md) 2 | ## mini-vue 3 | 4 | Implement the simplest vue3 model for in-depth study of vue3 source code 5 | 6 | ## Usage 7 | 8 | [Bilibili](https://www.bilibili.com/video/BV1Zy4y1J73E) Provides a video explaining how to use it 9 | 10 | > Can follow my [Bilibili](https://space.bilibili.com/175301983),Interpretation of live source code from time to time 11 | 12 | ## Discuss 13 | 14 | You can join the group to discuss the vue3 source code 15 | 16 | 17 | ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cbe1b6e9c67944828c3e653fd7919dc0~tplv-k3u1fbpfcp-watermark.image) 18 | 19 | > with WeChat 20 | 21 | ## Service 22 | 23 | Provide one-to-one video teaching services, and take you to see the mini-vue source code hand in hand 24 | 25 | > Can add group communication 26 | 27 | ## Why 28 | 29 | When we need to learn vue3 in depth, we need to look at the source code to learn, but like this kind of industrial-level library, there are a lot of logic in the source code for processing edge cases or compatible processing logic, which is not conducive to our learning. 30 | 31 | We should focus on the core logic, and the purpose of this library is to separate the core logic from the vue3 source code, leaving only the core logic for everyone to learn. 32 | 33 | ## How 34 | 35 | Based on the function points of vue3, split it out bit by bit 36 | 37 | The code naming will remain consistent with the source code, so that you can find logic in the source code through naming. 38 | 39 | ### Tasking 40 | 41 | - [x] support component type 42 | - [x] support element type 43 | - [x] init props of component 44 | - [x] context can get props and context in setup 45 | - [x] support component emit 46 | - [x] support proxy 47 | - [x] can get the object returned by setup in the render function 48 | - [x] Implementation of nextTick 49 | - [x] support getCurrentInstance 50 | - [x] support provide/inject 51 | - [x] support basic slots 52 | - [x] support text type 53 | 54 | ### roadmap 55 | 56 | - [ ] support english 57 | - [ ] normalize console.log 58 | 59 | ### build 60 | 61 | ```shell 62 | yarn build 63 | ``` 64 | 65 | ### example 66 | 67 | Open index.html under example/\* use server 68 | 69 | >  Recommended Use [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) 70 | 71 | ### Initialization 72 | 73 | #### flow chart 74 | 75 | ![初始化流程图](https://user-images.githubusercontent.com/12064746/138114565-3e0eecbb-7fd0-4203-bf36-5e5fd8003ce0.png) 76 | 77 | #### Key function call graph 78 | 79 | ![关键函数调用图1](https://user-gold-cdn.xitu.io/2020/6/22/172dc07fc42b7d2c?w=1342&h=144&f=png&s=54200) 80 | 81 | ![关键函数调用图2](https://user-gold-cdn.xitu.io/2020/6/22/172dc08840e25b42?w=1816&h=934&f=png&s=550722) 82 | 83 | > The source code content can be quickly searched based on the function name 84 | 85 | ### update 86 | 87 | #### flow chart 88 | 89 | ![update流程图](https://user-images.githubusercontent.com/12064746/138115157-1f4fb8a2-7e60-412d-96de-12e68eb0288c.png) 90 | 91 | #### Key function call graph 92 | 93 | ![update关键函数调用图](https://user-images.githubusercontent.com/12064746/138114969-9139e4af-b2df-41b2-a5d9-069d8b41903c.png) 94 | 95 | > The source code content can be quickly searched based on the function name 96 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | watchPathIgnorePatterns: ["/node_modules/", "/dist/", "/.git/"], 6 | moduleFileExtensions: ["ts", "tsx", "js", "json"], 7 | moduleNameMapper: { 8 | "^@mini-vue/(.*?)$": "/packages/$1/src", 9 | }, 10 | rootDir: __dirname, 11 | testMatch: ["/packages/**/__tests__/**/*spec.[jt]s?(x)"], 12 | testPathIgnorePatterns: ["/node_modules/"], 13 | }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "version": "0.0.1", 4 | "description": "Help you learn more efficiently vue3 source code", 5 | "main": "lib/mini-vue.cjs.js", 6 | "module": "lib/mini-vue.esm.js", 7 | "scripts": { 8 | "dev": "rollup -c -w", 9 | "build": "rollup -c", 10 | "test": "jest --no-cache" 11 | }, 12 | "author": "cuixiaorui", 13 | "homepage": "https://github.com/cuixiaorui", 14 | "license": "ISC", 15 | "devDependencies": { 16 | "@rollup/plugin-commonjs": "^13.0.0", 17 | "@rollup/plugin-node-resolve": "^8.1.0", 18 | "@rollup/plugin-replace": "^2.3.3", 19 | "@rollup/plugin-typescript": "^8.2.5", 20 | "@types/jest": "^26.0.24", 21 | "jest": "^27.5.1", 22 | "rollup": "^2.17.1", 23 | "rollup-plugin-sourcemaps": "^0.6.2", 24 | "ts-jest": "^27.0.5", 25 | "tslib": "^2.3.1", 26 | "typescript": "^4.4.3" 27 | }, 28 | "dependencies": { 29 | "@vue/reactivity": "^3.0.5", 30 | "pixi.js": "^6.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`element and interpolation 1`] = ` 4 | " 5 | const { toDisplayString : _toDisplayString, createElementVNode : _createElementVNode} = Vue 6 | 7 | 8 | return function render(_ctx) {return _createElementVNode('div', null, 'hi,' + _toDisplayString(_ctx.msg))}" 9 | `; 10 | 11 | exports[`interpolation module 1`] = ` 12 | " 13 | const { toDisplayString : _toDisplayString} = Vue 14 | 15 | 16 | return function render(_ctx) {return _toDisplayString(_ctx.hello)}" 17 | `; 18 | -------------------------------------------------------------------------------- /packages/compiler-core/__tests__/codegen.spec.ts: -------------------------------------------------------------------------------- 1 | import { generate } from "../src/codegen"; 2 | import { baseParse } from "../src/parse"; 3 | import { transform } from "../src/transform"; 4 | import { transformElement } from "../src/transforms/transformElement"; 5 | import { transformExpression } from "../src/transforms/transformExpression"; 6 | import { transformText } from "../src/transforms/transformText"; 7 | 8 | test("interpolation module", () => { 9 | const ast = baseParse("{{hello}}"); 10 | transform(ast, { 11 | nodeTransforms: [transformExpression], 12 | }); 13 | 14 | const { code } = generate(ast); 15 | expect(code).toMatchSnapshot(); 16 | }); 17 | 18 | test("element and interpolation", () => { 19 | const ast = baseParse("
hi,{{msg}}
"); 20 | transform(ast, { 21 | nodeTransforms: [transformElement, transformText, transformExpression], 22 | }); 23 | 24 | const { code } = generate(ast); 25 | expect(code).toMatchSnapshot(); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/compiler-core/__tests__/parse.spec.ts: -------------------------------------------------------------------------------- 1 | import { ElementTypes, NodeTypes } from "../src/ast"; 2 | import { baseParse } from "../src/parse"; 3 | 4 | describe("parser", () => { 5 | describe("text", () => { 6 | test("simple text", () => { 7 | const ast = baseParse("some text"); 8 | const text = ast.children[0]; 9 | 10 | expect(text).toStrictEqual({ 11 | type: NodeTypes.TEXT, 12 | content: "some text", 13 | }); 14 | }); 15 | 16 | test("simple text with invalid end tag", () => { 17 | const ast = baseParse("some text"); 18 | const text = ast.children[0]; 19 | 20 | expect(text).toStrictEqual({ 21 | type: NodeTypes.TEXT, 22 | content: "some text", 23 | }); 24 | }); 25 | 26 | test("text with interpolation", () => { 27 | const ast = baseParse("some {{ foo + bar }} text"); 28 | const text1 = ast.children[0]; 29 | const text2 = ast.children[2]; 30 | 31 | // ast.children[1] 应该是 interpolation 32 | expect(text1).toStrictEqual({ 33 | type: NodeTypes.TEXT, 34 | content: "some ", 35 | }); 36 | expect(text2).toStrictEqual({ 37 | type: NodeTypes.TEXT, 38 | content: " text", 39 | }); 40 | }); 41 | }); 42 | 43 | describe("Interpolation", () => { 44 | test("simple interpolation", () => { 45 | // 1. 看看是不是一个 {{ 开头的 46 | // 2. 是的话,那么就作为 插值来处理 47 | // 3. 获取内部 message 的内容即可 48 | const ast = baseParse("{{message}}"); 49 | const interpolation = ast.children[0]; 50 | 51 | expect(interpolation).toStrictEqual({ 52 | type: NodeTypes.INTERPOLATION, 53 | content: { 54 | type: NodeTypes.SIMPLE_EXPRESSION, 55 | content: `message`, 56 | }, 57 | }); 58 | }); 59 | }); 60 | 61 | describe("Element", () => { 62 | test("simple div", () => { 63 | const ast = baseParse("
hello
"); 64 | const element = ast.children[0]; 65 | 66 | expect(element).toStrictEqual({ 67 | type: NodeTypes.ELEMENT, 68 | tag: "div", 69 | tagType: ElementTypes.ELEMENT, 70 | children: [ 71 | { 72 | type: NodeTypes.TEXT, 73 | content: "hello", 74 | }, 75 | ], 76 | }); 77 | }); 78 | 79 | test("element with interpolation", () => { 80 | const ast = baseParse("
{{ msg }}
"); 81 | const element = ast.children[0]; 82 | 83 | expect(element).toStrictEqual({ 84 | type: NodeTypes.ELEMENT, 85 | tag: "div", 86 | tagType: ElementTypes.ELEMENT, 87 | children: [ 88 | { 89 | type: NodeTypes.INTERPOLATION, 90 | content: { 91 | type: NodeTypes.SIMPLE_EXPRESSION, 92 | content: `msg`, 93 | }, 94 | }, 95 | ], 96 | }); 97 | }); 98 | 99 | test("element with interpolation and text", () => { 100 | const ast = baseParse("
hi,{{ msg }}
"); 101 | const element = ast.children[0]; 102 | 103 | expect(element).toStrictEqual({ 104 | type: NodeTypes.ELEMENT, 105 | tag: "div", 106 | tagType: ElementTypes.ELEMENT, 107 | children: [ 108 | { 109 | type: NodeTypes.TEXT, 110 | content: "hi,", 111 | }, 112 | { 113 | type: NodeTypes.INTERPOLATION, 114 | content: { 115 | type: NodeTypes.SIMPLE_EXPRESSION, 116 | content: "msg", 117 | }, 118 | }, 119 | ], 120 | }); 121 | }); 122 | 123 | test("should throw error when lack end tag ", () => { 124 | expect(() => { 125 | baseParse("
"); 126 | }).toThrow("缺失结束标签:span"); 127 | }); 128 | }); 129 | }); 130 | -------------------------------------------------------------------------------- /packages/compiler-core/__tests__/transform.spec.ts: -------------------------------------------------------------------------------- 1 | import { baseParse } from "../src/parse"; 2 | import { TO_DISPLAY_STRING } from "../src/runtimeHelpers"; 3 | import { transform } from "../src/transform"; 4 | describe("Compiler: transform", () => { 5 | test("context state", () => { 6 | const ast = baseParse(`
hello {{ world }}
`); 7 | console.log(ast); 8 | 9 | // manually store call arguments because context is mutable and shared 10 | // across calls 11 | const calls: any[] = []; 12 | const plugin = (node, context) => { 13 | calls.push([node, { ...context }]); 14 | }; 15 | 16 | transform(ast, { 17 | nodeTransforms: [plugin], 18 | }); 19 | 20 | const div = ast.children[0]; 21 | expect(calls.length).toBe(4); 22 | expect(calls[0]).toMatchObject([ 23 | ast, 24 | {}, 25 | // TODO 26 | // { 27 | // parent: null, 28 | // currentNode: ast, 29 | // }, 30 | ]); 31 | expect(calls[1]).toMatchObject([ 32 | div, 33 | {}, 34 | // TODO 35 | // { 36 | // parent: ast, 37 | // currentNode: div, 38 | // }, 39 | ]); 40 | expect(calls[2]).toMatchObject([ 41 | div.children[0], 42 | {}, 43 | // { 44 | // parent: div, 45 | // currentNode: div.children[0], 46 | // }, 47 | ]); 48 | expect(calls[3]).toMatchObject([ 49 | div.children[1], 50 | {}, 51 | // { 52 | // parent: div, 53 | // currentNode: div.children[1], 54 | // }, 55 | ]); 56 | }); 57 | 58 | test("should inject toString helper for interpolations", () => { 59 | const ast = baseParse(`{{ foo }}`); 60 | transform(ast, {}); 61 | expect(ast.helpers).toContain(TO_DISPLAY_STRING); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /packages/compiler-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mini-vue/compiler-core", 3 | "version": "1.0.0", 4 | "description": "@mini-vue/compiler-core", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@mini-vue/shared": "workspace:^1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/compiler-core/src/ast.ts: -------------------------------------------------------------------------------- 1 | import { CREATE_ELEMENT_VNODE } from "./runtimeHelpers"; 2 | 3 | export const enum NodeTypes { 4 | TEXT, 5 | ROOT, 6 | INTERPOLATION, 7 | SIMPLE_EXPRESSION, 8 | ELEMENT, 9 | COMPOUND_EXPRESSION 10 | } 11 | 12 | export const enum ElementTypes { 13 | ELEMENT, 14 | } 15 | 16 | export function createSimpleExpression(content) { 17 | return { 18 | type: NodeTypes.SIMPLE_EXPRESSION, 19 | content, 20 | }; 21 | } 22 | 23 | export function createInterpolation(content) { 24 | return { 25 | type: NodeTypes.INTERPOLATION, 26 | content: content, 27 | }; 28 | } 29 | 30 | export function createVNodeCall(context, tag, props?, children?) { 31 | if (context) { 32 | context.helper(CREATE_ELEMENT_VNODE); 33 | } 34 | 35 | return { 36 | // TODO vue3 里面这里的 type 是 VNODE_CALL 37 | // 是为了 block 而 mini-vue 里面没有实现 block 38 | // 所以创建的是 Element 类型就够用了 39 | type: NodeTypes.ELEMENT, 40 | tag, 41 | props, 42 | children, 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /packages/compiler-core/src/codegen.ts: -------------------------------------------------------------------------------- 1 | import { isString } from "@mini-vue/shared"; 2 | import { NodeTypes } from "./ast"; 3 | import { 4 | CREATE_ELEMENT_VNODE, 5 | helperNameMap, 6 | TO_DISPLAY_STRING, 7 | } from "./runtimeHelpers"; 8 | 9 | export function generate(ast, options = {}) { 10 | // 先生成 context 11 | const context = createCodegenContext(ast, options); 12 | const { push, mode } = context; 13 | 14 | // 1. 先生成 preambleContext 15 | 16 | if (mode === "module") { 17 | genModulePreamble(ast, context); 18 | } else { 19 | genFunctionPreamble(ast, context); 20 | } 21 | 22 | const functionName = "render"; 23 | 24 | const args = ["_ctx"]; 25 | 26 | // _ctx,aaa,bbb,ccc 27 | // 需要把 args 处理成 上面的 string 28 | const signature = args.join(", "); 29 | push(`function ${functionName}(${signature}) {`); 30 | // 这里需要生成具体的代码内容 31 | // 开始生成 vnode tree 的表达式 32 | push("return "); 33 | genNode(ast.codegenNode, context); 34 | 35 | push("}"); 36 | 37 | return { 38 | code: context.code, 39 | }; 40 | } 41 | 42 | function genFunctionPreamble(ast: any, context: any) { 43 | const { runtimeGlobalName, push, newline } = context; 44 | const VueBinging = runtimeGlobalName; 45 | 46 | const aliasHelper = (s) => `${helperNameMap[s]} : _${helperNameMap[s]}`; 47 | 48 | if (ast.helpers.length > 0) { 49 | push( 50 | ` 51 | const { ${ast.helpers.map(aliasHelper).join(", ")}} = ${VueBinging} 52 | 53 | ` 54 | ); 55 | } 56 | 57 | newline(); 58 | push(`return `); 59 | } 60 | 61 | function genNode(node: any, context: any) { 62 | // 生成代码的规则就是读取 node ,然后基于不同的 node 来生成对应的代码块 63 | // 然后就是把代码快给拼接到一起就可以了 64 | 65 | switch (node.type) { 66 | case NodeTypes.INTERPOLATION: 67 | genInterpolation(node, context); 68 | break; 69 | case NodeTypes.SIMPLE_EXPRESSION: 70 | genExpression(node, context); 71 | break; 72 | 73 | case NodeTypes.ELEMENT: 74 | genElement(node, context); 75 | break; 76 | 77 | case NodeTypes.COMPOUND_EXPRESSION: 78 | genCompoundExpression(node, context); 79 | break; 80 | 81 | case NodeTypes.TEXT: 82 | genText(node, context); 83 | break; 84 | 85 | default: 86 | break; 87 | } 88 | } 89 | 90 | function genCompoundExpression(node: any, context: any) { 91 | const { push } = context; 92 | for (let i = 0; i < node.children.length; i++) { 93 | const child = node.children[i]; 94 | if (isString(child)) { 95 | push(child); 96 | } else { 97 | genNode(child, context); 98 | } 99 | } 100 | } 101 | 102 | function genText(node: any, context: any) { 103 | // Implement 104 | const { push } = context; 105 | 106 | push(`'${node.content}'`); 107 | } 108 | 109 | function genElement(node, context) { 110 | const { push, helper } = context; 111 | const { tag, props, children } = node; 112 | 113 | push(`${helper(CREATE_ELEMENT_VNODE)}(`); 114 | 115 | genNodeList(genNullableArgs([tag, props, children]), context); 116 | 117 | push(`)`); 118 | } 119 | 120 | function genNodeList(nodes: any, context: any) { 121 | const { push } = context; 122 | for (let i = 0; i < nodes.length; i++) { 123 | const node = nodes[i]; 124 | 125 | if (isString(node)) { 126 | push(`${node}`); 127 | } else { 128 | genNode(node, context); 129 | } 130 | // node 和 node 之间需要加上 逗号(,) 131 | // 但是最后一个不需要 "div", [props], [children] 132 | if (i < nodes.length - 1) { 133 | push(", "); 134 | } 135 | } 136 | } 137 | 138 | function genNullableArgs(args) { 139 | // 把末尾为null 的都删除掉 140 | // vue3源码中,后面可能会包含 patchFlag、dynamicProps 等编译优化的信息 141 | // 而这些信息有可能是不存在的,所以在这边的时候需要删除掉 142 | let i = args.length; 143 | // 这里 i-- 用的还是特别的巧妙的 144 | // 当为0 的时候自然就退出循环了 145 | while (i--) { 146 | if (args[i] != null) break; 147 | } 148 | 149 | // 把为 falsy 的值都替换成 "null" 150 | return args.slice(0, i + 1).map((arg) => arg || "null"); 151 | } 152 | 153 | function genExpression(node: any, context: any) { 154 | context.push(node.content, node); 155 | } 156 | 157 | function genInterpolation(node: any, context: any) { 158 | const { push, helper } = context; 159 | push(`${helper(TO_DISPLAY_STRING)}(`); 160 | genNode(node.content, context); 161 | push(")"); 162 | } 163 | 164 | function genModulePreamble(ast, context) { 165 | // preamble 就是 import 语句 166 | const { push, newline, runtimeModuleName } = context; 167 | 168 | if (ast.helpers.length) { 169 | // 比如 ast.helpers 里面有个 [toDisplayString] 170 | // 那么生成之后就是 import { toDisplayString as _toDisplayString } from "vue" 171 | const code = `import {${ast.helpers 172 | .map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`) 173 | .join(", ")} } from ${JSON.stringify(runtimeModuleName)}`; 174 | 175 | push(code); 176 | } 177 | 178 | newline(); 179 | push(`export `); 180 | } 181 | 182 | function createCodegenContext( 183 | ast: any, 184 | { runtimeModuleName = "vue", runtimeGlobalName = "Vue", mode = "function" } 185 | ): any { 186 | const context = { 187 | code: "", 188 | mode, 189 | runtimeModuleName, 190 | runtimeGlobalName, 191 | helper(key) { 192 | return `_${helperNameMap[key]}`; 193 | }, 194 | push(code) { 195 | context.code += code; 196 | }, 197 | newline() { 198 | // 换新行 199 | // TODO 需要额外处理缩进 200 | context.code += "\n"; 201 | }, 202 | }; 203 | 204 | return context; 205 | } 206 | -------------------------------------------------------------------------------- /packages/compiler-core/src/compile.ts: -------------------------------------------------------------------------------- 1 | import { generate } from "./codegen"; 2 | import { baseParse } from "./parse"; 3 | import { transform } from "./transform"; 4 | import { transformExpression } from "./transforms/transformExpression"; 5 | import { transformElement } from "./transforms/transformElement"; 6 | import { transformText } from "./transforms/transformText"; 7 | 8 | export function baseCompile(template, options) { 9 | // 1. 先把 template 也就是字符串 parse 成 ast 10 | const ast = baseParse(template); 11 | // 2. 给 ast 加点料(- -#) 12 | transform( 13 | ast, 14 | Object.assign(options, { 15 | nodeTransforms: [transformElement, transformText, transformExpression], 16 | }) 17 | ); 18 | 19 | // 3. 生成 render 函数代码 20 | return generate(ast); 21 | } 22 | -------------------------------------------------------------------------------- /packages/compiler-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { baseCompile } from "./compile"; 2 | -------------------------------------------------------------------------------- /packages/compiler-core/src/parse.ts: -------------------------------------------------------------------------------- 1 | import { ElementTypes, NodeTypes } from "./ast"; 2 | 3 | const enum TagType { 4 | Start, 5 | End, 6 | } 7 | 8 | export function baseParse(content: string) { 9 | const context = createParserContext(content); 10 | return createRoot(parseChildren(context, [])); 11 | } 12 | 13 | function createParserContext(content) { 14 | console.log("创建 paserContext"); 15 | return { 16 | source: content, 17 | }; 18 | } 19 | 20 | function parseChildren(context, ancestors) { 21 | console.log("开始解析 children"); 22 | const nodes: any = []; 23 | 24 | while (!isEnd(context, ancestors)) { 25 | let node; 26 | const s = context.source; 27 | 28 | if (startsWith(s, "{{")) { 29 | // 看看如果是 {{ 开头的话,那么就是一个插值, 那么去解析他 30 | node = parseInterpolation(context); 31 | } else if (s[0] === "<") { 32 | if (s[1] === "/") { 33 | // 这里属于 edge case 可以不用关心 34 | // 处理结束标签 35 | if (/[a-z]/i.test(s[2])) { 36 | // 匹配 37 | // 需要改变 context.source 的值 -> 也就是需要移动光标 38 | parseTag(context, TagType.End); 39 | // 结束标签就以为这都已经处理完了,所以就可以跳出本次循环了 40 | continue; 41 | } 42 | } else if (/[a-z]/i.test(s[1])) { 43 | node = parseElement(context, ancestors); 44 | } 45 | } 46 | 47 | if (!node) { 48 | node = parseText(context); 49 | } 50 | 51 | nodes.push(node); 52 | } 53 | 54 | return nodes; 55 | } 56 | 57 | function isEnd(context: any, ancestors) { 58 | // 检测标签的节点 59 | // 如果是结束标签的话,需要看看之前有没有开始标签,如果有的话,那么也应该结束 60 | // 这里的一个 edge case 是
61 | // 像这种情况下,其实就应该报错 62 | const s = context.source; 63 | if (context.source.startsWith("= 0; --i) { 67 | if (startsWithEndTagOpen(s, ancestors[i].tag)) { 68 | return true; 69 | } 70 | } 71 | } 72 | 73 | // 看看 context.source 还有没有值 74 | return !context.source; 75 | } 76 | 77 | function parseElement(context, ancestors) { 78 | // 应该如何解析 tag 呢 79 | //
80 | // 先解析开始 tag 81 | const element = parseTag(context, TagType.Start); 82 | 83 | ancestors.push(element); 84 | const children = parseChildren(context, ancestors); 85 | ancestors.pop(); 86 | 87 | // 解析 end tag 是为了检测语法是不是正确的 88 | // 检测是不是和 start tag 一致 89 | if (startsWithEndTagOpen(context.source, element.tag)) { 90 | parseTag(context, TagType.End); 91 | } else { 92 | throw new Error(`缺失结束标签:${element.tag}`); 93 | } 94 | 95 | element.children = children; 96 | 97 | return element; 98 | } 99 | 100 | function startsWithEndTagOpen(source: string, tag: string) { 101 | // 1. 头部 是不是以 的话,那么就把字符都收集起来 ->div 111 | // 正则 112 | const match: any = /^<\/?([a-z][^\r\n\t\f />]*)/i.exec(context.source); 113 | const tag = match[1]; 114 | 115 | // 移动光标 116 | //
120 | advanceBy(context, 1); 121 | 122 | if (type === TagType.End) return; 123 | 124 | let tagType = ElementTypes.ELEMENT; 125 | 126 | return { 127 | type: NodeTypes.ELEMENT, 128 | tag, 129 | tagType, 130 | }; 131 | } 132 | 133 | function parseInterpolation(context: any) { 134 | // 1. 先获取到结束的index 135 | // 2. 通过 closeIndex - startIndex 获取到内容的长度 contextLength 136 | // 3. 通过 slice 截取内容 137 | 138 | // }} 是插值的关闭 139 | // 优化点是从 {{ 后面搜索即可 140 | const openDelimiter = "{{"; 141 | const closeDelimiter = "}}"; 142 | 143 | const closeIndex = context.source.indexOf( 144 | closeDelimiter, 145 | openDelimiter.length 146 | ); 147 | 148 | // TODO closeIndex -1 需要报错的 149 | 150 | // 让代码前进2个长度,可以把 {{ 干掉 151 | advanceBy(context, 2); 152 | 153 | const rawContentLength = closeIndex - openDelimiter.length; 154 | const rawContent = context.source.slice(0, rawContentLength); 155 | 156 | const preTrimContent = parseTextData(context, rawContent.length); 157 | const content = preTrimContent.trim(); 158 | 159 | // 最后在让代码前进2个长度,可以把 }} 干掉 160 | advanceBy(context, closeDelimiter.length); 161 | 162 | return { 163 | type: NodeTypes.INTERPOLATION, 164 | content: { 165 | type: NodeTypes.SIMPLE_EXPRESSION, 166 | content, 167 | }, 168 | }; 169 | } 170 | 171 | function parseText(context): any { 172 | console.log("解析 text", context); 173 | 174 | // endIndex 应该看看有没有对应的 < 175 | // 比如 hello
176 | // 像这种情况下 endIndex 就应该是在 o 这里 177 | // { 178 | const endTokens = ["<", "{{"]; 179 | let endIndex = context.source.length; 180 | 181 | for (let i = 0; i < endTokens.length; i++) { 182 | const index = context.source.indexOf(endTokens[i]); 183 | // endIndex > index 是需要要 endIndex 尽可能的小 184 | // 比如说: 185 | // hi, {{123}}
186 | // 那么这里就应该停到 {{ 这里,而不是停到
index) { 188 | endIndex = index; 189 | } 190 | } 191 | 192 | const content = parseTextData(context, endIndex); 193 | 194 | return { 195 | type: NodeTypes.TEXT, 196 | content, 197 | }; 198 | } 199 | 200 | function parseTextData(context: any, length: number): any { 201 | console.log("解析 textData"); 202 | // 1. 直接返回 context.source 203 | // 从 length 切的话,是为了可以获取到 text 的值(需要用一个范围来确定) 204 | const rawText = context.source.slice(0, length); 205 | // 2. 移动光标 206 | advanceBy(context, length); 207 | 208 | return rawText; 209 | } 210 | 211 | function advanceBy(context, numberOfCharacters) { 212 | console.log("推进代码", context, numberOfCharacters); 213 | context.source = context.source.slice(numberOfCharacters); 214 | } 215 | 216 | function createRoot(children) { 217 | return { 218 | type: NodeTypes.ROOT, 219 | children, 220 | helpers: [], 221 | }; 222 | } 223 | 224 | function startsWith(source: string, searchString: string): boolean { 225 | return source.startsWith(searchString); 226 | } 227 | -------------------------------------------------------------------------------- /packages/compiler-core/src/runtimeHelpers.ts: -------------------------------------------------------------------------------- 1 | export const TO_DISPLAY_STRING = Symbol(`toDisplayString`); 2 | export const CREATE_ELEMENT_VNODE = Symbol("createElementVNode"); 3 | 4 | export const helperNameMap = { 5 | [TO_DISPLAY_STRING]: "toDisplayString", 6 | [CREATE_ELEMENT_VNODE]: "createElementVNode" 7 | }; 8 | -------------------------------------------------------------------------------- /packages/compiler-core/src/transform.ts: -------------------------------------------------------------------------------- 1 | import { NodeTypes } from "./ast"; 2 | import { TO_DISPLAY_STRING } from "./runtimeHelpers"; 3 | 4 | export function transform(root, options = {}) { 5 | // 1. 创建 context 6 | 7 | const context = createTransformContext(root, options); 8 | 9 | // 2. 遍历 node 10 | traverseNode(root, context); 11 | 12 | createRootCodegen(root, context); 13 | 14 | root.helpers.push(...context.helpers.keys()); 15 | } 16 | 17 | function traverseNode(node: any, context) { 18 | const type: NodeTypes = node.type; 19 | 20 | // 遍历调用所有的 nodeTransforms 21 | // 把 node 给到 transform 22 | // 用户可以对 node 做处理 23 | const nodeTransforms = context.nodeTransforms; 24 | const exitFns: any = []; 25 | for (let i = 0; i < nodeTransforms.length; i++) { 26 | const transform = nodeTransforms[i]; 27 | 28 | const onExit = transform(node, context); 29 | if (onExit) { 30 | exitFns.push(onExit); 31 | } 32 | } 33 | 34 | switch (type) { 35 | case NodeTypes.INTERPOLATION: 36 | // 插值的点,在于后续生成 render 代码的时候是获取变量的值 37 | context.helper(TO_DISPLAY_STRING); 38 | break; 39 | 40 | case NodeTypes.ROOT: 41 | case NodeTypes.ELEMENT: 42 | 43 | traverseChildren(node, context); 44 | break; 45 | 46 | default: 47 | break; 48 | } 49 | 50 | 51 | 52 | let i = exitFns.length; 53 | // i-- 这个很巧妙 54 | // 使用 while 是要比 for 快 (可以使用 https://jsbench.me/ 来测试一下) 55 | while (i--) { 56 | exitFns[i](); 57 | } 58 | } 59 | 60 | function traverseChildren(parent: any, context: any) { 61 | // node.children 62 | parent.children.forEach((node) => { 63 | // TODO 需要设置 context 的值 64 | traverseNode(node, context); 65 | }); 66 | } 67 | 68 | function createTransformContext(root, options): any { 69 | const context = { 70 | root, 71 | nodeTransforms: options.nodeTransforms || [], 72 | helpers: new Map(), 73 | helper(name) { 74 | // 这里会收集调用的次数 75 | // 收集次数是为了给删除做处理的, (当只有 count 为0 的时候才需要真的删除掉) 76 | // helpers 数据会在后续生成代码的时候用到 77 | const count = context.helpers.get(name) || 0; 78 | context.helpers.set(name, count + 1); 79 | }, 80 | }; 81 | 82 | return context; 83 | } 84 | 85 | function createRootCodegen(root: any, context: any) { 86 | const { children } = root; 87 | 88 | // 只支持有一个根节点 89 | // 并且还是一个 single text node 90 | const child = children[0]; 91 | 92 | // 如果是 element 类型的话 , 那么我们需要把它的 codegenNode 赋值给 root 93 | // root 其实是个空的什么数据都没有的节点 94 | // 所以这里需要额外的处理 codegenNode 95 | // codegenNode 的目的是专门为了 codegen 准备的 为的就是和 ast 的 node 分离开 96 | if (child.type === NodeTypes.ELEMENT && child.codegenNode) { 97 | const codegenNode = child.codegenNode; 98 | root.codegenNode = codegenNode; 99 | } else { 100 | root.codegenNode = child; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /packages/compiler-core/src/transforms/transformElement.ts: -------------------------------------------------------------------------------- 1 | import { createVNodeCall, NodeTypes } from "../ast"; 2 | 3 | export function transformElement(node, context) { 4 | if (node.type === NodeTypes.ELEMENT) { 5 | return () => { 6 | // 没有实现 block 所以这里直接创建 element 7 | 8 | // TODO 9 | // 需要把之前的 props 和 children 等一系列的数据都处理 10 | const vnodeTag = `'${node.tag}'`; 11 | // TODO props 暂时不支持 12 | const vnodeProps = null; 13 | let vnodeChildren = null; 14 | if (node.children.length > 0) { 15 | if (node.children.length === 1) { 16 | // 只有一个孩子节点 ,那么当生成 render 函数的时候就不用 [] 包裹 17 | const child = node.children[0]; 18 | vnodeChildren = child; 19 | } 20 | } 21 | 22 | // 创建一个新的 node 用于 codegen 的时候使用 23 | node.codegenNode = createVNodeCall( 24 | context, 25 | vnodeTag, 26 | vnodeProps, 27 | vnodeChildren 28 | ); 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/compiler-core/src/transforms/transformExpression.ts: -------------------------------------------------------------------------------- 1 | import { NodeTypes } from "../ast"; 2 | 3 | export function transformExpression(node) { 4 | if (node.type === NodeTypes.INTERPOLATION) { 5 | node.content = processExpression(node.content); 6 | } 7 | } 8 | 9 | function processExpression(node) { 10 | node.content = `_ctx.${node.content}`; 11 | 12 | return node 13 | } 14 | -------------------------------------------------------------------------------- /packages/compiler-core/src/transforms/transformText.ts: -------------------------------------------------------------------------------- 1 | import { NodeTypes } from "../ast"; 2 | import { isText } from "../utils"; 3 | 4 | export function transformText(node, context) { 5 | if (node.type === NodeTypes.ELEMENT) { 6 | // 在 exit 的时期执行 7 | // 下面的逻辑会改变 ast 树 8 | // 有些逻辑是需要在改变之前做处理的 9 | return () => { 10 | // hi,{{msg}} 11 | // 上面的模块会生成2个节点,一个是 text 一个是 interpolation 的话 12 | // 生成的 render 函数应该为 "hi," + _toDisplayString(_ctx.msg) 13 | // 这里面就会涉及到添加一个 “+” 操作符 14 | // 那这里的逻辑就是处理它 15 | 16 | // 检测下一个节点是不是 text 类型,如果是的话, 那么会创建一个 COMPOUND 类型 17 | // COMPOUND 类型把 2个 text || interpolation 包裹(相当于是父级容器) 18 | 19 | const children = node.children; 20 | let currentContainer; 21 | 22 | for (let i = 0; i < children.length; i++) { 23 | const child = children[i]; 24 | 25 | if (isText(child)) { 26 | // 看看下一个节点是不是 text 类 27 | for (let j = i + 1; j < children.length; j++) { 28 | const next = children[j]; 29 | if (isText(next)) { 30 | // currentContainer 的目的是把相邻的节点都放到一个 容器内 31 | if (!currentContainer) { 32 | currentContainer = children[i] = { 33 | type: NodeTypes.COMPOUND_EXPRESSION, 34 | loc: child.loc, 35 | children: [child], 36 | }; 37 | } 38 | 39 | currentContainer.children.push(` + `, next); 40 | // 把当前的节点放到容器内, 然后删除掉j 41 | children.splice(j, 1); 42 | // 因为把 j 删除了,所以这里就少了一个元素,那么 j 需要 -- 43 | j--; 44 | } else { 45 | currentContainer = undefined; 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/compiler-core/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { NodeTypes } from "./ast"; 2 | 3 | export function isText(node) { 4 | return node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT; 5 | } 6 | -------------------------------------------------------------------------------- /packages/reactivity/__tests__/computed.spec.ts: -------------------------------------------------------------------------------- 1 | import { computed } from "../src/computed"; 2 | import { reactive } from "../src/reactive"; 3 | 4 | describe("computed", () => { 5 | it("happy path", () => { 6 | const value = reactive({ 7 | foo: 1, 8 | }); 9 | 10 | const getter = computed(() => { 11 | return value.foo; 12 | }); 13 | 14 | value.foo = 2; 15 | expect(getter.value).toBe(2); 16 | }); 17 | 18 | it("should compute lazily", () => { 19 | const value = reactive({ 20 | foo: 1, 21 | }); 22 | const getter = jest.fn(() => { 23 | return value.foo; 24 | }); 25 | const cValue = computed(getter); 26 | 27 | // lazy 28 | expect(getter).not.toHaveBeenCalled(); 29 | 30 | expect(cValue.value).toBe(1); 31 | expect(getter).toHaveBeenCalledTimes(1); 32 | 33 | // should not compute again 34 | cValue.value; 35 | expect(getter).toHaveBeenCalledTimes(1); 36 | 37 | // should not compute until needed 38 | value.foo = 2; 39 | expect(getter).toHaveBeenCalledTimes(1); 40 | 41 | // now it should compute 42 | expect(cValue.value).toBe(2); 43 | expect(getter).toHaveBeenCalledTimes(2); 44 | 45 | // should not compute again 46 | cValue.value; 47 | expect(getter).toHaveBeenCalledTimes(2); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /packages/reactivity/__tests__/dep.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | describe('Dep', () => { 3 | 4 | 5 | it('new Set ', () => { 6 | // const set = new Set([1]) 7 | 8 | // console.log(set) 9 | 10 | 11 | }); 12 | 13 | 14 | }); -------------------------------------------------------------------------------- /packages/reactivity/__tests__/effect.spec.ts: -------------------------------------------------------------------------------- 1 | import { reactive } from "../src/reactive"; 2 | import { effect, stop } from "../src/effect"; 3 | 4 | describe("effect", () => { 5 | it("should run the passed function once (wrapped by a effect)", () => { 6 | const fnSpy = jest.fn(() => {}); 7 | effect(fnSpy); 8 | expect(fnSpy).toHaveBeenCalledTimes(1); 9 | }); 10 | 11 | it("should observe basic properties", () => { 12 | let dummy; 13 | const counter = reactive({ num: 0 }); 14 | effect(() => (dummy = counter.num)); 15 | 16 | expect(dummy).toBe(0); 17 | counter.num = 7; 18 | expect(dummy).toBe(7); 19 | }); 20 | 21 | it("should observe multiple properties", () => { 22 | let dummy; 23 | const counter = reactive({ num1: 0, num2: 0 }); 24 | effect(() => (dummy = counter.num1 + counter.num1 + counter.num2)); 25 | 26 | expect(dummy).toBe(0); 27 | counter.num1 = counter.num2 = 7; 28 | expect(dummy).toBe(21); 29 | }); 30 | it("should handle multiple effects", () => { 31 | let dummy1, dummy2; 32 | const counter = reactive({ num: 0 }); 33 | effect(() => (dummy1 = counter.num)); 34 | effect(() => (dummy2 = counter.num)); 35 | 36 | expect(dummy1).toBe(0); 37 | expect(dummy2).toBe(0); 38 | counter.num++; 39 | expect(dummy1).toBe(1); 40 | expect(dummy2).toBe(1); 41 | }); 42 | 43 | it("should observe nested properties", () => { 44 | let dummy; 45 | const counter = reactive({ nested: { num: 0 } }); 46 | effect(() => (dummy = counter.nested.num)); 47 | 48 | expect(dummy).toBe(0); 49 | counter.nested.num = 8; 50 | expect(dummy).toBe(8); 51 | }); 52 | 53 | it("should observe function call chains", () => { 54 | let dummy; 55 | const counter = reactive({ num: 0 }); 56 | effect(() => (dummy = getNum())); 57 | 58 | function getNum() { 59 | return counter.num; 60 | } 61 | 62 | expect(dummy).toBe(0); 63 | counter.num = 2; 64 | expect(dummy).toBe(2); 65 | }); 66 | it("scheduler", () => { 67 | let dummy; 68 | let run: any; 69 | const scheduler = jest.fn(() => { 70 | run = runner; 71 | }); 72 | const obj = reactive({ foo: 1 }); 73 | const runner = effect( 74 | () => { 75 | dummy = obj.foo; 76 | }, 77 | { scheduler } 78 | ); 79 | expect(scheduler).not.toHaveBeenCalled(); 80 | expect(dummy).toBe(1); 81 | // should be called on first trigger 82 | obj.foo++; 83 | expect(scheduler).toHaveBeenCalledTimes(1); 84 | // // should not run yet 85 | expect(dummy).toBe(1); 86 | // // manually run 87 | run(); 88 | // // should have run 89 | expect(dummy).toBe(2); 90 | }); 91 | 92 | it("stop", () => { 93 | let dummy; 94 | const obj = reactive({ prop: 1 }); 95 | const runner = effect(() => { 96 | dummy = obj.prop; 97 | }); 98 | obj.prop = 2; 99 | expect(dummy).toBe(2); 100 | stop(runner); 101 | // obj.prop = 3 102 | obj.prop++; 103 | expect(dummy).toBe(2); 104 | 105 | // stopped effect should still be manually callable 106 | runner(); 107 | expect(dummy).toBe(3); 108 | }); 109 | 110 | it("events: onStop", () => { 111 | const onStop = jest.fn(); 112 | const runner = effect(() => {}, { 113 | onStop, 114 | }); 115 | 116 | stop(runner); 117 | expect(onStop).toHaveBeenCalled(); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /packages/reactivity/__tests__/reactive.spec.ts: -------------------------------------------------------------------------------- 1 | import { reactive, isReactive, toRaw, reactiveMap } from "../src/reactive"; 2 | describe("reactive", () => { 3 | test("Object", () => { 4 | const original = { foo: 1 }; 5 | const observed = reactive(original); 6 | expect(observed).not.toBe(original); 7 | expect(isReactive(observed)).toBe(true); 8 | expect(isReactive(original)).toBe(false); 9 | // get 10 | expect(observed.foo).toBe(1); 11 | // // has 12 | expect("foo" in observed).toBe(true); 13 | // // ownKeys 14 | expect(Object.keys(observed)).toEqual(["foo"]); 15 | }); 16 | 17 | test("nested reactives", () => { 18 | const original = { 19 | nested: { 20 | foo: 1, 21 | }, 22 | array: [{ bar: 2 }], 23 | }; 24 | const observed = reactive(original); 25 | expect(isReactive(observed.nested)).toBe(true); 26 | expect(isReactive(observed.array)).toBe(true); 27 | expect(isReactive(observed.array[0])).toBe(true); 28 | }); 29 | 30 | test("toRaw", () => { 31 | const original = { foo: 1 }; 32 | const observed = reactive(original); 33 | expect(toRaw(observed)).toBe(original); 34 | expect(toRaw(original)).toBe(original); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/reactivity/__tests__/readonly.spec.ts: -------------------------------------------------------------------------------- 1 | import { isProxy, isReactive, isReadonly, readonly } from "../src/reactive"; 2 | 3 | describe("readonly", () => { 4 | it("should make nested values readonly", () => { 5 | const original = { foo: 1, bar: { baz: 2 } }; 6 | const wrapped = readonly(original); 7 | expect(wrapped).not.toBe(original); 8 | expect(isProxy(wrapped)).toBe(true); 9 | expect(isReactive(wrapped)).toBe(false); 10 | expect(isReadonly(wrapped)).toBe(true); 11 | expect(isReactive(original)).toBe(false); 12 | expect(isReadonly(original)).toBe(false); 13 | expect(isReactive(wrapped.bar)).toBe(false); 14 | expect(isReadonly(wrapped.bar)).toBe(true); 15 | expect(isReactive(original.bar)).toBe(false); 16 | expect(isReadonly(original.bar)).toBe(false); 17 | // get 18 | expect(wrapped.foo).toBe(1); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/reactivity/__tests__/ref.spec.ts: -------------------------------------------------------------------------------- 1 | import { effect } from "../src/effect"; 2 | import { reactive } from "../src/reactive"; 3 | import { isRef, ref, unRef,proxyRefs } from "../src/ref"; 4 | describe("ref", () => { 5 | it("should be reactive", () => { 6 | const a = ref(1); 7 | let dummy; 8 | let calls = 0; 9 | effect(() => { 10 | calls++; 11 | dummy = a.value; 12 | }); 13 | expect(calls).toBe(1); 14 | expect(dummy).toBe(1); 15 | a.value = 2; 16 | expect(calls).toBe(2); 17 | expect(dummy).toBe(2); 18 | // same value should not trigger 19 | a.value = 2; 20 | expect(calls).toBe(2); 21 | expect(dummy).toBe(2); 22 | }); 23 | 24 | it("should make nested properties reactive", () => { 25 | const a = ref({ 26 | count: 1, 27 | }); 28 | let dummy; 29 | effect(() => { 30 | dummy = a.value.count; 31 | }); 32 | expect(dummy).toBe(1); 33 | a.value.count = 2; 34 | expect(dummy).toBe(2); 35 | }); 36 | 37 | it("proxyRefs", () => { 38 | const user = { 39 | age: ref(10), 40 | name: "xiaohong", 41 | }; 42 | const proxyUser = proxyRefs(user); 43 | expect(user.age.value).toBe(10); 44 | expect(proxyUser.age).toBe(10); 45 | expect(proxyUser.name).toBe("xiaohong"); 46 | 47 | (proxyUser as any).age = 20; 48 | expect(proxyUser.age).toBe(20); 49 | expect(user.age.value).toBe(20); 50 | 51 | proxyUser.age = ref(10); 52 | expect(proxyUser.age).toBe(10); 53 | expect(user.age.value).toBe(10); 54 | }); 55 | 56 | it("isRef", () => { 57 | const a = ref(1); 58 | const user = reactive({ 59 | age: 1, 60 | }); 61 | expect(isRef(a)).toBe(true); 62 | expect(isRef(1)).toBe(false); 63 | expect(isRef(user)).toBe(false); 64 | }); 65 | 66 | it("unRef", () => { 67 | const a = ref(1); 68 | expect(unRef(a)).toBe(1); 69 | expect(unRef(1)).toBe(1); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /packages/reactivity/__tests__/shallowReadonly.spec.ts: -------------------------------------------------------------------------------- 1 | import { isReactive, isReadonly, readonly, shallowReadonly } from "../src/reactive"; 2 | 3 | describe("shallowReadonly", () => { 4 | test("should not make non-reactive properties reactive", () => { 5 | const props = shallowReadonly({ n: { foo: 1 } }); 6 | expect(isReactive(props.n)).toBe(false); 7 | }); 8 | test("should differentiate from normal readonly calls", async () => { 9 | const original = { foo: {} }; 10 | const shallowProxy = shallowReadonly(original); 11 | const reactiveProxy = readonly(original); 12 | expect(shallowProxy).not.toBe(reactiveProxy); 13 | expect(isReadonly(shallowProxy.foo)).toBe(false); 14 | expect(isReadonly(reactiveProxy.foo)).toBe(true); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/reactivity/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mini-vue/reactivity", 3 | "version": "1.0.0", 4 | "description": "@mini-vue/reactivity", 5 | "scripts": { 6 | "test": "jest" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@mini-vue/shared": "workspace:^1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/reactivity/src/baseHandlers.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveEffect, track, trigger } from "./effect"; 2 | import { 3 | reactive, 4 | ReactiveFlags, 5 | reactiveMap, 6 | readonly, 7 | readonlyMap, 8 | shallowReadonlyMap, 9 | } from "./reactive"; 10 | import { isObject } from "@mini-vue/shared"; 11 | 12 | const get = createGetter(); 13 | const set = createSetter(); 14 | const readonlyGet = createGetter(true); 15 | const shallowReadonlyGet = createGetter(true, true); 16 | 17 | function createGetter(isReadonly = false, shallow = false) { 18 | return function get(target, key, receiver) { 19 | const isExistInReactiveMap = () => 20 | key === ReactiveFlags.RAW && receiver === reactiveMap.get(target); 21 | 22 | const isExistInReadonlyMap = () => 23 | key === ReactiveFlags.RAW && receiver === readonlyMap.get(target); 24 | 25 | const isExistInShallowReadonlyMap = () => 26 | key === ReactiveFlags.RAW && receiver === shallowReadonlyMap.get(target); 27 | 28 | if (key === ReactiveFlags.IS_REACTIVE) { 29 | return !isReadonly; 30 | } else if (key === ReactiveFlags.IS_READONLY) { 31 | return isReadonly; 32 | } else if ( 33 | isExistInReactiveMap() || 34 | isExistInReadonlyMap() || 35 | isExistInShallowReadonlyMap() 36 | ) { 37 | return target; 38 | } 39 | 40 | const res = Reflect.get(target, key, receiver); 41 | 42 | // 问题:为什么是 readonly 的时候不做依赖收集呢 43 | // readonly 的话,是不可以被 set 的, 那不可以被 set 就意味着不会触发 trigger 44 | // 所有就没有收集依赖的必要了 45 | 46 | if (!isReadonly) { 47 | // 在触发 get 的时候进行依赖收集 48 | track(target, "get", key); 49 | } 50 | 51 | if (shallow) { 52 | return res; 53 | } 54 | 55 | if (isObject(res)) { 56 | // 把内部所有的是 object 的值都用 reactive 包裹,变成响应式对象 57 | // 如果说这个 res 值是一个对象的话,那么我们需要把获取到的 res 也转换成 reactive 58 | // res 等于 target[key] 59 | return isReadonly ? readonly(res) : reactive(res); 60 | } 61 | 62 | return res; 63 | }; 64 | } 65 | 66 | function createSetter() { 67 | return function set(target, key, value, receiver) { 68 | const result = Reflect.set(target, key, value, receiver); 69 | 70 | // 在触发 set 的时候进行触发依赖 71 | trigger(target, "set", key); 72 | 73 | return result; 74 | }; 75 | } 76 | 77 | export const readonlyHandlers = { 78 | get: readonlyGet, 79 | set(target, key) { 80 | // readonly 的响应式对象不可以修改值 81 | console.warn( 82 | `Set operation on key "${String(key)}" failed: target is readonly.`, 83 | target 84 | ); 85 | return true; 86 | }, 87 | }; 88 | 89 | export const mutableHandlers = { 90 | get, 91 | set, 92 | }; 93 | 94 | export const shallowReadonlyHandlers = { 95 | get: shallowReadonlyGet, 96 | set(target, key) { 97 | // readonly 的响应式对象不可以修改值 98 | console.warn( 99 | `Set operation on key "${String(key)}" failed: target is readonly.`, 100 | target 101 | ); 102 | return true; 103 | }, 104 | }; 105 | -------------------------------------------------------------------------------- /packages/reactivity/src/computed.ts: -------------------------------------------------------------------------------- 1 | import { createDep } from "./dep"; 2 | import { ReactiveEffect } from "./effect"; 3 | import { trackRefValue, triggerRefValue } from "./ref"; 4 | 5 | export class ComputedRefImpl { 6 | public dep: any; 7 | public effect: ReactiveEffect; 8 | 9 | private _dirty: boolean; 10 | private _value 11 | 12 | constructor(getter) { 13 | this._dirty = true; 14 | this.dep = createDep(); 15 | this.effect = new ReactiveEffect(getter, () => { 16 | // scheduler 17 | // 只要触发了这个函数说明响应式对象的值发生改变了 18 | // 那么就解锁,后续在调用 get 的时候就会重新执行,所以会得到最新的值 19 | if (this._dirty) return; 20 | 21 | this._dirty = true; 22 | triggerRefValue(this); 23 | }); 24 | } 25 | 26 | get value() { 27 | // 收集依赖 28 | trackRefValue(this); 29 | // 锁上,只可以调用一次 30 | // 当数据改变的时候才会解锁 31 | // 这里就是缓存实现的核心 32 | // 解锁是在 scheduler 里面做的 33 | if (this._dirty) { 34 | this._dirty = false; 35 | // 这里执行 run 的话,就是执行用户传入的 fn 36 | this._value = this.effect.run(); 37 | } 38 | 39 | return this._value; 40 | } 41 | } 42 | 43 | export function computed(getter) { 44 | return new ComputedRefImpl(getter); 45 | } 46 | -------------------------------------------------------------------------------- /packages/reactivity/src/dep.ts: -------------------------------------------------------------------------------- 1 | // 用于存储所有的 effect 对象 2 | export function createDep(effects?) { 3 | const dep = new Set(effects); 4 | return dep; 5 | } 6 | -------------------------------------------------------------------------------- /packages/reactivity/src/effect.ts: -------------------------------------------------------------------------------- 1 | import { createDep } from "./dep"; 2 | import { extend } from "@mini-vue/shared"; 3 | 4 | let activeEffect = void 0; 5 | let shouldTrack = false; 6 | const targetMap = new WeakMap(); 7 | 8 | // 用于依赖收集 9 | export class ReactiveEffect { 10 | active = true; 11 | deps = []; 12 | public onStop?: () => void; 13 | constructor(public fn, public scheduler?) { 14 | console.log("创建 ReactiveEffect 对象"); 15 | } 16 | 17 | run() { 18 | console.log("run"); 19 | // 运行 run 的时候,可以控制 要不要执行后续收集依赖的一步 20 | // 目前来看的话,只要执行了 fn 那么就默认执行了收集依赖 21 | // 这里就需要控制了 22 | 23 | // 是不是收集依赖的变量 24 | 25 | // 执行 fn 但是不收集依赖 26 | if (!this.active) { 27 | return this.fn(); 28 | } 29 | 30 | // 执行 fn 收集依赖 31 | // 可以开始收集依赖了 32 | shouldTrack = true; 33 | 34 | // 执行的时候给全局的 activeEffect 赋值 35 | // 利用全局属性来获取当前的 effect 36 | activeEffect = this as any; 37 | // 执行用户传入的 fn 38 | console.log("执行用户传入的 fn"); 39 | const result = this.fn(); 40 | // 重置 41 | shouldTrack = false; 42 | activeEffect = undefined; 43 | 44 | return result; 45 | } 46 | 47 | stop() { 48 | if (this.active) { 49 | // 如果第一次执行 stop 后 active 就 false 了 50 | // 这是为了防止重复的调用,执行 stop 逻辑 51 | cleanupEffect(this); 52 | if (this.onStop) { 53 | this.onStop(); 54 | } 55 | this.active = false; 56 | } 57 | } 58 | } 59 | 60 | function cleanupEffect(effect) { 61 | // 找到所有依赖这个 effect 的响应式对象 62 | // 从这些响应式对象里面把 effect 给删除掉 63 | effect.deps.forEach((dep) => { 64 | dep.delete(effect); 65 | }); 66 | 67 | effect.deps.length = 0; 68 | } 69 | 70 | export function effect(fn, options = {}) { 71 | const _effect = new ReactiveEffect(fn); 72 | 73 | // 把用户传过来的值合并到 _effect 对象上去 74 | // 缺点就是不是显式的,看代码的时候并不知道有什么值 75 | extend(_effect, options); 76 | _effect.run(); 77 | 78 | // 把 _effect.run 这个方法返回 79 | // 让用户可以自行选择调用的时机(调用 fn) 80 | const runner: any = _effect.run.bind(_effect); 81 | runner.effect = _effect; 82 | return runner; 83 | } 84 | 85 | export function stop(runner) { 86 | runner.effect.stop(); 87 | } 88 | 89 | export function track(target, type, key) { 90 | if (!isTracking()) { 91 | return; 92 | } 93 | console.log(`触发 track -> target: ${target} type:${type} key:${key}`); 94 | // 1. 先基于 target 找到对应的 dep 95 | // 如果是第一次的话,那么就需要初始化 96 | let depsMap = targetMap.get(target); 97 | if (!depsMap) { 98 | // 初始化 depsMap 的逻辑 99 | depsMap = new Map(); 100 | targetMap.set(target, depsMap); 101 | } 102 | 103 | let dep = depsMap.get(key); 104 | 105 | if (!dep) { 106 | dep = createDep(); 107 | 108 | depsMap.set(key, dep); 109 | } 110 | 111 | trackEffects(dep); 112 | } 113 | 114 | export function trackEffects(dep) { 115 | // 用 dep 来存放所有的 effect 116 | 117 | // TODO 118 | // 这里是一个优化点 119 | // 先看看这个依赖是不是已经收集了, 120 | // 已经收集的话,那么就不需要在收集一次了 121 | // 可能会影响 code path change 的情况 122 | // 需要每次都 cleanupEffect 123 | // shouldTrack = !dep.has(activeEffect!); 124 | if (!dep.has(activeEffect)) { 125 | dep.add(activeEffect); 126 | (activeEffect as any).deps.push(dep); 127 | } 128 | } 129 | 130 | export function trigger(target, type, key) { 131 | // 1. 先收集所有的 dep 放到 deps 里面, 132 | // 后面会统一处理 133 | let deps: Array = []; 134 | // dep 135 | 136 | const depsMap = targetMap.get(target); 137 | 138 | if (!depsMap) return; 139 | 140 | // 暂时只实现了 GET 类型 141 | // get 类型只需要取出来就可以 142 | const dep = depsMap.get(key); 143 | 144 | // 最后收集到 deps 内 145 | deps.push(dep); 146 | 147 | const effects: Array = []; 148 | deps.forEach((dep) => { 149 | // 这里解构 dep 得到的是 dep 内部存储的 effect 150 | effects.push(...dep); 151 | }); 152 | // 这里的目的是只有一个 dep ,这个dep 里面包含所有的 effect 153 | // 这里的目前应该是为了 triggerEffects 这个函数的复用 154 | triggerEffects(createDep(effects)); 155 | } 156 | 157 | export function isTracking() { 158 | return shouldTrack && activeEffect !== undefined; 159 | } 160 | 161 | export function triggerEffects(dep) { 162 | // 执行收集到的所有的 effect 的 run 方法 163 | for (const effect of dep) { 164 | if (effect.scheduler) { 165 | // scheduler 可以让用户自己选择调用的时机 166 | // 这样就可以灵活的控制调用了 167 | // 在 runtime-core 中,就是使用了 scheduler 实现了在 next ticker 中调用的逻辑 168 | effect.scheduler(); 169 | } else { 170 | effect.run(); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /packages/reactivity/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | reactive, 3 | readonly, 4 | shallowReadonly, 5 | isReadonly, 6 | isReactive, 7 | isProxy, 8 | } from "./reactive"; 9 | 10 | export { ref, proxyRefs, unRef, isRef } from "./ref"; 11 | 12 | export { effect, stop, ReactiveEffect } from "./effect"; 13 | 14 | export { computed } from "./computed"; 15 | -------------------------------------------------------------------------------- /packages/reactivity/src/reactive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | mutableHandlers, 3 | readonlyHandlers, 4 | shallowReadonlyHandlers, 5 | } from "./baseHandlers"; 6 | 7 | export const reactiveMap = new WeakMap(); 8 | export const readonlyMap = new WeakMap(); 9 | export const shallowReadonlyMap = new WeakMap(); 10 | 11 | export const enum ReactiveFlags { 12 | IS_REACTIVE = "__v_isReactive", 13 | IS_READONLY = "__v_isReadonly", 14 | RAW = "__v_raw", 15 | } 16 | 17 | export function reactive(target) { 18 | return createReactiveObject(target, reactiveMap, mutableHandlers); 19 | } 20 | 21 | export function readonly(target) { 22 | return createReactiveObject(target, readonlyMap, readonlyHandlers); 23 | } 24 | 25 | export function shallowReadonly(target) { 26 | return createReactiveObject( 27 | target, 28 | shallowReadonlyMap, 29 | shallowReadonlyHandlers 30 | ); 31 | } 32 | 33 | export function isProxy(value) { 34 | return isReactive(value) || isReadonly(value); 35 | } 36 | 37 | export function isReadonly(value) { 38 | return !!value[ReactiveFlags.IS_READONLY]; 39 | } 40 | 41 | export function isReactive(value) { 42 | // 如果 value 是 proxy 的话 43 | // 会触发 get 操作,而在 createGetter 里面会判断 44 | // 如果 value 是普通对象的话 45 | // 那么会返回 undefined ,那么就需要转换成布尔值 46 | return !!value[ReactiveFlags.IS_REACTIVE]; 47 | } 48 | 49 | export function toRaw(value) { 50 | // 如果 value 是 proxy 的话 ,那么直接返回就可以了 51 | // 因为会触发 createGetter 内的逻辑 52 | // 如果 value 是普通对象的话, 53 | // 我们就应该返回普通对象 54 | // 只要不是 proxy ,只要是得到了 undefined 的话,那么就一定是普通对象 55 | // TODO 这里和源码里面实现的不一样,不确定后面会不会有问题 56 | if (!value[ReactiveFlags.RAW]) { 57 | return value; 58 | } 59 | 60 | return value[ReactiveFlags.RAW]; 61 | } 62 | 63 | function createReactiveObject(target, proxyMap, baseHandlers) { 64 | // 核心就是 proxy 65 | // 目的是可以侦听到用户 get 或者 set 的动作 66 | 67 | // 如果命中的话就直接返回就好了 68 | // 使用缓存做的优化点 69 | const existingProxy = proxyMap.get(target); 70 | if (existingProxy) { 71 | return existingProxy; 72 | } 73 | 74 | const proxy = new Proxy(target, baseHandlers); 75 | 76 | // 把创建好的 proxy 给存起来, 77 | proxyMap.set(target, proxy); 78 | return proxy; 79 | } 80 | -------------------------------------------------------------------------------- /packages/reactivity/src/ref.ts: -------------------------------------------------------------------------------- 1 | import { trackEffects, triggerEffects, isTracking } from "./effect"; 2 | import { createDep } from "./dep"; 3 | import { isObject, hasChanged } from "@mini-vue/shared"; 4 | import { reactive } from "./reactive"; 5 | 6 | export class RefImpl { 7 | private _rawValue: any; 8 | private _value: any; 9 | public dep; 10 | public __v_isRef = true; 11 | 12 | constructor(value) { 13 | this._rawValue = value; 14 | // 看看value 是不是一个对象,如果是一个对象的话 15 | // 那么需要用 reactive 包裹一下 16 | this._value = convert(value); 17 | this.dep = createDep(); 18 | } 19 | 20 | get value() { 21 | // 收集依赖 22 | trackRefValue(this); 23 | return this._value; 24 | } 25 | 26 | set value(newValue) { 27 | // 当新的值不等于老的值的话, 28 | // 那么才需要触发依赖 29 | if (hasChanged(newValue, this._rawValue)) { 30 | // 更新值 31 | this._value = convert(newValue); 32 | this._rawValue = newValue; 33 | // 触发依赖 34 | triggerRefValue(this); 35 | } 36 | } 37 | } 38 | 39 | export function ref(value) { 40 | return createRef(value); 41 | } 42 | 43 | function convert(value) { 44 | return isObject(value) ? reactive(value) : value; 45 | } 46 | 47 | function createRef(value) { 48 | const refImpl = new RefImpl(value); 49 | 50 | return refImpl; 51 | } 52 | 53 | export function triggerRefValue(ref) { 54 | triggerEffects(ref.dep); 55 | } 56 | 57 | export function trackRefValue(ref) { 58 | if (isTracking()) { 59 | trackEffects(ref.dep); 60 | } 61 | } 62 | 63 | // 这个函数的目的是 64 | // 帮助解构 ref 65 | // 比如在 template 中使用 ref 的时候,直接使用就可以了 66 | // 例如: const count = ref(0) -> 在 template 中使用的话 可以直接 count 67 | // 解决方案就是通过 proxy 来对 ref 做处理 68 | 69 | const shallowUnwrapHandlers = { 70 | get(target, key, receiver) { 71 | // 如果里面是一个 ref 类型的话,那么就返回 .value 72 | // 如果不是的话,那么直接返回value 就可以了 73 | return unRef(Reflect.get(target, key, receiver)); 74 | }, 75 | set(target, key, value, receiver) { 76 | const oldValue = target[key]; 77 | if (isRef(oldValue) && !isRef(value)) { 78 | return (target[key].value = value); 79 | } else { 80 | return Reflect.set(target, key, value, receiver); 81 | } 82 | }, 83 | }; 84 | 85 | // 这里没有处理 objectWithRefs 是 reactive 类型的时候 86 | // TODO reactive 里面如果有 ref 类型的 key 的话, 那么也是不需要调用 ref.value 的 87 | // (but 这个逻辑在 reactive 里面没有实现) 88 | export function proxyRefs(objectWithRefs) { 89 | return new Proxy(objectWithRefs, shallowUnwrapHandlers); 90 | } 91 | 92 | // 把 ref 里面的值拿到 93 | export function unRef(ref) { 94 | return isRef(ref) ? ref.value : ref; 95 | } 96 | 97 | export function isRef(value) { 98 | return !!value.__v_isRef; 99 | } 100 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/apiWatch.spec.ts: -------------------------------------------------------------------------------- 1 | import { reactive } from "@mini-vue/reactivity"; 2 | import { watchEffect } from "../src/apiWatch"; 3 | import { nextTick } from "../src/scheduler"; 4 | 5 | describe("api: watch", () => { 6 | it("effect", async () => { 7 | const state = reactive({ count: 0 }); 8 | let dummy; 9 | watchEffect(() => { 10 | dummy = state.count; 11 | }); 12 | expect(dummy).toBe(0); 13 | 14 | state.count++; 15 | await nextTick(); 16 | expect(dummy).toBe(1); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/componentEmits.spec.ts: -------------------------------------------------------------------------------- 1 | import { nodeOps, render, h } from "@mini-vue/runtime-test"; 2 | 3 | describe("component: emits", () => { 4 | test("trigger handlers", () => { 5 | const Foo = { 6 | render() { 7 | return h("foo"); 8 | }, 9 | setup(props, { emit }) { 10 | // the `emit` function is bound on component instances 11 | emit("foo"); 12 | emit("bar"); 13 | }, 14 | }; 15 | 16 | const onfoo = jest.fn(); 17 | const onBar = jest.fn(); 18 | const Comp = { 19 | render() { 20 | return h(Foo, { onfoo, onBar }); 21 | }, 22 | }; 23 | render(h(Comp), nodeOps.createElement("div")); 24 | 25 | expect(onfoo).not.toHaveBeenCalled(); 26 | // only capitalized or special chars are considered event listeners 27 | expect(onBar).toHaveBeenCalled(); 28 | }); 29 | 30 | test("trigger camelCase handler", () => { 31 | const Foo = { 32 | render() { 33 | return h("foo"); 34 | }, 35 | setup(props, { emit }) { 36 | emit("test-event"); 37 | }, 38 | }; 39 | 40 | const fooSpy = jest.fn(); 41 | const Comp = { 42 | render() { 43 | return h(Foo, { onTestEvent: fooSpy }); 44 | }, 45 | }; 46 | render(h(Comp), nodeOps.createElement("div")); 47 | 48 | expect(fooSpy).toHaveBeenCalledTimes(1); 49 | }); 50 | 51 | test("trigger kebab-case handler", () => { 52 | const Foo = { 53 | render() { 54 | return h("foo"); 55 | }, 56 | setup(props, { emit }) { 57 | emit("test-event"); 58 | }, 59 | }; 60 | 61 | const fooSpy = jest.fn(); 62 | 63 | const Comp = { 64 | render() { 65 | return h(Foo, { "onTest-event": fooSpy }); 66 | }, 67 | }; 68 | render(h(Comp), nodeOps.createElement("div")); 69 | 70 | expect(fooSpy).toHaveBeenCalledTimes(1); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/rendererComponent.spec.ts: -------------------------------------------------------------------------------- 1 | import { h } from "@mini-vue/runtime-dom"; 2 | import { nodeOps, render, serializeInner } from "@mini-vue/runtime-test"; 3 | 4 | describe("renderer: component", () => { 5 | it("should create an Component ", () => { 6 | const Comp = { 7 | render: () => { 8 | return h("div"); 9 | }, 10 | }; 11 | const root = nodeOps.createElement("div"); 12 | render(h(Comp), root); 13 | expect(serializeInner(root)).toBe(`
`); 14 | }); 15 | 16 | it("should create an Component with direct text children", () => { 17 | const Comp = { 18 | render: () => { 19 | return h("div", null, "test"); 20 | }, 21 | }; 22 | const root = nodeOps.createElement("div"); 23 | render(h(Comp), root); 24 | expect(serializeInner(root)).toBe(`
test
`); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/rendererElement.spec.ts: -------------------------------------------------------------------------------- 1 | import { h } from "@mini-vue/runtime-core"; 2 | import { nodeOps, render, serializeInner as inner } from "@mini-vue/runtime-test"; 3 | 4 | describe("renderer: element", () => { 5 | let root; 6 | 7 | beforeEach(() => { 8 | root = nodeOps.createElement("div"); 9 | }); 10 | 11 | it("should create an element", () => { 12 | render(h("div"), root); 13 | expect(inner(root)).toBe("
"); 14 | }); 15 | 16 | it('should create an element with props', () => { 17 | render(h('div', { id: 'foo', class: 'bar' },[]), root) 18 | expect(inner(root)).toBe('
') 19 | }) 20 | it('should create an element with direct text children and props', () => { 21 | render(h('div', { id: 'foo' }, "bar"), root) 22 | expect(inner(root)).toBe('
bar
') 23 | }) 24 | }); 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/runtime-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mini-vue/runtime-core", 3 | "version": "1.0.0", 4 | "description": "@mini-vue/runtime-core", 5 | "scripts": { 6 | "test": "jest" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@mini-vue/reactivity": "workspace:^1.0.0", 13 | "@mini-vue/shared": "workspace:^1.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/runtime-core/src/.pnpm-debug.log: -------------------------------------------------------------------------------- 1 | { 2 | "0 debug pnpm:scope": { 3 | "selected": 1, 4 | "workspacePrefix": "/Users/cxr/projects/mini-vue/code/mini-vue" 5 | }, 6 | "1 error pnpm": { 7 | "errno": 1, 8 | "code": "ELIFECYCLE", 9 | "pkgid": "@mini-vue/runtime-core@1.0.0", 10 | "stage": "test", 11 | "script": "jest \"runtime-core\"", 12 | "pkgname": "@mini-vue/runtime-core", 13 | "err": { 14 | "name": "pnpm", 15 | "message": "@mini-vue/runtime-core@1.0.0 test: `jest \"runtime-core\"`\nExit status 1", 16 | "code": "ELIFECYCLE", 17 | "stack": "pnpm: @mini-vue/runtime-core@1.0.0 test: `jest \"runtime-core\"`\nExit status 1\n at EventEmitter. (/opt/homebrew/Cellar/pnpm/6.32.4/libexec/lib/node_modules/pnpm/dist/pnpm.cjs:105736:20)\n at EventEmitter.emit (node:events:527:28)\n at ChildProcess. (/opt/homebrew/Cellar/pnpm/6.32.4/libexec/lib/node_modules/pnpm/dist/pnpm.cjs:92297:18)\n at ChildProcess.emit (node:events:527:28)\n at maybeClose (node:internal/child_process:1092:16)\n at Process.ChildProcess._handle.onexit (node:internal/child_process:302:5)" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /packages/runtime-core/src/apiInject.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance } from "./component"; 2 | 3 | export function provide(key, value) { 4 | const currentInstance = getCurrentInstance(); 5 | 6 | if (currentInstance) { 7 | let { provides } = currentInstance; 8 | 9 | const parentProvides = currentInstance.parent?.provides; 10 | 11 | // 这里要解决一个问题 12 | // 当父级 key 和 爷爷级别的 key 重复的时候,对于子组件来讲,需要取最近的父级别组件的值 13 | // 那这里的解决方案就是利用原型链来解决 14 | // provides 初始化的时候是在 createComponent 时处理的,当时是直接把 parent.provides 赋值给组件的 provides 的 15 | // 所以,如果说这里发现 provides 和 parentProvides 相等的话,那么就说明是第一次做 provide(对于当前组件来讲) 16 | // 我们就可以把 parent.provides 作为 currentInstance.provides 的原型重新赋值 17 | // 至于为什么不在 createComponent 的时候做这个处理,可能的好处是在这里初始化的话,是有个懒执行的效果(优化点,只有需要的时候在初始化) 18 | if (parentProvides === provides) { 19 | provides = currentInstance.provides = Object.create(parentProvides); 20 | } 21 | 22 | provides[key] = value; 23 | } 24 | } 25 | 26 | export function inject(key, defaultValue) { 27 | const currentInstance = getCurrentInstance(); 28 | 29 | if (currentInstance) { 30 | const provides = currentInstance.parent?.provides; 31 | 32 | if (key in provides) { 33 | return provides[key]; 34 | } else if (defaultValue) { 35 | if (typeof defaultValue === "function") { 36 | return defaultValue(); 37 | } 38 | return defaultValue; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/runtime-core/src/apiWatch.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveEffect } from "@mini-vue/reactivity"; 2 | import { queuePreFlushCb } from "./scheduler"; 3 | 4 | // Simple effect. 5 | export function watchEffect(effect) { 6 | doWatch(effect); 7 | } 8 | 9 | function doWatch(source) { 10 | // 把 job 添加到 pre flush 里面 11 | // 也就是在视图更新完成之前进行渲染(待确认?) 12 | // 当逻辑执行到这里的时候 就已经触发了 watchEffect 13 | const job = () => { 14 | effect.run(); 15 | }; 16 | 17 | // 当触发 trigger 的时候会调用 scheduler 18 | // 这里用 scheduler 的目的就是在更新的时候 19 | // 让回调可以在 render 前执行 变成一个异步的行为(这里也可以通过 flush 来改变) 20 | const scheduler = () => queuePreFlushCb(job); 21 | 22 | // 这里是在执行 effect.run 的时候就会调用的 23 | const getter = () => { 24 | source(); 25 | }; 26 | 27 | const effect = new ReactiveEffect(getter, scheduler); 28 | 29 | // 这里执行的就是 getter 30 | effect.run(); 31 | } 32 | -------------------------------------------------------------------------------- /packages/runtime-core/src/component.ts: -------------------------------------------------------------------------------- 1 | import { initProps } from "./componentProps"; 2 | import { initSlots } from "./componentSlots"; 3 | import { emit } from "./componentEmits"; 4 | import { PublicInstanceProxyHandlers } from "./componentPublicInstance"; 5 | import { proxyRefs, shallowReadonly } from "@mini-vue/reactivity"; 6 | export function createComponentInstance(vnode, parent) { 7 | const instance = { 8 | type: vnode.type, 9 | vnode, 10 | next: null, // 需要更新的 vnode,用于更新 component 类型的组件 11 | props: {}, 12 | parent, 13 | provides: parent ? parent.provides : {}, // 获取 parent 的 provides 作为当前组件的初始化值 这样就可以继承 parent.provides 的属性了 14 | proxy: null, 15 | isMounted: false, 16 | attrs: {}, // 存放 attrs 的数据 17 | slots: {}, // 存放插槽的数据 18 | ctx: {}, // context 对象 19 | setupState: {}, // 存储 setup 的返回值 20 | emit: () => {}, 21 | }; 22 | 23 | // 在 prod 坏境下的 ctx 只是下面简单的结构 24 | // 在 dev 环境下会更复杂 25 | instance.ctx = { 26 | _: instance, 27 | }; 28 | 29 | // 赋值 emit 30 | // 这里使用 bind 把 instance 进行绑定 31 | // 后面用户使用的时候只需要给 event 和参数即可 32 | instance.emit = emit.bind(null, instance) as any; 33 | 34 | return instance; 35 | } 36 | 37 | export function setupComponent(instance) { 38 | // 1. 处理 props 39 | // 取出存在 vnode 里面的 props 40 | const { props, children } = instance.vnode; 41 | initProps(instance, props); 42 | // 2. 处理 slots 43 | initSlots(instance, children); 44 | 45 | // 源码里面有两种类型的 component 46 | // 一种是基于 options 创建的 47 | // 还有一种是 function 的 48 | // 这里处理的是 options 创建的 49 | // 叫做 stateful 类型 50 | setupStatefulComponent(instance); 51 | } 52 | 53 | function setupStatefulComponent(instance) { 54 | // todo 55 | // 1. 先创建代理 proxy 56 | console.log("创建 proxy"); 57 | 58 | // proxy 对象其实是代理了 instance.ctx 对象 59 | // 我们在使用的时候需要使用 instance.proxy 对象 60 | // 因为 instance.ctx 在 prod 和 dev 坏境下是不同的 61 | instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers); 62 | // 用户声明的对象就是 instance.type 63 | // const Component = {setup(),render()} .... 64 | const Component = instance.type; 65 | // 2. 调用 setup 66 | 67 | // 调用 setup 的时候传入 props 68 | const { setup } = Component; 69 | if (setup) { 70 | // 设置当前 currentInstance 的值 71 | // 必须要在调用 setup 之前 72 | setCurrentInstance(instance); 73 | 74 | const setupContext = createSetupContext(instance); 75 | // 真实的处理场景里面应该是只在 dev 环境才会把 props 设置为只读的 76 | const setupResult = 77 | setup && setup(shallowReadonly(instance.props), setupContext); 78 | 79 | setCurrentInstance(null); 80 | 81 | // 3. 处理 setupResult 82 | handleSetupResult(instance, setupResult); 83 | } else { 84 | finishComponentSetup(instance); 85 | } 86 | } 87 | 88 | function createSetupContext(instance) { 89 | console.log("初始化 setup context"); 90 | return { 91 | attrs: instance.attrs, 92 | slots: instance.slots, 93 | emit: instance.emit, 94 | expose: () => {}, // TODO 实现 expose 函数逻辑 95 | }; 96 | } 97 | 98 | function handleSetupResult(instance, setupResult) { 99 | // setup 返回值不一样的话,会有不同的处理 100 | // 1. 看看 setupResult 是个什么 101 | if (typeof setupResult === "function") { 102 | // 如果返回的是 function 的话,那么绑定到 render 上 103 | // 认为是 render 逻辑 104 | // setup(){ return ()=>(h("div")) } 105 | instance.render = setupResult; 106 | } else if (typeof setupResult === "object") { 107 | // 返回的是一个对象的话 108 | // 先存到 setupState 上 109 | // 先使用 @vue/reactivity 里面的 proxyRefs 110 | // 后面我们自己构建 111 | // proxyRefs 的作用就是把 setupResult 对象做一层代理 112 | // 方便用户直接访问 ref 类型的值 113 | // 比如 setupResult 里面有个 count 是个 ref 类型的对象,用户使用的时候就可以直接使用 count 了,而不需要在 count.value 114 | // 这里也就是官网里面说到的自动结构 Ref 类型 115 | instance.setupState = proxyRefs(setupResult); 116 | } 117 | 118 | finishComponentSetup(instance); 119 | } 120 | 121 | function finishComponentSetup(instance) { 122 | // 给 instance 设置 render 123 | 124 | // 先取到用户设置的 component options 125 | const Component = instance.type; 126 | 127 | if (!instance.render) { 128 | // 如果 compile 有值 并且当组件没有 render 函数,那么就需要把 template 编译成 render 函数 129 | if (compile && !Component.render) { 130 | if (Component.template) { 131 | // 这里就是 runtime 模块和 compile 模块结合点 132 | const template = Component.template; 133 | Component.render = compile(template); 134 | } 135 | } 136 | 137 | instance.render = Component.render; 138 | } 139 | 140 | // applyOptions() 141 | } 142 | 143 | function applyOptions() { 144 | // 兼容 vue2.x 145 | // todo 146 | // options api 147 | } 148 | 149 | let currentInstance = {}; 150 | // 这个接口暴露给用户,用户可以在 setup 中获取组件实例 instance 151 | export function getCurrentInstance(): any { 152 | return currentInstance; 153 | } 154 | 155 | export function setCurrentInstance(instance) { 156 | currentInstance = instance; 157 | } 158 | 159 | let compile; 160 | export function registerRuntimeCompiler(_compile) { 161 | compile = _compile; 162 | } 163 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentEmits.ts: -------------------------------------------------------------------------------- 1 | import { camelize, hyphenate, toHandlerKey } from "@mini-vue/shared"; 2 | export function emit(instance, event: string, ...rawArgs) { 3 | // 1. emit 是基于 props 里面的 onXXX 的函数来进行匹配的 4 | // 所以我们先从 props 中看看是否有对应的 event handler 5 | const props = instance.props; 6 | // ex: event -> click 那么这里取的就是 onClick 7 | // 让事情变的复杂一点如果是烤肉串命名的话,需要转换成 change-page -> changePage 8 | // 需要得到事件名称 9 | let handler = props[toHandlerKey(camelize(event))]; 10 | 11 | // 如果上面没有匹配的话 那么在检测一下 event 是不是 kebab-case 类型 12 | if (!handler) { 13 | handler = props[(toHandlerKey(hyphenate(event)))] 14 | } 15 | 16 | 17 | if (handler) { 18 | handler(...rawArgs); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentProps.ts: -------------------------------------------------------------------------------- 1 | export function initProps(instance, rawProps) { 2 | console.log("initProps"); 3 | 4 | // TODO 5 | // 应该还有 attrs 的概念 6 | // attrs 7 | // 如果组件声明了 props 的话,那么才可以进入 props 属性内 8 | // 不然的话是需要存储在 attrs 内 9 | // 这里暂时直接赋值给 instance.props 即可 10 | instance.props = rawProps; 11 | } 12 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentPublicInstance.ts: -------------------------------------------------------------------------------- 1 | import { hasOwn } from "@mini-vue/shared"; 2 | 3 | const publicPropertiesMap = { 4 | // 当用户调用 instance.proxy.$emit 时就会触发这个函数 5 | // i 就是 instance 的缩写 也就是组件实例对象 6 | $el: (i) => i.vnode.el, 7 | $emit: (i) => i.emit, 8 | $slots: (i) => i.slots, 9 | $props: (i) => i.props, 10 | }; 11 | 12 | // todo 需要让用户可以直接在 render 函数内直接使用 this 来触发 proxy 13 | export const PublicInstanceProxyHandlers = { 14 | get({ _: instance }, key) { 15 | // 用户访问 proxy[key] 16 | // 这里就匹配一下看看是否有对应的 function 17 | // 有的话就直接调用这个 function 18 | const { setupState, props } = instance; 19 | console.log(`触发 proxy hook , key -> : ${key}`); 20 | 21 | if (key[0] !== "$") { 22 | // 说明不是访问 public api 23 | // 先检测访问的 key 是否存在于 setupState 中, 是的话直接返回 24 | if (hasOwn(setupState, key)) { 25 | return setupState[key]; 26 | } else if (hasOwn(props, key)) { 27 | // 看看 key 是不是在 props 中 28 | // 代理是可以访问到 props 中的 key 的 29 | return props[key]; 30 | } 31 | } 32 | 33 | const publicGetter = publicPropertiesMap[key]; 34 | 35 | if (publicGetter) { 36 | return publicGetter(instance); 37 | } 38 | }, 39 | 40 | set({ _: instance }, key, value) { 41 | const { setupState } = instance; 42 | 43 | if (hasOwn(setupState, key)) { 44 | // 有的话 那么就直接赋值 45 | setupState[key] = value; 46 | } 47 | 48 | return true 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentRenderUtils.ts: -------------------------------------------------------------------------------- 1 | export function shouldUpdateComponent(prevVNode, nextVNode) { 2 | const { props: prevProps } = prevVNode; 3 | const { props: nextProps } = nextVNode; 4 | // const emits = component!.emitsOptions; 5 | 6 | // 这里主要是检测组件的 props 7 | // 核心:只要是 props 发生改变了,那么这个 component 就需要更新 8 | 9 | // 1. props 没有变化,那么不需要更新 10 | if (prevProps === nextProps) { 11 | return false; 12 | } 13 | // 如果之前没有 props,那么就需要看看现在有没有 props 了 14 | // 所以这里基于 nextProps 的值来决定是否更新 15 | if (!prevProps) { 16 | return !!nextProps; 17 | } 18 | // 之前有值,现在没值,那么肯定需要更新 19 | if (!nextProps) { 20 | return true; 21 | } 22 | 23 | // 以上都是比较明显的可以知道 props 是否是变化的 24 | // 在 hasPropsChanged 会做更细致的对比检测 25 | return hasPropsChanged(prevProps, nextProps); 26 | } 27 | 28 | function hasPropsChanged(prevProps, nextProps): boolean { 29 | // 依次对比每一个 props.key 30 | 31 | // 提前对比一下 length ,length 不一致肯定是需要更新的 32 | const nextKeys = Object.keys(nextProps); 33 | if (nextKeys.length !== Object.keys(prevProps).length) { 34 | return true; 35 | } 36 | 37 | // 只要现在的 prop 和之前的 prop 不一样那么就需要更新 38 | for (let i = 0; i < nextKeys.length; i++) { 39 | const key = nextKeys[i]; 40 | if (nextProps[key] !== prevProps[key]) { 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentSlots.ts: -------------------------------------------------------------------------------- 1 | import { ShapeFlags } from "@mini-vue/shared"; 2 | export function initSlots(instance, children) { 3 | const { vnode } = instance; 4 | 5 | console.log("初始化 slots"); 6 | 7 | if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) { 8 | normalizeObjectSlots(children, (instance.slots = {})); 9 | } 10 | } 11 | 12 | const normalizeSlotValue = (value) => { 13 | // 把 function 返回的值转换成 array ,这样 slot 就可以支持多个元素了 14 | return Array.isArray(value) ? value : [value]; 15 | }; 16 | 17 | const normalizeObjectSlots = (rawSlots, slots) => { 18 | for (const key in rawSlots) { 19 | const value = rawSlots[key]; 20 | if (typeof value === "function") { 21 | // 把这个函数给到slots 对象上存起来 22 | // 后续在 renderSlots 中调用 23 | // TODO 这里没有对 value 做 normalize, 24 | // 默认 slots 返回的就是一个 vnode 对象 25 | slots[key] = (props) => normalizeSlotValue(value(props)); 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /packages/runtime-core/src/createApp.ts: -------------------------------------------------------------------------------- 1 | import { createVNode } from "./vnode"; 2 | 3 | export function createAppAPI(render) { 4 | return function createApp(rootComponent) { 5 | const app = { 6 | _component: rootComponent, 7 | mount(rootContainer) { 8 | console.log("基于根组件创建 vnode"); 9 | const vnode = createVNode(rootComponent); 10 | console.log("调用 render,基于 vnode 进行开箱"); 11 | render(vnode, rootContainer); 12 | }, 13 | }; 14 | 15 | return app; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/runtime-core/src/h.ts: -------------------------------------------------------------------------------- 1 | import { createVNode } from "./vnode"; 2 | export const h = (type: any , props: any = null, children: string | Array = []) => { 3 | return createVNode(type, props, children); 4 | }; 5 | -------------------------------------------------------------------------------- /packages/runtime-core/src/helpers/renderSlot.ts: -------------------------------------------------------------------------------- 1 | import { createVNode, Fragment } from "../vnode"; 2 | 3 | /** 4 | * Compiler runtime helper for rendering `` 5 | * 用来 render slot 的 6 | * 之前是把 slot 的数据都存在 instance.slots 内(可以看 componentSlot.ts), 7 | * 这里就是取数据然后渲染出来的点 8 | * 这个是由 compiler 模块直接渲染出来的 -可以参看这个 demo https://vue-next-template-explorer.netlify.app/#%7B%22src%22%3A%22%3Cdiv%3E%5Cn%20%20%3Cslot%3E%3C%2Fslot%3E%5Cn%3C%2Fdiv%3E%22%2C%22ssr%22%3Afalse%2C%22options%22%3A%7B%22mode%22%3A%22module%22%2C%22prefixIdentifiers%22%3Afalse%2C%22optimizeImports%22%3Afalse%2C%22hoistStatic%22%3Afalse%2C%22cacheHandlers%22%3Afalse%2C%22scopeId%22%3Anull%2C%22inline%22%3Afalse%2C%22ssrCssVars%22%3A%22%7B%20color%20%7D%22%2C%22bindingMetadata%22%3A%7B%22TestComponent%22%3A%22setup-const%22%2C%22setupRef%22%3A%22setup-ref%22%2C%22setupConst%22%3A%22setup-const%22%2C%22setupLet%22%3A%22setup-let%22%2C%22setupMaybeRef%22%3A%22setup-maybe-ref%22%2C%22setupProp%22%3A%22props%22%2C%22vMySetupDir%22%3A%22setup-const%22%7D%2C%22optimizeBindings%22%3Afalse%7D%7D 9 | * 其最终目的就是在 render 函数中调用 renderSlot 取 instance.slots 内的数据 10 | * TODO 这里应该是一个返回一个 block ,但是暂时还没有支持 block ,所以这个暂时只需要返回一个 vnode 即可 11 | * 因为 block 的本质就是返回一个 vnode 12 | * 13 | * @private 14 | */ 15 | export function renderSlot(slots, name: string, props = {}) { 16 | const slot = slots[name]; 17 | console.log(`渲染插槽 slot -> ${name}`); 18 | if (slot) { 19 | // 因为 slot 是一个返回 vnode 的函数,我们只需要把这个结果返回出去即可 20 | // slot 就是一个函数,所以就可以把当前组件的一些数据给传出去,这个就是作用域插槽 21 | // 参数就是 props 22 | const slotContent = slot(props); 23 | return createVNode(Fragment, {}, slotContent); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/runtime-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./h"; 2 | export * from "./createApp"; 3 | export { getCurrentInstance, registerRuntimeCompiler } from "./component"; 4 | export { inject, provide } from "./apiInject"; 5 | export { renderSlot } from "./helpers/renderSlot"; 6 | export { createTextVNode, createElementVNode } from "./vnode"; 7 | export { createRenderer } from "./renderer"; 8 | export { toDisplayString } from "@mini-vue/shared"; 9 | export { watchEffect } from "./apiWatch"; 10 | export { 11 | // core 12 | reactive, 13 | ref, 14 | readonly, 15 | // utilities 16 | unRef, 17 | proxyRefs, 18 | isReadonly, 19 | isReactive, 20 | isProxy, 21 | isRef, 22 | // advanced 23 | shallowReadonly, 24 | // effect 25 | effect, 26 | stop, 27 | computed, 28 | } from "@mini-vue/reactivity"; 29 | -------------------------------------------------------------------------------- /packages/runtime-core/src/renderer.ts: -------------------------------------------------------------------------------- 1 | import { ShapeFlags } from "@mini-vue/shared"; 2 | import { createComponentInstance } from "./component"; 3 | import { queueJob } from "./scheduler"; 4 | import { effect } from "@mini-vue/reactivity"; 5 | import { setupComponent } from "./component"; 6 | import { Fragment, normalizeVNode, Text } from "./vnode"; 7 | import { shouldUpdateComponent } from "./componentRenderUtils"; 8 | import { createAppAPI } from "./createApp"; 9 | 10 | export function createRenderer(options) { 11 | const { 12 | createElement: hostCreateElement, 13 | setElementText: hostSetElementText, 14 | patchProp: hostPatchProp, 15 | insert: hostInsert, 16 | remove: hostRemove, 17 | setText: hostSetText, 18 | createText: hostCreateText, 19 | } = options; 20 | 21 | const render = (vnode, container) => { 22 | console.log("调用 patch") 23 | patch(null, vnode, container); 24 | }; 25 | 26 | function patch( 27 | n1, 28 | n2, 29 | container = null, 30 | anchor = null, 31 | parentComponent = null 32 | ) { 33 | // 基于 n2 的类型来判断 34 | // 因为 n2 是新的 vnode 35 | const { type, shapeFlag } = n2; 36 | switch (type) { 37 | case Text: 38 | processText(n1, n2, container); 39 | break; 40 | // 其中还有几个类型比如: static fragment comment 41 | case Fragment: 42 | processFragment(n1, n2, container); 43 | break; 44 | default: 45 | // 这里就基于 shapeFlag 来处理 46 | if (shapeFlag & ShapeFlags.ELEMENT) { 47 | console.log("处理 element"); 48 | processElement(n1, n2, container, anchor, parentComponent); 49 | } else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) { 50 | console.log("处理 component"); 51 | processComponent(n1, n2, container, parentComponent); 52 | } 53 | } 54 | } 55 | 56 | function processFragment(n1: any, n2: any, container: any) { 57 | // 只需要渲染 children ,然后给添加到 container 内 58 | if (!n1) { 59 | // 初始化 Fragment 逻辑点 60 | console.log("初始化 Fragment 类型的节点"); 61 | mountChildren(n2.children, container); 62 | } 63 | } 64 | 65 | function processText(n1, n2, container) { 66 | console.log("处理 Text 节点"); 67 | if (n1 === null) { 68 | // n1 是 null 说明是 init 的阶段 69 | // 基于 createText 创建出 text 节点,然后使用 insert 添加到 el 内 70 | console.log("初始化 Text 类型的节点"); 71 | hostInsert((n2.el = hostCreateText(n2.children as string)), container); 72 | } else { 73 | // update 74 | // 先对比一下 updated 之后的内容是否和之前的不一样 75 | // 在不一样的时候才需要 update text 76 | // 这里抽离出来的接口是 setText 77 | // 注意,这里一定要记得把 n1.el 赋值给 n2.el, 不然后续是找不到值的 78 | const el = (n2.el = n1.el!); 79 | if (n2.children !== n1.children) { 80 | console.log("更新 Text 类型的节点"); 81 | hostSetText(el, n2.children as string); 82 | } 83 | } 84 | } 85 | 86 | function processElement(n1, n2, container, anchor, parentComponent) { 87 | if (!n1) { 88 | mountElement(n2, container, anchor); 89 | } else { 90 | // todo 91 | updateElement(n1, n2, container, anchor, parentComponent); 92 | } 93 | } 94 | 95 | function updateElement(n1, n2, container, anchor, parentComponent) { 96 | const oldProps = (n1 && n1.props) || {}; 97 | const newProps = n2.props || {}; 98 | // 应该更新 element 99 | console.log("应该更新 element"); 100 | console.log("旧的 vnode", n1); 101 | console.log("新的 vnode", n2); 102 | 103 | // 需要把 el 挂载到新的 vnode 104 | const el = (n2.el = n1.el); 105 | 106 | // 对比 props 107 | patchProps(el, oldProps, newProps); 108 | 109 | // 对比 children 110 | patchChildren(n1, n2, el, anchor, parentComponent); 111 | } 112 | 113 | function patchProps(el, oldProps, newProps) { 114 | // 对比 props 有以下几种情况 115 | // 1. oldProps 有,newProps 也有,但是 val 值变更了 116 | // 举个栗子 117 | // 之前: oldProps.id = 1 ,更新后:newProps.id = 2 118 | 119 | // key 存在 oldProps 里 也存在 newProps 内 120 | // 以 newProps 作为基准 121 | for (const key in newProps) { 122 | const prevProp = oldProps[key]; 123 | const nextProp = newProps[key]; 124 | if (prevProp !== nextProp) { 125 | // 对比属性 126 | // 需要交给 host 来更新 key 127 | hostPatchProp(el, key, prevProp, nextProp); 128 | } 129 | } 130 | 131 | // 2. oldProps 有,而 newProps 没有了 132 | // 之前: {id:1,tId:2} 更新后: {id:1} 133 | // 这种情况下我们就应该以 oldProps 作为基准,因为在 newProps 里面是没有的 tId 的 134 | // 还需要注意一点,如果这个 key 在 newProps 里面已经存在了,说明已经处理过了,就不要在处理了 135 | for (const key in oldProps) { 136 | const prevProp = oldProps[key]; 137 | const nextProp = null; 138 | if (!(key in newProps)) { 139 | // 这里是以 oldProps 为基准来遍历, 140 | // 而且得到的值是 newProps 内没有的 141 | // 所以交给 host 更新的时候,把新的值设置为 null 142 | hostPatchProp(el, key, prevProp, nextProp); 143 | } 144 | } 145 | } 146 | 147 | function patchChildren(n1, n2, container, anchor, parentComponent) { 148 | const { shapeFlag: prevShapeFlag, children: c1 } = n1; 149 | const { shapeFlag, children: c2 } = n2; 150 | 151 | // 如果 n2 的 children 是 text 类型的话 152 | // 就看看和之前的 n1 的 children 是不是一样的 153 | // 如果不一样的话直接重新设置一下 text 即可 154 | if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { 155 | if (c2 !== c1) { 156 | console.log("类型为 text_children, 当前需要更新"); 157 | hostSetElementText(container, c2 as string); 158 | } 159 | } else { 160 | // 如果之前是 array_children 161 | // 现在还是 array_children 的话 162 | // 那么我们就需要对比两个 children 啦 163 | if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) { 164 | if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { 165 | patchKeyedChildren(c1, c2, container, anchor, parentComponent); 166 | } 167 | } 168 | } 169 | } 170 | 171 | function patchKeyedChildren( 172 | c1: any[], 173 | c2: any[], 174 | container, 175 | parentAnchor, 176 | parentComponent 177 | ) { 178 | let i = 0; 179 | const l2 = c2.length; 180 | let e1 = c1.length - 1; 181 | let e2 = l2 - 1; 182 | 183 | const isSameVNodeType = (n1, n2) => { 184 | return n1.type === n2.type && n1.key === n2.key; 185 | }; 186 | 187 | while (i <= e1 && i <= e2) { 188 | const prevChild = c1[i]; 189 | const nextChild = c2[i]; 190 | 191 | if (!isSameVNodeType(prevChild, nextChild)) { 192 | console.log("两个 child 不相等(从左往右比对)"); 193 | console.log(`prevChild:${prevChild}`); 194 | console.log(`nextChild:${nextChild}`); 195 | break; 196 | } 197 | 198 | console.log("两个 child 相等,接下来对比这两个 child 节点(从左往右比对)"); 199 | patch(prevChild, nextChild, container, parentAnchor, parentComponent); 200 | i++; 201 | } 202 | 203 | while (i <= e1 && i <= e2) { 204 | // 从右向左取值 205 | const prevChild = c1[e1]; 206 | const nextChild = c2[e2]; 207 | 208 | if (!isSameVNodeType(prevChild, nextChild)) { 209 | console.log("两个 child 不相等(从右往左比对)"); 210 | console.log(`prevChild:${prevChild}`); 211 | console.log(`nextChild:${nextChild}`); 212 | break; 213 | } 214 | console.log("两个 child 相等,接下来对比这两个 child 节点(从右往左比对)"); 215 | patch(prevChild, nextChild, container, parentAnchor, parentComponent); 216 | e1--; 217 | e2--; 218 | } 219 | 220 | if (i > e1 && i <= e2) { 221 | // 如果是这种情况的话就说明 e2 也就是新节点的数量大于旧节点的数量 222 | // 也就是说新增了 vnode 223 | // 应该循环 c2 224 | // 锚点的计算:新的节点有可能需要添加到尾部,也可能添加到头部,所以需要指定添加的问题 225 | // 要添加的位置是当前的位置(e2 开始)+1 226 | // 因为对于往左侧添加的话,应该获取到 c2 的第一个元素 227 | // 所以我们需要从 e2 + 1 取到锚点的位置 228 | const nextPos = e2 + 1; 229 | const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor; 230 | while (i <= e2) { 231 | console.log(`需要新创建一个 vnode: ${c2[i].key}`); 232 | patch(null, c2[i], container, anchor, parentComponent); 233 | i++; 234 | } 235 | } else if (i > e2 && i <= e1) { 236 | // 这种情况的话说明新节点的数量是小于旧节点的数量的 237 | // 那么我们就需要把多余的 238 | while (i <= e1) { 239 | console.log(`需要删除当前的 vnode: ${c1[i].key}`); 240 | hostRemove(c1[i].el); 241 | i++; 242 | } 243 | } else { 244 | // 左右两边都比对完了,然后剩下的就是中间部位顺序变动的 245 | // 例如下面的情况 246 | // a,b,[c,d,e],f,g 247 | // a,b,[e,c,d],f,g 248 | 249 | let s1 = i; 250 | let s2 = i; 251 | const keyToNewIndexMap = new Map(); 252 | let moved = false; 253 | let maxNewIndexSoFar = 0; 254 | // 先把 key 和 newIndex 绑定好,方便后续基于 key 找到 newIndex 255 | // 时间复杂度是 O(1) 256 | for (let i = s2; i <= e2; i++) { 257 | const nextChild = c2[i]; 258 | keyToNewIndexMap.set(nextChild.key, i); 259 | } 260 | 261 | // 需要处理新节点的数量 262 | const toBePatched = e2 - s2 + 1; 263 | let patched = 0; 264 | // 初始化 从新的index映射为老的index 265 | // 创建数组的时候给定数组的长度,这个是性能最快的写法 266 | const newIndexToOldIndexMap = new Array(toBePatched); 267 | // 初始化为 0 , 后面处理的时候 如果发现是 0 的话,那么就说明新值在老的里面不存在 268 | for (let i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0; 269 | 270 | // 遍历老节点 271 | // 1. 需要找出老节点有,而新节点没有的 -> 需要把这个节点删除掉 272 | // 2. 新老节点都有的,—> 需要 patch 273 | for (i = s1; i <= e1; i++) { 274 | const prevChild = c1[i]; 275 | 276 | // 优化点 277 | // 如果老的节点大于新节点的数量的话,那么这里在处理老节点的时候就直接删除即可 278 | if (patched >= toBePatched) { 279 | hostRemove(prevChild.el); 280 | continue; 281 | } 282 | 283 | let newIndex; 284 | if (prevChild.key != null) { 285 | // 这里就可以通过key快速的查找了, 看看在新的里面这个节点存在不存在 286 | // 时间复杂度O(1) 287 | newIndex = keyToNewIndexMap.get(prevChild.key); 288 | } else { 289 | // 如果没key 的话,那么只能是遍历所有的新节点来确定当前节点存在不存在了 290 | // 时间复杂度O(n) 291 | for (let j = s2; j <= e2; j++) { 292 | if (isSameVNodeType(prevChild, c2[j])) { 293 | newIndex = j; 294 | break; 295 | } 296 | } 297 | } 298 | 299 | // 因为有可能 nextIndex 的值为0(0也是正常值) 300 | // 所以需要通过值是不是 undefined 或者 null 来判断 301 | if (newIndex === undefined) { 302 | // 当前节点的key 不存在于 newChildren 中,需要把当前节点给删除掉 303 | hostRemove(prevChild.el); 304 | } else { 305 | // 新老节点都存在 306 | console.log("新老节点都存在"); 307 | // 把新节点的索引和老的节点的索引建立映射关系 308 | // i + 1 是因为 i 有可能是0 (0 的话会被认为新节点在老的节点中不存在) 309 | newIndexToOldIndexMap[newIndex - s2] = i + 1; 310 | // 来确定中间的节点是不是需要移动 311 | // 新的 newIndex 如果一直是升序的话,那么就说明没有移动 312 | // 所以我们可以记录最后一个节点在新的里面的索引,然后看看是不是升序 313 | // 不是升序的话,我们就可以确定节点移动过了 314 | if (newIndex >= maxNewIndexSoFar) { 315 | maxNewIndexSoFar = newIndex; 316 | } else { 317 | moved = true; 318 | } 319 | 320 | patch(prevChild, c2[newIndex], container, null, parentComponent); 321 | patched++; 322 | } 323 | } 324 | 325 | // 利用最长递增子序列来优化移动逻辑 326 | // 因为元素是升序的话,那么这些元素就是不需要移动的 327 | // 而我们就可以通过最长递增子序列来获取到升序的列表 328 | // 在移动的时候我们去对比这个列表,如果对比上的话,就说明当前元素不需要移动 329 | // 通过 moved 来进行优化,如果没有移动过的话 那么就不需要执行算法 330 | // getSequence 返回的是 newIndexToOldIndexMap 的索引值 331 | // 所以后面我们可以直接遍历索引值来处理,也就是直接使用 toBePatched 即可 332 | const increasingNewIndexSequence = moved 333 | ? getSequence(newIndexToOldIndexMap) 334 | : []; 335 | let j = increasingNewIndexSequence.length - 1; 336 | 337 | // 遍历新节点 338 | // 1. 需要找出老节点没有,而新节点有的 -> 需要把这个节点创建 339 | // 2. 最后需要移动一下位置,比如 [c,d,e] -> [e,c,d] 340 | 341 | // 这里倒循环是因为在 insert 的时候,需要保证锚点是处理完的节点(也就是已经确定位置了) 342 | // 因为 insert 逻辑是使用的 insertBefore() 343 | for (let i = toBePatched - 1; i >= 0; i--) { 344 | // 确定当前要处理的节点索引 345 | const nextIndex = s2 + i; 346 | const nextChild = c2[nextIndex]; 347 | // 锚点等于当前节点索引+1 348 | // 也就是当前节点的后面一个节点(又因为是倒遍历,所以锚点是位置确定的节点) 349 | const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor; 350 | 351 | if (newIndexToOldIndexMap[i] === 0) { 352 | // 说明新节点在老的里面不存在 353 | // 需要创建 354 | patch(null, nextChild, container, anchor, parentComponent); 355 | } else if (moved) { 356 | // 需要移动 357 | // 1. j 已经没有了 说明剩下的都需要移动了 358 | // 2. 最长子序列里面的值和当前的值匹配不上, 说明当前元素需要移动 359 | if (j < 0 || increasingNewIndexSequence[j] !== i) { 360 | // 移动的话使用 insert 即可 361 | hostInsert(nextChild.el, container, anchor); 362 | } else { 363 | // 这里就是命中了 index 和 最长递增子序列的值 364 | // 所以可以移动指针了 365 | j--; 366 | } 367 | } 368 | } 369 | } 370 | } 371 | 372 | function mountElement(vnode, container, anchor) { 373 | const { shapeFlag, props } = vnode; 374 | // 1. 先创建 element 375 | // 基于可扩展的渲染 api 376 | const el = (vnode.el = hostCreateElement(vnode.type)); 377 | 378 | // 支持单子组件和多子组件的创建 379 | if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { 380 | // 举个栗子 381 | // render(){ 382 | // return h("div",{},"test") 383 | // } 384 | // 这里 children 就是 test ,只需要渲染一下就完事了 385 | console.log(`处理文本:${vnode.children}`); 386 | hostSetElementText(el, vnode.children); 387 | } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { 388 | // 举个栗子 389 | // render(){ 390 | // Hello 是个 component 391 | // return h("div",{},[h("p"),h(Hello)]) 392 | // } 393 | // 这里 children 就是个数组了,就需要依次调用 patch 递归来处理 394 | mountChildren(vnode.children, el); 395 | } 396 | 397 | // 处理 props 398 | if (props) { 399 | for (const key in props) { 400 | // todo 401 | // 需要过滤掉vue自身用的key 402 | // 比如生命周期相关的 key: beforeMount、mounted 403 | const nextVal = props[key]; 404 | hostPatchProp(el, key, null, nextVal); 405 | } 406 | } 407 | 408 | // todo 409 | // 触发 beforeMount() 钩子 410 | console.log("vnodeHook -> onVnodeBeforeMount"); 411 | console.log("DirectiveHook -> beforeMount"); 412 | console.log("transition -> beforeEnter"); 413 | 414 | // 插入 415 | hostInsert(el, container, anchor); 416 | 417 | // todo 418 | // 触发 mounted() 钩子 419 | console.log("vnodeHook -> onVnodeMounted"); 420 | console.log("DirectiveHook -> mounted"); 421 | console.log("transition -> enter"); 422 | } 423 | 424 | function mountChildren(children, container) { 425 | children.forEach((VNodeChild) => { 426 | // todo 427 | // 这里应该需要处理一下 vnodeChild 428 | // 因为有可能不是 vnode 类型 429 | console.log("mountChildren:", VNodeChild); 430 | patch(null, VNodeChild, container); 431 | }); 432 | } 433 | 434 | function processComponent(n1, n2, container, parentComponent) { 435 | // 如果 n1 没有值的话,那么就是 mount 436 | if (!n1) { 437 | // 初始化 component 438 | mountComponent(n2, container, parentComponent); 439 | } else { 440 | updateComponent(n1, n2, container); 441 | } 442 | } 443 | 444 | // 组件的更新 445 | function updateComponent(n1, n2, container) { 446 | console.log("更新组件", n1, n2); 447 | // 更新组件实例引用 448 | const instance = (n2.component = n1.component); 449 | // 先看看这个组件是否应该更新 450 | if (shouldUpdateComponent(n1, n2)) { 451 | console.log(`组件需要更新: ${instance}`); 452 | // 那么 next 就是新的 vnode 了(也就是 n2) 453 | instance.next = n2; 454 | // 这里的 update 是在 setupRenderEffect 里面初始化的,update 函数除了当内部的响应式对象发生改变的时候会调用 455 | // 还可以直接主动的调用(这是属于 effect 的特性) 456 | // 调用 update 再次更新调用 patch 逻辑 457 | // 在update 中调用的 next 就变成了 n2了 458 | // ps:可以详细的看看 update 中 next 的应用 459 | // TODO 需要在 update 中处理支持 next 的逻辑 460 | instance.update(); 461 | } else { 462 | console.log(`组件不需要更新: ${instance}`); 463 | // 不需要更新的话,那么只需要覆盖下面的属性即可 464 | n2.component = n1.component; 465 | n2.el = n1.el; 466 | instance.vnode = n2; 467 | } 468 | } 469 | 470 | function mountComponent(initialVNode, container, parentComponent) { 471 | // 1. 先创建一个 component instance 472 | const instance = (initialVNode.component = createComponentInstance( 473 | initialVNode, 474 | parentComponent 475 | )); 476 | console.log(`创建组件实例:${instance.type.name}`); 477 | // 2. 给 instance 加工加工 478 | setupComponent(instance); 479 | 480 | setupRenderEffect(instance, initialVNode, container); 481 | } 482 | 483 | function setupRenderEffect(instance, initialVNode, container) { 484 | // 调用 render 485 | // 应该传入 ctx 也就是 proxy 486 | // ctx 可以选择暴露给用户的 api 487 | // 源代码里面是调用的 renderComponentRoot 函数 488 | // 这里为了简化直接调用 render 489 | 490 | // obj.name = "111" 491 | // obj.name = "2222" 492 | // 从哪里做一些事 493 | // 收集数据改变之后要做的事 (函数) 494 | // 依赖收集 effect 函数 495 | // 触发依赖 496 | function componentUpdateFn() { 497 | if (!instance.isMounted) { 498 | // 组件初始化的时候会执行这里 499 | // 为什么要在这里调用 render 函数呢 500 | // 是因为在 effect 内调用 render 才能触发依赖收集 501 | // 等到后面响应式的值变更后会再次触发这个函数 502 | console.log(`${instance.type.name}:调用 render,获取 subTree`); 503 | const proxyToUse = instance.proxy; 504 | // 可在 render 函数中通过 this 来使用 proxy 505 | const subTree = (instance.subTree = normalizeVNode( 506 | instance.render.call(proxyToUse, proxyToUse) 507 | )); 508 | console.log("subTree", subTree); 509 | 510 | // todo 511 | console.log(`${instance.type.name}:触发 beforeMount hook`); 512 | console.log(`${instance.type.name}:触发 onVnodeBeforeMount hook`); 513 | 514 | // 这里基于 subTree 再次调用 patch 515 | // 基于 render 返回的 vnode ,再次进行渲染 516 | // 这里我把这个行为隐喻成开箱 517 | // 一个组件就是一个箱子 518 | // 里面有可能是 element (也就是可以直接渲染的) 519 | // 也有可能还是 component 520 | // 这里就是递归的开箱 521 | // 而 subTree 就是当前的这个箱子(组件)装的东西 522 | // 箱子(组件)只是个概念,它实际是不需要渲染的 523 | // 要渲染的是箱子里面的 subTree 524 | patch(null, subTree, container, null, instance); 525 | // 把 root element 赋值给 组件的vnode.el ,为后续调用 $el 的时候获取值 526 | initialVNode.el = subTree.el; 527 | 528 | console.log(`${instance.type.name}:触发 mounted hook`); 529 | instance.isMounted = true; 530 | } else { 531 | // 响应式的值变更后会从这里执行逻辑 532 | // 主要就是拿到新的 vnode ,然后和之前的 vnode 进行对比 533 | console.log(`${instance.type.name}:调用更新逻辑`); 534 | // 拿到最新的 subTree 535 | const { next, vnode } = instance; 536 | 537 | // 如果有 next 的话, 说明需要更新组件的数据(props,slots 等) 538 | // 先更新组件的数据,然后更新完成后,在继续对比当前组件的子元素 539 | if (next) { 540 | // 问题是 next 和 vnode 的区别是什么 541 | next.el = vnode.el; 542 | updateComponentPreRender(instance, next); 543 | } 544 | 545 | const proxyToUse = instance.proxy; 546 | const nextTree = normalizeVNode( 547 | instance.render.call(proxyToUse, proxyToUse) 548 | ); 549 | // 替换之前的 subTree 550 | const prevTree = instance.subTree; 551 | instance.subTree = nextTree; 552 | 553 | // 触发 beforeUpdated hook 554 | console.log(`${instance.type.name}:触发 beforeUpdated hook`); 555 | console.log(`${instance.type.name}:触发 onVnodeBeforeUpdate hook`); 556 | 557 | // 用旧的 vnode 和新的 vnode 交给 patch 来处理 558 | patch(prevTree, nextTree, prevTree.el, null, instance); 559 | 560 | // 触发 updated hook 561 | console.log(`${instance.type.name}:触发 updated hook`); 562 | console.log(`${instance.type.name}:触发 onVnodeUpdated hook`); 563 | } 564 | } 565 | 566 | // 在 vue3.2 版本里面是使用的 new ReactiveEffect 567 | // 至于为什么不直接用 effect ,是因为需要一个 scope 参数来收集所有的 effect 568 | // 而 effect 这个函数是对外的 api ,是不可以轻易改变参数的,所以会使用 new ReactiveEffect 569 | // 因为 ReactiveEffect 是内部对象,加一个参数是无所谓的 570 | // 后面如果要实现 scope 的逻辑的时候 需要改过来 571 | // 现在就先算了 572 | instance.update = effect(componentUpdateFn, { 573 | scheduler: () => { 574 | // 把 effect 推到微任务的时候在执行 575 | // queueJob(effect); 576 | queueJob(instance.update); 577 | }, 578 | }); 579 | } 580 | 581 | function updateComponentPreRender(instance, nextVNode) { 582 | // 更新 nextVNode 的组件实例 583 | // 现在 instance.vnode 是组件实例更新前的 584 | // 所以之前的 props 就是基于 instance.vnode.props 来获取 585 | // 接着需要更新 vnode ,方便下一次更新的时候获取到正确的值 586 | nextVNode.component = instance; 587 | // TODO 后面更新 props 的时候需要对比 588 | // const prevProps = instance.vnode.props; 589 | instance.vnode = nextVNode; 590 | instance.next = null; 591 | 592 | const { props } = nextVNode; 593 | console.log("更新组件的 props", props); 594 | instance.props = props; 595 | console.log("更新组件的 slots"); 596 | // TODO 更新组件的 slots 597 | // 需要重置 vnode 598 | } 599 | 600 | return { 601 | render, 602 | createApp: createAppAPI(render), 603 | }; 604 | } 605 | 606 | function getSequence(arr: number[]): number[] { 607 | const p = arr.slice(); 608 | const result = [0]; 609 | let i, j, u, v, c; 610 | const len = arr.length; 611 | for (i = 0; i < len; i++) { 612 | const arrI = arr[i]; 613 | if (arrI !== 0) { 614 | j = result[result.length - 1]; 615 | if (arr[j] < arrI) { 616 | p[i] = j; 617 | result.push(i); 618 | continue; 619 | } 620 | u = 0; 621 | v = result.length - 1; 622 | while (u < v) { 623 | c = (u + v) >> 1; 624 | if (arr[result[c]] < arrI) { 625 | u = c + 1; 626 | } else { 627 | v = c; 628 | } 629 | } 630 | if (arrI < arr[result[u]]) { 631 | if (u > 0) { 632 | p[i] = result[u - 1]; 633 | } 634 | result[u] = i; 635 | } 636 | } 637 | } 638 | u = result.length; 639 | v = result[u - 1]; 640 | while (u-- > 0) { 641 | result[u] = v; 642 | v = p[v]; 643 | } 644 | return result; 645 | } 646 | -------------------------------------------------------------------------------- /packages/runtime-core/src/scheduler.ts: -------------------------------------------------------------------------------- 1 | const queue: any[] = []; 2 | const activePreFlushCbs: any = []; 3 | 4 | const p = Promise.resolve(); 5 | let isFlushPending = false; 6 | 7 | export function nextTick(fn?) { 8 | return fn ? p.then(fn) : p; 9 | } 10 | 11 | export function queueJob(job) { 12 | if (!queue.includes(job)) { 13 | queue.push(job); 14 | // 执行所有的 job 15 | queueFlush(); 16 | } 17 | } 18 | 19 | function queueFlush() { 20 | // 如果同时触发了两个组件的更新的话 21 | // 这里就会触发两次 then (微任务逻辑) 22 | // 但是着是没有必要的 23 | // 我们只需要触发一次即可处理完所有的 job 调用 24 | // 所以需要判断一下 如果已经触发过 nextTick 了 25 | // 那么后面就不需要再次触发一次 nextTick 逻辑了 26 | if (isFlushPending) return; 27 | isFlushPending = true; 28 | nextTick(flushJobs); 29 | } 30 | 31 | export function queuePreFlushCb(cb) { 32 | queueCb(cb, activePreFlushCbs); 33 | } 34 | 35 | function queueCb(cb, activeQueue) { 36 | // 直接添加到对应的列表内就ok 37 | // todo 这里没有考虑 activeQueue 是否已经存在 cb 的情况 38 | // 然后在执行 flushJobs 的时候就可以调用 activeQueue 了 39 | activeQueue.push(cb); 40 | 41 | // 然后执行队列里面所有的 job 42 | queueFlush() 43 | } 44 | 45 | function flushJobs() { 46 | isFlushPending = false; 47 | 48 | // 先执行 pre 类型的 job 49 | // 所以这里执行的job 是在渲染前的 50 | // 也就意味着执行这里的 job 的时候 页面还没有渲染 51 | flushPreFlushCbs(); 52 | 53 | // 这里是执行 queueJob 的 54 | // 比如 render 渲染就是属于这个类型的 job 55 | let job; 56 | while ((job = queue.shift())) { 57 | if (job) { 58 | job(); 59 | } 60 | } 61 | } 62 | 63 | function flushPreFlushCbs() { 64 | // 执行所有的 pre 类型的 job 65 | for (let i = 0; i < activePreFlushCbs.length; i++) { 66 | activePreFlushCbs[i](); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/runtime-core/src/vnode.ts: -------------------------------------------------------------------------------- 1 | import { ShapeFlags } from "@mini-vue/shared"; 2 | 3 | export { createVNode as createElementVNode } 4 | 5 | export const createVNode = function ( 6 | type: any, 7 | props?: any, 8 | children?: string | Array 9 | ) { 10 | // 注意 type 有可能是 string 也有可能是对象 11 | // 如果是对象的话,那么就是用户设置的 options 12 | // type 为 string 的时候 13 | // createVNode("div") 14 | // type 为组件对象的时候 15 | // createVNode(App) 16 | const vnode = { 17 | el: null, 18 | component: null, 19 | key: props?.key, 20 | type, 21 | props: props || {}, 22 | children, 23 | shapeFlag: getShapeFlag(type), 24 | }; 25 | 26 | // 基于 children 再次设置 shapeFlag 27 | if (Array.isArray(children)) { 28 | vnode.shapeFlag |= ShapeFlags.ARRAY_CHILDREN; 29 | } else if (typeof children === "string") { 30 | vnode.shapeFlag |= ShapeFlags.TEXT_CHILDREN; 31 | } 32 | 33 | normalizeChildren(vnode, children); 34 | 35 | return vnode; 36 | }; 37 | 38 | export function normalizeChildren(vnode, children) { 39 | if (typeof children === "object") { 40 | // 暂时主要是为了标识出 slots_children 这个类型来 41 | // 暂时我们只有 element 类型和 component 类型的组件 42 | // 所以我们这里除了 element ,那么只要是 component 的话,那么children 肯定就是 slots 了 43 | if (vnode.shapeFlag & ShapeFlags.ELEMENT) { 44 | // 如果是 element 类型的话,那么 children 肯定不是 slots 45 | } else { 46 | // 这里就必然是 component 了, 47 | vnode.shapeFlag |= ShapeFlags.SLOTS_CHILDREN; 48 | } 49 | } 50 | } 51 | // 用 symbol 作为唯一标识 52 | export const Text = Symbol("Text"); 53 | export const Fragment = Symbol("Fragment"); 54 | 55 | /** 56 | * @private 57 | */ 58 | export function createTextVNode(text: string = " ") { 59 | return createVNode(Text, {}, text); 60 | } 61 | 62 | // 标准化 vnode 的格式 63 | // 其目的是为了让 child 支持多种格式 64 | export function normalizeVNode(child) { 65 | // 暂时只支持处理 child 为 string 和 number 的情况 66 | if (typeof child === "string" || typeof child === "number") { 67 | return createVNode(Text, null, String(child)); 68 | } else { 69 | return child; 70 | } 71 | } 72 | 73 | // 基于 type 来判断是什么类型的组件 74 | function getShapeFlag(type: any) { 75 | return typeof type === "string" 76 | ? ShapeFlags.ELEMENT 77 | : ShapeFlags.STATEFUL_COMPONENT; 78 | } 79 | -------------------------------------------------------------------------------- /packages/runtime-dom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mini-vue/runtime-dom", 3 | "version": "1.0.0", 4 | "description": "@mini-vue/runtime-dom", 5 | "module": "dist/shared.esm-bundler.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@mini-vue/runtime-core": "workspace:^1.0.0", 14 | "@mini-vue/shared": "workspace:^1.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/runtime-dom/src/index.ts: -------------------------------------------------------------------------------- 1 | // 源码里面这些接口是由 runtime-dom 来实现 2 | // 这里先简单实现 3 | 4 | import { isOn } from "@mini-vue/shared"; 5 | import { createRenderer } from "@mini-vue/runtime-core"; 6 | 7 | // 后面也修改成和源码一样的实现 8 | function createElement(type) { 9 | console.log("CreateElement", type); 10 | const element = document.createElement(type); 11 | return element; 12 | } 13 | 14 | function createText(text) { 15 | return document.createTextNode(text); 16 | } 17 | 18 | function setText(node, text) { 19 | node.nodeValue = text; 20 | } 21 | 22 | function setElementText(el, text) { 23 | console.log("SetElementText", el, text); 24 | el.textContent = text; 25 | } 26 | 27 | function patchProp(el, key, preValue, nextValue) { 28 | // preValue 之前的值 29 | // 为了之后 update 做准备的值 30 | // nextValue 当前的值 31 | console.log(`PatchProp 设置属性:${key} 值:${nextValue}`); 32 | console.log(`key: ${key} 之前的值是:${preValue}`); 33 | 34 | if (isOn(key)) { 35 | // 添加事件处理函数的时候需要注意一下 36 | // 1. 添加的和删除的必须是一个函数,不然的话 删除不掉 37 | // 那么就需要把之前 add 的函数给存起来,后面删除的时候需要用到 38 | // 2. nextValue 有可能是匿名函数,当对比发现不一样的时候也可以通过缓存的机制来避免注册多次 39 | // 存储所有的事件函数 40 | const invokers = el._vei || (el._vei = {}); 41 | const existingInvoker = invokers[key]; 42 | if (nextValue && existingInvoker) { 43 | // patch 44 | // 直接修改函数的值即可 45 | existingInvoker.value = nextValue; 46 | } else { 47 | const eventName = key.slice(2).toLowerCase(); 48 | if (nextValue) { 49 | const invoker = (invokers[key] = nextValue); 50 | el.addEventListener(eventName, invoker); 51 | } else { 52 | el.removeEventListener(eventName, existingInvoker); 53 | invokers[key] = undefined; 54 | } 55 | } 56 | } else { 57 | if (nextValue === null || nextValue === "") { 58 | el.removeAttribute(key); 59 | } else { 60 | el.setAttribute(key, nextValue); 61 | } 62 | } 63 | } 64 | 65 | function insert(child, parent, anchor = null) { 66 | console.log("Insert"); 67 | parent.insertBefore(child, anchor); 68 | } 69 | 70 | function remove(child) { 71 | const parent = child.parentNode; 72 | if (parent) { 73 | parent.removeChild(child); 74 | } 75 | } 76 | 77 | let renderer; 78 | 79 | function ensureRenderer() { 80 | // 如果 renderer 有值的话,那么以后都不会初始化了 81 | return ( 82 | renderer || 83 | (renderer = createRenderer({ 84 | createElement, 85 | createText, 86 | setText, 87 | setElementText, 88 | patchProp, 89 | insert, 90 | remove, 91 | })) 92 | ); 93 | } 94 | 95 | export const createApp = (...args) => { 96 | return ensureRenderer().createApp(...args); 97 | }; 98 | 99 | export * from "@mini-vue/runtime-core" 100 | -------------------------------------------------------------------------------- /packages/runtime-test/src/index.ts: -------------------------------------------------------------------------------- 1 | // todo 2 | // 实现 render 的渲染接口 3 | // 实现序列化 4 | import { createRenderer } from "@mini-vue/runtime-core"; 5 | import { extend } from "@mini-vue/shared"; 6 | import { nodeOps } from "./nodeOps"; 7 | import { patchProp } from "./patchProp"; 8 | 9 | export const { render } = createRenderer(extend({ patchProp }, nodeOps)); 10 | 11 | export * from "./nodeOps"; 12 | export * from "./serialize" 13 | export * from '@mini-vue/runtime-core' -------------------------------------------------------------------------------- /packages/runtime-test/src/nodeOps.ts: -------------------------------------------------------------------------------- 1 | export const enum NodeTypes { 2 | ELEMENT = "element", 3 | TEXT = "TEXT", 4 | } 5 | 6 | let nodeId = 0; 7 | // 这个函数会在 runtime-core 初始化 element 的时候调用 8 | function createElement(tag: string) { 9 | // 如果是基于 dom 的话 那么这里会返回 dom 元素 10 | // 这里是为了测试 所以只需要反正一个对象就可以了 11 | // 后面的话 通过这个对象来做测试 12 | const node = { 13 | tag, 14 | id: nodeId++, 15 | type: NodeTypes.ELEMENT, 16 | props: {}, 17 | children: [], 18 | parentNode: null, 19 | }; 20 | 21 | return node; 22 | } 23 | 24 | function insert(child, parent) { 25 | parent.children.push(child); 26 | child.parentNode = parent; 27 | } 28 | 29 | function parentNode(node) { 30 | return node.parentNode; 31 | } 32 | 33 | function setElementText(el, text) { 34 | el.children = [ 35 | { 36 | id: nodeId++, 37 | type: NodeTypes.TEXT, 38 | text, 39 | parentNode: el, 40 | }, 41 | ]; 42 | } 43 | 44 | export const nodeOps = { createElement, insert, parentNode, setElementText }; 45 | -------------------------------------------------------------------------------- /packages/runtime-test/src/patchProp.ts: -------------------------------------------------------------------------------- 1 | export function patchProp(el, key, prevValue, nextValue) { 2 | el.props[key] = nextValue; 3 | } 4 | -------------------------------------------------------------------------------- /packages/runtime-test/src/serialize.ts: -------------------------------------------------------------------------------- 1 | // 把 node 给序列化 2 | // 测试的时候好对比 3 | 4 | import { NodeTypes } from "./nodeOps"; 5 | 6 | // 序列化: 把一个对象给处理成 string (进行流化) 7 | export function serialize(node) { 8 | if (node.type === NodeTypes.ELEMENT) { 9 | return serializeElement(node); 10 | } else { 11 | return serializeText(node); 12 | } 13 | } 14 | 15 | function serializeText(node) { 16 | return node.text; 17 | } 18 | 19 | export function serializeInner(node) { 20 | // 把所有节点变成一个string 21 | return node.children.map((c) => serialize(c)).join(``); 22 | } 23 | 24 | function serializeElement(node) { 25 | // 把 props 处理成字符串 26 | // 规则: 27 | // 如果 value 是 null 的话 那么直接返回 `` 28 | // 如果 value 是 `` 的话,那么返回 key 29 | // 不然的话返回 key = value(这里的值需要字符串化) 30 | const props = Object.keys(node.props) 31 | .map((key) => { 32 | const value = node.props[key]; 33 | return value == null 34 | ? `` 35 | : value === `` 36 | ? key 37 | : `${key}=${JSON.stringify(value)}`; 38 | }) 39 | .filter(Boolean) 40 | .join(" "); 41 | 42 | console.log("node---------", node.children); 43 | return `<${node.tag}${props ? ` ${props}` : ``}>${serializeInner(node)}`; 46 | } 47 | -------------------------------------------------------------------------------- /packages/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mini-vue/shared", 3 | "version": "1.0.0", 4 | "description": "@mini-vue/shared", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "../src/shapeFlags"; 2 | export * from "../src/toDisplayString"; 3 | 4 | export const isObject = (val) => { 5 | return val !== null && typeof val === "object"; 6 | }; 7 | 8 | export const isString = (val) => typeof val === "string"; 9 | 10 | const camelizeRE = /-(\w)/g; 11 | /** 12 | * @private 13 | * 把烤肉串命名方式转换成驼峰命名方式 14 | */ 15 | export const camelize = (str: string): string => { 16 | return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : "")); 17 | }; 18 | 19 | export const extend = Object.assign; 20 | 21 | // 必须是 on+一个大写字母的格式开头 22 | export const isOn = (key) => /^on[A-Z]/.test(key); 23 | 24 | export function hasChanged(value, oldValue) { 25 | return !Object.is(value, oldValue); 26 | } 27 | 28 | export function hasOwn(val, key) { 29 | return Object.prototype.hasOwnProperty.call(val, key); 30 | } 31 | 32 | /** 33 | * @private 34 | * 首字母大写 35 | */ 36 | export const capitalize = (str: string) => 37 | str.charAt(0).toUpperCase() + str.slice(1); 38 | 39 | /** 40 | * @private 41 | * 添加 on 前缀,并且首字母大写 42 | */ 43 | export const toHandlerKey = (str: string) => 44 | str ? `on${capitalize(str)}` : ``; 45 | 46 | // 用来匹配 kebab-case 的情况 47 | // 比如 onTest-event 可以匹配到 T 48 | // 然后取到 T 在前面加一个 - 就可以 49 | // \BT 就可以匹配到 T 前面是字母的位置 50 | const hyphenateRE = /\B([A-Z])/g; 51 | /** 52 | * @private 53 | */ 54 | export const hyphenate = (str: string) => 55 | str.replace(hyphenateRE, "-$1").toLowerCase(); 56 | -------------------------------------------------------------------------------- /packages/shared/src/shapeFlags.ts: -------------------------------------------------------------------------------- 1 | // 组件的类型 2 | export const enum ShapeFlags { 3 | // 最后要渲染的 element 类型 4 | ELEMENT = 1, 5 | // 组件类型 6 | STATEFUL_COMPONENT = 1 << 2, 7 | // vnode 的 children 为 string 类型 8 | TEXT_CHILDREN = 1 << 3, 9 | // vnode 的 children 为数组类型 10 | ARRAY_CHILDREN = 1 << 4, 11 | // vnode 的 children 为 slots 类型 12 | SLOTS_CHILDREN = 1 << 5 13 | } 14 | -------------------------------------------------------------------------------- /packages/shared/src/toDisplayString.ts: -------------------------------------------------------------------------------- 1 | export const toDisplayString = (val) => { 2 | return String(val); 3 | }; 4 | -------------------------------------------------------------------------------- /packages/vue/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Befend/mini-vue/d66704c6291806b1f1138ea1e529dbe4eef54064/packages/vue/.DS_Store -------------------------------------------------------------------------------- /packages/vue/dist/mini-vue.esm-bundler.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"mini-vue.esm-bundler.js","sources":["../../shared/src/shapeFlags.ts","../../shared/src/toDisplayString.ts","../../shared/src/index.ts","../../runtime-core/src/vnode.ts","../../runtime-core/src/h.ts","../../runtime-core/src/createApp.ts","../../runtime-core/src/componentProps.ts","../../runtime-core/src/componentSlots.ts","../../runtime-core/src/componentEmits.ts","../../runtime-core/src/componentPublicInstance.ts","../../reactivity/src/dep.ts","../../reactivity/src/effect.ts","../../reactivity/src/baseHandlers.ts","../../reactivity/src/reactive.ts","../../reactivity/src/ref.ts","../../reactivity/src/computed.ts","../../runtime-core/src/component.ts","../../runtime-core/src/apiInject.ts","../../runtime-core/src/helpers/renderSlot.ts","../../runtime-core/src/scheduler.ts","../../runtime-core/src/componentRenderUtils.ts","../../runtime-core/src/renderer.ts","../../runtime-dom/src/index.ts","../../compiler-core/src/runtimeHelpers.ts","../../compiler-core/src/codegen.ts","../../compiler-core/src/parse.ts","../../compiler-core/src/transform.ts","../../compiler-core/src/transforms/transformExpression.ts","../../compiler-core/src/ast.ts","../../compiler-core/src/transforms/transformElement.ts","../../compiler-core/src/utils.ts","../../compiler-core/src/transforms/transformText.ts","../../compiler-core/src/compile.ts","../src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /packages/vue/example/apiInject/App.js: -------------------------------------------------------------------------------- 1 | // 组件 provide 和 inject 功能 2 | import { 3 | h, 4 | provide, 5 | inject, 6 | } from "../../dist/mini-vue.esm-bundler.js"; 7 | 8 | const ProviderOne = { 9 | setup() { 10 | provide("foo", "foo"); 11 | provide("bar", "bar"); 12 | return () => h(ProviderTwo); 13 | }, 14 | }; 15 | 16 | const ProviderTwo = { 17 | setup() { 18 | // override parent value 19 | provide("foo", "fooOverride"); 20 | provide("baz", "baz"); 21 | const foo = inject("foo"); 22 | // 这里获取的 foo 的值应该是 "foo" 23 | // 这个组件的子组件获取的 foo ,才应该是 fooOverride 24 | if (foo !== "foo") { 25 | throw new Error("Foo should equal to foo"); 26 | } 27 | return () => h(Consumer); 28 | }, 29 | }; 30 | 31 | const Consumer = { 32 | setup() { 33 | const foo = inject("foo"); 34 | const bar = inject("bar"); 35 | const baz = inject("baz"); 36 | return () => { 37 | return h("div", {}, `${foo}-${bar}-${baz}`); 38 | }; 39 | }, 40 | }; 41 | 42 | export default { 43 | name: "App", 44 | setup() { 45 | return () => h("div", {}, [h("p", {}, "apiInject"), h(ProviderOne)]); 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /packages/vue/example/apiInject/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/compiler-base/App.js: -------------------------------------------------------------------------------- 1 | // 最简单的情况 2 | // template 只有一个 interpolation 3 | // export default { 4 | // template: `{{msg}}`, 5 | // setup() { 6 | // return { 7 | // msg: "vue3 - compiler", 8 | // }; 9 | // }, 10 | // }; 11 | 12 | 13 | // 复杂一点 14 | // template 包含 element 和 interpolation 15 | export default { 16 | template: `

{{msg}}

`, 17 | setup() { 18 | return { 19 | msg: "vue3 - compiler", 20 | }; 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/vue/example/compiler-base/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/vue/example/componentEmit.js/App.js: -------------------------------------------------------------------------------- 1 | // 组件 emit 逻辑 demo 2 | // click emit 发出 change, 可以触发 App 组件内定义好的侦听函数 3 | // 允许接收多个参数 4 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 5 | import Child from "./Child.js"; 6 | 7 | export default { 8 | name: "App", 9 | setup() {}, 10 | 11 | render() { 12 | return h("div", {}, [ 13 | h("div", {}, "你好"), 14 | h(Child, { 15 | msg: "your name is child", 16 | onChange(a, b) { 17 | console.log("---------------change------------------"); 18 | console.log(a, b); 19 | }, 20 | onChangePageName(a, b) { 21 | console.log("---------------change-page-name------------------"); 22 | console.log(a, b); 23 | }, 24 | }), 25 | ]); 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/vue/example/componentEmit.js/Child.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 2 | export default { 3 | name: "Child", 4 | setup(props, { emit }) { 5 | emit("change", "aaaaa", "bbbbbb"); 6 | // 支持多个 - 7 | emit("change-page-name", "start", "game"); 8 | }, 9 | render() { 10 | return h("div", {}, "child"); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /packages/vue/example/componentEmit.js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/componentProxy/App.js: -------------------------------------------------------------------------------- 1 | // 在 render 中使用 proxy 调用 emit 函数 2 | // 也可以直接使用 this 3 | // 验证 proxy 的实现逻辑 4 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 5 | import Child from "./Child.js"; 6 | 7 | export default { 8 | name: "App", 9 | setup() {}, 10 | 11 | render() { 12 | return h("div", {}, [ 13 | h("div", {}, "你好"), 14 | h(Child, { 15 | msg: "your name is child", 16 | onChange(a, b) { 17 | console.log("---------------change------------------"); 18 | console.log(a, b); 19 | }, 20 | }), 21 | ]); 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/vue/example/componentProxy/Child.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 2 | export default { 3 | name: "Child", 4 | setup(props, { emit }) {}, 5 | render(proxy) { 6 | const self = this 7 | return h("div", {}, [ 8 | h( 9 | "button", 10 | { 11 | onClick() { 12 | console.log(proxy); 13 | console.log("click"); 14 | proxy.$emit("change", "aaa", "bbbb"); 15 | // 使用 this 16 | console.log(this) 17 | self.$emit("change", "ccc", "ddd"); 18 | }, 19 | }, 20 | "emit" 21 | ), 22 | ]); 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/vue/example/componentProxy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/componentUpdate/App.js: -------------------------------------------------------------------------------- 1 | // 在 render 中使用 proxy 调用 emit 函数 2 | // 也可以直接使用 this 3 | // 验证 proxy 的实现逻辑 4 | import { h, ref } from "../../dist/mini-vue.esm-bundler.js"; 5 | import Child from "./Child.js"; 6 | 7 | export default { 8 | name: "App", 9 | setup() { 10 | const msg = ref("123"); 11 | window.msg = msg 12 | 13 | const changeChildProps = () => { 14 | msg.value = "456"; 15 | }; 16 | 17 | return { msg, changeChildProps }; 18 | }, 19 | 20 | render() { 21 | return h("div", {}, [ 22 | h("div", {}, "你好"), 23 | h( 24 | "button", 25 | { 26 | onClick: this.changeChildProps, 27 | }, 28 | "change child props" 29 | ), 30 | h(Child, { 31 | msg: this.msg, 32 | }), 33 | ]); 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/vue/example/componentUpdate/Child.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 2 | export default { 3 | name: "Child", 4 | setup(props, { emit }) {}, 5 | render(proxy) { 6 | return h("div", {}, [h("div", {}, "child" + this.$props.msg)]); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/vue/example/componentUpdate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/createTextVnode/App.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive, createTextVNode } from "../../dist/mini-vue.esm-bundler.js"; 2 | 3 | export default { 4 | name: "App", 5 | setup() {}, 6 | 7 | render() { 8 | return h("div", {}, [ 9 | h("div", {}, "你好"), 10 | createTextVNode("这是通过 createTextVNode 创建的节点"), 11 | ]); 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/vue/example/createTextVnode/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/customRenderer/App.js: -------------------------------------------------------------------------------- 1 | import { h, ref } from "../../dist/mini-vue.esm-bundler.js"; 2 | import { game } from "./game.js"; 3 | 4 | export default { 5 | name: "App", 6 | setup() { 7 | // 通过 ticker 来去更新 x 的值 8 | 9 | const x = ref(0); 10 | const y = ref(0); 11 | let dir = 1; 12 | const speed = 2; 13 | 14 | game.ticker.add(() => { 15 | if (x.value > 400) { 16 | dir = -1; 17 | } else if (x.value < 0) { 18 | dir = 1; 19 | } 20 | 21 | x.value += speed * dir; 22 | }); 23 | 24 | return { 25 | x, 26 | y, 27 | }; 28 | }, 29 | 30 | render() { 31 | return h("rect", { x: this.x, y: this.y }); 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/vue/example/customRenderer/game.js: -------------------------------------------------------------------------------- 1 | export const game = new PIXI.Application({ 2 | width: 500, 3 | height: 500, 4 | }); 5 | 6 | document.body.append(game.view); 7 | 8 | export function createRootContainer() { 9 | return game.stage; 10 | } 11 | -------------------------------------------------------------------------------- /packages/vue/example/customRenderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/vue/example/customRenderer/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js"; 2 | import { createApp } from "./renderer.js"; 3 | import { createRootContainer } from "./game.js"; 4 | 5 | createApp(App).mount(createRootContainer()); 6 | -------------------------------------------------------------------------------- /packages/vue/example/customRenderer/renderer.js: -------------------------------------------------------------------------------- 1 | import { createRenderer } from "../../dist/mini-vue.esm-bundler.js"; 2 | 3 | // 给基于 pixi.js 的渲染函数 4 | const renderer = createRenderer({ 5 | createElement(type) { 6 | const rect = new PIXI.Graphics(); 7 | rect.beginFill(0xff0000); 8 | rect.drawRect(0, 0, 100, 100); 9 | rect.endFill(); 10 | 11 | return rect; 12 | }, 13 | 14 | patchProp(el, key, prevValue, nextValue) { 15 | el[key] = nextValue; 16 | }, 17 | 18 | insert(el, parent) { 19 | parent.addChild(el); 20 | }, 21 | }); 22 | 23 | export function createApp(rootComponent) { 24 | return renderer.createApp(rootComponent); 25 | } 26 | -------------------------------------------------------------------------------- /packages/vue/example/getCurrentInstance/App.js: -------------------------------------------------------------------------------- 1 | // 可以在 setup 中使用 getCurrentInstance 获取组件实例对象 2 | import { h, getCurrentInstance } from "../../dist/mini-vue.esm-bundler.js"; 3 | 4 | export default { 5 | name: "App", 6 | setup() { 7 | 8 | console.log(getCurrentInstance()) 9 | 10 | 11 | 12 | return () => h("div", {}, [h("p", {}, "getCurrentInstance")]); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /packages/vue/example/getCurrentInstance/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/helloWorld/App.js: -------------------------------------------------------------------------------- 1 | import { h, ref } from "../../dist/mini-vue.esm-bundler.js"; 2 | 3 | const count = ref(0); 4 | 5 | const HelloWorld = { 6 | name: "HelloWorld", 7 | setup() {}, 8 | // TODO 第一个小目标 9 | // 可以在使用 template 只需要有一个插值表达式即 10 | // 可以解析 tag 标签 11 | // template: ` 12 | //
hi {{msg}}
13 | // 需要编译成 render 函数 14 | // `, 15 | render() { 16 | return h( 17 | "div", 18 | { tId: "helloWorld" }, 19 | `hello world: count: ${count.value}` 20 | ); 21 | }, 22 | }; 23 | 24 | export default { 25 | name: "App", 26 | setup() {}, 27 | 28 | render() { 29 | return h("div", { tId: 1 }, [h("p", {}, "主页"), h(HelloWorld)]); 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/vue/example/helloWorld/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/vue/example/helloWorld/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "../../dist/mini-vue.esm-bundler.js"; 2 | import App from "./App.js"; 3 | 4 | const rootContainer = document.querySelector("#root"); 5 | createApp(App).mount(rootContainer); 6 | -------------------------------------------------------------------------------- /packages/vue/example/nextTicker/App.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 2 | import NextTicker from "./NextTicker.js"; 3 | 4 | export default { 5 | name: "App", 6 | setup() {}, 7 | 8 | render() { 9 | return h("div", { tId: 1 }, [h("p", {}, "主页"), h(NextTicker)]); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/vue/example/nextTicker/NextTicker.js: -------------------------------------------------------------------------------- 1 | // 测试 nextTick 逻辑 2 | import { h, ref } from "../../dist/mini-vue.esm-bundler.js"; 3 | 4 | // 如果 for 循环改变 count 的值 100 次的话 5 | // 会同时触发 100 次的 update 页面逻辑 6 | // 这里可以把 update 页面的逻辑放到微任务中执行 7 | // 避免更改了响应式对象就会执行 update 的逻辑 8 | // 因为只有最后一次调用 update 才是有价值的 9 | window.count = ref(1); 10 | 11 | // 如果一个响应式变量同时触发了两个组件的 update 12 | // 会发生什么有趣的事呢? 13 | const Child1 = { 14 | name: "NextTickerChild1", 15 | setup() {}, 16 | render() { 17 | return h("div", {}, `child1 count: ${window.count.value}`); 18 | }, 19 | }; 20 | 21 | const Child2 = { 22 | name: "NextTickerChild2", 23 | setup() {}, 24 | render() { 25 | return h("div", {}, `child2 count: ${window.count.value}`); 26 | }, 27 | }; 28 | 29 | export default { 30 | name: "NextTicker", 31 | setup() {}, 32 | render() { 33 | return h( 34 | "div", 35 | { tId: "nextTicker" }, 36 | [h(Child1), h(Child2)] 37 | // `for nextTick: count: ${window.count.value}` 38 | ); 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /packages/vue/example/nextTicker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/vue/example/nextTicker/main.js: -------------------------------------------------------------------------------- 1 | import {createApp} from "../../dist/mini-vue.esm-bundler.js"; 2 | import App from "./App.js"; 3 | 4 | const rootContainer = document.querySelector("#root"); 5 | createApp(App).mount(rootContainer); 6 | -------------------------------------------------------------------------------- /packages/vue/example/patchChildren/App.js: -------------------------------------------------------------------------------- 1 | import { h } from "../../dist/mini-vue.esm-bundler.js"; 2 | import PatchChildren from "./PatchChildren.js"; 3 | 4 | export default { 5 | name: "App", 6 | setup() {}, 7 | 8 | render() { 9 | return h("div", { tId: 1 }, [h("p", {}, "主页"), h(PatchChildren)]); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/vue/example/patchChildren/PatchChildren.js: -------------------------------------------------------------------------------- 1 | import { h } from "../../dist/mini-vue.esm-bundler.js"; 2 | import { ref } from "../../dist/mini-vue.esm-bundler.js"; 3 | 4 | const isChange = ref(false); 5 | 6 | // 1. 左侧的对比 7 | // (a b) c 8 | // (a b) d e 9 | // const prevChildren = [ 10 | // h("p", { key: "A" }, "A"), 11 | // h("p", { key: "B" }, "B"), 12 | // h("p", { key: "C" }, "C"), 13 | // ]; 14 | // const nextChildren = [ 15 | // h("p", { key: "A" }, "A"), 16 | // h("p", { key: "B" }, "B"), 17 | // h("p", { key: "D" }, "D"), 18 | // h("p", { key: "E" }, "E"), 19 | // ]; 20 | 21 | // 2. 右侧的对比 22 | // a (b c) 23 | // d e (b c) 24 | // const prevChildren = [ 25 | // h("p", { key: "A" }, "A"), 26 | // h("p", { key: "B" }, "B"), 27 | // h("p", { key: "C" }, "C"), 28 | // ]; 29 | // const nextChildren = [ 30 | // h("p", { key: "D" }, "D"), 31 | // h("p", { key: "E" }, "E"), 32 | // h("p", { key: "B" }, "B"), 33 | // h("p", { key: "C" }, "C"), 34 | // ]; 35 | 36 | // 3. 新的比老的长 37 | // 创建新的 38 | // 左侧 39 | // (a b) 40 | // (a b) c 41 | // i = 2, e1 = 1, e2 = 2 42 | // const prevChildren = [h("p", { key: "A" }, "A"), h("p", { key: "B" }, "B")]; 43 | // const nextChildren = [ 44 | // h("p", { key: "A" }, "A"), 45 | // h("p", { key: "B" }, "B"), 46 | // h("p", { key: "C" }, "C"), 47 | // ]; 48 | 49 | // 右侧 50 | // (a b) 51 | // c (a b) 52 | // i = 0, e1 = -1, e2 = 0 53 | // const prevChildren = [h("p", { key: "A" }, "A"), h("p", { key: "B" }, "B")]; 54 | // const nextChildren = [ 55 | // h("p", { key: "C" }, "C"), 56 | // h("p", { key: "A" }, "A"), 57 | // h("p", { key: "B" }, "B"), 58 | // ]; 59 | 60 | // 4. 老的比新的长 61 | // 删除老的 62 | // 左侧 63 | // (a b) c 64 | // (a b) 65 | // i = 2, e1 = 2, e2 = 1 66 | // const prevChildren = [ 67 | // h("p", { key: "A" }, "A"), 68 | // h("p", { key: "B" }, "B"), 69 | // h("p", { key: "C" }, "C"), 70 | // ]; 71 | // const nextChildren = [h("p", { key: "A" }, "A"), h("p", { key: "B" }, "B")]; 72 | 73 | // 右侧 74 | // a (b c) 75 | // (b c) 76 | // i = 0, e1 = 0, e2 = -1 77 | 78 | // const prevChildren = [ 79 | // h("p", { key: "A" }, "A"), 80 | // h("p", { key: "B" }, "B"), 81 | // h("p", { key: "C" }, "C"), 82 | // ]; 83 | // const nextChildren = [h("p", { key: "B" }, "B"), h("p", { key: "C" }, "C")]; 84 | 85 | // 5. 对比中间的部分 86 | // 删除老的 (在老的里面存在,新的里面不存在) 87 | // 5.1 88 | // a,b,(c,d),f,g 89 | // a,b,(e,c),f,g 90 | // D 节点在新的里面是没有的 - 需要删除掉 91 | // C 节点 props 也发生了变化 92 | 93 | // const prevChildren = [ 94 | // h("p", { key: "A" }, "A"), 95 | // h("p", { key: "B" }, "B"), 96 | // h("p", { key: "C", id: "c-prev" }, "C"), 97 | // h("p", { key: "D" }, "D"), 98 | // h("p", { key: "F" }, "F"), 99 | // h("p", { key: "G" }, "G"), 100 | // ]; 101 | 102 | // const nextChildren = [ 103 | // h("p", { key: "A" }, "A"), 104 | // h("p", { key: "B" }, "B"), 105 | // h("p", { key: "E" }, "E"), 106 | // h("p", { key: "C", id:"c-next" }, "C"), 107 | // h("p", { key: "F" }, "F"), 108 | // h("p", { key: "G" }, "G"), 109 | // ]; 110 | 111 | // 5.1.1 112 | // a,b,(c,e,d),f,g 113 | // a,b,(e,c),f,g 114 | // 中间部分,老的比新的多, 那么多出来的直接就可以被干掉(优化删除逻辑) 115 | // const prevChildren = [ 116 | // h("p", { key: "A" }, "A"), 117 | // h("p", { key: "B" }, "B"), 118 | // h("p", { key: "C", id: "c-prev" }, "C"), 119 | // h("p", { key: "E" }, "E"), 120 | // h("p", { key: "D" }, "D"), 121 | // h("p", { key: "F" }, "F"), 122 | // h("p", { key: "G" }, "G"), 123 | // ]; 124 | 125 | // const nextChildren = [ 126 | // h("p", { key: "A" }, "A"), 127 | // h("p", { key: "B" }, "B"), 128 | // h("p", { key: "E" }, "E"), 129 | // h("p", { key: "C", id:"c-next" }, "C"), 130 | // h("p", { key: "F" }, "F"), 131 | // h("p", { key: "G" }, "G"), 132 | // ]; 133 | 134 | // 2 移动 (节点存在于新的和老的里面,但是位置变了) 135 | 136 | // 2.1 137 | // a,b,(c,d,e),f,g 138 | // a,b,(e,c,d),f,g 139 | // 最长子序列: [1,2] 140 | 141 | // const prevChildren = [ 142 | // h("p", { key: "A" }, "A"), 143 | // h("p", { key: "B" }, "B"), 144 | // h("p", { key: "C" }, "C"), 145 | // h("p", { key: "D" }, "D"), 146 | // h("p", { key: "E" }, "E"), 147 | // h("p", { key: "F" }, "F"), 148 | // h("p", { key: "G" }, "G"), 149 | // ]; 150 | 151 | // const nextChildren = [ 152 | // h("p", { key: "A" }, "A"), 153 | // h("p", { key: "B" }, "B"), 154 | // h("p", { key: "E" }, "E"), 155 | // h("p", { key: "C" }, "C"), 156 | // h("p", { key: "D" }, "D"), 157 | // h("p", { key: "F" }, "F"), 158 | // h("p", { key: "G" }, "G"), 159 | // ]; 160 | 161 | // 2.2 162 | // a,b,(c,d,e,z),f,g 163 | // a,b,(d,c,y,e),f,g 164 | // 最长子序列: [1,3] 165 | 166 | // const prevChildren = [ 167 | // h("p", { key: "A" }, "A"), 168 | // h("p", { key: "B" }, "B"), 169 | // h("p", { key: "C" }, "C"), 170 | // h("p", { key: "D" }, "D"), 171 | // h("p", { key: "E" }, "E"), 172 | // h("p", { key: "Z" }, "Z"), 173 | // h("p", { key: "F" }, "F"), 174 | // h("p", { key: "G" }, "G"), 175 | // ]; 176 | 177 | // const nextChildren = [ 178 | // h("p", { key: "A" }, "A"), 179 | // h("p", { key: "B" }, "B"), 180 | // h("p", { key: "D" }, "D"), 181 | // h("p", { key: "C" }, "C"), 182 | // h("p", { key: "Y" }, "Y"), 183 | // h("p", { key: "E" }, "E"), 184 | // h("p", { key: "F" }, "F"), 185 | // h("p", { key: "G" }, "G"), 186 | // ]; 187 | 188 | // 3. 创建新的节点 189 | // a,b,(c,e),f,g 190 | // a,b,(e,c,d),f,g 191 | // d 节点在老的节点中不存在,新的里面存在,所以需要创建 192 | const prevChildren = [ 193 | h("p", { key: "A" }, "A"), 194 | h("p", { key: "B" }, "B"), 195 | h("p", { key: "C" }, "C"), 196 | h("p", { key: "E" }, "E"), 197 | h("p", { key: "F" }, "F"), 198 | h("p", { key: "G" }, "G"), 199 | ]; 200 | 201 | const nextChildren = [ 202 | h("p", { key: "A" }, "A"), 203 | h("p", { key: "B" }, "B"), 204 | h("p", { key: "E" }, "E"), 205 | h("p", { key: "C" }, "C"), 206 | h("p", { key: "D" }, "D"), 207 | h("p", { key: "F" }, "F"), 208 | h("p", { key: "G" }, "G"), 209 | ]; 210 | 211 | export default { 212 | name: "PatchChildren", 213 | setup() {}, 214 | render() { 215 | return h("div", {}, [ 216 | h( 217 | "button", 218 | { 219 | onClick: () => { 220 | isChange.value = !isChange.value; 221 | }, 222 | }, 223 | "测试子组件之间的 patch 逻辑" 224 | ), 225 | h("children", {}, isChange.value === true ? nextChildren : prevChildren), 226 | ]); 227 | }, 228 | }; 229 | -------------------------------------------------------------------------------- /packages/vue/example/patchChildren/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/vue/example/patchChildren/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "../../dist/mini-vue.esm-bundler.js"; 2 | import App from "./App.js"; 3 | 4 | const rootContainer = document.querySelector("#root"); 5 | createApp(App).mount(rootContainer); 6 | -------------------------------------------------------------------------------- /packages/vue/example/renderComponent/App.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 2 | import Child from "./Child.js"; 3 | 4 | export default { 5 | name: "App", 6 | setup() {}, 7 | 8 | render() { 9 | return h("div", {}, [ 10 | h("div", {}, "你好"), 11 | h(Child, { 12 | msg: "your name is child", 13 | }), 14 | ]); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/vue/example/renderComponent/Child.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 2 | export default { 3 | name: "Child", 4 | setup(props, context) { 5 | console.log("props------------------>", props); 6 | console.log("context---------------->", context); 7 | }, 8 | render() { 9 | return h("div", {}, "child"); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/vue/example/renderComponent/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/setupStateRenderComponent/App.js: -------------------------------------------------------------------------------- 1 | // 在 render 中可以通过 this.xxx 访问到 setup 返回的对象 2 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 3 | 4 | export default { 5 | name: "App", 6 | setup() { 7 | const count = ref(0); 8 | const handleClick = () => { 9 | console.log("click"); 10 | count.value++; 11 | }; 12 | 13 | return { 14 | count, 15 | handleClick, 16 | }; 17 | }, 18 | 19 | render() { 20 | console.log(this.count); 21 | return h("div", {}, [ 22 | h("div", {}, String(this.count)), 23 | h("button", { onClick: this.handleClick }, "click"), 24 | ]); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/vue/example/setupStateRenderComponent/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/example/slotsComponent/App.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive } from "../../dist/mini-vue.esm-bundler.js"; 2 | import Child from "./Child.js"; 3 | 4 | export default { 5 | name: "App", 6 | setup() {}, 7 | 8 | render() { 9 | return h("div", {}, [ 10 | h("div", {}, "你好"), 11 | h( 12 | Child, 13 | { 14 | msg: "your name is child", 15 | }, 16 | { 17 | default: ({ age }) => [ 18 | h("p", {}, "我是通过 slot 渲染出来的第一个元素 "), 19 | h("p", {}, "我是通过 slot 渲染出来的第二个元素"), 20 | h("p", {}, `我可以接收到 age: ${age}`), 21 | ], 22 | } 23 | ), 24 | ]); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/vue/example/slotsComponent/Child.js: -------------------------------------------------------------------------------- 1 | import { h, ref, reactive, renderSlot } from "../../dist/mini-vue.esm-bundler.js"; 2 | export default { 3 | name: "Child", 4 | setup(props, context) {}, 5 | render() { 6 | return h("div", {}, [ 7 | h("div", {}, "child"), 8 | // renderSlot 会返回一个 vnode 9 | // 其本质和 h 是一样的 10 | // 第三个参数给出数据 11 | renderSlot(this.$slots, "default", { 12 | age: 16, 13 | }), 14 | ]); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/vue/example/slotsComponent/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/vue/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | // module.exports = require('./dist/mini-vue.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/mini-vue.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-vue", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "cuixiaorui", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@mini-vue/compiler-core": "workspace:^1.0.0", 14 | "@mini-vue/runtime-dom": "workspace:^1.0.0", 15 | "@mini-vue/shared": "workspace:^1.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/vue/src/index.ts: -------------------------------------------------------------------------------- 1 | // 这个文件充当 vue 模块 2 | import * as runtimeDom from "@mini-vue/runtime-dom"; 3 | import { registerRuntimeCompiler } from "@mini-vue/runtime-dom"; 4 | 5 | import { baseCompile } from "@mini-vue/compiler-core"; 6 | 7 | export * from "@mini-vue/runtime-dom"; 8 | 9 | 10 | function compileToFunction(template, options = {}) { 11 | const { code } = baseCompile(template, options); 12 | 13 | // 调用 compile 得到的代码在给封装到函数内, 14 | // 这里会依赖 runtimeDom 的一些函数,所以在这里通过参数的形式注入进去 15 | const render = new Function("Vue", code)(runtimeDom); 16 | 17 | return render; 18 | } 19 | 20 | registerRuntimeCompiler(compileToFunction); -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "@rollup/plugin-typescript"; 2 | import sourceMaps from "rollup-plugin-sourcemaps"; 3 | import resolve from "@rollup/plugin-node-resolve"; 4 | import commonjs from "@rollup/plugin-commonjs"; 5 | import replace from "@rollup/plugin-replace"; 6 | 7 | export default { 8 | input:"./packages/vue/src/index.ts", 9 | plugins: [ 10 | replace({ 11 | "process.env.NODE_ENV": JSON.stringify("development"), 12 | "process.env.VUE_ENV": JSON.stringify("browser"), 13 | "process.env.LANGUAGE": JSON.stringify(process.env.LANGUAGE), 14 | }), 15 | resolve(), 16 | commonjs(), 17 | typescript(), 18 | sourceMaps(), 19 | ], 20 | output: [ 21 | { 22 | format: "cjs", 23 | file: "./packages/vue/dist/mini-vue.cjs.js", 24 | sourcemap: true, 25 | }, 26 | { 27 | name: "vue", 28 | format: "es", 29 | file: "./packages/vue/dist/mini-vue.esm-bundler.js", 30 | sourcemap: true, 31 | }, 32 | ], 33 | onwarn: (msg, warn) => { 34 | // 忽略 Circular 的错误 35 | if (!/Circular/.test(msg)) { 36 | warn(msg); 37 | } 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "strict": true, 5 | "rootDir": ".", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "target": "es2016", 9 | "module": "esnext", 10 | "noImplicitAny": false, 11 | "removeComments": true, 12 | "preserveConstEnums": true, 13 | "sourceMap": true, 14 | "downlevelIteration": true, 15 | "lib": ["esnext", "DOM"], 16 | "types": ["jest"], 17 | "paths": { 18 | "@mini-vue/*": ["packages/*/src"] 19 | } 20 | }, 21 | "include": ["packages/*/src", "packages/*/__tests__"] 22 | } 23 | --------------------------------------------------------------------------------