Sentry 开发者贡献指南 - 前端(ReactJS生态)

简介: Sentry 开发者贡献指南 - 前端(ReactJS生态)

前端手册



本指南涵盖了我们如何在 Sentry 编写前端代码, 并特别关注 Sentry 和 Getsentry 代码库。它假设您使用的是 eslint-config-sentry 概述的 eslint 规则;因此,这里不会讨论由这些 linting 规则强制执行的代码风格。


目录结构


前端代码库当前位于 sentry 中的 src/sentry/static/sentry/appgetentry 中的 static/getsentry 下。(我们打算在未来与 static/sentry 保持一致。)


文件夹和文件结构


文件命名

  • 根据模块的功能或类的使用方式或使用它们的应用程序部分,有意义地命名文件。
  • 除非必要,否则不要使用前缀或后缀(即 dataScrubbingEditModaldataScrubbingAddModal),而是使用像 dataScrubbing/editModal 这样的名称。


使用 index.(j|t)?(sx)


在文件夹中有一个 index 文件提供了一种隐式导入主文件而不指定它的方法

index 文件的使用应遵循以下规则:


  • 如果创建文件夹来对一起使用的组件进行分组,并且有一个入口点组件,它使用分组内的组件(examplesavataridBadge)。入口点组件应该是 index 文件。
  • 不要使用 index.(j|t)?(sx) 文件,如果文件夹包含在应用程序的其他部分使用的组件,与入口点文件无关。(即,actionCreatorspanels
  • 不要仅仅为了重新导出而使用 index 文件。更倾向于导入单个组件。


React


定义 React 组件


新组件在需要访问 this 时使用 class 语法,以及类字段+箭头函数方法定义。


class Note extends React.Component {
  static propTypes = {
    author: PropTypes.object.isRequired,
    onEdit: PropTypes.func.isRequired,
  };
  // 请注意,方法是使用箭头函数类字段定义的(绑定“this”)
  handleChange = value => {
    let user = ConfigStore.get('user');
    if (user.isSuperuser) {
      this.props.onEdit(value);
    }
  };
  render() {
    let {content} = this.props; // 对 props 使用解构赋值
    return <div onChange={this.handleChange}>{content}</div>;
  }
}
export default Note;


一些较旧的组件使用 createReactClassmixins,但这已被弃用。


组件与视图


app/components/app/views 文件夹都包含 React 组件。

  • 使用通常不会在代码库的其他部分重用的 UI 视图。
  • 使用设计为高度可重用的 UI 组件。

组件应该有一个关联的 .stories.js 文件来记录它应该如何使用。

使用 yarn storybook 在本地运行 Storybook 或在 https://storybook.getsentry.net/ 上查看托管版本


PropTypes


使用它们,要明确,尽可能使用共享的自定义属性。

更倾向 Proptypes.arrayOf 而不是 PropTypes.arrayPropTypes.shape 而不是 PropTypes.object

如果你使用一组重要的、定义良好的 key(你的组件依赖)传递对象,那么使用 PropTypes.shape 显式定义它们:


PropTypes.shape({
  username: PropTypes.string.isRequired,
  email: PropTypes.string
})


如果您要重复使用自定义 prop-type 或传递常见的共享 shape(如 organizationprojectuser), 请确保从我们有用的自定义集合中导入 proptype


事件处理程序


我们使用不同的前缀来更好地区分事件处理程序事件回调属性

对事件处理程序使用 handle 前缀,例如:


<Button onClick={this.handleDelete}/>


对于传递给组件的事件回调属性,请使用 on 前缀,例如:


<Button onClick={this.props.onDelete}>


CSS 和 Emotion


  • 使用 Emotion,使用 theme 对象。
  • 最好的样式是您不编写的样式 - 尽可能使用现有组件。
  • 新代码应该使用 css-in-js 库 e m o t i o n - 它允许您将样式绑定到元素而无需全局选择器的间接性。你甚至不需要打开另一个文件!
  • 从 props.theme 获取常量(z-indexes, paddings, colors


import styled from 'react-emotion';
const SomeComponent = styled('div')`
  border-radius: 1.45em;
  font-weight: bold;
  z-index: ${p => p.theme.zIndex.modal};
  padding: ${p => p.theme.grid}px ${p => p.theme.grid * 2}px;
  border: 1px solid ${p => p.theme.borderLight};
  color: ${p => p.theme.purple};
  box-shadow: ${p => p.theme.dropShadowHeavy};
`;
export default SomeComponent;


  • 请注意,reflexbox(例如FlexBox)已被弃用,请避免在新代码中使用。

stylelint 错误


"No duplicate selectors"


当您使用样式组件(styled component)作为选择器时会发生这种情况,我们需要通过使用注释来辅助 linter 来告诉 stylelint 我们正在插入的是一个选择器。例如


const ButtonBar = styled("div")`
  ${/* sc-selector */Button) {
     border-radius: 0;
  }
`;


有关其他标签和更多信息,请参阅。


状态管理


我们目前使用 Reflux 来管理全局状态。

Reflux 实现了 Flux 概述的单向数据流模式。 Store 注册在 app/stores 下,用于存储应用程序使用的各种数据。 Action 需要在 app/actions 下注册。我们使用 action creator 函数(在 app/actionCreators 下)来分派 actionReflux store 监听 action 并相应地更新自己。


我们目前正在探索 Reflux 库的替代方案以供将来使用。


测试


我们正在远离 Enzyme,转而使用 React Testing Library。有关 RTL 提示,请查看此页面。


注意:你的文件名必须是 .spec.jsx 否则 jest 不会运行它!

我们在 setup.js 中定义了有用的 fixtures,使用这些!如果您以重复的方式定义模拟数据,则可能值得添加此文件。routerContext 是一种特别有用的方法,用于提供大多数视图所依赖的上下文对象。


Client.addMockResponse 是模拟 API 请求的最佳方式。这是我们的代码, 所以如果它让您感到困惑,只需将 console.log() 语句放入其逻辑中即可!

我们测试环境中的一个重要问题是,enzyme 修改了 react 生命周期的许多方面以同步评估(即使它们通常是异步的)。当您触发某些逻辑并且没有立即在您的断言逻辑中反映出来时,这可能会使您陷入一种虚假的安全感。


标记您的测试方法 async 并使用 await tick(); 实用程序可以让事件循环刷新运行事件并修复此问题:


wrapper.find('ExpandButton').simulate('click');
await tick();
expect(wrapper.find('CommitRow')).toHaveLength(2);


选择器


如果您正在编写 jest 测试,您可以使用 Component(和 Styled Component)名称作为选择器。此外,如果您需要使用 DOM 查询选择器,请使用 data-test-id 而不是类名。我们目前没有,但我们可以在构建过程中使用 babel 去除它。


测试中未定义的 theme 属性


而不是使用来自 enzymemount() ...使用这个:import {mountWithTheme} from 'sentry-test/enzyme' 以便被测组件用 <ThemeProvider>


Babel 语法插件


我们决定只使用处于 stage 3(或更高版本)的 ECMAScript 提案(参见 TC39 提案)。此外,因为我们正在迁移到 typescript,我们将与他们的编译器支持的内容保持一致。唯一的例外是装饰器。



新语法


可选链


可选链 帮助我们访问 [嵌套] 对象, 而无需在每个属性/方法访问之前检查是否存在。如果我们尝试访问 undefinednull 对象的属性,它将停止并返回 undefined


语法


可选链操作符拼写为 ?.。它可能出现在三个位置:


obj?.prop       // 可选的静态属性访问
obj?.[expr]     // 可选的动态属性访问
func?.(...args) // 可选的函数或方法调用


来自 https://github.com/tc39/proposal-optional-chaining


空值合并


这是一种设置“默认”值的方法。例如:以前你会做类似的事情


let x = volume || 0.5;


这是一个问题,因为 0volume 的有效值,但因为它的计算结果为 false -y,我们不会使表达式短路,并且 x 的值为 0.5


如果我们使用空值合并


let x = volume ?? 0.5


如果 volumenullundefined,它只会默认为 0.5


语法


基本情况。如果表达式在 ?? 的左侧运算符计算为 undefinednull,则返回其右侧。


const response = {
  settings: {
    nullValue: null,
    height: 400,
    animationDuration: 0,
    headerText: '',
    showSplashScreen: false
  }
};
const undefinedValue = response.settings.undefinedValue ?? 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue ?? 'some other default'; // result: 'some other default'
const headerText = response.settings.headerText ?? 'Hello, world!'; // result: ''
const animationDuration = response.settings.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // result: false


From https://github.com/tc39/proposal-nullish-coalescing

Lodash


确保不要使用默认的 lodash 包导入 lodash 实用程序。有一个 eslint 规则来确保这不会发生。而是直接导入实用程序,例如 import isEqual from 'lodash/isEqual';


以前我们使用了 lodash-webpack-plugin 和 babel-plugin-lodash 的组合, 但是在尝试使用新的 lodash 实用程序(例如这个 PR)时很容易忽略这些插件和配置。通过 webpack tree shakingeslint 强制执行,我们应该能够保持合理的包大小。


有关更多信息,请参阅此 PR。

我们更喜欢使用可选链空值合并而不是来自 lodash/getget


Typescript


  • Typing DefaultProps


迁移指南


  • Grid-Emotion


Storybook Styleguide


引用其文档,“Storybook 是用于 UI 组件的 UI 开发环境。有了它,您可以可视化 UI 组件的不同状态并以交互方式开发它们。”

更多细节在这里:


我们使用它吗?


是的!我们将 Storybook 用于 getsentry/sentry 项目。 Storybook 的配置可以在 https://github.com/getsentry/sentry/tree/master/.storybook 中找到。

要在本地运行 Storybook,请在 getsentry/sentry 存储库的根目录中运行 npm run storybook


它部署在某个地方吗?


Sentry 的 Storybook 是使用 Vercel 构建和部署的。每个 Pull Request 都有自己的部署,每次推送到主分支都会部署到 https://storybook.sentry.dev。


Typing DefaultProps


由于 Typescript 3.0 默认 props 可以更简单地输入。有几种不同的方法适合不同的场景。


类(Class)组件


import React from 'react';
type DefaultProps = {
  size: 'Small' | 'Medium' | 'Large'; // 这些不应标记为可选
};
// 没有 Partial<DefaultProps>
type Props = DefaultProps & {
  name: string;
  codename?: string;
};
class Planet extends React.Component<Props> {
  // 没有 Partial<Props> 因为它会将所有内容标记为可选
  static defaultProps: DefaultProps = {
    size: 'Medium',
  };
  render() {
    const {name, size, codename} = this.props;
    return (
      <p>
        {name} is a {size.toLowerCase()} planet.
        {codename && ` Its codename is ${codename}`}
      </p>
    );
  }
}
const planet = <Planet name="Mars" />;


或在 typeof 的帮助下:


import React from 'react';
const defaultProps = {
  size: 'Medium' as 'Small' | 'Medium' | 'Large',
};
type Props = {
  name: string;
  codename?: string;
} & typeof defaultProps;
// 没有 Partial<typeof defaultProps> 因为它会将所有内容标记为可选
class Planet extends React.Component<Props> {
  static defaultProps = defaultProps;
  render() {
    const {name, size, codename} = this.props;
    return (
      <p>
        {name} is a {size.toLowerCase()} planet. Its color is{' '}
        {codename && ` Its codename is ${codename}`}
      </p>
    );
  }
}
const planet = <Planet name="Mars" />;


函数式(Function)组件


import React from 'react';
// 函数组件上的 defaultProps 将在未来停止使用
// https://twitter.com/dan_abramov/status/1133878326358171650
// https://github.com/reactjs/rfcs/pull/107
// 我们应该使用默认参数
type Props = {
  name: string;
  size?: 'Small' | 'Medium' | 'Large'; // 具有 es6 默认参数的属性应标记为可选
  codename?: string;
};
// 共识是输入解构的 Props 比使用 React.FC<Props> 稍微好一点
// https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#function-components
const Planet = ({name, size = 'Medium', codename}: Props) => {
  return (
    <p>
      {name} is a {size.toLowerCase()} planet.
      {codename && ` Its codename is ${codename}`}
    </p>
  );
};
const planet = <Planet name="Mars" />;


参考


  • Typescript 3.0 Release notes
  • Stack Overflow question on typing default props


使用 Hooks


为了使组件更易于重用和更易于理解,ReactReact 生态系统一直趋向于函数式组件和 hooksHooks 是一种向功能组件添加状态副作用的便捷方式。它们还为库提供了一种公开行为的便捷方式。


虽然我们通常支持 hooks,但我们有一些关于 hooks 应该如何与 Sentry 前端一起使用的建议。


使用库中的 hooks


如果一个库提供了 hooks,你应该使用它们。通常,这将是使用库的唯一方法。例如,dnd-kit 通过钩子公开了它的所有原语(primitives),我们应该按照预期的方式使用该库。


我们不喜欢使用不用 hooks 的库。相反,与具有更大、更复杂的 API 或更大的包大小的库相比, 更喜欢具有更清晰、更简单的 API 和更小的包大小的库。


使用 react 的内置 hooks


useState, useMemo, useCallback, useContextuseRefhooks 在任何函数式组件中都是受欢迎的。在需要少量状态或访问 react 原语(如引用和上下文)的展示组件中,它们通常是一个不错的选择。例如,具有滑出(slide-out)可展开状态(expandable state)的组件。


useEffecthook 更复杂,您需要小心地跟踪您的依赖项并确保通过清理回调取消订阅。应避免 useEffect 的复杂链式应用程序,此时 'controller' 组件应保持基于类(class)。


同样,useReducer 钩子与目前尚未确定的状态管理重叠。我们希望避免 又一个 状态管理模式,因此此时避免使用useReducer


使用 context


当我们计划远离 Reflux 的路径时,useContexthook 提供了一个更简单的实现选项来共享状态和行为。当您需要创建新的共享状态源时,请考虑使用 contextuseContext 而不是 Reflux。此外,可以利用虫洞状态管理模式来公开共享状态突变函数


使用自定义 hooks


可以创建自定义 hooks 来共享应用程序中的可重用逻辑。创建自定义 hook 时,函数名称必须遵循约定,以 “use” 开头(例如 useTheme), 并且可以在自定义 hooks 内调用其他 hooks


注意 hooks 的规则和注意事项


React hooks 有一些规则。请注意 hooks 创建的规则和限制。我们使用 ESLint 规则来防止大多数 hook 规则被非法侵入。

此外,我们建议您尽量少使用 useEffect。使用多个 useEffect 回调表示您有一个高度有状态的组件, 您应该使用类(class)组件来代替。


我们的基础视图组件仍然是基于类的


我们的基础视图组件(AsyncViewAsyncComponent)是基于类的,并且会持续很长时间。在构建视图时请记住这一点。您将需要额外的 wrapper 组件来访问 hooks 或将 hook state 转换为您的 AsyncComponentprops


不要为 hooks 重写


虽然 hooks 可以在新代码中符合人体工程学,但我们应该避免重写现有代码以利用 hooks。重写需要时间,使我们面临风险,并且为最终用户提供的价值很小。

如果您需要重新设计一个组件以使用库中的 hooks,那么还可以考虑从一个类转换为一个函数组件。


使用 React Testing Library


我们正在将我们的测试从 Enzyme 转换为 React Testing Library。在本指南中,您将找到遵循最佳实践和避免常见陷阱的技巧。


我们有两个 ESLint 规则来帮助解决这个问题:

  • eslint-plugin-jest-dom
  • eslint-plugin-testing-library

我们努力以一种与应用程序使用方式非常相似的方式编写测试。

我们不是处理渲染组件的实例,而是以与用户相同的方式查询 DOM。我们通过 label 文本找到表单元素(就像用户一样),我们从他们的文本中找到链接和按钮(就像用户一样)。


作为此目标的一部分,我们避免测试实现细节,因此重构(更改实现但不是功能)不会破坏测试。


我们通常赞成用例覆盖而不是代码覆盖


查询


  • 尽可能使用 getBy...
  • 仅在检查不存在时使用 queryBy...
  • 仅当期望元素在可能不会立即发生的 DOM 更改后出现时才使用 await findBy...

为确保测试类似于用户与我们的代码交互的方式,我们建议使用以下优先级进行查询:

  1. getByRole - 这应该是几乎所有东西的首选选择器。

作为这个选择器的一个很好的奖励,我们确保我们的应用程序是可访问的。它很可能与 name 选项 getByRole('button', {name: /save/i}) 一起使用。 name 通常是表单元素的 labelbutton 的文本内容,或 aria-label 属性的值。如果不确定,请使用 logRoles 功能 或查阅可用角色列表。

  1. getByLabelText/getByPlaceholderText - 用户使用 label 文本查找表单元素,因此在测试表单时首选此选项。
  2. getByText - 在表单之外,文本内容是用户查找元素的主要方式。此方法可用于查找非交互式元素(如 divspanparagraph)。
  3. getByTestId - 因为这不反映用户如何与应用交互,所以只推荐用于不能使用任何其他选择器的情况

如果您仍然无法决定使用哪个查询, 请查看 testing-playground.com 以及 screen.logTestingPlaygroundURL() 及其浏览器扩展。

不要忘记,你可以在测试中的任何地方放置 screen.debug() 来查看当前的 DOM

在官方文档中阅读有关查询的更多信息。


技巧


避免从 render 方法中解构查询函数,而是使用 screen(examples)。当您添加/删除您需要的查询时,您不必使 render 调用解构保持最新。您只需要输入 screen 并让您的编辑器的自动完成功能处理其余的工作。


import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
const { getByRole } = mountWithTheme(<Example />);
const errorMessageNode = getByRole("alert");
// ✅
mountWithTheme(<Example />);
const errorMessageNode = screen.getByRole("alert");


除了检查不存在(examples)之外,避免将 queryBy... 用于任何事情。如果没有找到元素,getBy...findBy... 变量将抛出更有用的错误消息。


import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
expect(screen.queryByRole("alert")).toBeInTheDocument();
// ✅
mountWithTheme(<Example />);
expect(screen.getByRole("alert")).toBeInTheDocument();
expect(screen.queryByRole("button")).not.toBeInTheDocument();


避免使用 waitFor 等待出现,而是使用 findBy...(examples)。这两个基本上是等价的(findBy... 甚至在其里面使用了 waitFor),但是 findBy... 更简单,我们得到的错误信息也会更好。


import {
  mountWithTheme,
  screen,
  waitFor,
} from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
await waitFor(() => {
  expect(screen.getByRole("alert")).toBeInTheDocument();
});
// ✅
mountWithTheme(<Example />);
expect(await screen.findByRole("alert")).toBeInTheDocument();


避免使用 waitFor 等待消失,使用 waitForElementToBeRemoved 代替(examples)。

后者使用 MutationObserver,这比使用 waitFor 定期轮询 DOM 更有效。


import {
  mountWithTheme,
  screen,
  waitFor,
  waitForElementToBeRemoved,
} from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
await waitFor(() =>
  expect(screen.queryByRole("alert")).not.toBeInTheDocument()
);
// ✅
mountWithTheme(<Example />);
await waitForElementToBeRemoved(() => screen.getByRole("alert"));


更喜欢使用 jest-dom 断言(examples)。使用这些推荐的断言的优点是更好的错误消息、整体语义、一致性和统一性。


import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
expect(screen.getByRole("alert")).toBeTruthy();
expect(screen.getByRole("alert").textContent).toEqual("abc");
expect(screen.queryByRole("button")).toBeFalsy();
expect(screen.queryByRole("button")).toBeNull();
// ✅
mountWithTheme(<Example />);
expect(screen.getByRole("alert")).toBeInTheDocument();
expect(screen.getByRole("alert")).toHaveTextContent("abc");
expect(screen.queryByRole("button")).not.toBeInTheDocument();


按文本搜索时,最好使用不区分大小写的正则表达式。它将使测试更能适应变化。


import { mountWithTheme, screen } from "sentry-test/reactTestingLibrary";
// ❌
mountWithTheme(<Example />);
expect(screen.getByText("Hello World")).toBeInTheDocument();
// ✅
mountWithTheme(<Example />);
expect(screen.getByText(/hello world/i)).toBeInTheDocument();


尽可能在 fireEvent 上使用 userEventuserEvent 来自 @testing-library/user-event 包,它构建在 fireEvent 之上,但它提供了几种更类似于用户交互的方法。


// ❌
import {
  mountWithTheme,
  screen,
  fireEvent,
} from "sentry-test/reactTestingLibrary";
mountWithTheme(<Example />);
fireEvent.change(screen.getByLabelText("Search by name"), {
  target: { value: "sentry" },
});
// ✅
import {
  mountWithTheme,
  screen,
  userEvent,
} from "sentry-test/reactTestingLibrary";
mountWithTheme(<Example />);
userEvent.type(screen.getByLabelText("Search by name"), "sentry");


迁移 - grid-emotion


grid-emotion 已经被弃用一年多了,新项目是 reflexbox。为了升级到最新版本的 emotion,我们需要迁移出 grid-emotion

要迁移,请使用 emotion 将导入的 <Flex><Box> 组件替换为带样式的组件。


组件


用下面的替换组件,然后删除必要的 props 并移动到 styled component

<Flex>


const Flex = styled('div')`
  display: flex;
`;


<Box>


const Box = styled('div')`
`;


属性


如果您正在修改导出的组件,请确保通过该组件的代码库进行 grep 以确保它没有被渲染为特定于 grid-emotion 的附加属性。示例是<Panel> 组件。


margin 和 padding


Margin 属性 以 m 开头,以 p 填充。下面的例子将使用 margin 作为例子


旧 (grid-emotion) 新 (css/emotion/styled)
m={2} margin: ${space(2);
mx={2} margin-left: ${space(2); margin-right: ${space(2)};
my={2} margin-top: ${space(2); margin-bottom: ${space(2)};
ml={2} margin-left: ${space(2);
mr={2} margin-right: ${space(2);
mt={2} margin-top: ${space(2);
mb={2} margin-bottom: ${space(2);


flexbox

这些是 flexbox 属性


旧 (grid-emotion) 新 (css/emotion/styled)
align="center" align-items: center;
justify="center" justify-content: center;
direction="column" flex-direction: column;
wrap="wrap" flex-wrap: wrap;


现在只需忽略 grid-emotion 的导入语句,例如 // eslint-disable-line no-restricted-imports

相关文章
|
6天前
|
监控 前端开发 JavaScript
前端稳定性工具-Sentry
【11月更文挑战第9天】Sentry 是一个开源的错误和性能监控平台,支持多种编程语言和框架。它能够捕获前端应用中的各种错误和性能问题,提供详细的错误信息和用户行为关联,帮助开发团队快速定位和解决问题,优化应用性能。但需注意隐私保护、数据准确性和成本控制。
|
3月前
|
JavaScript 前端开发 测试技术
Vue.js开发者必看!Vue Test Utils携手端到端测试,打造无懈可击的应用体验,引领前端测试新风尚!
【8月更文挑战第30天】随着Vue.js的普及,构建可靠的Vue应用至关重要。测试不仅能确保应用质量,还能提升开发效率。Vue Test Utils作为官方测试库,方便进行单元测试,而结合端到端(E2E)测试,则能构建全面的测试体系,保障应用稳定性。本文将带你深入了解如何使用Vue Test Utils进行单元测试,通过具体示例展示如何测试组件行为;并通过Cypress进行E2E测试,确保整个应用流程的正确性。无论是单元测试还是E2E测试,都能显著提高Vue应用的质量,让你更加自信地交付高质量的应用。
66 0
|
3月前
|
存储 前端开发 JavaScript
Vuex 难题困扰无数开发者,掌握这些秘诀,让你在前端热潮中轻松突围!
【8月更文挑战第27天】Vuex 是 Vue.js 的一种状态管理模式,它支持集中式存储及状态管理。使用过程中,开发者常遇问题包括:状态更新不一致、异步操作困难以及模块组织不当等。为确保状态更新的一致性,应利用 mutations 进行状态更改。对于异步操作,推荐使用 actions 处理以避免状态变更追踪难题。此外,合理划分模块并采用清晰命名有助于提升大型项目的可维护性。
34 0
|
3月前
|
前端开发 JavaScript 算法
|
3月前
|
JavaScript 前端开发 算法
|
3月前
|
前端开发 数据安全/隐私保护 开发者
热门聚焦!Web 前端 CSS 选择器 —— 解锁精美网页的密码,触动开发者心灵深处!
【8月更文挑战第23天】CSS 选择器是 Web 前端设计中的关键工具,用于精准定位和美化页面元素。主要包括:直观的元素选择器(如 `p`),灵活的类选择器(如 `.my-class`),唯一的 ID 选择器(如 `#unique-div`),以及可根据属性选择的属性选择器(如 `a[title]`)。此外,后代选择器(如 `div p`)、子选择器(如 `ul &gt; li`)和相邻兄弟选择器(如 `h1 + p`)可用于更复杂的选择。通用选择器(如 `*`)则适用于所有元素。通过组合这些选择器,开发者能够创建出既复杂又美观的网页样式,提升用户体验。
37 0
|
3月前
|
存储 前端开发 开发者
Web 前端热点来袭!数组去重难题何解?快来探索这些超实用方法,引发开发者共鸣!
【8月更文挑战第23天】在Web前端开发中,去除数组中的重复项是提升数据准确性和效率的关键步骤。本文介绍了四种常用的数组去重方法:一是运用ES6的Set数据结构,通过构造Set对象并转换回数组,快速剔除重复值;二是结合for循环与`indexOf`方法,逐个检查元素是否已存在于新数组中;三是采用`forEach`循环与`includes`方法实现类似功能;四是利用`reduce`方法,以函数式编程方式完成累积检查和去重。这四种方法各有优势,可根据项目需求和环境选择使用。
62 0
|
6月前
|
前端开发 JavaScript 网络协议
【专栏】探讨了前端性能优化中的 Performance 工具,它能帮助开发者分析页面加载速度和交互体验
【4月更文挑战第29天】本文探讨了前端性能优化中的 Performance 工具,它能帮助开发者分析页面加载速度和交互体验。通过 Performance,可检测资源加载时间、JavaScript 执行时间、重绘与回流等关键指标,找到性能瓶颈。文中列举了三个实践案例,如优化图片加载、减少 JavaScript 执行时间和避免重绘回流,展示如何利用 Performance 改进页面性能,提升用户体验。开发者应定期使用 Performance 分析并学习新优化技术,以适应Web开发的快速发展。
240 1
|
6月前
|
前端开发 JavaScript NoSQL
从前端到后端:构建全栈开发者的必备技能
随着互联网技术的不断发展,全栈开发者的需求日益增长。本文将介绍如何从前端到后端,掌握全栈开发所需的关键技能,包括前端框架的选择、后端语言的学习以及数据库的应用,帮助读者构建成为全面的技术专家。
|
6月前
|
Dart 数据处理 开发者
【Flutter前端技术开发专栏】Flutter是谷歌的开源移动框架,以其高性能和跨平台能力受开发者青睐。
【4月更文挑战第30天】Flutter是谷歌的开源移动框架,以其高性能和跨平台能力受开发者青睐。本文聚焦Flutter开发关键知识点:1) Dart语言和Flutter框架基础,如Widget和State;2) 路由管理,包括基本和命名路由,以及路由传值;3) 使用http、dio等库进行网络请求和数据处理;4) ThemeData定义应用主题,实现样式主题化。掌握这些技能将提升Flutter开发效率和应用质量。
63 0