前端笔记


Create react app

官网:https://create-react-app.dev/docs/getting-started

中文:https://www.html.cn/create-react-app/docs/getting-started/

创建项目

创建ts项目:

npx create-react-app my-app --template typescript

ant design + ts:

参考:https://ant.design/docs/react/use-with-create-react-app-cn

yarn create react-app antd-demo-ts –-template typescript

npx create-react-app antd-demo-ts –typescript

yarn add antd

npm run start

  1. 修改 src/App.tsx,引入 antd 的按钮组件。

import { Button } from 'antd';

  1. 修改 src/App.css,在文件顶部引入 antd/dist/antd.css

@import '~antd/dist/antd.css';

使用scss

安装node-sass就可以在项目直接使用:

yarn add node-sass

npm install node-sass –save

使用less(虽然可用,但存在问题,暂时找不方案)

初始化的项目不支持less,,不像scss,需要修改配置文件;先安装less插件:

yarn add less less-loader

暴露配置文件:

npm run eject

如果报错:Remove untracked files, stash or commit any changes, and try again.

那么先提交:

修改配置文件:

const lessRegex = /\.less$/;

const lessModuleRegex = /\.module\.less$/;

 

{

  test: lessRegex,

  exclude: lessModuleRegex,

  use: getStyleLoaders({ importLoaders: 2 }, 'less-loader'),

},

{

  test: lessModuleRegex,

  use: getStyleLoaders(

    {

      importLoaders: 2,

      modules: true,

      getLocalIdent: getCSSModuleLocalIdent,

    },

    'less-loader'

  ),

 },

添加.prettierrc.json

 

别名配置

  1. 安装 react-app-rewired:

npm install -S react-app-rewired

  1. package.json文件中的脚本替换成如下:
    1. 创建config-overrides.js
    2. 配置tsconfig.json
3.     "scripts": {
4.         "start": "react-app-rewired start",
5.         "build": "react-app-rewired build",
6.         "test": "react-app-rewired test",
7.         "eject": "react-app-rewired eject"
8.      }
const path = require('path');
 
module.exports = function override(config) {
  config.resolve = {
    ...config.resolve,
    alias: {
      ...config.alias,
      '@': path.resolve(__dirname, 'src'),
    },
  };
 
  return config;
};

"paths": {

"@/*": ["./src/*"],

}

添加路由

yarn add react-router-dom

安装的是react-router-dom6 版本,与之前的旧版本用法很大区别参考:https://www.jianshu.com/p/7c777d5cd476

使用:

import { HashRouter, Routes, Route } from "react-router-dom";

const App: React.FC = () => {

  return (

   

     

        }>

          } />

          } />

          } />

       

     

   

  );

};

export default App;

子路由与跳页面:

import { Outlet, useNavigate } from "react-router-dom";

  return (

     

      // 子路由

   

  );

 

暴露配置文件的配置方式

npm run eject 需要全部提交暂存文件,才可以执行

按需加载ant design 样式

yarn add babel-plugin-import –D

package.json:

    "plugins": [

      [

        "import",

        { "libraryName": "antd", "style": "css" }

      ]

]

定制主题

图1圈住代码:

const lessRegex = /\.less$/;

const lessModuleRegex = /\.module\.less$/;

图2圈住代码:

if (preProcessor === "less-loader") {

  loaders.push(

    {

      loader: require.resolve("resolve-url-loader"),

      options: {

        sourceMap: isEnvProduction && shouldUseSourceMap,

      },

    },

    {

      loader: require.resolve(preProcessor),

      options: {

        lessOptions: {

          sourceMap: true,

          modifyVars: {

            "@primary-color": "red",

          },

          javascriptEnabled: true,

        },

      },

    }

  );

} else if (preProcessor) {

  // .....

图3圈住代码:

  test: lessRegex,

  exclude: lessModuleRegex,

  use: getStyleLoaders(

    {

      importLoaders: 3,

      sourceMap: isEnvProduction && shouldUseSourceMap,

    },

    "less-loader"

  ),

  sideEffects: true,

},

  test: lessModuleRegex,

  use: getStyleLoaders(

    {

      importLoaders: 3,

      sourceMap: isEnvProduction && shouldUseSourceMap,

      modules: { getLocalIdent: getCSSModuleLocalIdent },

    },

    "less-loader"

  ),

},

问题:样式变量不能使用rem

报错:

别名配置

"@": path.resolve(__dirname, "../src"),

