Web

Web开发06

Posted by RyoCY on 2025-02-10
Estimated Reading Time 14 Minutes
Words 3.3k In Total
Viewed Times

Vue

Vue是一个用于构建用户界面的渐进式框架

通过 CDN 使用 Vue

*CDN(Content Delivery Network)是一个分布式服务器网络,能够快速向用户交付静态资源(如 JavaScript、CSS、图片等)。这种方法比较简便,适合快速原型开发或小型项目。

全局构建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue via CDN</title>
</head>
<body>
<div id="app">
{{ message }}
</div>
<!-- 引入 Vue 3 的 CDN 链接 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
// 使用 Vue 3 的 createApp 方法
const { createApp } = Vue;

createApp({
data() {
return {
message: 'Hello, Vue!'
};
}
}).mount('#app'); // 挂载到 #app 元素
</script>
</body>
</html>

ES 模块构建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 3 via CDN with ES Module</title>
</head>
<body>
<div id="app">
{{ message }}
</div>

<!-- 使用 ES 模块方式引入 Vue 3 -->
<script type="module">
// 从 CDN 导入 Vue 3 的 ES 模块构建版本,注意与全局引入的URL不同
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';

// 创建 Vue 应用
createApp({
data() {
return {
message: 'Hello, Vue!'
};
}
}).mount('#app'); // 挂载到 #app 元素
</script>
</body>
</html>

创建 Vue 应用

打算创建项目的目录打开命令行运行以下命令:

1
npm create vue@latest

选择功能后运行以下命令:

1
2
3
cd <your-project-name>
npm install *注:安装依赖
npm run dev *注:启动开发服务器

删掉一些文件和代码恢复到初始空白页面,目录大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
your-project:
│ index.html 首页入口
│ jsconfig.json
│ package-lock.json
│ package.json 项目包文件
│ vite.config.js 基于vite的配置

├─node_modules
├─public

└─src
│ App.vue 根组件APP
main.js 入口文件
├─assets(空)放置一些图片和logo
└─components(空)组件

/src是项目的主要源代码目录,包含了所有的Vue组件、配置文件、静态资源等。/src/components
组件目录,存放可复用的Vue组件文件。/src/views
视图目录,存放页面级别的组件,对应应用的不同路由,会在其中包含页面的布局和导航逻辑。/src/router
路由目录,存放路由配置文件,定义了应用的路由规则和导航守卫。
/src/api
API请求目录,包含处理与后端服务器通信的API请求逻辑。我们项目中采用了Axios客户端库。
axios函数可以接收三个参数:urldataconfig

  1. url (必需): 这是一个字符串,表示你想要请求的服务器地址。例如:‘https://api.example.com/data’。

  2. data (可选): 这是一个对象,包含了你想要发送到服务器的数据。这个参数通常用于 POST、PUT 或 PATCH 请求。例如:{ key1: ‘value1’, key2: ‘value2’ }。当使用 POST 方法时,这些数据会被添加到请求体中。

  3. config (可选): 这是一个对象,包含了请求的配置选项。这个对象可以包含多个属性,用于定制请求的行为。以下是一些常用的配置选项:(1)headers: 一个对象,包含了你想要设置的HTTP请求头部。(2)params: 一个对象,包含了你想要添加到URL查询字符串中的参数。(3)responseType: 一个字符串,指定服务器响应的数据类型。可以是 ‘arraybuffer’, ‘blob’, ‘document’, ‘json’, ‘text’ 或 ‘stream’。

对于前面介绍的三种传递参数的方法:

  1. @PathVariable注解视为url参数中的变量,使用axios.post(url/${xxx}, data, config),其中data、config都可以省略

  2. @RequestParam注解视为config参数中的params,使用axios.post(url, data, {params: xxx}) ,其中data不可省略,如果是空就用null填补,不然会把config当成data

  3. @RequestBody注解视为data参数,使用axios.post(url, data, config),其中config可以省略

