Compare commits
No commits in common. "eb49928833807fa817a3b9300ff853e3c0c52525" and "ba61fc3655b62f0f6aae20e6149b269dadf1e92f" have entirely different histories.
eb49928833
...
ba61fc3655
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 276 B After Width: | Height: | Size: 276 B |
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>模板语法</title>
|
<title>模板语法</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>10-列表渲染</title>
|
<title>10-列表渲染</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>列表过滤</title>
|
<title>列表过滤</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>Vue监测数据的原理_数组</title>
|
<title>Vue监测数据的原理_数组</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<script src="../js/dayjs.min.js"></script>
|
<script src="js/dayjs.min.js"></script>
|
||||||
<title>过滤器</title>
|
<title>过滤器</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
<!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">
|
|
||||||
<h2>当前值:<span v-text="num"></span></h2>
|
|
||||||
<h2>放大十倍值:<span v-big="num"></span></h2>
|
|
||||||
<button @click="num++">点击</button>
|
|
||||||
|
|
||||||
<input type="text" v-fbind="num" />
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const vm = new Vue({
|
|
||||||
el: "#app",
|
|
||||||
data: { num: 10 },
|
|
||||||
directives: {
|
|
||||||
// 指令所在模板被重新解析时
|
|
||||||
big(el, binding) {
|
|
||||||
el.innerText = binding.value * 10;
|
|
||||||
},
|
|
||||||
fbind: {
|
|
||||||
bind(el, binding) {
|
|
||||||
console.log("bind");
|
|
||||||
el.value = binding.value;
|
|
||||||
},
|
|
||||||
inserted(el, binding) {
|
|
||||||
console.log("inserted");
|
|
||||||
el.focus();
|
|
||||||
},
|
|
||||||
update(el, binding) {
|
|
||||||
console.log("update");
|
|
||||||
el.value = binding.value;
|
|
||||||
el.focus();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,135 +0,0 @@
|
||||||
# Vue2 自定义指令全面指南
|
|
||||||
|
|
||||||
指令中的作用域时window!!!
|
|
||||||
|
|
||||||
## 一、指令类型对比
|
|
||||||
|
|
||||||
| 类型 | 语法 | 触发时机 | 适用场景 |
|
|
||||||
| ------ | ------------------------------------- | ------------- | ----------- |
|
|
||||||
| 函数式 | `directives: { big(el, binding) {} }` | bind + update | 简单DOM操作 |
|
|
||||||
| 对象式 | `directives: { fbind: { hooks } }` | 完整生命周期 | 复杂交互 |
|
|
||||||
|
|
||||||
## 二、对象式指令详解
|
|
||||||
|
|
||||||
### 1. 完整生命周期钩子
|
|
||||||
```javascript
|
|
||||||
fbind: {
|
|
||||||
// 指令第一次绑定到元素时调用(初始化设置)
|
|
||||||
bind(el, binding) {
|
|
||||||
el.value = binding.value;
|
|
||||||
},
|
|
||||||
// 被绑定元素插入父节点时调用
|
|
||||||
inserted(el, binding) {
|
|
||||||
el.focus(); // 自动获取焦点
|
|
||||||
},
|
|
||||||
// 所在组件更新时调用
|
|
||||||
update(el, binding) {
|
|
||||||
el.value = binding.value;
|
|
||||||
el.focus(); // 值更新后重新聚焦
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 执行流程示例
|
|
||||||
1. 初始化时:`bind` → `inserted`
|
|
||||||
2. 数据更新时:`update`
|
|
||||||
3. 模板重新解析时:`update`
|
|
||||||
|
|
||||||
## 三、最佳实践建议
|
|
||||||
|
|
||||||
### 1. 输入框增强指令
|
|
||||||
```javascript
|
|
||||||
Vue.directive('input-enhanced', {
|
|
||||||
inserted(el) {
|
|
||||||
el.select(); // 自动选中文本
|
|
||||||
},
|
|
||||||
update(el) {
|
|
||||||
// 防抖处理避免频繁触发
|
|
||||||
clearTimeout(el._timer);
|
|
||||||
el._timer = setTimeout(() => {
|
|
||||||
el.select();
|
|
||||||
}, 300);
|
|
||||||
},
|
|
||||||
unbind(el) {
|
|
||||||
// 清理工作
|
|
||||||
clearTimeout(el._timer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 性能优化技巧
|
|
||||||
```javascript
|
|
||||||
update(el, binding) {
|
|
||||||
if (binding.value !== binding.oldValue) {
|
|
||||||
// 只有值变化时才执行操作
|
|
||||||
el.value = binding.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、高级应用场景
|
|
||||||
|
|
||||||
### 1. 权限控制指令
|
|
||||||
```javascript
|
|
||||||
Vue.directive('permission', {
|
|
||||||
inserted(el, binding) {
|
|
||||||
if (!checkPermission(binding.value)) {
|
|
||||||
el.parentNode.removeChild(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
使用方式:
|
|
||||||
```html
|
|
||||||
<button v-permission="'admin'">管理按钮</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 拖拽指令实现
|
|
||||||
```javascript
|
|
||||||
Vue.directive('drag', {
|
|
||||||
inserted(el) {
|
|
||||||
el.style.cursor = 'move';
|
|
||||||
el.onmousedown = function(e) {
|
|
||||||
// 拖拽逻辑实现
|
|
||||||
}
|
|
||||||
},
|
|
||||||
unbind(el) {
|
|
||||||
el.onmousedown = null; // 清理事件
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## 五、常见问题解决方案
|
|
||||||
|
|
||||||
1. **指令不生效问题排查**
|
|
||||||
- 检查指令名称是否使用kebab-case(短横线)
|
|
||||||
- 确认绑定值是否为响应式数据
|
|
||||||
- 验证是否在正确的生命周期钩子操作DOM
|
|
||||||
|
|
||||||
2. **动态指令参数**
|
|
||||||
```html
|
|
||||||
<div v-demo:[dynamicArg]="value"></div>
|
|
||||||
```
|
|
||||||
```javascript
|
|
||||||
binding.arg // 获取动态参数
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **多修饰符处理**
|
|
||||||
```javascript
|
|
||||||
if (binding.modifiers.large) {
|
|
||||||
el.style.fontSize = '20px';
|
|
||||||
}
|
|
||||||
if (binding.modifiers.red) {
|
|
||||||
el.style.color = 'red';
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、与Vue3的对比
|
|
||||||
|
|
||||||
| 特性 | Vue2 | Vue3 |
|
|
||||||
| --------- | ---------------------- | ------------------- |
|
|
||||||
| 钩子名称 | `bind` → `beforeMount` | |
|
|
||||||
| | `inserted` → `mounted` | |
|
|
||||||
| | 新增`beforeUpdate` | |
|
|
||||||
| 参数传递 | 通过`binding`对象 | 更直观的上下文参数 |
|
|
||||||
| 组合式API | 无 | 支持setup中使用指令 |
|
|
|
@ -1,44 +0,0 @@
|
||||||
<!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>VueComponent构造函数</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app">
|
|
||||||
<School />
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const School = Vue.extend({
|
|
||||||
name: "School",
|
|
||||||
template: `
|
|
||||||
<div>
|
|
||||||
<h2>学校名称:{{name}} </h2>
|
|
||||||
<h2>学校地址:{{address}} </h2>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: "Bunny",
|
|
||||||
address: "昆山县印象花园",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 类型是构造函数
|
|
||||||
// ƒ VueComponent(options) {
|
|
||||||
// this._init(options);
|
|
||||||
// }
|
|
||||||
// 每次调用 Vue.extend 返回都是全新的 VueComponent
|
|
||||||
console.log(School);
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: "#app",
|
|
||||||
components: { School },
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,150 +0,0 @@
|
||||||
# Vue2 组件系统深度解析
|
|
||||||
|
|
||||||
## 一、组件构造函数原理
|
|
||||||
|
|
||||||
### 1. Vue.extend 核心机制
|
|
||||||
```javascript
|
|
||||||
const School = Vue.extend({
|
|
||||||
name: "School",
|
|
||||||
template: `...`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: "Bunny",
|
|
||||||
address: "昆山县印象花园"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 每次调用返回全新的构造函数
|
|
||||||
console.log(School); // ƒ VueComponent(options) { this._init(options); }
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 组件实例化过程
|
|
||||||
1. **模板编译**:将template编译为render函数
|
|
||||||
2. **选项合并**:合并Vue全局配置和组件选项
|
|
||||||
3. **构造函数生成**:基于Vue基类创建子类
|
|
||||||
4. **实例化**:通过`new Component(options)`创建实例
|
|
||||||
|
|
||||||
## 二、组件注册方式对比
|
|
||||||
|
|
||||||
| 注册方式 | 语法 | 作用域 | 生命周期 |
|
|
||||||
| -------- | ----------------------- | -------------- | ------------ |
|
|
||||||
| 全局注册 | `Vue.component()` | 全应用可用 | 与根实例相同 |
|
|
||||||
| 局部注册 | `components`选项 | 当前组件作用域 | 独立生命周期 |
|
|
||||||
| 动态注册 | `Vue.extend()`+手动挂载 | 灵活控制 | 手动管理 |
|
|
||||||
|
|
||||||
## 三、组件核心特性详解
|
|
||||||
|
|
||||||
### 1. 组件选项合并策略
|
|
||||||
```javascript
|
|
||||||
const Parent = Vue.extend({
|
|
||||||
created() {
|
|
||||||
console.log('Parent Hook');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const Child = Parent.extend({
|
|
||||||
created() {
|
|
||||||
console.log('Child Hook');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 执行顺序:Parent Hook → Child Hook
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 组件隔离机制
|
|
||||||
- **独立作用域**:每个组件实例维护独立的数据副本
|
|
||||||
- **样式隔离**:通过scoped CSS实现
|
|
||||||
- **命名空间**:基于组件树结构的$parent/$children关系
|
|
||||||
|
|
||||||
### 3. 性能优化技巧
|
|
||||||
```javascript
|
|
||||||
// 1. 异步组件
|
|
||||||
const AsyncComp = () => ({
|
|
||||||
component: import('./MyComp.vue'),
|
|
||||||
loading: LoadingComp,
|
|
||||||
error: ErrorComp
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. 函数式组件
|
|
||||||
const FuncComp = {
|
|
||||||
functional: true,
|
|
||||||
render(h, context) {
|
|
||||||
// 无状态、无实例
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、高级开发模式
|
|
||||||
|
|
||||||
### 1. 动态组件工厂
|
|
||||||
```javascript
|
|
||||||
function createComponent(template, data) {
|
|
||||||
return Vue.extend({
|
|
||||||
template,
|
|
||||||
data() {
|
|
||||||
return {...data};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 组件继承扩展
|
|
||||||
```javascript
|
|
||||||
const BaseList = Vue.extend({
|
|
||||||
methods: {
|
|
||||||
fetchData() {
|
|
||||||
// 基础实现
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const UserList = BaseList.extend({
|
|
||||||
methods: {
|
|
||||||
fetchData() {
|
|
||||||
// 扩展实现
|
|
||||||
super.fetchData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 组件通信体系
|
|
||||||
| 通信方式 | 适用场景 | 示例 |
|
|
||||||
| -------------- | -------- | ------------------------------------------ |
|
|
||||||
| Props/Events | 父子组件 | `<Child :value="data" @input="handle">` |
|
|
||||||
| Provide/Inject | 跨级组件 | `provide: { value: 1 }, inject: ['value']` |
|
|
||||||
| Event Bus | 任意组件 | `Vue.prototype.$bus = new Vue()` |
|
|
||||||
| Vuex | 复杂状态 | 集中式状态管理 |
|
|
||||||
|
|
||||||
## 五、调试与问题排查
|
|
||||||
|
|
||||||
### 1. 组件实例检查
|
|
||||||
```javascript
|
|
||||||
mounted() {
|
|
||||||
console.log(this.$options.name); // 组件名
|
|
||||||
console.log(this.$parent); // 父实例
|
|
||||||
console.log(this.$children); // 子实例
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 常见问题解决方案
|
|
||||||
1. **模板不渲染**:
|
|
||||||
- 检查组件是否正确定义和注册
|
|
||||||
- 验证template/content是否有效
|
|
||||||
|
|
||||||
2. **数据不更新**:
|
|
||||||
- 确认data是否为函数返回新对象
|
|
||||||
- 检查props是否响应式
|
|
||||||
|
|
||||||
3. **生命周期异常**:
|
|
||||||
- 避免在beforeCreate访问data
|
|
||||||
- 确保异步操作在mounted后执行
|
|
||||||
|
|
||||||
## 六、Vue3对比说明
|
|
||||||
|
|
||||||
| 特性 | Vue2 | Vue3 |
|
|
||||||
| -------- | ----------- | --------------- |
|
|
||||||
| 组件定义 | Options API | Composition API |
|
|
||||||
| 继承机制 | Vue.extend | defineComponent |
|
|
||||||
| 性能优化 | 手动配置 | 自动优化 |
|
|
||||||
| 片段支持 | 单根节点 | 多根节点 |
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>数据绑定</title>
|
<title>数据绑定</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>Vue中的数据代理</title>
|
<title>Vue中的数据代理</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>事件处理</title>
|
<title>事件处理</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>键盘事件</title>
|
<title>键盘事件</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>计算属性</title>
|
<title>计算属性</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="../js/vue@2.7.16.js"></script>
|
<script src="js/vue@2.7.16.js"></script>
|
||||||
<title>监视属性</title>
|
<title>监视属性</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,20 +0,0 @@
|
||||||
<template>
|
|
||||||
<School />
|
|
||||||
<Student />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import School from "./compontens/School.vue";
|
|
||||||
import Student from "./compontens/Student.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "App",
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
compontens: {
|
|
||||||
School,
|
|
||||||
Student,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,3 +0,0 @@
|
||||||
<template>
|
|
||||||
<h2>学校。。。</h2>
|
|
||||||
</template>
|
|
|
@ -1,3 +0,0 @@
|
||||||
<template>
|
|
||||||
<h2>学生</h2>
|
|
||||||
</template>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>单文件内容</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
</body>
|
|
||||||
<script type="module" src="../../js/vue@2.7.16.js"></script>
|
|
||||||
<script type="module" src="./main.js"></script>
|
|
||||||
</html>
|
|
|
@ -1,9 +0,0 @@
|
||||||
import App from "./App.vue";
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: "#app",
|
|
||||||
template: "<App></App>",
|
|
||||||
compontens: {
|
|
||||||
App,
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,23 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/dist
|
|
||||||
|
|
||||||
|
|
||||||
# local env files
|
|
||||||
.env.local
|
|
||||||
.env.*.local
|
|
||||||
|
|
||||||
# Log files
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
|
@ -1 +0,0 @@
|
||||||
shamefully-hoist=true
|
|
|
@ -1,24 +0,0 @@
|
||||||
# demo2
|
|
||||||
|
|
||||||
## Project setup
|
|
||||||
```
|
|
||||||
pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
|
||||||
```
|
|
||||||
pnpm run serve
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and minifies for production
|
|
||||||
```
|
|
||||||
pnpm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Lints and fixes files
|
|
||||||
```
|
|
||||||
pnpm run lint
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customize configuration
|
|
||||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
|
|
@ -1,3 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
presets: ["@vue/cli-plugin-babel/preset"],
|
|
||||||
};
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"module": "esnext",
|
|
||||||
"baseUrl": "./",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"paths": {
|
|
||||||
"@/*": [
|
|
||||||
"src/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"lib": [
|
|
||||||
"esnext",
|
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"scripthost"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
{
|
|
||||||
"name": "demo2",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"serve": "vue-cli-service serve",
|
|
||||||
"build": "vue-cli-service build",
|
|
||||||
"lint": "vue-cli-service lint"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"core-js": "^3.8.3",
|
|
||||||
"vue": "^2.6.14",
|
|
||||||
"vuex": "3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.27.4",
|
|
||||||
"@babel/eslint-parser": "^7.12.16",
|
|
||||||
"@babel/preset-env": "^7.27.2",
|
|
||||||
"@vue/cli-plugin-babel": "~5.0.8",
|
|
||||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
|
||||||
"@vue/cli-service": "~5.0.0",
|
|
||||||
"eslint": "^7.32.0",
|
|
||||||
"eslint-plugin-vue": "^8.0.3",
|
|
||||||
"vue-template-compiler": "^2.6.14"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"plugin:vue/essential",
|
|
||||||
"eslint:recommended"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"parser": "@babel/eslint-parser"
|
|
||||||
},
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
"browserslist": [
|
|
||||||
"> 1%",
|
|
||||||
"last 2 versions",
|
|
||||||
"not dead"
|
|
||||||
],
|
|
||||||
"packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677"
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,2 +0,0 @@
|
||||||
ignoredBuiltDependencies:
|
|
||||||
- core-js
|
|
|
@ -1,17 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<template>
|
|
||||||
<div id="app">
|
|
||||||
<!-- <Demo1 />
|
|
||||||
<hr />
|
|
||||||
<Demo2 />
|
|
||||||
<hr />
|
|
||||||
<Demo3 /> -->
|
|
||||||
<Demo4 />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// import Demo1 from "@/views/demo1/index.vue";
|
|
||||||
// import Demo2 from "@/views/demo2/index.vue";
|
|
||||||
// import Demo3 from "@/views/demo3/index.vue";
|
|
||||||
import Demo4 from "@/views/demo4/index.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "App",
|
|
||||||
// components: { Demo1, Demo2, Demo3 },
|
|
||||||
components: { Demo4 },
|
|
||||||
};
|
|
||||||
</script>
|
|
Binary file not shown.
Before Width: | Height: | Size: 6.7 KiB |
|
@ -1,10 +0,0 @@
|
||||||
import Vue from "vue";
|
|
||||||
import App from "./App.vue";
|
|
||||||
import store from "./store";
|
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
render: (h) => h(App),
|
|
||||||
store,
|
|
||||||
}).$mount("#app");
|
|
|
@ -1,13 +0,0 @@
|
||||||
import Vue from "vue";
|
|
||||||
import Vuex from "vuex";
|
|
||||||
import count from "./modules/count";
|
|
||||||
Vue.use(Vuex);
|
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
|
||||||
state: {},
|
|
||||||
mutations: {},
|
|
||||||
actions: {},
|
|
||||||
modules: { count },
|
|
||||||
});
|
|
||||||
|
|
||||||
export default store;
|
|
|
@ -1,41 +0,0 @@
|
||||||
import Vue from "vue";
|
|
||||||
import Vuex from "vuex";
|
|
||||||
Vue.use(Vuex);
|
|
||||||
|
|
||||||
const count = {
|
|
||||||
namespaced: true,
|
|
||||||
state: { num: 1, sum: 0 },
|
|
||||||
actions: {
|
|
||||||
// 奇数相加
|
|
||||||
incrementOdd(context) {
|
|
||||||
// 可以从上下文中获取sum
|
|
||||||
if (context.state.sum % 2) {
|
|
||||||
context.commit("INCREMENT_ODD");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
incrementWait(context) {
|
|
||||||
setTimeout(() => {
|
|
||||||
context.commit("INCREMENT_WAIT");
|
|
||||||
}, 500);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
// 相加
|
|
||||||
INCREMENT(state) {
|
|
||||||
state.sum += state.num;
|
|
||||||
},
|
|
||||||
// 相减
|
|
||||||
DECREMENT(state) {
|
|
||||||
state.sum -= state.num;
|
|
||||||
},
|
|
||||||
// 奇数相加
|
|
||||||
INCREMENT_ODD(state) {
|
|
||||||
state.sum += state.num;
|
|
||||||
},
|
|
||||||
INCREMENT_WAIT(state) {
|
|
||||||
state.sum += state.num;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default count;
|
|
|
@ -1,18 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="demo">
|
|
||||||
<h2>学校名称:{{ name }}</h2>
|
|
||||||
<h2>学校地址:{{ address }}</h2>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "SchoolInfo",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: "Bunny",
|
|
||||||
address: "昆山市印象花园",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,30 +0,0 @@
|
||||||
<template>
|
|
||||||
<div id="app">
|
|
||||||
<h1 ref="title" v-text="message"></h1>
|
|
||||||
<button @click="ShowDom">输出上方DOM元素</button>
|
|
||||||
<SchoolInfo ref="schoolInfoRef" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SchoolInfo from "@/views/demo1/components/SchoolInfo.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Demo-1",
|
|
||||||
components: { SchoolInfo },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
message: "学习Vue2",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
ShowDom() {
|
|
||||||
// 获取当前Ref对象内容
|
|
||||||
console.log("@@@", this.$refs.title);
|
|
||||||
// 获取子组件对象实例
|
|
||||||
console.log("@@@", this.$refs.schoolInfoRef);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,168 +0,0 @@
|
||||||
# Vue2 中的 ref 引用机制详解
|
|
||||||
|
|
||||||
## 一、ref 的基本概念
|
|
||||||
|
|
||||||
`ref` 是 Vue2 提供的一种特殊的属性,用于直接访问 DOM 元素或子组件实例。它比传统的 `document.getElementById` 等方式更适合在 Vue 生态中使用。
|
|
||||||
|
|
||||||
## 二、代码解析
|
|
||||||
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<div id="app">
|
|
||||||
<!-- 绑定到DOM元素 -->
|
|
||||||
<h1 ref="title" v-text="message"></h1>
|
|
||||||
<button @click="ShowDom">输出上方DOM元素</button>
|
|
||||||
|
|
||||||
<!-- 绑定到子组件 -->
|
|
||||||
<SchoolInfo ref="schoolInfoRef" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SchoolInfo from "@/components/SchoolInfo.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "App",
|
|
||||||
components: { SchoolInfo },
|
|
||||||
data() {
|
|
||||||
return { message: "学习Vue2" };
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
ShowDom() {
|
|
||||||
// 访问DOM元素
|
|
||||||
console.log(this.$refs.title); // 输出: <h1>学习Vue2</h1>
|
|
||||||
|
|
||||||
// 访问子组件实例
|
|
||||||
console.log(this.$refs.schoolInfoRef); // 输出: SchoolInfo组件实例
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 三、ref 的核心特性
|
|
||||||
|
|
||||||
### 1. 绑定目标类型
|
|
||||||
|
|
||||||
| 绑定目标 | 获取内容 | 使用场景 |
|
|
||||||
| --------- | ---------------- | ----------------------- |
|
|
||||||
| DOM元素 | 原生DOM节点 | 直接操作DOM |
|
|
||||||
| 组件 | 组件实例 | 调用子组件方法/访问数据 |
|
|
||||||
| v-for元素 | DOM数组/组件数组 | 操作列表元素 |
|
|
||||||
|
|
||||||
### 2. 生命周期时机
|
|
||||||
|
|
||||||
- **创建**:在组件 `mounted` 钩子之后可用
|
|
||||||
- **更新**:响应式更新后自动同步
|
|
||||||
- **销毁**:组件销毁时自动解除引用
|
|
||||||
|
|
||||||
### 3. 访问方式
|
|
||||||
|
|
||||||
通过组件实例的 `$refs` 对象访问:
|
|
||||||
```javascript
|
|
||||||
this.$refs.refName
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、最佳实践
|
|
||||||
|
|
||||||
### 1. 命名规范
|
|
||||||
- 使用 camelCase 命名(如 `schoolInfoRef`)
|
|
||||||
- 避免使用保留字或Vue内置名称
|
|
||||||
- 保持命名语义化(如 `formRef`, `tableRef`)
|
|
||||||
|
|
||||||
### 2. 安全访问模式
|
|
||||||
```javascript
|
|
||||||
methods: {
|
|
||||||
submitForm() {
|
|
||||||
// 先检查是否存在
|
|
||||||
if (this.$refs.formRef) {
|
|
||||||
this.$refs.formRef.validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 动态ref(Vue 2.6+)
|
|
||||||
```html
|
|
||||||
<div v-for="item in list" :ref="`item_${item.id}`"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
methods: {
|
|
||||||
getItemRef(id) {
|
|
||||||
return this.$refs[`item_${id}`];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 五、高级用法
|
|
||||||
|
|
||||||
### 1. 组件通信
|
|
||||||
```javascript
|
|
||||||
// 父组件
|
|
||||||
this.$refs.childComp.doSomething();
|
|
||||||
|
|
||||||
// 子组件 SchoolInfo.vue
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
doSomething() {
|
|
||||||
// 子组件暴露的方法
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 表单验证示例
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<form ref="formRef">
|
|
||||||
<input v-model="form.name" required>
|
|
||||||
<button @click.prevent="validate">提交</button>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
validate() {
|
|
||||||
const form = this.$refs.formRef;
|
|
||||||
if (form.checkValidity()) {
|
|
||||||
// 表单验证通过
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 结合第三方库
|
|
||||||
```javascript
|
|
||||||
mounted() {
|
|
||||||
// 使用ref初始化第三方库
|
|
||||||
this.chart = new Chart(this.$refs.chartCanvas, {
|
|
||||||
// 配置项
|
|
||||||
});
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
// 清理工作
|
|
||||||
this.chart.destroy();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、注意事项
|
|
||||||
|
|
||||||
1. **响应式限制**:
|
|
||||||
- `$refs` 不是响应式的
|
|
||||||
- 避免在模板或计算属性中使用
|
|
||||||
|
|
||||||
2. **执行时机**:
|
|
||||||
- 在 `mounted` 之前访问会是 `undefined`
|
|
||||||
- 更新后需要等待 `$nextTick` 获取最新引用
|
|
||||||
|
|
||||||
3. **过度使用问题**:
|
|
||||||
- 优先考虑 props/events 通信
|
|
||||||
- 避免形成紧密耦合的组件关系
|
|
||||||
|
|
||||||
4. **与 Vue3 的区别**:
|
|
||||||
- Vue3 中 ref 需要从 `vue` 导入
|
|
||||||
- 组合式 API 中使用 `ref()` 函数创建响应式引用
|
|
|
@ -1,192 +0,0 @@
|
||||||
# Vue2 Props 属性传递机制详解
|
|
||||||
|
|
||||||
## 一、Props 的基本概念
|
|
||||||
|
|
||||||
Props 是 Vue 组件之间数据传递的主要方式,允许父组件向子组件传递数据。它具有明确的单向数据流特性(父组件 → 子组件)。
|
|
||||||
|
|
||||||
## 二、三种 Props 声明方式对比
|
|
||||||
|
|
||||||
### 1. 数组式声明(简单接收)
|
|
||||||
```javascript
|
|
||||||
props: ["name", "sex", "age"]
|
|
||||||
```
|
|
||||||
- **特点**:仅指定属性名
|
|
||||||
- **适用场景**:快速原型开发
|
|
||||||
- **限制**:无类型检查,无验证逻辑
|
|
||||||
|
|
||||||
### 2. 对象式声明(类型检查)
|
|
||||||
```javascript
|
|
||||||
props: {
|
|
||||||
name: String,
|
|
||||||
sex: String,
|
|
||||||
age: Number
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **特点**:添加基础类型检查
|
|
||||||
- **优势**:在开发模式下会发出警告
|
|
||||||
- **不足**:无法设置必填/默认值
|
|
||||||
|
|
||||||
### 3. 配置对象声明(完整配置)
|
|
||||||
```javascript
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true // 必须传入
|
|
||||||
},
|
|
||||||
sex: {
|
|
||||||
type: String,
|
|
||||||
default: "男🚹" // 默认值
|
|
||||||
},
|
|
||||||
age: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
validator: value => value >= 6 && value <= 60 // 自定义验证
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **完整功能**:
|
|
||||||
- 类型检查 (`type`)
|
|
||||||
- 必填标记 (`required`)
|
|
||||||
- 默认值 (`default`)
|
|
||||||
- 自定义验证 (`validator`)
|
|
||||||
|
|
||||||
## 三、核心特性详解
|
|
||||||
|
|
||||||
### 1. 类型系统支持
|
|
||||||
Vue 支持以下原生构造函数作为类型:
|
|
||||||
- `String`
|
|
||||||
- `Number`
|
|
||||||
- `Boolean`
|
|
||||||
- `Array`
|
|
||||||
- `Object`
|
|
||||||
- `Date`
|
|
||||||
- `Function`
|
|
||||||
- `Symbol`
|
|
||||||
|
|
||||||
也可以使用自定义构造函数:
|
|
||||||
```javascript
|
|
||||||
props: {
|
|
||||||
author: Person // 检查是否是Person的实例
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 默认值函数
|
|
||||||
当默认值是对象或数组时,必须使用工厂函数:
|
|
||||||
```javascript
|
|
||||||
props: {
|
|
||||||
config: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({ pageSize: 10 })
|
|
||||||
},
|
|
||||||
list: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 自定义验证器
|
|
||||||
```javascript
|
|
||||||
age: {
|
|
||||||
type: Number,
|
|
||||||
validator: value => {
|
|
||||||
return value >= 6 && value <= 60;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、最佳实践
|
|
||||||
|
|
||||||
### 1. 命名规范
|
|
||||||
- **Prop 名称**:使用 camelCase(JavaScript)
|
|
||||||
- **HTML 特性**:使用 kebab-case(DOM)
|
|
||||||
```html
|
|
||||||
<!-- 父组件模板 -->
|
|
||||||
<child-component user-name="张三"></child-component>
|
|
||||||
|
|
||||||
<!-- 子组件声明 -->
|
|
||||||
props: ['userName']
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 单向数据流原则
|
|
||||||
- **禁止**子组件直接修改 prop
|
|
||||||
- **正确做法**:
|
|
||||||
```javascript
|
|
||||||
// 子组件中使用data接收
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
localAge: this.age
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 或使用计算属性
|
|
||||||
computed: {
|
|
||||||
normalizedAge() {
|
|
||||||
return this.age + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 类型定义技巧
|
|
||||||
```javascript
|
|
||||||
// 使用PropType增强类型提示
|
|
||||||
import { PropType } from 'vue';
|
|
||||||
|
|
||||||
props: {
|
|
||||||
user: {
|
|
||||||
type: Object as PropType<User>,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 五、高级用法
|
|
||||||
|
|
||||||
### 1. 非Prop特性
|
|
||||||
未在props中声明的特性会自动绑定到组件的根元素上:
|
|
||||||
```html
|
|
||||||
<my-component data-test="123" class="extra"></my-component>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 同步修饰符
|
|
||||||
`.sync` 修饰符实现双向绑定(Vue 2.3+):
|
|
||||||
```html
|
|
||||||
<!-- 父组件 -->
|
|
||||||
<child :title.sync="pageTitle"></child>
|
|
||||||
|
|
||||||
<!-- 等价于 -->
|
|
||||||
<child :title="pageTitle" @update:title="pageTitle = $event"></child>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. v-model 整合
|
|
||||||
自定义组件实现 v-model:
|
|
||||||
```javascript
|
|
||||||
props: ['value'],
|
|
||||||
methods: {
|
|
||||||
updateValue(newVal) {
|
|
||||||
this.$emit('input', newVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、常见问题
|
|
||||||
|
|
||||||
1. **Prop未更新问题**:
|
|
||||||
- 确保父组件数据是响应式的
|
|
||||||
- 对象/数组需要确保是新的引用
|
|
||||||
|
|
||||||
2. **类型检查失败**:
|
|
||||||
- 开发模式下会输出警告
|
|
||||||
- 生产环境会静默失败
|
|
||||||
|
|
||||||
3. **性能优化**:
|
|
||||||
- 避免传递大型对象
|
|
||||||
- 必要时使用 `v-once` 冻结静态内容
|
|
||||||
|
|
||||||
## 七、与 Vue3 的区别
|
|
||||||
|
|
||||||
| 特性 | Vue2 | Vue3 |
|
|
||||||
| ---------- | ------------ | ----------------------- |
|
|
||||||
| 默认值函数 | 必须使用函数 | 可以直接赋值 |
|
|
||||||
| 类型定义 | PropType | 原生TypeScript支持 |
|
|
||||||
| 废弃语法 | .sync | 移除,用v-model参数替代 |
|
|
|
@ -1,30 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h2>学生姓名:{{ name }}</h2>
|
|
||||||
<h2>学习性别:{{ sex }}</h2>
|
|
||||||
<h2>学生年龄:{{ age + 1 }}</h2>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "SchoolInfo",
|
|
||||||
// 第一种传入方法---简单接受
|
|
||||||
// props: ["name", "sex", "age"],
|
|
||||||
|
|
||||||
// 第二种传入方法---接受同时对数据进行限制
|
|
||||||
// props: {
|
|
||||||
// name: String,
|
|
||||||
// sex: String,
|
|
||||||
// age: Number,
|
|
||||||
// },
|
|
||||||
props: {
|
|
||||||
name: { type: String, required: true /* 必须要传入的 */ },
|
|
||||||
sex: { type: String, default: "男🚹" /* 给一个默认值 */ },
|
|
||||||
age: { type: Number, required: true },
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<SchoolInfo name="Bunny1" sex="女🚺" :age="16" />
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<SchoolInfo name="Bunny2" sex="女🚺" :age="16" />
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<SchoolInfo name="Bunny3" :age="16" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SchoolInfo from "./components/SchoolInfo.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Demo-2",
|
|
||||||
components: { SchoolInfo },
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,173 +0,0 @@
|
||||||
# Vue2 Mixins 混入机制详解
|
|
||||||
|
|
||||||
## 一、基本概念
|
|
||||||
|
|
||||||
Mixins(混入)是 Vue2 中用于分发组件可复用功能的灵活方式。一个混入对象可以包含任意组件选项,当组件使用混入对象时,所有混入对象的选项将被"混合"进入组件本身的选项。
|
|
||||||
|
|
||||||
## 二、代码示例解析
|
|
||||||
|
|
||||||
### 1. 混入对象定义 (test1.js)
|
|
||||||
```javascript
|
|
||||||
export const test1 = {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
username: "Bunny" // 将被合并到组件data中
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showMessage() { // 将被合并到组件methods中
|
|
||||||
alert(`展示消息:${this.username}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 组件中使用混入 (SchoolInfo.vue)
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h2 @click="showMessage">学生姓名-showMessage:{{ username }}</h2>
|
|
||||||
<h2>学习性别:{{ sex }}</h2>
|
|
||||||
<h2>学生年龄:{{ age + 1 }}</h2>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { test1 } from "../mixins/test1";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "SchoolInfo",
|
|
||||||
mixins: [test1], // 注册混入
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
username: "Bunny", // 与混入data合并
|
|
||||||
sex: "🚺",
|
|
||||||
age: 16
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 三、合并策略详解
|
|
||||||
|
|
||||||
### 1. 选项合并规则
|
|
||||||
| 选项类型 | 合并策略 | 示例结果 |
|
|
||||||
| ------------ | ---------------------- | ---------------------------- |
|
|
||||||
| data | 递归合并,组件优先 | 同名属性以组件为准 |
|
|
||||||
| methods | 合并,组件优先 | 同名方法覆盖 |
|
|
||||||
| 生命周期钩子 | 合并为数组,混入先执行 | 都会调用,顺序: mixin → 组件 |
|
|
||||||
| 其他对象值 | 组件优先 | 如components, directives等 |
|
|
||||||
|
|
||||||
### 2. 合并过程示例
|
|
||||||
```javascript
|
|
||||||
// 混入data
|
|
||||||
{ username: "Bunny" }
|
|
||||||
|
|
||||||
// 组件data
|
|
||||||
{
|
|
||||||
username: "Bunny", // 同名属性被保留
|
|
||||||
sex: "🚺",
|
|
||||||
age: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最终合并结果
|
|
||||||
{
|
|
||||||
username: "Bunny", // 来自组件
|
|
||||||
sex: "🚺", // 来自组件
|
|
||||||
age: 16 // 来自组件
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、最佳实践
|
|
||||||
|
|
||||||
### 1. 命名规范
|
|
||||||
- 混入文件使用 `mixin-` 前缀(如 `mixin-logger.js`)
|
|
||||||
- 混入变量使用 `mixin` 后缀(如 `userMixin`)
|
|
||||||
- 避免与组件属性和方法重名
|
|
||||||
|
|
||||||
### 2. 使用建议
|
|
||||||
- **适合场景**:
|
|
||||||
- 多个组件共享相同逻辑
|
|
||||||
- 功能模块解耦
|
|
||||||
- 插件开发
|
|
||||||
|
|
||||||
- **避免场景**:
|
|
||||||
- 过度使用导致代码难以追踪
|
|
||||||
- 复杂的多层级混入
|
|
||||||
|
|
||||||
### 3. 全局混入
|
|
||||||
```javascript
|
|
||||||
// 谨慎使用!会影响所有Vue实例
|
|
||||||
Vue.mixin({
|
|
||||||
created() {
|
|
||||||
console.log('全局混入的created钩子');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## 五、高级用法
|
|
||||||
|
|
||||||
### 1. 自定义合并策略
|
|
||||||
```javascript
|
|
||||||
Vue.config.optionMergeStrategies.myOption = (parent, child, vm) => {
|
|
||||||
return child || parent;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 混入组合
|
|
||||||
```javascript
|
|
||||||
// featureMixin.js
|
|
||||||
export const featureMixin = {
|
|
||||||
methods: {
|
|
||||||
featureMethod() { /*...*/ }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 组件中使用多个混入
|
|
||||||
import { userMixin, featureMixin } from './mixins';
|
|
||||||
export default {
|
|
||||||
mixins: [userMixin, featureMixin]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 混入与插件结合
|
|
||||||
```javascript
|
|
||||||
// plugin.js
|
|
||||||
export default {
|
|
||||||
install(Vue) {
|
|
||||||
Vue.mixin({
|
|
||||||
mounted() {
|
|
||||||
console.log('来自插件的混入');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、常见问题
|
|
||||||
|
|
||||||
1. **属性冲突**:
|
|
||||||
- 数据对象:组件data优先
|
|
||||||
- 方法:组件methods优先
|
|
||||||
- 钩子函数:都会执行,混入的先执行
|
|
||||||
|
|
||||||
2. **调试技巧**:
|
|
||||||
```javascript
|
|
||||||
mounted() {
|
|
||||||
console.log(this.$options); // 查看合并后的选项
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **与Vue3的区别**:
|
|
||||||
- Vue3 使用 Composition API 替代混入
|
|
||||||
- 提供 `setup` 函数更好地组织逻辑
|
|
||||||
|
|
||||||
## 七、替代方案比较
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 |
|
|
||||||
| --------------- | -------------- | ---------------- |
|
|
||||||
| Mixins | 逻辑复用简单 | 命名冲突风险 |
|
|
||||||
| 高阶组件 | 明确props传递 | 组件嵌套层次深 |
|
|
||||||
| 插件 | 全局功能扩展 | 过度使用影响性能 |
|
|
||||||
| Composition API | 更好的代码组织 | 仅Vue3支持 |
|
|
|
@ -1,23 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h2 @click="showMessage">学生姓名-showMessage:{{ username }}</h2>
|
|
||||||
<h2>学习性别:{{ sex }}</h2>
|
|
||||||
<h2>学生年龄:{{ age + 1 }}</h2>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { test1 } from "../mixins/test1";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "SchoolInfo",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
username: "Bunny",
|
|
||||||
sex: "🚺",
|
|
||||||
age: 16,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mixins: [test1],
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,14 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<SchoolInfo />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SchoolInfo from "./components/SchoolInfo.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Demo-3",
|
|
||||||
components: { SchoolInfo },
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,12 +0,0 @@
|
||||||
export const test1 = {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
username: "Bunny",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showMessage() {
|
|
||||||
alert(`展示消息:${this.username}`);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,178 +0,0 @@
|
||||||
# Vuex 状态管理最佳实践指南
|
|
||||||
|
|
||||||
## 一、核心架构解析
|
|
||||||
|
|
||||||
### 1. 模块化结构
|
|
||||||
```javascript
|
|
||||||
// store/index.js - 主入口文件
|
|
||||||
import count from './modules/count'
|
|
||||||
|
|
||||||
export default new Vuex.Store({
|
|
||||||
modules: {
|
|
||||||
count // 启用命名空间的计数模块
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// store/modules/count.js - 计数模块
|
|
||||||
export default {
|
|
||||||
namespaced: true, // 启用命名空间
|
|
||||||
state: {
|
|
||||||
num: 1,
|
|
||||||
sum: 0
|
|
||||||
},
|
|
||||||
mutations: { /* 同步操作 */ },
|
|
||||||
actions: { /* 异步操作 */ }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 数据流示意图
|
|
||||||
```
|
|
||||||
组件 → dispatch → Actions → commit → Mutations → mutate → State → 响应式更新 → 组件
|
|
||||||
```
|
|
||||||
|
|
||||||
## 二、代码优化建议
|
|
||||||
|
|
||||||
### 1. 组件中的优化
|
|
||||||
```javascript
|
|
||||||
// 使用map辅助函数简化代码
|
|
||||||
import { mapActions, mapMutations, mapState } from 'vuex'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
...mapState('count', ['sum', 'num'])
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapMutations('count', ['INCREMENT', 'DECREMENT']),
|
|
||||||
...mapActions('count', ['incrementOdd', 'incrementWait'])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Store 中的优化
|
|
||||||
```javascript
|
|
||||||
// 使用常量替代字符串 mutation 类型
|
|
||||||
const types = {
|
|
||||||
INCREMENT: 'INCREMENT',
|
|
||||||
DECREMENT: 'DECREMENT'
|
|
||||||
}
|
|
||||||
|
|
||||||
const count = {
|
|
||||||
mutations: {
|
|
||||||
[types.INCREMENT](state) {
|
|
||||||
state.sum += state.num
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 三、最佳实践规范
|
|
||||||
|
|
||||||
### 1. 命名规范
|
|
||||||
| 类型 | 命名风格 | 示例 |
|
|
||||||
| -------- | ------------ | ----------------- |
|
|
||||||
| Mutation | 大写+下划线 | `INCREMENT` |
|
|
||||||
| Action | camelCase | `fetchData` |
|
|
||||||
| Getter | 形容词/名词 | `filteredList` |
|
|
||||||
| Module | 业务相关名词 | `user`, `product` |
|
|
||||||
|
|
||||||
### 2. 操作分离原则
|
|
||||||
- **Mutations**:
|
|
||||||
- 只做简单状态赋值
|
|
||||||
- 必须是同步函数
|
|
||||||
- 命名类似事件(`SET_USER_INFO`)
|
|
||||||
|
|
||||||
- **Actions**:
|
|
||||||
- 处理业务逻辑
|
|
||||||
- 可以包含异步操作
|
|
||||||
- 命名类似方法(`fetchUserData`)
|
|
||||||
|
|
||||||
### 3. 模块化建议
|
|
||||||
1. 按功能/业务划分模块
|
|
||||||
2. 每个模块启用命名空间
|
|
||||||
3. 模块内维护独立state
|
|
||||||
|
|
||||||
## 四、高级应用场景
|
|
||||||
|
|
||||||
### 1. 动态模块注册
|
|
||||||
```javascript
|
|
||||||
// 在需要时加载模块
|
|
||||||
store.registerModule('dynamicModule', {
|
|
||||||
// 模块定义
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 表单处理方案
|
|
||||||
```javascript
|
|
||||||
// 使用双向绑定计算属性
|
|
||||||
computed: {
|
|
||||||
num: {
|
|
||||||
get() { return this.$store.state.count.num },
|
|
||||||
set(value) { this.$store.commit('count/UPDATE_NUM', value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 插件开发
|
|
||||||
```javascript
|
|
||||||
// 持久化插件示例
|
|
||||||
const persistPlugin = store => {
|
|
||||||
store.subscribe((mutation, state) => {
|
|
||||||
localStorage.setItem('vuex_state', JSON.stringify(state))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 五、性能优化技巧
|
|
||||||
|
|
||||||
### 1. 大型状态树
|
|
||||||
- 使用模块懒加载
|
|
||||||
- 避免在state中存储大对象
|
|
||||||
|
|
||||||
### 2. 计算属性缓存
|
|
||||||
```javascript
|
|
||||||
getters: {
|
|
||||||
bigSum: state => {
|
|
||||||
// 复杂计算会被缓存
|
|
||||||
return heavyCalculation(state.sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 批量更新
|
|
||||||
```javascript
|
|
||||||
// 合并多个mutation
|
|
||||||
actions: {
|
|
||||||
bulkUpdate({ commit }, payload) {
|
|
||||||
commit('UPDATE_A', payload.a)
|
|
||||||
commit('UPDATE_B', payload.b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、常见问题解决方案
|
|
||||||
|
|
||||||
1. **热重载问题**:
|
|
||||||
```javascript
|
|
||||||
if (module.hot) {
|
|
||||||
module.hot.accept(['./modules/count'], () => {
|
|
||||||
store.hotUpdate({ modules: { count } })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **循环依赖**:
|
|
||||||
- 避免模块间互相引用
|
|
||||||
- 使用全局事件总线处理跨模块通信
|
|
||||||
|
|
||||||
3. **调试技巧**:
|
|
||||||
- 使用Vue Devtools追踪状态变化
|
|
||||||
- 添加logger插件记录mutation
|
|
||||||
|
|
||||||
## 七、与Vue3的Pinia对比
|
|
||||||
|
|
||||||
| 特性 | Vuex | Pinia |
|
|
||||||
| ---------- | ------------ | -------- |
|
|
||||||
| 语法 | 较复杂 | 更简洁 |
|
|
||||||
| 模块化 | 需要配置 | 自动支持 |
|
|
||||||
| TypeScript | 需要额外配置 | 完美支持 |
|
|
||||||
| 体积 | 较大 | 更轻量 |
|
|
|
@ -1,44 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>当前求和为:{{ $store.state.count.sum }}</h1>
|
|
||||||
<select v-model.number="$store.state.count.num">
|
|
||||||
<option :value="1">1</option>
|
|
||||||
<option :value="2">2</option>
|
|
||||||
<option :value="3">3</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<button @click="increment">+</button>
|
|
||||||
<button @click="decrement">-</button>
|
|
||||||
<button @click="incrementOdd">当前求和为奇数再加</button>
|
|
||||||
<button @click="incrementWait">等一等再加</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "CountInfo",
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
increment() {
|
|
||||||
this.$store.commit("count/INCREMENT");
|
|
||||||
},
|
|
||||||
decrement() {
|
|
||||||
this.$store.commit("count/DECREMENT");
|
|
||||||
},
|
|
||||||
incrementOdd() {
|
|
||||||
this.$store.dispatch("count/incrementOdd");
|
|
||||||
},
|
|
||||||
incrementWait() {
|
|
||||||
this.$store.dispatch("count/incrementWait");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
button {
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,14 +0,0 @@
|
||||||
<template>
|
|
||||||
<Count />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Count from "./compontens/Count.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Demo-4",
|
|
||||||
components: {
|
|
||||||
Count,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,4 +0,0 @@
|
||||||
const { defineConfig } = require('@vue/cli-service')
|
|
||||||
module.exports = defineConfig({
|
|
||||||
transpileDependencies: true
|
|
||||||
})
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Document</title>
|
|
||||||
</head>
|
|
||||||
<body></body>
|
|
||||||
</html>
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
Loading…
Reference in New Issue