"paths": { "@/*": ["./src/*"] }

Ant Design Pro

初始化项目

yarn create umi umi-app

npx create-umi myapp

cd umi-app && yarn

启动:npm run start

问题1:如果使用npm run dev 启动,会登录不上

解决:使用npm run start

问题2:初始化项目后,不知道为什么import react from ‘react’ 报错:找不到模块“react”或其相应的类型声明

解决:重新打开vscode编辑器就没有

使用mock数据

官网:https://umijs.org/zh-CN/config#mock

配置完成,保存后,会自动生成数据:

禁用:

mock: false

也可以通过环境变量临时关闭:

MOCK=none umi dev

删除国际化

1: npm run i18n-remove

2: 删除locales文件夹

删除用例测试

删除:根目录下的tests文件夹

删除:\src\e2e文件夹

删除:配置文件:jest.config.js

删除:下面配置

设置浏览器title

问题

  1. 如果1设置title: false,后那么3路由title设置也会无效
  2. 如果使用了plugin-layout插件, 那么只能用插件来设置title, 1、3设置都会失效,如果2没设置,那么会使用默认值 ant-design-pro
  1. 使用了plugin-layout插件,同时设置了1或者3,那title会闪烁,先变1/3,在变2;
  2. 如果左侧有菜单,ttitle的表现形式是 “菜单名称”+ “layout设置的title”

解决

https://beta-pro.ant.design/docs/title-landing-cn

ProLayout 会根据菜单和路径来自动匹配浏览器的标题。可以设置 pageTitleRender=false 来关掉它。

  1. 如果项目由此至终都只需要一个title,那么可以这样设置:
  1. 如果需要根据路由来显示title,那么可以这样设置:

保留1的配置,然后各自在路由上设置title:

todo: 有个bug,就是在登录界面登进去,会显示config.js 上的title,刷新后才会显示路由设置的title, 可以让它们保持一致。

3.       果不设置pageTitleRender: false,ttitle的表现形式是 “菜单名称”+ “layout设置的title”; pageTitleRender 可以是一个方法,返回字符串,就是浏览器的title,只是在浏览器刷新时候生效,切换页面,会被路由的title或者 config.ts 设置的title 覆盖。
4.   当您想从 React 组件更改标题时,可以使用第三方库 React Helmet。react-helmet
https://www.npmjs.com/package/react-helmet

修改加载页

首次进入的加载

js 还没加载成功,但是 html 已经加载成功的 landing 页面:src\pages\document.ejs

使用了 home_bg.png ,pro_icon.svg 和 KDpgvguMpGfqaHPjicRK.svg 三个带有品牌信息的图片,你可以按需修改他们。

切换页面加载

项目中打开了代码分割的话,在每次路由切换的时候都会进入一个加载页面。

