Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
跨语言项目环境变量管理:GVM的灵活应用与实践_创想鸟

跨语言项目环境变量管理:GVM的灵活应用与实践

跨语言项目环境变量管理:GVM的灵活应用与实践

本文探讨了如何在Go项目及其他跨语言环境中高效管理项目专属的环境变量,旨在提供一种类似于virtualenvwrapper的通用解决方案,摆脱特定语言工具的限制。通过介绍Go版本管理器GVM,并结合自定义Shell脚本,我们将演示如何构建一套灵活的工作流,实现项目激活时自动设置环境变量,并在项目停用时进行清理,从而确保开发环境的隔离与整洁。

引言:跨项目环境变量管理的挑战

在日常开发中,我们经常需要在不同的项目之间切换,而每个项目可能依赖于特定的环境变量、工具版本或配置。例如,一个项目可能需要go 1.18,而另一个项目则需要go 1.20,同时还可能有一些项目独有的api密钥、路径设置或其他自定义环境变量。传统上,开发者会使用如python的virtualenvwrapper等工具来管理这些项目隔离的环境,它提供了workon、deactivate以及pre/post激活脚本等便捷功能。然而,当项目技术栈不限于python时,寻找一个通用且易于设置的解决方案就变得尤为重要。

本教程将以Go项目为例,介绍如何利用Go版本管理器GVM,并结合Shell脚本的灵活性,构建一个能够模拟virtualenvwrapper工作流的通用环境变量管理方案,满足跨语言项目的环境隔离需求。

GVM:Go项目环境管理的利器

对于Go项目而言,GVM(Go Version Manager)是一个强大的工具,它允许开发者在同一台机器上安装和管理多个Go版本,并根据项目需求轻松切换。它的设计理念与Ruby的RVM(Ruby Version Manager)非常相似,主要解决了Go版本冲突和环境隔离的问题。虽然GVM主要专注于Go版本管理,但我们可以巧妙地将其与自定义Shell脚本结合,扩展其功能以管理更广泛的项目环境变量。

GVM的安装与基本使用

首先,我们需要安装GVM并了解其基本用法。

1. 安装GVM

GVM的安装过程相对简单,通常只需执行以下命令:

bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)

安装完成后,根据提示将GVM的初始化脚本添加到您的Shell配置文件(如~/.bashrc、~/.zshrc)中:

# 例如,对于Bash用户[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"

然后重新加载您的Shell配置:

source ~/.bashrc # 或 source ~/.zshrc

2. 管理Go版本

GVM允许您轻松安装和切换Go版本。

列出可用Go版本:

gvm listall

安装特定Go版本:

gvm install go1.20 # 安装Go 1.20gvm install go1.18 # 安装Go 1.18

切换Go版本:

gvm use go1.20    # 切换到Go 1.20gvm use go1.18    # 切换到Go 1.18gvm use system    # 切换回系统默认的Go版本

通过gvm use 命令,GVM会修改当前Shell的环境变量(如GOROOT和PATH),使得go命令指向指定的版本。

构建类virtualenvwrapper的工作流

虽然GVM自身没有workon或deactivate等命令来管理任意环境变量,但我们可以通过自定义Shell函数和项目脚本来模拟这种行为,实现项目激活时的环境变量设置和停用时的清理。

1. 定义项目特定的环境变量文件

在每个项目根目录下,我们可以创建几个文件来存储项目特定的配置和脚本:

Shakker Shakker

多功能AI图像生成和编辑平台

Shakker 103 查看详情 Shakker ./.project_env.sh:存放项目所需的自定义环境变量,如export MYVAR=xxx。./.preactivate.sh:在项目环境激活前执行的脚本。./.postdeactivate.sh:在项目环境停用后执行的脚本。

示例:myproject/.project_env.sh

#!/bin/bashexport MY_CUSTOM_VAR="This is a project-specific value"export ANOTHER_PATH="/opt/myproject/bin:$PATH"echo "Project environment variables loaded."

示例:myproject/.preactivate.sh

