Google鼓励的13条代码审查标准

thbcm阅读(378)

文章分享自公众号: DevOps云学堂

如何在代码审查方面表现出色

在本文中,我们将简要介绍13种代码审查标准,这些标准可以极大地帮助改善软件的运行状况并保持开发人员满意。

顾名思义,代码审查是一个过程,其中一个或多个开发人员审查或筛选另一位开发者(作者)编写的代码,以确保:

  • 代码没有任何错误或问题。
  • 符合所有质量要求和标准。
  • 代码执行了预期的测试。
  • 合并后,它将使代码库的运行状况保持更好。

这就是为什么代码审查是软件开发的关键部分的原因。代码审阅者充当代码库管理员,负责确定代码是否处于要成为代码库的一部分并进入生产的状态。

Google以其卓越的技术而著称,它们具有有效的代码审查标准,这些标准似乎突出了审查代码时要记住的一些要点。在Google,代码审查的主要目的是确保Google代码库的整体代码运行状况随着时间的推移而不断改善。

这是您在查看更改列表时要记住的事项列表。

审查标准

1.该代码改善了系统的整体运行状况

每个更改列表(Pull Request)都会改善系统的整体运行状况。想法是,由于进行了如此小的改进,每次合并后,软件或代码库的运行状况都会得到改善。

2.快速的代码审查,响应和反馈

首先,不要延迟推送(合并)更好的代码。不要指望代码是完美的。如果它的状况可以改善系统的整体运行状况,则请推送。

“这里的关键是没有’完美’的代码,只有更好的代码。”

如果您不在一项重点任务的中间,那么请在代码完成后立即进行检查;但是,一个工作日是响应拉取请求所需的最长时间。预计变更列表将在一天之内获得多轮的部分/完整代码审查。

3.在代码审查期间进行教育和启发

通过尽可能共享知识和经验,在代码审查期间提供指导。

4.审查代码时遵循标准

始终牢记,编码标准此类文档是代码审查期间的绝对权威。例如,要在制表符和空格之间保持一致性,可以引用编码约定。

5.解决代码审查冲突

通过遵循样式指南和编码标准文档中商定的最佳实践,并寻求其他在产品领域具有更多知识和经验的人的建议,来解决冲突。根据严重性,处理冲突有所不同。

如果您的评论是可选的或次要的,请在评论中进行说明,然后由作者决定是解决还是忽略它们。作为代码审阅者,您至少可以建议在没有样式指南或编码标准的情况下,更改列表(请求)与其余代码库保持一致。

6.演示UI更改是代码审查的一部分

如果更改列表(Pull Request)更改了用户界面,则除了代码查看之外,还必须进行演示以确保外观上的所有外观均符合预期并与模拟匹配。

对于前端变更列表(Pull Request),始终建议进行演示/演练,或确保变更列表还包括必要的UI自动化测试,以验证添加/更新的功能。

7.确保代码审查伴随所有测试

除非紧急情况,否则拉取请求(更改列表)应伴随所有必要的测试,例如单元,集成,端到端等。

紧急情况可能是需要尽快修复的错误或安全漏洞,以后可以添加测试。在这种情况下,请确保创建了适当的问题,并确保有人在完成热修复或部署后立即拥有所有权才能完成。

没有足够的理由跳过测试。如果由于时间限制,某些目标有无法实现的风险,那么解决方案不是跳过测试,而是要对可交付成果进行范围界定。

8.专注时,不要打扰自己进行代码审查

如果您正处于重点工作中,请不要打扰自己,因为这可能需要很长时间才能恢复正常。换句话说,打断专注的开发人员所付出的代价比让开发人员等待代码审查要高得多。在计划的休息时间(例如午餐,咖啡等)之后进行代码检查。

期望并非总是在同一天完成并合并整个代码审查过程。重要的是迅速给作者一些反馈。例如,您可能无法进行完整的检查,但是您可以快速指出一些可以研究的内容。这将极大地减少代码审查期间的挫败感。

9.复习一切,不要做任何假设

查看分配给您检查的每一行代码。不要对人工编写的类和方法做任何假设,并且应该确保您了解代码在做什么。

确保了解您正在检查的代码。如果没有,请进行澄清或要求代码演练/解释。如果您有部分代码不具备审阅的资格,请确保还有其他合格的开发人员可以审阅代码的那些部分。

10.回顾代码时要顾全大局

从更广泛的背景来看变化通常是有帮助的。例如,更改了文件,并添加了四行代码。不要只查看四行代码;相反,请考虑查看整个文件并检查新添加的内容。它们会降低现有代码的质量,还是会使现有功能成为重构的候选对象?

如果不在函数/方法或类的上下文中检查此类简单的添加项,则随着时间的流逝,您将继承一个类,该类是不可维护的,超级复杂的,难以测试的,无法完成的所有工作,并且难以扩展或重构。

请记住,随着时间的推移,很少的改进加起来就可以产生具有最少数量缺陷的优质产品,同样,随着时间的流逝,轻微的代码降级或技术负担也会加重并导致产品难以维护和扩展。

11.认可并鼓励代码评审期间的良好工作

如果您在变更列表中看到了一些不错的东西,请别忘了喊出作者的出色作品并鼓励他们。这是我个人以前从未做过的事情。代码审查的目的不仅应该是发现错误,还应该鼓励和指导开发人员所做的出色工作。

12.在代码审查中要谨慎,尊重,友善和清晰

至关重要的是,在代码审阅期间,您要善良,清晰,礼貌和尊重,同时也要对作者非常清楚和乐于助人。查看代码时,请确保对代码而不是开发人员做出评论。

13.解释您的代码审查注释,并牢记范围

每当代码审阅意见提出替代方法或进行标记时,至关重要的是要解释原因并根据您的知识和经验提供示例,以帮助开发人员了解您的建议将如何帮助提高代码质量。

当建议修复或更改时,请在如何指导作者修复代码方面找到适当的平衡。例如,我很欣赏指导,解释,一些提示或建议,而不是整个解决方案。

以上就是W3Cschool编程狮关于Google鼓励的13条代码审查标准的相关介绍了,希望对大家有所帮助。

Java版本新发现:JDK15的14个新特性和变化

thbcm阅读(407)

Java Development Kit 15是 Oracle 对 Java SE(标准版)下一个版本的实现,已于8月初进入了发布候选阶段。JDK 15 的亮点包括文本块、隐藏类、外部内存访问 API ,以及密封类和记录的预览。

