Vue界面基础/ VUE 用自己的话讲.md
2024年10月28日13:40:04 姜姜
前提:主做公司后端(Java),想往全栈走。正好最近很多前端界面(Vue),补一下知识,看自己能不能对应补充前端功能点。
基础:B站走一遍
https://www.bilibili.com/video/BV1Wr4y1R7Bd/?spm_id_from=333.880.my_history.page.click&vd_source=e4d185f1cbe89993d29665a191af2d7c
深入全面:菜鸟教程看一遍。以下是看到 Vue3基础语法结束 https://www.runoob.com/vue3/vue3-directives.html
目录
文章目录
- Vue界面基础/ VUE 用自己的话讲.md
- 目录
- 1、VUE是三点式响应:创建实例HelloVueApp->挂载(动作)到使用界面元素,重点
- 1.2 找到id **mount('#hello-vue'**)
- 1.3 响应式修改意义:VUE 双向绑定的机制vm.count=5 (可以理解data() {} 函数里是初始化,通过双向绑定进行数据修改,因此一般是通过绑定的vm进行数据修改,理解到java中反而像是赋值给示例,同时会修改了整体所有类的值(用随后的所有初始化定义 ->理解)。)
- 1.4 data()函数 method{} 区别和关系
- 1.6 界面执行步骤:解析语言-->创建实例并绑定->根据实例函数初始化数据+初始化数据渲染到html调用处-->响应式变化
- 2、基础语法
- 5 组件 看懂了-详细解释
- 4、组件间通过props传递数据,那数据初始在哪定义呢-->数据流向
- 4.1 关键点总结:
- 4.2 记住:单向数据流,子组件不能直接修改,需要使用event时间通知父组件进行修改,
- 4.3 数据定义+赋值:--->因此数据格式理解为:父组件定义变量A、变量B, 子组件定义变量C、变量D,父子组件都在createApp即创建实例定义,但父组件在data()函数中,子组件在app.component()定义,即data()中有变量A、B,app.component()也有变量C、D,注意A与C名称可以不一致,B与D名称可以不一致。所以他们并没有直接关联关系,只是通过数据传递,把A赋值给了C,把B赋值给了D,因此注意定义与赋值。
- 4.3.2 Props 的详细定义和验证:
- 5 事件->事件,如何被触发,执行了哪些内容
1、VUE是三点式响应:创建实例HelloVueApp->挂载(动作)到使用界面元素,重点
1.1 重点语句:Vue.createApp(HelloVueApp).mount(‘#hello-vue’),这个位置不要分开理解,只要理解这一句即可。什么则对应匹配即可。
PS:双向数据绑定机制
1、使用位置
2、设置 变量显示内容
3、挂载 把设置挂载上使用位置。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/3.2.26/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
{{ message }}
</div>
<script>
const HelloVueApp = {
data() {
return {
message: 'Hello Vue!!'
}
}
}
Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</body>
</html>
1.2 找到id mount(‘#hello-vue’)
<div id="hello-vue"></div>
1.3 响应式修改意义:VUE 双向绑定的机制vm.count=5 (可以理解data() {} 函数里是初始化,通过双向绑定进行数据修改,因此一般是通过绑定的vm进行数据修改,理解到java中反而像是赋值给示例,同时会修改了整体所有类的值(用随后的所有初始化定义 ->理解)。)
-
首先,创建 Vue 实例:
const app = Vue.createApp({
data() {
return { count: 4 } // 初始化数据
}
})
-
挂载到 DOM:
const vm = app.mount('#app')
这时 Vue 会:
- 创建一个组件实例 vm
- 将数据对象
{ count: 4 }
转换为响应式对象 - 在 vm 上创建两种访问方式:
vm.count
(直接访问)vm.$data.count
(通过 $data 访问)
-
访问数据的两种方式:
document.write(vm.$data.count) // 输出: 4 (通过 $data 访问)
document.write(vm.count) // 输出: 4 (直接访问)
这两种方式实际上访问的是同一个数据源。
-
数据修改:
// 方式1:直接修改
vm.count = 5
document.write(vm.$data.count) // 输出: 5
// 方式2:通过 $data 修改
vm.$data.count = 6
document.write(vm.count) // 输出: 6
这里的工作原理是:
- Vue 使用 JavaScript 的 Proxy(在 Vue 3 中)来实现数据的响应式:
// 简化的内部实现原理示例
const vm = {
get count() {
return this.$data.count;
},
set count(value) {
this.$data.count = value;
// 触发更新视图
}
}
-
当你通过任意一种方式修改数据时:
vm.count = 5
// 或
vm.$data.count = 6
Vue 都会:
- 更新数据值
- 触发视图更新
- 保持两种访问方式的数据同步
所以无论你使用哪种方式修改数据,最终都会:
- 更新数据源中的值
- 通知 Vue 响应式系统
- 更新所有使用该数据的地方(包括视图和其他依赖)
这就是为什么你能看到:
vm.count = 5
console.log(vm.$data.count) // 5
vm.$data.count = 6
console.log(vm.count) // 6
这两种访问和修改方式是完全等价的,选择使用哪种主要取决于你的编码习惯和具体需求。不过在实际开发中,直接使用 vm.count
的方式更常见,因为写法更简洁。
1.4 data()函数 method{} 区别和关系
区别和关系
相同:都是实例里内容
不同:data约等于类中的全局变量,变量可改变
method 约等于类中的不同方法,可修改变量,可用于接收api
const app = Vue.createApp({
// 数据定义
data() {
return {
count: 0,
name: 'User',
items: []
}
},
// 方法定义
methods: {
// 修改数据的方法
increment() {
this.count++
},
// 带参数的方法
addItem(item) {
this.items.push(item)
},
// 复杂逻辑的方法
calculateTotal() {
return this.items.reduce((sum, item) => sum + item.price, 0)
},
// 组合使用数据的方法
greet() {
return `Hello, ${this.name}!`
}
}
})
1.5 data()–>定义响应式数据 method{}–>处理逻辑 定义 +使用
定义 data()–>定义响应式数据 method{}–>处理逻辑
const app = Vue.createApp({
// 数据定义
data() {
return {
count: 0,
name: 'User',
items: []
}
},
// 方法定义
methods: {
// 修改数据的方法
increment() {
this.count++
},
// 带参数的方法
addItem(item) {
this.items.push(item)
},
// 复杂逻辑的方法
calculateTotal() {
return this.items.reduce((sum, item) => sum + item.price, 0)
},
// 组合使用数据的方法
greet() {
return `Hello, ${this.name}!`
}
}
})
在html模板使用 {{ count }} {{ greet() }}
<div id="app">
<!-- 显示数据 -->
<p>Count: {{ count }}</p>
<!-- 调用方法 -->
<button @click="increment">Add 1</button>
<!-- 带参数的方法调用 -->
<button @click="addItem({name: 'Book', price: 10})">Add Item</button>
<!-- 在模板中使用方法返回值 -->
<p>{{ greet() }}</p>
<!-- 显示计算结果 -->
<p>Total: {{ calculateTotal() }}</p>
</div>
1.6 界面执行步骤:解析语言–>创建实例并绑定->根据实例函数初始化数据+初始化数据渲染到html调用处–>响应式变化
解析语言–>创建实例并绑定->根据实例函数初始化数据+初始化数据渲染到html调用处–>响应式变化
1、页面加载时,浏览器解析 HTML 和 JavaScript。
2、Vue.js 脚本执行时,创建了一个 Vue 应用实例,并将其绑定到 <div id="hello-vue"> 元素上。
3、Vue 应用实例根据 data() 中的初始数据,将 message 的值渲染到页面上的 {{ message }} 处。
4、当 message 数据发生变化时(例如通过用户交互或异步操作),页面会自动更新以反映这些变化。
2、基础语法
基础语法
https://www.runoob.com/vue3/vue3-syntax.html
{{ }}
v-bind: 简写:
v-if:
v-for
**v-model **双向数据绑定。
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
v-on 事件监听器 简写@
3、事件处理
在 Vue.js 中,你可以使用 v-on 指令来监听 DOM 事件,并在触发时执行一些 JavaScript 代码。
实例
4. 计算属性
计算属性是基于其依赖进行缓存的属性。计算属性只有在其相关依赖发生变化时才会重新计算。
实例
{{ reversedMessage }}
5、组件 看不懂举出来的例子(菜鸟网站)
组件是 Vue.js 最强大的功能之一。组件允许你使用小型、独立和通常可复用的组件构建大型应用。
定义:看懂了:实例里定义了template 为自定义界面元素 ,通过组件复用展示在界面,不用写多次
const app = createApp({});
app.component('my-component', {
template: '<div>A custom component!</div>'
});
使用组件: 看懂了->组件使用方式:
<div id="app">
<my-component></my-component>
</div>
<script>
const app = createApp({});
app.component('my-component', {
template: '<div>A custom component!</div>'
});
app.mount('#app');
</script>
5 组件 看懂了-详细解释
总结: 组件和普通的 Vue 实例最大的区别在于:
- 组件是可重用的
- 组件有自己独立的作用域
- 组件可以通过 props 接收数据
- 组件可以触发事件给父组件
- 组件更适合构建大型应用
让我详细解释 Vue 组件的概念和使用方式:
1、基本组件定义:
// 定义一个组件
app.component('my-component', {
template: '<div>A custom component!</div>'
})
2、带数据和方法的完整组件示例:
app.component('user-card', {
// 组件的数据
data() {
return {
userName: 'John Doe',
age: 25
}
},
// 组件的模板
template: `
<div class="user-card">
<h2>{{ userName }}</h2>
<p>Age: {{ age }}</p>
<button @click="increaseAge">Add Year</button>
</div>
`,
// 组件的方法
methods: {
increaseAge() {
this.age++
}
}
})
与普通的 Vue 实例的主要区别:
2.1 可复用性:
<div id="app">
<!-- 可以多次使用同一个组件 -->
<user-card></user-card>
<user-card></user-card>
<user-card></user-card>
</div>
2.2数据隔离:
app.component('counter-component', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
You clicked me {{ count }} times
</button>
`
})
使用:
<div id="app">
<!-- 每个组件实例都有自己独立的数据 -->
<counter-component></counter-component>
<counter-component></counter-component>
</div>
2.3 props 传递数据:
app.component('greeting-component', {
// 定义可接收的属性
props: ['name', 'age'],
template: `
<div>
<h3>Hello, {{ name }}!</h3>
<p>You are {{ age }} years old.</p>
</div>
`
})
使用:
<div id="app">
<greeting-component
name="Alice"
:age="25">
</greeting-component>
<greeting-component
name="Bob"
:age="30">
</greeting-component>
</div>
2.4 组件通信:
// 子组件
app.component('child-component', {
// 声明要触发的事件
emits: ['update'],
template: `
<button @click="$emit('update', 'new data')">
Update Parent
</button>
`
})
// 父组件
app.component('parent-component', {
data() {
return {
message: ''
}
},
template: `
<div>
<p>Message: {{ message }}</p>
<child-component
@update="message = $event">
</child-component>
</div>
`
})
2.5 组件的生命周期:
app.component('lifecycle-component', {
data() {
return {
message: 'Hello'
}
},
// 生命周期钩子
created() {
console.log('Component created')
},
mounted() {
console.log('Component mounted')
},
updated() {
console.log('Component updated')
},
template: `<div>{{ message }}</div>`
})
3 组件主要优势:
3.1 代码复用:
// 定义一次,多处使用
app.component('button-counter', {
data() {
return { count: 0 }
},
template: `
<button @click="count++">
Click me! ({{ count }})
</button>
`
})
3.2 维护性:
<div id="app">
<!-- 大型应用被分解为小组件,更易维护 -->
<header-component></header-component>
<nav-component></nav-component>
<main-content></main-content>
<footer-component></footer-component>
</div>
3.3 封装性:
app.component('user-profile', {
// 内部状态和逻辑被封装在组件内
data() {
return {
user: null
}
},
methods: {
async fetchUserData() {
// 实现获取用户数据的逻辑
}
},
template: `
<div class="user-profile">
<!-- 组件的完整UI结构 -->
</div>
`
})
总结: 组件和普通的 Vue 实例最大的区别在于:
- 组件是可重用的
- 组件有自己独立的作用域
- 组件可以通过 props 接收数据
- 组件可以触发事件给父组件
- 组件更适合构建大型应用
4、组件间通过props传递数据,那数据初始在哪定义呢–>数据流向
4.1 关键点总结:
- 数据通常在父组件定义
- 通过 props 向下传递给子组件
- 子组件通过 events 向上通知父组件
- 复杂应用可以使用状态管理
- Props 可以进行类型验证和设置默认值
4.2 记住:单向数据流,子组件不能直接修改,需要使用event时间通知父组件进行修改,
- Props 是单向数据流
- 子组件不能直接修改 props
- 需要修改数据时,应该通知父组件进行修改
4.3 数据定义+赋值:—>因此数据格式理解为:父组件定义变量A、变量B, 子组件定义变量C、变量D,父子组件都在createApp即创建实例定义,但父组件在data()函数中,子组件在app.component()定义,即data()中有变量A、B,app.component()也有变量C、D,注意A与C名称可以不一致,B与D名称可以不一致。所以他们并没有直接关联关系,只是通过数据传递,把A赋值给了C,把B赋值给了D,因此注意定义与赋值。
让我解释 Props 的数据流向和定义位置:
4.3.1 最基本的数据流向是从父组件到子组件: 仅组件定义,还没到props数据传递,但有props定义
// 父组件(根组件)
const app = createApp({
data() {
// 数据在这里初始定义
return {
postTitle: 'My journey with Vue',
postContent: 'This is my first post'
}
}
})
// 子组件
app.component('blog-post', {
// 声明可以接收哪些prop
props: ['title', 'content'],
template: `
<div>
<h3>{{ title }}</h3>
<p>{{ content }}</p>
</div>
`
})
使用:
<div id="app">
<!-- 将父组件的数据传递给子组件 -->
<blog-post
:title="postTitle"
:content="postContent">
</blog-post>
</div>
4.3.2 Props 的详细定义和验证:
4.3.2.1 Props 的详细定义
app.component('user-profile', {
props: {
// 基础类型检查
name: String,
// 多种类型
age: [Number, String],
// 必传项
userId: {
type: Number,
required: true
},
// 带默认值
status: {
type: String,
default: 'active'
},
// 对象默认值
author: {
type: Object,
default() {
return { name: 'Anonymous' }
}
},
// 自定义验证
level: {
validator(value) {
return ['beginner', 'intermediate', 'expert'].includes(value)
}
}
},
template: `
<div class="user-profile">
<h2>{{ name }}</h2>
<p>Age: {{ age }}</p>
<p>Status: {{ status }}</p>
<p>Level: {{ level }}</p>
</div>
`
})
4.3.2.2 完整的数据流向示例:
父子组件定义
// 父组件
const app = createApp({
data() {
return {
users: [
{ id: 1, name: 'John', age: 25 },
{ id: 2, name: 'Jane', age: 30 }
]
}
},
methods: {
handleUserUpdate(userId, newData) {
// 更新用户数据
const user = this.users.find(u => u.id === userId)
Object.assign(user, newData)
}
}
})
// 子组件
app.component('user-card', {
props: {
user: {
type: Object,
required: true
}
},
methods: {
updateUser() {
// 触发事件通知父组件
this.$emit('update', this.user.id, {
name: this.user.name,
age: this.user.age + 1
})
}
},
template: `
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>Age: {{ user.age }}</p>
<button @click="updateUser">
Increment Age
</button>
</div>
`
})
使用:
<div id="app">
<!-- 循环渲染用户卡片 -->
<user-card
v-for="user in users"
:key="user.id"
:user="user"
@update="handleUserUpdate">
</user-card>
</div>
数据流动的最佳实践(大型应用如何使用props 不同模块定义和使用):
// 全局状态管理(适用于大型应用)
const store = {
state: {
users: [],
currentUser: null
},
setUsers(users) {
this.state.users = users
},
updateUser(id, data) {
const user = this.state.users.find(u => u.id === id)
if (user) {
Object.assign(user, data)
}
}
}
// 根组件
const app = createApp({
data() {
return {
store: store.state
}
}
})
// 用户列表组件
app.component('user-list', {
data() {
return {
store: store.state
}
},
template: `
<div>
<user-card
v-for="user in store.users"
:key="user.id"
:user="user">
</user-card>
</div>
`
})
// 用户卡片组件
app.component('user-card', {
props: ['user'],
template: `
<div>
<h3>{{ user.name }}</h3>
<p>{{ user.age }}</p>
</div>
`
})
5 事件->事件,如何被触发,执行了哪些内容
5.1 例子 看不懂
<div id="app">
<button-counter @increment="incrementTotal"></button-counter>
<p>Total clicks: {{ total }}</p>
</div>
<script>
const app = createApp({
data() {
return {
total: 0
};
},
methods: {
incrementTotal() {
this.total++;
}
}
});
app.component('button-counter', {
template: '<button @click="increment">You clicked me {{ count }} times.</button>',
data() {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
this.$emit('increment');
}
}
});
app.mount('#app');
</script>
5.2 例子看懂了 知道
5.2.1 事件流 (子->父 )逻辑 子组件被处触发(先执行子组件逻辑),传递到父组件(继续执行父组件逻辑))
5.2.2 数据流 (父->子) 逻辑:父组件定义变量初始化,通过响应式进行变量变更
5.3 让我逐步解析这个事件流程:
5.3.1 组件结构:
// 父组件(根组件)
createApp({
data() {
return {
total: 0 // 父组件中的计数器
}
},
methods: {
incrementTotal() { // 父组件的方法
this.total++
}
}
})
// 子组件(button-counter)
app.component('button-counter', {
data() {
return {
count: 0 // 子组件中的计数器
}
},
methods: {
increment() { // 子组件的方法
this.count++ // 增加自己的计数
this.$emit('increment') // 触发事件通知父组件
}
}
})
5.3.2 事件触发流程:
<div id="app">
<!-- 监听子组件的 increment 事件 -->
<button-counter @increment="incrementTotal"></button-counter>
<p>Total clicks: {{ total }}</p>
</div>
5.3.3 完整的事件流程图:
Copy
用户点击按钮
↓
触发子组件的 @click 事件
↓
执行子组件的 increment() 方法
↓
子组件 count 增加
↓
子组件发出 increment 事件(this.$emit)
↓
父组件监听到 increment 事件
↓
执行父组件的 incrementTotal() 方法
↓
父组件 total 增加
5.3.4 详细的代码执行过程:
// 1. 用户点击按钮,触发这个模板中的点击事件
template: '<button @click="increment">You clicked me {{ count }} times.</button>'
// 2. 执行子组件的 increment 方法
methods: {
increment() {
// 2.1 增加子组件内部计数
this.count++
// 2.2 触发自定义事件通知父组件
this.$emit('increment')
}
}
// 3. 父组件接收到事件
<button-counter @increment="incrementTotal"></button-counter>
// 4. 执行父组件的方法
methods: {
incrementTotal() {
// 4.1 增加父组件的计数
this.total++
}
}
5.3.5 让我们通过一个更复杂的例子来深入理解:(深入)
例子
// 带参数的事件
app.component('enhanced-counter', {
data() {
return {
count: 0,
incrementBy: 1
}
},
methods: {
increment() {
this.count += this.incrementBy
// 发出事件时携带数据
this.$emit('increment', {
count: this.count,
incrementBy: this.incrementBy
})
}
},
template: `
<div>
<button @click="increment">
Add {{ incrementBy }}
</button>
<input v-model.number="incrementBy" type="number">
</div>
`
})
// 父组件
createApp({
data() {
return {
total: 0,
history: []
}
},
methods: {
// 接收事件数据
handleIncrement(data) {
this.total += data.incrementBy
this.history.push({
time: new Date(),
amount: data.incrementBy,
newCount: data.count
})
}
},
template: `
<div>
<enhanced-counter
@increment="handleIncrement">
</enhanced-counter>
<p>Total: {{ total }}</p>
<ul>
<li v-for="item in history">
Added {{ item.amount }} at {{ item.time }}
</li>
</ul>
</div>
`
})
事件修饰符的使用:
app.component('modified-counter', {
template: `
<div>
<!-- 停止事件冒泡 -->
<button @click.stop="increment">Stop Propagation</button>
<!-- 阻止默认行为 -->
<button @click.prevent="increment">Prevent Default</button>
<!-- 只触发一次 -->
<button @click.once="increment">Click Once</button>
<!-- 按键修饰符 -->
<input @keyup.enter="increment">
</div>
`
})
5.4 关键点总结:
- 事件流是自下而上的(子到父)
- 数据流是自上而下的(父到子)
- 子组件通过
$emit
触发事件 - 父组件通过
@事件名
监听事件 - 可以在触发事件时传递数据
- 事件和数据流向是单向的,形成完整闭环
这种设计保证了:
- 数据流向清晰
- 组件之间耦合度低
- 便于调试和维护
- 符合单向数据流原则