#!/bin/bashecho "Pre-activation script for myproject executed."# 可以在这里执行一些项目启动前的检查或准备工作

示例:myproject/.postdeactivate.sh

#!/bin/bashecho "Post-deactivation script for myproject executed."# 可以在这里执行一些项目清理或收尾工作

2. 创建自定义Shell函数

在您的Shell配置文件(~/.bashrc或~/.zshrc)中,添加以下函数来模拟workon和deactivate的行为:

# 存储当前激活的项目路径,用于 deactivate_CURRENT_PROJECT_ENV=""_ORIGINAL_PATH="$PATH" # 备份原始PATH# workon 函数:激活项目环境function workon() {    local project_path="$1"    local go_version="$2"    if [ -z "$project_path" ]; then        echo "Usage: workon  [go_version]"        return 1    fi    if [ ! -d "$project_path" ]; then        echo "Error: Project path '$project_path' does not exist."        return 1    fi    # 切换到项目目录    cd "$project_path" || return 1    echo "Working on project: $(basename "$project_path")"    # 设置Go版本 (如果指定)    if [ -n "$go_version" ]; then        gvm use "$go_version" || { echo "Error: Failed to use Go version '$go_version'."; return 1; }        echo "Go version set to: $(go version)"    else        echo "No specific Go version provided. Using current GVM/system default."    fi    # 备份当前环境,以便 deactivate 时恢复    _CURRENT_PROJECT_ENV=$(pwd)    export _ORIGINAL_PATH="$PATH" # 每次workon都更新原始PATH备份,以防多次workon    # 执行 preactivate 脚本    if [ -f ".preactivate.sh" ]; then        source ".preactivate.sh"    fi    # 加载项目环境变量    if [ -f ".project_env.sh" ]; then        source ".project_env.sh"        echo "Project-specific environment variables loaded from .project_env.sh."    fi}# deactivate 函数:停用项目环境function deactivate() {    if [ -z "$_CURRENT_PROJECT_ENV" ]; then        echo "No project environment is currently active."        return 1    fi    echo "Deactivating project environment for: $(basename "$_CURRENT_PROJECT_ENV")"    # 执行 postdeactivate 脚本    if [ -f "$_CURRENT_PROJECT_ENV/.postdeactivate.sh" ]; then        source "$_CURRENT_PROJECT_ENV/.postdeactivate.sh"    fi    # 清理通过 .project_env.sh 设置的环境变量    # 注意:这里需要手动反向操作,或者只unset那些明确知道的变量    # 更健壮的方法是,在workon时记录所有变更,deactivate时回滚。    # 为简化示例,我们只unset MY_CUSTOM_VAR和恢复PATH    unset MY_CUSTOM_VAR    export PATH="$_ORIGINAL_PATH" # 恢复原始PATH    # 切换回系统默认Go版本或清除GVM设置 (可选,根据需求)    # gvm use system # 如果你希望deactivate后总是回到系统Go    # 或者只是清除当前GVM的PATH设置,让它回到未激活状态    # GVM没有直接的“deactivate”功能,通常是切换到另一个版本或系统版本    # 清除当前项目环境标记    _CURRENT_PROJECT_ENV=""    echo "Project environment deactivated."}

重新加载Shell配置:

source ~/.bashrc # 或 source ~/.zshrc

3. 使用工作流

现在,您可以在任何地方使用这些函数来管理您的项目环境:

激活项目环境:

# 假设您的项目在 ~/projects/myproject,并需要Go 1.20workon ~/projects/myproject go1.20

执行此命令后,您将进入~/projects/myproject目录,Go版本将切换到1.20,并且myproject/.preactivate.sh和myproject/.project_env.sh中的环境变量将被加载。

停用项目环境:

deactivate

执行此命令后,myproject/.postdeactivate.sh将执行,自定义环境变量将被清除,PATH将被恢复到workon之前的状态。

注意事项与最佳实践

