Fork me on GitHub

如何从零开始创建React应用:简易指南

如何从零开始创建React应用:简易指南

React是一个流行的JavaScript库,用于构建用户界面。通过使用Create React App,一个官方提供的脚手架工具,我们可以轻松地创建、开发和部署React应用,而无需深入了解底层构建配置。

创建一个React项目通常涉及到使用Node.js和npm(Node包管理器),因为React是一个基于JavaScript的库,而npm帮助我们管理项目中的依赖。下面是创建一个基本React项目的步骤:

1. 确保安装了Node.js和npm

在开始之前,你需要在你的计算机上安装Node.js和npm。你可以通过在命令行(Windows上的CMD或PowerShell,macOS或Linux上的终端)运行以下命令来检查它们是否已经安装:

1
2
node -v
npm -v

如果这两个命令都返回了版本号,那么你已经安装了Node.js和npm。

2. 全局安装Create React App

Create React App是一个官方提供的脚手架工具,它可以帮助我们快速创建一个React项目,而无需手动设置构建过程。在命令行中运行以下命令来全局安装Create React App:

1
npm install -g create-react-app

3. 创建一个新的React应用

安装完Create React App后,你可以通过运行以下命令来创建一个新的React应用:

1
create-react-app my-app

my-app替换为你希望的项目名称。这个命令会在当前目录下创建一个名为my-app的新文件夹,并设置好所有必要的文件和依赖。

4. 进入项目目录

创建完项目后,使用以下命令进入项目目录:

1
cd my-app

5. 启动开发服务器

在项目目录中,你可以使用以下命令来启动一个开发服务器,它会在你的应用上执行热加载:

1
npm start

默认情况下,你的应用将在http://localhost:3000上可用。当你对代码进行更改时,开发服务器会自动重新加载页面。

6. 编写React代码

现在,你可以开始编写React代码了。打开项目中的src文件夹,你会看到几个预生成的文件,包括index.jsindex.htmlApp.jsApp.js是你的React应用的主要组件。

7. 构建和部署

当你准备好将你的应用部署到生产环境时,你可以使用以下命令来创建一个优化后的构建版本:

1
npm run build

这个命令会创建一个build文件夹,其中包含了所有静态文件,你可以将这些文件部署到任何静态文件服务器上,或者使用如GitHub Pages、Netlify等服务来托管你的应用。

结尾

以上就是创建一个基本React项目的全部步骤。随着你对React的进一步学习,你可能会需要添加额外的依赖和工具,如路由库(如React Router)、状态管理库(如Redux或MobX)等。但Create React App提供的脚手架工具已经为你搭建了一个良好的起点,让你可以专注于编写React代码。

Spring Boot 多环境配置:YML 文件的三种高效方法

Spring Boot 多环境配置:YML 文件的三种高效方法

在现代软件开发实践中,维护多个环境(如开发、测试和生产)已经成为标准操作。Spring Boot 通过其灵活的配置机制,使得在不同环境中管理应用设置变得简单。尤其是使用 YAML 文件进行配置,它提供了一种简洁、易读的方式来定义应用的配置。本文将探讨在 Spring Boot 中使用 YAML 文件进行多环境配置的三种方法。

1. 使用不同文件名进行环境配置

Spring Boot 允许你为不同的环境创建具有特定名称的配置文件。例如,你可以有 application.yml 用于默认配置,application-dev.yml 用于开发环境,application-test.yml 用于测试环境,以及 application-prod.yml 用于生产环境。

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
# application.yml (默认环境)
spring:
datasource:
url: ${DB_URL:localhost}

# application-dev.yml (开发环境)
spring:
profiles:
active: dev
datasource:
url: localhost:3306/db_dev

# application-test.yml (测试环境)
spring:
profiles:
active: test
datasource:
url: localhost:3306/db_test

# application-prod.yml (生产环境)
spring:
profiles:
active: prod
datasource:
url: localhost:3306/db_prod

你可以通过设置 spring.profiles.active 属性来激活特定的环境配置。例如,如果你想使用开发环境配置,可以在启动应用时添加 -Dspring.profiles.active=dev 参数。

2. 使用 YAML 文件内的部分进行环境配置

在单个 YAML 文件中,你可以使用“部分”来区分不同环境的配置。这种方法的好处是所有配置都集中在一个文件中,易于管理。

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
spring:
profiles:
include: common
# 公共配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver

# 特定环境配置
---
spring:
profiles: dev
datasource:
url: localhost:3306/db_dev
username: dev_user
password: dev_pass

---
spring:
profiles: test
datasource:
url: localhost:3306/db_test
username: test_user
password: test_pass

---
spring:
profiles: prod
datasource:
url: localhost:3306/db_prod
username: prod_user
password: prod_pass

在这个例子中,common 部分包含了所有环境共享的配置。每个 --- 分隔的块代表一个特定的环境配置。

3. 使用环境变量进行动态配置

Spring Boot 可以读取环境变量来动态设置配置。你可以在系统环境变量或启动参数中设置 SPRING_CONFIG_NAMESPRING_CONFIG_LOCATION 来指定配置文件的名称和位置。

例如,你可以创建一个 application-override.yml 文件来覆盖其他配置:

1
2
3
4
5
# application-override.yml
spring:
datasource:
username: override_user
password: override_pass

然后,通过设置环境变量 SPRING_CONFIG_NAME=applicationSPRING_CONFIG_LOCATION=/path/to/config,Spring Boot 会加载 application.yml 和指定路径下的配置文件,并使用 application-override.yml 中的配置覆盖它们。

掌握Spring与Redis的高效交互:从Jedis到Spring Data Redis

掌握Spring与Redis的高效交互:从Jedis到Spring Data Redis

