🎉 事件总结

This commit is contained in:
bunny 2025-06-14 18:16:48 +08:00
parent afd04a456b
commit 5ca6962782
11 changed files with 851 additions and 0 deletions

View File

@ -0,0 +1,21 @@
## Vue2 模板语法详解
在 Vue2 中,使用双大括号 `{{}}` 的语法称为插值语法Mustache 语法),它用于在模板中显示数据。
### 基本用法
```html
<h3>你好:{{name}}</h3>
```
### 实现原理
当 Vue 实例创建时,会将 data 对象中的属性转换为 getter/setter使其成为响应式数据。当数据变化时视图会自动更新。
```javascript
new Vue({
el: "#app",
data: {
name: "Bunny", // 响应式数据
url: "https://v2.cn.vuejs.org/"
}
})
```

View File

@ -0,0 +1,95 @@
# Vue2 数据绑定
本示例展示了 Vue2 中的两种数据绑定方式:
- 单向数据绑定 (`v-bind`)
- 双向数据绑定 (`v-model`)
## 核心概念详解
### 1. 数据绑定类型
| 类型 | 指令 | 特点 | 示例 |
| -------- | --------- | ----------------- | ----------------------------- |
| 单向绑定 | `v-bind` | 数据→视图单向流动 | `<input v-bind:value="name">` |
| 双向绑定 | `v-model` | 数据⇄视图双向同步 | `<input v-model="name">` |
### 2. Vue 实例创建方式
```javascript
// 推荐写法 (ES6简写)
data() {
return {
name: "数据绑定"
}
}
// 等价于
data: function() {
return {
name: "数据绑定"
}
}
// 不推荐在组件中使用
data: {
name: "数据绑定"
}
```
### 3. 动态挂载机制
```javascript
// 延迟挂载示例
setTimeout(() => {
v.$mount("#app"); // 手动挂载到#app
}, 1000);
// 实例销毁与重建
setTimeout(() => {
v.$destroy(); // 销毁旧实例
const v2 = new Vue({ // 创建新实例
data: { name: "新数据绑定" }
});
v2.$mount("#root"); // 挂载到新位置
}, 2000);
```
1. **数据绑定选择**
- 表单元素使用 `v-model` 实现双向绑定
- 普通属性使用 `v-bind` 实现单向绑定
2. **实例管理**
- 避免在同一个元素上重复挂载不同实例
- 切换挂载目标时务必先销毁旧实例
3. **数据定义**
- 组件中必须使用函数形式返回data对象
- 根实例可以使用对象形式
4. **性能优化**
- 大量数据绑定时考虑使用计算属性
- 复杂场景可使用自定义指令优化
## 扩展说明
### 1. `v-model` 原理
`v-model` 本质上是语法糖,等价于:
```html
<input
:value="name"
@input="name = $event.target.value">
```
### 2. 挂载方式对比
| 方式 | 示例 | 适用场景 |
| ---------- | ------------------- | -------------------- |
| el选项 | `el: "#app"` | 初始化时确定挂载点 |
| $mount方法 | `vm.$mount("#app")` | 需要延迟或条件挂载时 |
### 3. 实例生命周期
- `new Vue()` 创建实例
- `$mount()` 触发挂载流程
- `$destroy()` 触发销毁流程
通过本文档,您可以全面了解 Vue2 的数据绑定机制和实例管理方法,为实际开发提供参考。

View File

