Vue 学习笔记

0 阅读11分钟

1、概述

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架,发布于 2014 年 2 月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router:跳转,vue-resource:通信,vuex:管理)或既有项目整合。

2、第一个Vue程序

以下基于vue3的语法

  • 用下面的代码可以实现CDN导入
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

image.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
<!--    在头文件的内容里导入vue的CDN-->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<!--创建一个可供使用的div-->
<body>
<div id="app">
    {{msg}}
</div>
<script>
<!--    这个写法是选项式挂载,你也可以用解构赋值:const { createApp } = Vue-->
    // 或者是ES6模块:import { createApp } from 'vue',注意这个写法只在
 <!-- <script type="module">或者自定义语法糖的setup的《script》使用-->

const app=Vue.createApp({
    data() {
        return {
            msg: 'Hello Vue!'
        }
    }
}).mount('#app');//mount挂载到app上
</script>
</body>
</html>

Vue app是干嘛的?

  • 一个vueapp存了一些数据和操作,它挂在哪个标签哪个就有这些数据和操作
  • Vue 应用就像一个“数据+操作的包”,挂载到哪个标签,哪个标签就获得这些能力和数据。
// 挂载到不同的标签
app1.mount('#app1')  // 区域1 获得了这些数据和方法
app2.mount('#app2')  // 区域2 也获得了这些数据和方法

---------------------------------------------
你可以这么做
const app=createApp({});
app.data(){

};
app.mount("#div");

image.png

image.png