Redis以其卓越的性能和多功能性成为了不可或缺的组件。本文将深入探讨如何在Spring框架中通过Jedis和JedisPool直接操作Redis,以及如何利用Spring Data Redis简化数据交互过程。我们将从环境搭建开始,逐步介绍依赖引入、代码编写、功能实现,最终通过实例展示如何在Spring Boot应用中高效地使用Redis。无论你是初学者还是有经验的开发者,本文都将为你提供有价值的见解和实用的操作指南。

Spring中通过Jedis操作Redis数据库的实践指南

在现代的Web应用开发中,Redis已经成为了一个非常重要的组件,它以其出色的性能、完美的文档、简洁易懂的API和丰富的数据结构支持,被广泛应用于缓存、消息队列、排行榜、实时系统等多个场景。Spring作为Java界最受欢迎的框架之一,与Redis的结合使用自然也成为了开发者们的必备技能。本文将通过一段简单的代码示例,介绍如何在Spring框架中使用Jedis来操作Redis数据库。

环境准备

在开始之前,确保你的开发环境中已经安装了以下组件:

  • Java开发环境(JDK)
  • Maven或Gradle(用于依赖管理)
  • Redis服务端(可以从官网下载并自行搭建,也可以使用在线的Redis服务)
  • Spring Boot(简化Spring应用的搭建和配置)

引入依赖

在Spring Boot项目中,我们通常会使用Maven或Gradle来管理项目的依赖。为了使用Jedis,我们需要在项目的pom.xml(Maven)或build.gradle(Gradle)文件中添加以下依赖:

Maven:

1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>

Gradle:

1
implementation 'redis.clients:jedis:3.6.0'

请将最新版本号替换为当前Jedis的最新版本。

编写代码

以下是一个简单的Spring Boot控制器示例,展示了如何在一个GET请求中使用Jedis来操作Redis。

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
33
34
35
36
37
38
39
40
41
package com.example.springredis.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.util.Set;

@RestController
public class RedisController {

@GetMapping("test01")
public void test01() {
// 连接到Redis服务
Jedis jedis = new Jedis("127.0.0.1", 6379);
// 如果你的Redis设置了密码,需要进行认证
jedis.auth("yourPasswordHere");

// 获取所有的key
Set<String> keys = jedis.keys("*");
keys.forEach(System.out::println);

// 向Redis中添加几个key-value对
jedis.set("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
jedis.set("k4", "v4");
jedis.set("k5", "v5");

// 删除指定的key
Long c = jedis.del("k1", "k2", "k5");
System.out.println("删除key的个数: " + c);

// 判断指定的key是否存在
Boolean exists = jedis.exists("k2");
System.out.println("判断key是否存在: " + exists);

// 关闭资源
jedis.close();
}
}

代码解析

在这段代码中,我们首先创建了一个Jedis对象来连接到Redis服务。默认情况下,Jedis会尝试连接到127.0.0.1上的6379端口,这也是Redis的默认配置。如果你的Redis服务运行在不同的主机或端口上,你需要在构造Jedis对象时提供相应的参数。

接着,我们使用auth方法对Redis进行了认证,这一步仅在Redis设置了密码的情况下需要。

然后,我们使用keys方法获取了所有的key,并打印了出来。之后,我们通过set方法向Redis中添加了几个key-value对。

为了展示如何管理key,我们还演示了如何删除指定的key(使用del方法),以及如何检查一个key是否存在(使用exists方法)。

最后,我们关闭了Jedis对象,释放了与之相关的资源。

Spring中通过JedisPool高效操作Redis数据库的实践指南

在上一篇关于Spring通过Jedis操作Redis的文章中,我们了解了如何使用Jedis来执行基本的Redis操作。然而,在高并发的场景下,频繁地创建和销毁Jedis连接会导致性能问题。为了解决这一问题,Jedis提供了连接池的实现——JedisPool,它能够有效地管理Jedis连接,提高资源利用率和操作效率。本文将通过一个简单的Spring Boot控制器示例,介绍如何在Spring框架中使用JedisPool来高效操作Redis数据库。

引入依赖

首先,确保你的项目中已经引入了Jedis的依赖,如前文所述,在pom.xmlbuild.gradle中添加以下依赖:

Maven:

1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>

Gradle:

1
implementation 'redis.clients:jedis:3.6.0'

请将最新版本号替换为当前Jedis的最新版本。

编写代码

以下是一个Spring Boot控制器的示例,展示了如何使用JedisPool来操作Redis。

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
33
34
35
36
37
38
39
40
41
42
43
package com.example.springredis.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;
import java.util.Map;
import java.util.Set;

@RestController
public class RedisPoolController {

@GetMapping("test04")
public void test04() {
// 创建连接池的配置类
JedisPoolConfig config = new JedisPoolConfig();
config.setMinIdle(5); // 设置空闲的个数
config.setMaxIdle(10);
config.setMaxTotal(2000); // 设置最多的数量
config.setMaxWaitMillis(6000); // 设置最大的等待时长
config.setTestOnBorrow(true); // 是否检验池子中的jedis对象可用

// 创建jedis连接池对象
JedisPool jedisPool = new JedisPool(config, "127.0.0.1", 6379, 1000, "rootXH");

// 通过池子获取其中的一个连接
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
// 执行Redis操作
Map<String, String> map1 = jedis.hgetAll("myhash");
System.out.println("获取指定key对应的内容: " + map1);
} finally {
// 关闭Jedis连接
if (jedis != null) {
jedis.close();
}
}
}
}

代码解析

在这段代码中,我们首先创建了一个JedisPoolConfig对象,用于配置连接池的参数。我们设置了最小空闲连接数、最大空闲连接数、最大连接数、最大等待时长以及是否在借用连接时进行有效性检查。

接着,我们使用JedisPool构造函数创建了一个连接池对象。这里的参数包括配置类、Redis服务器的IP地址、端口号、超时时间和密码。请根据你的Redis服务器实际情况进行相应的修改。

