整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

2024 年,你应该知道的 5 种 React 设计

2024 年,你应该知道的 5 种 React 设计模式

者 | Lakindu Hewawasam

译者 | 许学文

策划 | 丁晓昀

如果你在开发工作中使用的是 React 框架,那么首当其冲要学习的就是思考如何设计组件。组件设计并非简单地将多个组件合成一个集合,而是需要思考如何设计更小、复用性更强的组件。例如,思考下面这张组件图:



简化的组件图


图中有三个组件,分别是:


  1. Typography 组件
  2. Footer 组件
  3. Sizeable Box 组件


如图所示,Typography 组件同时被 Footer 和 Sizeable Box 组件使用。通常我们以为这样,就能构建一个简单、易维护和易排除错误的应用了。但其实只是这样思考组件的设计是远远不够的。


如果你知道如何从组件的视角思考问题,就可以通过在 React 组件中使用设计模式来提高代码的整体模块性、可扩展性和可维护性。


因此,下面这五种设计模式,是你在使用 React 时必须要掌握的。


模式一:基础组件


首先,在使用 React 时候,请尝试为应用设计基础组件。


基础UI组件,就是一个具备默认行为且支持定制化的组件。


例如,每个应用都会通过基础的样式、按钮设计或者基础的排版,来实现应用在视觉和交互上的一致性。这些组件的设计特点是:


  1. 组件会应用一组默认的配置。因此,使用者无需进行任何额外的配置,就可以快速基于默认配置使用组件。
  2. 组件可以支持定制化,使用者通过定制化可以覆盖组件的默认行为,从而为组件提供自定义的整体视觉和交互效果。


通过一个 Button 组件就能很好地说明基础组件模式的实现。示例如下:


  1. Button 组件可能会有诸如空心、实心等不同形态。
  2. Button 组件可能会有默认的文本。


现在你就可以利用基础组件模式进行设计,使组件的使用者可以改变其行为。请参考我基于基础组件模式完成的Button组件,示例代码如下:


import React, { ButtonHTMLAttributes } from 'react';




// 按钮组件的形态:实心或者空心
type ButtonVariant='filled' | 'outlined';




export type ButtonProps={
  /**
   * the variant of the button to use 
   * @default 'outlined'
   */
  variant?: ButtonVariant;
} & ButtonHTMLAttributes<HTMLButtonElement>;;




const ButtonStyles: { [key in ButtonVariant]: React.CSSProperties }={
  filled: {
    backgroundColor: 'blue', // Change this to your filled button color
    color: 'white',
  },
  outlined: {
    border: '2px solid blue', // Change this to your outlined button color
    backgroundColor: 'transparent',
    color: 'blue',
  },
};




export function Button({ variant='outlined', children, style, ...rest }: ButtonProps) {
  return (
    <button
      type='button'
      style={{
        ...ButtonStyles[variant],
        padding: '10px 20px',
        borderRadius: '5px',
        cursor: 'pointer',
        ...style
      }} {...rest}>
      {children}
    </button>
  );
}

复制代码


仔细观察代码会发现,这里 Button 组件的 props 类型合并了原生 HTML 中 button 标签属性的全部类型。这意味着,使用者除了可以为 Button 组件设置默认配置外,还可以设置诸如 onClick、aria-label 等自定义配置。这些自定义配置会通过扩展运算符传递给 Button 组件内部的 button 标签。


通过不同的上下文设置,可以看到不同的 Button 组件的形态,效果截图如下图。


这个可以查看具体设置:

https://bit.cloud/lakinduhewa/react-design-patterns/base/button/~compositions



基础组件在不同上下文中的使用效果


通过不同的上下文,你可以设定组件的行为。这可以让组件成为更大组件的基础。


模式二:组合组件


在成功创建了基础组件后,你可能会希望基于基础组件创建一些新的组件。


例如,你可以使用之前创建的 Button 组件来实现一个标准的 DeleteButton 组件。通过在应用中使用该 DeleteButton,可以让应用中所有删除操作在颜色、形态以及字体上保持一致。