OpenJDK 15的新特性和变化包括:

1)外内存访问API(foreign-memory access API)

外内存访问 API 的第二个孵化器,它将使 Java 程序能够安全和有效地访问 Java 堆之外的外部内存。此 API 能够操作各种类型的外部内存,如本机、持久和托管堆。有许多 Java 程序是访问外部内存的,比如 Ignite 和MapDB。该API将有助于避免与垃圾收集相关的成本以及与跨进程共享内存以及通过将文件映射到内存来序列化和反序列化内存内容相关的不可预测性。该Java API目前没有为访问外部内存提供令人满意的解决方案。但是在新的提议中,API不应该破坏JVM的安全性。在JDK 14中,这个功能正在经历早期的孵化阶段,在JDK 15中还提供了改进。

2)密封类(sealed classes)的预览

与接口一样,密封类也限制其他类或接口可以扩展或实现它们。这个特性的目标包括——允许类或接口的作者来控制哪些代码负责实现、提供了比限制使用超类的访问修饰符声明方式更多选择,并通过支持对模式的详尽分析而支持模式匹配的未来发展。

3)相关支持的删除

删除对Solaris/SPARC、Solaris/x64和Linux/SPARC端口的源代码和构建支持,而在JDK 14中不赞成删除这些端口,但可在将来的版本中删除它们。许多正在开发的项目和功能(如Valhalla、Loom和Panama)需要进行重大更改以适应 CPU 架构和操作系统特定代码。放弃对Solaris和SPARC端口的支持将使OpenJDK社区的贡献者加快开发新特性,从而推动平台向前发展。近年来,Solaris和SPARC都被Linux操作系统和Intel处理器所取代。

4)记录结构(Records)

记录是充当不可变数据的透明载体类,在 JDK 14 中作为早期预览进行了首次调试之后,它将被包含在 JDK 15 的第二个预览版本中。计划的目标包括设计一个面向对象的结构,表达一个简单的聚合值,帮助程序员关注建模不可变的数据,而不是扩展行为,自动实现数据驱动的方法,如如equals和assessors,并保留 Java 中长期存在的原则,如名义类型和迁移兼容性 。Records(记录)可以被认为是名义元组。

5)数字签名算法

基于Edwards-Curve数字签名算法(EdDSA-Edwards-Curve Digital Signature Algorithm)的加密签名。EdDSA是一种现代的椭圆曲线方案,具有 JDK 中现有签名方案的优点。EdDSA将只在SunEC提供商中实现。EdDSA与其他签名方案相比,具有更高的安全性和性能,因此备受关注;它已经在OpenSSL和BoringSSL等加密库中得到支持。

6)套接字的更新实现

通过将java.net.datagram.Socket和java.net.MulticastSocket API的底层实现替换为更简单、更现代的实现来重新实现遗留的DatagramSocket API。新的实现:

1.易于调试和维护;

2.与Project Loom中正在探索的虚拟线程协同。

新的计划是 JDK Enhancement Proposal 353的后续,该方案重新实现了遗留的套接字 API。java.net.datagram.Socket和java.net.MulticastSocket的当前实现可以追溯到 JDK 1.0,那时 IPv6 还在开发中。因此,当前的多播套接字实现尝试调和 IPv4 和 IPv6 难以维护的方式。

7)禁用偏向/偏置锁定

在默认情况下禁用偏向锁定,并弃用所有相关命令行选项。目标是确定是否需要继续支持偏置锁定的高维护成本的遗留同步优化,HotSpot虚拟机使用该优化来减少非竞争锁定的开销。尽管某些 Java 应用程序在禁用偏向锁后可能会出现性能下降,但偏向锁的性能提高通常不像以前那么明显。

8)instanceof模式匹配

此为第二个预览版,之前是 JDK 14 中的首次预览。模式匹配允许程序中的通用逻辑(主要是有条件地从对象中提取组件)得到更精确的表达。像Haskell和c#这样的语言已经包含了模式匹配,因为它的简易性和安全性。

9)隐藏类(Hidden classes)

即不能被其他类的字节码直接使用的类,是为在运行时生成类并通过反射间接使用类的框架使用的。隐藏类可以定义为访问控制嵌套的成员,并且可以独立于其他类卸载。该提议将通过支持一个标准 API 来定义不可发现且生命周期有限的隐藏类,从而提高 JVM 上所有语言的效率。JDK 内部和外部的框架将能够动态生成类,而这些类可以定义隐藏类。许多构建在 JVM 上的语言都依赖动态类生成来获得灵活性和效率。这个提议的目标包括:允许框架将类定义为无法发现的框架实现细节,这样它们就不能被其他类链接,也不能通过反射被发现;支持扩展带有不可发现类的访问控制嵌套;并支持主动卸载不可发现的类,因此框架可以根据需要灵活地定义多个类。另一个目标是弃用非标准API misc.Unsafe::defineAnonymousClass,目的是为了在将来的版本中删除。另外, Java 语言不会因为这个建议而改变。

10)ZGC产品化

在这个提案下,Z垃圾收集器(ZGC-Z Garbage Collector)将从一个实验特性升级为产品。 ZGC 集成到2018年9月发布的JDK 11中,是一个可扩展的、低延迟的垃圾收集器。 ZGC 是作为一种实验性的功能引入的,因为 Java 开发人员决定应该小心地、逐步地引入这种规模和复杂性的特性。从那时起,添加了许多改进,从并发类卸载、未使用内存的解除提交、对类数据共享的支持到改进的 NUMA 感知和多线程堆预处理。此外,最大堆大小从4TB增加到16TB。支持的平台包括Linux、Windows和MacOS。

11)文本块

JDK 14和JDK 13中都预览版文本块,它旨在简化编写 Java 程序的任务,方法是简化表达跨越几行源代码的字符串,同时在常见情况下避免转义序列。文本块是一个多行字符串文字,它可以避免使用大多数转义序列、自动以可预测的方式格式化字符串,并在需要时为开发人员提供对格式的控制。文本块建议的一个目标是增强 Java 程序中表示用非J Java 语言编写的代码的字符串的可读性。另一个目标是通过规定任何新构造都可以将相同的字符串集表示为字符串文字,解释相同的转义序列,并以与字符串文字相同的方式操作,从而支持字符串文字的迁移。OpenJDK 开发人员希望添加转义序列来管理显式的空格和换行控件。

12)LPT GC正式可用

Shenandoah低暂停时间(low-pause-time)垃圾收集器将成为一个生产特性,不再处于实验阶段。它在一年前被集成到JDK 12中。

13)删除Nashorn

2014年3月在jdk8中首次亮相的Nashorn被移除,由于其被GraalVM等技术淘汰。OpenJDK 15 提议要求删除Nashorn APIs和用于调用Nashorn的jjs命令行工具。

14)RMI Activation进入不推荐期

不推荐 RMI 激活机制,以便将来删除。RMI 激活机制是 RMI 中一个过时的部分,自 Java 8 以来一直是可选的。RMI 激活机制增加了持续的维护负担。RMI 的其他部分将不被弃用。

JDK 15的早期访问版本可以在java.jdk.net网站中找到。JDK 15将是一个短期的特性发布,根据 Oracle 的6个月发布周期,它将被支持6个月。下一个长期支持(LTS-long-term support)版本是JDK 17,预计将于2021年9月发布,它将获得几年的支持。当前的LTS版本是JDK 11,是于2018年9月发布。

以上就是W3Cschool编程狮关于Java版本新发现:JDK15的14个新特性和变化的相关介绍了,希望对大家有所帮助。

Python 3中被忽视了的三大重要功能

thbcm阅读(368)

文章转载自公众号:读芯术

Python 3 上线已有一段时间,大多数开发人员(特别是那些首次编程的人)已经在使用它了。

不过,你确定已经对 Python 3 研究透彻了吗?事实上,其中还有许多新功能对大多数人来说仍处于未知状态。本文就将讨论 Python 3 中三个鲜为人知但非常有用的功能,我在其他语言中接触到并爱上了这些功能,它们的加入使得使 Python 3 体验更佳。

枚举

枚举是在 Java 和 Swift 中常使用到的功能,我把它扩展到Python中。在 Python 中创建枚举非常简单,在 Python 3 之前的版本中也可使用(尽管功能更受限):

from enum importEnum
             classState(Enum):
        AIR=0
        LAND=1
        SEA=2
        myState =State.AIR
             # Prints 0
      print(myState.value)
      # Prints AIR
      print(myState.name)

在上述代码中,可以看到通过构造一个类并使其成为枚举的子类,便可以轻松构造枚举。在这里只需在下面的行中定义每个状态。就我而言,我有 AIR、LAND、SEA。

Python 3 的新功能是运行.value和.name。这能获得与状态相关联的整数值或与之相关联的字符串。

在上面的代码中,输入State.LAND.name 返回LAND,因此功能不仅仅是一个整数枚举。

当需要描述性常量时,代码中的枚举类型十分有用。例如,与其检查状态是 0 还是 1 ,不如检查它是否是State.MOVING或State.STATIONARY状态。常量可能会改变,如果有人在看你的代码,MOVING比 0 更有意义,代码的可读性也会大大提高。

格式

在Python 3.6中添加的fstring是格式化文本的一种很好的方式。它们的可读性强,而且不容易出错。fstring比Python以前使用的格式更易读。以下是使用格式的示例:

name = Brett 
       blog_title = Medium 
             # Hi, my name isBrett and I am writing on my Medium blog.
       a ="Hi, myname is {} and I am writing on my {} blog.".format(name,blog_title)

如上所示,在字符串中打入括号,然后按顺序列出每个变量的名称。相同代码任务很多,但fstring极大地增加了代码的可读性,尤其是类似于用Swift格式化字符串。

name = Brett 
       blog_title = Medium
             # Hi, my name isBrett and I am writing on my Medium blog.
       a =f"Hi, myname is {name} and I am writing on my {blog_title} blog."

为了完成这个更简洁的字符串,只需在引号前面加上字母 f ,然后将变量或数据直接放入括号中,而不是使用空括号。由于变量本身是写在括号内的,所以不必计算格式中写入的项的数量来确定变量的位置,变量就在应在的位置。

相比于字符串连接或格式化字符串,fstring可以生成更可读、更可靠的代码。

数据类

数据类可能要比上述所谈更为晦涩难懂,所以我将简要地解释一下。我在Kotlin中逐渐喜欢上了数据类,因此很想在Python中使用它们。

数据类实际上是一个类,其唯一目的是保存数据的类。类将具有可以访问和写入的变量,但上面没有额外的逻辑。

假设你有一个程序,在不同的类之间传递一个字符串和一个数字数组。使用pass(str,arr)这样的方法也可行,最好是创建一个只包含字符串作为字段和数组的数据类。通过创建一个数据类,你所做的将更加清晰,单元测试也将更加容易。

下面这个示例将说明如何创建一个表示三维向量的简单数据类,但这可以很容易地扩展为表示不同数据的任意组合:

from dataclasses import dataclass
             # Definedataclass
           @dataclass
           classVector3D:
              x: int
              y: int
              z: int


           # Create a vector
           u =Vector3D(1,1,-1)
             # Outputs: Vector3D(x=1,y=1, z=-1)
           print(u)

在这里,你可以看到数据类的定义与声明普通类非常相似,只是我们先用了@dataclass,然后每个字段的名称都是name:type。

虽然我们创建的Vector3D功能有限,但是数据类的目的只是提高效率并减少代码中的错误,传递Vector3D比传递int变量要好得多。

以上就是W3Cschool编程狮关于Python 3中被忽视了的三大重要功能的相关介绍了,希望对大家有所帮助。

Go 1.15.1和Go 1.14.8发布:一个安全问题修复

thbcm阅读(360)

Go Team 发布了 Go 1.15.1 和 Go 1.14.8,以解决最近报告的安全问题。建议所有受影响的用户更新到以下版本之一(如果不确定哪个版本,请选择 Go 1.15.1)。

如果处理程序未明确设置 Content-Type 响应头,则 net/http/cgi 和 net/http/fcgi包将默认设置为 “text/html”,如果攻击者可以控制响应的内容,则可能会导致跨站点脚本漏洞。

现在修改改为根据第一次 Write 的内容通过 http.DetectContentType 来设置Content-Type 响应头,这与 net/http 包的行为一致。