然后,我们通过getResource方法从连接池中获取了一个Jedis实例。在这个实例上,我们可以执行各种Redis操作,例如本例中的hgetAll方法,它用于获取指定key对应的哈希表中的所有字段和值。

最后,我们使用finally块确保Jedis连接被正确关闭,无论操作是否成功。这是一个非常重要的步骤,因为它可以避免连接泄露和其他潜在的资源问题。

Spring Boot中使用Spring Data Redis操作数据库的实践指南

在现代的Web应用开发中,Redis已经成为了一个非常重要的组件,它以其出色的性能、完美的文档、简洁易懂的API和丰富的数据结构支持,被广泛应用于缓存、消息队列、排行榜、实时系统等多个场景。Spring Data Redis是Spring提供的一个简化Redis操作的框架,它使得我们可以更加便捷地在Spring应用中使用Redis。本文将通过一个Spring Boot控制器示例,介绍如何使用Spring Data Redis来操作Redis数据库。

环境准备

在开始之前,确保你的开发环境中已经安装了以下组件:

  • Java开发环境(JDK)
  • Maven或Gradle(用于依赖管理)
  • Redis服务端(可以从官网下载并自行搭建,也可以使用在线的Redis服务)
  • Spring Boot(简化Spring应用的搭建和配置)

引入依赖

在Spring Boot项目中,我们通常会使用Maven或Gradle来管理项目的依赖。为了使用Spring Data Redis,我们需要在项目的pom.xml(Maven)或build.gradle(Gradle)文件中添加以下依赖:

Maven:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Gradle:

1
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

编写代码

以下是一个Spring Boot控制器的示例,展示了如何使用Spring Data Redis来操作Redis。

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
33
34
35
36
37
package com.example.springredis.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisDataController {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@GetMapping("test05")
public void test05() {
// 获取对string类型操作的类对象
ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();

// 设置键值对
forValue.set("n1", "测试数据1");
forValue.set("n2", "2");
forValue.set("n3", "测试数据3");

// 如果存在 则不存入 不存在则存入
Boolean aBoolean = forValue.setIfAbsent("n4", "测试数据4", 25, TimeUnit.SECONDS);
System.out.println("是否存入成功 " + aBoolean);

// 获取对应的值
String n1 = forValue.get("n1");
System.out.println("n1 = " + n1);

// 递增操作
Long n2 = forValue.increment("n2");
System.out.println("n2递增后的值 " + n2);
}
}

代码解析

在这段代码中,我们首先通过@Autowired注解自动注入了一个StringRedisTemplate对象。StringRedisTemplate是Spring Data Redis提供的一个类,它封装了对字符串类型数据的操作。

接着,我们通过opsForValue方法获取了一个ValueOperations对象,它提供了对Redis中的字符串类型数据进行操作的方法。

我们使用set方法设置了三个键值对。然后,我们使用setIfAbsent方法尝试设置一个新的键值对,但仅当指定的key不存在时才进行设置。这个方法非常有用,当我们想要保证某个键值对的唯一性时。

之后,我们通过get方法获取了一个已经设置的键对应的值,并打印了出来。

最后,我们使用increment方法对一个键对应的值进行了递增操作。这个方法可以接受一个可选的增量参数,如果不指定,则默认递增1。

Vue项目实战:基于用户身份的动态路由管理

Vue项目实战:基于用户身份的动态路由管理

在现代Web应用中,根据不同用户的身份展示不同的路由和页面是一项常见的需求。Vue.js结合vue-router提供了强大的路由管理能力,允许我们根据后端接口的返回数据动态地添加和控制路由。本文将介绍如何在Vue项目中实现基于用户身份的动态路由管理,以及如何利用Vue的module模块来加载这些路由。

动态路由的基本概念

动态路由不仅指路径参数会变化的路由,还可以根据应用程序的运行时状态来动态添加或修改。在Vue中,这通常是通过vue-router的addRoute方法来实现的。

实现基于用户身份的动态路由

1. 配置基础路由

首先,在router.jsrouter/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
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../components/Home.vue'
import Login from '../components/Login.vue'
import Dashboard from '../components/Dashboard.vue'

const routes = [
{
path: '/',
component: Home
},
{
path: '/login',
component: Login
},
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true } // 标记需要身份验证的路由
}
]

const router = createRouter({
history: createWebHistory(),
routes
})

2. 用户身份验证模块

在Vue的module模块中处理用户身份验证逻辑。这通常涉及到与后端接口的交互,获取用户的登录状态和可访问的路由信息。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// store/auth.js
import { defineStore } from 'pinia'
const modules = import.meta.glob("../views/**/**.vue");

export const useAuthStore = defineStore('auth', {
state: () => ({
isAuthenticated: false,
routes: []
}),
actions: {
async login({ userId, routes }) {
this.isAuthenticated = true;
this.routes = routes;
// 动态添加路由
this.addRoutes(routes);
},
logout() {
this.isAuthenticated = false;
this.routes = [];
// 清除动态添加的路由
this.removeRoutes();
}
},
methods: {
addRoutes(newRoutes) {
newRoutes.forEach(route => {
router.addRoute(this.createRoute(route));
});
},
removeRoutes() {
this.routes.forEach(route => {
const routeRecord = router.getRoute(route.name);
if (routeRecord) {
router.removeRoute(routeRecord);
}
});
},
createRoute(config) {
return {
path: config.path,
component: modules[`../components/${config.component}.vue`],
meta: { requiresAuth: true }
};
}
}
})

3. 导航守卫

使用vue-router的导航守卫来检查用户是否有权限访问某个路由。

1
2
3
4
5
6
7
8
9
10
11
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (useAuthStore().isAuthenticated) {
next();
} else {
next('/login');
}
} else {
next();
}
})

