共计 12037 个字符,预计需要花费 31 分钟才能阅读完成。
前言
工欲善其事,必先利其器 –《论语》
在现在被三大框架摆布的前端畛域,曾经很少有人不晓得 Vue 了。2014 年,前 Google 工程师尤雨溪公布了所谓的渐进式(Progressive)前端利用框架 Vue,其简化的模版绑定和组件化思维给过后还是 jQuery 时代的前端畛域产生了踊跃而深远的影响。Vue 的诞生,造福了那些不习惯 TS 或 JSX 语法的前端开发者。而且,Vue 较低的学习门槛,也让初学者非常容易上手。这也是为什么 Vue 能在短时间内迅速推广的重要起因。从 State of JS 的考察中能够看到,Vue 的知名度靠近 100%,而且整体用户满意度也比拟高。
Vue 既弱小又易学,这是不是意味着 Vue 是一个完满框架呢?很遗憾,答案是否定的。尽管 Vue 的上手门槛不高,灵便易用,然而这种劣势同时也成为了一把双刃剑,为构建大型项目带来了肯定的局限性。很多用 Vue 2 开发过大型项目的前端工程师对 Vue 是又爱又恨。不过,随着 Vue 3 的公布,这些开发大型项目时凸显进去的劣势失去了无效解决,这让 Vue 框架变得十分全能,真正具备了跟 “ 前端框架一哥 ” React 一争高下的后劲。Vue 3 到底带来了什么重要的新个性呢?本篇文章将对此进行具体介绍。
Vue 概览
Vue 是前 Google 工程师尤雨溪于 2013 年开发、2014 年公布的前端框架。对于 Vue 的具体定义,这里摘抄 Vue 官网里的介绍。
Vue (读音 /vjuː/,相似于 view) 是一套用于构建用户界面的 渐进式框架。与其它大型框架不同的是,Vue 被设计为能够自底向上逐层利用。Vue 的外围库只关注视图层,不仅易于上手,还便于与第三方库或既有我的项目整合。另一方面,当与现代化的工具链以及各种反对类库联合应用时,Vue 也齐全可能为简单的单页利用提供驱动。
渐进式框架
很多人可能不了解渐进式框架(Progressive Framework)的含意。这里简略解释一下。渐进 次要是针对我的项目开发过程来说的。传统的软件我的项目开发通常是瀑布流式(Waterfall)的,也就是说,软件设计开发工作通常有明确的工夫线,工作与工作之间有明确的依赖关系,这意味着我的项目的不确定性容忍度(Intolerance to Uncertainty)比拟低。这种开发模式在古代日趋简单而疾速变动的商业情景曾经显得比拟过期了,因为很多时候需要是不确定的,这会给我的项目带来很大的危险。
而渐进式框架或渐进式开发模式则能够解决这种问题。以 Vue 为例:我的项目开始时,性能要求简略,能够用一些比较简单的 API;当我的项目逐步开发,一些公共组件须要形象进去,因而用到了 Vue 的组件化性能;当我的项目变得十分大的时候,能够援用 Vue Router 或者 Vuex 等模块来进一步工程化前端零碎。看到了么,这样一来,开发流程变得十分麻利,不必提前设计整个零碎,只用按需开发,因而能够疾速开发产品原型以及扩大到生产零碎。
框架个性
Vue 是利用模版语法来渲染页面的,这也称做 申明式渲染。Vue 好上手的重要起因也是因为这个,因为它合乎了前端开发者的习惯。例如上面这个例子。
<div id="app">
{{message}}
</div>
<script>
var app = new Vue({
el: '#app',
data: {message: 'Hello Vue!'}
})
</script>
能够看到,el
指定 Vue 实例绑定的元素,data
中的 message
与 DOM 元素的内容进行绑定。只须要操控 JS 中的数据,HTML 内容也会随之扭转。
另外,Vue 将 HTML、CSS、JS 全副整合在同一个文件 .vue
中,以 组件化利用构建 的形式来组织代码,从语法个性上激励 “ 高内聚、低耦合 ” 的设计理念,让代码组织变得更加正当,晋升了可读性与逻辑性。上面是一个官方网站给出的根底 .vue
文件例子。
<template>
<p>{{greeting}} World!</p>
</template>
<script>
module.exports = {data: function () {
return {greeting: 'Hello'}
}
}
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
组件的骨架(HTML)、款式(CSS)和数据或操作(JS)都在同一个中央,开发者须要思考如何将整个零碎拆分成更小的子模块,或者组件。这对于构建大型项目是十分有帮忙的。
其实,除了上述两个特点,Vue 还有很多其余的实用个性,但限于篇幅的起因,咱们这里不具体解释了。感兴趣的读者能够去[官方网站深刻理解。
框架毛病
没有什么货色是完满的,Vue 同样如此。当 Vue 的知名度和用户量一直减少时,一些前端开发者开始埋怨 Vue 的灵活性太高导致构建大型项目时短少束缚,从而容易产生大量 bug。甚至应用 Vue 生态圈里的状态管理系统 Vuex 也无奈无效解决。对于 Vue 是否适宜大型项目的问题,网上有不少争执,甚至尤大自己都亲自上知乎参加了探讨(吃瓜传送门)。
主观来讲,Vue 尽管具备较低的上手门槛,但这并不意味着 Vue 不适宜开发大型项目。然而,咱们也必须抵赖大型项目通常要求较高的稳定性和可维护性,而 Vue 框架较高的灵活性以及短少足够的束缚让其容易被经验不足的前端开发者所滥用,从而产生臭不可闻的、难以直视的 “ 屎山 ” 代码。其实,代码可维护性并不强制要求较低的灵活性与自由度,只是这种自在可能会对我的项目的整体稳固带来危险。
Vue 作者尤雨溪其实很早就留神到这个问题,因而才会打算从底层重构 Vue,让其更好的反对 TypeScript。这就是 2020 年 9 月公布的 Vue 3。
Vue 3 新个性
Vue 3 有很多实用的新个性,包含 TS 反对、组合式 API 以及 Teleport 等等。本文不是对于 Vue 3 的参考文,因而不会介绍其中全副的新个性,咱们只会关注其中比拟重要的个性,尤其是能增强代码束缚的 TypeScript(简称 TS)。
TS 反对
技术上来说,TS 反对并不是 Vue 3 的新个性,因为 Vue 2 版本就曾经可能反对 TS 了。但 Vue 2 版本的 TS 反对,是通过 vue-class-component 这种糟糕的装璜器形式来实现的。笔者对 “ 糟糕 ” 这个评估深有体会,因为笔者已经迁徙过 Vue 2 版本的生产环境我的项目,最初发现收益并不高:语法有很大的不同,花了大量工夫来重构,发现只晋升了一些代码的规范性,然而代码整体变得更臃肿了,可读性变得更差。
而在 Vue 3 中,TS 是原生反对的,因为 Vue 3 自身就是用 TS 编写的,TS 成为了 Vue 3 中的 “ 一等公民 ”。TS 反对在我看来是 Vue 3 中最重要的个性,特地是对构建大型前端我的项目来说。为什么说它重要?因为 TS 无效的解决了前端工程化和规模化的问题,它在代码标准和设计模式上极大的进步代码品质,进而加强零碎的可靠性、稳定性和可维护性。对于 TS 的重要性,笔者在该公众号前一篇文章《为什么说 TypeScript 是开发大型前端我的项目的必备语言》曾经做了具体介绍,感兴趣的读者能够持续深刻浏览一下。
Vue 3 定义了很多 TS 接口(Interface)和类型(Type),帮忙开发者定义和束缚各个变量、办法、类的品种。上面就是一个十分根底的例子。
import {defineComponent} from 'vue'
// 定义 Book 接口
interface Book {
title: string
author: string
year: number
}
// defineComponent 定义组件类型
const Component = defineComponent({data() {
return {
book: {
title: 'Vue 3 Guide',
author: 'Vue Team',
year: 2020
} as Book // as Book 是一个断言
}
}
})
上述代码通过 defineComponent
定义了组件类型,而在 data
里定义了外部变量 book
,这个是通过接口 Book
来定义的。因而,其余组件在援用该组件时,就可能主动推断出该组件的类型、外部变量类型,等等。如果援用方与被援用方的任何一个接口、变量类型不统一,TS 就会抛错,让你能够提前躲避很多谬误。
尽管 Vue 3 在传统定义 Vue 实例形式中(Options API)可能很好的反对 TS,然而咱们更举荐用 TS 配合另一种新的形式来定义 Vue 实例,也就是接下来要介绍的组合式 API(Compositional API)。
组合式 API
组合式 API 的诞生是来自于大型项目中无奈优雅而无效地复用大量组件的问题。如果你曾经理解 Vue,你或者应该晓得之前版本的 Vue 实例中蕴含很多固定的 API,包含 data
、computed
、methods
等。这种定义形式有个比较突出的问题:它将 Vue 实例中的性能依照类型的不同别离固定在不同的 API 中,而没有依据理论的性能来划分,这将导致一个简单组件中的代码变得十分散乱,就像如下这张图一样。
在这个 “ 迷信怪人 ” 式的传统组件中,同一种色彩的代码负责同一种性能,但它们却依据不同类型扩散在不同的区域,这将导致首次接触该组件的开发人员难以疾速了解整个组件的性能和逻辑。而组合式 API 则容许开发者将组件中相干的性能和变量聚合在一个中央,在内部按需援用,从而防止了传统形式的逻辑散乱问题。
在 Vue 3 的组合式 API 中,所有性能和逻辑只须要定义在 setup
这个办法中。setup
承受属性 props
和上下文 context
两个参数,并在办法外部定义所须要的变量和办法,返回值是蕴含公共变量和办法的对象,它们能够供其余组件和模块应用。传统 Vue 实例的大部分 API,例如 data
、computed
、methods
等,都能够在 setup
中定义。上面是官网对于组合式 API 的例子。
// src/components/UserRepositories.vue
import {toRefs} from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'
export default {
// 援用子组件
components: {RepositoriesFilters, RepositoriesSortBy, RepositoriesList},
// 属性
props: {user: { type: String}
},
setup(props) {
// 解构属性,如果间接在 setup 中援用,必须要加 toRefs
const {user} = toRefs(props)
// 获取 repository 相干公共办法,在其余模块中定义
const {repositories, getUserRepositories} = useUserRepositories(user)
// 搜寻 repository 相干公共办法,在其余模块中定义
const {
searchQuery,
repositoriesMatchingSearchQuery
} = useRepositoryNameSearch(repositories)
// 过滤 repository 相干公共办法,在其余模块中定义
const {
filters,
updateFilters,
filteredRepositories
} = useRepositoryFilters(repositoriesMatchingSearchQuery)
return {
// 因为咱们并不关怀未经过滤的仓库
// 咱们能够在 `repositories` 名称下裸露过滤后的后果
repositories: filteredRepositories,
getUserRepositories,
searchQuery,
filters,
updateFilters
}
}
}
在这个例子中,该组件须要的变量或办法全副在其余模块定义了,并通过 useXXX
的函数裸露给内部组件,而且还能够被其余组件重复使用。这样看上去是不是更清新了呢?
你可能会思考怎么写 useXXX
这种函数。其实非常简单,上面就是一个例子。
// src/composables/useUserRepositories.js
import {fetchUserRepositories} from '@/api/repositories'
import {ref, onMounted, watch} from 'vue'
export default function useUserRepositories(user) {
// 外部列表变量
const repositories = ref([])
// 获取列表办法
const getUserRepositories = async () => {repositories.value = await fetchUserRepositories(user.value)
}
// 首次获取列表,挂载后执行,相当于传统组件中的 mounted
onMounted(getUserRepositories)
// 监听 user 并依据变动来获取最新列表,相当于传统组件中的 watch
watch(user, getUserRepositories)
// 返回公共变量和办法
return {
repositories,
getUserRepositories
}
}
传统组件中的一些 API,例如 mounted
和 watch
,曾经成为了按需援用的函数,性能跟之前截然不同。而之前的 data
、computed
、methods
变成 setup
函数中的外部变量,并依据是否返回来决定是否裸露给内部。
须要留神的是,Vue 3 中引入了响应式 API 的概念,之前的变量都须要依据须要用不同的响应式 API 来定义。其具体原理不深刻介绍了,感兴趣的读者能够到官网文档持续深刻学习。
其余新个性
Vue 3 还有其余一些新个性,限于篇幅起因就不具体介绍了。这里只列出一些比拟实用的新个性及其简略介绍。
- Teleport – 实用于 Modal、Popover 等须要挂载在全局 DOM 元素中的组件
- 片段 – 组件反对多个根节点
- 触发组件选项 – 对于事件的相干 API 变更
全副变更列表,请参考官网文档(英文)。
大型项目实战
后面介绍了这么多理论知识,对于前端工程师来说可能还不够,要在工作中让所学常识发挥作用,还必须要用到我的项目实际中,特地是大型项目。因而,这个大节将着重介绍如何用 Vue 3 来构建企业级我的项目。本大节将用笔者的一个 Github 仓库 作为演示,解说如何用 Vue 3 构建大型前端我的项目。
这个仓库是笔者的一个开源我的项目 Crawlab 的下一个版本 v0.6 的前端局部。它目前还处于开发中的状态,并不是成品;不过代码组织构造曾经成型,作为演示来说曾经足够。之前的版本是用 Vue 2 写的,用的是传统 Vue API。这个 Vue 3 版本将应用 TS 和组合式 API 来实现重构和迁徙,而后在此基础上退出更多实用的性能。对该前端我的项目感兴趣的读者能够拜访该 Github 仓库理解代码细节,同时也十分欢送大家跟我探讨任何相干问题,包含不合理或须要优化的中央。
仓库地址: https://github.com/crawlab-team/crawlab-frontend
我的项目构造
该项目标代码组织构造如下。其中疏忽了一些不重要的文件或目录。
.
├── public // 公共资源
├── src // 源代码目录
│ ├── assets // 动态资源
│ ├── components // 组件
│ ├── constants // 常量
│ ├── i18n // 国际化
│ ├── interfaces // TS 类型申明
│ ├── layouts // 布局
│ ├── router // 路由
│ ├── services // 服务
│ ├── store // 状态治理
│ ├── styles // CSS/SCSS 款式
│ ├── test // 测试
│ ├── utils // 辅助办法
│ ├── views // 页面
│ ├── App.vue // 主利用
│ ├── main.ts // 主入口
│ └── shims-vue.d.ts // 兼容 Vue 申明文件
├── .eslintrc.js // ESLint 配置文件
├── .eslintignore // ESLint Ignore 文件
├── babel.config.js // Babel 编译配置文件
├── jest.config.ts // 单元测试配置文件
├── package.json // 我的项目配置文件
└── tsconfig.json // TS 配置文件
能够看到,这个前端我的项目有十分多的子模块,包含组件、布局、状态治理等等。在 src
目录中有十多个子目录,也就是十多个模块,这还不包含各个模块下的子目录,因而模块十分多,构造也非常复杂。这是一个典型的大型前端我的项目的我的项目构造。企业级我的项目,例如 ERP、CRM、ITSM 或其余后盾管理系统,大部分都有很多功能模块以及清晰的我的项目构造。这些模块各司其职,相互协作,独特形成了整个前端利用。
其实这种我的项目构造并不只实用于 Vue,其余框架的我的项目例如 React、Angular 都能够是相似的。
TS 类型申明
TS 简直是古代大型前端我的项目的标配,其弱小的类型零碎能够躲避大型项目中很多常见的谬误和危险。对于 TS 为何实用于大型项目,笔者在前一篇文章中曾经做了具体论述。因而,咱们在这个前端我的项目中也采纳了 TS 来做类型零碎。
在后面的我的项目构造中,咱们在 src/interfaces
目录中申明 TS 类型。类型申明文件用 <name>.d.ts
来示意,name
示意是跟这个模块相干的类型申明。例如,在 src/interfaces/layout/TabsView.d.ts
这个文件中,咱们定义了跟 TabsView
这个布局组件相干的类型,内容如下。
interface Tab {
id?: number;
path: string;
dragging?: boolean;
}
更简单的例子是状态治理的类型申明文件,例如 src/interfaces/store/spider.d.ts
,这是 Vue 中状态治理库 Vuex 的其中一个模块申明文件,内容如下。
// 引入第三方类型
import {GetterTree, Module, MutationTree} from 'vuex';
// 如果引入了第三方类型,须要显式做全局申明
declare global {
// 继承 Vuex 的根底类型 Module
interface SpiderStoreModule extends Module<SpiderStoreState, RootStoreState> {
getters: SpiderStoreGetters;
mutations: SpiderStoreMutations;
}
// 状态类型
// NavItem 为自定义类型
interface SpiderStoreState {
sidebarCollapsed: boolean;
actionsCollapsed: boolean;
tabs: NavItem[];}
// Getters
// StoreGetter 为自定义根底类型
interface SpiderStoreGetters extends GetterTree<SpiderStoreState, RootStoreState> {tabName: StoreGetter<SpiderStoreState, RootStoreState, SpiderTabName>;}
// Mutations
// StoreMutation 为自定义根底类型
interface SpiderStoreMutations extends MutationTree<SpiderStoreState> {
setSidebarCollapsed: StoreMutation<SpiderStoreState, boolean>;
setActionsCollapsed: StoreMutation<SpiderStoreState, boolean>;
}
}
其中,尖括号 <...>
里的内容是 TS 中的泛型,这能大幅度提高类型的通用性,通常用作根底类型。
上面是援用 TS 类型的例子 src/store/modules/spider.ts
。
import router from '@/router';
export default {
namespaced: true,
state: {
sidebarCollapsed: false,
actionsCollapsed: false,
tabs: [{id: 'overview', title: 'Overview'},
{id: 'files', title: 'Files'},
{id: 'tasks', title: 'Tasks'},
{id: 'settings', title: 'Settings'},
],
},
getters: {tabName: () => {const arr = router.currentRoute.value.path.split('/');
if (arr.length < 3) return null;
return arr[3];
}
},
mutations: {setSidebarCollapsed: (state: SpiderStoreState, value: boolean) => {state.sidebarCollapsed = value;},
setActionsCollapsed: (state: SpiderStoreState, value: boolean) => {state.actionsCollapsed = value;},
},
actions: {}} as SpiderStoreModule;
这里用了 as SpiderStoreModule
的断言,TS 动态检测器会主动将 SpiderStoreModule
中的元素推断进去,并与理论的变量做比对。如果呈现了不统一,就会抛错。
组件化
组件化是古代前端我的项目的支流,在 Vue 3 中也不例外。Vue 3 的组件化跟 Vue 2 比拟相似,都是用 Vue 实例来定义各类组件。在这个前端我的项目中,组件被分类成了不同品种,同一品种的放在一个文件夹中,如下。
.
└── src
└── components
├── button // 按钮
├── context-menu // 右键菜单
├── drag // 拖拽
├── file // 文件
├── icon // Icon
├── nav // 导航
├── table // 表格
└── ...
组件文件为 <ComponentName>.vue
定义,如下是其中一个对于右键菜单的例子 src/components/context-menu/ContextMenu.vue
。
<template>
<el-popover
:placement="placement"
:show-arrow="false"
:visible="visible"
popper-class="context-menu"
trigger="manual"
>
<template #default>
<slot name="default"></slot>
</template>
<template #reference>
<div v-click-outside="onClickOutside">
<slot name="reference"></slot>
</div>
</template>
</el-popover>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import {ClickOutside} from 'element-plus/lib/directives';
// 定义属性
export const contextMenuDefaultProps = {
visible: {
type: Boolean,
default: false,
},
placement: {
type: String,
default: 'right-start',
},
};
// 定义触发事件
export const contextMenuDefaultEmits = ['hide',];
// 定义组件
export default defineComponent({
// 组件名称
name: 'ContextMenu',
// 援用内部指令
directives: {ClickOutside,},
// 触发事件
emits: contextMenuDefaultEmits,
// 属性
props: contextMenuDefaultProps,
// 组合式 API
setup(props, {emit}) {
// 点击事件函数
const onClickOutside = () => {emit('hide');
};
// 返回公共对象
return {onClickOutside,};
},
});
</script>
你可能会有疑虑:这里仿佛没用到 TS 中的类型零碎啊。其实这只是一个非常简单的组件,蕴含残缺 TS 个性的组件例子能够参考上面这个组件。
src/file/FileEditor.vue
: https://github.com/crawlab-team/crawlab-frontend/blob/main/src/components/file/FileEditor.vue
其余
限于篇幅起因,本文不会具体介绍其余所有模块。这里只简略列举一下。
- UI 框架(UI Framework)- 用了 Element+ 作为 UI 框架
- 布局(Layouts)- 根底布局
BasicLayout
定义了顶部、侧边栏、底部等元素 - 状态治理(State Management)- 相当于全局数据管理系统
- 路由(Routing)- 页面路由配置
- 国际化(Internationalization)- 多语言配置
- 款式(Styles)- 利用 SCSS 定义了全局款式以及款式变量等
- 服务(Services)- 包含与后端 API 的交互函数
- 常量(Constants)
- 辅助办法(Utilities)
如何学习 Vue 3
对于 Vue 3 的学习路径,其实首先应该是 浏览官网文档,理解 Vue 3 的根底概念、高阶原理以及如何工程化等等。作者尤雨溪曾经在文档中十分具体的介绍了对于 Vue 的各个方面,图文并茂、深入浅出的解说了对于 Vue 3 的概念和常识。总之 Vue 3 的文档对于初学者来说十分敌对。如果你对英文比拟相熟,举荐间接浏览英文官网文档,其中内容个别是最新的。
除开浏览官网文档以外,笔者还举荐 浏览优良的 Vue 3 开源我的项目,例如 Element+、Ant Design Vue、Vue-Admin-Beautiful,Github 上有很多优良的 Vue 3 我的项目,浏览它们的源码能够帮忙你相熟如何应用 Vue 3,以及构建大型项目的代码组织形式。
当然,本人入手 用 Vue 3 实际一个前端我的项目 可能帮忙你深刻了解 Vue 3 的原理和开发方式,特地是将 Vue 3 的新个性用在工作我的项目中。笔者在理解了 Vue 3 的根底语法和新个性之后,将所学常识使用在了本人的开源我的项目中,边学边做,就十分疾速的把握了 Vue 3 的外围常识。
总结
这篇文章次要介绍了 Vue 3 在大型前端我的项目中的劣势,尤其是新个性 TS 反对和组合式 API,可能大幅加强代码的可读性和可维护性。这让自身就上手容易的 Vue 框架变得锦上添花,使其可能胜任大型前端我的项目的设计和开发。对 TS 的原生反对,可能让 Vue 3 的我的项目代码可能具备良好的可预测性。而组合式 API,可能将散乱的代码逻辑变得更有秩序。这些都有助于加强 Vue 3 前端我的项目的健壮性,从而让前端人员更容易编写出稳固而可保护的代码。另外,本文还通过笔者的一个开发中的前端我的项目(Crawlab Frontend),来演示如何利用 Vue 3 开发企业级前端我的项目,并展现了相干的我的项目构造、TS 类型申明以及组件化,等等。
比拟资深的前端工程师可能会对 Vue 3 的新个性等闲视之,因为所谓的 TS 反对和组合式 API 都在其余出名框架以其余名字被率先引入,例如 React 的 React Hooks,Vue 3 仿佛只是借鉴了过来。然而,这种观点十分不可取。在技术背后,任何计划都没有高低贵贱,只有合不适合。就像相亲一样,只有适合的,才是最好的。尤雨溪也抵赖,AngularJS 和 React 都有很多优良的技术,Vue 也借鉴了一部分。但你绝不能因而而宣判它是剽窃。就像 C# 跟 Java 语法和个性相似,但你必定无奈证实 C# 是剽窃的 Java(其实 C# 相较于 Java 有很多优良个性,例如隐式类型推断,这也是笔者比拟喜爱 C# 的起因之一)。Vue 的胜利,相对不是必然性的,它的易用性和绝对丰盛的文档资源,让初学者可能疾速上手,这对于前端开发者来说是福音。咱们做技术的应该对新技术抱有包容心,可能辩证而感性的对待问题,这样才不致于变得偏激从而走火入魔。
参考
- Vue 3 官网文档: https://www.vue3js.cn/docs/zh/
- TypeScript 官网文档: https://www.typescriptlang.or…
- Crawlab: https://github.com/crawlab-te…
- Crawlab Frontend: https://github.com/crawlab-te…
- 《为什么说 TypeScript 是开发大型前端我的项目的必备语言》
社区
如果您对笔者的文章感兴趣,能够加笔者微信 tikazyq1 并注明 “ 码之道 ”,笔者会将你拉入 “ 码之道 ” 交换群。