1 vue2
1.1 vue双向绑定
01.示例
1.双向绑定
2.计算(computed):缓存问题
3.监听(watch):两种方式
4.生命周期
1.2 class样式与style样式
01.示例
1.class样式:变量、对象、数组
2.更改样式:单击事件
3.样式共存:传统使用与VUE使用
4.style样式:驼峰写法
1.3 条件渲染与列表渲染
01.示例
1.v-if与-show的区别
2.遍历(某个具体对像)
3.删除/更新(对象数组]
4.过滤(对象数组)
5.排序(对象数组)
1.4 事件的传播行为
01.示例
1.传递当前对象:$event
2.事件的传播行为:单击事件
3.事件的传播行为:超链接
4.事件的传播行为:键盘事件
1.5 表单输入绑定
01.示例
<body>
<div id="vue01">
<form action="" @submit.prevent="mysubmit">
用户名:<input type="text" v-model="user.username"><br>
密码:<input type="password" v-model="user.password"><br>
性别:
<input type="radio" value="male" v-model="user.sex">男
<input type="radio" value="female" v-model="user.sex">女<br>
兴趣:
<input type="checkbox" value="football" v-model="user.hobbies">足球
<input type="checkbox" value="basketball" v-model="user.hobbies">篮球
<input type="checkbox" value="pingpang" v-model="user.hobbies">乒乓球<br>
城市:
<select name="" id="" v-model="user.city">
<option :value="c.id" v-for="c in optionalCities">
{{c.name}}
</option>
</select><br>
<input type="submit" value="注册">
</form>
</div>
</body>
<head>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: "#vue01",
data:{
//对象
user:{
username: '',
password: '',
sex: '',
hobbies: [],
city: ''
},
//对象数组
optionalCities:[
{id:1, name:'xa'},
{id:2, name:'bj'},
{id:3, name:'sh'},
]
},
methods: {
mysubmit(){
console.log(JSON.stringify(this.user));
}
}
})
</script>
</head>
1.6 过渡效果与过滤器
01.示例
1.CSS过渡/动画
2.过滤器
1.7 自定义指令与自定义插件
01.示例
1.自定义指令
2.自定义插件
1.8 脚手架Vue CLI
01.示例
1.环境搭建
2.部署服务器
3.项目源码解读
4.ESlint代码规范工具
5.Vuex状态管理组件
场景一:共享变量c0unt打印
场景二:共享变量count自增
场景三:Action实现异步调用
场景四:getters计算共享变量
场景五:mapGetters、mapActions
1.9 vue-resource
01.示例
1.Get请求、Post请求
2.Jsoup跨域问题
3.Node.js读取文件:嵌套递归
4.Promisei读取文件:链式写法
1.10 组件
01.示例
1.全局组件
2.局部组件
3.内置组件:component
4.动态组件
5.组件的过渡效果
6.组件的参数传递
7.调用组件内部定义的方法
1.11 ref引用和render渲染函数
01.示例
1.ref引用
2.render渲染函数
2 vue3
2.1 创建应用
01.写法一
const { createApp, reactive, ref } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
const App = {
setup() {
// 加载中...
const loading = ref(true);
// 返回
return {
loading,
};
},
data() {
return {};
},
mounted() {},
watch: {},
methods: {},
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
02.写法2
onMounted:组件挂载后触发,更新 currentLifecycleHook 和 mountTime。
onUpdated:组件更新后触发,更新 currentLifecycleHook 和 updateTime。
onBeforeUnmount:组件卸载前触发,更新 currentLifecycleHook。
onUnmounted:组件卸载后触发,更新 currentLifecycleHook。
---------------------------------------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 生命周期钩子示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<style>
.section {
margin-bottom: 30px;
}
.form-group {
margin-bottom: 15px;
}
</style>
</head>
<body>
<div id="app">
<h2>Vue 3 生命周期钩子示例</h2>
<!-- 显示当前生命周期钩子 -->
<div class="section">
<h3>生命周期钩子</h3>
<p>{{ currentLifecycleHook }}</p>
</div>
<!-- 显示挂载的时间 -->
<div class="section">
<h3>组件挂载时间</h3>
<p>{{ mountTime }}</p>
</div>
<!-- 更新的时间 -->
<div class="section">
<h3>组件更新时间</h3>
<p>{{ updateTime }}</p>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- Vue 3 代码 -->
<script>
const { createApp, ref, onMounted, onUpdated, onBeforeUnmount, onUnmounted } = Vue;
const App = {
setup() {
// 当前生命周期钩子
const currentLifecycleHook = ref('');
// 组件挂载时间
const mountTime = ref('');
// 组件更新时间
const updateTime = ref('');
// 生命周期钩子示例
onMounted(() => {
currentLifecycleHook.value = '组件已挂载';
const now = new Date().toLocaleTimeString();
mountTime.value = `挂载时间: ${now}`;
});
onUpdated(() => {
currentLifecycleHook.value = '组件已更新';
const now = new Date().toLocaleTimeString();
updateTime.value = `更新时间: ${now}`;
});
onBeforeUnmount(() => {
currentLifecycleHook.value = '组件即将卸载';
});
onUnmounted(() => {
currentLifecycleHook.value = '组件已卸载';
});
// 返回数据
return {
currentLifecycleHook,
mountTime,
updateTime
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.2 模板语法
01.ref使用
vue2.x语法:出现在html
<div ref="mydiv">
普通元素div
</div>
<my-com ref="mycom"></my-com>
-----------------------------------------------------------------------------------------------------
this.$refs.mydiv.innerText;
this.$refs.mycom.mymsg;
this.$refs.mycom.myComponentShow();
vue3.x语法:出现在js
<script type="module">
import { createApp, ref } from 'vue'
createApp({
setup() {
// “ref”是用来存储值的响应式数据源。
// 理论上我们在展示该字符串的时候不需要将其包装在 ref() 中,
// 但是在下一个示例中更改这个值的时候,我们就需要它了。
const message = ref('Hello World!')
return {
message
}
}
}).mount('#app')
</script>
<div id="app">
<h1>{{ message }}</h1>
</div>
02.模板语法
文本插值
原始HTML
Attribute绑定
使用JavaScript表达式
指令Directives
03.附:内置指令
v-text:更新元素的文本内容
v-html:更新元素的 innerHTML
v-show:基于表达式值的真假性,来改变元素的可见性
v-if:基于表达式值的真假性,来条件性地渲染元素或者模板片段
v-else:表示 v-if 或 v-if / v-else-if 链式调用的“else 块”
v-else-if:表示 v-if 的“else if 块”。可以进行链式调用
v-for:基于原始数据多次渲染元素或模板块
V-on:给元素绑定事件监听器
v-bind:动态的绑定一个或多个 attribute,也可以是组件的 prop
v-model:在表单输入元素或组件上创建双向绑定
v-slot:用于声明具名插槽或是期望接收 props 的作用域插槽
v-pre:跳过该元素及其所有子元素的编译
v-once:仅渲染元素和组件一次,并跳过之后的更新
v-memo:期望的绑定值类型:any[]
v-cloak:用于隐藏尚未完成编译的 DOM 模板
2.3 响应式基础:ref、reactive
01.ref使用
<script setup>直接暴露全部属性+方法,即全部JS内容
ref该.value属性给予 Vue一个机会来检测ref何时被访问或修改,类似get、set
Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构
当修改了响应式状态时,DOM 会被自动更新。不管进行了多少次状态修改,每个组件都只会被更新一次
a.<script setup>直接暴露全部属性+方法,即全部JS内容
在 setup() 函数中手动暴露大量的状态和方法非常繁琐。
幸运的是,我们可以通过使用单文件组件 (SFC) 来避免这种情况。我们可以使用 <script setup> 来大幅度地简化代码:
<script setup> 中的顶层的导入、声明的变量和函数可在同一组件的模板中直接使用。
你可以理解为模板是在同一作用域内声明的一个 JavaScript 函数——它自然可以访问与它一起声明的所有内容。
b.为什么要使用 ref?
当你在模板中使用了一个 ref,然后改变了这个 ref 的值时,Vue 会自动检测到这个变化,并且相应地更新 DOM。
这是通过一个基于依赖追踪的响应式系统实现的。
当一个组件首次渲染时,Vue 会追踪在渲染过程中使用的每一个 ref。
然后,当一个 ref 被修改时,它会触发追踪它的组件的一次重新渲染。
-----------------------------------------------------------------------------------------------------
在标准的 JavaScript 中,检测普通变量的访问或修改是行不通的。
然而,我们可以通过 getter 和 setter 方法来拦截对象属性的 get 和 set 操作。
-----------------------------------------------------------------------------------------------------
该 .value 属性给予了 Vue 一个机会来检测 ref 何时被访问或修改。
在其内部,Vue 在它的 getter 中执行追踪,在它的 setter 中执行触发。
从概念上讲,你可以将 ref 看作是一个像这样的对象:
// 伪代码,不是真正的实现
const myRef = {
_value: 0,
get value() {
track()
return this._value
},
set value(newValue) {
this._value = newValue
trigger()
}
}
-----------------------------------------------------------------------------------------------------
另一个 ref 的好处是,与普通变量不同,你可以将 ref 传递给函数,
同时保留对最新值和响应式连接的访问。当将复杂的逻辑重构为可重用的代码时,这将非常有用。
c.深层响应性
Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构,比如 Map。
Ref 会使它的值具有深层响应性。这意味着即使改变嵌套对象或数组时,变化也会被检测到:
-----------------------------------------------------------------------------------------------------
import { ref } from 'vue'
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply() {
// 以下都会按照期望工作
obj.value.nested.count++
obj.value.arr.push('baz')
}
-----------------------------------------------------------------------------------------------------
非原始值将通过 reactive() 转换为响应式代理,该函数将在后面讨论。
也可以通过 shallow ref 来放弃深层响应性。对于浅层 ref,只有 .value 的访问会被追踪。
浅层 ref 可以用于避免对大型数据的响应性开销来优化性能、或者有外部库管理其内部状态的情况。
d.DOM 更新时机
当你修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。
Vue 会在“next tick”更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。
-----------------------------------------------------------------------------------------------------
要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API:
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 现在 DOM 已经更新了
}
02.reactive()使用
将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性
它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型
a.将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性
import { reactive } from 'vue'
const state = reactive({ count: 0 })
b.reactive() 的局限性
a.有限的值类型:
它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。
b.不能替换整个对象:
由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。
这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:
-------------------------------------------------------------------------------------------------
let state = reactive({ count: 0 })
// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })
c.对解构操作不友好:
当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
-------------------------------------------------------------------------------------------------
const state = reactive({ count: 0 })
// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)
-------------------------------------------------------------------------------------------------
由于这些限制,我们建议使用 ref() 作为声明响应式状态的主要 API。
03.总结
01.ref()
a.解释
ref() 是 Vue 3 提供的一个函数,用于创建一个包含单个值的响应式引用。
它通常用于简单类型(如数字、字符串、布尔值)或原始值。
b.示例
const count = ref(0);
count.value++; // 增加 count 的值
console.log(count.value); // 输出 1
c.面试回答示例
ref() 是 Vue 3 中用于创建一个包含单个值的响应式引用的函数。
当我们需要对一个简单的原始值(如数字、字符串、布尔值)进行响应式处理时,我们会使用 ref()。与 reactive() 不同的是,
ref() 返回一个对象,其中包含一个 value 属性来存储实际的值。
02.reactive()
a解释
reactive() 是 Vue 3 提供的一个函数,用于创建一个深层响应的对象。它通常用于复杂的对象结构,如数组或对象。
b.示例
const state = reactive({
count: 0
});
state.count++; // 增加 state.count 的值
console.log(state.count); // 输出 1
c.面试回答示例
reactive() 是 Vue 3 中用于创建一个深层响应对象的函数。
我们通常会用它来处理复杂的对象结构,如数组或嵌套对象。
与 ref() 不同,reactive() 会对对象的所有嵌套属性进行深度监听。
03.深层响应性
a.解释
深层响应性指的是 Vue 3 能够监听并响应对象内部嵌套属性的变化,而不仅仅是对象的顶层属性。
b.示例
const state = reactive({
nested: {
count: 0
}
});
state.nested.count++; // 增加 state.nested.count 的值
console.log(state.nested.count); // 输出 1
c.面试回答示例
深层响应性是指 Vue 3 可以监听并响应对象内部嵌套属性的变化。
使用 reactive() 创建的对象会自动具有深层响应性,这意味着对对象内部嵌套属性的任何更改都会被追踪并触发视图更新。
这使得管理复杂的状态变得更加方便。
04.Reactive Proxy 与原始对象的区别
a.解释
在 Vue 3 中,使用 reactive() 创建的对象实际上是一个 Proxy 对象,
它拦截对原始对象的操作以实现响应性。Proxy 对象与原始对象是不同的实体,但它们共享同一数据。
b.示例
const state = reactive({ count: 0 });
const original = state;
state.count++;
console.log(state.count); // 输出 1
console.log(original.count); // 输出 1
c.面试回答示例
在 Vue 3 中,使用 reactive() 创建的对象实际上是一个 Proxy 对象。
这个 Proxy 对象拦截对原始对象的操作以实现响应性。
虽然 Proxy 和原始对象是不同的实体,但它们共享同一个数据源。
这意味着对 Proxy 对象的操作会反映在原始对象上,反之亦然。
04.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue3 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue3 示例</h1>
<div>
<button @click="showRefExample">声明响应式状态: ref()</button>
<button @click="showReactiveExample">声明响应式状态: reactive()</button>
<button @click="showDeepReactiveExample">深层响应性</button>
<button @click="showReactiveVsOriginalExample">Reactive Proxy vs. Original</button>
</div>
<div v-if="message">{{ message }}</div>
</div>
</body>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script>
const {createApp, reactive, ref} = Vue;
const {ElMessage, ElMessageBox} = ElementPlus;
const App = {
setup() {
// 加载中...
const loading = ref(true);
const message = ref('');
// 示例:声明响应式状态: ref()
const showRefExample = () => {
const count = ref(0);
count.value++;
message.value = `ref 示例: ${count.value}`;
};
// 示例:声明响应式状态: reactive()
const showReactiveExample = () => {
const state = reactive({
count: 0
});
state.count++;
message.value = `reactive 示例: ${state.count}`;
};
// 示例:深层响应性
const showDeepReactiveExample = () => {
const state = reactive({
nested: {
count: 0
}
});
state.nested.count++;
message.value = `深层响应性 示例: ${state.nested.count}`;
};
// 示例:Reactive Proxy vs. Original
const showReactiveVsOriginalExample = () => {
const state = reactive({
count: 0
});
const original = state;
state.count++;
message.value = `Reactive Proxy vs. Original 示例: reactive = ${state.count}, original = ${original.count}`;
};
return {
loading,
message,
showRefExample,
showReactiveExample,
showDeepReactiveExample,
showReactiveVsOriginalExample
};
},
data() {
return {
};
},
mounted() {
},
watch: {
},
methods: {
},
};
createApp(App).use(ElementPlus,{locale: ElementPlusLocaleZhCn}).mount("#app");
</script>
</html>
2.4 核心:Class与Style绑定
01.总结
a.绑定HTML class
a.解释
Vue3 提供了一种绑定 HTML class 的方式,可以根据条件动态地应用 class。
b.示例
const isActive = ref(true);
const classObject = reactive({
active: isActive.value,
inactive: !isActive.value
});
const toggleClass = () => {
isActive.value = !isActive.value;
classObject.active = isActive.value;
classObject.inactive = !isActive.value;
};
b.绑定内联样式
a.解释
Vue3 允许使用对象语法或数组语法动态绑定内联样式。
b.示例
const styleObject = reactive({
color: isActive.value ? 'red' : 'blue',
fontSize: '20px'
});
const toggleStyle = () => {
isActive.value = !isActive.value;
styleObject.color = isActive.value ? 'red' : 'blue';
};
02.代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue3 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
.active {
color: red;
}
.inactive {
color: blue;
}
</style>
</head>
<body>
<div id="app">
<h1>Vue3 示例</h1>
<div>
<button @click="toggleClass">绑定HTML class</button>
<button @click="toggleStyle">绑定内联样式</button>
</div>
<div :class="classObject">这个 div 的 class 会根据条件变化</div>
<div :style="styleObject">这个 div 的样式会根据条件变化</div>
</div>
</body>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script>
const { createApp, reactive, ref } = Vue;
const App = {
setup() {
const isActive = ref(true);
// 示例:绑定HTML class
const classObject = reactive({
active: isActive.value,
inactive: !isActive.value
});
const toggleClass = () => {
isActive.value = !isActive.value;
classObject.active = isActive.value;
classObject.inactive = !isActive.value;
};
// 示例:绑定内联样式
const styleObject = reactive({
color: isActive.value ? 'red' : 'blue',
fontSize: '20px'
});
const toggleStyle = () => {
isActive.value = !isActive.value;
styleObject.color = isActive.value ? 'red' : 'blue';
};
return {
classObject,
styleObject,
toggleClass,
toggleStyle
};
}
};
createApp(App).use(ElementPlus).mount("#app");
</script>
</html>
2.5 核心:计算属性
01.总结
a.基础示例
a.解释
计算属性是基于其依赖项缓存的。在依赖项发生改变之前,它们不会重新计算。
b.示例
const count = ref(1);
const double = computed(() => count.value * 2);
message.value = `基础示例: ${double.value}`;
b.计算属性缓存 vs 方法
a.解释
计算属性是基于其依赖项缓存的,而方法在每次调用时都会执行计算。
b.示例
const count = ref(1);
const double = computed(() => count.value * 2);
const doubleMethod = () => count.value * 2;
const start = performance.now();
for (let i = 0; i < 100000; i++) {
double.value;
}
const computedTime = performance.now() - start;
const startMethod = performance.now();
for (let i = 0; i < 100000; i++) {
doubleMethod();
}
const methodTime = performance.now() - startMethod;
message.value = `计算属性缓存 vs 方法: 计算属性时间 = ${computedTime}ms, 方法时间 = ${methodTime}ms`;
c.可写计算属性
a.解释
计算属性不仅可以读取,还可以写入。通过提供 get 和 set 函数,我们可以创建可写的计算属性。
b.示例
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue) => {
const names = newValue.split(' ');
firstName.value = names[0];
lastName.value = names[1];
}
});
fullName.value = 'Jane Smith';
message.value = `可写计算属性: ${fullName.value}`;
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue3 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue3 示例</h1>
<div>
<button @click="showBasicExample">基础示例</button>
<button @click="showCachedVsMethodsExample">计算属性缓存vs方法</button>
<button @click="showWritableComputedExample">可写计算属性</button>
</div>
<div>{{ result }}</div>
</div>
</body>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script>
const { createApp, reactive, ref, computed } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
const App = {
setup() {
const message = ref('');
// 示例:基础示例
const showBasicExample = () => {
const count = ref(1);
const double = computed(() => count.value * 2);
message.value = `基础示例: ${double.value}`;
};
// 示例:计算属性缓存vs方法
const showCachedVsMethodsExample = () => {
const count = ref(1);
const double = computed(() => count.value * 2);
const doubleMethod = () => count.value * 2;
const start = performance.now();
for (let i = 0; i < 100000; i++) {
double.value;
}
const computedTime = performance.now() - start;
const startMethod = performance.now();
for (let i = 0; i < 100000; i++) {
doubleMethod();
}
const methodTime = performance.now() - startMethod;
message.value = `计算属性缓存 vs 方法: 计算属性时间 = ${computedTime}ms, 方法时间 = ${methodTime}ms`;
};
// 示例:可写计算属性
const showWritableComputedExample = () => {
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue) => {
const names = newValue.split(' ');
firstName.value = names[0];
lastName.value = names[1];
}
});
fullName.value = 'Jane Smith';
message.value = `可写计算属性: ${fullName.value}`;
};
return {
result: message,
showBasicExample,
showCachedVsMethodsExample,
showWritableComputedExample
};
},
data() {
return {
}
},
mounted() {
},
watch: {
},
methods: {
},
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app");
</script>
</html>
2.6 核心:侦听器
01.总结
a.基本示例
a.解释
基本示例展示了如何使用watch侦听简单的响应式数据变化。
b.示例
const count = ref(0);
watch(count, (newValue, oldValue) => {
message.value = `基本示例: ${oldValue} -> ${newValue}`;
});
count.value++;
b.深层侦听器
a.解释
深层侦听器用于侦听嵌套对象内部的变化。
b.示例
const state = reactive({ nested: { count: 0 } });
watch(() => state.nested, (newValue, oldValue) => {
message.value = `深层侦听器: ${JSON.stringify(oldValue)} -> ${JSON.stringify(newValue)}`;
}, { deep: true });
state.nested.count++;
c.即时回调的侦听器
a.解释
即时回调的侦听器会在侦听器创建时立即执行回调函数。
b.示例
const count = ref(0);
watch(count, (newValue, oldValue) => {
message.value = `即时回调的侦听器: ${oldValue} -> ${newValue}`;
}, { immediate: true });
count.value++;
d.一次性侦听器
a.解释
一次性侦听器在第一次侦听到变化后会自动停止侦听。
b.示例
const count = ref(0);
const stop = watch(count, (newValue, oldValue) => {
message.value = `一次性侦听器: ${oldValue} -> ${newValue}`;
stop();
});
count.value++;
e.watchEffect
a.解释
watchEffect会立即执行传入的回调函数,并在响应式依赖发生变化时重新运行。
b.示例
const count = ref(0);
watchEffect(() => {
message.value = `watchEffect: ${count.value}`;
});
count.value++;
f.回调的触发时机
a.解释
在watch回调函数中,可以通过onCleanup注册清理函数,该清理函数会在依赖变化前执行。
b.示例
const count = ref(0);
watch(count, (newValue, oldValue, onCleanup) => {
onCleanup(() => {
message.value = `回调的触发时机: Cleanup before newValue = ${newValue}`;
});
message.value = `回调的触发时机: ${oldValue} -> ${newValue}`;
});
count.value++;
g.停止侦听器
a.解释
可以通过调用返回的stop函数手动停止侦听器。
b.示例
const count = ref(0);
const stop = watch(count, (newValue, oldValue) => {
message.value = `停止侦听器: ${oldValue} -> ${newValue}`;
});
stop();
count.value++;
02.代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue3 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue3 示例</h1>
<div>
<button @click="showBasicExample">基本示例</button>
<button @click="showDeepWatchExample">深层侦听器</button>
<button @click="showImmediateWatchExample">即时回调的侦听器</button>
<button @click="showOnceWatchExample">一次性侦听器</button>
<button @click="showWatchEffectExample">watchEffect()</button>
<button @click="showTriggerTimingExample">回调的触发时机</button>
<button @click="showStopWatchExample">停止侦听器</button>
</div>
<div>{{ message }}</div>
</div>
</body>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script>
const { createApp, reactive, ref, watch, watchEffect } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
const App = {
setup() {
const message = ref('');
// 基本示例
const showBasicExample = () => {
const count = ref(0);
watch(count, (newValue, oldValue) => {
message.value = `基本示例: ${oldValue} -> ${newValue}`;
});
count.value++;
};
// 深层侦听器
const showDeepWatchExample = () => {
const state = reactive({ nested: { count: 0 } });
watch(() => state.nested, (newValue, oldValue) => {
message.value = `深层侦听器: ${JSON.stringify(oldValue)} -> ${JSON.stringify(newValue)}`;
}, { deep: true });
state.nested.count++;
};
// 即时回调的侦听器
const showImmediateWatchExample = () => {
const count = ref(0);
watch(count, (newValue, oldValue) => {
message.value = `即时回调的侦听器: ${oldValue} -> ${newValue}`;
}, { immediate: true });
count.value++;
};
// 一次性侦听器
const showOnceWatchExample = () => {
const count = ref(0);
const stop = watch(count, (newValue, oldValue) => {
message.value = `一次性侦听器: ${oldValue} -> ${newValue}`;
stop();
});
count.value++;
};
// watchEffect
const showWatchEffectExample = () => {
const count = ref(0);
watchEffect(() => {
message.value = `watchEffect: ${count.value}`;
});
count.value++;
};
// 回调的触发时机
const showTriggerTimingExample = () => {
const count = ref(0);
watch(count, (newValue, oldValue, onCleanup) => {
onCleanup(() => {
message.value = `回调的触发时机: Cleanup before newValue = ${newValue}`;
});
message.value = `回调的触发时机: ${oldValue} -> ${newValue}`;
});
count.value++;
};
// 停止侦听器
const showStopWatchExample = () => {
const count = ref(0);
const stop = watch(count, (newValue, oldValue) => {
message.value = `停止侦听器: ${oldValue} -> ${newValue}`;
});
stop();
count.value++;
};
return {
message,
showBasicExample,
showDeepWatchExample,
showImmediateWatchExample,
showOnceWatchExample,
showWatchEffectExample,
showTriggerTimingExample,
showStopWatchExample
};
},
data() {
return {
}
},
mounted() {
},
watch: {
},
methods: {
},
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app");
</script>
</html>
2.7 核心:列表渲染
01.总结
1.基本 v-for 使用:在 <ul> 中展示 items 数组。
2.v-for 与对象:在 <ul> 中展示 objectItems 对象的键值对。
3.在 v-for 里使用范围值:展示 itemsWithIndex 数组中的索引和项名。
4.<template> 上的 v-for:使用 <template> 渲染 items。
5.v-for 与 v-if:展示 items 中 id 为偶数的项。
6.通过 key 管理状态:展示 items 并显示 key。
7.组件上使用 v-for:渲染自定义组件 MyItem 列表。
8.数组变化侦测:使用按钮动态添加项到 items。
9.展示过滤或排序后的结果:展示排序后的 items
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<style>
.item {
margin-bottom: 10px;
}
.filtered-item {
color: red;
}
.section {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div id="app">
<h2>v-for 示例</h2>
<!-- 1. 基本 v-for 使用 -->
<div class="section">
<h3>1. 基本 v-for 使用</h3>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</div>
<!-- 2. v-for 与对象 -->
<div class="section">
<h3>2. v-for 与对象</h3>
<ul>
<li v-for="(value, key) in objectItems" :key="key">{{ key }}: {{ value }}</li>
</ul>
</div>
<!-- 3. 在 v-for 里使用范围值 -->
<div class="section">
<h3>3. 在 v-for 里使用范围值</h3>
<ul>
<li v-for="(item, index) in itemsWithIndex" :key="index">{{ index }}: {{ item.name }}</li>
</ul>
</div>
<!-- 4. <template> 上的 v-for -->
<div class="section">
<h3>4. template 上的 v-for</h3>
<template v-for="item in items">
<p :key="item.id">{{ item.name }}</p>
</template>
</div>
<!-- 5. v-for 与 v-if -->
<div class="section">
<h3>5. v-for 与 v-if</h3>
<ul>
<li v-for="item in items" :key="item.id" v-if="true">{{ item.name }}</li>
</ul>
</div>
<!-- 6. 通过 key 管理状态 -->
<div class="section">
<h3>6. 通过 key 管理状态</h3>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }} - Key: {{ item.id }}</li>
</ul>
</div>
<!-- 7. 组件上使用 v-for -->
<div class="section">
<h3>7. 组件上使用 v-for</h3>
<my-item v-for="item in items" :key="item.id" :item="item"></my-item>
</div>
<!-- 8. 数组变化侦测 -->
<div class="section">
<h3>8. 数组变化侦测</h3>
<button @click="addItem">添加项</button>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</div>
<!-- 9. 展示过滤或排序后的结果 -->
<div class="section">
<h3>9. 展示过滤或排序后的结果</h3>
<button @click="sortItems">排序</button>
<ul>
<li v-for="item in sortedItems" :key="item.id" class="filtered-item">{{ item.name }}</li>
</ul>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- Vue 3 代码 -->
<script>
const { createApp, ref, computed } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
// 组件
const MyItem = {
props: ['item'],
template: '<div class="item">{{ item.name }}</div>'
};
// 应用
const App = {
components: {
MyItem
},
setup() {
// 1. 基本 v-for 示例数据
const items = ref([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
]);
// 2. v-for 与对象示例数据
const objectItems = ref({
key1: 'Value 1',
key2: 'Value 2',
key3: 'Value 3'
});
// 3. 在 v-for 里使用范围值示例数据
const itemsWithIndex = ref([
{ name: 'Item A' },
{ name: 'Item B' },
{ name: 'Item C' }
]);
// 8. 数组变化侦测示例数据
const addItem = () => {
items.value.push({ id: items.value.length + 1, name: `Item ${items.value.length + 1}` });
};
// 9. 展示过滤或排序后的结果示例数据
const sortedItems = computed(() => {
return [...items.value].sort((a, b) => a.name.localeCompare(b.name));
});
const sortItems = () => {
// 排序的逻辑已在 computed 属性中处理
};
// 返回数据
return {
items,
objectItems,
itemsWithIndex,
addItem,
sortedItems,
sortItems
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.8 核心:条件渲染
01.总结
1.v-if 示例:显示或隐藏一个消息。
2.v-else 示例:显示条件下的不同消息。
3.v-else-if 示例:根据不同的状态显示不同的消息。
4.<template> 上的 v-if 示例:在 <template> 上使用 v-if 渲染内容。
5.v-show 示例:使用 v-show 来显示或隐藏内容。
6.v-if vs. v-show:比较 v-if 和 v-show 的不同之处。
7.v-if 和 v-for 示例:在 v-for 循环中结合使用 v-if。
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 条件渲染示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<style>
.section {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div id="app">
<h2>Vue 3 条件渲染示例</h2>
<!-- 1. v-if 示例 -->
<div class="section">
<h3>1. v-if 示例</h3>
<p v-if="showMessage">这是一个 v-if 示例。</p>
<button @click="toggleMessage">切换消息显示</button>
</div>
<!-- 2. v-else 示例 -->
<div class="section">
<h3>2. v-else 示例</h3>
<p v-if="showElseMessage">这是 v-if 部分的消息。</p>
<p v-else>这是 v-else 部分的消息。</p>
<button @click="toggleElseMessage">切换消息显示</button>
</div>
<!-- 3. v-else-if 示例 -->
<div class="section">
<h3>3. v-else-if 示例</h3>
<p v-if="currentState === 'state1'">状态 1</p>
<p v-else-if="currentState === 'state2'">状态 2</p>
<p v-else>其他状态</p>
<button @click="changeState('state1')">状态 1</button>
<button @click="changeState('state2')">状态 2</button>
<button @click="changeState('other')">其他状态</button>
</div>
<!-- 4. <template> 上的 v-if 示例 -->
<div class="section">
<h3>4. <template> 上的 v-if 示例</h3>
<template v-if="templateCondition">
<p>这是一个 <template> 上的 v-if 示例。</p>
</template>
<button @click="toggleTemplateCondition">切换模板条件</button>
</div>
<!-- 5. v-show 示例 -->
<div class="section">
<h3>5. v-show 示例</h3>
<p v-show="show">这是一个 v-show 示例。</p>
<button @click="toggleShowVisibility">切换显示</button>
</div>
<!-- 6. v-if vs. v-show -->
<div class="section">
<h3>6. v-if vs. v-show</h3>
<p v-if="showIf">v-if:这是显示的内容。</p>
<p v-show="showShow">v-show:这是显示的内容。</p>
<button @click="toggleIfVisibility">切换 v-if</button>
<button @click="toggleShowVisibility">切换 v-show</button>
</div>
<!-- 7. v-if 和 v-for -->
<div class="section">
<h3>7. v-if 和 v-for 示例</h3>
<ul>
<li v-for="item in items" :key="item.id" >{{ item.isVisible }}</li>
</ul>
<button @click="toggleItemVisibility">切换项的可见性</button>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- Vue 3 代码 -->
<script>
const { createApp, ref } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
// 应用
const App = {
setup() {
// 1. v-if 示例
const showMessage = ref(true);
const toggleMessage = () => {
showMessage.value = !showMessage.value;
};
// 2. v-else 示例
const showElseMessage = ref(true);
const toggleElseMessage = () => {
showElseMessage.value = !showElseMessage.value;
};
// 3. v-else-if 示例
const currentState = ref('state1');
const changeState = (state) => {
currentState.value = state;
};
// 4. <template> 上的 v-if 示例
const templateCondition = ref(true);
const toggleTemplateCondition = () => {
templateCondition.value = !templateCondition.value;
};
// 5. v-show 示例
const show = ref(true);
const toggleShowVisibility = () => {
show.value = !show.value;
};
// 6. v-if vs. v-show
const showIf = ref(true);
const showShow = ref(true);
const toggleIfVisibility = () => {
showIf.value = !showIf.value;
};
// 7. v-if 和 v-for 示例
const items = ref([
{ id: 1, name: 'Item 1', isVisible: true },
{ id: 2, name: 'Item 2', isVisible: true },
{ id: 3, name: 'Item 3', isVisible: true }
]);
const toggleItemVisibility = () => {
items.value.forEach(item => {
item.isVisible = !item.isVisible;
});
};
// 返回数据
return {
showMessage,
toggleMessage,
showElseMessage,
toggleElseMessage,
currentState,
changeState,
templateCondition,
toggleTemplateCondition,
show,
toggleShowVisibility,
showIf,
showShow,
toggleIfVisibility,
items,
toggleItemVisibility
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.9 核心:表单绑定
01.总结
a.基本用法:展示了如何使用 v-model 绑定到一个普通输入框。
文本:处理单行文本输入。
多行文本:处理 textarea 多行文本输入。
复选框:演示如何使用 v-model 绑定复选框。
单选按钮:演示如何使用 v-model 绑定单选按钮。
选择器:展示了如何使用 v-model 绑定 select 下拉选择器。
b.值绑定:演示了 .lazy 修饰符的用法,它只在输入框失去焦点时更新数据。
复选框(组):处理多个复选框的绑定。
单选按钮(组):处理多个单选按钮的绑定。
选择器选项:演示如何动态渲染 select 选项。
c.修饰符:展示了 .lazy, .number, .trim 修饰符的用法。
d.组件上的 v-model:演示如何在自定义组件中使用 v-model 进行双向绑定。
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 表单示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<style>
.section {
margin-bottom: 30px;
}
.form-group {
margin-bottom: 15px;
}
</style>
</head>
<body>
<div id="app">
<h2>Vue 3 表单示例</h2>
<!-- 1. 基本用法 -->
<div class="section">
<h3>1. 基本用法</h3>
<div class="form-group">
<label for="basic-input">基本输入框:</label>
<input id="basic-input" v-model="basicInput" type="text" placeholder="输入文本" />
<p>输入值: {{ basicInput }}</p>
</div>
</div>
<!-- 2. 文本 -->
<div class="section">
<h3>2. 文本</h3>
<div class="form-group">
<label for="text-input">文本输入框:</label>
<input id="text-input" v-model="textInput" type="text" placeholder="输入文本" />
<p>文本值: {{ textInput }}</p>
</div>
</div>
<!-- 3. 多行文本 -->
<div class="section">
<h3>3. 多行文本</h3>
<div class="form-group">
<label for="textarea">多行文本:</label>
<textarea id="textarea" v-model="textarea" placeholder="输入多行文本"></textarea>
<p>多行文本: {{ textarea }}</p>
</div>
</div>
<!-- 4. 复选框 -->
<div class="section">
<h3>4. 复选框</h3>
<div class="form-group">
<label>
<input type="checkbox" v-model="checked" />
选中状态: {{ checked }}
</label>
</div>
</div>
<!-- 5. 单选按钮 -->
<div class="section">
<h3>5. 单选按钮</h3>
<div class="form-group">
<label>
<input type="radio" value="Option 1" v-model="radio" />
选项 1
</label>
<label>
<input type="radio" value="Option 2" v-model="radio" />
选项 2
</label>
<p>选中的选项: {{ radio }}</p>
</div>
</div>
<!-- 6. 选择器 -->
<div class="section">
<h3>6. 选择器</h3>
<div class="form-group">
<label for="select">选择器:</label>
<select id="select" v-model="selected">
<option disabled value="">请选择一个选项</option>
<option value="Option A">选项 A</option>
<option value="Option B">选项 B</option>
<option value="Option C">选项 C</option>
</select>
<p>选中的选项: {{ selected }}</p>
</div>
</div>
<!-- 7. 值绑定 -->
<div class="section">
<h3>7. 值绑定</h3>
<div class="form-group">
<label for="value-binding">绑定值:</label>
<input id="value-binding" v-model.lazy="valueBinding" type="text" placeholder="输入文本" />
<p>绑定值: {{ valueBinding }}</p>
</div>
</div>
<!-- 8. 复选框(组) -->
<div class="section">
<h3>8. 复选框(组)</h3>
<div class="form-group">
<label v-for="option in checkboxOptions" :key="option.value">
<input type="checkbox" :value="option.value" v-model="checkedOptions" />
{{ option.label }}
</label>
<p>选中的选项: {{ checkedOptions }}</p>
</div>
</div>
<!-- 9. 单选按钮(组) -->
<div class="section">
<h3>9. 单选按钮(组)</h3>
<div class="form-group">
<label v-for="option in radioOptions" :key="option.value">
<input type="radio" :value="option.value" v-model="radioGroup" />
{{ option.label }}
</label>
<p>选中的选项: {{ radioGroup }}</p>
</div>
</div>
<!-- 10. 选择器选项 -->
<div class="section">
<h3>10. 选择器选项</h3>
<div class="form-group">
<label for="select-options">选择器选项:</label>
<select id="select-options" v-model="selectOptions">
<option v-for="option in selectOptionsList" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
<p>选中的选项: {{ selectOptions }}</p>
</div>
</div>
<!-- 11. 修饰符 -->
<div class="section">
<h3>11. 修饰符</h3>
<div class="form-group">
<label for="lazy-input">.lazy 修饰符:</label>
<input id="lazy-input" v-model.lazy="lazyInput" type="text" placeholder="输入文本" />
<p>懒加载值: {{ lazyInput }}</p>
</div>
<div class="form-group">
<label for="number-input">.number 修饰符:</label>
<input id="number-input" v-model.number="numberInput" type="text" placeholder="输入数字" />
<p>数字值: {{ numberInput }}</p>
</div>
<div class="form-group">
<label for="trim-input">.trim 修饰符:</label>
<input id="trim-input" v-model.trim="trimInput" type="text" placeholder="输入文本" />
<p>去除空格值: "{{ trimInput }}"</p>
</div>
</div>
<!-- 12. 组件上的 v-model -->
<div class="section">
<h3>12. 组件上的 v-model</h3>
<custom-input v-model="componentValue"></custom-input>
<p>组件绑定值: {{ componentValue }}</p>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- Vue 3 代码 -->
<script>
const { createApp, ref } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
// 自定义组件
const CustomInput = {
props: ['modelValue'],
template: `
<div>
<input :value="modelValue" @input="$emit('update:modelValue', $event)" />
</div>
`
};
// 应用
const App = {
components: { CustomInput },
setup() {
// 1. 基本用法
const basicInput = ref('');
// 2. 文本
const textInput = ref('');
// 3. 多行文本
const textarea = ref('');
// 4. 复选框
const checked = ref(false);
// 5. 单选按钮
const radio = ref('Option 1');
// 6. 选择器
const selected = ref('');
// 7. 值绑定
const valueBinding = ref('');
// 8. 复选框(组)
const checkboxOptions = ref([
{ label: '选项 1', value: 'option1' },
{ label: '选项 2', value: 'option2' },
{ label: '选项 3', value: 'option3' }
]);
const checkedOptions = ref([]);
// 9. 单选按钮(组)
const radioOptions = ref([
{ label: '选项 A', value: 'A' },
{ label: '选项 B', value: 'B' },
{ label: '选项 C', value: 'C' }
]);
const radioGroup = ref('A');
// 10. 选择器选项
const selectOptionsList = ref([
{ label: '选项 X', value: 'X' },
{ label: '选项 Y', value: 'Y' },
{ label: '选项 Z', value: 'Z' }
]);
const selectOptions = ref('X');
// 11. 修饰符
const lazyInput = ref('');
const numberInput = ref(0);
const trimInput = ref('');
// 12. 组件上的 v-model
const componentValue = ref('');
// 返回数据
return {
basicInput,
textInput,
textarea,
checked,
radio,
selected,
valueBinding,
checkboxOptions,
checkedOptions,
radioOptions,
radioGroup,
selectOptionsList,
selectOptions,
lazyInput,
numberInput,
trimInput,
componentValue
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.10 核心:事件处理
01.总结
1.监听事件:演示如何监听事件并处理。
2.内联事件处理器:在模板内直接设置事件处理器。
3.方法事件处理器:使用方法作为事件处理器。
4.在内联处理器中调用方法:在事件处理器内调用方法并传递参数。
5.在内联事件处理器中访问事件参数:访问事件对象的属性。
6.事件修饰符:使用 .stop 和 .prevent 修饰符来控制事件行为。
7.按键修饰符:处理按键事件,例如 .enter。
8.鼠标按键修饰符:处理鼠标按键事件,例如 .right。
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 事件处理示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<style>
.section {
margin-bottom: 30px;
}
.form-group {
margin-bottom: 15px;
}
</style>
</head>
<body>
<div id="app">
<h2>Vue 3 事件处理示例</h2>
<!-- 1. 监听事件 -->
<div class="section">
<h3>1. 监听事件</h3>
<button @click="handleClick">点击我</button>
<p>{{ message }}</p>
</div>
<!-- 2. 内联事件处理器 -->
<div class="section">
<h3>2. 内联事件处理器</h3>
<button @click="message = '内联事件处理器触发'">点击我(内联处理器)</button>
<p>{{ message }}</p>
</div>
<!-- 3. 方法事件处理器 -->
<div class="section">
<h3>3. 方法事件处理器</h3>
<button @click="updateMessage('方法事件处理器触发')">点击我(方法处理器)</button>
<p>{{ message }}</p>
</div>
<!-- 4. 在内联处理器中调用方法 -->
<div class="section">
<h3>4. 在内联处理器中调用方法</h3>
<button @click="methodWithParam('内联处理器调用方法')">点击我(调用方法)</button>
<p>{{ message }}</p>
</div>
<!-- 5. 在内联事件处理器中访问事件参数 -->
<div class="section">
<h3>5. 在内联事件处理器中访问事件参数</h3>
<button @click="eventHandler($event)">点击我(访问事件参数)</button>
<p>{{ eventInfo }}</p>
</div>
<!-- 6. 事件修饰符 -->
<div class="section">
<h3>6. 事件修饰符</h3>
<button @click.stop="stopPropagation">点击我(阻止事件传播)</button>
<button @click.prevent="preventDefault">点击我(阻止默认行为)</button>
<p>{{ message }}</p>
</div>
<!-- 7. 按键修饰符 -->
<div class="section">
<h3>7. 按键修饰符</h3>
<input type="text" @keyup.enter="handleEnter" placeholder="按回车键" />
<p>{{ enterMessage }}</p>
</div>
<!-- 8. 鼠标按键修饰符 -->
<div class="section">
<h3>8. 鼠标按键修饰符</h3>
<button @click.right="rightClick">右键点击我</button>
<p>{{ clickMessage }}</p>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- Vue 3 代码 -->
<script>
const { createApp, ref } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
const App = {
setup() {
// 1. 监听事件
const message = ref('');
// 2. 内联事件处理器
const handleClick = () => {
message.value = '事件处理器触发';
};
// 3. 方法事件处理器
const updateMessage = (newMessage) => {
message.value = newMessage;
};
// 4. 在内联处理器中调用方法
const methodWithParam = (newMessage) => {
message.value = newMessage;
};
// 5. 在内联事件处理器中访问事件参数
const eventInfo = ref('');
const eventHandler = (event) => {
eventInfo.value = `事件类型: ${event.type}, 事件目标: ${event.target.tagName}`;
};
// 6. 事件修饰符
const stopPropagation = () => {
message.value = '事件传播被阻止';
};
const preventDefault = () => {
message.value = '默认行为被阻止';
};
// 7. 按键修饰符
const enterMessage = ref('');
const handleEnter = () => {
enterMessage.value = '回车键被按下';
};
// 8. 鼠标按键修饰符
const clickMessage = ref('');
const rightClick = (event) => {
event.preventDefault();
clickMessage.value = '右键点击事件触发';
};
// 返回数据和方法
return {
message,
handleClick,
updateMessage,
methodWithParam,
eventInfo,
eventHandler,
stopPropagation,
preventDefault,
enterMessage,
handleEnter,
clickMessage,
rightClick
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.11 组件:基础
01.总结
定义和使用组件
传递 props
监听事件
通过插槽分配内容
使用动态组件
处理 DOM 内模板解析
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 组件示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
</head>
<body>
<div id="app">
<h1>Vue 3 组件示例</h1>
<!-- 使用自定义组件 -->
<custom-component msg="Hello from parent component!"></custom-component>
<!-- 监听事件 -->
<event-component @custom-event="handleEvent"></event-component>
<!-- 插槽示例 -->
<slot-component>
<template #header>
<h3>Custom Header</h3>
</template>
<template #default>
<p>This is the main content.</p>
</template>
<template #footer>
<p>Custom Footer</p>
</template>
</slot-component>
<!-- 动态组件 -->
<component :is="currentComponent"></component>
<button @click="toggleComponent">Toggle Component</button>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- Vue 3 代码 -->
<script>
const { createApp, ref } = Vue;
// 定义一个组件
const CustomComponent = {
props: ['msg'],
template: `<div>{{ msg }}</div>`
};
// 监听事件的组件
const EventComponent = {
template: `<button @click="emitEvent">Click me to emit event</button>`,
methods: {
emitEvent() {
this.$emit('custom-event', 'Event Payload');
}
}
};
// 插槽组件
const SlotComponent = {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
};
// 动态组件
const ComponentA = {
template: `<div>Component A</div>`
};
const ComponentB = {
template: `<div>Component B</div>`
};
const App = {
components: {
'custom-component': CustomComponent,
'event-component': EventComponent,
'slot-component': SlotComponent,
'component-a': ComponentA,
'component-b': ComponentB
},
setup() {
const currentComponent = ref('component-a');
const toggleComponent = () => {
currentComponent.value = currentComponent.value === 'component-a' ? 'component-b' : 'component-a';
};
const handleEvent = (payload) => {
console.log('Event received with payload:', payload);
ElementPlus.ElMessage({
message: `Event received with payload: ${payload}`,
type: 'success',
});
};
return {
currentComponent,
toggleComponent,
handleEvent
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.12 组件:注册
01.总结
a.全局注册组件:
const GlobalComponent = {
template: `<div>这是一个全局注册的组件</div>`
};
-----------------------------------------------------------------------------------------------------
const app = createApp(App);
app.component('global-component', GlobalComponent);
b.局部注册组件:
const App = {
// 局部注册组件
components: {
'local-component': {
template: `<div>这是一个局部注册的组件</div>`
},
'kebab-case-component': {
template: `<div>这是一个 Kebab Case 组件</div>`
},
camelCaseComponent: {
template: `<div>这是一个 Camel Case 组件</div>`
}
},
setup() {
return {};
}
};
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 组件注册示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
</head>
<body>
<div id="app">
<h1>Vue 3 组件注册示例</h1>
<!-- 全局注册组件 -->
<global-component></global-component>
<!-- 局部注册组件 -->
<local-component></local-component>
<!-- 组件名格式示例 -->
<kebab-case-component></kebab-case-component>
<camelCaseComponent></camelCaseComponent>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<script>
const { createApp } = Vue;
// 全局注册组件
const GlobalComponent = {
template: `<div>这是一个全局注册的组件</div>`
};
// 主应用组件
const App = {
// 局部注册组件
components: {
'local-component': {
template: `<div>这是一个局部注册的组件</div>`
},
'kebab-case-component': {
template: `<div>这是一个 Kebab Case 组件</div>`
},
camelCaseComponent: {
template: `<div>这是一个 Camel Case 组件</div>`
}
},
setup() {
return {};
}
};
const app = createApp(App);
// 全局注册组件
app.component('global-component', GlobalComponent);
app.use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.13 组件:props
01.总结
a.Props 声明:
const PropDemo = {
props: ['message'],
template: `<div>Props 声明: {{ message }}</div>`
};
b.单向数据流:
const OneWayFlow = {
props: ['initialCount'],
data() {
return {
count: this.initialCount
};
},
template: `<div>单向数据流: {{ count }}</div>`
};
c.Prop 校验:
const PropValidation = {
props: {
name: {
type: String,
required: true
}
},
template: `<div>Prop 校验: {{ name }}</div>`
};
d.Boolean 类型转换:
const BooleanProp = {
props: {
isActive: {
type: Boolean,
default: false
}
},
template: `<div>Boolean 类型转换: {{ isActive }}</div>`
};
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 Props 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue 3 Props 示例</h1>
<!-- Props 声明 -->
<prop-demo :message="propMessage"></prop-demo>
<!-- 单向数据流 -->
<one-way-flow :initial-count="0"></one-way-flow>
<!-- Prop 校验 -->
<prop-validation :name="123"></prop-validation>
<!-- Boolean 类型转换 -->
<boolean-prop :is-active="true"></boolean-prop>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<script>
const { createApp } = Vue;
// Props 声明
const PropDemo = {
props: ['message'],
template: `<div>Props 声明: {{ message }}</div>`
};
// 单向数据流
const OneWayFlow = {
props: ['initialCount'],
data() {
return {
count: this.initialCount
};
},
template: `<div>单向数据流: {{ count }}</div>`
};
// Prop 校验
const PropValidation = {
props: {
name: {
type: String,
required: true
}
},
template: `<div>Prop 校验: {{ name }}</div>`
};
// Boolean 类型转换
const BooleanProp = {
props: {
isActive: {
type: Boolean,
default: false
}
},
template: `<div>Boolean 类型转换: {{ isActive }}</div>`
};
const App = {
components: {
'prop-demo': PropDemo,
'one-way-flow': OneWayFlow,
'prop-validation': PropValidation,
'boolean-prop': BooleanProp
},
data() {
return {
propMessage: 'Hello, Vue 3 Props!'
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.14 组件:事件
01.总结
a.触发与监听事件:
const TriggerListen = defineComponent({
template: `
<div>
<button @click="emitEvent">点击触发事件</button>
<div>事件消息: {{ message }}</div>
</div>
`,
data() {
return {
message: ''
};
},
emits: ['custom-event'],
methods: {
emitEvent() {
this.$emit('custom-event', '事件被触发');
}
}
});
b.事件参数:
const EventParams = defineComponent({
template: `
<div>
<button @click="emitEvent('Hello', 'World')">点击触发事件</button>
<div>事件参数: {{ param1 }} {{ param2 }}</div>
</div>
`,
data() {
return {
param1: '',
param2: ''
};
},
emits: ['custom-event'],
methods: {
emitEvent(p1, p2) {
this.$emit('custom-event', p1, p2);
}
}
});
c.声明触发的事件:
const DeclareEvent = defineComponent({
template: `
<div>
<button @click="emitEvent">点击触发事件</button>
</div>
`,
emits: ['custom-event'],
methods: {
emitEvent() {
this.$emit('custom-event', '事件声明');
}
}
});
d.事件校验:
const EventValidation = defineComponent({
template: `
<div>
<button @click="emitEvent">点击触发事件</button>
</div>
`,
emits: ['validate-event'],
methods: {
emitEvent() {
this.$emit('validate-event', '事件校验');
}
}
});
e.处理事件的父组件方法:
const App = {
components: {
'trigger-listen': TriggerListen,
'event-params': EventParams,
'declare-event': DeclareEvent,
'event-validation': EventValidation
},
methods: {
handleCustomEvent(message) {
ElMessage.info(message);
},
handleCustomEventParams(param1, param2) {
ElMessage.info(`参数1: ${param1}, 参数2: ${param2}`);
},
handleDeclaredEvent(message) {
ElMessage.info(message);
},
handleValidateEvent(message) {
if (typeof message === 'string') {
ElMessage.success('事件校验成功: ' + message);
} else {
ElMessage.error('事件校验失败');
}
}
}
};
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 事件示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue 3 事件示例</h1>
<!-- 触发与监听事件 -->
<trigger-listen @custom-event="handleCustomEvent"></trigger-listen>
<!-- 事件参数 -->
<event-params @custom-event="handleCustomEventParams"></event-params>
<!-- 声明触发的事件 -->
<declare-event @custom-event="handleDeclaredEvent"></declare-event>
<!-- 事件校验 -->
<event-validation @validate-event="handleValidateEvent"></event-validation>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<script>
const { createApp, ref, defineComponent } = Vue;
const { ElMessage } = ElementPlus;
// 触发与监听事件
const TriggerListen = defineComponent({
template: `
<div>
<button @click="emitEvent">点击触发事件</button>
<div>事件消息: {{ message }}</div>
</div>
`,
data() {
return {
message: ''
};
},
emits: ['custom-event'],
methods: {
emitEvent() {
this.$emit('custom-event', '事件被触发');
}
}
});
// 事件参数
const EventParams = defineComponent({
template: `
<div>
<button @click="emitEvent('Hello', 'World')">点击触发事件</button>
<div>事件参数: {{ param1 }} {{ param2 }}</div>
</div>
`,
data() {
return {
param1: '',
param2: ''
};
},
emits: ['custom-event'],
methods: {
emitEvent(p1, p2) {
this.$emit('custom-event', p1, p2);
}
}
});
// 声明触发的事件
const DeclareEvent = defineComponent({
template: `
<div>
<button @click="emitEvent">点击触发事件</button>
</div>
`,
emits: ['custom-event'],
methods: {
emitEvent() {
this.$emit('custom-event', '事件声明');
}
}
});
// 事件校验
const EventValidation = defineComponent({
template: `
<div>
<button @click="emitEvent">点击触发事件</button>
</div>
`,
emits: ['validate-event'],
methods: {
emitEvent() {
this.$emit('validate-event', '事件校验');
}
}
});
const App = {
components: {
'trigger-listen': TriggerListen,
'event-params': EventParams,
'declare-event': DeclareEvent,
'event-validation': EventValidation
},
methods: {
handleCustomEvent(message) {
ElMessage.info(message);
},
handleCustomEventParams(param1, param2) {
ElMessage.info(`参数1: ${param1}, 参数2: ${param2}`);
},
handleDeclaredEvent(message) {
ElMessage.info(message);
},
handleValidateEvent(message) {
if (typeof message === 'string') {
ElMessage.success('事件校验成功: ' + message);
} else {
ElMessage.error('事件校验失败');
}
}
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.15 组件:v-model
01.总结
a.基本用法:
输入框使用 v-model 绑定 basicInput。
显示用户输入的内容。
b.v-model的参数:
创建自定义组件 CustomInput,使用 v-model:title 绑定 title。
显示输入的标题。
c.多个v-model绑定:
创建自定义组件 CustomCheckbox,同时使用 v-model:checked 和 v-model:label 绑定 checked 和 label。
显示选中状态和标签。
d.处理v-model修饰符:
输入框使用 v-model.trim 绑定 trimmedInput。
显示去除空格后的输入内容。
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue3 v-model 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<style>
/* 其他样式 */
</style>
</head>
<body>
<div id="app">
<div>
<h2>基本用法</h2>
<input v-model="basicInput" placeholder="输入一些文字">
<p>你输入的是: {{ basicInput }}</p>
</div>
<div>
<h2>v-model的参数</h2>
<custom-input v-model:title="title"></custom-input>
<p>标题是: {{ title }}</p>
</div>
<div>
<h2>多个v-model绑定</h2>
<custom-checkbox
v-model:checked="checked"
v-model:label="label">
</custom-checkbox>
<p>选中状态: {{ checked }}</p>
<p>标签: {{ label }}</p>
</div>
<div>
<h2>处理v-model修饰符</h2>
<input v-model.trim="trimmedInput" placeholder="输入一些文字">
<p>去除空格后的输入: {{ trimmedInput }}</p>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script>
const { createApp, ref, reactive } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
// 定义 custom-input 组件
const CustomInput = {
props: ['modelValue'],
emits: ['update:modelValue'],
template: `<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" placeholder="输入标题">`
};
// 定义 custom-checkbox 组件
const CustomCheckbox = {
props: ['checked', 'label'],
emits: ['update:checked', 'update:label'],
template: `
<div>
<input type="checkbox" :checked="checked" @change="$emit('update:checked', $event.target.checked)">
<input :value="label" @input="$emit('update:label', $event.target.value)" placeholder="输入标签">
</div>
`
};
const App = {
components: {
CustomInput,
CustomCheckbox
},
setup() {
// 基本用法
const basicInput = ref('');
// v-model的参数
const title = ref('');
// 多个v-model绑定
const checked = ref(false);
const label = ref('');
// 处理v-model修饰符
const trimmedInput = ref('');
return {
basicInput,
title,
checked,
label,
trimmedInput
};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app");
</script>
</html>
2.16 组件:Attributes
01.总结
a.Attributes 继承:
const AttributesInheritance = defineComponent({
template: `
<div>
<p>这个组件会继承父组件的 class 和 style。</p>
</div>
`
});
b.对 class 和 style 的合并:
const ClassStyleMerge = defineComponent({
template: `
<div class="merge-class" style="background-color: yellow;">
<p>这个组件会合并父组件的 class 和 style。</p>
</div>
`
});
c.V-on 监听器继承:
const VOnListener = defineComponent({
template: `
<div>
<button @click="$emit('click')">点击触发父组件事件</button>
</div>
`
});
d.深层组件继承:
const DeepInheritanceChild = defineComponent({
template: `
<div>
<p>这是深层嵌套的子组件,继承的 class 为: {{ $attrs.class }}</p>
</div>
`
});
const DeepInheritance = defineComponent({
components: {
'deep-inheritance-child': DeepInheritanceChild
},
template: `
<div>
<deep-inheritance-child v-bind="$attrs"></deep-inheritance-child>
</div>
`
});
e.禁用 Attributes 继承:
const DisableInheritance = defineComponent({
inheritAttrs: false,
template: `
<div>
<p>这个组件禁用了 Attributes 继承。</p>
</div>
`
});
f.多根节点的 Attributes 继承:
const MultiRootInheritance = defineComponent({
template: `
<div>
<p>根节点1</p>
<p>根节点2</p>
</div>
`
});
g.在 JavaScript 中访问透传 Attributes:
const AccessAttributes = defineComponent({
template: `
<div>
<p>在 JavaScript 中访问透传的 Attributes: {{ $attrs }}</p>
</div>
`
});
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 Attributes 继承示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue 3 Attributes 继承示例</h1>
<!-- Attributes 继承 -->
<attributes-inheritance class="parent-class" style="color: red;"></attributes-inheritance>
<!-- 对 class 和 style 的合并 -->
<class-style-merge class="additional-class" style="font-size: 20px;"></class-style-merge>
<!-- V-on 监听器继承 -->
<v-on-listener @click="handleClick"></v-on-listener>
<!-- 深层组件继承 -->
<deep-inheritance class="deep-class"></deep-inheritance>
<!-- 禁用 Attributes 继承 -->
<disable-inheritance class="should-not-be-inherited"></disable-inheritance>
<!-- 多根节点的 Attributes 继承 -->
<multi-root-inheritance class="multi-root-class"></multi-root-inheritance>
<!-- 在 JavaScript 中访问透传 Attributes -->
<access-attributes></access-attributes>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<script>
const { createApp, defineComponent } = Vue;
const { ElMessage } = ElementPlus;
// Attributes 继承
const AttributesInheritance = defineComponent({
template: `
<div>
<p>这个组件会继承父组件的 class 和 style。</p>
</div>
`
});
// 对 class 和 style 的合并
const ClassStyleMerge = defineComponent({
template: `
<div class="merge-class" style="background-color: yellow;">
<p>这个组件会合并父组件的 class 和 style。</p>
</div>
`
});
// V-on 监听器继承
const VOnListener = defineComponent({
template: `
<div>
<button @click="$emit('click')">点击触发父组件事件</button>
</div>
`
});
// 深层组件继承
const DeepInheritanceChild = defineComponent({
template: `
<div>
<p>这是深层嵌套的子组件,继承的 class 为: {{ $attrs.class }}</p>
</div>
`
});
const DeepInheritance = defineComponent({
components: {
'deep-inheritance-child': DeepInheritanceChild
},
template: `
<div>
<deep-inheritance-child v-bind="$attrs"></deep-inheritance-child>
</div>
`
});
// 禁用 Attributes 继承
const DisableInheritance = defineComponent({
inheritAttrs: false,
template: `
<div>
<p>这个组件禁用了 Attributes 继承。</p>
</div>
`
});
// 多根节点的 Attributes 继承
const MultiRootInheritance = defineComponent({
template: `
<div>
<p>根节点1</p>
<p>根节点2</p>
</div>
`
});
// 在 JavaScript 中访问透传 Attributes
const AccessAttributes = defineComponent({
template: `
<div>
<p>在 JavaScript 中访问透传的 Attributes: {{ $attrs }}</p>
</div>
`
});
const App = {
components: {
'attributes-inheritance': AttributesInheritance,
'class-style-merge': ClassStyleMerge,
'v-on-listener': VOnListener,
'deep-inheritance': DeepInheritance,
'disable-inheritance': DisableInheritance,
'multi-root-inheritance': MultiRootInheritance,
'access-attributes': AccessAttributes
},
methods: {
handleClick() {
ElMessage.info('点击事件被触发');
}
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.17 组件:插槽
01.总结
插槽内容与出口:使用 <slot> 标签定义默认插槽内容。
渲染作用域:通过 slot 传递值给插槽内容。
默认内容:定义一个简单的插槽,显示默认内容。
具名插槽:使用 name 属性定义具名插槽,如 header 和 footer。
条件插槽:通过 v-if 和 v-else 切换显示的插槽内容。
动态插槽名:使用方括号 [] 绑定动态插槽名。
作用域插槽:通过 slot 传递值给插槽内容。
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue3 插槽示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<style>
/* 其他样式 */
</style>
</head>
<body>
<div id="app">
<div>
<h2>插槽内容与出口</h2>
<content-slot>
<template v-slot>这是插槽的默认内容</template>
</content-slot>
</div>
<div>
<h2>渲染作用域</h2>
<scope-slot>
<template v-slot:default="slotProps">接收到的值: {{ slotProps.value }}</template>
</scope-slot>
</div>
<div>
<h2>默认内容</h2>
<default-slot></default-slot>
</div>
<div>
<h2>具名插槽</h2>
<named-slot>
<template v-slot:header>这是头部内容</template>
<template v-slot:footer>这是尾部内容</template>
</named-slot>
</div>
<div>
<h2>条件插槽</h2>
<conditional-slot>
<template v-slot:default>显示的内容</template>
<template v-slot:alternative>替代内容</template>
</conditional-slot>
</div>
<div>
<h2>动态插槽名</h2>
<dynamic-slot :dynamic-slot-name="dynamicSlotName">
<template v-slot:[dynamicSlotName]>动态插槽内容</template>
</dynamic-slot>
</div>
<div>
<h2>作用域插槽</h2>
<scoped-slot>
<template v-slot:default="slotProps">接收到的值: {{ slotProps.value }}</template>
</scoped-slot>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script>
const { createApp, ref } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
// 定义 content-slot 组件
const ContentSlot = {
template: `<div><slot>默认插槽内容</slot></div>`
};
// 定义 scope-slot 组件
const ScopeSlot = {
template: `<div><slot :value="value"></slot></div>`,
setup() {
const value = ref('这是作用域插槽的值');
return { value };
}
};
// 定义 default-slot 组件
const DefaultSlot = {
template: `<div><slot>这是默认内容</slot></div>`
};
// 定义 named-slot 组件
const NamedSlot = {
template: `
<div>
<header><slot name="header">默认头部内容</slot></header>
<footer><slot name="footer">默认尾部内容</slot></footer>
</div>
`
};
// 定义 conditional-slot 组件
const ConditionalSlot = {
template: `
<div>
<slot v-if="showDefault">显示的内容</slot>
<slot name="alternative" v-else>替代内容</slot>
</div>
`,
setup() {
const showDefault = ref(true);
return { showDefault };
}
};
// 定义 dynamic-slot 组件
const DynamicSlot = {
props: ['dynamicSlotName'],
template: `<div><slot :name="dynamicSlotName">默认动态插槽内容</slot></div>`
};
// 定义 scoped-slot 组件
const ScopedSlot = {
template: `<div><slot :value="value"></slot></div>`,
setup() {
const value = ref('这是作用域插槽的值');
return { value };
}
};
const App = {
components: {
ContentSlot,
ScopeSlot,
DefaultSlot,
NamedSlot,
ConditionalSlot,
DynamicSlot,
ScopedSlot
},
setup() {
const dynamicSlotName = ref('default');
return { dynamicSlotName };
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app");
</script>
</html>
2.18 组件:依赖注入
01.总结
a.Prop逐级透传问题(示例):
const PropDrillingChild = defineComponent({
props: ['message'],
template: `<div>子组件收到的消息: {{ message }}</div>`
});
const PropDrillingParent = defineComponent({
template: `
<div>
<prop-drilling-child :message="message"></prop-drilling-child>
</div>
`,
components: {
'prop-drilling-child': PropDrillingChild
},
data() {
return { message: '这是逐级透传的问题' }
}
});
b.Provide 示例:
const ProvideExample = defineComponent({
template: `
<div>
<p>Provide 示例</p>
<provide-child></provide-child>
</div>
`,
components: {
'provide-child': {
inject: ['providedData'],
template: `<div>注入的数据: {{ providedData }}</div>`
}
},
setup() {
provide('providedData', '这是通过 provide 提供的数据');
}
});
c.应用层 Provide 示例:
const ApplicationProvideExample = defineComponent({
template: `
<div>
<p>应用层 Provide 示例</p>
<provide-child></provide-child>
</div>
`,
components: {
'provide-child': {
inject: ['appProvidedData'],
template: `<div>应用层注入的数据: {{ appProvidedData }}</div>`
}
}
});
d.Inject 示例:
const InjectExample = defineComponent({
template: `
<div>
<p>Inject 示例</p>
<provide-child></provide-child>
</div>
`,
components: {
'provide-child': {
inject: ['injectedData'],
template: `<div>注入的数据: {{ injectedData }}</div>`
}
},
setup() {
provide('injectedData', '这是通过 inject 注入的数据');
}
});
e.和响应式数据配合使用:
const ReactiveProvideExample = defineComponent({
template: `
<div>
<p>和响应式数据配合使用</p>
<reactive-child></reactive-child>
</div>
`,
components: {
'reactive-child': {
inject: ['reactiveData'],
template: `<div>响应式数据: {{ reactiveData.count }}</div>`
}
},
setup() {
const reactiveData = reactive({ count: 0 });
provide('reactiveData', reactiveData);
setInterval(() => {
reactiveData.count++;
}, 1000);
}
});
f.使用 Symbol 作注入名:
const SymbolInjectExample = defineComponent({
template: `
<div>
<p>使用 Symbol 作注入名</p>
<symbol-child></symbol-child>
</div>
`,
components: {
'symbol-child': {
inject: ['symbolData'],
template: `<div>注入的 Symbol 数据: {{ symbolData }}</div>`
}
},
setup() {
const symbolKey = Symbol('symbolKey');
provide(symbolKey, '这是使用 Symbol 作为注入名的数据');
return { symbolKey };
}
});
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 Provide/Inject 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue 3 Provide/Inject 示例</h1>
<!-- Provide 示例 -->
<provide-example></provide-example>
<!-- 应用层 Provide 示例 -->
<application-provide-example></application-provide-example>
<!-- Inject 示例 -->
<inject-example></inject-example>
<!-- 和响应式数据配合使用 -->
<reactive-provide-example></reactive-provide-example>
<!-- 使用 Symbol 作注入名 -->
<symbol-inject-example></symbol-inject-example>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<script>
const { createApp, defineComponent, reactive, ref, provide, inject } = Vue;
const { ElMessage } = ElementPlus;
// Prop逐级透传问题(示例)
const PropDrillingChild = defineComponent({
props: ['message'],
template: `<div>子组件收到的消息: {{ message }}</div>`
});
const PropDrillingParent = defineComponent({
template: `
<div>
<prop-drilling-child :message="message"></prop-drilling-child>
</div>
`,
components: {
'prop-drilling-child': PropDrillingChild
},
data() {
return { message: '这是逐级透传的问题' }
}
});
// Provide 示例
const ProvideExample = defineComponent({
template: `
<div>
<p>Provide 示例</p>
<provide-child></provide-child>
</div>
`,
components: {
'provide-child': {
inject: ['providedData'],
template: `<div>注入的数据: {{ providedData }}</div>`
}
},
setup() {
provide('providedData', '这是通过 provide 提供的数据');
}
});
// 应用层 Provide 示例
const ApplicationProvideExample = defineComponent({
template: `
<div>
<p>应用层 Provide 示例</p>
<provide-child></provide-child>
</div>
`,
components: {
'provide-child': {
inject: ['appProvidedData'],
template: `<div>应用层注入的数据: {{ appProvidedData }}</div>`
}
}
});
// Inject 示例
const InjectExample = defineComponent({
template: `
<div>
<p>Inject 示例</p>
<provide-child></provide-child>
</div>
`,
components: {
'provide-child': {
inject: ['injectedData'],
template: `<div>注入的数据: {{ injectedData }}</div>`
}
},
setup() {
provide('injectedData', '这是通过 inject 注入的数据');
}
});
// 和响应式数据配合使用
const ReactiveProvideExample = defineComponent({
template: `
<div>
<p>和响应式数据配合使用</p>
<reactive-child></reactive-child>
</div>
`,
components: {
'reactive-child': {
inject: ['reactiveData'],
template: `<div>响应式数据: {{ reactiveData.count }}</div>`
}
},
setup() {
const reactiveData = reactive({ count: 0 });
provide('reactiveData', reactiveData);
setInterval(() => {
reactiveData.count++;
}, 1000);
}
});
// 使用 Symbol 作注入名
const SymbolInjectExample = defineComponent({
template: `
<div>
<p>使用 Symbol 作注入名</p>
<symbol-child></symbol-child>
</div>
`,
components: {
'symbol-child': {
inject: ['symbolData'],
template: `<div>注入的 Symbol 数据: {{ symbolData }}</div>`
}
},
setup() {
const symbolKey = Symbol('symbolKey');
provide(symbolKey, '这是使用 Symbol 作为注入名的数据');
return { symbolKey };
}
});
const App = {
components: {
'provide-example': ProvideExample,
'application-provide-example': ApplicationProvideExample,
'inject-example': InjectExample,
'reactive-provide-example': ReactiveProvideExample,
'symbol-inject-example': SymbolInjectExample,
'prop-drilling-parent': PropDrillingParent
}
};
const app = createApp(App);
app.provide('appProvidedData', '这是应用层提供的数据');
app.use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.19 组件:异步组件
01.总结
a.代码说明
BasicComponent: 一个简单的组件,显示基本内容。
ErrorComponent: 一个模拟可能出错的组件,展示加载与错误状态。
SuspenseComponent: 一个用于展示 Suspense 机制的组件。
b.异步组件的实现
AsyncBasic: 异步加载 BasicComponent。
AsyncError: 异步加载 ErrorComponent,并处理加载和错误状态。
SuspenseExample: 使用 Suspense 机制加载 SuspenseComponent。
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue3 异步组件示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
/* 其他样式 */
</style>
</head>
<body>
<div id="app">
<div>
<h2>基本用法</h2>
<async-basic></async-basic>
</div>
<div>
<h2>加载与错误状态</h2>
<async-error></async-error>
</div>
<div>
<h2>搭配 Suspense 使用</h2>
<suspense-example></suspense-example>
</div>
</div>
</body>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script>
const { createApp, defineAsyncComponent, defineComponent, h, ref } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
// 定义 BasicComponent 组件
const BasicComponent = defineComponent({
template: `<div>这是一个基本组件</div>`
});
// 定义 ErrorComponent 组件
const ErrorComponent = defineComponent({
template: `<div>这是一个可能出错的组件</div>`
});
// 定义 SuspenseComponent 组件
const SuspenseComponent = defineComponent({
template: `<div>这是一个使用 Suspense 的组件</div>`
});
// 基本用法
const AsyncBasic = defineAsyncComponent(() =>
new Promise(resolve => {
setTimeout(() => {
resolve(BasicComponent);
}, 1000);
})
);
// 加载与错误状态
const AsyncError = defineAsyncComponent({
loader: () =>
new Promise((resolve, reject) => {
setTimeout(() => {
// 模拟加载失败
const fail = Math.random() > 0.5;
if (fail) {
reject('加载失败');
} else {
resolve(ErrorComponent);
}
}, 1000);
}),
loadingComponent: {
template: '<p>Loading...</p>'
},
errorComponent: {
template: '<p>Error loading component.</p>'
},
delay: 200,
timeout: 3000
});
// 搭配 Suspense 使用
const SuspenseExample = {
template: `
<Suspense>
<template #default>
<AsyncSuspense />
</template>
<template #fallback>
<p>Loading...</p>
</template>
</Suspense>
`,
components: {
AsyncSuspense: defineAsyncComponent(() =>
new Promise(resolve => {
setTimeout(() => {
resolve(SuspenseComponent);
}, 1000);
})
)
}
};
const App = {
components: {
AsyncBasic,
AsyncError,
SuspenseExample
},
setup() {
return {};
}
};
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app");
</script>
</html>
2.20 附:TS与组合式
01.总结
为组件的props标注类型
为组件的emits标注类型
为ref()标注类型
为reactive()标注类型
为computed()标注类型
为事件处理函数标注类型
为provide/inject标注类型
为模板引用标注类型
为组件模板引用标注类型
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue3 TypeScript 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
/* 其他样式 */
</style>
</head>
<body>
<div id="app">
<div>
<h2>为组件的props标注类型</h2>
<props-demo></props-demo>
</div>
<div>
<h2>为组件的emits标注类型</h2>
<emits-demo></emits-demo>
</div>
<div>
<h2>为ref标注类型</h2>
<ref-demo></ref-demo>
</div>
<div>
<h2>为reactive标注类型</h2>
<reactive-demo></reactive-demo>
</div>
<div>
<h2>为computed标注类型</h2>
<computed-demo></computed-demo>
</div>
<div>
<h2>为事件处理函数标注类型</h2>
<event-handler-demo></event-handler-demo>
</div>
<div>
<h2>为provide/inject标注类型</h2>
<provide-inject-demo></provide-inject-demo>
</div>
<div>
<h2>为模板引用标注类型</h2>
<template-ref-demo></template-ref-demo>
</div>
<div>
<h2>为组件模板引用标注类型</h2>
<component-template-ref-demo></component-template-ref-demo>
</div>
</div>
</body>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<script type="module">
const { createApp, defineComponent, ref, reactive, computed, provide, inject } = Vue;
const { ElMessage, ElMessageBox } = ElementPlus;
// 为组件的props标注类型
const PropsDemo = defineComponent({
props: {
message: String
},
template: `<div>{{ message }}</div>`
});
// 为组件的emits标注类型
const EmitsDemo = defineComponent({
emits: {
'update': (value: string) => typeof value === 'string'
},
setup(props, { emit }) {
const updateValue = () => emit('update', 'new value');
return { updateValue };
},
template: `<button @click="updateValue">Update</button>`
});
// 为ref标注类型
const RefDemo = defineComponent({
setup() {
const count = ref<number>(0);
return { count };
},
template: `<div>{{ count }}</div>`
});
// 为reactive标注类型
const ReactiveDemo = defineComponent({
setup() {
const state = reactive<{ count: number }>({ count: 0 });
return { state };
},
template: `<div>{{ state.count }}</div>`
});
// 为computed标注类型
const ComputedDemo = defineComponent({
setup() {
const count = ref<number>(0);
const doubleCount = computed<number>(() => count.value * 2);
return { count, doubleCount };
},
template: `<div>{{ doubleCount }}</div>`
});
// 为事件处理函数标注类型
const EventHandlerDemo = defineComponent({
setup() {
const handleClick = (event: MouseEvent) => {
console.log(event);
};
return { handleClick };
},
template: `<button @click="handleClick">Click me</button>`
});
// 为provide/inject标注类型
const ProvideInjectDemo = defineComponent({
setup() {
provide<string>('message', 'Hello from Provide');
},
template: `<inject-demo></inject-demo>`,
components: {
InjectDemo: defineComponent({
setup() {
const message = inject<string>('message');
return { message };
},
template: `<div>{{ message }}</div>`
})
}
});
// 为模板引用标注类型
const TemplateRefDemo = defineComponent({
setup() {
const inputRef = ref<HTMLInputElement | null>(null);
const focusInput = () => {
if (inputRef.value) inputRef.value.focus();
};
return { inputRef, focusInput };
},
template: `
<div>
<input ref="inputRef" type="text">
<button @click="focusInput">Focus Input</button>
</div>
`
});
// 为组件模板引用标注类型
const ComponentTemplateRefDemo = defineComponent({
components: {
ChildComponent: defineComponent({
template: `<div>Child Component</div>`
})
},
setup() {
const childRef = ref<InstanceType<typeof ChildComponent> | null>(null);
return { childRef };
},
template: `
<div>
<child-component ref="childRef"></child-component>
</div>
`
});
const App = defineComponent({
components: {
PropsDemo,
EmitsDemo,
RefDemo,
ReactiveDemo,
ComputedDemo,
EventHandlerDemo,
ProvideInjectDemo,
TemplateRefDemo,
ComponentTemplateRefDemo
}
});
createApp(App).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app");
</script>
</html>
2.21 附:TS与选项式
01.总结
为组件的props标注类型
为组件的emits标注类型
为计算属性标记类型
为事件处理函数标注类型
扩展全局属性
扩展自定义选项
02.代码
<!DOCTYPE html>
<html lang="en">
<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">
<title>Vue 3 TypeScript Options API 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
<!-- 其他头部信息 -->
<style>
</style>
</head>
<body>
<div id="app">
<h1>Vue 3 TypeScript Options API 示例</h1>
<!-- 为组件的props标注类型 -->
<props-type-example :message="'Hello, Vue3 with TypeScript!'"></props-type-example>
<!-- 为组件的emits标注类型 -->
<emits-type-example @update="handleUpdate"></emits-type-example>
<!-- 为计算属性标记类型 -->
<computed-type-example></computed-type-example>
<!-- 为事件处理函数标注类型 -->
<event-handler-type-example></event-handler-type-example>
<!-- 扩展全局属性 -->
<global-property-example></global-property-example>
<!-- 扩展自定义选项 -->
<custom-option-example></custom-option-example>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 使用 TypeScript -->
<script type="module">
const { defineComponent, createApp, ref, computed } = Vue;
const { ElMessage } = ElementPlus;
// 为组件的props标注类型
const PropsTypeExample = defineComponent({
props: {
message: {
type: String,
required: true
}
},
template: `<div>{{ message }}</div>`
});
// 为组件的emits标注类型
const EmitsTypeExample = defineComponent({
emits: ['update'],
setup(props, { emit }) {
const handleClick = () => {
emit('update', 'Updated Value');
};
return { handleClick };
},
template: `<button @click="handleClick">点击触发 update 事件</button>`
});
// 为计算属性标记类型
const ComputedTypeExample = defineComponent({
setup() {
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
return { count, doubleCount };
},
template: `<div>Count: {{ count }} | Double Count: {{ doubleCount }}</div>`
});
// 为事件处理函数标注类型
const EventHandlerTypeExample = defineComponent({
setup() {
const handleClick = (event: MouseEvent) => {
ElMessage(`事件类型: ${event.type}`);
};
return { handleClick };
},
template: `<button @click="handleClick">点击我</button>`
});
// 扩展全局属性
const GlobalPropertyExample = defineComponent({
template: `<div>{{ $myGlobalProperty }}</div>`,
mounted() {
this.$myGlobalProperty = 'Hello from global property!';
}
});
// 扩展自定义选项
const CustomOptionExample = defineComponent({
myOption: 'customOptionValue',
mounted() {
console.log(this.$options.myOption); // customOptionValue
},
template: `<div>检查控制台日志以查看自定义选项</div>`
});
const app = createApp({
components: {
'props-type-example': PropsTypeExample,
'emits-type-example': EmitsTypeExample,
'computed-type-example': ComputedTypeExample,
'event-handler-type-example': EventHandlerTypeExample,
'global-property-example': GlobalPropertyExample,
'custom-option-example': CustomOptionExample
},
methods: {
handleUpdate(value) {
ElMessage(`接收到更新事件: ${value}`);
}
}
});
// 扩展全局属性
app.config.globalProperties.$myGlobalProperty = '';
// 扩展自定义选项
app.mixin({
created() {
if (this.$options.myOption) {
console.log(this.$options.myOption);
}
}
});
app.use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount('#app');
</script>
</body>
</html>
2.22 附:ref用法
01.总结
a.基本使用:
const count = ref(0); 创建一个响应式引用变量 count。
count.value++; 更新引用变量的值需要通过 .value 属性。
b.响应式引用对象:
const user = ref({ name: '张三', age: 25 }); 创建一个响应式引用对象 user。
更新对象属性时直接修改 user.value 的属性。
c.响应式引用数组:
const items = ref(['项目1', '项目2', '项目3']); 创建一个响应式引用数组 items。
可以通过数组方法(如 push)修改引用数组的内容。
d.DOM 元素引用:
const inputRef = ref(null); 创建一个 DOM 元素引用。
在模板中使用 ref="inputRef" 绑定引用。
可以通过 inputRef.value 访问 DOM 元素并调用其方法,如 focus()
02.代码
<!DOCTYPE html>
<html lang="zh-CN">
<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">
<title>Vue 3 ref 示例</title>
<!-- 导入 ICON -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<!-- 导入 ElementPlus -->
<link rel="stylesheet" href="../../../statics/element-plus/index.css">
<!-- 导入 Layui -->
<link rel="stylesheet" href="../../../statics/layui-v2.9.2/css/layui.css">
</head>
<body>
<div id="app">
<div>
<h2>基本使用</h2>
<p>计数器: {{ count }}</p>
<button @click="increment">增加</button>
</div>
<div>
<h2>响应式引用对象</h2>
<p>用户信息: {{ user.name }}, {{ user.age }}岁</p>
<button @click="updateUser">更新用户信息</button>
</div>
<div>
<h2>响应式引用数组</h2>
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
<button @click="addItem">添加项目</button>
</div>
<div>
<h2>DOM 元素引用</h2>
<input ref="inputRef" placeholder="输入一些内容">
<button @click="focusInput">聚焦输入框</button>
</div>
</div>
<!-- 导入 Vue3 -->
<script src="../../../statics/vue/dist/vue.global.prod.js"></script>
<script src="../../../statics/axios/axios.min.js"></script>
<!-- 导入 ElementPlus -->
<script src="../../../statics/element-plus/index.full.min.js"></script>
<script src="../../../statics/element-plus/locale/zh-cn.min.js"></script>
<!-- 导入 Layui -->
<script src="../../../statics/layui-v2.9.2/layui.js"></script>
<!-- 导入 Dayjs -->
<script src="../../../statics/dayjs/dayjs.min.js"></script>
<!-- 导入 Echarts -->
<script src="../../../statics/echarts/echarts.js"></script>
<!-- 导入 Jquery -->
<script src="../../../statics/jquery/jquery.min.js"></script>
<!-- JavaScript 代码 -->
<script>
const { createApp, ref } = Vue;
const App = {
setup() {
// 基本使用
const count = ref(0);
const increment = () => {
count.value++;
};
// 响应式引用对象
const user = ref({ name: '张三', age: 25 });
const updateUser = () => {
user.value.name = '李四';
user.value.age = 30;
};
// 响应式引用数组
const items = ref(['项目1', '项目2', '项目3']);
const addItem = () => {
items.value.push('新项目');
};
// DOM 元素引用
const inputRef = ref(null);
const focusInput = () => {
inputRef.value.focus();
};
return {
count,
increment,
user,
updateUser,
items,
addItem,
inputRef,
focusInput
};
}
};
createApp(App).mount('#app');
</script>
</body>
</html>
2.23 附:四种写法
01.选项式+HTML
<script type="module">
import { createApp } from 'vue'
createApp({
data() {
return {
message: 'Hello World!'
}
}
}).mount('#app')
</script>
<div id="app">
<h1>{{ message }}</h1>
</div>
02.选项式+单文件组件
<script>
export default {
data() {
return {
message: 'Hello World!'
}
}
}
</script>
<template>
<h1>{{ message }}</h1>
</template>
03.组合式+HTML
<script type="module">
import { createApp, ref } from 'vue'
createApp({
setup() {
const message = ref('Hello World!')
return {
message
}
}
}).mount('#app')
</script>
<div id="app">
<h1>{{ message }}</h1>
</div>
04.组合式+单文件组件
<script setup>
import { ref } from 'vue'
const message = ref('Hello World!')
</script>
<template>
<h1>{{ message }}</h1>
</template>
2.24 [新]3.5:Props解构赋值
01.带响应式Props解构赋值
a.介绍
简述:以前我们对Props直接进行解构赋值是会失去响应式的,需要配合使用toRefs或者toRef解构才会有响应式,
那么就多了toRefs或者toRef这工序,而最新Vue3.5版本已经不需要了。
b.这样直接解构,testCount能直接渲染显示,但会失去响应式,当我们修改testCount时页面不更新。
<template>
<div>
{{ testCount }}
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
testCount: {
type: number,
default: 0,
},
});
const { testCount } = props;
</script>
c.保留响应式的老写法,使用toRefs或者toRef解构
<template>
<div>
{{ testCount }}
</div>
</template>
<script setup>
import { defineProps, toRef, toRefs } from 'vue';
const props = defineProps({
testCount: {
type: number,
default: 0,
},
});
const { testCount } = toRefs(props);
// 或者
const testCount = toRef(props, 'testCount');
</script>
d.最新Vue3.5写法,不借助”外力“直接解构,依然保持响应式
<template>
<div>
{{ testCount }}
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const { testCount } = defineProps({
testCount: {
type: number,
},
});
</script>
02.Props默认值新写法
a.介绍
以前默认值都是用default: ***去设置,现在不用了,现在只需要解构的时候直接设置默认值,不需要额外处理。
b.先看看旧的default: ***默认值写法,如下第12就是旧写法,其它以前Vue2也是这样设置默认值
<template>
<div>
{{ props.testCount }}
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
testCount: {
type: number,
default: 1
},
});
</script>
c.最新优化的写法,如下第9行,解构的时候直接一步到位设置默认值,更接近js语法的法写。
<template>
<div>
{{ testCount }}
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const { testCount=18 } = defineProps({
testCount: {
type: number,
},
});
</script>