不过,如果出现重复组合一组组件来实现相同效果的现象,那么你可以考虑将它们封装到一个组件中。


下面,让我们来看看其中一种实现方案:

https://bit.cloud/lakinduhewa/react-design-patterns/composition/delete-button



使用组合模式创建组件


如上面的组件依赖图所示,DeleteButton 组件使用基础的 Button 组件为所有与删除相关的操作提供标准的实现。下面是基本代码实现:


// 这里引入了,基础按钮组件和其props
import { Button, ButtonProps } from '@lakinduhewa/react-design-patterns.base.button';
import React from 'react';




export type DeleteButtonProps={} & ButtonProps;




export function DeleteButton({ ...rest }: DeleteButtonProps) {
  return (
    <Button
      variant='filled'
      style={{
        background: 'red',
        color: 'white'
      }}
      {...rest}
    >
      DELETE
    </Button>
  );
}

复制代码


我们使用基于模式一创建的 Button 组件来实现的 DeleteButton 组件的效果如下:



现在我们可以在应用中使用统一的删除按钮。此外,如果你使用类似 Bit 的构建系统进行组件的设计和构建,那么当 Button 组件发生改变时,可以让CI服务自动将此改变传递到DeleteButton组件上,就像下面这样(当 Button 组件从 0.0.3 升级到了 0.0.4,那么 CI 服务会自动触发,将 DeleteButton 组件从 0.0.1 升级到 0.0.2):


Bit 上的一个 CI 构建


模式三:使用 Hooks


React Hooks 是React v16就推出来的特性,它不依赖类组件实现状态管理、负效应等概念。简而言之,就是你可以通过利用 Hooks API 摆脱对类组件的使用需求。useSate 和 useEffect 是最广为人知的两个 Hooks API,但本文不打算讨论它们,我想重点讨论如何利用 Hooks 来提高组件的整体可维护性。


例如,请考虑下面这个场景:


  1. 有一个 BlogList 组件。
  2. BlogList 组件会通过调用一个简单的 API,获取博客文章列表数据,同时将其渲染在组件上。


基于上面的案例,你可能会像下面这样将 API 逻辑直接写在函数组件中:


