前端笔记
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
- 修改
src/App.tsx
,引入 antd 的按钮组件。
import { Button } from 'antd';
- 修改
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
别名配置
- 安装 react-app-rewired:
npm install -S react-app-rewired
- package.json文件中的脚本替换成如下:
- 创建config-overrides.js
- 配置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设置title: false,后那么3路由title设置也会无效
- 如果使用了plugin-layout插件, 那么只能用插件来设置title, 1、3设置都会失效,如果2没设置,那么会使用默认值 ant-design-pro
- 使用了plugin-layout插件,同时设置了1或者3,那title会闪烁,先变1/3,在变2;
- 如果左侧有菜单,ttitle的表现形式是 “菜单名称”+ “layout设置的title”
解决
https://beta-pro.ant.design/docs/title-landing-cn
ProLayout 会根据菜单和路径来自动匹配浏览器的标题。可以设置 pageTitleRender=false 来关掉它。
- 如果项目由此至终都只需要一个title,那么可以这样设置:
- 如果需要根据路由来显示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
} 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); // 不知道为什么类型没有提示
样式/图片
样式
- 约定
src/global.css
为全局样式,如果存在此文件,会被自动引入到入口文件最前面,可以用于覆盖ui组件样式。 - Umi 会自动识别 CSS Modules 的使用,你把他当做 CSS Modules 用时才是 CSS Modules。
- 内置支持 less,不支持 sass 和 stylus,但如果有需求,可以通过 chainWebpack 配置或者 umi 插件的形式支持。
图片/svg
export default () =>
export default () =>
import { ReactComponent as Logo } from './logo.svg'
import logoSrc from './logo.svg'
相对路径引用: 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
获取到history
、location
、match
对象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 具有更强大的功能。
- 在项目中直接运行指令,直接运行node_modules中的某个指令,不需要输入文件路径
- 避免全局安装模块:npx 临时安装一个模块,使用过后删除这个模块(下面的两个模块不需要全局安装)
- 使用不同版本的命令,使用本地或者下载的命令
设置全局ts类型
Src文件夹添加typings.d.ts文件:
上面为例子,src下面所有的tsx都可以这样使用CompTableAPI.ColumnsProps