Skip to main content

cra使用记录

无论您使用 React 还是其他库,Create React App 都可以让您专注于代码,而不是构建工具。

创建应用

  • 创建一个TypeScript应用:yarn create react-app fl-home.onrender.com --template typescript
    tip

    Expected node version "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"

生成的目录文件的用途

public目录

  • 如果将文件放入 public 文件夹中,webpack 不会处理该文件。相反,它将原封不动地复制到构建文件夹中。要引用公共文件夹中的资源,您需要使用名为 PUBLIC_URL 的环境变量。只有公用文件夹内的文件才能通过%PUBLIC_URL%前缀访问。

    index.html
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  • 当您运行 npm run build 时,Create React App 会将 %PUBLIC_URL% 替换为正确的绝对路径,这样即使您使用客户端路由或将其托管在非根 URL 上,您的项目也能正常运行。

  • 在 JavaScript 代码中,您可以使用 process.env.PUBLIC_URL

    render() {
    // Note: this is an escape hatch and should be used sparingly!
    // Normally we recommend using `import` for getting asset URLs
    // as described in “Adding Images and Fonts” above this section.
    return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
    }

public/manifest.json

{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
info

当用户在移动设备上添加网站到主屏幕时,manifest.json 中的元数据将确定显示该网站时要使用的图标、名称和品牌颜色等。 Web应用程序清单指南提供了有关每个字段含义以及您的自定义将如何影响用户体验的更多上下文。

ios添加网站到主屏幕:

  1. 添加到主屏幕
  2. 添加到主屏幕

public/robots.txt

# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
tip

robots.txt 简介。robots.txt 文件应位于网站的根目录下。因此,对于网站 www.example.com,robots.txt 文件的路径应为 www.example.com/robots.txt。robots.txt 是一种遵循漫游器排除标准的纯文本文件,由一条或多条规则组成。每条规则可禁止或允许所有或特定抓取工具抓取托管 robots.txt 文件的网域或子网域上的指定文件路径。除非您在 robots.txt 文件中另行指定,否则所有文件均隐式允许抓取。该 robots.txt 文件的含义是所有用户代理均可抓取整个网站。

src/reportWebVitals.ts

import { ReportHandler } from 'web-vitals';

const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};

export default reportWebVitals;
tip

src/index.tsx调用了reportWebVitals

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

当页面上任何指标的最终值完成计算时,将触发此函数。您可以使用它将任何结果记录到控制台(reportWebVitals(console.log)))或发送到特定端点:

function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
const url = 'https://example.com/analytics'; // 某个分析站点

// Use `navigator.sendBeacon()` if available, falling back to `fetch()`
if (navigator.sendBeacon) {
navigator.sendBeacon(url, body);
} else {
fetch(url, { body, method: 'POST', keepalive: true });
}
}

// 将网页指标结果发送到分析站点,以测量和跟踪网站上的真实用户性能
reportWebVitals(sendToAnalytics);
info

Web Vitals 是一组有用的指标,旨在捕获网页的用户体验。在 Create React App 中,使用第三方库(web-vitals)来衡量这些指标。

  • Cumulative Layout Shift (CLS):衡量的是视觉稳定性。CLS 用于衡量在网页的整个生命周期内发生的每次意外布局偏移的最大突发布局偏移分数。为了提供良好的用户体验,页面应将 CLS 保持在 0.1 或更低。

  • First Input Delay (FID):衡量互动。FID 衡量的是从用户首次与网页互动(即,点击链接、点按按钮或使用由 JavaScript 提供支持的自定义控件)到浏览器能够实际开始处理事件处理脚本以响应该互动的时间。为了提供良好的用户体验,网页的 FID 不应超过 100 毫秒。

  • First Contentful Paint (FCP):衡量感知的加载速度。首次内容绘制 (FCP) 指标用于测量从用户首次导航到页面至页面内容的任何部分呈现在屏幕上的时间。要点:请务必注意,FCP 包含上一个页面的所有卸载时间、连接设置时间、重定向时间以及首字节时间 (TTFB)。在实际测量时,这些结果会非常重要,并且可能会导致现场测量结果和实验室测量结果之间存在差异。

  • Largest Contentful Paint (LCP):衡量加载性能。为了提供良好的用户体验,LCP 应在网页首次开始加载后的 2.5 秒内发生。Largest Contentful Paint (LCP) 指标会报告视口内可见的最大图片或文本块的呈现时间(相对于用户首次导航到页面的时间)。

  • Time To First Byte (TTFB):有助于确定 Web 服务器何时响应速度过慢。TTFB 用于测量资源请求与响应的第一个字节开始到达之间的时间。 ttfb

