react测试组件

This article is part of my studies on how to build sustainable and consistent software. In this post, we will talk about the thinking behind the testing driven development and how to apply this knowledge to simple functions, web accessibility, and React components, mostly with Jest and React Testing Library.

本文是我关于如何构建可持续且一致的软件的研究的一部分。 在本文中,我们将讨论测试驱动开发背后的思想,以及如何将这些知识应用于简单功能,Web可访问性和React组件(主要是通过Jest和React Testing库)。

Automated tests are a big part of software development. It gives us, developers, confidence to ship code to be there, but we increase the confidence that the software will be up and running and working appropriately.

自动化测试是软件开发的重要组成部分。 它给我们开发人员提供了将代码发布到那里的信心,但是,我们增加了该软件可以正常运行并运行的信心。

I began my software career in the Ruby community writing tests from the first day I learned the language. The Ruby (and Rails) community was always strong in the testing automation area. It helped shape my mindset on how to write good software.

从学习语言的第一天起,我就在Ruby社区开始了我的软件职业生涯,编写测试。 Ruby(和Rails)社区在测试自动化领域一直很强大。 它帮助我塑造了如何编写优质软件的心态。

So using Ruby and Rails, I did a lot of backend stuff like background jobs, data structure modeling, API building, and so on. In this scope, the user is always one: the developer user. If building an API, the user would be the developer that's consuming the API. If building the models, the user would be the developer that will use this model.

因此,使用Ruby和Rails,我做了很多后端工作,例如后台作业,数据结构建模,API构建等等。 在此范围内,用户始终是一个:开发人员用户。 如果构建API,则用户将是使用该API的开发人员。 如果构建模型,则用户将是将使用此模型的开发人员。

Now doing a lof of frontend stuff too, after 1 intense year of building PWAs using mostly React and Redux, at first some thoughts came to my mind:

经过一年的紧张工作(主要使用React和Redux构建PWA)之后,现在也正在做一些前端工作,首先想到的是:

  • TDD is impossible when building UI stuff. How do I know if it is a div or span?在构建UI内容​​时,TDD是不可能的。 我怎么知道它是div还是span?
  • Testing can be "complex". Should I shallow or should I mount? Test everything? Ensure every div should be the right place?测试可以是“复杂的”。 我应该变浅还是应该安装? 测试一切? 确保每个div应该在正确的位置?

So I started re-thinking about these testing practices and how to make it productive.

因此,我开始重新考虑这些测试实践以及如何使其富有成效。

TDD is possible. If I'm wondering if I should expect a div or a span, I'm probably testing the wrong thing. Remember: tests should give us the confidence to ship, not necessarily to cover every bit or implementation details. We will dive into this topic later!

TDD是可能的。 如果我想知道是否应该使用div或span,我可能正在测试错误的东西。 请记住:测试应该使我们充满信心,而不一定要覆盖每一个细节或实施细节。 我们稍后将深入探讨该主题!

I want to build tests that:

我想建立以下测试:

  • Ensure the software works appropriately确保软件正常运行
  • Give the confidence to ship code to production放心将代码交付生产
  • Make us think about software design让我们考虑软件设计

And tests that make software:

以及制作软件的测试:

  • Easy to maintain易于维护
  • Easy to refactor易于重构

测试驱动开发 (Testing Driven Development)

TDD shouldn't be complex. It is just a process of 3 steps:

TDD应该不复杂。 这只是一个3个步骤的过程:

  • Make a test做个测试
  • Make it run使它运行
  • Make it right改正它

We start writing a simple test to cover how we expect the software works. Then we make the first implementation of the code (class, function, script, etc). Now the software is behaving. It works as expected. Time to make it right. Time to make it better.

我们开始编写一个简单的测试,以涵盖我们期望软件如何工作。 然后,我们对代码(类,函数,脚本等)进行第一个实现。 现在该软件运行正常。 它按预期工作。 是时候做对了。 是时候让它变得更好。

The goal is a clean code that works. We solve the "that works" problem first and then make the code clean.

目标是有效的干净代码。 我们首先解决“有效”的问题,然后使代码干净。

It is pretty simple. And it should be. I didn't say it is easy. But it is simple, straightforward, just 3 steps. Every time you exercise this process of writing tests first, code after, and then refactoring, you feel more confident.

这很简单。 它应该是。 我没有说这很容易。 但这很简单,直接,只需三个步骤。 每次执行此过程时,首先要编写测试,然后编写代码,然后进行重构,您会感到更加自信。