MVVM模式(Model-View-ViewModel

MVVM 模式和 MVC 模式一样,主要目的是分离视图(View)和模型(Model),有几大好处

  • 低耦合:视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的 View 上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
  • 可复用:你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 View 重用这段视图逻辑。
  • 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
  • 可测试:界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写。
  • 上面的图体现了mv,如何体现vm(ViewModel)呢,如下图 数据变化自动更新视图,视图变化自动更新数据,数据逻辑和视图分离,通过 ViewModel 桥接

image.png

  • View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

3、Vue语法

1、V-bind

v-bind 是 Vue 中用于动态绑定属性的指令,它的核心作用是将数据(JavaScript 表达式)与 HTML 属性的值关联起来

简单理解:HTML 的静态属性(如 srchrefclassstyle 等)的值通常是固定的字符串,而 v-bind 可以让这些属性的值动态化,从 Vue 实例的数据中获取。

<!-- 静态写法:图片地址是固定的 -->
<img src="logo.png">

<!-- 动态写法:图片地址从数据中获取 -->
<img v-bind:src="imageUrl">
<!-- 或简写为 -->
<img :src="imageUrl">
  • 静态写法: <img src="logo.png"> 就像是用笔在纸上画了一个图案。这个图案是固定的,除非你拿橡皮擦掉重画(修改HTML文件),否则它永远不变。
  • 动态写法: <img :src="imageUrl"> 就像是把这个图案和一个“遥控器”绑定在了一起。你不需要去碰那张纸,只需要按下遥控器(改变 imageUrl 的值),纸上的图案就会自动改变。

2、v-if/v-else

  • 和Java里的if,else-if,else逻辑一样
<div id="app">

   <h1 v-if="msg=='a'">a</h1>
   <h1 v-else-if="msg=='b'">b</h1>
   <h1 v-else-if="msg=='c'">c</h1>
  <h1 v-else>msg为false</h1>
</div>
<script>
  const app=Vue.createApp({
    data() {
      return {
        msg: "a"
      }
    }
  }).mount('#app');//mount挂载到app上
</script>

image.png

3、v-for遍历

image.png

  • vue如何动态操作数组?
方法作用是否改变原数组触发视图更新使用示例
push()在数组末尾添加一个或多个元素✅ 是✅ 是this.items.push({msg: "d"})
pop()删除数组最后一个元素✅ 是✅ 是this.items.pop()
shift()删除数组第一个元素✅ 是✅ 是this.items.shift()
unshift()在数组开头添加一个或多个元素✅ 是✅ 是this.items.unshift({msg: "新"})
splice()删除/插入/替换元素(万能方法)✅ 是✅ 是见下方详解
sort()对数组元素进行排序✅ 是✅ 是this.items.sort((a,b) => a.id - b.id)
reverse()反转数组元素的顺序✅ 是✅ 是this.items.reverse()
============================================================================
用法语法示例效果
删除splice(起始索引, 删除个数)this.items.splice(1, 2)从索引1开始删除2个元素
插入splice(起始索引, 0, 插入的元素)this.items.splice(2, 0, {msg:"新"})在索引2的位置插入新元素
替换splice(起始索引, 删除个数, 插入的元素)this.items.splice(1, 1, {msg:"替换"})删除索引1的1个元素,再插入新元素

4、事件绑定

<body>
<div id="app">
<button v-on:click=sayHi>click me</button>
<!--  这个是简化的写法-->
<button @click="sayHi2">click me</button>
  
</div>
<script>
  const app=Vue.createApp({
    data() {
      return {
        msg:"Hello World"
      }
    },
    methods:{
      // 这是简化的写法
      sayHi (){
        alert(this.msg);
      },
      // 这是传统写法
      sayHi2:function (){
        alert(this.msg);
      }
    }
  }).mount('#app');//mount挂载到app上
</script>

image.png

鼠标事件

事件触发时机使用示例说明
click点击元素@click="handleClick"最常用,鼠标左键点击
dblclick双击元素@dblclick="handleDblClick"鼠标双击
mouseenter鼠标进入元素@mouseenter="handleEnter"进入元素区域(不冒泡)
mouseleave鼠标离开元素@mouseleave="handleLeave"离开元素区域(不冒泡)
mouseover鼠标悬停@mouseover="handleOver"进入元素或其子元素
mouseout鼠标移出@mouseout="handleOut"离开元素或其子元素
mousemove鼠标移动@mousemove="handleMove"在元素上移动时连续触发
mousedown按下鼠标@mousedown="handleDown"在元素上按下任意鼠标键
mouseup松开鼠标@mouseup="handleUp"在元素上松开鼠标键
contextmenu右键菜单@contextmenu.prevent="handleMenu"鼠标右键点击(常用禁用右键)

键盘事件

事件触发时机使用示例说明
keydown按下键盘@keydown="handleKey"按下任意键
keyup松开键盘@keyup="handleKeyUp"松开任意键
keypress按下字符键@keypress="handlePress"按下产生字符的键(已废弃,不推荐)

5、v-model

v-model 是 Vue 中用于双向数据绑定的核心指令,它实现了表单输入和应用状态之间的自动同步。它会自动把data数据与表单元素的value关联起来,比如单选框的value,选中了即把value给了data

v-bind 是单向绑定(数据 → 视图),v-model 是双向绑定(数据 ⇄ 视图),即 v-model = v-bind + input 事件监听。

<div id="app">
   <input type="text" v-model="msg">{{msg}}
</div>
<script>
    const app=Vue.createApp({
        data() {
            return {
                msg:"Hello World"
            }
        },

    }).mount('#app');//mount挂载到app上
</script>

image.png

4、组件

什么是组件

组件是可复用的 Vue 实例,说白了就是一组可以重复使用的模板,跟 JSTL 的自定义标签、Thymeleaf 的 th:fragment 等框架有着异曲同工之妙。通常一个应用会以一棵嵌套的组件树的形式来组织:

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。浏览时总是一个组件在变化,其他组件不变

<body>
<div id="app">
<app1></app1>
</div>
<script>
    const app = Vue.createApp({});
    app.component("app1", {
        template: `<h1>这是一个组件</h1>`
    });
    app.mount("#app")
</script>
</body>

image.png

下面这个代码体现了props数据传递与根组件是老板,子组件是员工的关系

根组件是数据源(老板),子组件是执行者(员工),props 是传递数据的通道(任务单)。

老板(根组件)通过 v-for 循环派发任务,用 :item="任务内容" 把数据递给员工,员工(子组件)通过 props: ['item'] 接收,然后用 template 展示出来。数据从上往下单向流动,根组件管数据,子组件管显示。

<body>
<div id="app">
    <app1
            v-for="(item, index) in items"

            :item="item"
    ></app1>

</div>
<script>
    const app = Vue.createApp({
        // 数据放根组件
        data(){
            return{
                items: [
                    "java",
                    "javascript",
                    "html"
                ]
            }

        }
    });
    app.component("app1", {
        props: ['item'],
        template: '<li>{{item}}</li>',

    });
    app.mount("#app")
</script>
</body>

5、AXIOS异步通信

Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,她的主要作用就是实现 AJAX 异步通信,其功能特点如下:

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API [JS中链式编程]
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF(跨站请求伪造)
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
// 1. 你写的请求代码
const loginData = {
  username: 'zhangsan',
  password: 'mypassword123'
};
axios.post('/api/login', loginData) // 请求后端接口
  .then(response => {
    // 3. 后端返回数据,你在这里处理
    if (response.data.code === 200) {
      console.log('登录成功!用户token是:', response.data.data.token);
    }
  });
@RestController
@RequestMapping("/api")
public class LoginController {
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        String username = request.getUsername();
        String password = request.getPassword();
        
        if ("zhangsan".equals(username) && "mypassword123".equals(password)) {
            String token = generateTokenForUser(username);
            return ResponseEntity.ok(new LoginResponse(200, "登录成功", token));
        } else {
            return ResponseEntity.status(401).body(new ErrorResponse(401, "用户名或密码错误"));
        }
    }
}