环境变量清理: 上述deactivate函数对环境变量的清理是手动的,只针对示例中的MY_CUSTOM_VAR和PATH。对于更复杂的场景,您可能需要一个更健壮的机制来记录workon时设置的所有环境变量,并在deactivate时精确地unset它们,或者恢复到workon之前的完整环境快照。GVM与Shell函数的结合: GVM主要管理Go版本,而自定义Shell函数则负责实现virtualenvwrapper式的通用环境变量管理。两者结合,可以为Go项目提供全面的环境隔离。脚本的幂等性: 确保.preactivate.sh和.postdeactivate.sh脚本是幂等的,即多次执行不会产生副作用。项目路径: workon函数需要提供项目的绝对或相对路径。您也可以进一步优化,使其能够像virtualenvwrapper一样,通过项目名称自动查找路径。替代方案: 对于真正语言无关的、更通用的环境变量管理,可以考虑direnv或autoenv这类工具。它们通过在目录进入/退出时自动加载/卸载.envrc或.env文件来实现环境变量的自动管理,与GVM结合使用也能达到很好的效果。然而,本教程严格遵循了利用GVM作为核心的解决方案。

总结

通过将Go版本管理器GVM与自定义Shell函数及项目特定的脚本相结合,我们成功构建了一个灵活且功能强大的跨项目环境变量管理方案。这个方案不仅能够满足Go项目对特定Go版本环境的需求,还能通过pre/post激活脚本管理任意项目独有的环境变量,从而在不同项目之间提供清晰、隔离的开发环境。这种方法摆脱了对特定语言工具的依赖,为多技术栈的开发者提供了一个通用的环境管理框架。

以上就是跨语言项目环境变量管理:GVM的灵活应用与实践的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1136414.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
伪元素覆盖出现白边?两种方法帮你解决
上一篇 2025年12月2日 17:54:34
MySQL如何实现更新查询?
下一篇 2025年12月2日 17:54:45

