Effective TypeScript:精进TypeScript代码的83个实践方法(第二版)
| 运费: | ¥ 5.00-22.00 |
| 库存: | 300 件 |
商品详情
书名:Effective TypeScript:精进TypeScript代码的83个实践方法(第二版)
书号:978-7-5239-0493-0
定价:148元
作者:[美]丹范德卡姆(Dan Vanderkam)
出版时间:2025-11-20
出版社:中国电力出版社
页码: 500 字数(千字):578
开本:16开 版次:1 印次:1
品牌介绍
中国电力出版社成立于 1951 年,作为中国成立最早的中央科技出版社之一,曾隶属于水利电力部、能源部、电力工业部、国家电力公司,现为国家电网公司所属的科技出版社,在电气技术专业出版领域享有极高的声誉。该社作为以图书出版为主体,音像、电子出版物、期刊、网络出版共同发展的大型出版企业,以强大的出版资源和高素质的专业队伍,致力于向读者提供包括电力工程、电气工程、建筑工程、电子技术、信息技术、外语、大中专教材、家教等学科门类齐全的权威出版物,也竭力为广大师生提供精品教材,是教育部和北京市教委规划教材的出版基地之一。
编辑推荐
如果你已经有一定的JavaScript 和TypeScript 实践经验,那么你无疑能从本书中获得最大的收益。本书的目标不是教你TypeScript 或JavaScript,而是帮助你从初学者或中级用户成长为专家。 编辑推荐 TypeScript作为JavaScript的类型化超集,以其独特禀赋化解了JavaScript诸多为人诟病的痼疾。然而,其学习门槛颇为陡峭,唯有经年累月的实践方能洞悉个中奥妙。本书承袭《Effective C++》与《Effective Java》的经典范式,以务实风格呈现了83条精要准则,于进退取舍间勾勒出驾驭这门语言的智慧图谱。 本书作者通过详尽的实例演示,让每一条建议都蕴含着实战的气息。无论你是初窥门径的新手,还是已有根基的中阶开发人员,本书都将引领你突破TypeScript的认知边界,蜕变为深谙其道的语言大师。 新版内容特别针对TypeScript 5进行了重构,增补了类型级编程和TypeScript指南两大篇章。 专家推荐 “这本书探讨了在使用TypeScript时最常见的问题,并提供了实用且以结果为导向的建议。无论你是TypeScript的新手,还是有经验的开发者,这本书都能为你带来新的收获。” ——Ryan Cavanaugh, 微软TypeScript工程主管
产品特色
本书探讨了我们在使用TypeScript时最常见的问题,并提供了实用的、注重结果的建议。无论你的TypeScript经验水平如何,你都可以从这本书中学到一些东西。
作者介绍
Dan Vanderkam,独立软件工程师,TypeScript领域深耕多年的实践者、布道者与生态共建者。他历任Sidewalk Labs首席软件工程师、谷歌首席软件工程师,工作成果服务了全球数十亿的搜索用户。
内容介绍
本书的主要内容有:解析TypeScript类型系统的运作机理,领会核心精髓。善用类型推断,以最简注解构筑周全的类型防护壁垒。精研类型设计之道,铸就安全性与可读性兼具的代码基底。活用泛型与类型级编程,建模复杂接口模型。解析依赖关系与声明文件,驾驭模块化工程的隐秘脉络。制定JavaScript向TypeScript的渐进迁移方略,实现工程的平稳演进。
本书适用于如果你已经有一定的JavaScript和TypeScript实践经验,那么你无疑能从本书中获得最大的收益。本书的目标不是教你TypeScript或JavaScript,而是帮助你从初学者或中级用户成长为专家。
前言
前言 难以置信,本书的第一版出版至今已经将近五年了。这本书及其配套网站(https://effectivetypescript.com)都受到了广泛好评,并帮助了无数开发者加深对TypeScript 的理解,提高他们的实际应用能力。 令我惊讶的是,仅仅六个月后,就有人问我这本书是否已经过时了!考虑到第一版出版前几年TypeScript 的迅猛发展,当时我的确也在担忧。因此,我尽量避免在书中加入可能很快会过时的内容,更关注于那些具有长期价值的主题,比如语言基础和程序设计,而不是具体的库或框架。从整体来看,第一版的大部分内容至今仍然适用。 随着TypeScript 的发展和新功能的加入,尽管第一版的内容并没有被淘汰,但在覆盖范围上显得有所不足。要撰写一本关于“Effective”的书籍,仅仅知道某个功能如何运作是不够的,还需要有丰富的实践经验,才能判断哪些模式行之有效,哪些模式无法长期适用。例如,在2019 年,条件类型(conditional types)才刚刚被引入,因此我当时对它们的应用经验有限。而在这一版中,这部分内容得到了更深入的探讨。此外,模板字面量类型(template literal types)是过去几年里TypeScript 增加的最重要的功能之一,它极大地拓展了类型系统的可能性,这在条款54 中得到了详细介绍。 此外,得益于“Type Challenges”(https://tsch.js.org)等项目,TypeScript开发者在类型系统中的探索变得更加大胆。泛型(generics)和类型级编程(type-level programming)在第一版中仅有轻描淡写地提及,在这一版中,它们拥有了完整的一章(即第6 章)。 自从我第一次尝试TypeScript 以来,已经过去了八年多的时间。我依然乐在其中,每当阅读最新的版本说明,或看到Anders Hejlsberg 提出的某个大胆的PR被广泛讨论时,都会感到兴奋。同时,我也依然热衷于帮助其他开发者学习TypeScript,提升他们的使用能力。我希望这些热情能够在本书中传达给你,也希望本书能让你我一起享受在TypeScript 里编程的乐趣! Wallkill, NY 2024 年3 月 本书的读者对象 Effective 系列书籍的目标是成为该领域的“第二标准书籍”。如果你已经有一定的JavaScript 和TypeScript 实践经验,那么你无疑能从本书中获得最大的收益。本书的目标不是教你TypeScript 或JavaScript,而是帮助你从初学者或中级用户成长为专家。书中的各条将帮助你建立对TypeScript 及其生态系统的认知模型,让你认识到需要规避的陷阱,并指导你如何以最有效的方式使用TypeScript 的众多功能。 一本参考书可能会告诉你某个功能的五种实现方式,而一本Effective 系列的书籍会告诉你应该选择哪一种,并解释其中的原因。 近年来,TypeScript 发展迅速,但我希望它已经足够稳定,好让本书的内容在未来几年内保持有效。本书主要关注TypeScript 语言本身,而不是特定的框架或构建工具。因此,你不会在本书中找到如何在TypeScript 中使用React 或Vue,或如何配置TypeScript 以配合webpack 或Vite。本书的技巧适用于所有TypeScript 用户。 写作目的 当我刚开始在Google 工作时,我收到了一本Scott Meyers 的《Effective C++》(Addison-Wesley Professional)。这本书与我读过的其他编程书籍完全不同。它并没有试图迎合初学者,也不是对C++ 语言的完整指南。它没有单纯介绍C++ 的各种特性,而是告诉你应该如何(以及不应该如何)使用它们。 书中的内容由几十个简短且具体的条款组成,每条都基于实际案例进行讲解。当我在日常工作中使用C++ 的同时阅读这些示例时,它对我的影响是显而易见的。我之前虽然使用过C++,但这是我第一次真正感知到对这门语言的熟悉,并且知道如何权衡它所提供的各种选择。 后来,我在阅读Joshua Bloch 的《Effective Java》和David Herman 的《Effective JavaScript》(均由Addison-Wesley Professional 出版)时,也有类似的体验。如果你已经熟悉几种不同的编程语言,那么直接深入了解一门新语言的独特之处,必将是一种挑战思维模式并加深理解的有效方式。在撰写本书的过程中,我对TypeScript 有了极大的收获。我希望你在阅读本书时,也能获得同样的体验! 本书结构 本书由多个“条款”组成,每条都是一篇简短的技术文章,围绕TypeScript 的某个方面提供具体的建议。这些条款按主题划分到不同的章节中,但你可以自由跳跃阅读,选择自己感兴趣的部分。 每个条款的标题都概括了核心要点。这些都是你在使用TypeScript 时应当牢记的知识点,因此值得快速浏览一遍目录,让它们在你的脑海中形成印象。例如,如果你在编写注释文件时,纠结要不要写类型信息,那么你就应该阅读条款31:“类型勿繁,注释需简”。 条款的正文部分会围绕标题中的建议展开,通过具体示例和技术论证来支持其观点。本书几乎每个观点都会通过示例代码加以说明。我自己在阅读技术书籍时,往往会先看代码示例,再快速浏览文字内容,我猜你可能也有类似的习惯。当然,我希望你能阅读完整的文本和解释,即便只是浏览示例代码,也能掌握要点。 阅读完一个条款后,你应该能够理解它如何帮助你更有效地使用TypeScript。同时,你也需要判断它是否适用于你的具体情况。Scott Meyers(《Effective C++》的作者)曾分享过一个有趣的例子:他遇到过一组负责开发导弹软件的工程师,知道自己可以忽略他关于“防止资源泄漏”的建议,因为他们的软件在导弹击中目标、硬件被摧毁时就会终止运行。目前我还没听说哪款导弹运行JavaScript,但詹姆斯韦布空间望远镜(James Webb Space Telescope)确实搭载了一个JavaScript 运行时,所以未来的可能性谁又能说得准呢? 最后,每个条款都会以“小结”结尾。这部分以要点列表的形式总结该条款的内容。如果你只想要大略浏览,可以先阅读要点来了解条款的核心思想,并决定是否深入阅读。当然,完整阅读条款仍然是最佳选择!但如果时间紧迫,这份摘要也可以作为临时的参考。 TypeScript 示例的表示法 本书中的所有代码示例默认都是TypeScript,除非从上下文可以明显看出是JSON、HTML 或其他语言。在使用TypeScript 的过程中,编辑器的交互体验至关重要,而这一点在印刷版书籍中较难呈现。为了弥补这一不足,我更改了一些文字符号。 大多数编辑器会使用波浪下划线来标示错误,并允许用户将鼠标悬停在标记处查看完整的错误信息。为了在代码示例中清晰地表示错误,我在错误位置的下一行使用注释加上波浪线的形式来标明错误: let str = 'not a number'; let num: number = str; // ~~~ Type 'string' is not assignable to type 'number' 我偶尔会对错误消息进行编辑,以使其更清晰和简洁,但绝不会删除任何错误。如果你将书中的代码示例复制粘贴到编辑器中,你应该会看到与书中标示完全一致的错误,不多不少。 为了强调代码没有错误的情况,我使用// OK 注释: let str = 'not a number'; let num: number = str as any; // OK 你可以在编辑器中悬停在符号上,以查看TypeScript 推断的该符号类型。为了在文本中表示这一点,我使用了Twoslash 语法(^?)的注释: let v = {str: 'hello', num: 42}; // ^? let v: { str: string; num: number; } 该注释表示如果你在编辑器中将鼠标悬停在插入符号(^)上方的字段上,你会看到的类型信息。这与TypeScript Playground 中的表示一致。如果你将代码示例复制到TypeScript Playground 并删除^? 之后的内容,TypeScript 将自动填充相应的类型信息。你在Playground 中看到的内容(见图1)应与书中的内容完全匹配。 我会偶尔引入无操作(no-op)语句,以明确变量在特定代码行上的类型信息: function foo(value: string | string[]) { if (Array.isArray(value)) { value; // ^? (parameter) value: string[] } else { value; // ^? (parameter) value: string } } 只有value; 的那两行仅用于展示在条件语句的不同分支中变量的类型。你不需要(也不应该)在自己的代码中加入这样的语句。 除非特别说明或上下文明确,否则书中的代码示例默认使用--strict 标志进行类型检查。虽然书籍的印刷内容是固定的,但TypeScript 仍在不断发展,因此某些代码示例的类型或错误信息在未来可能会有所不同。你可以访问Effective TypeScript 的GitHub 仓库(https://github.com/danvk/effective-typescript)获取示例代码的最新版本。本书的所有代码示例均使用literate-ts(https://oreil.ly/LFR0l)工具进行验证,并基于TypeScript 5.4 进行测试。 排版约定 本书采用以下排版约定。 斜体(Italic) 表示新术语、URL、电子邮件地址、文件名和文件扩展名。 等宽字体(Constant width) 表示程序清单,在段落内表示程序元素,例如变量、函数名称、数据库、数据类型、环境变量、语句和关键字。 粗体等宽字体(Constant width bold) 表示应由用户原封不动输入的命令或其他文本。 斜体等宽字体(Constant width italic) 表示应该替换成用户提供值的文本,或者由上下文决定的值。 代码示例 本书的补充材料(代码示例、练习等)可在https://github.com/danvk/effectivetypescript中下载。 本书是要帮你完成工作的。一般来说,如果本书提供了示例代码,你可以把它用在你的程序或文档中。除非你使用了很大一部分代码,否则无需联系我们获得许可。比如,用本书的几个代码片段写一个程序就无需获得许可,销售或分发O’Reilly 图书的示例集则需要获得许可;引用本书中的示例代码回答问题无需获得许可,将书中大量的代码放到你的产品文档中则需要获得许可。 我们很希望但并不强制要求你在引用本书内容时加上引用说明。引用说明一般包括书名、作者、出版社和ISBN,例如:“Effective TypeScript, 2nd ed., by DanVanderkam (O’Reilly). Copyright 2024 Dan Vanderkam, 978-1-492-05374-3”。 如果你认为自己对示例代码的使用超出了上述许可范围,请联系permissions@oreilly.com。 O’Reilly 在线学习平台(O’Reilly Online Learning) 近40 年来,O’Reilly Media 致力于提供技术和商业培训、知识和卓越见解,来帮助众多公司取得成功。 公司独有的专家和改革创新者网络通过O’Reilly 书籍、文章以及在线学习平台,分享他们的专业知识和实践经验。O’Reilly 在线学习平台按照您的需要提供实时培训课程、深入学习渠道、交互式编程环境以及来自O’Reilly 和其他200 多家出版商的大量书籍与视频资料。更多信息,请访问网站:https://www.oreilly.com/。 联系我们 任何有关本书的意见或疑问,请按照以下地址联系出版社。 美国: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 中国: 北京市西城区西直门南大街2 号成铭大厦C 座807 室(100035) 奥莱利技术咨询(北京)有限公司 我们为本书创建了一个网页,列出了勘误、示例及其他相关信息。你可以访问https://oreil.ly/effective-typescript-2e 查看详情。 对本书中文版有任何建议可以发电子邮件到errata@oreilly.com.cn。 欲了解本社图书和课程的新闻和信息,请访问我们的网站http://oreilly.com。 我们的LinkedIn:https://linkedin.com/company/oreilly-media。 我们的YouTube:http://youtube.com/oreillymedia。 致谢 尽管我曾抱有希望,但撰写第二版并未比第一版更轻松,所耗费的时间也并未减少。在这一过程中,本书的内容从62 条增加到了83 条。除了新增的22 条(其中一条被合并到另一条中),我还对原有内容进行了全面审查和深入修订。其中一些条款(如条款45 和条款55)几乎完全重写。 许多新增要点最初源自Effective TypeScript 博客(https://effectivetypescript.com), 不过所有内容都经过了大幅修改。此外, 第6 章主要基于我在Sidewalk Labs 参与Delve 项目时开发crosswalk(https://oreil.ly/-XQ6A)和crudely-typed(https://oreil.ly/E7_gV)库的个人经验。 以下是第二版中新增条款的来源: • 条款28 改编自博客文章“Use Classes and Currying to Create New Inference Sites”(https://oreil.ly/OAApn)。 • 条款32 源自代码审查。直到我看到有人违反这个规则,我才意识到它的存在! • 条款36 受到了我在无数次代码审查中提供反馈的启发。 • 条款37 基于我的个人经验,以及Evan Martin 的博客文章“Why Not Add an Option for That?”(https://oreil.ly/YJWQR)。Cory House 在这个话题上的频繁推文也让我有勇气将其纳入本书。 • 条款38 受到Alan Perlis 的一句名言(我经常引用)以及Scott Meyers 的规则的启发。 • 条款39 来自我所在团队对Jsonify 适配器的实践经验。我们一开始兴致勃勃地采用它,后来又更兴奋地将其弃用。这一经历促使我写下博客文章“The Trouble with Jsonify: Unify Types Instead of Modeling Small Differences”(https://oreil.ly/zVod4)。 • 条款48 改编自博客文章“The Seven Sources of Unsoundness in TypeScript”(https://oreil.ly/NiTnr),Ryan Cavanaugh 也提供了重要的意见。 • 条款50 受到了对“类型本质是什么”的深入思考的启发,同时借鉴了一篇关于依赖类型(dependent types)的Stack Overflow 回答。 • 条款51 改编自博客文章“The Golden Rule of Generics”(https://oreil.ly/yaxs8),而该文章本身则源自TypeScript 官方手册中的建议。 • 条款53 受到我在crosswalk 和crudely-typed 库上的工作启发,同时也出于对各种[T] 包装类型的好奇。 • 条款54 受到我在TypeScript 4.1 发布后对模板字面量类型(template literal types)的探索启发,最终形成了博客文章“TypeScript Splits the Atom!” (https://oreil.ly/Es6Ep)。 • 条款56 是我长期以来对这个话题的兴趣的总结。最初的契机是Titian Cernicova-Dragomir 在Stack Overflow 上对_.invert 的类型化解答,随后结合了我在crosswalk 和crudely-typed 库中的实践经验,最终促成了博客文章“The Display of Types”(https://oreil.ly/bue9Q)。 • 条款57 受到TypeScript 4.5 发布说明中新增尾递归(tail recursion)优化的启发。 • 条款58 来自我在TypeScript 与数据库集成方面的实践经验,并最终促成了我在TypeScript Congress 2022 的演讲“TypeScript and SQL: Six Ways to Bridge the Divide”(https://oreil.ly/ofuph)。 • 条款59 介绍了一种广泛使用的技巧,在审阅第一版时由Jesse Hallett 介绍给我。其中的“pairs”变体来自Tom Hicks 2021 年的一条推文。 • 条款62 受到Artsy 博客文章“Conditional Types in TypeScript”(https://oreil.ly/r-7E0)的启发。 • 条款63 源于Ryan Cavanaugh 对第一版的反馈,我最终将其总结为博客文章“Exclusive Or and the Optional never Trick”(https://oreil.ly/os01S)。 Stefan Baumgartner 在TypeScript Cookbook(O’Reilly)中对这个技巧的热情让我决定将其纳入本书。 • 条款71 受到我与Evan Martin 在Reddit 上的讨论启发,以及一个令人沮丧的bug,该bug 归结于new Set("string") 的行为。这促使我撰写了博客文章“In Defense of Interface: Using Declaration Merging to Disable ‘Bad Parts’”(https://oreil.ly/iYGLY)。 • 条款74 讨论了一个经常出现的话题,特别是在你对TypeScript 没有正确的认知模型时。 • 条款76 受到无数次调试经历的启发,这些调试最终都归因于对运行环境的错误理解。 • 条款77 受到我个人对该主题的好奇心、一些Stack Overflow 问题以及Gary Bernhardt 一场演讲的启发。 • 条款78 受到了TypeScript 运行变慢的痛苦经历启发, 其内容基于TypeScript Wiki 以及博客文章“What’s TypeScript Compiling? Use a Treemap to Find Out”(https://oreil.ly/QRilV)。 感谢我的技术审阅者Josh Goldberg、Stefan Baumgartner、Ryan Cavanaugh、Boris Cherny 和Titian Cernicova-Dragomir,你们的反馈极大地提升了本书的质量。感谢我在Delve 体验小组的同事们(尤其是Stephanie Chew、Luda Zhao、Ha Vu 和Amanda Meurer),感谢你们在代码审查中的帮助,以及对我对TypeScript 无限热情的包容。感谢O’Reilly 团队的每一位成员,正是你们让这本书得以诞生:Angela Rufino、Ashley Stussy、Amanda Quinn、ClareLaylock、Sonia Saruba。感谢Chris Mischaikow 在最后时刻的校对。 Spotify 的Jazzy Morning 播放列表(以Arta Porting 的Beautiful Sunrise 开场)为我的写作和编辑提供了完美的背景音乐。 最后,感谢Alex 一路以来的支持—— 经历了疫情、线上线下婚礼、职业变动和一场重要的搬迁,我很庆幸至少有一件事情始终未变!
目录
目录
前言 1
第一版前言 13
第1 章 认识TypeScript 17
条款1:TypeScript 与JavaScript 18
条款2:了解常用的编译选项25
条款3:代码生成独立于类型30
条款4:结构类型的自由度 39
条款5:使用any 要慎重 44
第2 章 TypeScript 的类型系统 51
条款6:借助编辑器探索类型世界 51
条款7:值的集合即类型 58
条款8:类型空间与值空间 67
条款9:类型注解胜于类型断言 74
条款10:避免使用对象类型(String、Number、Boolean、Symbol、BigInt) 79
条款11:多余属性检查和类型检查 83
条款12:函数整体声明类型更安全 88
条款13:type 别名与interface 93
条款14:使用readonly 守护不可变值 101
条款15:类型操作配泛型,避免重复造轮子 108
条款16:类型要精准,索引签名请慎用 120
条款17:慎用数字索引签名 126
第3 章 类型推断与控制流分析 131
条款18:避免类型冗余,保持代码整洁 132
条款19:类型专属,变量分立 141
条款20:类型能溯源,推断全了解 144
条款21:对象整装,一次性构造 151
条款22:认识类型收窄 154
条款23:别名要使用,类型得一致 160
条款24:以上下文为基础,类型推导有根有据164
条款25:类型演变,时刻了解 170
条款26:函数式构建引航,类型贯通流转 175
条款27:async 取代回调,类型流转更畅通 180
条款28:类与柯里化并举,新建类型推断点 186
第4 章 类型设计 193
条款29:类型即契约,状态需有效 193
条款30:宽进严出,契约必守 200
条款31:类型勿繁,注释需简 205
条款32:类型别名切勿包含null 或undefined208
条款33:空值需在类型之外 209
条款34:接口组合胜过属性组合 214
条款35:类型精确优先,慎用字符串 219
条款36:特殊值要有独立类型 224
条款37:慎用可选属性 228
条款38:同类型参数不传两次 233
条款39:统一类型胜过细微差异建模 235
条款40:不完全精确的类型胜过不准确的类型238
条款41:以专业领域语言来命名类型 244
条款42:孤例不证 247
第5 章 非健全性与any 类型 255
条款43:压缩any 的作用域 255
条款44:更精确的any 变体胜过any 本身 259
条款45:在签名正确的函数内部隐藏不安全类型断言262
条款46:类型未知,unknown 胜过any 267
条款47:类型安全的方法胜过猴子补丁 272
条款48:规避健全性陷阱 277
条款49:跟踪类型覆盖率,避免类型安全回退289
第6 章 泛型与类型级编程 293
条款50:视泛型为类型间的函数 294
条款51:避免非必要的类型参数 301
条款52:条件类型胜过重载签名 308
条款53:控制条件类型在联合类型上的分布 313
条款54:使用模板字面量类型建模DSL 和字符串之间的关系 319
条款55:类型也要测试 326
条款56:优化类型的展示方式 337
条款57:泛型也要尾递归 342
条款58:自动代码生成胜过手写复杂类型 347
第7 章 TypeScript 指南 353
条款59:使用Never 类型进行类型穷举检查 353
条款60:遍历对象 361
条款61:使用Record 类型保持值的同步 365
条款62:使用剩余参数和元组类型来建模可变参数函数 369
条款63:使用可选的never 属性来建模异或关系 372
条款64:使用Brand 构建名义类型 376
第8 章 类型声明和@types 381
条款65:将TypeScript 和@types 放在devDependencies 中 381
条款66:理解类型声明中的三个版本 384
条款67:导出所有出现在公共API 中的类型 389
条款68:使用TSDoc 注释API 390
条款69:如果this 是API 的一部分,请在回调中为其指定类型 394
条款70:使用镜像类型断开依赖关系 399
条款71:使用模块增强来改善类型 402
第9 章 编写和运行代码 409
条款72:与Typescript 相比,ECMAScript 更胜一筹 409
条款73:使用源码映射调试TypeScript 418
条款74:了解运行时类型的重组 426
条款75:理解DOM 的层级关系 432
条款76:根据环境创建精确的模型 439
条款77:理解类型检查和单元测试之间的关系442
条款78:关注编译器的性能 447
第10 章 现代化与迁移 457
条款79:编写现代化的JavaScript 458
条款80:使用@tscheck 和JSDoc 试验TypeScript 463
条款81:使用allowJs 混合TypeScript 和JavaScript 469
条款82:按照依赖逐个进行模块转换 470
条款83:在启用noImplicitAny 前,不要认为迁移已完成 477
附录 第一版与第二版条款对照表 481
- 有电书房
- 扫描二维码,访问我们的微信店铺