一:Vue 导语
1.1 什么是 Vue
Vue 是一款流行的开源 JavaScript 框架,用于构建用户界面。Vue 由尤雨溪在 2014 年开发,是一个轻量级、灵活的框架,被广泛应用于构建单页面应用和动态 Web 界面。
Vue.js 的特点和作用:
Vue.js 主要用于构建交互式的、动态的 Web 界面,其作用包括但不限于:
- 构建用户界面:Vue.js 提供了简洁、灵活的语法和 API,使得开发者可以轻松构建交互式的用户界面。
- 数据驱动:Vue.js 使用了响应式数据绑定机制,使得界面的数据与视图保持同步,当数据变化时,视图会自动更新。
- 组件化开发:Vue.js 鼓励开发者使用组件化的方式构建界面,将界面拆分成多个独立、可复用的组件,使得代码更加模块化、易于维护。
- 易学易用:Vue.js 的语法简洁清晰,文档完善,上手容易,适合初学者和有经验的开发者使用。
- 社区活跃:Vue.js 拥有庞大的开发者社区和生态系统,有大量的插件和工具可供选择,能够满足各种需求。
二:初识 Vue
2.1 Vue 的基本代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<script type="text/javascript" src="../js/vue.js"></script><!-- 引入Vue -->
</head>
<body>
<!-- 准备好一个容器 -->
<div id="demo">
<h1>Hello,{{name.toUpperCase()}},{{address}}</h1><!-- 在容器中,我们可以直接访问 Vue 实例中的数据 -->
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
//创建Vue实例
new Vue({
el:'#demo', //el用于指定当前Vue实例为哪个容器服务。
data:{ //data中用于存储数据,这些数据供el所指定的容器去使用。
name:'atguigu',
address:'北京'
}
})
</script>
</body>
</html>
运行结果如图所示:
<div id="demo">
<h1>Hello,{{name.toUpperCase()}},{{address}}</h1>
</div>
注意:
-
Vue 实例和容器是一 一对应的,不能一个容器对应多个实例,也不能一个实例对应多个容器;
-
真实开发中只有一个 Vue 实例,并且会配合着组件一起使用;
-
一旦 data 中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
2.3 数据绑定
数据绑定有两种:
- 单向数据绑定 v-bind:
- 双向数据绑定 v-model:
那么什么是单向数据绑定,什么是双向数据绑定?
- 单向数据绑定只能让数据从 data 流向页面 。
- 双向数据绑定不仅能让数据从 data 流向页面,还可以让数据从页面流向 data。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 数据绑定示例</title>
<!-- 引入 Vue.js -->
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<h2>单向数据绑定示例</h2>
<p v-bind:title="message">鼠标悬停查看提示</p>
<img v-bind:src="imageSrc" alt="动态图片">
<!-- 双向数据绑定 v-model -->
<h2>双向数据绑定示例</h2>
<p>输入你的名字:<input v-model="name"></p> <!-- v-model:value` 可以简写为 `v-model`,因为 `v-model` 默认收集的就是 `value` 值。 -->
<p>你好,{{ name }}!</p>
</div>
<script>
new Vue({
el: '#app',
data: {
// 单向绑定示例的数据
message: '这是一个提示信息,单向数据绑定到 title 属性',
imageSrc: 'https://via.placeholder.com/150',
// 双向绑定示例的数据
name: ''
}
});
</script>
</body>
</html>
2.4 el 和 data 的两种写法
在 Vue 中,有两种写法可以定义 el
属性,两种写法可以定义 data
属性。
2.4.1 el的两种写法
- .在创建 Vue 实例时进行配置。
- 先创建 Vue 实例,然后通过
vm.$mount('#root')
的方式指定el
的值。
//el的两种写法
const v = new Vue({
el:'#root', //第一种写法
data:{
name:'尚硅谷'
}
})
console.log(v)
v.$mount('#root') //第二种写法,mount翻译为挂载,把 v 挂载到 root 上
2.4.2 data 的两种写法
- 对象式:直接在 Vue 实例中以对象的形式定义
data
。 - 函数式:在 Vue 实例中以函数的形式返回一个对象,用于定义
data
。
new Vue({
el:'#root',
//data的第一种写法:对象式
data:{
name:'尚硅谷'
}
//data的第二种写法:函数式
data(){
console.log('@@@',this) //此处的this是Vue实例对象
return{
name:'尚硅谷'
}
})
目前任何一种写法都可以,但是在学习到组件时,必须使用函数式定义 data
。
2.4.3 箭头函数的指向
- 在 Vue 管理的函数中,要用普通函数。
- 不被 Vue 所管理的函数,要用箭头函数
// 错误示例:使用箭头函数导致上下文错误
new Vue({
data: {
message: 'Hello Vue!'
},
created: () => {
console.log(this.message); // 错误!此时的 this 指向的不是 Vue 实例
}
})
// 正确示例:使用普通函数确保正确的上下文绑定
new Vue({
data: {
message: 'Hello Vue!'
},
created() {
console.log(this.message); // 正确!此时的 this 指向 Vue 实例
}
})
在 Vue 中,"Vue 管理的函数"通常指的是以下几类函数:
- 生命周期钩子函数:
- 计算属性:
- methods:
- Watchers:
2.5 MVVM 模型
MVVM 是一种软件架构模式,它将应用程序分为三个主要部分:模型( Model )、视图( View )和视图模型( ViewModel )。MVVM 的核心思想是将用户界面的逻辑与数据分离,使代码更易于维护和测试。
- M:模型 ( Model ) :对应 data 中的数据
- V:视图 ( View ) :模板,用户界面的可视部分
- VM:视图模型 ( ViewModel ) :Vue 实例对象,是连接视图和模型之间的桥梁
MVVM 的工作原理如下:
- 当用户与视图交互时,视图会触发相应的事件,例如点击按钮或输入文本。
- 视图模型监听这些事件,并根据用户的操作更新模型中的数据。
- 模型的数据发生变化时,视图模型会自动更新视图,使用户界面保持同步。
注意:
- data 中所有的属性,都会出现在 vm 身上,目的是让 vm 代理 data 的数据。
- vm 身上所有的属性及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用。
Vue 将 data 中的属性代理到 Vue 实例上,这样做的意义在于提供了一种简单直接的方式来访问和组件的数据。
2.6 数据代理
2.6.1 Object.defineproperty 方法
Object.defineProperty 是 JavaScript 中用于定义对象属性的方法之一,它主要用于实现 Vue 的响应式数据系统。
基本语法:
Object.defineProperty(obj, prop, descriptor)
- obj:要新增或修改属性的对象。
- prop:要新增或修改的属性名。
- descriptor:在此要实现 getter 和 setter 方法,还可以指定属性的特性
当有人读取了 prop 属性,调用 getter 方法,当有人修改了 prop 属性,调用 setter 方法。
代码示例:
<body>
<script type="text/javascript" >
let number = 18
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false
//当有人读取person的age属性时,get函数(getter)就会被调用
get(){
console.log('有人读取age属性了')
return number
},
//当有人修改person的age属性时,set函数(setter)就会被调用
set(value){
console.log('有人修改了age属性,且值是',value)
number = value
}
})
console.log(person)
</script>
</body>
2.6.2 模拟一个简单的数据代理
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>何为数据代理</title>
</head>
<body>
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)-->
<script type="text/javascript" >
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
</body>
</html>
当在代码中修改 obj2.x 的值时,实际上还会修改 obj.x 的值。这种机制类似于 Vue 中的响应式数据系统,能够实现数据的双向绑定效果。
2.6.3 Vue 中的数据代理
Vue 中的数据代理:通过 vm 对象来代理 data 对象中属性的操作,这能够让我们更加方便的操 data 中的数据。
基本原理:
- 通过 Object.defineProperty() 将 data 对象中所有属性添加到 vm 上。
- 为每一个添加到 vm 上的属性,都指定一个 getter 和 setter。
- 在 getter 和 setter 内部去操作 data 中对应的属性。
这样,当我们通过 vm 对象访问或修改属性时,实际上也是在访问或修改 data 对象中的属性,通过代理的方式,能让访问和修改更加方便,同时隐藏了内部的实现细节。
2.7 事件处理
2.7.1 事件的基本使用
事件的基本使用:
- 使用 v-on:xxx 或 @xxx 绑定事件,其中 xxx 是事件名;
- 事件的回调需要配置在methods对象中,这个方法最终会在 vm 上;
- methods 中配置的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或组件实例对象。
- @click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件的基本使用</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo1(event){
alert('同学你好!')
},
showInfo2(event,number){
console.log(event,number)
alert('同学你好!!')
}
}
})
</script>
</html>
效果如图所示:
2.7.2 事件修饰符
Vue 提供了一系列内置的事件修饰符,用于处理常见的事件需求,以下是 Vue 中常用的事件修饰符:
- .stop:阻止事件冒泡。
- .prevent:阻止事件的默认行为。
- .capture:在捕获阶段触发而不是冒泡阶段。
- .self:只当事件是从侦听器绑定的元素本身触发时才触发回调。如果子元素触发事件,不会触发回调。
- .once:事件将只触发一次。
- .passive:事件的默认行为立即执行,无需等待事件回调执行完毕。
- .native:表明事件是原生DOM事件。
代码示例:
<!-- 阻止事件冒泡 -->
<button @click.stop="handleClick">Click me</button>
<!-- 阻止事件的默认行为 -->
<form @submit.prevent="handleSubmit">
<!-- 表单内容 -->
</form>
<!-- 只在事件是从绑定的元素本身触发时才触发 -->
<div @click.self="handleClick">I won't trigger if you click on my children</div>
<!-- 只触发一次 -->
<button @click.once="handleClick">Click me once</button>
2.7.3 键盘事件
在 Vue 中,可以使用按键别名和系统修饰键来处理键盘事件
- 按键别名
- 回车:enter
- 删除:delete
- 退出:esc
- 空格:space
- 换行:tab( 需配合 keydown 使用 )
- 上箭头:up
- 下箭头:down
- 左箭头:left
- 右箭头:right
- 系统修饰键
- ctrl:Ctrl 键
- alt:Alt 键
- shift:Shif t键
- meta:Meta 键(Command 键或 Windows 键)
系统修饰键的使用方式:
- 配合 keyup 使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- 配合 keydown 使用:正常触发事件。
示例:
<!-- 使用按键别名绑定事件 -->
<input type="text" @keyup.enter="handleEnter">
<!-- 使用系统修饰键绑定事件 -->
<input type="text" @keyup.ctrl.67="handleCtrlC"> <!-- 当按下Ctrl+C时触发 -->
2.8 计算属性
2.8.1 计算属性的使用
在 Vue 中,计算属性是一种用于对数据进行处理和计算的特殊属性,它的值是根据其依赖的数据动态计算而来的,当依赖的数据发生变化时,计算属性会自动重新求值,计算属性最终会出现在 vm 上,直接读取使用即可。
如何使用计算属性:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 计算属性示例</title>
<script src="vue.js"></script> <!-- 这里是你的 Vue.js 路径 -->
</head>
<body>
<div id="app">
<h1>{{ message }}</h1>
<p>大写消息: {{ uppercaseMessage }}</p>
<input v-model="message" placeholder="输入消息">
<h2>计数: {{ count }}</h2>
<p>平方值: {{ squaredCount }}</p>
<input v-model="count" type="number" placeholder="输入数字">
</div>
<script>
new Vue({
el: '#app',
data: {
message: '你好',
count: 0
},
computed: {
uppercaseMessage: {
get() {
return this.message.toUpperCase(); // 返回大写的 message
},
set(value) {
this.message = value.toLowerCase(); // 设置时转换为小写
}
},
squaredCount: {
get() {
return this.count * this.count; // 返回 count 的平方
},
set(value) {
this.count = Math.sqrt(value); // 设置时计算平方根
}
}
}
});
</script>
</body>
</html>
注意:计算属性中的 get 函数有两次执行时机:
- 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
而 set 的执行时机只有一次:
- 当计算属性被修改时
计算属性最终会出现在 vm 上,直接读取使用即可。
2.8.2 计算属性的简写
简写方式通常适用于只需要定义 get
方法的情况,如果需要同时定义 set
方法,还是需要使用完整的方式。
简写形式:
computed: {
uppercaseMessage() {// uppercaseMessage 是计算属性 函数则是 get
return this.message.toUpperCase();
},
squaredCount() { // squaredCount 是计算属性,函数是get
return this.count * this.count;
}
}
完整形式:
computed: {
uppercaseMessage: {
get() {
return this.message.toUpperCase();
},
set(value) {
this.message = value.toLowerCase();
}
},
squaredCount: {
get() {
return this.count * this.count;
},
set(value) {
this.count = Math.sqrt(value);
}
}
}
2.9 监视属性
监视属性 watch:
- 当被监视的属性变化时, 回调函数自动调用, 进行相关操作
- 监视的属性必须存在,存在才能进行监视
监视的两种写法:
- new Vue 时传入 watch 配置
- 通过 vm.$watch 监视
2.9.1 监视属性基础
- 使用
watch
选项
new Vue({
el: '#app',
data: {
message: 'Hello',
count: 0
},
watch: {
message(newVal, oldVal) {
console.log(`消息改变了:从 ${oldVal} 变为 ${newVal}`);
},
count(newVal, oldVal) {
console.log(`计数改变了:从 ${oldVal} 变为 ${newVal}`);
}
}
});
- 手动创建
watcher
对象
const vm = new Vue({
el: '#app',
data: {
message: 'Hello',
count: 0
}
});
// 动态监视 'message' 属性
vm.$watch('message', function(newVal, oldVal) {
console.log(`消息改变了:从 ${oldVal} 变为 ${newVal}`);
});
// 动态监视 'count' 属性
vm.$watch('count', function(newVal, oldVal) {
console.log(`计数改变了:从 ${oldVal} 变为 ${newVal}`);
});
注意:
- oldVal:是属性在发生变化前的原始值。
- newVal:是属性在发生变化后更新后的值。
2.9.2 深度监视
默认情况下,Vue 的 watch 只会对对象本身进行浅层监视。如果对象的内部属性发生变化,而对象本身的引用没有改变,Vue 不会触发 watch,通过启用深度监视,可以监听对象内部的所有属性变化。
我们可以通过在 watch 选项中设置 deep: true 来实现深度监视:
const vm = new Vue({
data: {
obj: {
a: 1,
b: 2
}
},
watch: {
obj: {
changer(newVal, oldVal) {
console.log('obj 变化了');
},
deep: true
}
}
});
// 通过以下方式修改 obj 内部的属性,会触发深度监视
vm.obj.a = 3; // 控制台输出:obj 变化了
2.9.3 监视属性简写
- 第一种写法的简写:
watch: {
// 使用完整形式的监视属性
isHot: {
changer(newValue, oldValue) {
console.log('isHot 被修改了', newValue, oldValue);
}
},
// 使用监视属性简写
isHot(newValue, oldValue) {
console.log('isHot 被修改了', newValue, oldValue, this);
}
}
});
- 第二种写法的简写:
// 正常写法
vm.$watch('Hot', {
immediate: true, // 初始化时让 handler 调用一下
deep: true, // 深度监视
changer(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
}
});
// 简写
vm.$watch('isHot', (newValue, oldValue) => {
console.log('isHot被修改了', newValue, oldValue, this);
});