vue3基础
官网:[简介 | Vue.js (vuejs.org)](https://cn. vuejs.org/guide/introduction.html)
可以使用CDN
可以用源码
使用html界面,vscode导入vue插件,简单学习vue语法
hello word
## v-html及v-text
## v-if及v-else、v-show
```HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- v-if 是 Vue.js 中用于条件性地渲染一块内容,内容只会在指令的表达返回true值的时候被渲染。
v-else 可用于提供条件为flase时的替代内容。
功能:当 age 的值大于 18 时,页面会显示“成年人”;
否则,显示“未成年人”。初始值设定为 18,因此会显示“未成年人” -->
<!-- v-if -->
<h1 v-if="age > 18">成年人</h1>
<h1 v-else>未成年人</h1>
<h1 v-show="ok">hello!</h1>
</div>
<script>
const app= {
data() {
return{
age:19,
}
}
}
Vue.createApp(app).mount('#app');
</script>
</body>
</html>
v-for
基于数组来渲染一个列表,需要 使用 it em in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in list"
:key="index">{{item}}</li>
</ul>
</div>
<script>
const app = {
data(){
return{
list:['张三','关于','刘备']
}
}
}
Vue.createApp(app).mount('#app');
</script>
<!-- vue应用app1 -->
<div id="app1">
<ul>
<!-- 不加index,直接使用数组的索引作为key,但是如果元素顺序改变,那么会丢失渲染效果或者状态
key可以为每个渲染的元素提供一个唯一表示,[动态更新列表]的时候可以使用-->
<li v-for="item in items"
:key="index">{{item}}</li>
</ul>
</div>
<script>
const app1 = {
data(){
return{
// 数组多个元素
items:['张三','关于','刘备']
}
}
}
Vue.createApp(app1).mount('#app1');
// 挂载app1
</script>
<!-- key的功能:为每一个渲染元素提供唯一标识,动态更新列表
购物车案例-->
<!-- 当商品被删除或添加时,Vue 可以通过 id 快速识别哪些商品发生了变化,
[从而只更新这些特定的元素,而不是重新渲染整个列表。]
这提高了性能并减少了潜在的错误(例如,状态丢失)。 -->
<ul id="app2">
<li v-for="item in cart"
:key="item.id">{{ item.name }}</li>
<!-- 可以.属性获取对象的不同属性 -->
</ul>
<script>
const app2 ={
data() {
return {
// 数组包含对象[{"name": "Alice"}, {"name": "Bob"}]【js语法称对象】
cart: [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
]
}
}
}
Vue.createApp(app2).mount('#app2');
// vue的应用app2,用mount挂载app2
</script>
</body>
</html>
维护状态
- 当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更 新”的策略。
- 如果数据项的顺序被改变,Vue 将不会移动 DOM 元素 来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每 个索引位置正确渲染。
- 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和 重新排序现有元素,你需要为每项提供一个唯一的 key attribute:
也就是购物车的例子。
事件处理
v-on、内联处理器(事件传递参数)
v-on监听事件
v-on 指令 (通常缩写为 @ 符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为 v-on:click="methodName"
或 使用快捷方式 @click="methodName
事件处理方法
- 然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写 在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法。
内联处理器中的方法(事件传递参数)
- 这是官方的翻译称呼,其实我们可以直接叫他 “事件传递参数”
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.status {
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
background-color: orange;
color: #fff;
}
</style>
</head>
<body>
<!-- v-on监听dom事件,并在触发事件的时候执行一些js -->
<div id="app">
<div class="status" v-on:click="methodName">点我</div>
<p></p>
<div class="status" @click="methodName1">点我缩写的1</div>
<button @click="counter += 1">加1</button>
<p>当前计数: {{ counter }}</p>
<p>---内联处理器(事件传递参数)</p>
<button @click="say('hi')">say hiokok</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const app = {
methods: { // 记住为methods
methodName() {
console.log('我被点了一下');
},
methodName1() {
console.log('我被点了一下');
},
say(message){
alert(message)
}
},
data() {
return {
counter: 0
}
}
}
Vue.createApp(app).mount('#app');
</script>
</body>
</html>
表单输入绑定
v-model
- 用 v-model 指令在表单、 及 元素上创 建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
- 用起来非常神奇
- 但 v-model 本质上不过是语法糖。它负责监听用 户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理。
语法糖:
v-model
被称为“语法糖”,这意味着它为你简化了代码的写法。实际上,v-model
处理了事件监听和数据更新,背后使用了相应的方法(如input
事件)来同步数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<!-- v-model 在input textarea select元素上进行数据绑定,-->
<div id = 'app'>
<input type="text" v-model = "account" @keyup = "change">
<p>当前账户: {{ account }}</p>
<!-- 每次在输入框中输入内容时,account 的值会更新 -->
</div>
<script>
const app = {
data(){
return {
account:'zs'
}
},
methods:{
change(){
console.log(this.account)
}
}
}
Vue.createApp(app).mount('#app');
</script>
</body>
</html>
v-bind
v-bind能动态绑定html标签中的属性,在这个例子中,当vue实例中的值变化的时候,html标签中的id值也会随着动态变化?
ChatGPT
是的,正如你所描述的,当 Vue 实例中的 dynamicId
值发生变化时,使用 v-bind
绑定的 HTML 标签中的 id
属性值也会自动更新。这是 Vue 的响应式特性之一。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 插值表达式 -->
<p> {{msg}} - {{title}} - {{price}} 123</p>
<!-- 指令v-html,v-text -->
<!-- 指令v-html和v-text是将标签中的内容进行覆盖
功能:为了输出原始的html,需要使用v-html指令
-->
<p v-html="msg">我被覆盖了</p>
<p v-text="title">我也被覆盖了</p>
<!--
v-bind
v-bind 将 Vue 实例中的数据动态绑定到 HTML 元素的属性,而不是使用 Mustache 语法。
-->
<!-- 此处将id属性绑定为“动态id”与vue实例中的值绑定 -->
<p v-bind:id = "dynamicId">我是v-bind</p>
<!-- v-html输出html标签渲染的内容,显示在页面上 -->
<p v-html="rawhtml">我被覆盖了</p>
<!-- v-text输出原始的html标签代码,显示在页面上
也就是输出文本 -->
<p v-text="rawhtml">我也被覆盖了</p>
</div>
<script>
Vue.createApp({
data() {
return {
msg: 'hello vue3',
title: 'one piece',
price: '$163',
rawhtml:"<a href='https://www.baidu.com'>百度</a>",
dynamicId:1001
}
}
}).mount('#app')
</script>
</body>
</html>
修饰符.lazy
- 默认行为:
- 默认情况下,
v-model
会在每次input
事件触发后将输入框的值与数据同步。这意味着每当用户在输入框中输入内容时,Vue 实例中的数据会实时更新。
- 默认情况下,
.lazy
修饰符:- 使用
.lazy
修饰符后,数据将会在change
事件触发时进行同步,而不是在每次input
事件时。这适用于希望在用户完成输入并离开输入框时才更新数据的场景。
- 使用
change事件
- 只有在输入框失去焦点(例如,点击其他地方)
- 或者用户按下“Enter”键等触发
change
事件时,message
的值才会更新。
可以更控制何时更新数据,避免频繁的更新操作。
第二个vueapp1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<!-- v-model 在input textarea select元素上进行数据绑定
此处account有初始值zs,输入框输入内容会改变account变量的值,进行绑定了-->
<div id = 'app'>
<input type="text" v-model = "account" @keyup = "change">
<p>当前账户: {{ account }}</p>
<!-- 每次在输入框中输入内容时,account 的值会更新 -->
</div>
<script>
const app = {
data(){
return {
account:'zs'
}
},
methods:{
change(){
console.log(this.account)
}
}
}
Vue.createApp(app).mount('#app');
</script>
<!-- 第二个vueapp 对输入框的内容与 message 变量绑定。
输入框内容和Vues实例的data()函数的message变量进行绑定。
在输入框中输入内容,message的值会自动更新,p标签也显示message的值
。-->
<div id="app1">
<!-- <input v-model="message" placeholder="edit me" /> -->
<input v-model.lazy="message" placeholder="edit me" />
<p>Message is: {{ message }}</p>
</div>
<script>
const app1 = {
data() {
return {
message:""
}
}
}
Vue.createApp(app1).mount('#app1');
</script>
</body>
</html>
修饰符.trim
过滤用户输入的首尾空白字符
报错
- vue.global.js:2260 [Vue warn]: Property “methodName1” was accessed during render but is not defined on instance. at
- 属性"methodName1"在渲染期间内被访问了,但是没有自傲实例中定义。
调用了methodNme1但是没有写该方法导致的。
hello
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.status {
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
background-color: orange;
color: #fff;
}
</style>
</head>
<body>
<!-- v-on监听dom事件,并在触发事件的时候执行一些js -->
<div id="app">
<div class="status" v-on:click="methodName">点我</div>
<p></p>
<div class="status" @click="methodName1">点我缩写的1</div>
<button @click="counter += 1">加1</button>
<p>当前计数: {{ counter }}</p>
<p>---内联处理器(事件传递参数)</p>
<button @click="say('hi')">say hiokok</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const app = {
methods: { // 记住为methods
methodName() {
console.log('我被点了一下');
},
methodName1() {
console.log('我被点了一下');
},
say(message){
alert(message)
}
},
data() {
return {
counter: 0
}
}
}
Vue.createApp(app).mount('#app');
</script>
</body>
</html>
vue中js表达式
-
Vue 允许在模板中使用单个 JavaScript 表达式进行数据绑定。
但不能使用多条语句或复杂的控制结构。
这些表达式在当前 Vue 实例的数据作用域下被解析。
例如:
- JavaScript 表达式:
{{ number + 1 }}
:将number
增加 1。{{ ok ? 'YES' : 'NO' }}
:根据ok
的布尔值返回'YES'
或'NO'
。{{ message.split('').reverse().join('') }}
:将message
字符串反转。
- 限制:
- 但每个绑定只能包含单个表达式,不能是语句。
- 例如,以下示例无效:
{{ vara = 1 }}
:这是一个赋值语句,不是表达式。if (ok) return message
:这是一个控制结构,也不是表达式。
- 条件逻辑使用三元表达式:
- 对于条件逻辑,你需要使用三元运算符(
? :
),而不能使用if
语句,因为if
是语句,不会返回值。
- 对于条件逻辑,你需要使用三元运算符(
单文件组件
Vue单文件组件Single File Component,*.Vue文件,缩写为SFC
通常以 .vue
为扩展名。它将 Vue 组件的模板、逻辑和样式封装在一个文件中,使得组件的结构更加清晰和易于管理。
一个 Vue 单文件组件通常包含三个部分:<template>
、<script>
、<style>
<template>
- 这一部分定义了组件的 HTML 模板。在这里,你可以使用 Vue 的指令和语法来绑定数据和事件。
主组件
```JS
<template>
<!-- 如何在主组件中加载组件? -->
<div>
<h1>我的主应用</h1>
<!-- 3.显示组件 -->
<Zujian-vue></Zujian-vue>
</div>
</template>
<script>
// 1.引入
import Zujian from './zujian.vue';
export default {
// 2.挂载组件
components: {
Zujian
}
};
</script>
<style scoped>
h3{
color:rgb(5, 0, 99);
}
</style>
创建Vue项目,运行单文件组件
npm install -g @vue/cli
vue --version
vue create vue-demo
按键盘数字选择Babel和 Progressive Web App (PWA) Support 两个选项即可
npm run serve
运行成功后,删除App.vue
在/src目录下创建MainZujian.vue
在src/components目录下创建zujian.vue
如下:
<template>
<!-- zujian.vue模板 -->
<h3>单文件组件</h3>
</template>
<script>
// 逻辑
export default {
name:"Zujian"
}
</script>
<style scoped>
/* 样式 */
h3{
color:red;
}
</style>
<template>
<!-- MainZujian.vue-->
<!-- 如何在主组件中加载组件? -->
<div>
<h1>我的主应用</h1>
<!-- 3.显示组件 -->
<!-- <Zujian-vue></Zujian-vue> -->
<zujian></zujian>
</div>
</template>
<script>
// 1.引入子组件
import Zujian from './components/zujian.vue';
export default {
// 2.挂载组件
components: {
Zujian
}
};
</script>
<style scoped>
h3{
color:rgb(5, 0, 99);
}
</style>
npm run serve
看到实现了
Props组件交互(自定义的属性)
- 组件与组件之间是需要存在交互的,否则完全没关系,组件的意义就很小了
Prop 是什么:
-
Prop 是你可以在组件上注册的一些自定义 attribute(属性)
- 这句话写的就不像人话
-
我来说:它就是 Vue 组件中自定义的属性,用于父组件向子组件传递数据。
在父组件使用子组件时,可以通过类似 :title="标题"
这样的方式传递一个值到子组件的 title
属性。
在父组件中:
<zujian title="我是父组件中使用子组件,定义的标题属性,即将传递给子组件"></zujian>
子组件中:
Vue模板的逻辑中,添加:
props:{
title:{
type:String,
default:""
//二、 default就是如果没设置子组件的title属性那么默认为空字符串
}
代码:
子组件:
<template>
<!-- 模板 -->
<div>
<h3>单文件组件</h3>
<p>{{ title }} </p>
<!-- 二、插值语法展示从父组件传过来的title属性的值 -->
<slot></slot>
</div>
</template>
<script>
// 逻辑
export default {
name:"Zujian",
// 二、这里是子组件的 自定义属性props
props:{
title:{
type:String,
default:""
//二、 default就是如果没设置子组件的title属性那么默认为空字符串
}
}
}
</script>
<style scoped>
/* 样式 */
h3{
color:red;
}
</style>
主组件:
<template>
<!-- 如何在主组件中加载组件? -->
<div>
<h1>我的主应用</h1>
<!-- 3.显示组件 -->
<!-- <Zujian-vue></Zujian-vue> -->
<zujian title="我是父组件使用子组件,定义的标题属性 1+1">123</zujian>
<!-- 二、prop的传递:title的值传递给子组件 -->
</div>
</template>
<script>
// 1.引入子组件
import Zujian from './components/zujian.vue';
export default {
// 2.挂载组件
components: {
Zujian
}
};
</script>
<style scoped>
h3{
color:rgb(5, 0, 99);
}
</style>
- 一定记得进入,有package.json在的文件目录中运行,否则报错缺少配置文件。
- 同样如果删除了package.json需要手动导入依赖,或者重新创建vue项目。
报错:The template root requires exactly one element.eslint-plugin-vue
- Vue模板需要有且仅有一个根元素
- 有该报错网页仍然可正常运行
显然这里,在template模板中,有两个元素了。
解决:加上div包裹起来就行。
问题:Vue使用组件时,标签内的内容默认不会自动显示在子组件中
在这里表示为,父组件中的子组件标签中的123不会显示出来。
<zujian title="我是父组件使用子组件,定义的标题属性 1+1">123</zujian>
解决:显式地使用:插槽slot
子组件的模板中使用 <slot>
即可
<template>
<!-- 模板 -->
<div>
<h3>单文件组件</h3>
<p>{{ title }} </p>
<!-- 二、插值语法展示从父组件传过来的title属性的值 -->
<slot></slot>
</div>
</template>
Prop类型
Prop传递参数其实是没有类型限制的
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,//返回工厂模式
author: Object,//返回工厂模式
callback: Function
}
数据类型为数组或者对象的时候,默认值是需要返回工厂模式
- 因为如果直接使用数组或对象作为默认值,所有组件实例将共享同一个引用,这可能导致意外的副作用
可以看到在prps中自定义的attribute属性有:
- titile标签是String
- likes是Number类型
Number
是 JavaScript 中的一种基本数据类型,用于表示数值。表示整数正负数和浮点数。
- isPublished: Boolean,布尔类型
- 评论id是数组类型
- 作者是Object对象类型
- callback是Function函数,用于父子组件之间的交互。
callback
:
- 类型: Function
- 用途: 这是一个函数 Prop,通常用于处理事件或与父组件进行交互。例如,在子组件中可能会有一些操作(如删除评论、点赞等),当这些操作发生时,子组件可以调用这个函数来通知父组件执行相应的操作。
工厂模式
工厂模式要求你返回一个函数,该函数每次调用时都会返回一个新的数组或对象实例。
这样,每个组件实例都有自己的独立数据,避免了共享引用带来的潜在问题。
示例:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: {
type: Array,
default: () => [] // 返回一个新的空数组
},
author: {
type: Object,
default: () => ({}) // 返回一个新的空对象
},
callback: Function
}
解释:
default: () => []
:这里使用了一个箭头函数,每次组件实例创建时都会调用这个函数,返回一个新的空数组。default: () => ({})
:同样,返回一个新的空对象。
确保了每个组件实例都拥有自己独立的 commentIds
和 author
数据,避免了共享引用带来的潜在问题。
$emit自定义事件组件交互
自定义事件可以在组件中反向传递数据
我们知道: prop 可以将数据从父组件 传递到子组件
自定义事件$emit
-
作用:发出自定义事件(同时传递参数),让父组件能够响应子组件中的特定操作。
- 而响应需要父组件中用@onCustom=“getData"进行监听
- 然后再调用函数执行特定操作。
- 而响应需要父组件中用@onCustom=“getData"进行监听
-
主要作用:
- 事件触发: 子组件可以通过
$emit
方法触发事件,向父组件发送通知。例如,用户在子组件中点击按钮后,子组件可以通过$emit
触发一个事件。 - 数据传递:
$emit
还可以携带参数,使得子组件能够将数据传递给父组件。这对于需要在子组件中处理一些逻辑,然后将结果反馈给父组件的场景非常有用。 - 增强组件复用性: 使用
$emit
使得子组件更具灵活性和可复用性。父组件可以根据自己的需求来处理子组件发出的事件,而子组件本身不需要知道具体的实现细节。
- 事件触发: 子组件可以通过
子组件:
<template>
<div>
<h3>单文件组件</h3>
<button @click="sendHandle">发送数据</button>
</div>
</template>
<script>
export default {
name: "Zujian",
props: {
title: {
type: String,
default: ""
}
},
methods: {
sendHandle() {
this.$emit("onCustom", "数据"); // 触发自定义事件,并传递数据
}
}
}
</script>
<style scoped>
h3 {
color: red;
}
</style>
主组件:
<template>
<div>
<h1>我的主应用</h1>
<zujian @onCustom="getData"></zujian> <!-- 监听 onCustom 事件 -->
</div>
</template>
<script>
import Zujian from './components/zujian.vue';
export default {
components: {
Zujian
},
methods: {
getData(data) {
console.log(data); // 打印接收到的数据
}
}
}
</script>
<style scoped>
h3 {
color: rgb(5, 0, 99);
}
</style>
可以看到,子组件中设置点击按钮@click触发sendHandle方法, 然后$emit该方法,发出onCustom的事件,传递数据
作为参数。
在主组件中@onCustom=“getData"监听子组件发出的onCustom事件,被触发后getData调用打印传递的数据。
组件的生命周期
每个组件在被创建时都要经过一系列的初始化过程——例如,需要 设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更 新 DOM 等。
同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会
为了方便记忆,我们可以将他们分类
创建时:beforeCreate、created
渲染时:beforeMount、mounted
更新时:beforeUpdate、updated
卸载时:beforeUnmount、unmounted