/src/assets
静态资源目录,存放图片、CSS样式表、字体文件等静态资源。/src/utils
工具函数目录,包含项目中使用的各种辅助函数和工具类,如日期处理、字符串操作、请求封装(request.ts)等。/src/App.vue
根组件,所有界面都是从这个界面发展来的,包含了应用的全局布局和导航。通过来表示:展示当前router路由对应的界面。/src/main.ts
入口文件,用于创建Vue实例,引入全局样式、插件、路由、状态管理等。index.html
入口HTML文件,Vue实例将挂载到这个元素上。package.json
依赖和脚本配置文件,列出了项目的所有依赖项,并提供了用于开发和构建项目的脚本。
vue.config.ts
Vue CLI配置文件,用于配置Vue CLI,包括开发服务器、构建选项等。
.gitignore
Git忽略文件,指定git操作时应忽略的文件和目录。/public
公共资源目录,存放不会被Webpack处理的公共静态资源。

选项API与组合API

  • 选项API,data 选项写数据,methods 选项写逻辑,代码分散。

  • 组合API,数据、逻辑和其他都在 setup 内写,代码集中,利于维护。
    两种API
    *本学习笔记选择组合API

setup

setup 是 Vue 3 组合式 API(Composition API)的核心函数,是组合式逻辑的入口。在组件初始化时执行,早于 beforeCreatecreated 生命周期钩子。
setup 中访问 thisundefined

  1. 传统 <script> 语法在传统的 <script> 语法中,setup 是一个函数,需要显式定义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /* APP.vue */
    <script>
    export default {
    name: "App",
    setup() {
    console.log("setup执行了");
    return {
    // 返回的对象中的属性和方法可以在模板中使用
    message: "Hello, Vue 3!",
    };
    },
    };
    </script>
  2. <script setup> 语法糖在这种语法中,setup 是隐式的。所有顶级绑定(变量、函数等)都会自动暴露给模板。

    1
    2
    3
    4
    5
    /* APP.vue */
    <script setup>
    const message = "Hello, Vue 3!";
    console.log("setup执行了");
    </script>

ref 和 reactive

ref 创建

  • 作用:定义响应式变量,可以是基本类型、对象类型。

  • 语法:let xxx = ref(初始值)

JS中操作数据需要:xxx.value,但 <template> 中不需要 .value,直接使用。对于let name = ref('张三')来说,name不是响应式的,name.value是响应式的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup>
import { ref } from 'vue';

// 基本类型
const count = ref(0); // { value: 0 }
console.log(count.value); // 0
count.value++; // 修改值

// 对象类型
const user = ref({ name: 'Alice', age: 25 });
user.value.name = 'Bob'; // 修改属性(自动响应)
</script>

<template>
<div>
<p>{{ count }}</p> <!-- 自动解包,无需 .value -->
<button @click="count++">+1</button>
</div>
</template>

reactive 创建

  • 作用:定义一个响应式对象(基本类型要用ref)

  • 语法:let 响应式对象= reactive(源对象)

JS无需 .value,直接通过属性名访问。解构或赋值时可能丢失响应性,需用 toRefs 解决,把对象中的每一个属性做一次包装成为响应式数据。

⚠️reactive() 返回的是一个原始对象的 Proxy,它和原始对象是不相等的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script setup>
import { reactive } from 'vue';

const state = reactive({
count: 0,
user: { name: 'Alice', age: 25 },
});

console.log(state.count); // 0
state.count++; // 直接修改属性

// 修改嵌套对象
state.user.name = 'Bob';
</script>

<template>
<div>
<p>{{ state.count }}</p>
<button @click="state.count++">+1</button>
</div>
</template>

使用原则

  1. 若需要一个基本类型的响应式数据,必须使用ref。

  2. 若需要一个响应式对象,层级不深,ref、reactive都可以。

  3. 若需要一个响应式对象,且层级较深,推荐使用reactive。

标签的 ref 属性

用于注册模板引用。

  1. 通过 ref 获取某个 DOM 元素的引用,允许直接操作该 DOM 元素(每个 HTML 元素(如 <div>, <p>, <button> 等)都对应一个 DOM 元素对象)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <template>
    <div>
    <!-- 定义一个 ref 属性,绑定到 DOM 元素 -->
    <!-- 表示我们要通过 inputElement 引用这个 input 元素 -->
    <input ref="inputElement" type="text" placeholder="请输入文本" />
    <button @click="focusInput">聚焦输入框</button>
    </div>
    </template>

    <script>
    import { ref } from 'vue';

    export default {
    setup() {
    // 使用 ref 来获取 DOM 元素的引用
    // 当组件渲染完成后,Vue 会自动把 input 元素绑定到这个引用上。
    const inputElement = ref(null);

    /* 定义一个方法,通过 ref 来操作 DOM 元素
    inputElement 本身是一个对象,可以通过 .value 访问到实际的 DOM 元素
    并调用其原生的 JS 方法 */
    const focusInput = () => {
    // 通过 .value 访问 DOM 元素并设置焦点
    inputElement.value.focus();
    };

    return { inputElement, focusInput };
    }
    };
    </script>
  2. 通过 ref 获取到子组件的实例,允许调用子组件的方法或访问子组件的属性。