4. 登录组件逻辑

在登录组件中调用身份验证模块的login方法,传入用户信息和可访问的路由。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// components/Login.vue
<template>
<!-- 登录表单 -->
</template>

<script>
import { useAuthStore } from '../store/auth'

export default {
methods: {
async onLogin() {
// 假设这是从后端接口获取的用户信息和路由
const userInfo = { userId: '123', routes: [{ path: '/dashboard', component: 'Dashboard' }]}
const authStore = useAuthStore();
await authStore.login(userInfo.userId, userInfo.routes);
}
}
}
</script>

结语

通过上述步骤,我们可以实现一个基于用户身份的动态路由管理系统。这种系统可以根据用户的登录状态和权限动态地添加和移除路由,确保用户只能访问他们被授权的页面。使用Vue的module模块和vue-router的addRoute方法,我们可以灵活地管理应用程序的路由结构,同时保持代码的清晰和可维护性。这种方法在构建企业级应用时尤其有用,因为它可以帮助我们实现复杂的权限控制和路由管理。

Vite多环境配置与打包:灵活高效的Vue开发工作流

Vite多环境配置与打包:灵活高效的Vue开发工作流

Vite作为一个新兴的前端构建工具,提供了快速的冷启动、即时模块热更新以及真正的按需编译,极大地提高了Vue开发效率。本文将向你展示如何在Vite中设置多环境开发和打包,包括如何配置环境变量、如何应用这些变量以及如何根据不同的环境进行项目的构建。通过这些步骤,你可以确保在开发、测试和生产等不同阶段中,你的应用都能以最佳状态运行。无论你是Vite的新手还是希望优化现有工作流的开发者,本文都将为你提供实用的指导和建议。

1. 安装依赖

1
npm install --save-dev cross-env

2. 配置环境变量

在项目根目录下创建 .env 文件,分别为不同环境创建不同的环境变量配置。例如:

  • .env:默认环境
  • .env.development:开发环境
  • .env.production:生产环境

在这些文件中,你可以设置不同的环境变量,例如 API 端点或其他服务的配置。

1
2
3
4
5
6
7
8
# .env
VITE_API_URL=https://api.example.com

# .env.development
VITE_API_URL=http://localhost:3000

# .env.production
VITE_API_URL=https://api.prod.example.com

3. 应用环境变量

在项目中,你可以通过 import.meta.env 访问这些环境变量。

1
2
// 在 Vue 组件或任何 JS 文件中
console.log(import.meta.env.VITE_API_URL);

4. 运行和构建项目

使用不同的环境变量运行和构建项目。在命令行中,你可以通过 --mode 选项指定环境。

1
2
3
4
5
# 开发环境
vite --mode development

# 生产环境
vite build --mode production

或者,你可以在 package.json 的脚本中定义不同的命令,以便更方便地运行和构建。

1
2
3
4
5
6
7
8
// package.json
{
"scripts": {
"dev": "vite --mode development",
"build:dev": "vite build --mode development",
"build:prod": "vite build --mode production"
}
}

然后,你可以使用 npm 或 yarn 运行这些脚本:

1
2
3
npm run dev
npm run build:dev
npm run build:prod

或者,如果你使用 Yarn:

1
2
3
yarn dev
yarn build:dev
yarn build:prod

通过这种方式,你可以轻松地在不同的环境之间切换,并针对不同的环境进行开发和打包。记得在部署到生产环境之前,使用生产环境的配置进行构建,以确保所有的环境变量和优化都已正确设置。

前端实现自动获取农历日期:探索JavaScript的跨文化编程

前端实现自动获取农历日期:探索JavaScript的跨文化编程

在全球化的今天,编写能够适应不同文化和地区的软件变得尤为重要。JavaScript作为前端开发的核心语言,不仅能够处理日常的交互逻辑,还能实现一些特定文化的日期计算,例如农历日期。本文将介绍如何使用JavaScript在前端自动获取并展示当前的农历日期,同时扩展我们的知识面,探索跨文化编程的重要性。

JavaScript在跨文化编程中的作用

JavaScript是一种动态、弱类型、基于原型的轻量级编程语言,它在网页上实现了交互性,使得网页从静态文档变成了可以与用户进行复杂交互的动态应用。在跨文化编程中,JavaScript的灵活性和广泛的浏览器支持使其成为实现文化相关功能的理想选择。

1. 国际化和本地化

JavaScript可以处理不同语言和地区的日期、货币、数字格式等,通过内置的Intl对象,开发者可以轻松实现国际化(Internationalization)和本地化(Localization)。

2. 文化敏感的UI设计

通过JavaScript,开发者可以根据用户的文化背景动态调整UI元素,例如使用适当的日期选择器、显示正确的货币符号等。

3. 时区处理

JavaScript能够处理时区转换,这对于创建需要考虑不同时区的应用程序(如在线会议调度工具)至关重要。

实现自动获取农历日期

以下是一个简单的JavaScript函数,用于获取并返回当前日期对应的农历信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getLunar(newDate) {
// ... 省略部分代码 ...

return Draw(); // 调用Draw函数返回农历信息
}

function getDate(date) {
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
return year + '-' + month + '-' + day;
}

export default getLunar(getDate(new Date())); // 调用getLunar函数获取当前农历日期

这个函数首先定义了一系列变量和辅助函数,然后通过Lunar类计算农历日期。最后,Draw函数将农历月份和日期格式化为字符串并返回。

兼容性和性能考虑