import React, { useState, useEffect } from 'react';
import axios from 'axios';
const BlogList=()=> {
    const [blogs, setBlogs]=useState([]);
    const [isLoading, setIsLoading]=useState(true);
    const [error, setError]=useState(null);
    useEffect(()=> {
        axios.get('https://api.example.com/blogs')
            .then(response=> {
                setBlogs(response.data);
                setIsLoading(false);
            })
            .catch(error=> {
                setError(error);
                setIsLoading(false);
            });
    }, []);
    if (isLoading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;
    return (
        <div>
            <h2>Blog List</h2>
            <ul>
                {blogs.map(blog=> (
                    <li key={blog.id}>{blog.title}</li>
                ))}
            </ul>
        </div>
    );
};
export default BlogList;

复制代码


这样写,组件也能正常工作。它将会获取博客文章列表并且渲染在 UI 上。但是,这里将 UI 逻辑和 API 逻辑混在一起了。


理想情况下,React 组件应该不需要关系如何获取数据。而只需要关心接收一个数据数组,然后将其呈现在 DOM 上。


因此,实现这一目标的最佳方法是将 API 逻辑抽象到 React Hook 中,以便在组件内部进行调用。这样做就可以打破 API 调用与组件之间的耦合。通过这种方式,就可以在不影响组件的情况下,修改底层的数据获取逻辑。


其中一种实现方式如下。


1.useBlog hook


import { useEffect, useState } from 'react';
import { Blog } from './blog.type';
import { Blogs } from './blog.mock';




export function useBlog() {
  const [blogs, setBlogs]=useState<Blog[]>([]);
  const [loading, setLoading]=useState<boolean>(false);




  useEffect(()=> {
    setLoading(true);
     // 注意:这里的setTimeout非实际需要,只是为了模拟API调用
    setTimeout(()=> {
      setBlogs(Blogs);
      setLoading(false);
    }, 3000);
  }, []);




  return { blogs, loading }
}

复制代码


如上代码所示,useBlog hook 获取博客列表数据,然后赋值给状态变量,最后通过导出变量给到消费者(BlogList 组件)使用:



Hook 效果


2.BlogList 组件


import React from 'react';
// 引入上面封装的 useBlog hook
import { useBlog } from '@lakinduhewa/react-design-patterns.hooks.use-blog';
export function BlogList() {
  const { blogs, loading }=useBlog();
  if (loading) {
    return (
      <p>We are loading the blogs...</p>
    )
  }
  return (
    <ul>
      {blogs.map((blog)=> <ol
        key={blog.id}
      >
        {blog.title}
      </ol>)}
    </ul>
  );
}

复制代码



BlogList 组件效果


通过调用 useBlog 和使用其导出的状态变量,我们在 BlogList 组件中使用了 Hooks。如此,相对于之前,我们可以减少大量代码,并以最少的代码和精力维护两个组件。


此外,当你使用类似 Bit 这样的构建系统时(就像我一样),只需将 useBlog 组件导入本地开发环境,然后再修改完成之后重新推送回 Bit Cloud。Bit Cloud 的构建服务器可以依托依赖树将此修改传递给整个应用。因此如果只执行一些简单修改,甚至不需要访问整个应用。


模式四:React Providers


此模式的核心是解决组件状态共享。我们都曾是 props 下钻式传递的受害者。但如果你还没有经历过,那这里简单解释下:“props 下钻式传递”就是当你在组件树中进行 props 传递时,这些 props 只会在最底层组件中被使用,而中间层的组件都不会使用该 props。例如,看看下面这张图:

props 下钻式传递


从 BlogListComponent 一直向下传递一个 isLoading 的 props 到 Loader。但是,isLoading 只在 Loader 组件中使用。因此,在这种情况下,组件不但会引入不必要的 props,还会有性能开销。因为当 isLoading 发生变化时,即使组件没有使用它,React 依然会重新渲染你的组件树。


因此,解决方案之一就是通过利用 React Context 来使用 React Context Provider 模式。React Context 是一组组件的状态管理器,通过它,你可以为一组组件创建特定的上下文。通过这种方式,你可以在上下文中定义和管理状态,让不同层级的组件都可以直接访问上下文,并按需使用 props。这样就可以避免 props 下钻式传递了。


主题组件就是该模式的一个常见场景。例如,你需要在应用程序中全局访问主题。但将主题传递到应用中的每个组件并不现实。你可以创建一个包含主题信息的 Context,然后通过 Context 来设置主题。看一下我是如何通过React Context实现主题的,以便更好地理解这一点:

https://bit.cloud/lakinduhewa/react-design-patterns/contexts/consumer-component


import { useContext, createContext } from 'react';
export type SampleContextContextType={
  /**
   * primary color of theme.
   */
  color?: string;
};
export const SampleContextContext=createContext<SampleContextContextType>({
  color: 'aqua'
});
export const useSampleContext=()=> useContext(SampleContextContext);

复制代码


在 Context 中定义了一种主题颜色,它将在所有实现中使用该颜色来设置字体颜色。接下来,我还导出了一个 hook——useSampleContext,该 hook 让消费者可以直接使用 Context。


只是这样还不行,我们还需要定义一个 Provider。Provider 是回答 "我应该与哪些组件共享状态?"问题的组件。Provider的实现示例如下:


import React, { ReactNode } from 'react';
import { SampleContextContext } from './sample-context-context';
export type SampleContextProviderProps={
  /**
   * primary color of theme.
   */
  color?: string,
  /**
   * children to be rendered within this theme.
   */
  children: ReactNode
};
export function SampleContextProvider({ color, children }: SampleContextProviderProps) {
  return <SampleContextContext.Provider value={{ color }}>{children}</SampleContextContext.Provider>
}

复制代码


Provider 在管理初始状态和设置 Context 可访问状态的组件方面起着至关重要的作用。


接下来,你可以创建一个消费者组件来使用状态:



消费者组件


模式五:条件渲染


最后一个想和大家分享的是条件渲染模式。今天,人人都知道 React 中的条件渲染。它通过条件判断来选择组件进行渲染。


但在实际使用中我们的用法常常是错误的:


// ComponentA.js
const ComponentA=()=> {
    return <div>This is Component A</div>;
};
// ComponentB.js
const ComponentB=()=> {
    return <div>This is Component B</div>;
};
// ConditionalComponent.js
import React, { useState } from 'react';
import ComponentA from './ComponentA';
import ComponentB from './ComponentB';
const ConditionalComponent=()=> {
    const [toggle, setToggle]=useState(true);
    return (
        <div>
            <button onClick={()=> setToggle(!toggle)}>Toggle Component</button>
            {toggle ? <ComponentA /> : <ComponentB />}
        </div>
    );
};
export default ConditionalComponent;

复制代码


你是否注意到,这里我们将基于条件的逻辑耦合到了 JSX 代码片段中。通常,你不应该在 JSX 代码中中添加任何与计算相关的逻辑,而只将与 UI 渲染相关的内容放在其中。


解决这个问题的方法之一是使用条件渲染组件模式。创建一个可重用的 React 组件,该组件可以根据条件渲染两个不同的组件。它的实现过程如下:


import React, { ReactNode } from 'react';
export type ConditionalProps={
  /**
   * the condition to test against
   */
  condition: boolean
  /**
   * the component to render when condition is true
   */
  whenTrue: ReactNode
  /**
   * the component to render when condition is false
   */
  whenFalse: ReactNode
};
export function Conditional({ condition, whenFalse, whenTrue }: ConditionalProps) {
  return condition ? whenTrue : whenFalse;
}

复制代码


我们创建了一个可以按条件渲染两个组件的组件。当我们将其集成到其他组件中时,会使代码更简洁,因为无需在 React 组件中加入复杂的渲染逻辑。你可以像下面这样使用它:


export const ConditionalTrue=()=> {
  return (
    <Conditional
      condition
      whenFalse="You're False"
      whenTrue="You're True"
    />
  );
}
export const ConditionalFalse=()=> {
  return (
    <Conditional
      condition={false}
      whenFalse="You're False"
      whenTrue="You're True"
    />
  );
}

复制代码


实际的输入如下:



总结


掌握这五种设计模式,为 2024 年做好充分准备,构建出可扩展和可维护的应用吧。


如果你想详细深入本文中讨论的模式,请随时查看我在Bit Cloud的空间:

https://bit.cloud/lakinduhewa/react-design-patterns


感谢你的阅读!

原文链接:2024年,你应该知道的5种React设计模式_架构/框架_InfoQ精选文章

日常开发中,我们有一些组件的样式是重复的。

例如,Text 组件的 width、height 都是一样的。

@Entry
@Component
struct Index {
  build() {
    Column() {
      Text("春明不觉晓")
        .width(100)
        .height(50)

      Text("处处闻啼鸟")
        .width(100)
        .height(50)

      Text("夜来风雨声")
        .width(100)
        .height(50)

      Text("花落知多少")
        .width(100)
        .height(50)
    }
    .width('100%')
  }
}

那么,我们可以将这些重复的样式抽出来,做成一个公共样式

公共样式是一个函数,格式为:@Style 样式名称() { 样式描述 }

例如这里的 @Style textSize(){}。

@Entry
@Component
struct Index {
  @Styles textSize(){
    .width(100)
    .height(50)
  }

  build() {
    Column() {
      Text("春明不觉晓")
        .width(100)
        .height(50)

      Text("处处闻啼鸟")
        .width(100)
        .height(50)

      Text("夜来风雨声")
        .width(100)
        .height(50)

      Text("花落知多少")
        .width(100)
        .height(50)
    }
    .width('100%')
  }
}

接下来,我们就可以使用 textSize 去替换掉那些重复的样式。

@Entry
@Component
struct Index {
  @Styles textSize(){
    .width(100)
    .height(50)
  }

  build() {
    Column() {
      Text("春明不觉晓")
        .textSize()

      Text("处处闻啼鸟")
        .textSize()

      Text("夜来风雨声")
        .textSize()

      Text("花落知多少")
        .textSize()
    }
    .width('100%')
  }
}

运行结果是不变的:

使用公共样式可以帮我们减少很多重复的代码。

推荐大家在日常开发中多多使用 @Style 定义公共样式。

注意:当前 @Styles 仅支持通用属性和通用事件

通用属性文档地址:

https://developer.harmonyos.com/cn/docs/documentation/doc-references-V3/ts-universal-attributes-size-0000001428061700-V3

通用事件文档地址:

https://developer.harmonyos.com/cn/docs/documentation/doc-references-V3/ts-universal-events-click-0000001477981153-V3

还有一点需要注意的是,@Styles方法不支持参数。下列操作是不允许的:

// 错误写法,不允许有参数
@Styles textSize(message: string){
  .width(100)
  .height(50)
}

至此,@Style 基本使用方法介绍完毕。

者|Next.js 团队

译者|无明

出处丨前端之巅

在经过 26 次金丝雀发布和 340 万次下载之后,近日,我们正式发布了 Next.js 7.0,新功能包括:

  • DX 改进:启动速度提高 57%,重新编译速度提高 42%;
  • 使用 react-error-overlay 更好地报告错误;
  • 编译管道升级:Webpack 4 和 Babel 7;
  • 标准化的动态导入;
  • 静态 CDN 支持;
  • 较小的初始 HTML 载荷;
  • App 和 Page 之间的 React Context(服务器端渲染)。

DX 改进

Next.js 的主要目标之一是提供最佳的性能和开发者体验。最新版本为构建和调试管道带来了很多重大改进。

得益于 Webpack 4 和 Babel 7,以及我们对代码库做出的很多改进和优化,Next.js 现在在开发过程中的启动速度提高了 57%。

我们新增了增量编译缓存,让变更代码的构建速度快了 40%。

以下是我们收集的一些示例数据:


因为使用了 webpackbar,在开发和构建的同时可以看到更好的实时反馈:


使用 react-error-overlay 更好地报告错误

准确地渲染错误对于良好的开发和调试体验来说是至关重要的。到目前为止,我们可以渲染错误消息和堆栈跟踪信息。我们在此基础上更进一步,我们使用 react-error-overlay 来丰富堆栈跟踪信息:

  • 准确的服务器端和客户端错误位置;
  • 高亮显示错误来源;
  • 完整的堆栈跟踪信息。

这是之前和之后的错误显示比较:


另外,借助 react-error-overlay,你只需单击特定代码块就可以轻松打开文本编辑器。

支持 Webpack 4

从发布第一个版本以来,Next.js 一直使用 Webpack 来打包代码和重用丰富的插件。Next.js 现在使用了最新的 Webpack 4,其中包含很多改进和 bug 修复。

  • 支持.mjs 源文件;
  • 代码拆分改进;
  • 更好的摇树优化(删除未使用的代码)支持。

另一个新功能是支持 WebAssembly,Next.js 甚至可以进行 WebAssembly 服务器渲染。

这里有一个例子:

https://github.com/zeit/next.js/tree/canary/examples/with-webassembly

CSS 导入

因为使用了 Webpack 4,我们引入了一种从捆绑包中提取 CSS 的新方法,这个插件叫作 mini-extract-css-plugin(https://github.com/webpack-contrib/mini-css-extract-plugin)。

mini-extract-css-plugin 提供了 @zeit/next-css、@zeit/next-less、@zeit/next-sass 和 @zeit/next-stylus。

这些 Next.js 插件的新版本解决了与 CSS 导入相关的 20 个问题,例如,现在支持 import() 动态导入 CSS:

// components/my-dynamic-component.js
import './my-dynamic-component.css'
export default ()=> <h1>My dynamic component</h1>
// pages/index.js
import dynamic from 'next/dynamic'
const MyDynamicComponent=dynamic(import('../components/my-dynamic-component'))
export default ()=> <div>
 <MyDynamicComponent/>
</div>

一个重大改进是现在不再需要在 pages/_document.js 中添加一下内容:

<link rel="stylesheet" href="/_next/static/style.css" />

Next.js 会自动注入这个 CSS 文件。在生产环境中,Next.js 还会自动向 CSS URL 中添加内容哈希,当文件发生变更时,最终用户就不会得到旧文件,并且能够获得不可变的永久缓存。

简而言之,要在 Next.js 项目中支持导入.css 文件,只需要在 next.config.js 中注册 withCSS 插件:

const withCSS=require('@zeit/next-css')
module.exports=withCSS({/* my next config */})

标准化动态导入

从版本 3 开始,Next.js 就通过 next/dynamic 来支持动态导入。

作为这项技术的早期采用者,我们必须自己编写解决方案来处理 import()。

因此,Next.js 逐渐缺失 Webpack 后来引入的一些功能,包括 import()。

例如,无法手动命名和捆绑某些文件:

import(/* webpackChunkName: 'my-chunk' */ '../lib/my-library')

另一个例子是在 next/dyanmic 模块之外使用 import()。

从 Next.js 7 开始,我们不再直接使用默认的 import(),Next.js 为我们提供了开箱即用的 import() 支持。

这个变更也是完全向后兼容的。使用动态组件非常简单:

import dynamic from 'next/dynamic'
const MyComponent=dynamic(import('../components/my-component'))
export default ()=> {
 return <div>
 <MyComponent />
 </div>
}

这段代码的作用是为 my-component 创建一个新的 JavaScript 文件,并只在渲染< MyComponent/>时加载它。

最重要的是,如果没有进行渲染,< script>标记就不会出现在初始 HTML 文档中。

为了进一步简化我们的代码库并利用优秀的 React 生态系统,在 Next.js 7 中,我们使用 react-loadable 重写了 next/dynamic 模块。这为动态组件引入了两个很棒的新特性:

  • 使用 next/dynamic 的 timeout 选项设置超时;
  • 使用 next/dynamic 的 delay 选项设置组件加载延迟。例如,如果导入非常快,可以通过这个选项让加载组件在渲染加载状态之前等待一小段时间。

支持 Babel 7

Next.js 6 中就已经引入了 Babel 7 测试版。后来 Babel 7 稳定版本发布,现在,Next.js 7 正在使用这个新发布的稳定版 Babel 7。

一些主要特性:

  • Typescript 支持,在 Next.js 中可以使用 @zeit/next-
  • typescript;
  • 片段语法<>支持;
  • babel.config.js 支持;
  • 通过 overrides 属性将预设 / 插件应用于文件或目录的子集。

如果你的 Next.js 项目中没有自定义 Babel 配置,那么就不存在重大变更。

但如果你具有自定义 Babel 配置,则必须将相应的自定义插件 / 预设升级到最新版本。

如果你从 Next.js 6 以下的版本升级,可以使用 babel-upgrade 工具。

除了升级到 Babel 7 之外,当 NODE_ENV 被设置为 test 时,Next.js Babel 预设(next/babel)现在默认将 modules 选项设置为 commonjs。

这个配置选项通常是在 Next.js 项目中创建自定义.babelrc 的唯一理由:

{
 "env": {
 "development": {
 "presets": ["next/babel"]
 },
 "production": {
 "presets": ["next/babel"]
 },
 "test": {
 "presets": [["next/babel", { "preset-env": { "modules": "commonjs" } }]]
 }
 }
}

使用 Next.js 7,这将变成:

{
 "presets": ["next/babel"]
}

现在可以删除.babelrc,因为在没有 Babel 配置时,Next.js 将自动使用 next/babel。

较小的初始 HTML 载荷

Next.js 在预渲染 HTML 时会将页面内容放在< html>、< head>、< body>结构中,并包含页面所需的 JavaScript 文件。

这个初始载荷之前约为 1.62kB。在 Next.js 7 中,我们优化了初始 HTML 载荷,现在为 1.5kB,减少了 7.4%,让页面变得更加精简。


我们主要通过以下几种方式来缩小文件:

  • 移除 __next-error div;
  • 内联脚本被最小化,在未来的版本中,它们将被完全移除;
  • 去掉未使用的 NEXT_DATA 属性,例如 nextExport 和 assetPrefix 属性。

静态 CDN 支持

在 Next.js 5 中,我们引入了 assetPrefix 支持,让 Next.js 可以自动从某个位置(通常是 CDN)加载资源。如果你的 CDN 支持代理,可以使用这种办法。你可以像这样请求资源:

https://cdn.example.com/_next/static/<buildid>/pages/index.js

通常,CDN 先检查缓存中是否包含这个文件,否则直接从源中请求文件。

不过,某些解决方案需要将目录直接预先上传到 CDN 中。这样做的问题在于 Next.js 的 URL 结构与.next 文件夹中的文件夹结构不匹配。例如我们之前的例子:

https://cdn.example.com/_next/static/<buildid>/pages/index.js
// 映射到:
.next/page/index.js

在 Next.js 7 中,我们改变了.next 的目录结构,让它与 URL 结构相匹配:

https://cdn.example.com/_next/static/<buildid>/pages/index.js
// 映射到:
.next/static/<buildid>/pages/index.js

尽管我们建议使用代理类型的 CDN,但新结构也允许不同类型 CDN 的用户将.next 目录上传到 CDN。

styled-jsx 3

我们也引入了 styled-jsx 3,Next.js 的默认 CSS-in-JS 解决方案,现在已经为 React Suspense 做好了准备。

如果一个组件不属于当前组件作用域的一部分,那么该如何设置这个子组件的样式呢?例如,如果你将一个组件包含在父组件中,并只有当它被用在父组件中时才需要特定的样式:

const ChildComponent=()=> <div>
 <p>some text</p>
</div>
export default ()=> <div>
 <ChildComponent />
 <style jsx>{`
 p { color: black }
 `}</style>
</div>

上面的代码试图选择 p 标签,但其实不起作用,因为 styled-jsx 样式被限定在当前组件,并没有泄漏到子组件中。解决这个问题的一种方法是使用:global 方法,将 p 标记的前缀移除。但这样又引入了一个新问题,即样式泄露到了整个页面中。

在 styled-jsx 3 中,通过引入一个新的 API css.resolve 解决了这个问题,它将为给定的 syled-jsx 字符串生成 className 和< style>标签(styles 属性):

import css from 'styled-jsx/css'
const ChildComponent=({className})=> <div>
 <p className={className}>some text</p>
</div>
const { className, styles }=css.resolve`p { color: black }`
export default ()=> <div>
 <ChildComponent className={className} />
 {styles}
</div>

这个新 API 可以将自定义样式传给子组件。

由于这是 styled-jsx 的主要版本,如果你使用了 styles-jsx/css,那么在捆绑包大小方面有一个重大变化。在 styled-jsx 2 中,我们将生成外部样式的“scoped”和“global”版本,即使只使用“scoped”版本,我们也会将“global”版本包含在内。

使用 styled-jsx 3 时,全局样式必须使用 css.global 而不是 css,这样 styled-jsx 才能对包大小进行优化。

App 和 Page 之间的 React Context(服务器端渲染)

从 Next.js 7 开始,我们支持 pages/_app.js 和页面组件之间的 React Context API。

以前,我们无法在服务器端的页面之间使用 React 上下文。原因是 Webpack 保留了内部缓存模块而不是使用 require.cache,我们开发了一个自定义 Webpack 插件来改变这种行为,以便在页面之间共享模块实例。

这样我们不仅可以使用新的 React 上下文,在页面之间共享代码时还能减少 Next.js 的内存占用。


社 区

从 Next.js 首次发布以来,就已获得相当多的用户,从财富 500 强公司到个人博客。我们非常高兴看到 Next.js 的采用量一直在增长。

目前,超过 12,500 个被公开索引的域名在使用 Next.js。我们有超过 500 名贡献者,他们至少提交过一次代码。在 GitHub 上,这个项目已经有 29,000 个 star。自首次发布以来,已提交了大约 2200 个拉取请求。

Next.js 社区在 spectrum.chat/next-js 上拥有近 2000 名成员。

英文原文

https://nextjs.org/blog/next-7