computed

创建计算属性的工具,基于响应式数据自动计算值,并且只有依赖数据变化时才重新计算。

1
2
3
4
5
6
7
8
import { computed, ref } from 'vue';
export default {
setup() {
const count = ref(0);
const doubled = computed(() => count.value * 2);
return { count, doubled };
},
};

watch

监听响应式数据变化,watch 会在其中任何一个数据变化时触发回调。
watch('需要监听的数据',数据改变执行函数,配置对象)

  1. 监视ref定义的基本类型数据:直接写数据名即可,监视的是其value值的改变。

    1
    2
    3
    4
    5
    const count = ref(0);
    const name = ref("Alice");
    watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
    console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`);
    });
  2. 监视ref定义的对象类型数据:直接写数据名,监视的是对象的地址值,若想监视对象内部的数据,要手动开启深度监视

  3. 监视reactive定义的对象类型数据,默认开启了深度监视,不需要显式设置。

    1
    2
    3
    4
    const user = ref({ name: 'Alice', age: 25 });
    watch(user, (newValue, oldValue) => {
    console.log('User changed:', newValue);
    }, { deep: true });
  4. 监视ref或reactive定义的对象类型数据中的某个属性,一般写成函数形式。

    1
    2
    3
    watch(()=>user.name,(newValue,oldValue)=>{
    console.log('user.name changed:',newValue,oldValue)
    },{deep:true})
  5. 立即触发:默认情况下,watch 只会在数据变化时触发回调。如果希望在创建时就立即执行一次回调,可以使用 {immediate: true}

生命周期

Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue会在合适的时机调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子。生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。

Vue3的生命周期:

  1. 创建阶段:setup

  2. 挂载阶段:onBeforeMountonMounted(常用)

  3. 更新阶段:onBeforeUpdateonUpdated(常用)

  4. 卸载阶段:onBeforeUnmount(常用)、onUnmounted

Hook 是允许在特定生命周期点插入自定义代码的机制。

1
2
3
4
5
6
7
8
9
import { onMounted } from "vue";
export default {
name: "App",
setup() {
onMounted(()=>{
console.log('onMounted触发了')
})
},
};

父子通信

父子组件之间的通讯主要通过两种方式:propsemit
父子通信

  1. 父传子:父组件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // Parent.vue
    <template>
    <Child :message="parentMessage" />
    </template>

    <script>
    import { ref } from 'vue';
    import Child from './Child.vue';

    export default {
    components: { Child },
    setup() {
    const parentMessage = ref('Hello from Parent!');
    return { parentMessage };
    }
    };
    </script>

    子组件 通过 props 接收数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Child.vue
    <template>
    <div>{{ message }}</div>
    </template>

    <script>
    export default {
    props: {
    message: String
    }
    };
    </script>
  2. 子传父:父组件 监听子组件触发的事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // Parent.vue
    <template>
    <Child @childEvent="handleChildEvent" />
    </template>

    <script>
    import { ref } from 'vue';
    import Child from './Child.vue';

    export default {
    components: { Child },
    setup() {
    const handleChildEvent = (data) => {
    console.log('Received from child:', data);
    };

    return { handleChildEvent };
    }
    };
    </script>

    子组件 通过 emit 触发事件并传递数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // Child.vue
    <template>
    <button @click="sendToParent">Send to Parent</button>
    </template>

    <script>
    import { defineComponent } from 'vue';

    export default defineComponent({
    setup(props, { emit }) {
    const sendToParent = () => {
    emit('childEvent', 'Hello from Child!');
    //第二个参数解构出emit函数,emits选项需要声明自定义事件名称
    };

    return { sendToParent };
    }
    });
    </script>
  3. 通过 provideinject 跨级传递