尽管这可以保护某些验证上传文件内容的应用程序,但未在攻击者控制的任何文件上明确设置 Content-Type 响应头是不安全的,应避免出现。也就是说,你应该总是明确设置 Content-Type 响应头。

RedTeam Pentesting GmbH 报告此问题。此问题为CVE-2020-24553,相应的 issue 见:github.com/golang/go/issues/40928

以前偶然发现的 Java Bug(JDK 9及之前仍未修复)

thbcm阅读(354)

文章转载自公众号:叨叨软件测试 作者: 蒋李恒

背景

15年在中信银行做持续集成时,由于当时的项目是基于三方采购的 Java 配置开发平台做的,平台自己基于 Ant 插件实现了增量和热部署。

其中有几个项目在持续集成部署时,经常发现 Linux 平台部署成功后(Windows 不会出现,Linux 也是偶发现象),新版本代码并没有生效(反编译 class)。

起初我是在本地 windows 上跟踪调试基于 Ant 插件的代码,但始终重现不了(最后测试发现 Windows 无此 Bug)。

后来,通过分析代码逻辑,其中有段逻辑是通过文件的最后修改时间(File.lastModified())来判断要不要覆盖部署的,最后通过单测发现,是由于 Java 的 File.lastModified() 方法在 Windows 和 Linux/Unix平台获取的精度不一样导致的,Windows 精度为毫秒,而 Linux/Unix 只能到秒(JDK Bug:JDK-8177809)。

所以也解释了,为什么是偶发现象,文件修改时间如果判断的两个值正好跨秒时,部署就是成功的,否则失败。

Bug 重现

测试代码:FileTest.java

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;


public class FileTest {
    private static final long LM = 1599276952718L;


    public static void main(String[] args) throws IOException {
        // java版本号
        System.out.println("Java Version:" + System.getProperty("java.version"));


        File f = new File("test.txt");
        f.createNewFile();


        // 设置最后修改时间
        f.setLastModified(LM);


        // 获取修改时间,存在 bug
        System.out.printf("Test f.lastModified [%s]: %b\n",
f.lastModified(), f.lastModified() == LM);
        // 格式化输出,正确不存在 bug
        System.out.printf("Test f.lastModified DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));


        // Files.getLastModifiedTime() 获取修改时间,同样存在 bug
        System.out.printf("Test Files.getLastModifiedTime [%s]: %b\n",
Files.getLastModifiedTime(f.toPath()).toMillis(),
(Files.getLastModifiedTime(f.toPath()).toMillis() == LM));
        // 格式化输出,正确不存在 bug
        System.out.printf("Test Files.getLastModifiedTime DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));


        f.delete();
    }
}

命令行下编译、执行:

# 编译执行
$ javac FileTest.java && java FileTest 

Windows 执行结果

Windows 平台不存在此 Bug。

# 编译执行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_202
Test f.lastModified [1599276952718]: true
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952718]: true
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

Mac 执行结果

JDK 8 最新版本,目前仍然没有修复该问题。

# 编译执行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_261
Test f.lastModified [1599276952000]: false
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952000]: false
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

Linux 执行结果

# 编译执行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_171
Test f.lastModified [1599276952000]: false
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952000]: false
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

官方 Bug 链接

JDK10 已修复,但是之前版本目前仍然未修复。

  • bugs.openjdk.java.net/browse/JDK-8177809

以上就是W3Cschool编程狮关于以前偶然发现的 Java Bug(JDK 9及之前仍未修复)的相关介绍了,希望对大家有所帮助。

Linux/Unix 效率工具:快速路径切换 z 命令

thbcm阅读(358)

文章转载自公众号:叨叨软件测试

简介

z 是一个开源的 Linux 快速路径切换工具(类似工具还有z.lua、autojump、fasd)。通过 Frecency 机制对日常访问的路径进行 Frecent 权重计算,z 会帮你切换到所有匹配正则关键字的路径中权重值最高的那条路径。

Frecency 是由 Mozilla 开发,将访问的频率和新近度(上一次访问到现在的时间差)合并为一个度量的启发式方法。

详细内容见:github.com/rupa/z

安装

# /usr/local 安装
$ cd /usr/local
$ sudo git clone https://github.com/rupa/z.git
$ sudo chmod +x z.sh


# zsh 配置变量
$ echo '. /usr/local/z/z.sh' >> ~/.zshrc
$ source ~/.zshrc


# bash 配置变量
$ echo '. /usr/local/z/z.sh' >> ~/.bash_profile
$ source ~/.bash_profile


# 安装 manpage
$ cp z.1 /usr/local/share/man/man1


# 验证安装
$ z -h
$ man z

使用

技巧:z 命令可以使用 tab 来进行补齐,以提高切换效率。

# 查看帮助
$ man z
或
$ tldr z


# 显示记录的路径
$ z


# 切换到一个名字带有 "foo" 的路径
$ z foo


# 切换到一个名字带有 "foo" 并且后面带有 "bar" 的路径(例:fooesbar):
$ z foo bar


# 切换到名字带有 "foo" 并且拥有最高访问次数的路径
$ z -r foo


# 切换到最近使用的名字带有 "foo" 的路径
$ z -t foo


# 列出在 z 的数据库中名字带有 "foo" 的路径
$ z -l foo


# 将当前路径从 z 的数据库中移除
$ z -x .

以上就是W3Cschool编程狮关于Linux/Unix 效率工具:快速路径切换 z 命令的相关介绍了,希望对大家有所帮助。

(Add sumOf)一道字节前端原题

thbcm阅读(357)

文章转载自公众号:蓝色的秋风

前言

最近学弟去面了字节跳动,但是由于面试经验少,面试的时候紧张了,一时之间没有写出来,之后来我交流了一下。那我就来分析分析这道题目。

正文

这题的规则是这样的

给定有一个 Add 函数,要支持以下形式的调用


Add(1)(2)(3).sumOf(); // 输出 6
Add(1,2)(3)(4).sumOf(); // 输出 10
Add(1,2,...)(3)(4)(...).sumOf();  // ...

拿到这种题目,我先来说说我自己的做题流程,一般会去找它最简单的形态。我们一步一步来拆解。

先去掉 sumOf() 变成了以下形态

Add(1,2,...)(3)(4)(...)

嗯….有点熟悉…但是还是有点复杂,那我们再去掉无限调用这个限制。

Add(1,2,...)(3)(4)

唔,还是有点难呀…没关系,再砍, 不要传入多个参数。

Add(1)(2)(3)

有….有….有那味了….这….这不就是柯里化吗….

有些小朋友可能没有听过,对于大朋友而言耳熟能详,融会贯通。

我们还是来介绍一下。

在《javascript高级程序设计》这本书中有如下介绍:

与函数绑定紧密相关的主题是函数柯里化,它用于创建已经设置好的一个或者多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。

我们来写写看:

function Add(x) {
 return function (y) {
  return return functio (z) {
   return x + y + z;
  }
 }
}
// 简洁写法
const Add = x => y => z => x+y+z;

执行一下

Add(1)(2)(3) // 6 

是我们要的那味~

那么我们既然已经写出了这个形态,我们就一步一步反推。

这个时候千万别紧张,我们从最低级的形态出发,写出一个最基本的形态,能够有效地帮助我们建立自信心,吃下定心丸,按照这种方式,哪怕我们最终没有写出完美的结果,让面试官看到你思考解题的过程,也是一种加分。

好,接着说~

那我们接下来需要实现这个样子。

Add(1,2,...)(3)(4)

传入参数不止一个

我们知道,对于不确定参数个数,我们可以使用 arguments 这个对象来获取到所有的入参,但是 arguments 不是一个 Array,但是我们可以使用 ES6 中的 Spread syntax (展开语法)去将他变成一个数组。表演继续。

function Add() {
 const nums = [...arguments];
 return function() {
  nums.push(...arguments);
  return function() {
   nums.push(...arguments);
   return nums.reduce((a, b) => a + b);
  }
 }
}

nice!已经离我们最终的形态越来越近了。接下来是这个函数能够无限的进行调用。

Add(1,2,...)(3)(4)(...)

那么怎么样才能无限调用呢?没错,用递归。

function Add() {
 const nums = [...arguments];
 function AddPro() {
  nums.push(...arguments);
    return AddPro;
 }
 return AddPro;
}

嗯,其实我们写到这里发现了… 由于是无限递归,我们没办法确定最后一次函数调用,因此我们需要最后显式调用一个结束的方法来打印出最后的数据。

很自然地,我们可以在 AddPro 添加一个方法 sumOf 来解决这个问题。

学弟就是卡在这里地方,被函数添加上一个方法搞懵了。你是否知道呢?

function Add() {
 const nums = [...arguments];
 function AddPro() {
  nums.push(...arguments);
    return AddPro;
 }
 AddPro.sumOf = () => {
  return nums.reduce((a, b) => a + b);
 }
 return AddPro;
}

好啦好啦,结束啦。

等等

在最后,我再来补充一种方案,function 不仅可以继续挂载 function ~ 还可以挂载变量哦~

function Add() {
 if (!Add.nums) {
   Add.nums = [];
  }
  Add.nums.push(...arguments);
  return Add;
}
Add.sumOf = () => {
 return Add.nums.reduce((a, b) => a + b);
}

我们总结一下,小小的面试题涉及到的基础知识。

闭包、递归、作用域、函数与对象

基础就是基础,永远是你爸爸,掌握好基础,以不变应万变。

后记

也许你觉得这题有点简单,通过简单的重复练习就能轻松记住,但是最主要的是思路,很多事情都是一样,掌握事情的方法和方向是最重要的。毕竟淘宝也不是一蹴而就的~ 但是只要方向正确了,都会好起来的。

以上就是W3Cschool编程狮关于(Add sumOf)一道字节前端原题的相关介绍了,希望对大家有所帮助。

webpack 构建进度条 ProgressPlugin 源码剖析

thbcm阅读(373)

文章转载自公众号:墨筝

前言

我们在使用 webpack 的时候经常会用到 webpackbar 或者 progress-bar-webpack-plugin 之类的 webpack 插件通过进度条等方式来展示 webpack 的构建进度,以提升构建过程中的反馈体验。对于不同的插件来说,它们只是进度条的 UI 展示形式不同而已,但最核心的 webpack 构建的实时进度的数据来源却是一致的,均由 webpack 内部的 ProgressPlugin 这个插件提供。下面我会结合源码来讲解该插件是如何计算 webpack 的构建进度并将进度数据暴露给第三方的进度条插件。在阅读下文之前可以试着问下自己:如果是你,你会如何计算 webpack 的构建进度。

构建进度的计算

该插件主要根据 webpack 的构建阶段来定义当前进度值。webpack 的构建过程分为很多不同的阶段,在每个阶段 webpack 都暴露了对应的事件钩子。ProgressPlugin 正是通过这些事件钩子对每个阶段都定义了一个基础进度值,代码如下所示:

上述代码中的 interceptHook 方法可以先忽略,这个后续会提到。 通过上述代码你会发现 ProgressPlugin 给 compiler 中的每个钩子都设置了一个指定的进度值。但这些进度值还不够细致到反映 webpack 的详细构建过程,中间还差了 0.06 到 0.69 以及 0.69 到 0.95 两个阶段的数值。webpack构建的具体执行过程主要在 compilation 中,这两个阶段的数值由 compilation 的钩子填充。

0.06~0.69

update 方法的调用由 compilation 的钩子触发,如下所示:

这个阶段的主要工作是 module,entry 以及 dependencies 的处理和构建。换个角度从 ProgressPlugin 给该阶段设置的进度值来看,这部分工作也是 webpack 最耗时的地方。

0.69~0.95

从上述代码中可以看出这个间隔段就完全是根据 compilation 的 hooks 来计算和指定当前的构建进度值,从hook 的描述中可以看出这个阶段主要是 module, chunk 以及assets等资源的优化工作。 基本上整个 webpack 构建过程的进度值就是根据上述中的 compiler 和 compilation 的 hooks 来设置的。

进度数据的透出

webpack 的构建进度确定之后剩下的任务就是将进度数据透出给第三方的进度条插件进行展示。要完成该任务需要ProgressPlugin完成两件事情,一是提供回调函数的切入口;二是需要能在对应的 hook 节点执行该回调函数进行进度的百分比值的传入。以下是这两点的实现原理

回调函数

ProgressPlugin 定义了 handler 函数来作为回调函数切入,代码如下所示:

hook 劫持

hook 劫持的实现非常简单,主要利用 webpack hook 原生提供的intercept方法,前文中提到的interceptHook方法只是对于 intercept 方法的封装,示例代码如下:

结语

webpack 构建的进度条实现原理就是如此简单,给每个构建阶段对应的 hook 设置一个进度值,然后通过 handler 回调和 hook 劫持切入到构建环节将进度信息传入回调函数,最终第三插件通过 handler 获取到进度值后将其展示出来。 webpack 的很多其他功能其实也没有想象中的那么复杂和高大上,通过阅读其源代码了解其实现原理后,你很可能会一拍大腿,大叹一声原来如此,这本身就是一件挺有意思的事儿。

以上就是W3Cschool编程狮关于webpack 构建进度条 ProgressPlugin 源码剖析的相关介绍了,希望对大家有所帮助。

do-while,Java中容易被忽略的语句

thbcm阅读(353)

文章转载自公众号:Java中文社群

最近在看 Java 的基础知识,其中有部分是关于循环的,在 Java 中,循环的语法总共分为 3 种:for、 while、 do-while,如下图所示:

但我惊奇的发现,在之前的职业生涯中(11 年),竟从未用过 do-while(尴尬),于是问了群里的小伙伴,发现也是鲜有人用。

do-while 语法分析

我们先来了解一下 do-while 的语法:

do {
     // statements
} while (expression);

注意:最后一个分号不能省略,否则会提示编译出错。

它的执行流程如下图所示:

那它究竟在什么地方用呢?

do-while 使用场景

在我多次的搜索和请教下,终于找到了两个相对满意的使用场景,接下来一起来看。

使用场景一:抢票

对于抢票业务来说,无论三七二十一,先抢了再说,然后在判断是否抢票成功,如果抢票成功则退出循环,否则继续执行抢票,实现的伪代码如下所示:

do {
    // 抢票代码...
} while (没抢到票);

思路提供者:贾鲲

使用场景二:进制转换

经过大量搜索发现,在 JDK 的源码中,也存在少量使用 do-while 的场景,比如 Integer 中进制转换,相关源码如下:

static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
    int charPos = len;
    int radix = 1 << shift;
    int mask = radix - 1;
    do {
        buf[offset + --charPos] = Integer.digits[val & mask];
        val >>>= shift;
    } while (val != 0 && charPos > 0);


    return charPos;
}

比如十进制转二进制就会执行此方法,在进制转换的业务中,无论任何情况,一定会至少执行一次进制转换的,因此这种业务场景就非常适合 do-while。

总结

孔子说:温故而知新。当我们学完很多知识之后,回过头来再琢磨这些知识,发现很有趣,这就是知识的一大乐趣吧。本文我们介绍了两种 do-while 的使用场景,抢票和进制转换,你还知道哪些 do-while 的使用场景吗?

以上就是W3Cschool编程狮关于do-while,Java中容易被忽略的语句的相关介绍了,希望对大家有所帮助。

使用Vue3.0,我收获了哪些知识点(一)

thbcm阅读(382)

这篇文章主要是将自己前期学习Vue3.0时候整理的一些笔记内容进行了汇总,通过对本文的阅读,你将可以自己完成Vue3.0环境搭建,同时还会对Vue3.0的一些新的特性进行了解,方便自己进行Vue3.0的学习。

本文所有的示例均使用ant design vue2.0实现,关于ant design vue2.0请参考 2x.antdv.com/docs/vue/introduce-cn/

初始化环境

在前面的文章中,我们通过vite搭建了一个开发环境,但是实际上现在vite并没有完善到支撑一个完整项目的地步,所以本文我们依然选择使用vue-cli脚手架进行环境搭建。

小编使用的vue-cli版本是4.5.4,如果您的版本比较旧可以通过npm update @vue/cli来升级脚手架版本,如果没有安装可以通过npm install @vue/cli -g进行安装

使用脚手架新建项目

  1. 在工作空间打开终端(cmd),然后通过vue create my-vue3-test命令初始化项目
  1. 在第一步先选择Manually select features,进行手动选择功能
  1. 然后通过Space和上下键依次选择
   Choose Vue version
   Babel
   TypeScript
   Router
   Vuex
   CSS Pre-processors
   Linter/Formatter