@ -0,0 +1,76 @@
# `Object.defineProperty` 方法
## 一、基本概念
`Object.defineProperty()` 是 JavaScript 中用于直接在一个对象上定义一个新属性,或者修改一个对象的现有属性的方法。它允许精确控制属性的行为特性。
## 属性描述符详解
### 1. 数据描述符(已注释部分)
```javascript
{
value: 18, // 属性值
enumerable: true, // 是否可枚举for...in或Object.keys()
writable: true, // 是否可修改
configurable: true // 是否可删除或修改特性
}
```
### 2. 存取描述符(实际使用部分)
```javascript
{
get() {
// 读取属性时调用
return age;
},
set(value) {
// 设置属性时调用
age = value;
}
}
```
## 特性说明
| 特性 | 类型 | 默认值 | 描述 |
| ------------ | ---- | --------- | ------------------------ |
| configurable | 布尔 | false | 是否可删除属性或修改特性 |
| enumerable | 布尔 | false | 是否出现在枚举属性中 |
| value | 任意 | undefined | 属性值 |
| writable | 布尔 | false | 是否可被赋值运算符改变 |
| get | 函数 | undefined | 读取属性时调用的函数 |
| set | 函数 | undefined | 设置属性时调用的函数 |
## 使用场景
1. **实现数据响应式**如Vue2的核心实现
2. **创建私有属性**通过getter/setter控制访问
3. **属性访问拦截**(在读取或设置时执行额外操作)
4. **定义不可枚举属性**(如内置对象的一些方法)
## 注意事项
1. 数据描述符value, writable和存取描述符get, set不能同时使用
2. 默认情况下通过defineProperty添加的属性不可枚举、不可写、不可配置
3. 在严格模式下setter必须设置一个参数否则会抛出错误
4. getter不应有副作用如修改其他属性值
**实现简单响应式**
```javascript
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${val}`);
return val;
},
set(newVal) {
console.log(`设置 ${key}: ${newVal}`);
val = newVal;
}
});
}
const data = {};
defineReactive(data, 'message', 'Hello');
```

View File

@ -0,0 +1,104 @@
# JavaScript 数据代理实现文档
## 一、基本概念
数据代理是指通过一个对象(代理对象)间接访问和操作另一个对象(目标对象)的属性。本示例展示了如何使用 `Object.defineProperty` 实现简单的数据代理。
## 二、代码实现解析
```javascript
let obj1 = { x: 100 }; // 目标对象(被代理对象)
let obj2 = { y: 200 }; // 代理对象
// 在obj2上定义x属性的代理
Object.defineProperty(obj2, "x", {
get() {
return obj1.x; // 读取时返回obj1的x属性
},
set(value) {
obj1.x = value; // 设置时修改obj1的x属性
}
});
```
## 三、核心机制说明
1. **代理原理**
- 通过 `Object.defineProperty` 在代理对象obj2上定义新属性
- 使用 getter/setter 方法实现对目标对象obj1属性的间接访问
2. **访问流程**
- 读取 `obj2.x` → 触发 getter → 返回 `obj1.x` 的值
- 设置 `obj2.x` → 触发 setter → 将值赋给 `obj1.x`
3. **特性**
- 透明的属性访问(使用者无需知道代理存在)
- 可以在访问前后执行额外逻辑(如验证、日志等)
## 四、应用场景
| 场景 | 说明 | 示例 |
| -------- | -------------------- | ------------------------------ |
| 属性转发 | 跨对象访问属性 | 本示例实现 |
| 数据验证 | 设置属性前检查有效性 | setter中添加验证逻辑 |
| 访问控制 | 限制某些属性的访问 | getter中添加权限检查 |
| 日志记录 | 跟踪属性访问 | getter/setter中添加console.log |
## 五、扩展实现
### 1. 多属性代理
```javascript
function proxyProperties(target, source, props) {
props.forEach(prop => {
Object.defineProperty(target, prop, {
get() {
return source[prop];
},
set(value) {
source[prop] = value;
}
});
});
}
// 使用示例
proxyProperties(obj2, obj1, ['x', 'a', 'b']);
```
### 2. Vue2 风格的数据代理
```javascript
function observe(obj) {
const handler = {
get(target, prop) {
console.log(`读取 ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`设置 ${prop} 为 ${value}`);
target[prop] = value;
return true;
}
};
return new Proxy(obj, handler);
}
const observed = observe(obj1);
```
## 六、注意事项
1. **性能考虑**
- 每个代理属性都会增加访问开销
- 避免在性能关键路径上过度使用
2. **引用关系**
- 代理对象和目标对象保持独立
- 修改代理属性会影响原始对象
3. **枚举特性**
- 默认情况下代理属性不可枚举
- 需要显式设置 `enumerable: true`
4. **兼容性**
- `Object.defineProperty` 是 ES5 特性
- 现代开发可考虑使用 ES6 Proxy

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/vue@2.7.16.js"></script>
<title>Vue中的数据代理</title>
</head>
<body>
<div id="app">
Vue中的数据代理: <span>{{name}}</span>
</div>
</body>
<script>
const vm = new Vue({
el: "#app",
data: {
name: "代理..."
}
});
// 如果要查看可以使用 vm._data 看到name在_data中做了数据劫持
// 验证data==_data 可以将data定义到外部之后使用 vm._data == data 即可
// 通过 Object.defineProperty 对vm中对象进行映射只要修改了 vm._data 或者 vm.xxx 会触发双向绑定
</script>
</html>

View File

@ -0,0 +1,146 @@
# Vue2 数据代理机制详解
## 一、核心概念
Vue2 中的数据代理是指 Vue 实例vm代理其 `data` 对象中属性的访问和修改操作。通过这种机制,我们可以直接通过 `vm.xxx` 访问 `data` 中的属性,而不需要写 `vm._data.xxx`
## 二、代码示例解析
```html
<div id="app">
Vue中的数据代理: <span>{{name}}</span>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
name: "代理..."
}
});
</script>
```
## 三、实现原理
### 1. 数据存储结构
- 原始 `data` 对象被存储在 `vm._data` 属性中
- 通过 `Object.defineProperty``data` 的属性代理到 Vue 实例上
### 2. 验证方法
```javascript
// 验证代理关系
const externalData = { name: "代理..." };
const vm = new Vue({
el: "#app",
data: externalData
});
console.log(vm._data === externalData); // true
console.log(vm.name === vm._data.name); // true
```
### 3. 代理实现机制
Vue 内部大致执行了以下操作:
```javascript
// 伪代码展示代理原理
Object.keys(data).forEach(key => {
Object.defineProperty(vm, key, {
get() {
return vm._data[key];
},
set(value) {
vm._data[key] = value;
}
});
});
```
## 四、数据流图示
```
模板访问
vm.name (代理访问)
vm._data.name (实际数据存储)
数据劫持 (响应式系统)
```
## 五、关键特性
| 特性 | 说明 | 示例 |
| ---------- | ---------------------- | ------------------------------ |
| 直接访问 | 省略 `_data` 前缀 | `vm.name` 代替 `vm._data.name` |
| 响应式绑定 | 代理属性也是响应式的 | 修改 `vm.name` 会触发视图更新 |
| 数据隔离 | `_data` 保存原始数据 | 防止直接修改破坏响应式 |
| 统一入口 | 提供一致的数据访问方式 | 简化模板和方法的编写 |
## 六、与数据劫持的关系
1. **数据代理**
- 解决的是访问便捷性问题(`vm.xxx` vs `vm._data.xxx`
- 通过 `Object.defineProperty` 实现属性转发
2. **数据劫持**
- 解决的是响应式问题(数据变化触发视图更新)
- 在 `vm._data` 上实现,通过重写 getter/setter
3. **协作流程**
- 模板访问 `{{name}}` → 触发代理 getter
- 代理 getter 访问 `_data.name` → 触发劫持 getter
- 建立依赖收集关系
## 七、注意事项
1. **新增属性**
- 直接 `vm.newProp = value` 不会成为响应式
- 必须使用 `Vue.set` 或预先声明
2. **性能影响**
- 每个代理属性都会产生一定的内存开销
- 大型项目应考虑分模块管理数据
3. **调试技巧**
- 通过 `vm._data` 查看原始数据
- 使用 Vue Devtools 观察数据变化
4. **与 Vue3 区别**
- Vue3 使用 Proxy 实现更完善的代理
- 可以检测属性添加/删除操作
## 八、扩展应用
### 1. 自定义代理逻辑
```javascript
// 在Vue实例上添加自定义代理
created() {
Object.defineProperty(this, 'fullName', {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
const parts = value.split(' ');
this.firstName = parts[0];
this.lastName = parts[1] || '';
}
});
}
```
### 2. 代理模式应用
```javascript
// 实现组件间的数据代理
const proxyMixin = {
methods: {
proxy(prop) {
return {
get: () => this[prop],
set: (val) => this[prop] = val
};
}
}
};
```

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/vue@2.7.16.js"></script>
<title>事件处理</title>
</head>
<body>
<div id="app">
<h1>Vue2-事件处理</h1>
<button @click="showinfo1">函数不传参</button>
<button @click="showinfo2($event,66)">函数传参</button>
</div>
</body>
<script>
const vm = new Vue({
el: "#app",
data: {
},
methods: {
showinfo1() {
alert("函数不传参");
},
showinfo2(event, val) {
alert("函数传参")
console.log(event, val);
}
}
})
</script>
</html>

View File

@ -0,0 +1,158 @@
# Vue2 事件处理机制文档
## 一、基本概念
Vue2 提供了 `v-on` 指令(简写为 `@`)用于监听 DOM 事件并执行相应的 JavaScript 代码。本示例展示了 Vue2 中事件处理的基本用法,包括不传参和传参两种场景。
## 二、代码示例解析
```html
<div id="app">
<h1>Vue2-事件处理</h1>
<button @click="showinfo1">函数不传参</button>
<button @click="showinfo2($event,66)">函数传参</button>
</div>
<script>
const vm = new Vue({
el: "#app",
methods: {
showinfo1() {
alert("函数不传参");
},
showinfo2(event, val) {
alert("函数传参")
console.log(event, val);
}
}
})
</script>
```
## 三、核心特性说明
### 1. 事件绑定语法
| 语法形式 | 示例 | 说明 |
| -------- | ---------------------- | ----------------- |
| 完整写法 | `v-on:click="handler"` | 使用 `v-on:` 前缀 |
| 简写形式 | `@click="handler"` | 更简洁的写法 |
### 2. 方法定义位置
所有事件处理函数应定义在 Vue 实例的 `methods` 选项中:
```javascript
methods: {
handler1() { /* ... */ },
handler2() { /* ... */ }
}
```
### 3. 参数传递方式
| 场景 | 示例 | 说明 |
| -------------- | ------------------------------- | ------------------------------ |
| 不传参 | `@click="showinfo1"` | 自动传入事件对象 |
| 传参 | `@click="showinfo2($event,66)"` | 使用 `$event` 传递原生事件对象 |
| 仅传自定义参数 | `@click="showinfo2(66)"` | 事件对象将不可用 |
## 四、事件对象详解
### 1. 获取事件对象的方式
- **不传参时**:自动作为第一个参数传入
```javascript
methods: {
showinfo1(event) {
// event 可用
}
}
```
- **传参时需要**:必须显式传递 `$event`
```html
<button @click="showinfo2($event,66)">按钮</button>
```
### 2. 常用事件对象属性
| 属性/方法 | 类型 | 说明 |
| ----------------------- | -------- | -------------- |
| event.target | Element | 触发事件的元素 |
| event.currentTarget | Element | 绑定事件的元素 |
| event.preventDefault() | Function | 阻止默认行为 |
| event.stopPropagation() | Function | 停止事件冒泡 |
## 五、最佳实践建议
1. **方法命名**
- 使用动词开头(如 `handleClick`、`submitForm`
- 保持名称语义化
2. **参数处理**
- 需要事件对象时确保正确传递 `$event`
- 复杂参数建议使用对象形式:
```html
<button @click="handleClick({id: 1, name: 'test'})">按钮</button>
```
3. **方法组织**
- 相关功能的方法放在一起
- 大型组件可考虑按功能分模块
## 六、扩展应用
### 1. 事件修饰符
Vue 提供了特殊的事件修饰符:
```html
<!-- 阻止默认行为 -->
<form @submit.prevent="onSubmit"></form>
<!-- 停止事件冒泡 -->
<div @click.stop="doThis"></div>
<!-- 按键修饰符 -->
<input @keyup.enter="submit">
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThat"></button>
```
![image-20250614180522000](./images/image-20250614180522000.png)
### 2. 自定义事件
组件间通信时使用:
```javascript
// 子组件
this.$emit('my-event', payload);
// 父组件
<child-component @my-event="handleEvent"></child-component>
```
### 3. 原生事件绑定到组件
使用 `.native` 修饰符:
```html
<my-component @click.native="handleClick"></my-component>
```
## 七、注意事项
1. **避免内联计算**
```html
<!-- 不推荐 -->
<button @click="counter++">增加</button>
<!-- 推荐 -->
<button @click="increment">增加</button>
```
2. **this 指向**
- methods 中的方法自动绑定到 Vue 实例
- 避免使用箭头函数定义方法,会导致 this 指向错误
3. **性能考虑**
- 频繁触发的事件(如 scroll建议使用防抖/节流
- 大量事件监听应考虑事件委托

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/vue@2.7.16.js"></script>
<title>键盘事件</title>
</head>
<body>
<div id="app">
键盘事件:<input type="text" @keydown.huiche="showinfo">
</div>
</body>
<script>
Vue.config.keyCodes.huiche = 13
new Vue({
el: "#app",
data: {},
methods: {
showinfo() {
alert("showinfo")
}
}
})
</script>
</html>

View File

@ -0,0 +1,153 @@
# Vue2 键盘事件处理文档
## 一、基本概念
Vue2 提供了对键盘事件的支持,允许开发者监听特定的按键操作。本示例展示了如何自定义键盘事件别名并处理回车键事件。
## 二、代码示例解析
```html
<div id="app">
键盘事件:<input type="text" @keydown.huiche="showinfo">
</div>
<script>
// 自定义按键别名
Vue.config.keyCodes.huiche = 13; // 13是回车键的keyCode
new Vue({
el: "#app",
methods: {
showinfo() {
alert("回车键被按下");
}
}
});
</script>
```
## 三、核心机制说明
### 1. 键盘事件绑定语法
| 语法形式 | 示例 | 说明 |
| ---------- | --------------------------- | ------------------ |
| 基础写法 | `@keydown="handler"` | 监听所有按键 |
| 按键修饰符 | `@keydown.enter="handler"` | 仅监听回车键 |
| 自定义别名 | `@keydown.huiche="handler"` | 使用自定义按键别名 |
### 2. 按键修饰符类型
Vue 提供了这些常用内置按键别名:
- `.enter`
- `.tab`
- `.delete` (捕获"删除"和"退格"键)
- `.esc`
- `.space`
- `.up`
- `.down`
- `.left`
- `.right`
### 3. 自定义按键别名
通过 `Vue.config.keyCodes` 对象添加:
```javascript
// 语法
Vue.config.keyCodes.自定义名称 = 按键keyCode;
// 示例将F1键(112)别名为help
Vue.config.keyCodes.help = 112;
```
## 四、键盘事件对象
在方法中可以访问原生事件对象:
```javascript
methods: {
showinfo(event) {
console.log(event.key); // 按键名称(如'Enter'
console.log(event.keyCode); // 按键代码如13
console.log(event.target); // 触发事件的元素
}
}
```
## 五、最佳实践建议
1. **优先使用内置别名**
- 提高代码可读性
- 避免记忆keyCode数字
2. **复杂按键组合**
```html
<!-- Ctrl + Enter -->
<input @keydown.ctrl.enter="submit">
<!-- Alt + C -->
<input @keydown.alt.67="copy">
```
3. **系统修饰键**
- `.ctrl`
- `.alt`
- `.shift`
- `.meta` (Mac是Command键Windows是Windows键)
## 六、扩展应用
### 1. 自动聚焦示例
```html
<input @keyup.enter="nextInput" ref="currentInput">
methods: {
nextInput() {
this.$refs.nextInput.focus();
}
}
```
### 2. 表单提交优化
```html
<form @submit.prevent>
<input @keyup.enter="submit">
<button @click="submit">提交</button>
</form>
```
### 3. 游戏控制示例
```javascript
// 监听方向键控制游戏角色
created() {
window.addEventListener('keyup', this.handleKeyup);
},
methods: {
handleKeyup(e) {
switch(e.keyCode) {
case 37: this.moveLeft(); break;
case 38: this.moveUp(); break;
// ...
}
}
}
```
## 七、注意事项
1. **keyCode已废弃**
- 现代浏览器建议使用 `event.key` 代替
- Vue3 已移除对keyCode的支持
2. **输入法问题**
- 某些输入法下可能无法正确触发事件
- 中文输入时考虑使用 `@keyup` 代替 `@keydown`
3. **移动端适配**
- 虚拟键盘行为可能不一致
- 建议配合触摸事件使用
4. **事件冒泡**
- 使用 `.stop` 修饰符阻止冒泡
```html
<div @keydown.enter.stop="handleEnter">
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB