SPA单页应用
zhuanlan.zhihu.com/p/505743865
技术栈
-
编程语言:TypeScript 4.x + JavaScript
-
构建工具:create-react-app 脚手架 redux-typescript
-
配置文件修改: customize-cra":"^1.0.0-alpha.0", "react-app-rewired": "^2.2.1",
-
前端框架:React 18.2.0
-
路由工具:React-router-dom 6.x
-
状态管理:Redux/toolkit 1.8.5 官网 持久化存储redux-persist ^6.0.0
-
PC 端 UI 框架:Ant Design
-
CSS 预编译:SCSS (create-react-app 默认支持) 支持嵌套 伪类 继承
-
hook库: ahooks ^3.7.1,
-
HTTP 工具:Axios
-
数据模拟: mockjs ^1.1.0, json-server
下面这个三个还没用到
-
Git Hook 工具:husky + lint-staged
-
代码规范:EditorConfig + Prettier + ESLint + Airbnb JavaScript Style Guide
-
提交规范:Commitizen + Commitlint
公共类封装
1 路由拦截与鉴权,路由首页懒加载 ---首屏加载事件过长问题
2 请求响应拦截器
3页面layout布局
4 配置代理proxy
[TOC]
代码书写顺序
1 模块化引入 提交时删除未使用的声明 可以设置自动化删除
2 变量及声明 翻译插件统一 :www.deepl.com/translator column行列定义 3 事件 useEffect 4 return dom 5 模块化导出: export default HomePage; 最后有;
命名规则
1 小驼峰命名
特例: 组件采用大驼峰 且组件内部组件名也是大驼峰, 大驼峰情况举例如下:
2 使用英文命名
严禁拼音/拼音英文混用
英文不要缩写: 比如 abstractClass 命名为absClass
英文检索: 统一采用同一个翻译器 www.deepl.com/translator
prettierrc
{
// 每行末尾不会自动添加分号
"semi": false,
"tabWidth": 2,
// 字符串使用单引号
"singleQuote": true,
//是否在对象属性添加空格
// true: { foo: bar }
// false: {foo: bar}
"bracketSpacing": true,
//在jsx中把'>' 是否单独放一行
"jsxBracketSameLine": false,
// 换行长度,默认80
"printWidth": 200
}
注释
1 需要注释的四个部位; 组件注释: jsn写在每个组件最上方 变量注释:useState变量注释 传参变量如当前行 尤其是英文名 写在当前定义变量上方(另起一行) 自定义事件注释 ():有特定含义的事件需要注释 注: onChange 这种很容易理解可以不写注释
2 头部注释
插件: koroFileHeader
/*
* @Author:
* @Date: 2022-11-03 18:16:19
* @LastEditors:
* @LastEditTime: 2022-11-03 18:55:56
* @FilePath: \bbs-web\src\services\article.ts
* @Description: 位于文件头部 简述组件作用
*/
3 可选择 配置注释vscode快捷键
{
"Print to jsNoteTitle": {
"prefix": "jsNoteTitle",
"body": [
"/*",
" *@description:",
" *@author: liuzy",
" *@date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}",
"*/"
],
"description": ""
},
"Print to jsfn": {
"prefix": "jsfn",
"body": [
"/*",
" *@functionName: ${TM_CURRENT_LINE}",
" *@params1: ${1:参数1}",
" *@params2: ${2:参数2}",
" *@description:",
" *@author: liuzy",
" *@date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}",
"*/"
],
"description": ""
},
"Print to jsModify": {
"prefix": "jsModify",
"body": [
"/*",
" *@description:",
" *@modifyContent:",
" *@author: liuzy",
" *@date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}",
"*/"
],
"description": ""
},
"Print to jsVariable": {
"prefix": "jsVariable",
"body": [
"/*",
" *@description:",
" *@author: liuzy",
" *@date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}",
" *@variable1: ${1:变量1}",
" *@variable2: ${2:变量2}",
"*/"
],
"description": ""
}
}
文件目录
cfg 部署配置文件
public 公用文件 首页最外层index.html
src 主要内容
app+features redux文件夹 存储仓库和reducer文件
assets:资源文件 如图片文件
components: 公共组件 : 此文件夹下命名采用大驼峰
data.d.ts: 类型文件
json: jsonserver文件存储位置 现在使用mock方式 这个文件夹没用
layout: 首页外层布局文件
mock: mock假数据
pages: 页面文件和交互操作
routes: 存放路由信息
services: 调用接口文件夹
utils: 公共类封装
react-app-env.d.ts : 全局支持的文件后缀配置
reportWebVitals.ts
setupProxy.js: 配置代理
setupTests.ts: 测试文件 还未使用
.commitlint.config.js: git commit 配置文件 约束提交注释
lintstagedrc.js : eslin 配置
craco.config.js: craco给webpack打补丁
tsconfig.json: 配置绝对路径src 自动找到src下文件 sass使用时在绝对路径加('~asset/') 否则会报错
package.json: 包版本号
prettierignore : prettier 自动格式化忽略文件
.prettierrc.json: prettier 自动格式化 配置文件
git规范
/**
* feature:新功能
* update:更新某功能
* fix:修补某功能的bug
* refactor:重构某个功能
* optimize: 优化构建工具或运行时性能
* style:仅样式改动
* docs:仅文档新增/改动
* chore:构建过程或辅助工具的变动
* build: 打包
*/
配置镜像源
切换 npm config set registry registry.npm.taobao.org/ 查看 npm config get registry
另:三秒删除node module包
npm install rimraf -g
rimraf node_modules
项目打包
本系统采用线下打包 线上部署方式
打包命令如下: yarn build /npm run build 测试和生产同
当无法提交提示文件太大(The remote end hung up unexpectedly)
使用如下命令扩容为500M git config http.postBuffer 524288000 合并时经常出现冲突 最好增加配置 push时候自动删除build目录再打包
项目分支管理问题
测试和生产目前暂时均使用test_branch分支
git 写错分支如何切换
在branch1 commit,但是想在branch2 commit
在branch1记住commit的id.切换到branch2 然后执行git cherry-pick id
如果遇到冲突 先解决冲突 再提交
git push报错
the remote end hung up unexpectedly
添加
[http]
postBuffer = 524288000
脚手架创建
npx create-react-app xxx(不能有大写字母)
npx create-react-app 项目名称 --template typescript
npx create-react-app react-admin --template redux-typescript
3.报错(文件名、目录名或卷标语法不正确。)问题介绍 在命令正确的情况下,如npm, yarn ,umi,create-react-app 这些正确的情况下 去node的如下目录
nodejs/node_global/bin/对应命令的.cmd看这个cmd文件是不是
@"C:\Users\find me\AppData\Local\Yarn\Data\global\node_modules.bin\create-react-app.cmd" %* 极大可能是这里多了%~dp0 @"%~dp0\C:\Users\find me\AppData\Local\Yarn\Data\global\node_modules.bin\create-react-app.cmd" %*
严格模式
<React.StrictMode>
<App />
</React.StrictMode>
加载速度
import reportWebVitals from './reportWebVitals';
reportWebVitals();
const reportWebVitals = onPerfEntry => {
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;
react 18 新特性 距离16.8已经三年了
ReactDOM.createRoot
rfce 快捷生成函数
报错: You are running
create-react-app5.0.0, which is behind the latest release (5.0. 1).
1、删除C:\Users\用户(当前计算机用户名)\下的.npmrc文件(如果不存在,表示当前文件可能被隐藏。)
2、在命令行输入npm cache clean --force
结果如下
最后执行 unistall
create-react-app.bootcss.com/docs/folder…
安装antd
ant-design.gitee.io/docs/react/…
$ yarn add antd
@import '~antd/dist/antd.css';
yarn错误The engine "node" is incompatible with this module except version ^14.15.0
yarn config set ignore-engines true
版本号
- 如果只是修复bug,需要更新Z位。
- 如果是新增了功能,但是向下兼容,需要更新Y位。
- 如果有大变动,向下不兼容,需要更新X位。
^version 更新到当前 major version(也就是第一位数字)中最新的版本
从左数,第1个不为 0 的数字 + 1,后面的变成 0
^1.2.3 >=1.2.3 <2.0.0 ^0.2.3 >=0.2.3 <0.3.0 ^0.0.3 >=0.0.3 <0.0.4
~version 更新到当前 minor version(也就是中间的那位数字)中最新的版本
从左数,第2个数字 + 1,后面的变成 0
~1.2.3 >=1.2.3 <1.3.0 为了方便记忆,我们可以这样想,在正则表达式,放在首位的 ^ 表示匹配第一个,所以也就对应到这里的 major,而波浪线 ~ 表示波动到第二位,也就是 minor。
node官网:nodejs.org/en/download… 选择LTS长期支持版本
添加路由router
yarn add react-router-dom
create-react-app.bootcss.com/docs/adding…
router6 新特性
1 在app外层包
这种大版本更新不能跟随上一个的经验 会影响判断 要从0开始
每次点击路由 都会调navlink
navlink里面clasName是函数
实现高亮
计算样式类名: 实现复用
重定向no
路由表
useLocation 传参
最外层
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
router路由表
export default [
{
path: '/',
element: <Navigate to="/testaa" />
},
{
path: '/testaa',
element: <Testaa />
},
{
path: '/testbb',
element: <Testbb />,
children: [
{
path: 'message',
element: <MessageCom />,
children: [
{
// path: 'detail/:id/:title/:content',
path: 'detail',
element: <Details />
}
]
},
{
path: 'news',
element: <News />
},
]
},
]
路由拦截和权限控制
参考:
src/router/index.js 首页 布局 登录 404 403 首先渲染的地方 此处进行路由拦截 onRouteBefore
逻辑: 判断==是否需要登录== 如果需要登录
判断==是否含有用户信息==,如果没有=> 调用接口获取UserInfo
userInfo会返回accessIdList 授权列表 ,根据授权列表查询用户==是否具有权限==,没有权限返回403
如果有用户信息: 但是没权限 返回403
src/router/nest.tsx 一般路由配置
前端路由模式之 hashRouter和BrowserRouter
hashRouter 默认有个#
browserHistory 没有# ,==需要server端支持==
刷新会丢失404(上线中会出现问题 本地开发中不会)
底层原理不一样:
-
BrowserRoute使用的是H5的history API,不兼容IE9及以下版本。 HashRouter使用的是URL的哈希值。
-
path表现形式不一样 BrowserRouter的路径中没有#,例如:localhost:3000/demo/test HashRouter的路径包含#,例如:localhost:3000/#/demo/test 刷新后对路由state参数的影响
-
BrowserRouter没有任何影响,因为state保存在history对象中。 HashRouter刷新后会导致路由state参数的丢失!!! 备注:HashRouter可以用于解决一些路径错误相关的问题。
!配置模块化 修改webpack配置
1 暴露配置 不推荐 此操作不可逆 慎用!! 不贴出来了
eject 适用于修改大量配置的情况
2 打补丁的方式 覆盖文件
cnpm i customize-cra --save-dev
npm i react-app-rewired --save-dev
customize-cra
用来找到config-overrider.js
来进行修改
react-app-rewired
是修改package.json
里的启动配置的
config-overrider.js
是修改规则文件
在package.json的同级目录中新建一个config-overrides.js文件
更改 package.json 中的 scripts 配置,如下所示。即用 react-app-rewired命令代替 react-scripts
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
按需引入antddesign 修改主题色
antd样式自定义主题 自定义主题需要用到 less 变量覆盖功能。我们可以引入 customize-cra 中提供的 less 相关的函数 addLessLoader 来帮助加载 less 样式,同时修改 config-overrides.js 文件。
customize-cra依赖库我们在进行样式的按需引入时已经安装过了,我们只需要安装less以及less-loader即可:
npm install less less-loader@5
yarn add less less-loader@5
修改config-overrides.js文件:
const { fixBabelImports, override, addLessLoader } = require("customize-cra");
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
// style: 'css',
style: true,
}),
addLessLoader({
javascriptEnabled: true,
modifyVars: { '@primary-color': 'red' },
}),
// ...其他配置...
);
我升级了nodejs 项目启动失败了
删除node module文件夹
这里给给出个快速的方法
首先下载个包管理器:rimraf
直接在所在包含nodemodules的文件夹内打开命令行安装包
npm install rimraf -g
rimraf
包的作用:以包的形式包装rm -rf
命令,用来删除文件和文件夹的,不管文件夹是否为空,都可删除.
然后再执行删除操作:
rimraf node_modules
node_modules目录没了,秒删!
安装ts
npx create-react-app 项目名称 --template typescript
模块化配置 在最外层加文件 global.d.ts,否则可能会不识别 。module.css文件
declare module '*.css';
declare module '*.less';
declare module '*.scss';
declare module '*.sass';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
'isFragment' (imported as 'isFragment') was not found in 'react-is'
卸载重装包 删除node module
ts 基本用法
// 字符串
let a:string
let a:string[]
// 数组泛型
let a:Array<number> =[1,2]
//对象 详细写属性
let a:object 替换为
//数组
let a:number
let arr:number[]=[1,2]
// 布尔
let flag:boolean=true
类型断言
<number> str
str as number
let b:string|number // a or b
let c?:string // c属性可以不存在
接口
// 形参
interface Params{
title:string;
content:string;
say:()=>string //函数
}
let Question: Params{
title:"a";
content:"b";
say:():string=>{return "aaa"}
}
继承 单
interface Person{
age:number
}
interface Musician extends Person{
song:string
}
let a=<Musician>{};
a.age=33;
a.song="sing"
interface Child extend Person,Musician{}
let Iobj:Child={age:12,song:'eqewq'}
对象
let object_name={
key1:"value1",
key2:"a",
key3:["a","b"],
key4:function(){
}
}
no安装dva
显示webpack
从create-react-app创建项目后,运行npm run eject报错解决方法
create-react-app app-demo
cd app-demo
git init
git add .
git commit -m 'Saving before ejecting'
npm run eject
react生态
react toolkit 官方 redux封装
modx 用的人也不少
hookstate 完美结合hook 新出的 用的人少 但是是未来趋势
useContext 官方用法
dva 是类组件时代的产物 现在社区停止更新了 和umi很像
umi 体积较大 编译打包很久 需要研究官方文档 封装的比较狠 umi属于dva的升级版。都有模板代码,同时失去了灵活性
- 启动时间长
- 热更新慢
- 太臃肿
- 框架 BUG 修复不及时
- 过度封装,自定义插件难度大
- 约定式功能太单一
vite 快 但底层不是webpack
我认为最佳: vite(+esbuild+)+ ts+react router+react hooks+hookStates+ antd+ mock
目前:create react app(+ webpack+ )+ redux toolkit + ts+react router+react hooks+ antd+ mock
安装axios
npm install axios -g
import axios from 'axios';
export async function select(){
return axios.get('/question/select')
.then(function(response){
console.log('axios response.data',response.data)
return response;
})
.then(function(error){
return error
})
}
export const insert = async({title})=>{
axios.post('/question/select',{
title
})
.then(function(response){
console.log('axios response.data',response.data)
return response;
})
.then(function(error){
return error
})
}
安装axios
配置拦截器
安装react-cookies库
这个过程出现一个问题就是用node安装成功但是package.json就是没有,最后用git bash安装一遍
就安装成功了 注意--save
配置src/utils文件夹的http.js文件
import axios from "axios";
import cookie from 'react-cookies';
let baseUrl = '/api'
// 创建axios实例,在这里可以设置请求的默认配置
const instance = axios.create({
timeout: 20000, // 设置超时时间10s
// baseURL: baseUrl // 根据自己配置的反向代理去设置不同环境的baseUrl
})
// 文档中的统一设置post请求头。下面会说到post请求的几种'Content-Type'
instance.defaults.headers.post['Content-Type'] = 'application/json'
/** 添加请求拦截器 **/
instance.interceptors.request.use(config => {
var token = cookie.load('token')//获取本地存储的token
// 判断cookie有没有存储token,有的话加入到请求头里
if (token) {
if (config && config.headers) {
config.headers['token'] = token//在请求头中加入token
}
}
// 如果还需要在请求头内添加其他内容可以自己添加 [] 内为自定义的字段名 = 后的内容为字段名对应的内容
// config.headers['api'] = api
return config
}, error => {
// 对请求错误做些什么
return Promise.reject(error)
})
/** 添加响应拦截器 **/
instance.interceptors.response.use(response => {
if (response.statusText === 'OK') {
return Promise.resolve(response.data)
} else {
return Promise.reject(response.data.msg)
}
}, error => {
// 请求报错的回调可以和后端协调返回什么状态码,在此根据对应状态码进行对应处理
if (error.response) {
// 如401我就让用户返回登录页
if (error.response.status === 401) {
// this.props.history.push('/login');
}
return Promise.reject(error)
} else {
return Promise.reject('请求超时, 请刷新重试')
}
})
/* 统一封装get请求 */
export const get = (url, params, config = {}) => {
debugger
return new Promise((resolve, reject) => {
instance({
method: 'get',
url,
params,
...config
}).then(response => {
resolve(response)
}).catch(error => {
reject(error)
})
})
}
/* 统一封装post请求 */
export const post = (url, data, config = {}) => {
return new Promise((resolve, reject) => {
instance({
method: 'post',
url,
data,
...config
}).then(response => {
resolve(response)
}).catch(error => {
reject(error)
})
})
}
export async function select(params?:QuestionParams){
get('/article/select')
}
安装ahooks
配置代理 proxy
==位置 src下的setupProxy.js 一定是这个名 别的都不识别== ==还有位置注意是src下==
安装中间件: npm install http-proxy-middleware
==修改完一定要重启 配置才能生效==
注意中间件==版本不同 写法不同== 我这里是新版的写法
// commonJs写法 是前端模块化的一种规范
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
createProxyMiddleware('/article', {
//api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
target: 'http://localhost:8080', //配置转发目标地址(能返回数据的服务器地址)
changeOrigin: true, //控制服务器接收到的请求头中host字段的值
// pathRewrite: { '^/article': '' }, //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
}),
createProxyMiddleware('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: { '^/api2': '' },
})
)
}
也可以写在package.json里面 但不方便
配置mock
注意mock文件放到src下面
在app.tsx中引入mock文件
import './mock';
安装引入redux-toolkit
自动生成一个完成的
如何使用还未知
配置按需引入 覆盖webpack默认配置 config.override.js
craco
的作用类似 react-app-rewired
,但是比它的配置更友好。craco
将常用的配置提取出来作为配置项,而 react-app-rewired
只能通过转换函数的方式修改 webpack
的配置,往往还需要 customize-cra
配合使用。
最近在熟悉react,通过react create app创建项目后,并不像以前一样可以进行webpack的配置,官方文档有写到可以npm run eject暴露所有配置,会发现根目录下生成了 config 目录,但是此操作是不可逆的。
如果需要在项目中配置一些webpack配置,需要在根目录下新建一个名称为config-overrides.js的文件
1、下载antd 包
npm install antd
2、安装customize-cra,引入react-app-rewired插件
react-app-rewired的作用就是在不eject的情况下,覆盖create-react-app的配置
npm install react-app-rewired customize-cra babel-plugin-import
3、自定义less-loader,改变antd默认样式
npm install less less-loader
4、在根目录下面新建config-overrides.js,并修改相关配置
const { override, fixBabelImports, addLessLoader, addWebpackAlias } = require("customize-cra");
const path = require("path");
module.exports = override(
// 针对antd 实现按需打包:根据import来打包 (使用babel-plugin-import)
fixBabelImports("import", {
libraryName: "antd",
libraryDirectory: "es",
style: true, //自动打包相关的样式 默认为 style:'css'
}),
//增加路径别名的处理
addWebpackAlias({
'@': path.resolve('./src')
})
);
5、修改packge.json 的配置文件
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react--app-rewired test",
"eject": "react-scripts eject"
},
6、最后,在app.js引入需要的组件即可
import { Button, Table } from "antd";
npm install antd babel-plugin-import *--save*
[create-react-app 创建 react 项目 css 模块化处理]
引入less
less作为预编译语言 需要转换为css才能执行,与此相似的还有sass
less相比于css 具有诸多优点
1 嵌套写类选择器 省去堆叠
2 定义变量 方便进行主题修改
关键字搜索: react customize-cra 配置less modules 或者直接搜索module.export addLoadLess
查看网上,基本上都是eject,把webpack暴露出来更改,太吓人了
1 安装less load
npm install less less-loader --dev
2 配置config-overrides.js文件
根据版本不同写法不同
"customize-cra": "^1.0.0-alpha.0",
"less": "^4.1.3",
const theme = require("package.json").theme;
addLessLoader({
lessOptions: { // 这层不写会报错 根据版本不同
javascriptEnabled: true,
localIdentName: '[local]--[hash:base64:5]',
modifyVars: theme
}
}),
3 配置支持less后缀 ,CRA默认不支持需要手动配置
/// <reference types="react-scripts" />
// 添加了对导入资源文件的支持
declare module "*.less" {
const content: { [className: string]: string };
export default content;
}
4 需要模块化引入 文件后缀module.less
==5 之后修改配置把module省略 就顺眼很多了==
默认配置文件 react-app-env.d.ts
1、react-app-env.d.ts的作用
在使用create-react-app xxx --typescript
生成一个react typescript
项目时,在src目录下会生成一个react-app-env.d.ts
类型声明文件
/// <reference types="react-scripts" />
三斜线指令是包含单个XML
标签的单行注释。 注释的内容会做为编译器指令使用。
三斜线指令仅可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。 如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。
三斜线引用告诉编译器在编译过程中要引入的额外的文件。
三斜线指令中有两种types
和 path
两种不同的属性,它们的区别是:types
用于声明对另一个库的依赖,而 path
用于声明对另一个文件的依赖。上面react-app-env.d.ts
依赖react-scripts库的类型声明文件,react-scripts
下的package.json
中types
指定了TypeScript
的入口文件
当项目编译时将会根据tsconfig.json
中include
指定的目录去找代码所需要的类型声明文件,而react-app-env.d.ts
会告诉编译器含有哪些类型声明,里面含有一些常用的类型声明,比如react、react-dom
的一些API
类型声明,图片、样式模块类型声明等等。
有了上面的类型声明,我们就可以在项目中直接引入图片或者样式
import contentBg from "@assets/image/content-bg.png"
import styles from './index.module.scss';
假如引入的包没有相应的类型声明呢?
比如使用gridmanager-react
制作表格,但是目前gridmanager-react
还没有提供类型声明,怎么办呢?其实可以在react-app-env.d.ts
或者在src
目录下另外定义一个.d.ts
文件中加上该模块的类型声明
/// <reference types="react-scripts" />
declare module 'gridmanager-react' {
const classes: any;
export default classes;
}
这样,再导入gridmanager-react
就不会报如下错误:
2、react-app-env.d.ts如何生成的
把react-app-env.d.ts
文件删掉,然后执行yarn start
启动项目会报类型错误吗?
答案是不会,在执行构建之前会重新生成react-app-env.d.ts
快捷清除clg 不使用用debugger
console.log.*$
1 模块化配置 css写法 试一下
2 数据返回后需要进行是否为空的判断
// 2 webpack打包+eslint+typescript data.d.ts配置
路由鉴权实现:
1 遇到一直循环的问题
需要加入else条件判断
2 一堆userxxx ^^^^^^^^^^^
)
import { message } from "antd";
import { useEffect } from "react";
import {
Location, NavigateFunction, useLocation, useNavigate, useRoutes
} from "react-router-dom";
import { useAppSelector } from "../app/hooks";
import { selectAccessIdList } from "../features/counter/counterSlice";
// import { message } from "antd";
interface RouteObject {
caseSensitive?: boolean;
children?: RouteObject[];
element?: React.ReactNode;
index?: boolean;
path?: string;
auth?: boolean;
accessId?: string
}
//递归查询对应的路由
export function searchroutedetail(
path: string,
routes: RouteObject[]
): RouteObject | null {
for (let item of routes) {
if (item.path === path) return item;
if (item.children) {
return searchroutedetail(path, item.children);
}
}
return null;
}
//全局路由守卫
function Guard(
location: Location,
navigate: NavigateFunction,
routes: RouteObject[],
accessIdList: string[]
) {
const { pathname } = location;
let routedetail = searchroutedetail(pathname, routes);
console.log('路由拦截目标地址', routedetail)
console.log(sessionStorage)
//没有找到路由,跳转*404
if (pathname === "/404") {
return;
}
if (pathname === "/403") {
return;
}
if (!routedetail) {
navigate("/404");
} else if (routedetail.path === "/login") {
console.log("login页面正常跳转 不拦截");
} else if (sessionStorage.length === 0) {
message.warn("请登录");
navigate("/login");
} else if (sessionStorage) {
console.log("有session正常跳转 下一步判断权限");
// 如果没有权限
if (routedetail.accessId) {
if (accessIdList.includes(routedetail.accessId)) {
console.log("权限列表包含正常跳转 不拦截", routedetail.accessId)
return;
}
else {
navigate("/403")
}
}
return true
}
}
export const RouterGuard = (routes: any) => {
const location = useLocation();
const navigate = useNavigate();
const accessIdList = useAppSelector(selectAccessIdList)
console.log('accessIdList', accessIdList);
useEffect(() => {
Guard(location, navigate, routes, accessIdList)
}, [location, navigate, routes, accessIdList]);
// document.documentElement.scrollTo(0, 0);
const Route = useRoutes(routes);
return Route;
}
懒加载
react16后具备新特性suspense和loading lazy() 实现懒加载
function App() {
return (
<Suspense>{
// routerIndex就是路由表
RouterGuard(RoutesIndex)
}</Suspense>
)
}
export default App;
/**
* @Description: 路由统一配置
* @Author: liuzy521
* @Date: 2022-09-20
* @LastEditTime: 2022-09-29
* @LastEditors: liuzy521
*/
import FourOThree from '../components/403';
import Error from '../components/404';
// import HomePage from '../pages/HomePage';
import LoginIndex from '../pages/Login';
import Message from '../pages/Message';
import Details from '../pages/Message/details';
import News from '../pages/News';
import Testbb from '../pages/testbb';
import { lazy } from "react";
const HomePage = lazy(() => import("../pages/HomePage/index"));
export interface RouteObject {
caseSensitive?: boolean;
children?: RouteObject[];
element?: any;
index?: boolean;
path?: string;
auth?: boolean
accessId: string
}
/**
* @description: 全局路由配置
* @param {string} accessId // 路由页面权限id
*/
const RoutesIndex: RouteObject[] = [
{
path: '/login',
element: <LoginIndex />,
accessId: '10000',
auth: true
},
{
path: '/',
element: <HomePage />,
accessId: '10007',
auth: true,
children: [
{
path: '/testbb',
element: <Testbb />,
accessId: '10004',
auth: true,
children: [
{
path: '/testbb/message',
element: <Message />,
accessId: '10005',
auth: true,
children: [
{
// path: 'detail/:id/:title/:content',
path: '/testbb/message/detail',
element: <Details />,
accessId: '10006',
}
]
},
{
path: 'news',
element: <News />,
accessId: '10008',
auth: true,
},
]
},
]
},
{
path: '/403',
element: <FourOThree />,
accessId: '10008',
auth: true,
},
{
path: '/404',
element: <Error />,
accessId: '10009',
}
]
export {
RoutesIndex,
};
组件渲染,需要注意的是使用react.lazy动态加载路由必须指定Suspense,并添加回调渲染组件,用于在加载组件过程中显示loading效果,如果不指定该组件,react会报错
如何判断 懒加载 是否真的 ‘懒加载: 看source左侧
懒加载可以解决首屏加载过慢的问题
登录前 左侧文件 点击登录完成的文件
日后再说 nginx 配置
正式发布到nginx服务器上时,需要解决browserHistory模式带来的404问题,参照下面配置做nginx.conf的修改。
``
location / {
# browserHistory模式 404问题
try_files $uri $uri/ /react/index.html;
index index.html;
}
``
小问题
1 省略module的webpack配置
2 eslint自定义 配置
3 删除空行 ^\s*(?=\r?$)\n
4 相对路径优化 @ 绝对路径
5 设置ctrl s 全部保存快捷键
6 tsconfig.json baseUrl:src 后续用@
样式文件也可以
7 sass
yarn add sass
绝对路径加~
1 获取staffInfo信息
2 获取accessMenuList信息
3 存到cookies中
4 守卫渲染这里面的数据
login登录之前无需守卫
路由监听引入的位置
1 element映射为路径
2 routes先存到token
注释规范vscode配置
src绝对路径
import LoginIndex from 'pages/login';
引入 redux持久化插件 redux-persist
==思路:404无法回退: 设置监听事件 当为404时 navigate(-2) 退回两次==
npm i redux-persist --save
app/store.ts
import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit';
import { combineReducers } from 'redux';
import thunk from 'redux-thunk';
// 持久化存储
import { persistReducer, persistStore } from 'redux-persist';
import storage from 'redux-persist/es/storage';
import counterSlice from '../features/counter/counterSlice';
// 缓存数据配置
const persistConfig = {
key: 'root',
storage,
blacklist: [ ] // 写在这块的数据不会存在storage
}
const reducers = combineReducers({
counterSlice
})
const persistedReducer = persistReducer(persistConfig,reducers)
export const store = configureStore({
reducer: persistedReducer,
// reducer: {
// counter: counterReducer,
// },
devTools: process.env.NODE_ENV !== 'production',
middleware: [ thunk ]
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
export const persist = persistStore(store) // 数据持久化存储
counterSlice.ts
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
staffInfoReducer:(state,action)=>{
// console.log('进到counter里面了')
state.staffInfo=action.payload
},
accessMenuListReducer:(state,action)=>{
state.accessMenuList=action.payload
console.log('仓库中的数据权限列表',state.accessMenuList)
}
}
});
export const { increment, decrement, incrementByAmount ,staffInfoReducer,accessMenuListReducer} = counterSlice.actions;
export default counterSlice.reducer;
sessionStorage
sessionStorage 是HTML5新增的一个会话存储对象, 用于临时保存同一窗口(或标签页)的数据(key/value), 在关闭窗口或标签页之后将会删除这些数据。是window下的对象。
seesionStorage的存储方式采用key、value的方式。value的值必须为字符串类型。
/**
* @Description: 公共api接口集合
* @Author: liuzy521
* @Date: 2022-09-18
* @LastEditTime: 2022-09-18
* @LastEditors: liuzy521
*/
// import http from '@/utils/request/http'
const GetUserInfo = {
// 获取用户信息
// getUserInfo: (data) => http('get', '', '/proxy/getUserInfo', data),
// 模拟从接口获取用户信息
getUserInfo () {
return new Promise((resolve) => {
setTimeout(() => {
console.log('got userInfo')
resolve({
data: {
userId: '666',
nickName: 'Neo',
accessMenuList: ['10000', '10001', '10002']
},
errorCode: 0,
})
}, 200)
})
},
}
export default GetUserInfo
index.tsx
<Provider store={store}>
<PersistGate persistor={persist}>
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
</PersistGate>
</Provider>
if (result.code === 200) {
Cookies.set('x-auth-token', "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdGFmZkNvZGUiOiIxMDYwMjU4NyIsIm9yZ0NvZGUiOiIwMDU2MDA4M")
(option)=>{
return{
}
}
json.parse(option.body)
过滤option.body
## 其他
表单初始化
验证
table loading加载效果
查询按钮加载效果 执行第二次不能点了 下面暗 防止毫秒级别练点
富文本编辑器
文件: 上传 下载 附件
nginx #刷新
表单初始化,loading加载效果,引入富文本编辑器和文件上传下载等细节优化
## scss使用
插件实时编译
@use @forward
\$light-red 未定义
预编译语言
### craco
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
localIdentName: '[local]--[hash:base64:5]',
javascriptEnabled: true,
},
},
fixBabelImports: ("import", {
libraryName: "antd",
libraryDirectory: "es",
style: true, //自动打包相关的样式 默认为 style:'css'
}),
options: {
module: true,
}
},
},
],
};
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
<https://www.likecs.com/ask-171833.html?sc=5900>
git commit line
`.cz-config.js `
module.exports = { // 可选类型 types: [ { value: 'feat', name: 'feat: 新功能' }, { value: 'fix', name: 'fix: 修复' }, { value: 'docs', name: 'docs: 文档变更' }, { value: 'style', name: 'style: 代码格式(不影响代码运行的变动)' }, { value: 'refactor', name: 'refactor: 重构(既不是增加feature,也不是修复bug)' }, { value: 'perf', name: 'perf: 性能优化' }, { value: 'test', name: 'test: 增加测试' }, { value: 'chore', name: 'chore: 构建过程或辅助工具的变动' }, { value: 'revert', name: 'revert: 回退' }, { value: 'build', name: 'build: 打包' } ], // 消息步骤 messages: { type: '请选择提交类型:', customScope: '请输入修改范围(可选):', subject: '请简要描述提交(必填):', body: '请输入详细描述(可选):', footer: '请输入要关闭的issue(可选):', confirmCommit: '确认使用以上信息提交?(y/n/e/h)' }, // 跳过问题 skipQuestions: ['body', 'footer'], // subject文字长度默认是72 subjectLimit: 72 }
package.json
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
},
## 富文本编辑器
难点: 光标位置错乱

level 0 DOM的contentEditable 原生编辑能力 输入流畅,但是浏览器兼容差,组合功能的实现复杂
level 1 quill(20w star) ;
draft.js (react):Facebook开源的开发React富文本编辑器开发框架,而是可以直接编写React组件实现编辑器的UI
level 2 slate.js 扩展性特别好 需要大量的二次开发
vscode 个性化配置
星星插件

## 前端规范 参考阿里
https://developer.aliyun.com/article/850913
## sourceTree
git 可视化版本管理工具 sourceTree 每次执行后会生成git命令供学习
1 官网下载
2 注册: 我是使用公司邮箱注册
3 密钥生成: generate 然后save private key 存储路径
C:\Users\Miss Liu\Documents\ssl\id_rsa.pub
## 规范Git检查工作流
husky+lint-staged+prettier+ commitlint
<https://cloud.tencent.com/developer/article/2045915?from=article.detail.1450249>
* **代码规范落地难**:归根结底在于需要工具去强行保证代码必须经过代码开发规范的扫描;
* **低质量代码带入线上应用**:最好的方式本地进行commit的时候,最起码需要保证当前代码能够满足团队制定的开发规范,如果不通过,commit都无法成功,这样能够从最源头保证代码质量问题;
* **代码格式难统一**:需要一种工具强制保证团队内代码的格式是一致;
* 最好的办法是在**本地提交代码时就能够扫描出潜在的错误**,并**强制将其修改后才能提交**,这样就不会将问题代码携带到线上,就
### 1 husky 用于git 拦截
1. 含义: Husky can prevent bad git commit, git push and more ? woof!
2. 安装:
<!---->
npm install husky --save-dev
3. 在package.json中增加的 prepare命令,执行 husky install 这时会在根目录生成 .husky 文件夹,如图:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"prepare": "husky install"
},
4. 运行完会生成.husky文件夹,在.husky文件夹下有一个pre-commit,这个文件是用来定义git commit之前应该执行什么命令,默认内容如下
生成 husky pre文件
npx husky add .husky/pre-commit =
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"
pre-commit 内容修改 如下 最后一行的undefined改为lint-staged ==代码校验==
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
commit-msg 如下 ==提交信息校验==
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no -- commitlint --edit $1
### 2 lint-staged 对暂存 git 校验(add . 而不是全部文件)
yarn add --dev husky lint-staged
.lintstagedrc.js
module.exports = {
"src/**/\*{js,jsx,ts,tsx,md,html}": ["eslint", "prettier --write"],
"src/**/*.scss": ["stylelint --fix", "git add"],
}
或者在package.json 里添加如下代码
```javascript
{
"lint-staged": {
"src/**/*.{less,scss,css}": [
"stylelint --fix --syntax=less",
"git add ."
],
"src/**/*.{js,ts,tsx,vue}": [
"eslint ./src --ext .js,.tsx,.ts,.vue --cache --fix",
"git add ."
]
},
"scripts": {
"lint": "eslint --fix src",
"lint:style": "stylelint --fix ./**/*.{scss,css,vue} --custom-syntax",
"prepare": "husky install"
},
"devDependencies": {
"@blueking/eslint-config-bk": "^2.1.0-beta.6",
"@blueking/stylelint-config-bk": "^2.0.0",
"@commitlint/cli": "^17.0.3",
"@commitlint/config-conventional": "^17.0.3",
"bk-vision-cli": "^4.0.4",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
}
}
3 prettier自动修正 格式化
- 安装
yarn add --dev --exact prettier
2. 在最外层添加配置文件
echo {}> .prettierrc.json
3. 测试prettier会不会自动修正
新建一个test1.js文件
var a = 0
var b = 555;
var c = function () {
return 0;
}
运行prettier
npx prettier --write test1.js
会自动修正
4.commitlint
- 安装
yarn add --save-dev @commitlint/config-conventional @commitlint/cli
创建commitlint.config.js ==前面没有.==
/**
* feature:新功能
* update:更新某功能
* fix:修补某功能的bug
* refactor:重构某个功能
* optimize: 优化构建工具或运行时性能
* style:仅样式改动
* docs:仅文档新增/改动
* chore:构建过程或辅助工具的变动
*/
module.exports = {
extends: [
'@commitlint/config-conventional'
],
rules: {
'type-enum': [2, 'always', [
'feature', 'update', 'fix', 'refactor', 'optimize', 'style', 'docs', 'chore'
]],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 72]
}
};
5 git提交规范不对导致报错
git commit -m "fix: 测试"
如果想跳过 就删除husky 的commit-msg 最后一行 不建议!
koro1FileHeader
文件头:control + ⌘ + i
方法:control + ⌘ + t
兼容性问题
原因:
react16.8 之后框架不支持ie兼容 js不兼容 还会有语法不兼容的情况 没报错说明是框架本身的问题 不是部分语法的问题
尝试1
html添加:不好使
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
尝试2:craco.config.ts webpack添加配置
alias: {
'react': path.resolve('./node_modules/react'),
'react-dom': path.resolve('./node_modules/react-dom'),
},
尝试3
最后一招: 安装polyfill 兼容性工具解决
package.json
postcss
babel
babel7之前
"presets":["stage-0"]
browserslist
官网: browsersl.ist/
位置: package.json配置或者单独文件都可
实现功能: 设置兼容浏览器支持
能兼容node版本 ios 各种版本 近两年 哪年开始等等以及not 兼容哪些
注意`: webpack配置 proset target会覆盖browserlist 尽量不要在这配 仅js生效
命令查询
npx browserslist
npx browserslist ">1%,last 2 years"
是或的关系
chrome 79兼容的话 chrome8也兼容 但是市场占有率低 所以没列出来
"browserslist": {
"production": [
">0.2%",// 市场占有率 eg >5% in US
"last 2 version", //最新两个版本
"not dead",// 24个月之内都没有官网更新 不再兼容
"Firefox ESR",// 专为一些学校提供的内核
"ie > 9"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version",
"ie > 9"
]
},
工程化
webpack
exclude: /node module/
bundle.js
entry 第三方库
usebuiltlns
.home_sider
:global{
.uni-layout-sider-children{
background: white
}
.uni-menu-sub-menu{
background: white
}
}
home.less
改样式
@layout-body-background定制主题
菜单和省分菜单
左侧菜单实际用的是省分菜单的代码 !! 坑死我了!!!!!
不能相信名字 要一点一点定位
defaultSelectedKeys={['1']}
npm install yarn@latest -g
Error: http://ccp.tianti.tg.unicom.local/artifactory/api/npm/sjxt-npm-virtual/@ant-design/pro-components/-/pro-components-2.6.35.tgz: Request failed "404 Not Found"
at ResponseError.ExtendableBuiltin (D:\nodejs\node_global\node_modules\yarn\lib\cli.js:696:66)
at new ResponseError (D:\nodejs\node_global\node_modules\yarn\lib\cli.js:802:124)
at Request.<anonymous> (D:\nodejs\node_global\node_modules\yarn\lib\cli.js:67099:16)
at Request.emit (node:events:513:28)
at Request.module.exports.Request.onRequestResponse (D:\nodejs\node_global\node_modules\yarn\lib\cli.js:141760:10)
at ClientRequest.emit (node:events:513:28)
at HTTPParser.parserOnIncomingClient (node:_http_client:674:27)
at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
at HTTPParser.execute (<anonymous>)
at Socket.socketOnData (node:_http_client:521:22)
改了menu和layout为antd menu
.npmrc 配置多镜像源
registry=https://registry.npm.taobao.org/
@union-design:registry=http://ccp.tianti.tg.unicom.local/artifactory/api/npm/sjxt-npm-virtual/
npm config list 查看配置
设置折叠代码
const [marginStyle, setMarginStyle] = useState<any>({
marginLeft:'210px'
})
// 折叠展开
const onChangeVisible=(e:boolean)=>{
console.log(e)
// 折叠
if(e===false){
setMarginStyle(null)
}
//展开
else if(e===true){
setMarginStyle({
marginLeft:'210px'
})
}
}
<Content className={styles.content} style={marginStyle}>
classname加判断逻辑
className={`${collapsed===true? styles.home_sider_Collapsed : styles.home_sider} `}
第三方代码处理bundle代码
第一种 不使用polyfill
第二种 使用usage
第三种 使用entry
webpack
webpack.docschina.org/concepts/
webpack+babel
优秀博客
了解
entry:入口 指明了需要打包文件的入口,让webpack知道从按个文件开始打包 output: 输出 指明了打包的文件要输出到哪里去,并且如何命名等 loader:加载器 由于webpack本省的功能很少,只能解析js、json等资源,所以在处于其他资源的时候就需要借助loader,webpack才能解析 plugins:插件 就好比浏览器的插件一样,可以扩展webpack的功能,我们需要下载并且引用它 mode:模式 生产模式:production 开发模式:deveplopment
1 安装webpack
conflict peer dependency or retry this command with
这个问题时因为npm的v7以后的版本都默认安装了peerDependency,它虽然解决了依赖安装的冗余的问题,但有时也会导致依赖的包版本与各个子项目依赖的包版本相互不兼容,所以安装时要加上--legacy-peer-deps
2
webpack
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
app: path.resolve(__dirname, 'src/app'), // 适应你的项目结构
services: path.resolve(__dirname, 'src/services'), // 适应你的项目结构
routes: path.resolve(__dirname, 'src/routes'), // 适应你的项目结构
layout:path.resolve(__dirname, 'src/layout'),
features:path.resolve(__dirname, 'src/features'),
components:path.resolve(__dirname, 'src/components'),
pages: path.resolve(__dirname, 'src/pages'), // 适应你的项目结构
context: path.resolve(__dirname, 'src/context'), // 适应你的项目结构
assets: path.resolve(__dirname, 'src/assets'), // 适应你的项目结构
utils: path.resolve(__dirname, 'src/utils'), // 适应你的项目结构
}
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /\.module.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
},
},
'sass-loader',
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/, // 匹配 .scss 或 .sass 文件
use: [
'style-loader', // 创建 <style> 标签并将样式插入其中
'css-loader', // 将 CSS 转换为 CommonJS 模块
'sass-loader' // 将 Sass 编译为 CSS
],
},
{
test: /\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
loader: 'file-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
}),
new MiniCssExtractPlugin(),
],
devServer: {
contentBase: './build',
port: 3000,
historyApiFallback: true,
},
};
报错
{
test: /\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
loader: 'file-loader',
},
throw new Error("Module build failed (from ./node_modules/sass-loader/dist/cjs.js):\nexpected "{".\n ╷\n2 │ import API from "!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";\n │ ^\n ╵\n src\pages\videoZone\index.module.scss 2:101 root stylesheet");
npm install sass-loader@latest
npm install node-sass@latest
npm cache clean --force
npm install
yarn add postcss-loader
yarn add style-loader
yarn add sass-loader
yarn add postcss-preset-env
webpack --config webpack.config.js
webpack -cli 命令行
webpack 代码中
autoprefixer -D 自动添加浏览器前缀
{
test: /\.css$/,
use: [
'style-loader', 'css-loader',
{
loader: 'postcss-loader',
options:{
postcssOptions:{
plugins:"autoprefixer"
}
}
}
],
},
postcss是一个功能 给postcss再添加插件
postcss-preset-env 预设 安装对应的插件,配置对应的插件
错误:
bbs-web\node_modules\style-loader\dist\cjs.js!D:\react\bbsWebTest\bbs-web\node_modules\css-loader\dist\t\bbsWebTest\bbs-web\src\pages\postReview\index.module.scss 1 | throw new Error("Module build failed (from ./node_modules/sass-loader/dist/cjs.js):\nexpected "{".\n ╷\njectStylesIntoStyleTag.js";\n │
- style-loader: 通过注入
<style>
标签将CSS添加到DOM 查看webpack中文网解释 - css-loader: 解释
@import
和url()
, 会import/require()
再解析(resolve)
它们查看webpack中文网解释
很多人在说版本不兼容的问题
11.9
原因:
react16.8 之后框架不支持ie兼容 js不兼容 还会有语法不兼容的情况 没报错说明是框架本身的问题 不是部分语法的问题
尝试1 html配置
html添加:不好使
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
尝试2 修改已有webpack配置
craco.config.ts webpack添加配置
alias: {
'react': path.resolve('./node_modules/react'),
'react-dom': path.resolve('./node_modules/react-dom'),
},
尝试3 browserslist
官网: browsersl.ist/
位置: package.json配置或者单独文件都可
实现功能: 设置兼容浏览器支持
能兼容node版本 ios 各种版本 近两年 哪年开始等等以及not 兼容哪些
注意`: webpack配置 proset target会覆盖browserlist 尽量不要在这配 仅js生效
命令查询
npx browserslist
npx browserslist ">1%,last 2 years"
是或的关系
chrome 79兼容的话 chrome8也兼容 但是市场占有率低 所以没列出来
"browserslist": {
"production": [
">0.2%",// 市场占有率 eg >5% in US
"last 2 version", //最新两个版本
"not dead",// 24个月之内都没有官网更新 不再兼容
"Firefox ESR",// 专为一些学校提供的内核
"ie > 9"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version",
"ie > 9"
]
},
工程化
webpack
exclude: /node module/
bundle.js
entry 第三方库
usebuiltlns
webpack
webpack.docschina.org/concepts/
尝试4 polyfill
最后一招: 安装polyfill 兼容性工具解决
package.json
postcss
babel
babel7之前
"presets":["stage-0"]
webpack+babel
优秀博客
了解
entry:入口 指明了需要打包文件的入口,让webpack知道从按个文件开始打包 output: 输出 指明了打包的文件要输出到哪里去,并且如何命名等 loader:加载器 由于webpack本省的功能很少,只能解析js、json等资源,所以在处于其他资源的时候就需要借助loader,webpack才能解析 plugins:插件 就好比浏览器的插件一样,可以扩展webpack的功能,我们需要下载并且引用它 mode:模式 生产模式:production 开发模式:deveplopment
webpack
官网: webpack.docschina.org/concepts/
1 安装webpack
安装webpack和cli
webpack -cli 命令行
webpack 代码中
11.9
原因:
react16.8 之后框架不支持ie兼容 js不兼容 还会有语法不兼容的情况 没报错说明是框架本身的问题 不是部分语法的问题
尝试1 html配置
html添加:不好使
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
尝试2 修改已有webpack配置
craco.config.ts webpack添加配置
alias: {
'react': path.resolve('./node_modules/react'),
'react-dom': path.resolve('./node_modules/react-dom'),
},
尝试3 browserslist
官网: browsersl.ist/
位置: package.json配置或者单独文件都可
实现功能: 设置兼容浏览器支持
能兼容node版本 ios 各种版本 近两年 哪年开始等等以及not 兼容哪些
注意`: webpack配置 proset target会覆盖browserlist 尽量不要在这配 仅js生效
命令查询
npx browserslist
npx browserslist ">1%,last 2 years"
是或的关系
chrome 79兼容的话 chrome8也兼容 但是市场占有率低 所以没列出来
"browserslist": {
"production": [
">0.2%",// 市场占有率 eg >5% in US
"last 2 version", //最新两个版本
"not dead",// 24个月之内都没有官网更新 不再兼容
"Firefox ESR",// 专为一些学校提供的内核
"ie > 9"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version",
"ie > 9"
]
},
工程化
webpack
exclude: /node module/
bundle.js
entry 第三方库
usebuiltlns
webpack
webpack.docschina.org/concepts/
尝试4 polyfill
最后一招: 安装polyfill 兼容性工具解决
package.json
postcss
babel
babel7之前
"presets":["stage-0"]
webpack+babel
优秀博客
了解
entry:入口 指明了需要打包文件的入口,让webpack知道从按个文件开始打包 output: 输出 指明了打包的文件要输出到哪里去,并且如何命名等 loader:加载器 由于webpack本省的功能很少,只能解析js、json等资源,所以在处于其他资源的时候就需要借助loader,webpack才能解析 plugins:插件 就好比浏览器的插件一样,可以扩展webpack的功能,我们需要下载并且引用它 mode:模式 生产模式:production 开发模式:deveplopment
webpack
官网: webpack.docschina.org/concepts/
1 安装webpack
安装webpack和cli
webpack -cli 命令行
webpack 代码中
conflict peer dependency or retry this command with
这个问题时因为npm的v7以后的版本都默认安装了peerDependency,它虽然解决了依赖安装的冗余的问题,但有时也会导致依赖的包版本与各个子项目依赖的包版本相互不兼容,所以安装时要加上--legacy-peer-deps
2
2 .webpack文件备份1
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
app: path.resolve(__dirname, 'src/app'), // 适应你的项目结构
services: path.resolve(__dirname, 'src/services'), // 适应你的项目结构
routes: path.resolve(__dirname, 'src/routes'), // 适应你的项目结构
layout:path.resolve(__dirname, 'src/layout'),
features:path.resolve(__dirname, 'src/features'),
components:path.resolve(__dirname, 'src/components'),
pages: path.resolve(__dirname, 'src/pages'), // 适应你的项目结构
context: path.resolve(__dirname, 'src/context'), // 适应你的项目结构
assets: path.resolve(__dirname, 'src/assets'), // 适应你的项目结构
utils: path.resolve(__dirname, 'src/utils'), // 适应你的项目结构
}
},
module: {
rules: [
{
test: /.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /.module.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
},
},
'sass-loader',
],
},
{
test: /.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /.s[ac]ss$/, // 匹配 .scss 或 .sass 文件
use: [
'style-loader', // 创建 <style> 标签并将样式插入其中
'css-loader', // 将 CSS 转换为 CommonJS 模块
'sass-loader' // 将 Sass 编译为 CSS
],
},
{
test: /.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
loader: 'file-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
}),
new MiniCssExtractPlugin(),
],
devServer: {
contentBase: './build',
port: 3000,
historyApiFallback: true,
},
};
3. 报错1 文件配置
{
test: /.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
loader: 'file-loader',
},
报错2 sass loader
throw new Error("Module build failed (from ./node_modules/sass-loader/dist/cjs.js):\nexpected "{".\n ╷\n2 │ import API from "!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";\n │ ^\n ╵\n src\pages\videoZone\index.module.scss 2:101 root stylesheet");
安装css loader命令
npm install sass-loader@latest
npm install node-sass@latest
npm cache clean --force
npm install
yarn add postcss-loader
yarn add style-loader
yarn add sass-loader
yarn add postcss-preset-env
webpack --config webpack.config.js
autoprefixer -D 自动添加浏览器前缀
{
test: /.css$/,
use: [
'style-loader', 'css-loader',
{
loader: 'postcss-loader',
options:{
postcssOptions:{
plugins:"autoprefixer"
}
}
}
],
},
postcss是一个功能 给postcss再添加插件
postcss-preset-env 预设 安装对应的插件,配置对应的插件
1 | throw new Error("Module build failed (from ./node_modules/sass-loader/dist/cjs.js):\nexpected "{".\n ╷\njectStylesIntoStyleTag.js";\n │
- style-loader: 通过注入
<style>
标签将CSS添加到DOM 查看webpack中文网解释 - css-loader: 解释
@import
和url()
, 会import/require()
再解析(resolve)
它们查看webpack中文网解释
很多人在说版本不兼容的问题
1 .打包css-loader 顺序 自定义解析
{
test: /.css$/,
use: ['style-loader', 'css-loader','postcss-loader'],
},
完整写法 是对象 里面可以加options 从右向左读取 style-loader写在前面,在css基础上继续读取
{
test: /.css$/,
use: [
{ loader: 'style-loader'},
{
loader: 'css-loader',
options: {
modules: true,
},
}
],
},
less 报错 添加less
{
test: /.less$/,
use: ['style-loader', 'css-loader','less-loader'],// 先less 再css 再style
},
postcss插件 -postcssOptions-plugins
1 postcss是一个功能 给postcss再添加插件
{
test: /.css$/,
use: ['style-loader', 'css-loader','postcss-loader'],// postcss 在里面添加插件 自动添加浏览器前缀 css样式重置
},
2 autoprefixer -D 自动添加浏览器前缀
{
test: /.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options:{
postcssOptions:{ // 抽取从这里开始 规则是插件自己定义的
plugins:"autoprefixer"
}
}
}
],
},
3 postcss-loader可以单独抽取一个文件 postcss.config.js*=*> module.exports={plugins:"autoprefixer"}
postcss插件-postcss-preset-env 插件
1 预设 安装对应的插件,配置对应的插件 相当于内置autoprefixer
可以将现代css转化为大多数浏览器认识的css,并且根据目标浏览器或运行时环境添加所需的 polyfill
module.exports={plugins:"postcss-preset-env"}
2 将px转化为rem vw
=> webpack不是本身强大, 社区强大 插件很多
现代的前端开发模式,不需要手动添加浏览器前缀 比如umi
2. 打包资源文件--加载文件-内置
{
test: /.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
// loader: 'file-loader',
type: "asset"
},
{
test: /.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
// loader: 'file-loader',
type: "asset/resource" // 发现生成图片,而且重命名 设置了新路径
// type: "asset/inline" // 将图片base64编码 直接放到js中
},
性能:
inline高 优:少发送n个文件次http网络请求,缺点: js时间长
resource: 缺:多网络请求
合理规范: 对于小图片base64 编码,对于大图片单独url-设置
默认asset:自动
{
test: /.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
// loader: 'file-loader',
// 设置加载类型
type: "asset",
// 控制加载大小
parser:{
dataUrlCondition:{
maxSize:60*1024
}
},
// 重命名: 占位符 +8位hash+扩展符
generator:{
// hash值 唯一
filename:"[name]_[hash:8][ext]"
}
},
3 打包js文件-babel-loader
es6不兼容ie8,只在ie10、ie11中兼容了部分es6的API;实现兼容的方法:可以利用“babel-loader”在ie8中把es6的代码编译成es5执行,使用“npm install babel-loader”即可安装
es6转es5
1 babel
{
test: /.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: 'babel-loader',
options:{
plugins:[
// 箭头函数变function 不可能每个语法都用插件
"@bable/plugin-transform-arrow-functions"
]
}
},
2 babel预设 常见三种预设
babel.config.js
module.exports={
// plugins:[
// // 箭头函数变function 不可能每个语法都用插件
// "@bable/plugin-transform-arrow-functions"
// ]
presets: [
"@babel/preset-env",//es6
"@babel/preset-react",
"@babel/preset-typescript"
]
}
"build": "webpack --mode development",
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
publicPath :'/bbs/'
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
app: path.resolve(__dirname, 'src/app'),
services: path.resolve(__dirname, 'src/services'),
routes: path.resolve(__dirname, 'src/routes'),
layout:path.resolve(__dirname, 'src/layout'),
features:path.resolve(__dirname, 'src/features'),
components:path.resolve(__dirname, 'src/components'),
pages: path.resolve(__dirname, 'src/pages'),
context: path.resolve(__dirname, 'src/context'),
assets: path.resolve(__dirname, 'src/assets'),
utils: path.resolve(__dirname, 'src/utils'),
}
},
module: {
rules: [
{
test: /.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /.module.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'style-loader'},
{
loader: 'css-loader',
options: {
modules: true,
},
},
'sass-loader',
],
},
{
test: /.css$/,
use: ['style-loader', 'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions:{ // 抽取从这里开始 规则是插件自己定义的
plugins:"postcss-preset-env"
}
},
},],
},
{
test: /.s[c]ss$/, // 匹配 .scss 或 .sass 文件
use: [
'style-loader', // 创建 <style> 标签并将样式插入其中
'css-loader', // 将 CSS 转换为 CommonJS 模块
'sass-loader' // 将 Sass 编译为 CSS
],
},
{
test: /.less$/, // 匹配 .scss 或 .sass 文件
use: [
'style-loader', // 创建 <style> 标签并将样式插入其中
'css-loader', // 将 CSS 转换为 CommonJS 模块
'less-loader' // 将 less 编译为 CSS
],
},
// 资源文件
{
test: /.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)$/,
// loader: 'file-loader',
// 设置加载类型
type: "asset",
// 控制加载大小
parser:{
dataUrlCondition:{
maxSize:60*1024
}
},
// 重命名: 占位符 +8位hash+扩展符
generator:{
// hash值 唯一
filename:"[name]_[hash:8][ext]"
}
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
}),
new MiniCssExtractPlugin(),
],
devServer: {
contentBase: './build',
port: 3000,
historyApiFallback: true,
},
};
craco 直接改webpack
问题截图:
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to: - add a fallback 'resolve.fallback: { "process": require.resolve("process/browser") }' - install 'process' If you don't want to include a polyfill, you can use an empty module like this: resolve.fallback: { "process": false } @ ./src/App.vue?vue&type=script&lang=js 1:0-189 1:0-189 1:190-368 1:190-368 @ ./src/App.vue 2:0-54 3:0-49 3:0-49 6:49-55 @ ./src/main.js 4:0-28 6:22-25
webpack compiled with 1 error 问题描述: 查了很多资料发现是因为webpack版本引起的,在webpack5中移除了nodejs核心模块的polyfill自动引入,具体可查看这篇文章
通过对日志的分析因为有其他组件引用到了 polyfills 的核心组件并没有安装,所以报错了,这里需要执行 npm install 命令进行包安装即可。
原因是由于在webpack5中移除了nodejs核心模块的polyfill自动引入,所以需要手动引入
解决方案: 1、运行下面这行指令,安装在 Webpack 中 Polyfill Node.js 核心模块。
npm install node-polyfill-webpack-plugin 2、在vue.config.json中添加(本文作者没有用到这一步,用第一步命令安装后就可以运行)
//头部引用 const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
//加入 configureWebpack: { plugins: [new NodePolyfillPlugin()] } 完整vue.config.json文件如下:
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
module.exports = defineConfig({ configureWebpack: { plugins: [new NodePolyfillPlugin()] } })
craco 直接改webpack
问题截图:
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to: - add a fallback 'resolve.fallback: { "process": require.resolve("process/browser") }' - install 'process' If you don't want to include a polyfill, you can use an empty module like this: resolve.fallback: { "process": false } @ ./src/App.vue?vue&type=script&lang=js 1:0-189 1:0-189 1:190-368 1:190-368 @ ./src/App.vue 2:0-54 3:0-49 3:0-49 6:49-55 @ ./src/main.js 4:0-28 6:22-25
webpack compiled with 1 error 问题描述: 查了很多资料发现是因为webpack版本引起的,在webpack5中移除了nodejs核心模块的polyfill自动引入,具体可查看这篇文章
通过对日志的分析因为有其他组件引用到了 polyfills 的核心组件并没有安装,所以报错了,这里需要执行 npm install 命令进行包安装即可。
原因是由于在webpack5中移除了nodejs核心模块的polyfill自动引入,所以需要手动引入
解决方案: 1、运行下面这行指令,安装在 Webpack 中 Polyfill Node.js 核心模块。
npm install node-polyfill-webpack-plugin 2、在vue.config.json中添加(本文作者没有用到这一步,用第一步命令安装后就可以运行)
//头部引用 const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
//加入 configureWebpack: { plugins: [new NodePolyfillPlugin()] } 完整vue.config.json文件如下:
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
module.exports = defineConfig({ configureWebpack: { plugins: [new NodePolyfillPlugin()] } })
conflict peer dependency or retry this command with
这个问题时因为npm的v7以后的版本都默认安装了peerDependency,它虽然解决了依赖安装的冗余的问题,但有时也会导致依赖的包版本与各个子项目依赖的包版本相互不兼容,所以安装时要加上--legacy-peer-deps
2
域名重定向网站打不开
host文件 内容清空 -还是打不开 但是这个错误不报了 排查下一个问题
C:\Windows\System32\drivers\etc
Copyright (c) 1993-2009 Microsoft Corp.
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
10.161.142.99 datav.cloud.view.newbuy.chinaunicom.cn
10.161.142.99 datav.oss.datav.cloud.view.newbuy.chinaunicom.cn
10.161.142.99 oss.datav.cloud.view.newbuy.chinaunicom.cn
# ::1 localhost
54.192.150.46 developer.mozilla.org
# This line is auto added by aTrustAgent, do not modify, or aTrustAgent may unable to work
127.0.0.1 localhost.sangfor.com.cn
# Added by Docker Desktop
192.168.124.5 host.docker.internal
192.168.124.5 gateway.docker.internal
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section
注意 前缀问题
本系统需要以bbs开头 在云端服务器需要配置bbs前缀方可访问
后续增加integrate功能 以integrate开头 如需其他前缀 自行配置
静态资源文件也统一以bbs开头 配置在cracod的webpack 如需更改 自行
nginx也配置了bbs开头
目前存在的一些问题
1 ts管理不严格 很多类型使用泛型
2 测试和生产变量未区分配置 目前是根据测试和生产地址来手动配置不同的地址
eg: let headerUrl= url.startsWith('http://10.111/')?2:1;
实现思路 :
const { REACT_APP_ENV } = process.env;
组件中判断变量实现不同的跳转逻辑
const InspectorWrapper = process.env.NODE_ENV === 'development' ? Inspector : React.Fragment;
const Layout: React.FC = ({ children }) => { return {children}; }; 3 未配置前端测试类 test.js 批量统一测试
4 兼容性问题未配置 统一使用polyfill处理 解决中
5 提交规范未完全生效 关键字配置生效 拦截未生效
6 删除无用代码 影响观看
7 路由拦截 :属于接口管理规范 后端返回的格式必须满足resultVo格式 否则前端全部拦截 promise结果返回reject
前端常用宝藏网站
1、CODEPEN
首先要介绍的第一个网站是CODEPEN,这是每一个前端工程师都知道的网站,顾名思义就是代码笔,是前端设计师测试在线测试代码的神器,同时也是前端设计学习和调试、寻找灵感的网站,全球超过180万以上前端设计师在这里分享作品和学习前端技术,除了支持自己的在线调试,还可以查看一些流行炫酷的JS效果。在这里大多数分享的作品都是开源的,你可以学习到优秀的开发人员是如何构建动画和测试功能。
2、w3school
这个网站提供了一些可供开发者参考的学习路径和学习资料,对初学者来说很有用的一个网站,比较适合前端新人,包括HTML、JavaScript、CSS等基本教程,教的都是很简单很容易上手的基础知识,如果你是零基础的前端小白,我建议你可以从这里开始,边学边练。
3、Responsively
一款专用于web开发的浏览器,在地址栏输入网址,你就能看到该网页在移动端和桌面端的显示效果,便于调整排版,提高开发效率,整体来说是一款非常不错的web开发工具。
4、Small Dev Tools
Small Dev Tools是一个前端工具网站,包含了很多实用的功能,比如JSON解码器、JSON格式化程序、UTF8编码、Base64编码、Base64解码、CSS格式化程序、CSS压缩器等。
5、Web Page Test
任何网站的性能一直是一个热门话题。为你的网站提供更慢的服务可能会导致销售额大幅下降成SEO评级降低。网页测试工具将帮助你了解Web应用程序发送到用户浏览器的用户,专业且详细地让你明白如何优化网站。
6、overAPI
不论是多么优秀的程序员都不可能记住一切。倘若在你编写程序的过程中碰到问题需要查阅手册的时候有现成的在线手册参考可以大大提升效率。而overAPI就是这样一个网站,它收集了众多对开发人员非常有用的手册。
7、Vscode Dev
我们可以通过Vscode Dev访问远程GitHub存储库,与GitHub插件相结合是进行代码审查的绝佳伴侣。
8、Can I use
Can I use是一款前端兼容性自查工具。将代码交付到生产环境时,了解你所依赖的本机浏览器功能的支持非常重要,这个可以帮助你了解其对浏览器的整体支持。
9、daily.dev
daily.dev是一个为开发者而生的信息聚合平台,提供了超过350+个开发者资讯来源,汇总了一万多个技术标签,是一个获取最新开发资讯的好渠道。除了Chrome插件之外,它还提供了Firefox、Edge等插件供用户下载。
10、CSS-TRICKS
很多前端工程师都讨厌写CSS,但CSS又是必需的,那这个时候你就缺少不了CSS-TRICKS。这个网站,它不断地在更新一些优秀的教程和技巧,也一直在改版,为前端社区作出了很大的贡献。