4.然后回车

  1. 然后提示选择Vue版本,选择3.x(Preview)
  2. Use class-style component syntax?选择n,即输入n然后回车
  3. 然后提示Use Babel alongside TypeScript,输入y`
  4. Use history mode for router输入n
  5. 然后css预处理器选择Less
  6. eslint选择ESLint + Prettier
  7. 然后是Lint on save和In dedicater config files
  8. 最后一路回车即可完成项目搭建

启动项目

新建完项目之后,进入到项目中cd my-vue3-test,然后执行 yarn serve即可启动项目

启动之后即可通过访问http://localhost:8080/访问项目

配置ant design vue

在当前Vue3.0正式版还未发布之际,国内比较出名的前端UI库中率先将Vue3.0继承到自家的UI库中的,PC端主要是ant-design-vue,移动端主要是vant, 本文所有示例代码都会基于ant-design-vue来进行,首先我们先安装ant-design-vue

  1. 安装依赖
   yarn add ant-design-vue@2.0.0-beta.6
   yarn add babel-plugin-import -D

  1. 配置ant-design-vue按需加载

进入项目根目录,然后打开babel.config.js文件,将里面的内容修改为

   module.exports = {
     presets: ["@vue/cli-plugin-babel/preset"],
     plugins: [
       // 按需加载
       [
         "import",
         // style 为 true 加载 less文件
         { libraryName: "ant-design-vue", libraryDirectory: "es", style: "css" }
       ]
     ]
   };

  1. 尝试使用vue3 + antdv来添加一个小页面, 我们直接将views/Home.vue文件里面的代码替换为
<template>
  <a-form layout="inline" :model="state.form">
    <a-form-item>
      <a-input v-model:value="state.form.user" placeholder="Username">
        <template v-slot:prefix
          ><UserOutlined style="color:rgba(0,0,0,.25)"
        /></template>
      </a-input>
    </a-form-item>
    <a-form-item>
      <a-input
        v-model:value="state.form.password"
        type="password"
        placeholder="Password"
      >
        <template v-slot:prefix
          ><LockOutlined style="color:rgba(0,0,0,.25)"
        /></template>
      </a-input>
    </a-form-item>
    <a-form-item>
      <a-button
        type="primary"
        :disabled="state.form.user === '' || state.form.password === ''"
        @click="handleSubmit"
      >
        登录
      </a-button>
    </a-form-item>
  </a-form>
</template>
<script>
import { UserOutlined, LockOutlined } from "@ant-design/icons-vue";
import { Form, Input, Button } from "ant-design-vue";
import { reactive } from "vue";


export default {
  components: {
    UserOutlined,
    LockOutlined,
    [Form.name]: Form,
    [Form.Item.name]: Form.Item,
    [Input.name]: Input,
    [Button.name]: Button
  },
  setup() {
    const state = reactive({
      form: {
        user: "",
        password: ""
      }
    });


    function handleSubmit() {
      console.log(state.form);
    }


    return {
      state,
      handleSubmit
    };
  }
};
</script>

然后重启一下项目,就可以发现已经可以正常使用ant-design-vue了。

Vue3.0新体验之setup

对于Vue3.0的问世,最吸引大家注意力的便是Vue3.0的Composition API,对于Componsition API,可以说是两极分化特别严重,一部分人特别喜欢这个新的设计与开发方式,而另一部分人则感觉使用Composition API很容易写出来意大利面式的代码(可能这部分人不知道兰州拉面吧)。到底Composition API是好是坏,小编不做评论,反正我只是一个搬砖的。而本小节介绍的setup就是Composition API的入口。

setup介绍

setup是Vue3.0提供的一个新的属性,可以在setup中使用Composition API,在上面的示例代码中我们已经使用到了setup,在上文代码中我们在setup中通过reactive初始化了一个响应式数据,然后通过return返回了一个对象,对象中包含了声明的响应式数据和一个方法,而这些数据就可以直接使用到了template中了,就像上文代码中的那样。关于reactive,我将会在下一小节为你带来说明。

setup 的参数说明

setup函数有两个参数,分别是props和context。

  1. props

props是setup函数的第一个参数,是组件外部传入进来的属性,与vue2.0的props基本是一致的,比如下面代码

   export  default {
     props: {
       value: {
         type: String,
         default: ""
       }
     },
     setup(props) {
       console.log(props.value)
     }
   }

但是需要注意的是,在setup中,props是不能使用解构的,即不能将上面的代码改写成

   setup({value}) {
       console.log(value)
    }

虽然template中使用的是setup返回的对象,但是对于props,我们不需要在setup中返回,而是直接可以在template使用,比如上面的value,可以直接在template写成

   <custom-component :value="value"></custom-component>

  1. context

context是setup函数的第二个参数,context是一个对象,里面包含了三个属性,分别是

  • attrs

attrs与Vue2.0的this.$attrs是一样的,即外部传入的未在props中定义的属性。对于attrs与props一样,我们不能对attrs使用es6的解构,必须使用attrs.name的写法

  • slots

slots对应的是组件的插槽,与Vue2.0的this.$slots是对应的,与props和attrs一样,slots也是不能解构的。

  • emit

emit对应的是Vue2.0的this.$emit, 即对外暴露事件。

setup 返回值

setup函数一般会返回一个对象,这个对象里面包含了组件模板里面要使用到的data与一些函数或者事件,但是setup也可以返回一个函数,这个函数对应的就是Vue2.0的render函数,可以在这个函数里面使用JSX,对于Vue3.0中使用JSX,小编将在后面的系列文章中为您带来更多说明。

最后需要注意的是,不要在setup中使用this,在setup中的this和你真正要用到的this是不同的,通过props和context基本是可以满足我们的开发需求的。

了解Composition API,先从reactive和ref开始

在使用Vue2.0的时候,我们一般声明组件的属性都会像下面的代码一样

export  default {
  data() {
    return {
      name: '编程狮',
      sex: '男'
    }
  }
}

然后就可以在需要用到的地方比如computed,watch,methods,template等地方使用,但是这样存在一个比较明显的问题,即我声明data的地方与使用data的地方在代码结构中可能相距很远,有一种君住长江头,我住长江尾,日日思君不见君,共饮一江水的感觉。而Composition API的诞生的一个很重要的原因就是解决这个问题。在尤大大在关于Composition API的动机中是这样描述解决的问题的:

  1. 随着功能的增长,复杂组件的代码变得越来越难以阅读和理解。这种情况在开发人员阅读他人编写的代码时尤为常见。根本原因是 Vue 现有的 API 迫使我们通过选项组织代码,但是有的时候通过逻辑关系组织代码更有意义。
  2. 目前缺少一种简洁且低成本的机制来提取和重用多个组件之间的逻辑。

现在我们先了解一下Compositon API中的reactive和ref

介绍reactive

在Vue2.6中, 出现了一个新的api,Vue.observer,通过这个api可以创建一个响应式的对象,而reactive就和Vue.ovserver的功能基本是一致的。首先我们先来看一个例子

<template>
  <!--在模板中通过state.name使用setup中返回的数据-->
  <div>{{ state.name }}</div>
</template>
<script>
import { reactive } from "vue";
export default {
  setup() {
    // 通过reactive声明一个可响应式的对象
    const state = reactive({
      name: "编程狮"
    });
    // 5秒后将编程狮修改为 W3Cschool
    setTimeout(() => {
      state.name = "W3Cschool";
    }, 1000 * 5);
    // 将state添加到一个对象中然后返回
    return {
      state
    };
  }
};
</script>

上面的例子就是reactive的一个基本的用法,我们通过上面的代码可以看到reactive和Vue.observer声明可响应式对象的方法是很像的,但是他们之间还是存在一些差别的。我们在使用vue2.0的时候,最常见的一个问题就是经常会遇到一些数据明明修改了值,但是界面却并没有刷新,这时候就需要使用Vue.set来解决,这个问题是因为Vue2.0使用的Object.defineProperty无法监听到某些场景比如新增属性,但是到了Vue3.0中通过Proxy将这个问题解决了,所以我们可以直接在reactive声明的对象上面添加新的属性,一起看看下面的例子

<template>
  <div>
    <div>姓名:{{ state.name }}</div>
    <div>网站:{{ state.wz }}</div>
  </div>
</template>
<script>
import { reactive } from "vue";
export default {
  setup() {
    const state = reactive({
      name: "编程狮"
    });
    // 5秒后新增属性wz W3Cschool.cn
    setTimeout(() => {
      state.wz = "W3Cschool.cn";
    }, 1000 * 5);
    return {
      state
    };
  }
};
</script>

上面的例子虽然在state中并没有声明gzh属性,但是在5s后我们可以直接给state添加gzh属性,这时候并不需要使用Vue.set来解决新增属性无法响应的问题。

在上面的代码中,reactive通过传入一个对象然后返回了一个state,需要注意的是state与传入的对象是不用的,reactive对原始的对象并没有进行修改,而是返回了一个全新的对象,返回的对象是Proxy的实例。需要注意的是在项目中尽量去使用reactive返回的响应式对象,而不是原始对象。

const obj = {}
const state = reactive(obj)
// 输出false
console.log(obj === state)

介绍ref

假如现在我们需要在一个函数里面声明用户的信息,那么我们可能会有两种不一样的写法

// 写法1
let name = '编程狮'
let wz = 'W3Cschool.cn'
// 写法2
let userInfo = {
  name: '编程狮',
  wz: 'W3Cschool.cn'
}

上面两种不同的声明方式,我们使用的时候也是不同的,对于写法1我们直接使用变量就可以了,而对于写法2,我们需要写成userInfo.name的方式。我们可以发现userInfo的写法与reactive是比较相似的,而Vue3.0也提供了另一种写法,就像写法1一样,即ref。先来看一个例子。

<template>
  <div>
    <div>姓名:{{ name }}</div>
  </div>
</template>
<script>
import { ref } from "vue";
export default {
  setup() {
    const name = ref("编程狮");
    console.log('姓名',name.value)
    // 5秒后修改name为 W3Cschool
    setTimeout(() => {
      name.value = "W3Cschool";
    }, 1000 * 5);
    return {
      name
    };
  }
};
</script>

通过上面的代码,可以对比出来reactive与ref的区别

  1. reactive传入的是一个对象,返回的是一个响应式对象,而ref传入的是一个基本数据类型(其实引用类型也可以),返回的是传入值的响应式值
  2. reactive获取或修改属性可以直接通过state.prop来操作,而ref返回值需要通过name.value的方式来修改或者读取数据。但是需要注意的是,在template中并不需要通过.value来获取值,这是因为template中已经做了解套。

在Vue3.0优雅的使用v-model

v-model并不是vue3.0新推出的新特性,在Vue2.0中我们已经大量的到了v-model,但是V3和V2还是有很大的区别的。本节我们将主要为大家带来如何在Vue3.0中使用v-model,Vue3.0中的v-model提供了哪些惊喜以及如何在Vue3.0中自定义v-model。

在Vue2.0和Vue3.0中使用v-model

在Vue2.0中如何实现双向数据绑定呢?常用的方式又两种,一种是v-model,另一种是.sync,为什么会有两种呢?这是因为一个组件只能用于一个v-model,但是有的组件需要有多个可以双向响应的数据,所以就出现了.sync。在Vue3.0中为了实现统一,实现了让一个组件可以拥有多个v-model,同时删除掉了.sync。如下面的代码,分别是Vue2.0与Vue3.0使用v-model的区别。

  1. 在Vue2.0中使用v-model
   <template>
     <a-input v-model="value" placeholder="Basic usage" />
   </template>
   <script>
   export default {
     data() {
       return {
         value: '',
       };
     },
   };
   </script>

  1. 在Vue3.0中使用v-model
   <template>
     <!--在vue3.0中,v-model后面需要跟一个modelValue,即要双向绑定的属性名-->
     <a-input v-model:value="value" placeholder="Basic usage" />
   </template>
   <script>
   export default {
     // 在Vue3.0中也可以继续使用`Vue2.0`的写法
     data() {
       return {
         value: '',
       };
     },
   };
   </script>

在vue3.0中,v-model后面需要跟一个modelValue,即要双向绑定的属性名,Vue3.0就是通过给不同的v-model指定不同的modelValue来实现多个v-model。对于v-model的原理,下文将通过自定义v-model来说明。

自定义v-model

使用Vue2.0自定义一个v-model示例

  1. 组件代码
<template>
  <div class="custom-input">
    <input :value="value" @input="$_handleChange" />
  </div>
</template>
<script>
export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  methods: {
    $_handleChange(e) {
      this.$emit('input', e.target.value)
    }
  }
}
</script>

2 . 在代码中使用组件

<template>
      <custom-input v-model="value"></custom-input>
</template>
<script>
 export default {
    data() {
      return {
        value: ''
      }
    }
  }
</script>

在Vue2.0中我们通过为组件设置名为value属性同时触发名为input的事件来实现的v-model,当然也可以通过model来修改属性名和事件名,可以看我以前的文章中有详解。

使用Vue3.0自定义一个v-model示例

  1. 组件代码
   <template>
     <div class="custom-input">
       <input :value="value" @input="_handleChangeValue" />
     </div>
   </template>
   <script>
   export default {
     props: {
       value: {
         type: String,
         default: ""
       }
     },
     name: "CustomInput",
     setup(props, { emit }) {
       function _handleChangeValue(e) {
         // vue3.0 是通过emit事件名为 update:modelValue来更新v-model的
         emit("update:value", e.target.value);
       }
       return {
         _handleChangeValue
       };
     }
   };
   </script>

到了Vue3.0中,因为一个组件支持多个v-model,所以v-model的实现方式有了新的改变。首先我们不需要使用固定的属性名和事件名了,在上例中因为是input输入框,属性名我们依然使用的是value,但是也可以是其他任何的比如name,data,val等等,而在值发生变化后对外暴露的事件名变成了update:value,即update:属性名。而在调用组件的地方也就使用了v-model:属性名来区分不同的v-model。

  1. 在代码中使用组件
<template>
  <!--在使用v-model需要指定modelValue-->
  <custom-input v-model:value="state.inputValue"></custom-input>
</template>
<script>
import { reactive } from "vue";
import CustomInput from "../components/custom-input";
export default {
  name: "Home",
  components: {
    CustomInput
  },
  setup() {
    const state = reactive({
      inputValue: ""
    });
    return {
      state
    };
  }
};
</script>

总结

在本文中我们主要讲解了开发环境的搭建,setup,reactive,ref,v-model等的介绍,同时通过对比Vue3.0与Vue2.0的不同,让大家对Vue3.0有了一定的了解。

以上就是W3Cschool编程狮关于使用Vue3.0,我收获了哪些知识点(一)的相关介绍了,希望对大家有所帮助。

联系我们