在实现农历日期计算时,需要考虑不同浏览器的兼容性。上述代码主要针对现代浏览器,对于旧版浏览器可能需要额外的polyfill或转译。同时,考虑到性能,复杂的日期计算最好在页面加载时进行,避免频繁的计算和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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* 获取某个日期的农历
* @parmas {newDate} 日期 年-月-日
*/
function getLunar(newDate) {
var nyear;
var nmonth;
var nday = -1;
var nwday;
var nhrs;
var nmin;
var nsec;
var newDate = newDate;

var lmonth, lday, lleap; //农历参数

function Draw() {
NewTick();
//显示时间
var s = nyear + '年' + nmonth + '月' + nday + '日 ' + '星期' + cweekday(nwday) + ' ' + shapetime(nhrs, nmin, nsec);
s += " 农历" + lmonth + "月" + lday; //农历
var lunar_month_day = lmonth + "月" + lday;
console.log(lunar_month_day);
return lunar_month_day;
}


function NewTick() {
var noww = newDate ? new Date(newDate) : new Date();
if (noww.getDate() != nday) {
nyear = noww.getFullYear();
nmonth = noww.getMonth() + 1;
nwday = noww.getDay();
nday = noww.getDate();

getlunar(); //获取农历
}
nhrs = noww.getHours();
nmin = noww.getMinutes();
nsec = noww.getSeconds();
}


//辅助函数
var hzWeek = new Array("日", "一", "二", "三", "四", "五", "六", "日");

function cweekday(wday) {
return hzWeek[wday];
}

function shapetime(vhrs, vmin, vsec) {
if (vsec <= 9) vsec = "0" + vsec;
if (vmin <= 9) vmin = "0" + vmin;
if (vhrs <= 9) vhrs = "0" + vhrs;
return vhrs + ":" + vmin + ":" + vsec
}

//农历函数开始
var lunarInfo = new Array(0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, //1990
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, 0x14b63);

function lYearDays(y) {
var i, sum = 348;
for (i = 0x8000; i > 0x8; i >>= 1) sum += (lunarInfo[y - 1900] & i) ? 1 : 0;
return (sum + leapDays(y));
}

function leapDays(y) {
if (leapMonth(y)) return ((lunarInfo[y - 1900] & 0x10000) ? 30 : 29);
else return (0);
}

function leapMonth(y) {
return (lunarInfo[y - 1900] & 0xf);
}

function monthDays(y, m) {
return ((lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29);
}

function Lunar(y, m, d) {
var i, leap = 0,
temp = 0;
var offset = (Date.UTC(y, m, d) - Date.UTC(1900, 0, 31)) / 86400000;
for (i = 1900; i < 2050 && offset > 0; i++) {
temp = lYearDays(i);
offset -= temp;
}
if (offset < 0) {
offset += temp;
i--;
}
this.year = i;
leap = leapMonth(i);
this.isLeap = false;
for (i = 1; i < 13 && offset > 0; i++) {
if (leap > 0 && i == (leap + 1) && this.isLeap == false) {
--i;
this.isLeap = true;
temp = leapDays(this.year);
} else {
temp = monthDays(this.year, i);
}
if (this.isLeap == true && i == (leap + 1)) this.isLeap = false;
offset -= temp;
}
if (offset == 0 && leap > 0 && i == leap + 1) if (this.isLeap) {
this.isLeap = false;
} else {
this.isLeap = true;
--i;
}
if (offset < 0) {
offset += temp;
--i;
}
this.month = i;
this.day = offset + 1;
}

var nStr1 = new Array('', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '十二');
var nStr2 = new Array('初', '十', '廿', '卅', '□');

function GetcDay(d) {
var s;
switch (d) {
case 10:
s = '初十';
break;
case 20:
s = '二十';
break;
case 30:
s = '三十';
break;
default:
s = nStr2[Math.floor(d / 10)];
s += nStr1[d % 10];
break;
}
return (s);
}

function GetcMon(m) {
if (m == 1) return '正';
else return nStr1[m];
}

function getlunar() {
var lObj = new Lunar(nyear, nmonth - 1, nday);
lmonth = GetcMon(lObj.month);
lday = GetcDay(lObj.day);
lleap = lObj.isLeap;
if (lleap == 1) {
lmonth = "闰" + lmonth;
}
}
//农历函数结束
return Draw();
}

function getDate(date) {
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
return year + '-' + month + '-' + day;
}

export default getLunar(getDate(new Date())); // 三月初六

跨文化编程的重要性

在开发面向全球用户的Web应用时,考虑文化差异是提升用户体验的关键。通过JavaScript,我们可以:

  • 提供本地化的内容:根据用户的地区和语言习惯展示内容。
  • 尊重文化习俗:在设计UI和交互时避免文化冲突。
  • 增强可访问性:确保所有用户,无论文化背景如何,都能轻松使用应用。

结语

通过JavaScript,我们不仅能够创建动态的Web应用,还能够编写出尊重并适应不同文化背景的代码。实现农历日期的自动获取只是跨文化编程的一个例子,它展示了如何将特定文化的需求融入到Web开发中。随着Web技术的不断进步,我们期待未来能够有更多的工具和方法来支持跨文化编程,让Web应用更加多元化和包容。

自定义滚动条样式:前端实现跨浏览器兼容

自定义滚动条样式:前端实现跨浏览器兼容

滚动条不仅是用户与网页交互的重要元素,也是网页美观性的一个细节体现。在前端开发中,自定义滚动条样式可以提升用户体验,使网页更加个性化。本文将介绍如何在前端设置滚轮滚动条样式,并确保兼容各大主流浏览器。

为什么自定义滚动条样式

  • 提升用户体验:符合网站整体风格的滚动条可以提供更一致的用户体验。
  • 增强视觉效果:独特的滚动条样式可以增强网页的视觉效果,吸引用户注意。
  • 保持一致性:在网站的不同部分保持滚动条样式的一致性,有助于维护网站的专业形象。

实现自定义滚动条

跨浏览器自定义滚动条样式主要依赖于CSS。下面是一些关键的CSS属性和技术,可以帮助你实现自定义滚动条。

1. 基本样式设置

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
/* 针对Webkit核心的浏览器(如Chrome和Safari) */
::-webkit-scrollbar {
width: 12px; /* 横向滚动条的宽度 */
height: 12px; /* 纵向滚动条的高度 */
}

::-webkit-scrollbar-thumb {
background: #c0c0c0; /* 滚动条滑块的背景颜色 */
border-radius: 6px; /* 滑块的圆角 */
}

::-webkit-scrollbar-track {
background: #f1f1f1; /* 滚动条轨道的背景颜色 */
}

/* 针对Firefox浏览器 */
scrollbar {
-moz-appearance: none;
width: 12px;
height: 12px;
}

scrollbar-thumb {
background: #c0c0c0;
border-radius: 6px;
}

scrollbar-track {
background: #f1f1f1;
}

2. 激活状态和悬停效果

你还可以为滚动条添加激活状态和悬停效果,以提供视觉反馈。

1
2
3
4
5
6
7
::-webkit-scrollbar-thumb:hover {
background: #a0a0a0; /* 滑块悬停时的背景颜色 */
}

scrollbar-thumb:hover {
background: #a0a0a0;
}

3. 针对特定元素的滚动条样式

如果你只想为页面中的特定元素设置滚动条样式,可以使用以下选择器:

1
2
3
4
5
6
7
8
9
10
11
12
/* 为ID为'scrollable-area'的元素设置滚动条样式 */
#scrollable-area::-webkit-scrollbar {
/* Webkit样式 */
}