One good technique when writing your tests first is to think about use cases and simulate how it should be used (as a function, component, or used by a real user).

首先编写测试的一种好方法是考虑用例并模拟应如何使用它(作为功能,组件或由实际用户使用)。

功能 (Functions)

Let's apply this TDD thing into simple functions.

让我们将此TDD事物应用到简单函数中。

Some time ago I was implementing a draft feature for a real estate registration flow. Part of the feature was to show a modal if the user had a not finished real estate. The function we will implement is the one that answers if the user has at least one real estate draft.

不久前,我正在为房地产注册流程实施功能草案。 该功能的一部分是在用户未完成房地产交易时显示模式。 如果用户至少拥有一份房地产草案,我们将实现的功能就是回答这一功能。

So first step: writing the test! Let's think of the use cases of this function. It always responds a boolean: true or false.

所以第一步:编写测试! 让我们考虑一下此函数的用例。 它总是响应一个布尔值:true或false。

  • Has no unsaved real estate draft: false

    没有未保存的房地产汇票: false

  • Has at least one unsaved real estate draft: true

    至少有一份未保存的房地产汇票: true

Let's write the tests that represent this behavior:

让我们编写代表这种行为的测试:

describe('hasRealEstateDraft', () => {describe('with real estate drafts', () => {it('returns true', () => {const realEstateDrafts = [{address: 'São Paulo',status: 'UNSAVED'}];expect(hasRealEstateDraft(realEstateDrafts)).toBeTruthy();});});describe('with not drafts', () => {it('returns false', () => {expect(hasRealEstateDraft([])).toBeFalsy();});});
});

We wrote the tests. But when running it, it shows go red: 2 broken tests because we do not have the function implemented yet.

我们编写了测试。 但是运行它时,它会变成红色:2个失败的测试,因为我们尚未实现该功能。

Second step: make it run! In this case, it is pretty simple. We need to receive this array object and return if it has or hasn't at least one real estate draft.

第二步:使其运行! 在这种情况下,这非常简单。 我们需要接收此数组对象,并返回是否包含至少一个房地产草案。

const hasRealEstateDraft = (realEstateDrafts) => realEstateDrafts.length > 0;

Great! Simple function. Simple tests. We could go to step 3: make it right! But in this case, our function is really simple and we've already got it right.

大! 功能简单。 简单的测试。 我们可以进行第3步:正确设置! 但是在这种情况下,我们的功能确实很简单,并且我们已经做好了。

But now we need the function to get the real estate drafts and pass it to the hasRealEstateDraft.

但是现在我们需要函数来获取房地产草稿并将其传递给hasRealEstateDraft

Which use case we can think of?

我们能想到哪个用例?

  • An empty list of real estates空的房地产清单
  • Only saved real estates仅保存的房地产
  • Only unsaved real estates仅未保存的房地产
  • Mixed: save and unsaved real estates混合:保存和未保存的房地产

Let's write the tests to represent it:

让我们编写测试来表示它:

describe('getRealEstateDrafts', () => {describe('with an empty list', () => {it('returns an empty list', () => {const realEstates = [];expect(getRealEstateDrafts(realEstates)).toMatchObject([]);});});describe('with only unsaved real estates', () => {it('returns the drafts', () => {const realEstates = [{address: 'São Paulo',status: 'UNSAVED'},{address: 'Tokyo',status: 'UNSAVED'}];expect(getRealEstateDrafts(realEstates)).toMatchObject(realEstates);});});describe('with only saved real estates', () => {it('returns an empty list', () => {const realEstates = [{address: 'São Paulo',status: 'SAVED'},{address: 'Tokyo',status: 'SAVED'}];expect(getRealEstateDrafts(realEstates)).toMatchObject([]);});});describe('with saved and unsaved real estates', () => {it('returns the drafts', () => {const realEstates = [{address: 'São Paulo',status: 'SAVED'},{address: 'Tokyo',status: 'UNSAVED'}];expect(getRealEstateDrafts(realEstates)).toMatchObject([{address: 'Tokyo',status: 'UNSAVED'}]);});});
});

Great! We run the tests. It doesn't work.. yet! Now implement the function.

大! 我们运行测试。 它不起作用..呢! 现在实现该功能。

const getRealEstatesDrafts = (realEstates) => {const unsavedRealEstates = realEstates.filter((realEstate) => realEstate.status === 'UNSAVED');return unsavedRealEstates;
};