dynamicImport: {

  loading: '@ant-design/pro-layout/es/PageLoading',

业务中的加载

等待用户信息或者鉴权系统的请求完成后才能展示页面。 getInitialState支持了异步请求,同时在请求时会停止页面的渲染。这种情况下加载页的。我们可以在 src\app.tsx 中配置:

/** 获取用户信息比较慢的时候会展示一个 loading */

export const initialStateConfig = {

  loading: ,

};

插件

文档:https://umijs.org/zh-CN/docs/plugin

全局数据

插件:https://umijs.org/zh-CN/plugins/plugin-initial-state

有 src/app.ts 并且导出 getInitialState 方法时启用

本插件不可直接使用,必须搭配 @umijs/plugin-model 一起使用。

getInitialState

使用插件plugin-initial-state, 项目启动会先在app.tsx 执行getInitialState方法,是async,可以执行异步请求;返回数据后才会加载路由页面,数据可以全局使用。

代码模板:

export async function getInitialState(): Promise<{

  loading?: boolean;

  currentUser?: API.CurrentUser;

  fetchUserInfo?: () => Promise;

}> {

  await Promise.resolve('')

  return {

    loading: false,

    currentUser: {}

  };

}

获取数据:

import { useModel } from 'umi';

const { initialState } = useModel('@@initialState');

console.log(initialState?.currentUser);

initialStateConfig

initialStateConfig 是 getInitialState 的补充配置,getInitialState 支持异步的设置,在初始化没有完成之前我们展示了一个 loading,initialStateConfig 可以配置这个 loading。

import { PageLoading } from '@ant-design/pro-layout';

/** 获取用户信息比较慢的时候会展示一个 loading */

export const initialStateConfig = {

  loading: ,

};

布局

使用插件:plugin-layout

插件文档:https://umijs.org/zh-CN/plugins/plugin-layout

配置文档:https://procomponents.ant.design/components/layout/

运行时配置布局:

childrenRender

这是文档找不到的配置,可以在每一个路由页面添加点东西:

权限

有 src/access.ts 时启用。约定了 src/access.ts 为我们的权限定义文件,需要默认导出一个方法,导出的方法会在项目初始化时被执行。该方法需要返回一个对象,对象的每一个值就对应定义了一条权限。如下所示:

initialState 是通过初始化状态插件 @umijs/plugin-initial-state 提供的数据,你可以使用该数据来初始化你的用户权限。

useAccess

我们提供了一个 Hooks 用于在组件中获取权限相关信息,如下所示:

import { useAccess } from 'umi';

const access = useAccess();

if (access.canReadFoo) {  }

Access

组件  对应用进行权限控制, 支持的属性如下:

accessible:    Type: boolean 是否有权限,通常通过 useAccess 获取后传入进来。
fallback:       Type: React.ReactNode无权限时的显示,默认无权限不显示任何内容。

children:      Type: React.ReactNode有权限时的显示。

import { useAccess, Access } from 'umi';

const access = useAccess();


  accessible={access.canReadFoo}
fallback={
无权限显示
}>
有权限显示


菜单/路由

type RouteType = {

  path?: string;

  component?: string | (() => any);

  wrappers?: string[];

  redirect?: string;

  exact?: boolean;

  routes?: any[];

  [k: string]: any;

};

interface MenuType {

  path?: string;

  component?: string;

  name?: string;

  icon?: string;

  target?: string;

  headerRender?: boolean;

  footerRender?: boolean;

  menuRender?: boolean;

  menuHeaderRender?: boolean;

  access?: string;

  hideChildrenInMenu?: boolean;

  hideInMenu?: boolean;

  hideInBreadcrumb?: boolean;

  flatMenu?: boolean;

}

type RoutersType = (RouteType & MenuType)[];

菜单

菜单可以根据routes.ts自动生成,参考:

https://pro.ant.design/zh-CN/docs/new-page#%E5%9C%A8%E8%8F%9C%E5%8D%95%E4%B8%AD%E4%BD%BF%E7%94%A8-iconfont

下面是routes配置中,关于菜单的配置说明:

name

  • name:string 配置菜单的 name,不配置,不会显示菜单,配置了国际化,name 为国际化的 key。
  • icon:string 配置菜单的图标,默认使用 antd 的 icon 名,默认不适用二级菜单的 icon。
  • access:string 权限配置,需要预先配置权限
  • hideChildrenInMenu:true 用于隐藏不需要在菜单中展示的子路由。
  • layout:false 隐藏布局
  • hideInMenu:true 可以在菜单中不展示这个路由,包括子路由。
  • hideInBreadcrumb:true 可以在面包屑中不展示这个路由,包括子路由。
  • headerRender:false 当前路由不展示顶栏
  • footerRender:false 当前路由不展示页脚
  • menuRender: false 当前路由不展示菜单
  • menuHeaderRender: false 当前路由不展示菜单顶栏
  • flatMenu 子项往上提,只是不展示父菜单

Icon

access

hideChildrenInMenu

layout

hideInMenu

hideInBreadcrumb

headerRender

footerRender

menuRender

menuHeaderRender

flatMenu 

路由

文档:https://umijs.org/zh-CN/docs/routing

配置文件中通过 routes 进行配置,格式为路由信息的数组。

import type { IConfigFromPlugins } from '@@/core/pluginConfig';

type RoutersType = IConfigFromPlugins['routes'];

 

const routers: RoutersType = [

  { exact: true, path: '/', component: 'index' },

  { exact: true, path: '/user', component: 'user' },

];

 

export default routers;

path

配置可以被 path-to-regexp@^1.7.0 理解的路径通配符。

component

React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages 开始找起。可以用 @,也可以用 ../。比如

 component: '@/layouts/basic'

 component: '../layouts/basic'

exact

Default: true 表示是否严格匹配,即 location 是否和 path 完全对应上

    // url 为 /one/two 时匹配失败
    { path: '/one', exact: true },
    
    // url 为 /one/two 时匹配成功
    { path: '/one' },
    { path: '/one', exact: false },

routes

配置子路由,通常在需要为多个路径增加 layout 组件时使用

    {
      path: '/',
      component: '@/layouts/index',
      routes: [
        { path: '/list', component: 'list' },
        { path: '/admin', component: 'admin' },
      ]
    }

在 src/layouts/index 中通过 props.children 渲染子路由

export default (props) => {
  return 
{ props.children }
;
}

这样,访问 /list 和 /admin 就会带上 src/layouts/index 这个 layout 组件

redirect

重定向,例子:

{ exact: true, path: '/', redirect: '/list' }

访问 / 会跳转到 /list,并由 src/pages/list 文件进行渲染

wrappers

配置路由的高阶组件封装,比如,可以用于路由级别的权限校验:

export default {

  routes: [

    { path: '/user', component: 'user', wrappers: ['@/wrappers/auth'] },

  ],

};

然后在 src/wrappers/auth 中:

import { Redirect } from 'umi';

export default (props: any) => {

  const isLogin = false;

  if (isLogin) {

    return

{props.children}
;

  } else {

    return ;

  }

};

target

{
// path 支持为一个 url,必须要以 http 开头
path: 'https://pro.ant.design/docs/getting-started-cn',
target: '_blank', // 点击新窗口打开
name: '文档',
}

页面跳转

import { history } from 'umi';

history.push('/list');

history.push('/list?a=b');

history.push({ pathname: '/list', query: { a: 'b' } });

history.goBack();

link: 只用于单页应用的内部跳转,如果是外部地址跳转请使用 a 标签

import { Link } from 'umi';

Users Page

获取参数

import { useLocation, history } from 'umi';

  const query = history.location.query;

 

const location = useLocation();

console.log(location.query); // 不知道为什么类型没有提示

样式/图片

样式

  1. 约定 src/global.css 为全局样式,如果存在此文件,会被自动引入到入口文件最前面,可以用于覆盖ui组件样式。
  2. Umi 会自动识别 CSS Modules 的使用,你把他当做 CSS Modules 用时才是 CSS Modules。
  3. 内置支持 less,不支持 sass 和 stylus,但如果有需求,可以通过 chainWebpack 配置或者 umi 插件的形式支持。

图片/svg

export default () =>

export default () =>

import { ReactComponent as Logo } from './logo.svg'

import logoSrc from './logo.svg'

logo

相对路径引用: background: url(./foo.png);

支持别名: background: url(~@/foo.png);

Umijs api

官网:https://umijs.org/zh-CN/api

dynamic

动态加载组件。使用场景:组件体积太大,不适合直接计入 bundle 中,以免影响首屏加载速度

// AsyncHugeA.tsx

import { dynamic } from 'umi';

export default dynamic({
  loader: async function () {
    // 注释 webpackChunkName:webpack 将组件HugeA以这个名字单独拆出去
    const { default: HugeA } = await import(
      /* webpackChunkName: "external_A" */ './HugeA'
    );
    return HugeA;
  }
});
 
// 使用:
import AsyncHugeA from './AsyncHugeA';

history

获取信息

// location 对象,包含 pathname、search 和 hash、query
console.log(history.location.pathname);
console.log(history.location.search);
console.log(history.location.hash);
console.log(history.location.query);
 

跳转路由

history.push('/list');

history.push('/list?a=b');

history.push({ pathname: '/list', query: { a: 'b' } });

history.goBack();

监听路由变化

const unlisten = history.listen((location, action) => {
  console.log(location.pathname);
});
unlisten(); // 取消监听

Link

import { Link } from 'umi';
Courses

pathname: '/list',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true },
}}>List
 