#scrollable-area::-webkit-scrollbar-thumb {
/* Webkit滑块样式 */
}

scrollbar[orient="vertical"] {
/* Firefox样式 */
}

兼容性考虑

  • Webkit核心浏览器:包括Chrome、Safari和Opera,支持::-webkit-scrollbar伪元素。
  • Firefox浏览器:使用scrollbar伪元素,但不支持border-radius
  • Edge浏览器:新版Edge基于Chromium,与Chrome的兼容性相同。
  • Internet Explorer:不支持自定义滚动条样式。

结语

自定义滚动条样式是一个简单但有效的前端技巧,可以提升网页的整体美观度和用户体验。通过上述CSS代码,你可以为主流浏览器创建一致的滚动条样式。需要注意的是,自定义滚动条样式可能受到浏览器更新和用户设置的影响,因此在实际应用中需要进行充分的测试,确保在不同环境下都能保持良好的兼容性和稳定性。

无缝集成:使用Spring Boot和Vue实现头像上传与回显功能

无缝集成:使用Spring Boot和Vue实现头像上传与回显功能

在现代Web应用中,用户个性化体验尤为重要,其中头像上传与回显是一个常见的功能需求。本文将详细介绍如何使用Spring Boot和Vue.js构建一个前后端协同工作的头像上传系统,并实现图片的即时回显。

技术栈介绍

  • Spring Boot:一个基于Spring框架的开源项目,用于简化Spring应用的初始搭建以及开发过程。它通过提供默认配置减少开发中的配置工作,使得开发者能够快速启动和部署Spring应用。

  • Vue.js:一个渐进式JavaScript框架,用于构建用户界面。Vue的核心库只关注视图层,易于上手,同时也能够配合其他库或现有项目使用。Vue.js以其轻量级、高效性和灵活性而广受欢迎。

后端实现

后端使用Spring Boot框架,通过@RestController注解定义一个控制器UploadController,提供/upload端点处理文件上传请求。代码中使用了MultipartFile来接收上传的文件,并将其保存到服务器的指定目录。

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

代码注释

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
@RestController
public class UploadController {
@Operation(summary = "上传图片到本地")
@PostMapping("/upload")
public String upload(MultipartFile file) {
if (file.isEmpty()) {
return "图片为空";
}
String originalFilename = file.getOriginalFilename();
String fileNamePrefix = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
assert originalFilename != null;
String fileNameSuffix = "." + originalFilename.split("\\.")[1];
String fileName = fileNamePrefix + fileNameSuffix;
ApplicationHome applicationHome = new ApplicationHome(this.getClass());
//String pre = applicationHome.getDir().getParentFile().getParentFile().getAbsolutePath() + "\\src\\main\\resources\\static\\images\\";
String pre = applicationHome.getDir().getParentFile().getParentFile().getParentFile().getAbsolutePath() + "\\spring-ui\\src\\assets\\avatar\\";
String path = pre + fileName;
try {
file.transferTo(new File(path));
String replace = path.replace(applicationHome.getDir().getParentFile().getParentFile().getParentFile().getAbsolutePath() + "\\spring-ui\\src", "/src");
return replace.replace("\\", "/");
} catch (IOException e) {
e.printStackTrace();
}
return "图片上传失败";
}
}

前端实现

前端使用Vue.js框架,通过封装axios请求和全局配置,实现与后端的通信。同时,使用Vite作为构建工具,配置代理解决跨域问题。

代码注释

全局配置封装

1
2
3
4
// config/index.ts
export const baseURL = '/api'; // 基础URL
export const timeout = 10000; // 请求超时时间
export const headers = { 'X-Custom-Header': 'foobar' }; // 自定义请求头

Axios二次封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// request/index.ts
const request = axios.create({
// 配置axios实例
baseURL,
timeout,
headers
});
// 添加请求和响应拦截器
request.interceptors.request.use((config) => {
// 请求拦截器逻辑
return config;
}, (error) => {
// 响应拦截器逻辑
return Promise.reject(error);
});

Api接口请求

1
2
3
4
5
6
7
8
9
10
//api/index.ts
import * as common from '@/api/common'
import * as user from '@/api/user'

const api = {
common,
user,
}

export default api;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//api/common.ts
import request from "@/request";

// 响应接口
export interface UploadRes {}