We simply filter by the real estate status and return it. Great, the tests are passing, the bar is green! And the software is behaving, but we can make it better: step 3!

我们仅按房地产状态进行过滤并返回。 太好了,测试通过了,酒吧是绿色的! 软件运行正常,但是我们可以做得更好:第3步!

What about extracting the anonymous function within the filter function and make the 'UNSAVED' be represented by an enum?

如何在filter函数中提取匿名函数并使'UNSAVED'由枚举表示呢?

const STATUS = {UNSAVED: 'UNSAVED',SAVED: 'SAVED',
};const byUnsaved = (realEstate) => realEstate.status === STATUS.UNSAVED;const getRealEstatesDrafts = (realEstates) => realEstates.filter(byUnsaved);

The tests are still passing and we have a better solution.

测试仍在通过,我们有一个更好的解决方案。

One thing to have in mind here: I isolated the data source from the logic. What does it mean? We get the data from local storage (data source), but we test only the functions responsible to the logic to get drafts and see if it has at least one draft. The functions with the logic, we ensure that it works and it is clean code.

这里要记住的一件事:我将数据源与逻辑隔离。 这是什么意思? 我们从本地存储(数据源)获取数据,但是我们仅测试负责获取草稿的逻辑的功能,并查看其是否至少有一份草稿。 具有逻辑的功能,我们确保它可以正常工作,并且代码干净。

If we get the localStorage inside our functions, it becomes hard to test. So we separate the responsibility and make the tests easy to write. Pure functions are easier to maintain and simpler to write tests.

如果我们将localStorage放入函数中,则很难进行测试。 因此,我们将职责分开,并使测试易于编写。 纯函数更易于维护,编写测试也更简单。

React组件 (React Components)

Now let's talk about React components. Back to the introduction, we talked about writing tests that test implementation details. And now we will see how we can make it better, more sustainable, and have more confidence.

现在让我们谈谈React组件。 回到引言,我们讨论了编写测试实现细节的测试。 现在,我们将看到如何使它变得更好,更具可持续性并更有信心。

A couple of days ago I was planning to build the new onboarding information for the real estate owner. It is basically a bunch of pages with the same design, but it changes the icon, title, and description of the pages.

几天前,我正计划为房地产所有者建立新的入职信息。 它基本上是一堆具有相同设计的页面,但是它更改了页面的图标,标题和描述。

I wanted to build just one component: Content and pass the information needed to render the correct icon, title, and description. I would pass businessContext and step as props and it would render the correct content to the onboarding page.

我只想构建一个组件: Content并传递渲染正确的图标,标题和描述所需的信息。 我将把businessContextstep作为道具传递,它将正确的内容呈现给入职页面。

We don't want to know if we will render a div or paragraph tag. Our test needs to ensure that for a given business context and step, the correct content will be there. So I came with these use cases:

我们不想知道是否将渲染div或段落标签。 我们的测试需要确保对于给定的业务环境和步骤,正确的内容在那里。 因此,我想到了这些用例:

  • The first step of the rental business context租赁业务环境的第一步
  • Last step of the rental business context租赁业务环境的最后一步
  • The first step of the sales business context销售业务环境的第一步
  • Last step of the sales business context销售业务环境的最后一步

Let's see the tests:

让我们看一下测试:

describe('Content', () => {describe('in the rental context', () => {const defaultProps = {businessContext: BUSINESS_CONTEXT.RENTAL};it('renders the title and description for the first step', () => {const step = 0;const { getByText } = render(<Content {...defaultProps} step={step} />);expect(getByText('the first step title')).toBeInTheDocument();expect(getByText('the first step description')).toBeInTheDocument();});it('renders the title and description for the forth step', () => {const step = 3;const { getByText } = render(<Content {...defaultProps} step={step} />);expect(getByText('the last step title')).toBeInTheDocument();expect(getByText('the last step description')).toBeInTheDocument();});});describe('in the sales context', () => {const defaultProps = {businessContext: BUSINESS_CONTEXT.SALE};it('renders the title and description for the first step', () => {const step = 0;const { getByText } = render(<Content {...defaultProps} step={step} />);expect(getByText('the first step title')).toBeInTheDocument();expect(getByText('the first step description')).toBeInTheDocument();});it('renders the title and description for the last step', () => {const step = 6;const { getByText } = render(<Content {...defaultProps} step={step} />);expect(getByText('the last step title')).toBeInTheDocument();expect(getByText('the last step description')).toBeInTheDocument();});});
});

