# hello_react
**Repository Path**: liu-jing-jing/hello_react
## Basic Information
- **Project Name**: hello_react
- **Description**: React Native Study
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2020-07-15
- **Last Updated**: 2025-05-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# hello_react
React 0.13运行在iOS 6的设备上无压力
#### 介绍
React Native Study
#### Vue3 架构
- script setup语法糖更方便的
- composition API重构页面
- 修改ref数据需要用.value++
- value很有用,避免丢失响应式
-
#### 代码解析
当然,我可以帮助你描述代码执行逻辑的思维导图。由于我无法直接绘制图表,我将使用文字描述来帮助你构建思维导图。你可以使用这一描述在思维导图软件中创建图表,如 XMind、MindMeister 或 MindNode。
### 主节点:代码执行逻辑
#### 子节点 1:初始化MVVM实例
1. 子节点 1.1
:构造函数
```
MVVM(options)
```
- **子节点 1.1.1**:保存传递的`data`和`methods`到实例属性
- **子节点 1.1.2**:调用`observe(this.data)`进行数据劫持
- **子节点 1.1.3**:遍历`data`属性,使用`Object.defineProperty`将其代理到实例上
- **子节点 1.1.4**:遍历`methods`属性,将方法直接绑定到实例上
- **子节点 1.1.5**:获取DOM元素并调用`compileNode`函数编译模板
#### 子节点 2:数据劫持(observe)
1. **子节点 2.1**:函数`observe(data)`
- **子节点 2.1.1**:检查数据是否为对象
- **子节点 2.1.2**:遍历对象属性并调用`defineReactive`
2. **子节点 2.2**:函数`defineReactive(obj, key, val)`
- **子节点 2.2.1**:递归调用`observe`处理嵌套对象
- **子节点 2.2.2**:创建依赖管理器`Dep`
- **子节点 2.2.3**:使用`Object.defineProperty`为属性定义getter和setter
- **子节点 2.2.4**:在getter中添加依赖收集
- **子节点 2.2.5**:在setter中触发依赖通知
#### 子节点 3:编译模板(compileNode)
1. **子节点 3.1**:函数`compileNode(node, vm)`
- **子节点 3.1.1**:遍历子节点并调用`compile`函数
2. **子节点 3.2**:函数`compile(node, vm)`
- **子节点 3.2.1**:检查节点类型
- 子节点 3.2.2
:处理元素节点
- **子节点 3.2.2.1**:遍历属性列表
- **子节点 3.2.2.2**:处理`v-model`指令
- **子节点 3.2.2.3**:处理`v-for`指令
- **子节点 3.2.2.4**:处理事件绑定指令(`v-bind:`或`@`)
- **子节点 3.2.2.5**:处理`v-show`指令
- 子节点 3.2.3
:处理文本节点
- **子节点 3.2.3.1**:匹配插值表达式
- **子节点 3.2.3.2**:创建并更新文本节点
#### 子节点 4:依赖管理(Dep和Watcher)
1. **子节点 4.1**:类`Dep`
- **子节点 4.1.1**:管理依赖的收集和通知
- **子节点 4.1.2**:方法`addSub`添加订阅者
- **子节点 4.1.3**:方法`notify`通知所有订阅者更新
2. **子节点 4.2**:类`Watcher`
- **子节点 4.2.1**:构造函数初始化观察者
- **子节点 4.2.2**:方法`get`收集依赖并获取当前值
- **子节点 4.2.3**:方法`update`比较新旧值并调用回调函数
#### 子节点 5:示例应用
1. **子节点 5.1**:数据和方法
```
javascript复制代码const vm = new MVVM({
el: 'app',
data: {
message: 'Hello World',
items: ['Item 1', 'Item 2', 'Item 3'],
isVisible: true
},
methods: {
addItem: function() {
this.items.push('New Item');
},
logMessage: function() {
console.log(this.message);
},
toggleVisibility: function() {
this.isVisible = !this.isVisible;
}
}
});
```
2. **子节点 5.2**:HTML模板
```
html复制代码
```
### 在思维导图软件中的结构
1. 代码执行逻辑
- 初始化MVVM实例
- 构造函数
```
MVVM(options)
```
- 保存`data`和`methods`
- 调用`observe(this.data)`
- 遍历`data`属性
- 遍历`methods`属性
- 获取DOM元素并调用`compileNode`
- 数据劫持(observe)
- 函数
```
observe(data)
```
- 检查数据是否为对象
- 遍历对象属性并调用`defineReactive`
- 函数
```
defineReactive(obj, key, val)
```
- 递归调用`observe`
- 创建`Dep`
- 使用`Object.defineProperty`
- 依赖收集
- 触发依赖通知
- 编译模板(compileNode)
- 函数
```
compileNode(node, vm)
```
- 遍历子节点并调用`compile`
- 函数
```
compile(node, vm)
```
- 检查节点类型
- 处理元素节点
- 遍历属性列表
- 处理`v-model`
- 处理`v-for`
- 处理事件绑定指令
- 处理`v-show`
- 处理文本节点
- 匹配插值表达式
- 创建并更新文本节点
- 依赖管理(Dep和Watcher)
- 类
```
Dep
```
- 管理依赖收集和通知
- 方法`addSub`
- 方法`notify`
- 类
```
Watcher
```
- 构造函数初始化
- 方法`get`
- 方法`update`
- 示例应用
- 数据和方法
- HTML模板
使用上述文字描述,你可以在思维导图软件中创建一个详细的图表来展示代码的执行逻辑。每个子节点可以展开进一步的细节,帮助你理解代码的工作原理。
#### MVVM模型的作用
`componentVm` 是组件的视图模型(ViewModel),用于存储组件的数据(data)和方法(methods)。在组件化系统中,我们需要将组件的数据和方法绑定到一个视图模型对象中,并在编译模板时使用该视图模型对象。这使得组件拥有自己的独立作用域,不会与其他组件或全局数据混淆。
具体来说,`componentVm` 的作用如下:
1. **存储组件的数据**:将组件的数据存储在 `componentVm` 中,以便在模板编译过程中能够访问和更新这些数据。
2. **绑定组件的方法**:将组件的方法绑定到 `componentVm` 上,以便在模板中调用这些方法时,能够正确地引用组件实例。
### 示例解释
假设有这样一个组件:
```
javascript复制代码registerComponent('my-component', {
template: `
{{ message }}
`,
data: function() {
return {
message: 'Hello from component'
};
},
methods: {
reverseMessage: function() {
this.message = this.message.split('').reverse().join('');
}
}
});
```
### 在编译阶段
编译函数 `compile` 会检测到 `my-component` 标签,然后执行以下操作:
1. **创建视图模型 `componentVm`**:
- 将组件的数据存储到 `componentVm` 中。
- 将组件的方法绑定到 `componentVm` 中。
2. **模板编译和替换**:
- 将组件的模板编译成 DOM 片段。
- 使用 `componentVm` 作为数据和方法的上下文编译模板。
- 将编译后的 DOM 片段替换原始的组件标签。
### 修改后的代码
为了更具体地展示 `componentVm` 的作用,这里是修改后的代码:
```
javascript复制代码function compile(node, vm) {
if (node.nodeType === 1) { // 元素节点
const tagName = node.tagName.toLowerCase();
if (components[tagName]) {
const component = components[tagName];
const data = typeof component.data === 'function' ? component.data() : component.data;
observe(data);
// 创建组件的视图模型
let componentVm = {};
for (const key in data) {
if (data.hasOwnProperty(key)) {
(function(componentVm, key, data) {
Object.defineProperty(componentVm, key, {
get: function() {
return data[key];
},
set: function(newVal) {
data[key] = newVal;
}
});
})(componentVm, key, data);
}
}
for (const key in component.methods) {
if (component.methods.hasOwnProperty(key)) {
componentVm[key] = component.methods[key].bind(componentVm);
}
}
// 编译组件的模板
const tempDiv = document.createElement('div');
tempDiv.innerHTML = component.template.trim();
const fragment = document.createDocumentFragment();
while (tempDiv.firstChild) {
fragment.appendChild(tempDiv.firstChild);
}
compileNode(fragment, componentVm);
// 用编译后的 DOM 替换组件标签
node.parentNode.replaceChild(fragment, node);
return;
}
// 处理其他指令
const attrs = node.attributes;
for (let i = 0; i < attrs.length; i++) {
const attrName = attrs[i].name;
const exp = attrs[i].value;
if (attrName === 'v-model') {
node.value = vm[exp];
node.addEventListener('input', (function(exp) {
return function(e) {
vm[exp] = e.target.value;
};
})(exp));
} else if (attrName === 'v-for') {
const parts = exp.split(' in ');
const item = parts[0].trim();
const arr = parts[1].trim();
node.removeAttribute('v-for');
const parentNode = node.parentNode;
const placeholder = document.createComment('v-for');
parentNode.insertBefore(placeholder, node);
parentNode.removeChild(node);
function renderList() {
const items = vm[arr];
while (parentNode.firstChild && parentNode.firstChild !== placeholder) {
parentNode.removeChild(parentNode.firstChild);
}
for (let i = 0; i < items.length; i++) {
const clone = node.cloneNode(true);
(function(clone, item, value) {
clone.textContent = clone.textContent.replace(/\{\{\s*item\s*\}\}/g, value);
parentNode.insertBefore(clone, placeholder);
})(clone, item, items[i]);
}
}
new Watcher(vm, arr, renderList);
renderList();
} else if (attrName.startsWith('v-bind:') || attrName.startsWith('@')) {
const eventType = attrName.startsWith('v-bind:') ? attrName.slice(7) : attrName.slice(1);
bindEvent(node, eventType, exp, vm);
} else if (attrName === 'v-show') {
(function(node, exp) {
function updateVisibility() {
node.style.display = vm[exp] ? '' : 'none';
}
updateVisibility();
new Watcher(vm, exp, updateVisibility);
})(node, exp);
}
}
} else if (node.nodeType === 3) { // 文本节点
const text = node.textContent;
const reg = /\{\{(.+?)\}\}/g;
const match = reg.exec(text);
if (match) {
(function(node, exp) {
const initialText = text;
function updateText() {
node.textContent = initialText.replace(reg, function(match, p1) {
return vm[p1.trim()];
});
}
updateText();
new Watcher(vm, exp, updateText);
})(node, match[1].trim());
}
}
}
```
### 总结
`componentVm` 的主要作用是创建一个独立的视图模型,用于存储和管理组件的数据和方法。在编译和渲染组件时,`componentVm` 作为上下文,使得组件的数据和方法能够被正确访问和调用,确保组件的独立性和作用域隔离。这样,我们就可以正确地处理组件的方法调用问题,例如 `reverseMessage is not a function` 这样的错误。
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 码云特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)