// 跳转到指定 /profile 路由,附带所有当前 location 上的参数
 {return { ... loca, pathname: '/profile' }}}/>
 
// 转到指定 /courses 路由,替换当前 history stack 中的记录

NavLink

特殊版本的  。当指定路由(to=指定路由)命中时,可以附着特定样式。

https://umijs.org/zh-CN/api#link

Prompt

{/* 用户要跳转到首页时,提示一个选择 */}
 loc.pathname !== '/' ? true : `您确定要跳转到首页么?`}/>
{/* 根据一个状态来确定用户离开页面时是否给一个提示选择 */}

withRouter

高阶组件,可以通过 withRouter 获取到 historylocationmatch 对象withRouter(({ history, location, match }) => {})

useHistory

hooks,获取 history 对象

useLocation

hooks,获取 location 对象

useParams

hooks,获取 params 对象。 params 对象为动态路由(例如:/users/:id)里的参数键值对。

Umijs 配置

官网:https://umijs.org/zh-CN/config#alias

proxy代理

  proxy: {

    '/api': {

      'target': 'http://jsonplaceholder.typicode.com/',

      'changeOrigin': true,

      'pathRewrite': { '^/api' : '' },

    }

  }

访问 /api/users 就能访问到 http://jsonplaceholder.typicode.com/users 的数据