We have one describe block for each business context and an it block for each step. I also created an accessibility test to ensure the component we are building is accessible.

对于每个业务上下文,我们都有一个describe块,对于每个步骤都有一个it块。 我还创建了可访问性测试,以确保我们正在构建的组件可访问。

it('has not accessibility violations', async () => {const props = {businessContext: BUSINESS_CONTEXT.SALE,step: 0,};const { container } = render(<Content {...props} />);const results = await axe(container);expect(results).toHaveNoViolations();
});

Now we need to make it run! Basically, the UI part of this component is just the icon, the title, and the description. Something like:

现在我们需要使其运行! 基本上,此组件的UI部分只是图标,标题和描述。 就像是:

<Fragment><Icon /><h1>{title}</h1><p>{description}</p>
</Fragment>

We just need to build the logic to get all these correct data. As I have the businessContext and the step in this component, I wanted to just do something like

我们只需要构建逻辑来获取所有这些正确的数据。 因为有了businessContext和该组件中的step ,所以我想做一些类似的事情

content[businessContext][step]

And it gets the correct content. So I built a data structure to work that way.

并且它获取正确的内容。 因此,我建立了一个数据结构以这种方式工作。

const onboardingStepsContent = {alugar: {0: {Icon: Home,title: 'first step title',description: 'first step description',},// ...},vender: {0: {Icon: Home,title: 'first step title',description: 'first step description',},// ...},
};

It's just an object with the first keys as the business context data and for each business context, it has keys that represent each step of the onboarding. And our component would be:

它只是一个具有第一个键作为业务上下文数据的对象,并且对于每个业务上下文,它都有代表入门过程中每个步骤的键。 我们的组件将是:

const Content = ({ businessContext, step }) => {const onboardingStepsContent = {alugar: {0: {Icon: Home,title: 'first step title',description: 'first step description',},// ...},vender: {0: {Icon: Home,title: 'first step title',description: 'first step description',},// ...},};const { Icon, title, description } = onboardingStepsContent[businessContext][step];return (<Fragment><Icon /><h1>{title}</h1><p>{description}</p></Fragment>);
};

It works! Now let's make it better. I wanted to make the get content more resilient. What if it receives a step that doesn't exist for example? These are the use cases:

有用! 现在,让它变得更好。 我想使获取的内容更具弹性。 例如,如果收到不存在的步骤怎么办? 这些是用例:

  • The first step of the rental business context租赁业务环境的第一步
  • Last step of the rental business context租赁业务环境的最后一步
  • The first step of the sales business context销售业务环境的第一步
  • Last step of the sales business context销售业务环境的最后一步
  • Inexistent step of the rental business context租赁业务环境中不存在的步骤
  • Inexistent step of the sales business context销售业务环境中不存在的步骤

Let's see the tests:

让我们看一下测试:

describe('getOnboardingStepContent', () => {describe('when it receives existent businessContext and step', () => {it('returns the correct content for the step in "alugar" businessContext', () => {const businessContext = 'alugar';const step = 0;expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({Icon: Home,title: 'first step title',description: 'first step description',});});it('returns the correct content for the step in "vender" businessContext', () => {const businessContext = 'vender';const step = 5;expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({Icon: ContractSign,title: 'last step title',description: 'last step description',});});});describe('when it receives inexistent step for a given businessContext', () => {it('returns the first step of "alugar" businessContext', () => {const businessContext = 'alugar';const step = 7;expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({Icon: Home,title: 'first step title',description: 'first step description',});});it('returns the first step of "vender" businessContext', () => {const businessContext = 'vender';const step = 10;expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({Icon: Home,title: 'first step title',description: 'first step description',});});});
});

Great! Now let's build our getOnboardingStepContent function to handle this logic.

大! 现在,让我们构建getOnboardingStepContent函数来处理此逻辑。

const getOnboardingStepContent = ({ businessContext, step }) => {const content = onboardingStepsContent[businessContext][step];return content? content: onboardingStepsContent[businessContext][0];
};

We try to get content. If we have it, just return it. If we don't have it, return the first step of the onboarding.

我们尝试获取内容。 如果有,请退回。 如果没有,请返回入职的第一步。

Neat! But we can improve it. What about using the || operator? No need to assign to a variable, no need to use a ternary.

整齐! 但是我们可以改善它。 怎样使用|| 操作员? 无需分配变量,无需使用三进制。

const getOnboardingStepContent = ({ businessContext, step }) =>onboardingStepsContent[businessContext][step] ||onboardingStepsContent[businessContext][0];

If it finds the content, just return it. If it didn't find, return the first step of the given business context.

如果找到内容,则将其返回。 如果找不到,请返回给定业务环境的第一步。

Now our component is only UI.

现在,我们的组件只是UI。

const Content = ({ businessContext, step }) => {const {Icon,title,description,} = getOnboardingStepContent({ businessContext, step });return (<Fragment><Icon /><h1>{title}</h1><p>{description}</p></Fragment>);
};


最后的想法 (Final thoughts)

I like to think deeply about the tests I'm writing. And I think all developers should too. It does need to give us the confidence to ship more code and have a bigger impact on the market we are working on.

我喜欢对正在编写的测试进行深入思考。 而且我认为所有开发人员也应该这样做。 它确实需要使我们有信心发布更多代码,并对我们正在开发的市场产生更大的影响。

Like all code, when we write smelly and bad tests, it influences other developers to follow the "pattern". It gets worse in bigger companies. It scales badly. But we are always able to stop, reflect on the status quo, and take action to make it better.

像所有代码一样,当我们编写有臭味和不良的测试时,它会影响其他开发人员遵循“模式”。 在大公司中情况变得更糟。 它缩放严重。 但是我们总是能够停下来,反思现状,并采取行动使其变得更好。

I shared some resources I found interesting reading and learning. If you want to get a great introduction to TDD, I really recommend TDD by example, a book from Kent Beck.

我分享了一些有趣的阅读和学习资源。 如果您想对TDD进行很好的介绍,我真的以示例的方式推荐了TDD,这是一本肯特·贝克(Kent Beck)的书。

I will write more about tests, TDD, and React. And how we can make our software more consistent and feel safe when shipping code to production.

我将写更多关于测试,TDD和React的内容。 以及在将代码发布到生产环境时,如何使我们的软件更一致,更安全。

依存关系 (Dependencies)

  • jest-axe: jest matchers for testing accessibility

    jest-axe :用于测试可访问性的jest匹配器

  • testing-library/react-testing-library: testing utilities to help test react

    testing-library / react-testing-library :测试实用程序以帮助测试React

  • testing-library/jest-dom: jest matchers to test the state of the DOM

    testing-library / jest-dom :用于测试DOM状态的玩笑匹配者

资源资源 (Resources)

  • Beginner JavaScript Course

    初学者JavaScript课程

  • React for Beginners Course

    对初学者的React课程

  • Advanced React Course

    高级React课程

  • ES6 Course

    ES6课程

  • The Road to learn React

    学习React之路

  • JavaScript Fundamentals Before Learning React

    学习React之前JavaScript基础

  • Reintroducing React: V16 and Beyond

    重新引入React:V16及更高版本

  • Advanced React Patterns With Hooks

    带钩的高级React模式

  • Practical Redux

    实用的Redux

  • JavaScript Course by OneMonth

    OneMonthJavaScript课程

  • Test Driven Development by example book by Kent Beck

    肯特·贝克(Kent Beck)的示例书进行测试驱动开发

  • Testable Javascript book by Mark Ethan Trostler

    Mark Ethan Trostler编写的可测试的Java脚本书

  • Blog post source code

    博客文章源代码

  • Testing React applications with jest, jest-axe, and react-testing-library

    用玩笑,玩笑轴和React-Testing-Library测试React应用程序

  • Modern React testing, part 3: Jest and React Testing Library

    现代React测试,第3部分:Jest和React测试库

  • What we found when we tested tools on the world’s least-accessible webpage

    在世界上访问最少的网页上测试工具时发现的东西

  • Testing Implementation Details

    测试实施细节

  • Learn React by building an App

    通过构建应用来学习React

You can other articles like this on my blog.

您可以像这样的其他文章 在我的博客上 。

翻译自: https://www.freecodecamp.org/news/tdd-functions-and-react-components/

react测试组件

react测试组件_测试驱动的开发,功能和React组件相关推荐

  1. 测试驱动开发 测试前移_测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做...

    测试驱动开发 测试前移 by Navdeep Singh 通过Navdeep Singh 测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做 (Test-driven developmen ...

  2. 测试驱动开发 测试前移_测试驱动开发:它是什么,什么不是。

    测试驱动开发 测试前移 by Andrea Koutifaris 由Andrea Koutifaris Test driven development has become popular over ...

  3. pls-00302: 必须声明 组件_手把手教你开发vue组件库

    前言 Vue是一套用于构建用户界面的渐进式框架,目前有越来越多的开发者在学习和使用.在笔者写完 从0到1教你搭建前端团队的组件系统 之后很多朋友希望了解一下如何搭建基于vue的组件系统,所以作为这篇文 ...

  4. 怎样编写测试类测试分支_测试技巧–不编写测试

    怎样编写测试类测试分支 对此没有太多疑问,测试代码的方式是一个有争议的问题. 不同的测试技术由于各种原因(包括企业文化,经验和总体心理观点)而受到不同开发人员的青睐. 例如,您可能更喜欢编写经典的单元 ...

  5. ssis组件_用于SSIS的Melissa Data Quality免费组件

    ssis组件 In this article, we will talk briefly about data quality in SQL Server. Then, we will give a ...

  6. react node服务器_适用于初学者的服务器上的React:构建通用的React和Node应用程序...

    react node服务器 In this article we are going to learn how to build a simple "Universal JavaScript ...

  7. react 生命挂钩_如何在GraphQL API中使用React挂钩来管理状态

    react 生命挂钩 In this blog post, we are going to learn - 在这篇博客中,我们将学习- What React hooks are什么是React钩子 H ...

  8. 微服务pact测试框架_消费者驱动的Pact和Spring Boot测试

    微服务pact测试框架 最近,我的一位同事偶然发现了Pact.io ,我们目前的应用程序已扩展到 50多种服务,并且我们开始出现一些集成测试失败和脆弱的开发/验收测试环境. 因此,我们决定研究尝试与此 ...

  9. 测试驱动开发 测试前移_测试驱动陷阱,第2部分

    测试驱动开发 测试前移 单元测试中单元的故事 在本文的上半部分 ,您可能会看到一些不好但很受欢迎的测试示例. 但是我不是一个专业的批评家(也被称为"巨魔"或"仇恨者&qu ...

最新文章

  1. mfc如何将一个数组中的字节数据用串口发送出去_RS232串口多机通信
  2. cute editor详细设置
  3. 用jquery + iframe實現iframe子頁面加載完前的緩沖效果
  4. JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结
  5. linux 3.10中完成量的使用
  6. 触发器-当表1插入数据时将表1的数据插入表2
  7. MyBatis3源码解析(3)查询语句执行
  8. ef 子表和父表不同时保存_canon粉不懂镜头参数?我只能嘲笑你
  9. pyecharts基础图表汇总(三国演义可视化)
  10. 防火墙添加ip白名单_宝塔防火墙IP白名单添加/导入知道创宇云CDN节点IP段
  11. Linux上的视频播放及MPV播放器与SVP4插帧
  12. 中级微观经济学:Chap 15 市场需求
  13. 入行 AI,如何选个脚踏实地的岗位?
  14. 网易邮箱注册界面设计 html
  15. gdb、ida调试笔记
  16. 7-39 抽卡游戏 (30分) 本题的灵感来源于一个古典的概率模型。 AliceAliceAlice 在一个卡池里抽卡,里面有 xxx 张 sss 卡和 yyy 张 aaa 卡。 AliceAlice
  17. 【Electron】使用vue-electron+electron-store创建项目,NSIS打包为exe可安装文件
  18. 令人十分期待的ECShopX BBC商城开放终身免费商用
  19. XinChaCha Trust SSL Organization Validated
  20. 2021年05月软件设计师真题透析

热门文章

  1. CSS之布局(盒模型)
  2. c++窗口管理系统是什么_优秀的食堂管理系统让你对校园生活更充满希望
  3. tomcat限速_WEB服务的下载限速(二)(限速模块安装与配置)
  4. bootstrap轮播如何支持移动端滑动手势
  5. [学习笔记]最小割之最小点权覆盖最大点权独立集
  6. Elasticsearch 参考指南(脚本)
  7. bzoj1227: [SDOI2009]虔诚的墓主人(树状数组,组合数)
  8. 二十二、新人成才之路《做人七项原则 做一个节俭惜福的人》
  9. spring boot 实战 / 可执行war启动参数详解
  10. Fragment 和 FragmentActivity的使用