父组件和子组件

vue中,父组件给子组件传值,子组件给父组件传值,怎么传,请举例说明

在Vue中,父组件向子组件传递数据通常使用props,而子组件向父组件传递数据则使用自定义事件($emit)。下面我将通过一个简单的例子来说明这两个过程。

如果你在使用Vue 3的setup语法糖的同时,还想保持选项式API的风格,你可以这样做:

父组件向子组件传值

父组件 (ParentComponent.vue):

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<template>
<div>
<h1>父组件</h1>
<ChildComponent :message="parentMessage"/>
</div>
</template>

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

const parentMessage = ref('来自父组件的消息');
</script>

子组件 (ChildComponent.vue):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<template>
<div>
<h2>子组件</h2>
<p>{{ message }}</p>
</div>
</template>

<script setup>
import {defineProps} from 'vue';

const props = defineProps({
message: String
});
</script>

子组件向父组件传值

子组件 (ChildComponent.vue):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<template>
<div>
<h2>子组件</h2>
<button @click="sendMessageToParent">发送消息给父组件</button>
</div>
</template>

<script setup>
import {defineEmits} from 'vue';

const emit = defineEmits(['child-event']);

function sendMessageToParent() {
emit('child-event', '来自子组件的消息');
}
</script>

父组件 (ParentComponent.vue):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<template>
<div>
<h1>父组件</h1>
<ChildComponent @child-event="handleChildEvent"/>
<p>从子组件接收到的消息: {{ childMessage }}</p>
</div>
</template>

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

const childMessage = ref('');

function handleChildEvent(message) {
childMessage.value = message;
}
</script>

在这个例子中,我们使用了<script setup>语法糖,这是Vue 3中推荐的一种更简洁的组件编写方式。在<script setup>
中,我们可以直接使用ref来定义响应式数据,使用defineProps来定义组件的props,以及使用defineEmits来定义组件可以触发的事件。

这种方式结合了组合式API的简洁性和选项式API的风格,使得代码更加清晰和易于维护。

reactive和ref

reactive

接受类型:对象类型(object)

ref

接受类型:简单类型或者对象类型(object)

computed计算函数

对某一个值进行计算(包括过滤filter等语法运用)

1
2
3
4
const list = ref([1, 2, 3, 4, 5, 6])
const reslist = computed(() => {
return list.value.filter(item => item > 2)
})

注意:

不包括任何副作用:只是对值操作,不进行任何dom操作或者异步操作(可以给watch()做)
通常只是读操作;不建议直接修改计算属性的值

watch函数

在watch里面ref是不用写.value的。

1
2
3
4
5
const count = ref(0);
const name = ref('czy');
wacth([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log("变化了")
})

deep深度监听