src/setupTests.ts

// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
tip

该文件会在运行测试之前自动执行。

执行 yarn test 报错:Error: Failed to initialize watch plugin "node_modules/jest-watch-typeahead/filename.js"。原因是node版本较低,切换到16以上版本就可以了。(参考:关于React npm run test错误jest-watcher

info

@testing-library/jest-dom 是测试库的配套库,为 Jest 提供自定义 DOM 元素匹配器。

src/react-app-env.d.ts

/// <reference types="react-scripts" />

项目的tsconfig.json配置了"include": [ "src" ],所以默认会编译src目录下的.ts.tsx.d.ts文件。如果打开了allowJs,那么还会编译.js.jsx。编译src/react-app-env.d.ts会添加 react-scripts 的类型库,没有单独的类型声明模块@types/react-scripts,则编译时实际添加的脚本是react-scripts模块指定的类型声明文件的路径node_modules/react-scripts/lib/react-app.d.ts。该文件内容如下:

node_modules/react-scripts/lib/react-app.d.ts
/// <reference types="node" />
/// <reference types="react" />
/// <reference types="react-dom" />

declare namespace NodeJS {
interface ProcessEnv {
readonly NODE_ENV: 'development' | 'production' | 'test';
readonly PUBLIC_URL: string;
}
}

declare module '*.avif' {
const src: string;
export default src;
}

declare module '*.bmp' {
const src: string;
export default src;
}

declare module '*.gif' {
const src: string;
export default src;
}

declare module '*.jpg' {
const src: string;
export default src;
}

declare module '*.jpeg' {
const src: string;
export default src;
}

declare module '*.png' {
const src: string;
export default src;
}

declare module '*.webp' {
const src: string;
export default src;
}

declare module '*.svg' {
import * as React from 'react';

export const ReactComponent: React.FunctionComponent<React.SVGProps<
SVGSVGElement
> & { title?: string }>;

const src: string;
export default src;
}

declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}

declare module '*.module.scss' {
const classes: { readonly [key: string]: string };
export default classes;
}

declare module '*.module.sass' {
const classes: { readonly [key: string]: string };
export default classes;
}

