0
点赞
收藏
分享

微信扫一扫

一款基于.NET8开源且免费的中小型酒店管理系统

兵部尚输 2024-11-06 阅读 9

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

目录

文章目录

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中反而像是赋值给示例,同时会修改了整体所有类的值(用随后的所有初始化定义 ->理解)。)

  1. 首先,创建 Vue 实例:
const app = Vue.createApp({
  data() {
    return { count: 4 } // 初始化数据
  }
})
  1. 挂载到 DOM:
const vm = app.mount('#app')

这时 Vue 会:

  • 创建一个组件实例 vm
  • 将数据对象 { count: 4 } 转换为响应式对象
  • 在 vm 上创建两种访问方式:
    • vm.count (直接访问)
    • vm.$data.count (通过 $data 访问)
  1. 访问数据的两种方式:
document.write(vm.$data.count) // 输出: 4 (通过 $data 访问)
document.write(vm.count)       // 输出: 4 (直接访问)

这两种方式实际上访问的是同一个数据源。

  1. 数据修改:
// 方式1:直接修改
vm.count = 5
document.write(vm.$data.count) // 输出: 5

// 方式2:通过 $data 修改
vm.$data.count = 6
document.write(vm.count) // 输出: 6
这里的工作原理是:
  1. Vue 使用 JavaScript 的 Proxy(在 Vue 3 中)来实现数据的响应式:
// 简化的内部实现原理示例
const vm = {
  get count() {
    return this.$data.count;
  },
  set count(value) {
    this.$data.count = value;
    // 触发更新视图
  }
}
  1. 当你通过任意一种方式修改数据时:
vm.count = 5
// 或
vm.$data.count = 6

Vue 都会:

  • 更新数据值
  • 触发视图更新
  • 保持两种访问方式的数据同步

所以无论你使用哪种方式修改数据,最终都会:

  1. 更新数据源中的值
  2. 通知 Vue 响应式系统
  3. 更新所有使用该数据的地方(包括视图和其他依赖)

这就是为什么你能看到:

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 实例最大的区别在于:

  1. 组件是可重用的
  2. 组件有自己独立的作用域
  3. 组件可以通过 props 接收数据
  4. 组件可以触发事件给父组件
  5. 组件更适合构建大型应用

让我详细解释 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 实例最大的区别在于:

  1. 组件是可重用的
  2. 组件有自己独立的作用域
  3. 组件可以通过 props 接收数据
  4. 组件可以触发事件给父组件
  5. 组件更适合构建大型应用

4、组件间通过props传递数据,那数据初始在哪定义呢–>数据流向

4.1 关键点总结:
  1. 数据通常在父组件定义
  2. 通过 props 向下传递给子组件
  3. 子组件通过 events 向上通知父组件
  4. 复杂应用可以使用状态管理
  5. 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 关键点总结:
  1. 事件流是自下而上的(子到父)
  2. 数据流是自上而下的(父到子)
  3. 子组件通过 $emit 触发事件
  4. 父组件通过 @事件名 监听事件
  5. 可以在触发事件时传递数据
  6. 事件和数据流向是单向的,形成完整闭环

这种设计保证了:

  • 数据流向清晰
  • 组件之间耦合度低
  • 便于调试和维护
  • 符合单向数据流原则
举报

相关推荐

0 条评论