alias别名

export default { alias: { foo: '/tmp/a/b/foo'} };
然后 import('foo'),实际上是 import('/tmp/a/b/foo')

Umi 内置了以下别名:

@,项目 src 目录

@@,临时目录,通常是 src/.umi 目录

umi,当前所运行的 umi 仓库目录

base路由前缀

Default: /

设置路由前缀,通常用于部署到非根目录。

比如,你有路由 / 和 /users,然后设置base为 /foo/,那么就可以通过 /foo/ 和 /foo/users 访问到之前的路由。

publicPath

Default: /

配置 webpack 的 publicPath。当打包的时候,webpack 会在静态文件路径前面添加 publicPath 的值,当你需要修改静态文件地址时,比如使用 CDN 部署,把 publicPath 的值设为 CDN 的值就可以。

如果你的应用部署在域名的子路径上,例如 https://www.your-app.com/foo/,你需要设置 publicPath 为 /foo/,如果同时要兼顾开发环境正常调试,你可以这样配置:

publicPath: process.env.NODE_ENV === 'production' ? '/foo/' : '/',

chainWebpack webpack配置

通过 webpack-chain 的 API 修改 webpack 配置。

dynamicImport

是否启用按需加载,即是否把构建产物进行拆分,在需要的时候下载额外的 JS 再执行。关闭时,只生成一个 js 和一个 css,即 umi.js 和 umi.css。优点是省心,部署方便;缺点是对用户来说初次打开网站会比较慢。

包含以下子配置项: loading, 类型为字符串,指向 loading 组件文件

externals

favicon

Type: string: 配置 favicon 地址(href 属性)。

配置:

favicon: '/ass/favicon.ico',

生成:

fastRefresh 

  • Type: object

快速刷新(Fast Refresh),开发时可以保持组件状态,同时编辑提供即时反馈

hash

links/metas/styles

配置额外的 link 标签。

配置额外的 meta 标签。数组中可以配置key:value形式的对象。

Default: [] 配置额外 CSS。

headScripts/scripts

headScripts: 配置  里的额外脚本,数组项为字符串或对象。

Scripts: 同 headScripts,配置  里的额外脚本。

ignoreMomentLocale

  • Type: boolean
  • Default: false

忽略 moment 的 locale 文件,用于减少尺寸。

mock

theme

配置主题,实际上是配 less 变量。

export default {
  theme: {
    '@primary-color': '#1DA57A',
  },
};

Theme for antd: https://ant.design/docs/react/customize-theme-cn

title

配置标题。(设置false可以关闭)

 title: '标题',

less文档

https://less.bootcss.com/#%E6%A6%82%E8%A7%88

变量(Variables)

命名规范,参考:

https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less

@width: 10px;

@height: @width + 10px;

#header {

width: @width;

height: @height;

混合(Mixins)

.bordered {

border-top: dotted 1px black;

border-bottom: solid 2px black;

#menu a { color: #111; .bordered(); }

.post a { color: red; .bordered(); }

嵌套(Nesting)

.clearfix {

display: block;

zoom: 1;

&:after {

content: " ";

display: block; font-size: 0;

height: 0; clear: both;

visibility: hidden;

(& 表示当前选择器的父级)

css module修改UI库样式

使用:global

ts文档

Required / Readonly

Required 的作用就是将某个类型里的属性全部变为必选项。

Readonly 的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值。

Record

Partial

extends

in

typeof

keyof

Pick

Exclude

Extract

Omit

ReturnType

其他

设置代码片段

npx和npm的区别

npx 是 npm 的高级版本,npx 具有更强大的功能。

  1. 在项目中直接运行指令,直接运行node_modules中的某个指令,不需要输入文件路径
  1. 避免全局安装模块:npx 临时安装一个模块,使用过后删除这个模块(下面的两个模块不需要全局安装)

 

  1. 使用不同版本的命令,使用本地或者下载的命令

设置全局ts类型

Src文件夹添加typings.d.ts文件:

上面为例子,src下面所有的tsx都可以这样使用CompTableAPI.ColumnsProps

相关