相关推荐

  • Java Stream API处理嵌套对象字段求和:以购物车条目为例

    Java Stream API处理嵌套对象字段求和:以购物车条目为例Java Stream API处理嵌套对象字段求和:以购物车条目为例Java Stream API处理嵌套对象字段求和:以购物车条目为例Java Stream API处理嵌套对象字段求和:以购物车条目为例

    本文深入探讨如何利用Java Stream API高效地对复杂嵌套对象中的数值字段进行聚合求和。通过一个购物车条目的具体案例,演示了如何从多层级对象中提取并累加BigDecimal类型的费用数据,强调了mapToDouble、BigDecimal的精确计算以及在处理可能为空的字段时,结合注解进行nu…

    2026年9月27日 • 用户投稿
    000
  • 抖音带货橱窗怎么弄?抖音怎么开通橱窗

    抖音平台已成为电商盈利的关键渠道。抖音带货橱窗作为其核心功能,为众多电商从业者提供了高效的销售渠道。本文将详细介绍如何创建抖音带货橱窗,助您实现电商收益。 一、抖音带货橱窗的基础定义 抖音带货橱窗是商家通过平台开设的展示区,用于陈列商品信息并促成销售。橱窗涵盖服饰、美妆、家居、食品等多种商品类型,为…

    2026年9月27日
    000
  • MAC如何安全移除外置硬盘_macOS正确推出外置磁盘与U盘方法

    MAC如何安全移除外置硬盘_macOS正确推出外置磁盘与U盘方法MAC如何安全移除外置硬盘_macOS正确推出外置磁盘与U盘方法MAC如何安全移除外置硬盘_macOS正确推出外置磁盘与U盘方法MAC如何安全移除外置硬盘_macOS正确推出外置磁盘与U盘方法

    答案:可通过桌面图标、访达、拖拽至废纸篓、菜单栏图标、快捷键或终端命令安全弹出外置设备。具体操作包括右键点击桌面图标选择推出、在访达侧边栏点击弹出按钮、拖动设备图标到废纸篓、从菜单栏状态图标选择弹出、使用Command+E快捷键,或在终端输入diskutil unmountDisk命令完成弹出,确保…

    2026年9月27日 • 用户投稿
    000
  • Stripe Webhook签名验证错误解析与中间件顺序优化

    Stripe Webhook签名验证错误解析与中间件顺序优化Stripe Webhook签名验证错误解析与中间件顺序优化Stripe Webhook签名验证错误解析与中间件顺序优化Stripe Webhook签名验证错误解析与中间件顺序优化

    Stripe Webhook签名验证时出现”Payload must be provided as a string or a Buffer”错误,通常是由于Express应用中全局express.json()中间件过早解析了原始请求体。本文将深入解析此问题,并提供通过调整中…

    2026年9月27日 • 用户投稿
    000
  • OpenAI 推出全新 AI 功能 ChatGPT Pulse

    OpenAI 推出全新 AI 功能 ChatGPT PulseOpenAI 推出全新 AI 功能 ChatGPT PulseOpenAI 推出全新 AI 功能 ChatGPT PulseOpenAI 推出全新 AI 功能 ChatGPT Pulse

    openai 宣布上线全新功能 chatgpt pulse,标志着 chatgpt 正式从被动响应式对话工具进化为具备主动服务能力的个性化助手。该功能目前以预览版本的形式率先登陆移动端,仅对 pro 订阅用户(每月 200 美元)开放使用权限。 据官方介绍,Pulse 是 ChatGPT 全新打造的…

    2026年9月27日 • 用户投稿
    100
  • windows怎么查看电脑支持的内存大小 windows查看电脑最大支持内存方法

    windows怎么查看电脑支持的内存大小 windows查看电脑最大支持内存方法windows怎么查看电脑支持的内存大小 windows查看电脑最大支持内存方法windows怎么查看电脑支持的内存大小 windows查看电脑最大支持内存方法windows怎么查看电脑支持的内存大小 windows查看电脑最大支持内存方法

    首先通过命令提示符输入wmic memphysical get maxcapacity获取主板最大支持内存,再结合CPU-Z等工具或官网规格确认具体参数,最后用任务管理器查看插槽使用情况以规划升级方案。 如果您计划升级电脑内存,但不确定主板或系统最大支持多大的内存容量,则需要准确查询硬件的限制。以下…

    2026年9月27日 • 用户投稿
    100
  • sublime怎么设置在按下回车时自动缩进_sublime按回车自动缩进设置

    sublime怎么设置在按下回车时自动缩进_sublime按回车自动缩进设置sublime怎么设置在按下回车时自动缩进_sublime按回车自动缩进设置sublime怎么设置在按下回车时自动缩进_sublime按回车自动缩进设置sublime怎么设置在按下回车时自动缩进_sublime按回车自动缩进设置

    确保”auto_indent”: true已启用,使新行继承上一行缩进;2. 检查并正确设置文件语法类型,确保语言识别无误;3. 推荐配置”translate_tabs_to_spaces”: true和”tab_size”: …

    2026年9月27日 • 用户投稿
    200
  • JScrollPane滚动条自动更新行为的控制策略与实践

    JScrollPane滚动条自动更新行为的控制策略与实践JScrollPane滚动条自动更新行为的控制策略与实践JScrollPane滚动条自动更新行为的控制策略与实践JScrollPane滚动条自动更新行为的控制策略与实践

    本教程详细探讨如何有效控制JScrollPane的滚动条自动更新行为,特别是在内容重绘后避免意外滚动。文章重点介绍通过设置滚动条策略(如ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER)来禁用滚动条或其自动更新,并简要提及Viewport尺寸调整的替代方法…

    2026年9月27日 • 用户投稿
    000
  • iCloud官网怎么使用隐藏邮件_iCloud隐藏邮件地址功能

    iCloud官网怎么使用隐藏邮件_iCloud隐藏邮件地址功能iCloud官网怎么使用隐藏邮件_iCloud隐藏邮件地址功能iCloud官网怎么使用隐藏邮件_iCloud隐藏邮件地址功能iCloud官网怎么使用隐藏邮件_iCloud隐藏邮件地址功能

    使用iCloud+“隐藏邮件地址”功能可保护真实邮箱,通过iPhone设置或iCloud官网创建唯一转发邮箱,支持添加标签、启用垃圾邮件防护,并可随时停用或删除已创建的地址。 如果您希望在注册网站或服务时保护您的真实电子邮件地址,iCloud+ 提供的“隐藏邮件地址”功能可以生成一个唯一的、可转发的…

    2026年9月27日 • 用户投稿
    500
  • 京东拼购入驻,所需条件与费用有哪些?详解商家资质、经验要求及质保金、平台费、扣点明细!

    京东拼购入驻,所需条件与费用有哪些?详解商家资质、经验要求及质保金、平台费、扣点明细!京东拼购入驻,所需条件与费用有哪些?详解商家资质、经验要求及质保金、平台费、扣点明细!京东拼购入驻,所需条件与费用有哪些?详解商家资质、经验要求及质保金、平台费、扣点明细!京东拼购入驻,所需条件与费用有哪些?详解商家资质、经验要求及质保金、平台费、扣点明细!

    在电商迅猛发展的当下,京东拼购为大量商家开辟了一个充满潜力的销售渠道。然而,若想成功入驻该平台,商家必须清楚了解其入驻门槛及相关费用信息。这不仅有助于提前筹备相关材料,还能有效提升审核通过率,助力商家快速切入这一活跃市场,开启线上增长新篇章。 一、京东拼购入驻条件 (一)基本资质要求 1. 持有有效…

    2026年9月27日 • 用户投稿
    200
  • linux Kernel

    linux Kernellinux Kernellinux Kernellinux Kernel

    探索linux内核的奥秘,从基础到高级,逐步揭开其神秘面纱。无论你是初学者还是资深开发者,本文都将为你提供深入了解linux内核所需的知识和工具。 以上就是linux Kernel的详细内容,更多请关注创想鸟其它相关文章!

    2026年9月27日 • 用户投稿
    100
  • Codename One:实现安全的数字密码输入框

    Codename One:实现安全的数字密码输入框Codename One:实现安全的数字密码输入框Codename One:实现安全的数字密码输入框Codename One:实现安全的数字密码输入框

    本文详细介绍了在Codename One中创建既能接受纯数字输入又能进行密码遮罩的文本输入框的方法。通过使用位或运算符(|)组合TextArea.NUMERIC和TextArea.PASSWORD约束,可以有效地实现这一需求,避免了链式调用constraint()的常见误区,从而提升用户体验和输入安…

    2026年9月27日 • 用户投稿
    100
  • 苹果 A19 芯片内部特写:架构微调带来能效提升

    苹果 A19 芯片内部特写:架构微调带来能效提升苹果 A19 芯片内部特写:架构微调带来能效提升苹果 A19 芯片内部特写:架构微调带来能效提升苹果 A19 芯片内部特写:架构微调带来能效提升

    苹果最新一代的a19芯片在芯片制造工艺方面较a18有显著提升,近期一组显微照片首次揭示了iphone 17系列最新芯片的内部细节。 本次随iPhone 17发布亮相的A19及A19 Pro芯片,是苹果自研芯片设计的又一进步。尽管苹果方面针对性能提升作出多项官方承诺,但由ChipWise团队拍摄的So…

    2026年9月27日 • 用户投稿
    200
  • AZ录屏画笔使用技巧

    AZ录屏画笔使用技巧AZ录屏画笔使用技巧AZ录屏画笔使用技巧AZ录屏画笔使用技巧

    az屏幕录制是一款功能多样的录屏软件,除了提供常规的录屏操作外,还拥有许多实用的附加功能,比如在录制时可使用的画笔标注功能。 1、 打开手机中的AZ屏幕录制应用 2、 进入应用设置界面后向下滑动,找到“录制工具”相关选项 3、 开启画笔功能后,屏幕上将会出现画笔图标 笔魂AI 笔魂AI绘画-在线AI…

    2026年9月27日 • 用户投稿
    000
  • win8如何查看windows体验指数_Win8体验指数查看方法

    win8如何查看windows体验指数_Win8体验指数查看方法win8如何查看windows体验指数_Win8体验指数查看方法win8如何查看windows体验指数_Win8体验指数查看方法win8如何查看windows体验指数_Win8体验指数查看方法

    1、可通过控制面板或运行命令查看Windows体验指数。首先在控制面板中进入“系统和安全”→“操作中心”→“维护”→“查看性能信息”,即可查看总分及各硬件子分数;或使用“Win+R”输入“shell:games”打开游戏文件夹,右键空白处选择“属性”,直接查看Windows体验指数的基准分数和各项子…

    2026年9月27日 • 用户投稿
    100
  • 豆包AI如何实现智能排版 豆包AI文档自动格式化功能

    豆包AI提供的智能排版功能,能够显著提升文档处理效率。本文将详细介绍如何利用豆包AI的文档自动格式化能力,解决文档排版耗时的问题。我们将逐步解析使用该功能的过程,包括如何启用、输入内容以及完成格式化,以便用户能够轻松掌握并应用。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 …

    2026年9月27日
    100
  • VSCode如何实现代码片段云端同步 VSCode代码片段跨设备同步方案

    vscode实现代码片段云端同步的核心是将代码片段存储于云端并通过扩展或服务在多设备间同步;2. 可通过settings sync扩展结合github gist同步设置与代码片段;3. 可将代码片段文件夹置于dropbox、google drive等云存储服务中实现自动同步;4. 可使用git私有仓…

    2026年9月27日
    200
  • Java Swing GUI:构建交互式逻辑门(AND门示例)

    Java Swing GUI:构建交互式逻辑门(AND门示例)Java Swing GUI:构建交互式逻辑门(AND门示例)Java Swing GUI:构建交互式逻辑门(AND门示例)Java Swing GUI:构建交互式逻辑门(AND门示例)

    本文详细介绍了如何使用Java Swing构建一个简单的AND逻辑门GUI应用。通过结合JCheckBox作为输入和JLabel作为视觉输出,并利用ChangeListener监听组件状态变化,实现当两个复选框都被选中时显示“绿色”,否则显示“红色”的功能。教程涵盖了组件创建、事件监听以及将自定义面…

    2026年9月27日 • 用户投稿
    100
  • AI Overviews如何设置智能告警 AI Overviews异常检测阈值

    AI Overviews如何设置智能告警 AI Overviews异常检测阈值AI Overviews如何设置智能告警 AI Overviews异常检测阈值AI Overviews如何设置智能告警 AI Overviews异常检测阈值AI Overviews如何设置智能告警 AI Overviews异常检测阈值

    本文将详细指导您如何在AI Overviews中设置智能告警以及调整异常检测阈值,帮助您及时发现并响应潜在问题。我们将分步骤讲解具体操作过程,确保您能够顺利配置这些关键功能,从而实现更主动、更精准的系统监控。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R…

    2026年9月27日 • 用户投稿
    000
  • 曝华为Mate80系列新增Air机型 支持eSIM 采用微泵液

    曝华为Mate80系列新增Air机型 支持eSIM 采用微泵液曝华为Mate80系列新增Air机型 支持eSIM 采用微泵液曝华为Mate80系列新增Air机型 支持eSIM 采用微泵液曝华为Mate80系列新增Air机型 支持eSIM 采用微泵液

    9月24日,cnmo获悉,有数码博主透露了华为mate80 pro+的部分配置详情。据消息显示,该机型顶配版将配备6000mah大容量电池,搭配24gb运行内存与2tb机身存储。此外,华为mate80系列将新增一款air版本,采用创新的微泵液冷技术(无风扇设计),并支持esim功能。 华为Mate …

    2026年9月27日 • 用户投稿
    100

发表回复

登录后才能评论
关注微信