Vue初体验
案例一:展示动态数据
<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>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./lib/vue.js"></script>
<script>
// 通过引入的文件,创建Vue对象
const app = Vue.createApp({
// 利用插值语法将data中设置的变量展示到页面中
template: `<h2>{{message}}</h2>`,
// 参数
data: function() {
return {
message: "Hello Vue"
}
}
})
// 将对象挂在到id=app的组件上
app.mount("#app")
</script>
<!-- <script>
const app = Vue.createApp({
// 插值语法: {{title}}
template: `<h2>{{message}}</h2>`,
data: function() {
return {
title: "Hello World",
message: "你好啊, Vue3"
}
}
})
app.mount("#app")
</script> -->
</body>
</html>
案例二:展示数组
<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>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./lib/vue.js"></script>
<script>
// 通过引入的文件,创建Vue对象
const app = Vue.createApp({
// 利用插值语法将data中设置的变量展示到页面中
template: `<h2>{{message}}</h2>`,
// 参数
data: function() {
return {
message: "Hello Vue"
}
}
})
// 将对象挂在到id=app的组件上
app.mount("#app")
</script>
<!-- <script>
const app = Vue.createApp({
// 插值语法: {{title}}
template: `<h2>{{message}}</h2>`,
data: function() {
return {
title: "Hello World",
message: "你好啊, Vue3"
}
}
})
app.mount("#app")
</script> -->
</body>
</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>Document</title>
</head>
<body>
<div id="app">
<h2>当前计数:{{count}}</h2>
<!-- 绑定方法 -->
<button @click="sub">-1</button>
<button @click="add">+1</button>
</div>
<!-- 引入vue -->
<script src="./lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function() {
return {
count: 0
}
},
// 创建methods方法集合
methods: {
add() {
this.count++;
},
sub() {
this.count--;
}
}
})
app.mount("#app")
</script>
</body>
</html>
声明式和命令式
命令式编程关注的是 “how to do”自己完成整个how的过程:不如JavaScript 声明式编程程关注的是 “what to do”,由框架(机器)完成 “how”的过程
MVVM模型
早期MVC是常使用的架构模式
- Model:管理数据及业务逻辑。
- View:负责界面展示。
- Controller:接收用户输入,协调 Model 和 View 的交互。
MVC里,Model处理数据,View显示界面,Controller作为中间层处理逻辑。用户交互会先到Controller,然后更新Model,再通知View更新。但有时候,View和Model之间可能会有直接的联系,特别是在一些框架里
现在最常用的架构模式是mvvm
- Model:数据层,与 MVC 类似。
- View:纯 UI 展示,不处理逻辑。
- ViewModel:代替 Controller,将 Model 数据转换为 View 可用的形式(通过数据绑定),并处理业务逻辑。
Model和View还是类似的角色,但ViewModel取代了Controller的位置。ViewModel负责将Model的数据转换成View能直接显示的形式,并且通过数据绑定自动同步,比如使用双向绑定技术。这样的话,View和ViewModel之间的交互更自动化,减少了样板代码,View层会更被动,只负责显示数据,而业务逻辑都在ViewModel里处理。
data属性
存放在data中的数据将会被Vue的响应式系统劫持,之后对该对象的修改或者访问都会在劫持中被处理:
methods
methods属性是一个对象,通常我们会在这个对象中定义很多的方法;这些方法可以被绑定到 模板中;在该方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性;
computed
在模板中可以直接通过插值语法显示一些data中的数据。 但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示; 那么有三种方法可以实现:
- 插值语法
- methods:每一次调用都要执行一遍代码
- computed:计算属性会基于它们的依赖关系进行缓存,只有在依赖数据发生改变的时候,才会去执行代码;
<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>Document</title>
</head>
<body>
<div id="app">
<!-- 插值语法表达式直接进行拼接 -->
<!-- 1.拼接名字 -->
<h2>{{ fullname }}</h2>
<h2>{{ fullname }}</h2>
<h2>{{ fullname }}</h2>
<!-- 2.显示分数等级 -->
<h2>{{ scoreLevel }}</h2>
<!-- 3.反转单词显示文本 -->
<h2>{{ reverseMessage }}</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
// 1.姓名
firstName: "kobe",
lastName: "bryant",
// 2.分数: 及格/不及格
score: 80,
// 3.一串文本: 对文本中的单词进行反转显示
message: "my name is why"
}
},
computed: {
fullname() {
return this.firstName + " " + this.lastName
},
scoreLevel() {
return this.score >= 60? "及格":"不及格"
},
reverseMessage() {
return this.message.split(" ").reverse().join(" ")
}
}
// computed: {
// // 1.计算属性默认对应的是一个函数
// fullname() {
// return this.firstName + " " + this.lastName
// },
// scoreLevel() {
// return this.score >= 60 ? "及格": "不及格"
// },
// reverseMessage() {
// return this.message.split(" ").reverse().join(" ")
// }
// }
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>
完整写法
计算属性在大多数情况下,只需要一个getter方法即可,所以我们会将计算属性直接写成一个函数;但是如果需要给计算属性设置值,就要写setter方法
<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>Document</title>
</head>
<body>
<div id="app">
<h2>{{ fullname }}</h2>
<button @click="setFullname">设置fullname</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
firstname: "coder",
lastname: "why"
}
},
computed: {
// 语法糖的写法
// fullname() {
// return this.firstname + " " + this.lastname
// },
// 完整的写法:
fullname: {
get: function() {
return this.firstname + " " + this.lastname
},
set: function(value) {
const names = value.split(" ")
this.firstname = names[0]
this.lastname = names[1]
}
}
},
methods: {
setFullname() {
this.fullname = "kobe bryant"
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>
watch
开发中我们在data返回的对象中定义了数据,这个数据通过插值语法等方式绑定到template中;当数据变化时,template会自动进行更新来显示最新的数据;但是在某些情况下,我们希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器watch来完成了;
<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>Document</title>
</head>
<body>
<div id="app">
<h2>{{message}}</h2>
<button @click="changeMessage">修改message</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// Proxy -> Reflect
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
message: "Hello Vue",
info: { name: "why", age: 18 }
}
},
methods: {
changeMessage() {
this.message = "你好啊, 李银河!"
this.info = { name: "kobe" }
}
},
watch: {
// 1.默认有两个参数: newValue/oldValue
message(newValue, oldValue) {
console.log("message数据发生了变化:", newValue, oldValue)
},
info(newValue, oldValue) {
// 2.如果是对象类型, 那么拿到的是代理对象
console.log("info数据发生了变化:", newValue, oldValue)
console.log(newValue.name, oldValue.name)
// 3.获取原生对象
// console.log({ ...newValue })
// console.log(Vue.toRaw(newValue))
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>
其他参数
当监听的对象是一个拥有多个对象的属性时,当对里面的属性进行修改时,只监听对象本身的是监听不到的,如果需要监听,则需要监听具体的属性,或者也可以使用deep选项进行深度监听;如果想要代码一运行就进行监听,则可以加上immediate选项,这个时候无论后面数据是否有变化,侦听的函数都会有限执行一次
<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>Document</title>
</head>
<body>
<div id="app">
<h2>{{ info.name }}</h2>
<button @click="changeInfo">修改info</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
info: { name: "why", age: 18 }
}
},
methods: {
changeInfo() {
// 1.创建一个新对象, 赋值给info
// this.info = { name: "kobe" }
// 2.直接修改原对象某一个属性
this.info.name = "kobe"
}
},
watch: {
// 默认watch监听不会进行深度监听
// info(newValue, oldValue) {
// console.log("侦听到info改变:", newValue, oldValue)
// }
// 进行深度监听
info: {
handler(newValue, oldValue) {
console.log("侦听到info改变:", newValue, oldValue)
console.log(newValue === oldValue)
},
// 监听器选项:
// info进行深度监听
deep: true,
// 第一次渲染直接执行一次监听器
immediate: true
},
"info.name": function(newValue, oldValue) {
console.log("name发生改变:", newValue, oldValue)
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>
监听方式二
还有另外一种方式就是使用 watchs 来侦听;第一个参数是要侦听的源;第二个参数是侦听的回调函数callback;第三个参数是额外的其他选项,比如deep、immediate;
<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>Document</title>
</head>
<body>
<div id="app">
<h2>{{message}}</h2>
<button @click="changeMessage">修改message</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
message: "Hello Vue"
}
},
methods: {
changeMessage() {
this.message = "你好啊, 李银河!"
}
},
// 生命周期回调函数: 当前的组件被创建时自动执行
// 一般在该函数中, 会进行网络请求
created() {
// ajax/fetch/axios
console.log("created")
this.$watch("message", (newValue, oldValue) => {
console.log("message数据变化:", newValue, oldValue)
}, { deep: true })
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>
v-model
v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定;
原理:v-bind绑定value属性的值;v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;
<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>Document</title>
</head>
<body>
<div id="app">
<!-- 1.手动的实现了双向绑定 -->
<!-- <input type="text" :value="message" @input="inputChange"> -->
<!-- 2.v-model实现双向绑定 -->
<!-- <input type="text" v-model="message"> -->
<!-- 3.登录功能 -->
<label for="account">
账号:<input id="account" type="text" v-model="account">
</label>
<label for="password">
密码:<input id="password" type="password" v-model="password">
</label>
<button @click="loginClick">登录</button>
<h2>{{message}}</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
message: "Hello Model",
account: "",
password: ""
}
},
methods: {
inputChange(event) {
this.message = event.target.value
},
loginClick() {
const account = this.account
const password = this.password
// url发送网络请求
console.log(account, password)
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>