开启deep就是深度侦听:可以侦听object的属性(不开启是不能的

1
2
3
4
5
6
7
8
9
10
11
12
const count = ref(0);
const name = ref('czy');
const user = ref({name: 'czy'});
wacth((count) => {
console.log("改变了")
}, {deep: true})
//deep是有损耗的,尽量不开启,如果要监听obeject里的属性可以
//写成这样:
wacth(() => user.value.name,
() => {
console.log("改变了")
})

生命周期函数

生命周期

Vue.js 3 的生命周期函数与 Vue.js 2 有一些不同,但它们都提供了一种在组件生命周期的不同阶段执行特定逻辑的方法。以下是 Vue.js
3 中的主要生命周期钩子及其用途的详细解释:

创建阶段

  1. beforeCreate (不推荐使用)

    • 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
    • 由于 Vue 3 中引入了 Composition API,beforeCreate 钩子很少使用,因为大多数初始化逻辑应该在 setup() 函数中完成。
  2. created

    • 在实例创建完成后被立即调用。
    • 此时实例已完成数据观测,属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
    • 适合进行数据获取和事件监听等不需要 DOM 的操作。

挂载阶段

  1. beforeMount

    • 在挂载开始之前被调用:相关的 render 函数首次被调用。
    • 此时模板编译完成,但尚未挂载到 DOM 上。
  2. mounted

    • 在实例挂载到 DOM 后被调用。
    • 此时可以访问到 DOM 元素,适合进行 DOM 操作和第三方库的初始化。

更新阶段

  1. beforeUpdate

    • 在数据更新时调用,发生在虚拟 DOM 打补丁之前。
    • 可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
  2. updated

    • 在由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。
    • 此时组件 DOM 已经更新,可以执行依赖于 DOM 的操作。

销毁阶段

  1. beforeUnmount (Vue 3 新增)

    • 在卸载组件实例之前调用。
    • 在这个阶段,实例仍然是完全正常的,适合进行清理工作,比如取消定时器或清除监听器。
  2. unmounted (Vue 3 新增)

    • 在卸载组件实例之后调用。
    • 此时组件实例的所有指令都被解绑,所有的事件监听器被移除,所有的子组件实例也都被卸载。

错误处理

  1. errorCaptured (Vue 3 新增)
    • 当捕获一个来自子孙组件的错误时被调用。
    • 可以返回 false 来阻止该错误继续向上传播。

其他

  • onMounted, onUpdated, onUnmounted 等是 Composition API 中的生命周期钩子,它们与选项 API
    中的 mounted, updated, unmounted 类似,但需要在 setup() 函数中使用,并且是作为组合式函数调用的。

在 Vue 3 中,Composition API
提供了一种更灵活的方式来组织和重用逻辑,而生命周期钩子则是这种模式的一部分。开发者可以根据需要在 setup()
函数中使用这些钩子,或者继续使用选项 API 的生命周期钩子。

provide和inject跨组件通信

provideinject 是 Vue.js 3 中 Composition API
的一部分,用于跨组件层级直接传递数据,而不需要通过每个层级的组件显式地通过 propsevents
传递。这种方式特别适用于高层次的插件/组件库作者,或者当你需要在多个组件之间共享状态时。

provide

provide 允许你定义一些数据或方法,这些数据或方法可以被注入到任何子孙组件中。它通常在组件的 setup 函数中使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import {provide} from 'vue';

export default {
setup() {
const sharedData = 'shared data';

// 使用 provide 提供数据
provide('sharedDataKey', sharedData);

// 也可以提供响应式的数据
const sharedReactiveData = Vue.reactive({count: 0});
provide('sharedReactiveDataKey', sharedReactiveData);
}
};

inject

inject 允许子孙组件接收由祖先组件提供的 provide 数据。它也通常在组件的 setup 函数中使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {inject} from 'vue';

export default {
setup() {
// 使用 inject 接收数据
const sharedData = inject('sharedDataKey');
const sharedReactiveData = inject('sharedReactiveDataKey');

// 如果没有找到对应 key 的数据,可以提供一个默认值
const optionalData = inject('optionalDataKey', 'default value');

return {
sharedData,
sharedReactiveData,
optionalData
};
}
};

注意:也可以把父组件的方法传给子组件(第二个参数换成函数名即可)

特性

  • 跨层级: provideinject 可以跨越多个组件层级,不需要中间组件做任何处理。
  • 响应式: 当 provide 的数据是响应式的(例如使用 reactiveref 创建),注入的数据也会保持响应式。
  • 非响应式默认值: 如果 provide 没有提供某个 key 的数据,而 inject 请求了这个 key,可以提供一个默认值。
  • 类型推断: 在 TypeScript 中使用 provideinject 时,可以利用类型推断来提供更好的开发体验。

注意事项

  • provideinject 主要用于非父子组件间的通信,应当谨慎使用,以避免造成组件间的过度耦合。
  • 在 Vue 3 中,provideinject 是独立于 propsevents 的,它们不会触发视图的更新,除非提供的数据是响应式的。
  • 使用 provideinject 时,应当确保提供的数据是不可变的,或者至少是响应式的,以避免状态管理上的问题。

provideinject 提供了一种强大的机制来在组件树中传递数据,但应当谨慎使用,以保持代码的可维护性和清晰性。

pinia状态管理

解构赋值

响应式数据解构赋值:storeToRefs()

(ref的数据(不是方法)

这两个都是pinia里的响应式数据使用

1
const {count, doubleCount} = storeToRefs(counterStore)

解构方法

这是pinia管理的方法,还是普通解构就可以

1
const {ComputedCount} = counterStore

按需导入element-plus

lines
1
npm install -D unplugin-vue-components unplugin-auto-import

然后把下列代码插入到Vite配置文件中

Vite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// vite.config.ts
import {defineConfig} from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'

export default defineConfig({
// ...
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
})

状态集中管理

· eg:
· 在layout中进行pinia的store里的action(也就是方法)将获取的值共享在store里的state(也就是数据)
· 在子子孙孙组件中调用store里的state
这样就做到了,将需要重复调用的接口只向后端读一次,却能在很多地方获取这一个数据。
ps:但我个人悟出来是觉得,向什么商场分类啊这样比较固定的数据能够共享,变动几率大的不一定。(不过我不好说,还没有学到后面,突然想起来有个响应式)

配置主题色

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
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver({importStyle: "sass"})],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "~/styles/element/index.scss" as *;`,
},
},
}
})

自定义指令

参考官网:自定义指令
<script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。
在上面的例子中(详情请看官网),vFocus 即可以在模板中以 v-focus 的形式使用。
<script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。在上面的例子中,vFocus 即可以在模板中以
v-focus 的形式使用。

在没有使用 <script setup> 的情况下,自定义指令需要通过 directives 选项注册:

1
2
3
4
5
6
7
8
9
10
11
export default {
setup() {
/*...*/
},
directives: {
// 在模板中启用 v-focus
focus: {
/* ... */
}
}
}

将一个自定义指令全局注册到应用层级也是一种常见的做法:

1
2
3
4
5
6
const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
/* ... */
})

只有当所需功能只能通过直接的 DOM 操作来实现时,才应该使用自定义指令。其他情况下应该尽可能地使用 v-bind
这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好。

指令钩子

一个指令的定义对象可以提供几种钩子函数 (都是可选的):

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
   const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode) {
},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode) {
},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {
},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {
},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode) {
},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode) {
}
}

具体参考官方文档。
eg:图片懒加载功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//全局自定义指令(只看写法,位置不合理,后期改为插件注册到app中
app.directive('img-lazy', {
mounted(el, binding) {
//el 是绑定的元素
//binding binding.value是指令等于号后面绑定的表达式的值 即img的url
console.log(el, binding.value);
useIntersectionObserver
(
el,
([{isIntersecting}]) => {
console.log(isIntersecting);
}
)
}
})

懒加载优化

在src/directives/index.js

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
//定义懒加载插件
import {useIntersectionObserver} from '@vueuse/core'

export const lazyPlugin = {
//固定install
install(app) {
//懒加载逻辑
app.directive('img-lazy', {
mounted(el, binding) {
//el 是绑定的元素
//binding binding.value是指令等于号后面绑定的表达式的值 即img的url
console.log(el, binding.value);
useIntersectionObserver
(
el,
([{isIntersecting}]) => {
console.log(isIntersecting);
if (isIntersecting) {
el.src = binding.value
}
}
)
}
})
}
}

在main.js中写:

1
2
3
4
// 引入懒加载插件并注册
import {lazyPlugin} from "@/directives/index.js";
//import ....
app.use(router).use(pinia).use(ElementPlus).use(lazyPlugin)

发布Vue+Vite到云服务器

参考官方文档:Vite官网
Linux系统
ps:我的后端是springboot使用java部署的
似乎全部都用nginx反向代理会更复杂一些,后续我会开发有一下下。

vite.config.js

1
2
3
4
export default defineConfig({
//.....别的配置
base: "/test",
})

这个base是nginx的root目录的放文件那个文件名字:

eg:
我的nginx根目录是/nginx_img
但是我把前端项目文件放在了/nginx_img/test里,所以需要在vite.config.js写:
base: "/test",

1
2
3
4
5
6
7
8
9
server
{
listen 2024;
server_name phpmyadmin;
location / {
try_files $uri $uri/ /test/index.html;
}
root /nginx_img;
index index.html index.htm index.php;

try_files $uri $uri/ /test/index.html是vue-router为防止出现空白页和刷新页面出现404而写的。
具体可见官网:vue-router官网

Vuex知识点简述

Vuex是什么

腾讯元宝如是说道:

Vuex是一个专为Vue.js应用程序开发的状态管理模式和库。

一、状态管理方面

  1. 集中式存储

    在Vue应用中,随着组件数量的增多和交互逻辑的复杂,组件之间的数据传递可能会变得非常繁琐。Vuex提供了一个集中式的存储场所来管理应用的所有组件的状态(state)。例如,在一个电商应用中,用户的登录状态、购物车中的商品信息等都可以存储在Vuex的state中。
    • 这样,无论组件在组件树中的哪个位置,只要能访问到Vuex实例,就可以获取或修改这些共享状态。
  2. 响应式更新
    • Vuex中的state是响应式的。当state中的数据发生变化时,依赖于这些数据的组件会自动重新渲染。比如,购物车中的商品数量发生改变,显示购物车商品总数的组件会立即更新显示结果。

二、模块组织方面

  1. 模块划分
    • 对于大型应用,Vuex允许将store分割成模块。每个模块可以拥有自己的state、mutation、action和getter等。例如,在一个企业级管理系统中,可以有用户管理模块、权限管理模块、订单管理模块等。
    • 每个模块内部的状态管理逻辑相互独立,便于团队协作开发和代码维护。

三、数据操作方面

  1. Mutations

    Mutations是唯一修改state的方法。它是同步函数,通过提交mutation来改变state中的数据。例如,定义一个增加购物车商品数量的mutation,在组件中可以通过this.$store.commit('incrementCartQuantity', productId)
    这样的方式来触发这个mutation。
  2. Actions
    • Actions类似于mutations,但它是异步的。它可以包含任意的异步操作,比如发送网络请求获取数据后再修改state。例如,在登录功能中,先在action中发送登录请求,登录成功后再通过mutation修改用户的登录状态。
  3. Getters
    • Getters用于从state中派生出一些状态。比如计算购物车中商品的总价,可以在getters中定义一个计算总价的方法,组件可以直接使用这个计算结果而无需在每个组件内部重复计算。

四、插件支持方面

  1. 扩展功能
    • Vuex支持插件扩展。例如,可以编写插件来记录state的变化历史,方便调试;或者编写插件来实现状态的持久化存储,如将用户的设置信息保存到本地存储中,下次打开应用时自动恢复。

Vuex的state

在Vuex中,state 是用于存储应用中共享状态的数据对象。它类似于组件中的 data,但 state 是全局的,可以在多个组件之间共享和访问。state 中的数据是响应式的,这意味着当 state 发生变化时,依赖于这些数据的组件会自动重新渲染。

主要特点

  1. 集中式存储

    • state 提供了一个集中式的存储场所,用于管理应用的所有组件的状态。例如,用户的登录状态、购物车中的商品信息等都可以存储在 state 中。
  2. 响应式

    • state 中的数据是响应式的。当 state 中的数据发生变化时,依赖于这些数据的组件会自动重新渲染。

使用示例

假设我们有一个简单的计数器应用,我们可以这样定义和使用 state

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
31
32
// store.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
}
},
actions: {
increment({ commit }) {
commit('increment');
},
decrement({ commit }) {
commit('decrement');
}
},
getters: {
count: state => state.count
}
});

export default store;

在组件中使用 state

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
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>

<script>
export default {
computed: {
count() {
return this.$store.state.count;
}
},
methods: {
increment() {
this.$store.dispatch('increment');
},
decrement() {
this.$store.dispatch('decrement');
}
}
};
</script>

模块化

对于大型应用,state 可以被分割成多个模块,每个模块有自己的 statemutationsactionsgetters。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// store.js
const moduleA = {
state: { count: 0 },
mutations: { increment(state) { state.count++; } },
actions: { increment({ commit }) { commit('increment'); } },
getters: { count: state => state.count }
};

const moduleB = {
state: { count: 0 },
mutations: { increment(state) { state.count++; } },
actions: { increment({ commit }) { commit('increment'); } },
getters: { count: state => state.count }
};

const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
});

export default store;

在组件中使用模块化的 state

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
<template>
<div>
<p>Module A Count: {{ countA }}</p>
<p>Module B Count: {{ countB }}</p>
<button @click="incrementA">Increment A</button>
<button @click="incrementB">Increment B</button>
</div>
</template>

<script>
export default {
computed: {
countA() {
return this.$store.state.a.count;
},
countB() {
return this.$store.state.b.count;
}
},
methods: {
incrementA() {
this.$store.dispatch('a/increment');
},
incrementB() {
this.$store.dispatch('b/increment');
}
}
};
</script>

通过这种方式,state 可以更好地组织和管理复杂应用的状态。

vue前端开发的环境变量

环境变量简述

环境变量简述

vue对环境变量的要求

img.png