Axios 语法

// GET 请求
axios.get(url)
axios.get(url, { params: { 参数名: 值 } })

// POST 请求
axios.post(url, { 参数名: 值 })

// PUT 请求(修改)
axios.put(url, { 参数名: 值 })

// DELETE 请求(删除)
axios.delete(url)
// 写法1:Promise(.then)
axios.get('/user').then(response => {
    console.log(response.data)  // 拿数据
}).catch(error => {
    console.log(error)          // 处理错误
})

// 写法2:async/await(推荐)
async function getData() {
    try {
        const response = await axios.get('/user')
        console.log(response.data)
    } catch (error) {
        console.log(error)
    }
}

xios.get(网址).then(拿数据) 或 await axios.get(网址),就这么简单。

6、计算属性 computed

计算属性是通过已有数据计算出来的新数据,它最大的特点是“带缓存”——只要依赖的数据没变,它就不会重新计算,直接返回上次的结果。 比如 fullName 依赖 firstNamelastName,只要这两个没变,多次使用 fullName 也不会重复执行逻辑。这跟方法不一样,方法每次调用都会重新执行。所以计算属性适合做数据派生(如全名、总价、过滤列表),方法适合做实时获取(如时间戳、随机数)。

<div id="app">
    <p>{{currentTime()}}</p>
    <p>{{currentTime1}}</p>
</div>
<script>
    const app=Vue.createApp({
        methods: {
            currentTime(){
                return Date.now();
            }
        },
        computed:{
            currentTime1(){
                return Date.now();
            }
        }
    });
    const a=app.mount("#app")
    //你这里挂载才是实例化了vue app,上面相当于定义对象,所以调用方法是a.方法。
</script>
</body>

image.png

你可以看到,这两个方式返回的时间戳一致,但是调用产生的效果大不一样

  • {{currentTime()}}:每次刷新页面或重新渲染,时间戳都会变
  • {{currentTime1}}时间戳固定不变(因为被缓存了)
  • currentTime1是作为一个属性被访问的,没有括号
  • 计算属性就是 Vue 实例的一个属性

7、插槽 slot

插槽(slot)是子组件里的占位符,父组件可以在组件标签中间写内容,这些内容会自动替换到子组件的 <slot> 位置。 比如子组件模板写了 <slot></slot>,父组件用 <my-button>点我</my-button>,最终渲染出来就是“点我”出现在子组件那个位置。它的作用是让子组件的部分内容由父组件来决定,实现组件的灵活复用。

  • 父组件就是调用子组件的组件,下面的例子里body里的组件调用带有slot的组件,所以就是父组件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
  <my-slot>
    <button>插入按钮</button>
  </my-slot>

  <my-slot>
    <h3>插入字</h3>
  </my-slot>

  <my-slot>
    <test-slots>
    </test-slots>
  </my-slot>