/**
* 上传图片到本地
* @param {string} file
* @returns
*/
export function upload(file: object): Promise<UploadRes> {
return request.post(`/upload`, file);
}
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//api/user.ts
import request from "@/request";

// 参数接口
export interface UpdateUserParams {
id?: number;
name?: string;
sex?: string;
age?: number;
pic?: string;
acc?: string;
pwd?: string;
phone?: string;
email?: string;
dept?: string;
post?: string;
status?: string;
createBy?: string;
createTime?: string | unknown;
updateBy?: string;
updateTime?: string | unknown;
remark?: string;
}

// 响应接口
export interface UpdateUserRes {
message: string;
success: boolean;
code: number;
data: Record<string, unknown>;
}

/**
* 修改-账号
* @param {object} params $!{table.comment}
* @param {number} params.id
* @param {string} params.name 姓名
* @param {string} params.sex 性别
* @param {number} params.age 年龄
* @param {string} params.pic 头像
* @param {string} params.acc 账号
* @param {string} params.pwd 密码
* @param {string} params.phone 电话号码
* @param {string} params.email 电子邮件
* @param {string} params.dept 用户部门
* @param {string} params.post 用户岗位
* @param {string} params.status 状态(0正常 1停用)
* @param {string} params.createBy 创建者
* @param {object} params.createTime 创建时间
* @param {string} params.updateBy 更新者
* @param {object} params.updateTime 更新时间
* @param {string} params.remark 备注
* @returns
*/
export function updateUser(params: UpdateUserParams): Promise<UpdateUserRes> {
return request.post(`/userEntity/update`, params);
}

解决CORS跨域问题

1
2
3
4
5
6
7
8
9
10
11
12
// vite.config.ts
export default defineConfig({
server: {
proxy: {
"/api": {
target: "http://localhost:9090/", // 后端服务地址
changeOrigin: true, // 是否改变源
rewrite: (path) => path.replace(/\/api/, ""), // 重写路径
},
},
},
});

业务处理代码

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<template>
<!-- 上传组件和提示信息 -->
<el-upload drag :show-file-list="false" :limit="1" action="#" :auto-upload="false" accept=".png"
:on-change="handleChanges">
<el-icon class="el-icon--upload">
<upload-filled/>
</el-icon>
<div class="el-upload__text">
<em> 点击 </em> 或<em> 拖动文件 </em>上传
</div>
<template #tip>
<div class="el-upload__tip">
仅支持 jpg/png 格式文件大小小于 2MB
</div>
</template>
</el-upload>
</template>

<script setup>
import { ref } from "vue";
import api from "@/api";

// 响应式引用,用于存储用户信息
const user = ref({});

// 生命周期钩子,初始化时获取用户信息
onMounted(() => {
user.value = JSON.parse(localStorage.getItem("user"));
});

// 处理文件变化,上传文件并更新用户信息

//修改头像
const handleChanges = (file) => {
if (file.raw.type !== 'image/png') {//限制文件类型
ElMessage.error({message: "只能上传png格式的文件", grouping: true, showClose: true});
return false;
}
if (file.raw.size / 1024 / 1024 > 5) {
ElMessage.error('文件大于 5MB!')
return false;
}
const param = new FormData();
param.append('file', file.raw);
api.common.upload(param).then((res: any) => {
if (res !== null) ElMessage.success("上传成功");
if (res === null) ElMessage.error("上传失败");
api.user.updateUser({id: user.value.id, pic: res}).then((res: any) => {
api.user.selectUserByAcc(user.value.acc).then((res: any) => {
//更新缓存
localStorage.setItem("user", JSON.stringify(res.data));
//更新左侧描述列表
user.value = res.data;
})
})
})
};
</script>

<style scoped>
:deep(.el-descriptions__label) {
min-width: 60px !important;
}
</style>

功能演示

在文章的最后,我们将展示上传头像功能的完整流程,包括前端的上传界面、后端的文件保存逻辑,以及成功上传后的头像回显效果。

结语

通过本文的介绍,我们学习了如何使用Spring Boot和Vue.js实现一个完整的头像上传与回显功能。这种前后端分离的开发模式不仅提高了开发效率,也使得系统更加模块化和易于维护。随着技术的不断进步,我们期待未来能够有更多类似的协同工作解决方案出现,以满足不同场景下的开发需求。

Vue2和Vue3组件通信:父子与兄弟间的桥梁

Vue2和Vue3组件通信:父子与兄弟间的桥梁

在构建Vue应用时,掌握组件间的数据传输是至关重要的。本文将深入探讨Vue 2和Vue 3中父子组件间的通信机制,以及兄弟组件如何通过provide/inject API进行数据共享。我们还将讨论跨层级组件通信的策略,包括使用事件总线(Event Bus)的模式。通过实用的代码示例和清晰的解释,你将学习到如何有效地在不同层级的Vue组件之间传递数据。无论你是Vue新手还是有经验的开发者,本文都将帮助你更好地理解和应用Vue的组件通信模式,从而构建出更加健壮和可维护的应用程序。

父组件向子组件传递数据

1
2
3
4
5
6
7
8
9
10
11
<!-- 父组件 -->
<template>
<ChildComponent :parentMessage="message" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const message = ref('来自父组件的消息');
</script>
1
2
3
4
5
6
7
8
9
10
11
<!-- 子组件 -->
<template>
<div>{{ parentMessage }}</div>
</template>

<script setup lang="ts">
import { defineProps } from 'vue';
const props = defineProps({
parentMessage: String
});
</script>

子组件向父组件传递数据

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 子组件 -->
<template>
<button @click="sendMessageToParent">点击我发送消息给父组件</button>
</template>

<script setup lang="ts">
import { defineEmits } from 'vue';
const emit = defineEmits(['message-from-child']);