可以看到,该文件声明了avif bmp gif jpg png webp格式的图片、svg、css、scss、sass的类型,这样在项目文件中引入这些类型的文件时不会ts报错(参考:react-app-env.d.ts的作用以及如何生成的?

import contentBg from "@assets/image/content-bg.png"
import styles from './index.module.scss';

PUBLIC_URL

如果将文件放入 public 文件夹中,webpack 不会处理该文件。相反,它将原封不动地复制到构建文件夹中。要引用公共文件夹中的资源,您需要使用名为 PUBLIC_URL 的环境变量。

index.html中可以如下使用,当您运行 npm run build 时,Create React App 会将 %PUBLIC_URL% 替换为正确的绝对路径,这样即使您使用客户端路由或将其托管在非根 URL 上,您的项目也能正常运行。

<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />

在 JavaScript 代码中,您可以使用 process.env.PUBLIC_URL 实现类似目的:

render() {
// Note: this is an escape hatch and should be used sparingly!
// Normally we recommend using `import` for getting asset URLs
// as described in “Adding Images and Fonts” above this section.
return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
}

webpack配置

查看react-scripts依赖包中config/webpack.config.js

  1. 设置了别名src对应为根目录下的src目录,所以导入Home组件时可以使用src/pages/home/Home

使用.scss.sass

yarn add sass # 安装后就可以使用`.scss` 或 `.sass`文件

要在 Sass 文件之间共享变量,可以使用 Sass 的 @use 规则。例如,src/App.scss和其他组件样式文件可以包含@use “./shared.scss”;与变量定义。

@use 'styles/_colors.scss'; // 假设 src/ 下有一个 styles 目录,则可以这样使用

可以在路径前加上 ~ 前缀,如下所示,以解析 node_modules 中的模块:

@use '~nprogress/nprogress'; // loading a css file from the nprogress node module

注意:LibSass 以及构建在其之上的软件包(包括 Node Sass)已被弃用。如果您是 Node Sass 的用户,可以通过将 package.json 文件中的 node-sass 替换为 sass 或运行以下命令来迁移到 Dart Sass:

yarn remove node-sass
yarn add sass

eject的替代方案

  1. fork create-react-app 仓库

  2. packages/react-scripts/scripts/init.js中的增加log来验证后续使用的是自定义的react-scripts

    packages/react-scripts/scripts/init.js
    console.log(`Success! Created ${appName} at ${appPath}`);
    console.log('scripts version is @rawlinsfeng/react-scripts.')
  3. 查看packages/react-scripts/scripts/build.js发现webpack的配置文件目录为packages/react-scripts/config/webpack.config.js

  4. cd 到 packages/react-scripts 执行 yarn add postcss-px-to-viewport

  5. packages/react-scripts/config/webpack.config.js中的postcss-loader配置项中配置postcss-px-to-viewport

    packages/react-scripts/config/webpack.config.js
     const loaders = [
    isEnvDevelopment && require.resolve('style-loader'),
    isEnvProduction && {
    loader: MiniCssExtractPlugin.loader,
    // css is located in `static/css`, use '../../' to locate index.html folder
    // in production `paths.publicUrlOrPath` can be a relative path
    options: paths.publicUrlOrPath.startsWith('.')
    ? { publicPath: '../../' }
    : {},
    },
    {
    loader: require.resolve('css-loader'),
    options: cssOptions,
    },
    {
    // Options for PostCSS as we reference these options twice
    // Adds vendor prefixing based on your specified browser support in
    // package.json
    loader: require.resolve('postcss-loader'),
    options: {
    postcssOptions: {
    // Necessary for external CSS imports to work
    // https://github.com/facebook/create-react-app/issues/2677
    ident: 'postcss',
    config: false,
    plugins: !useTailwind
    ? [
    'postcss-flexbugs-fixes',
    [
    'postcss-preset-env',
    {
    autoprefixer: {
    flexbox: 'no-2009',
    },
    stage: 3,
    },
    ],
    // Adds PostCSS Normalize as the reset css with default options,
    // so that it honors browserslist config in package.json
    // which in turn let's users customize the target behavior as per their needs.
    'postcss-normalize',
    // Adds postcss-px-to-viewport
    [
    'postcss-px-to-viewport',
    {
    viewportWidth: 750,
    exclude: [/node_modules/],
    },
    ]
    ]
    : [
    'tailwindcss',
    'postcss-flexbugs-fixes',
    [
    'postcss-preset-env',
    {
    autoprefixer: {
    flexbox: 'no-2009',
    },
    stage: 3,
    },
    ],
    ],
    },
    sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
    },
    },
    ].filter(Boolean);
  6. 更改packages/react-scripts/package.json的name、description、repository url为自己的

  7. cd到packages/react-scripts目录,登录npm,发布npm包

  8. 使用:

    # npx
    npx create-react-app my-app --scripts-version @rawlinsfeng/react-scripts
    # yarn
    yarn create react-app my-app --scripts-version @rawlinsfeng/react-scripts
    # pnpm
    pnpm create react-app my-app --scripts-version @rawlinsfeng/react-scripts

    # Creating a TypeScript app
    # npx
    npx create-react-app my-app --scripts-version @rawlinsfeng/react-scripts --template typescript
    # yarn
    yarn create react-app my-app --scripts-version @rawlinsfeng/react-scripts --template typescript
    # pnpm
    pnpm create react-app my-app --scripts-version @rawlinsfeng/react-scripts --template typescript

区分环境

您可以通过在 shell 中或使用 .env 设置环境变量来调整各种开发和生产设置。