</div>
<script>
  const app=Vue.createApp({
    components:{
      mySlot:{
        template:`
        <slot></slot>
        <h2>这是我的插槽 </h2>
        `
      },
      testSlots:{
        template:`
        <p>插入一个模板</p>`
      }
    }
  });
  const a=app.mount("#app")
</script>
</body>
</html>

image.png

8、自定义事件

子组件:

<template>
  <button @click="sendData">点我传值</button>
</template>

<script>
export default {
  methods: {
    sendData() {
      this.$emit('my-event', '来自子组件的数据')
    }
  }
}
</script>

父组件:

<template>
  <Child @my-event="handleEvent" />
</template>

<script>
import Child from './Child.vue'

export default {
  components: { Child },
  methods: {
    handleEvent(data) {
      console.log('接收到的数据:', data)
    }
  }
}
</script>

this.$emit('my-event', 数据)

它的意思是:触发一个叫 my-event 的自定义事件,并带上数据

my-event 不是父组件的方法名,而是“事件名”
真正的方法是你绑定的 handleEvent

9、Vite

Vite = 新一代工具
≈(脚手架 + 构建工具)的组合
基本取代了:Vue CLI + Webpack 这一整套

Vite 用于快速生成一个 vue 的项目模板;

预先定义好的目录结构及基础代码,就好比咱们在创建 Maven 项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速;

主要的功能:

  • 统一的目录结构
  • 本地调试
  • 热部署
  • 单元测试
  • 集成打包上线

1.安装nodejs

nodejs是让 JavaScript 可以在“浏览器之外(比如服务器)运行”的环境

nodejs.org/en/download

nodejs会自动添加系统变量,自带依赖下载工具npm

image.png

为npm配置国内镜像

npm config set registry https://registry.npmmirror.com

image.png

2、创建一个模板vue项目

  • cd到你想创建vue项目的目录

image.png

  • 执行创建命令
npm create vite@latest
  • 选择项目名,语言,模板等
  • 安装依赖
npm install
  • 运行
npm run dev

image.png

image.png

3、vue-router

Vue Router 是 Vue 官方的路由管理库,用来在单页面应用(SPA)中根据不同的 URL(比如 /home/about)切换显示不同的组件,而不需要刷新整个页面;简单说,它就是帮你实现“页面跳转”的工具,让前端应用像多页面网站一样导航,但本质仍然是一个页面在动态切换内容。

在项目目录执行:

npm install vue-router

1、创建router

新建一个router目录来存放所有的路由,一个index.js来配置所有的路由,这是约定俗成的实践

import {createRouter, createMemoryHistory, createWebHistory} from "vue-router";
import App from "../App.vue";

const routes=[
    {path:'/',component:App},
    {path: '/about',component: ()=>import('../components/About.vue')}
]

export default createRouter(
    {
        history:createWebHistory(),
        routes
    }
)
  • 嵌套路由
    • 父组件必须有 <router-view>
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/user',
    component: () => import('../views/User.vue'),
    children: [
      {
        path: 'profile',
        component: () => import('../views/Profile.vue')
      },
      {
        path: 'orders',
        component: () => import('../views/Orders.vue')
      }
    ]
  }
]

export default createRouter({
  history: createWebHistory(),
  routes
})
  • history 决定了 URL 长什么样 + 路由怎么工作的
模式URL 样子刷新是否 404使用场景
createWebHistory/about⚠️ 可能正常项目(推荐)
createWebHashHistory/#/about❌ 不会简单部署 / 静态站
createMemoryHistory无,不变不涉及测试 / SSR

2、在main.js中注册

main.js 不是类似 XML 的“配置文件” ,它更像是程序的入口代码(启动器),main.js 是启动 Vue 应用的“总开关”,负责创建、配置、并挂载整个应用

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'

const app=createApp(App)
app.use(router)
app.mount('#app')

3、在页面上显示

  • 建一个跳转目的地
<script setup lang="ts">

</script>

<template>
<h1>关于</h1>
</template>

<style scoped>

</style>
  • 配置跳转
<script setup>

</script>

<template>
  <router-link to="/about">关于</router-link>
<br>
  <router-view/>
</template>

image.png

image.png

如果想直接跳转a标签就可以实现

4、结合ElementUi组件库

1、安装element-ui

element-plus.org/zh-CN/guide…

npm install element-plus --save