Compare commits
11 Commits
ba61fc3655
...
eb49928833
Author | SHA1 | Date |
---|---|---|
|
eb49928833 | |
|
5546635e31 | |
|
bfaaeb8843 | |
|
84e7108798 | |
|
39e7447e70 | |
|
926cb154cb | |
|
85c1ae88d5 | |
|
cdbdbec0f8 | |
|
466b18afed | |
|
61b71b3163 | |
|
dbf4e201ce |
|
@ -3,7 +3,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>模板语法</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>10-列表渲染</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>列表过滤</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>Vue监测数据的原理_数组</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<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>
|
||||
<script src="js/dayjs.min.js"></script>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<script src="../js/dayjs.min.js"></script>
|
||||
<title>过滤器</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<!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>
|
|
@ -0,0 +1,135 @@
|
|||
# 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中使用指令 |
|
|
@ -0,0 +1,44 @@
|
|||
<!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>
|
|
@ -0,0 +1,150 @@
|
|||
# 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>
|
||||
<meta charset="UTF-8">
|
||||
<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>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>Vue中的数据代理</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>事件处理</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>键盘事件</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>计算属性</title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<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>
|
||||
<script src="../js/vue@2.7.16.js"></script>
|
||||
<title>监视属性</title>
|
||||
</head>
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
|||
<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>
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<h2>学校。。。</h2>
|
||||
</template>
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<h2>学生</h2>
|
||||
</template>
|
|
@ -0,0 +1,13 @@
|
|||
<!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>
|
|
@ -0,0 +1,9 @@
|
|||
import App from "./App.vue";
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
template: "<App></App>",
|
||||
compontens: {
|
||||
App,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
.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?
|
|
@ -0,0 +1 @@
|
|||
shamefully-hoist=true
|
|
@ -0,0 +1,24 @@
|
|||
# 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/).
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"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
|
@ -0,0 +1,2 @@
|
|||
ignoredBuiltDependencies:
|
||||
- core-js
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,17 @@
|
|||
<!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>
|
|
@ -0,0 +1,23 @@
|
|||
<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.
After Width: | Height: | Size: 6.7 KiB |
|
@ -0,0 +1,10 @@
|
|||
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");
|
|
@ -0,0 +1,13 @@
|
|||
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;
|
|
@ -0,0 +1,41 @@
|
|||
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;
|
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<div class="demo">
|
||||
<h2>学校名称:{{ name }}</h2>
|
||||
<h2>学校地址:{{ address }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SchoolInfo",
|
||||
data() {
|
||||
return {
|
||||
name: "Bunny",
|
||||
address: "昆山市印象花园",
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,30 @@
|
|||
<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>
|
|
@ -0,0 +1,168 @@
|
|||
# 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()` 函数创建响应式引用
|
|
@ -0,0 +1,192 @@
|
|||
# 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参数替代 |
|
|
@ -0,0 +1,30 @@
|
|||
<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>
|
|
@ -0,0 +1,20 @@
|
|||
<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>
|
|
@ -0,0 +1,173 @@
|
|||
# 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支持 |
|
|
@ -0,0 +1,23 @@
|
|||
<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>
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div>
|
||||
<SchoolInfo />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SchoolInfo from "./components/SchoolInfo.vue";
|
||||
|
||||
export default {
|
||||
name: "Demo-3",
|
||||
components: { SchoolInfo },
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,12 @@
|
|||
export const test1 = {
|
||||
data() {
|
||||
return {
|
||||
username: "Bunny",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showMessage() {
|
||||
alert(`展示消息:${this.username}`);
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,178 @@
|
|||
# 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 | 需要额外配置 | 完美支持 |
|
||||
| 体积 | 较大 | 更轻量 |
|
|
@ -0,0 +1,44 @@
|
|||
<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>
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<Count />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Count from "./compontens/Count.vue";
|
||||
|
||||
export default {
|
||||
name: "Demo-4",
|
||||
components: {
|
||||
Count,
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
const { defineConfig } = require('@vue/cli-service')
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true
|
||||
})
|
|
@ -0,0 +1,9 @@
|
|||
<!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.
After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 276 B After Width: | Height: | Size: 276 B |
Loading…
Reference in New Issue