function sendMessageToParent() {
emit('message-from-child', '这是来自子组件的消息');
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 父组件 -->
<template>
<ChildComponent @message-from-child="receiveMessage" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const receiveMessage = (message: string) => {
console.log('父组件接收到的消息:', message);
}
</script>

兄弟组件间的数据传输

使用provide/inject API

1
2
3
4
5
6
7
8
9
<!-- 父组件 -->
<script setup lang="ts">
import { ref, provide } from 'vue';
import SiblingComponentA from './SiblingComponentA.vue';
import SiblingComponentB from './SiblingComponentB.vue';

const sharedData = ref('兄弟组件共享的数据');
provide('sharedDataKey', sharedData.value);
</script>
1
2
3
4
5
6
<!-- 兄弟组件A -->
<script setup lang="ts">
import { inject } from 'vue';

const sharedData = inject('sharedDataKey');
</script>
1
2
3
4
5
6
<!-- 兄弟组件B -->
<script setup lang="ts">
import { inject } from 'vue';

const sharedData = inject('sharedDataKey');
</script>

跨层级组件间的数据传输

跨层级组件的通信可以使用Vuex或Vue 3的provide/inject API,或者通过事件总线(Event Bus)。

使用事件总线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// eventBus.ts
import { reactive, readonly } from 'vue';

export const EventBus = reactive({
events: new Map<string, Array<(...args: any[]) => void>>()
});

export function emit(event: string, ...args: any[]) {
if (EventBus.events.has(event)) {
EventBus.events.get(event)?.forEach(handler => handler(...args));
}
}

export function on(event: string, handler: (...args: any[]) => void) {
if (!EventBus.events.has(event)) {
EventBus.events.set(event, new Array<(...args: any[]) => void>());
}
EventBus.events.get(event)?.push(handler);
}

在需要发送事件的组件中:

1
2
3
4
5
6
7
8
<script setup lang="ts">
import { onMounted } from 'vue';
import { emit } from './eventBus';

onMounted(() => {
emit('event-name', { data: '这是来自子组件的消息' });
});
</script>

在需要接收事件的组件中:

1
2
3
4
5
6
7
8
9
10
<script setup lang="ts">
import { onMounted } from 'vue';
import { on } from './eventBus';

onMounted(() => {
on('event-name', (data) => {
console.log('接收到的事件数据:', data.data);
});
});
</script>

结语

使用 <script setup> 语法可以让我们以更简洁的方式编写Vue组件,尤其是在TypeScript的支持下,我们可以享受到更好的类型推断和自动补全。这使得组件间的通信代码更加清晰和易于维护。在构建大型应用时,合理地使用这些通信模式将有助于保持应用的模块化和可扩展性。

前端开发全景指南:语言与框架的精粹

前端开发全景指南:语言与框架的精粹

前端开发是现代Web应用的基石,它涉及到一系列编程语言和框架的使用,这些工具共同塑造了用户与互联网世界的交互体验。本文将深入探讨前端开发中的核心语言和流行框架,为你揭示构建网页和应用的精髓。

核心语言:前端开发的基石

HTML:构建内容的骨架

HTML是网页内容的标记语言,它定义了网页的结构和内容。作为前端开发的基础,HTML为后续的样式和交互提供了必要的框架。

CSS:美化视觉的艺术家

CSS负责网页的表现和样式,它通过选择器和属性控制元素的外观。CSS的灵活性和强大的布局能力使得设计师的创意得以完美呈现。

JavaScript:动态交互的魔法师

JavaScript为网页注入活力,它能够响应用户的行为,动态更新内容,甚至与服务器进行通信。JavaScript的生态圈庞大,支持开发者创建丰富多彩的用户体验。

TypeScript:JavaScript的严谨进化

TypeScript是JavaScript的超集,它引入了静态类型系统,使得代码更加健壮和易于维护。TypeScript在现代前端开发中越来越受到欢迎,尤其是在大型项目中。

JSON:数据交换的新宠

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它易于人阅读和编写,同时也易于机器解析和生成。JSON在Web服务和API中广泛使用,成为前后端数据交换的标准格式。

流行框架:提升开发效率的利器

Angular:全面解决方案的构建者

Angular是一个由Google支持的全面前端框架,它提供了从模板、表单处理到依赖注入的一整套解决方案。Angular的设计哲学是约定优于配置,适合构建大型企业级应用。

Vue.js:渐进式框架的代表

Vue.js以其轻量级和易用性著称,它允许开发者以渐进的方式集成到项目中。Vue的核心库专注于视图层,同时它的生态系统也支持构建复杂的单页应用。

React.js:组件化开发的先驱

React.js是Facebook开发的一个库,它通过组件化和虚拟DOM技术,提供了高效的用户界面构建方案。React的生态系统丰富,社区活跃,是当前最受欢迎的前端框架之一。

Svelte:新兴的编译型框架

Svelte是一个独特的前端框架,它在构建阶段进行大部分工作,将应用编译成高效的JavaScript代码。Svelte的应用性能优异,开发体验流畅。

Ember.js和Backbone.js:功能丰富的框架

Ember.js和Backbone.js是两个提供全面功能的前端框架。它们提供了丰富的工具和约定,帮助开发者构建结构化和可维护的Web应用。

结语

前端开发的世界丰富多彩,选择合适的语言和框架对于项目的成功至关重要。无论是基础的HTML、CSS和JavaScript,还是强大的框架如React、Angular和Vue,每一种工具都有其独特的价值和应用场景。了解它们的特点是每个前端开发者的必修课,也是创造令人印象深刻的Web体验的关键。随着技术的不断进步,前端领域将继续发展,带来更多的可能性和挑战。

  • Copyrights © 2022-2024 何福海
  • 访问人数: | 浏览次数:

请我喝杯奶茶吧~

支付宝
微信