airbub项目
项目规范
◼ 1.文件夹、文件名称统一小写、多个单词以连接符(-)连接;
◼ 2.JavaScript变量名称采用小驼峰标识,常量全部使用大写字母,组件采用大驼峰;
◼ 3.CSS采用普通CSS和styled-component结合来编写(全局采用普通CSS、局部采用styled-component);
◼ 4.整个项目不再使用class组件,统一使用函数式组件,并且全面拥抱Hooks; ◼ 5.所有的函数式组件,为了避免不必要的渲染,全部使用memo进行包裹;
◼ 6.组件内部的状态,使用useState、useReducer;业务数据全部放在redux中管理;
◼ 7.函数组件内部基本按照如下顺序编写代码:
组件内部state管理;
redux的hooks代码;
其他hooks相关代码(比如自定义hooks);
其他逻辑代码;
返回JSX代码;
◼ 8.redux代码规范如下:
redux目前我们学习了两种模式,在项目实战中尽量两个都用起来,都需要掌握;
每个模块有自己独立的reducer或者slice,之后合并在一起;
redux中会存在共享的状态、从服务器获取到的数据状态;
◼ 9.网络请求采用axios
对axios进行二次封装;
所有的模块请求会放到一个请求文件中单独管理;
◼ 10.项目使用AntDesign、MUI(Material UI)
项目中某些AntDesign、MUI中的组件会被拿过来使用;
但是多部分组件还是自己进行编写、封装、实现;
◼ 其他规范在项目中根据实际情况决定和编写;
创建项目:create-react-app
项目配置
配置项目的icon
配置项目的标题
配置jsconfig.json
◼ 通过craco配置别名和less文件:
因为我们想配置webpack的一些东西要么使用npm run eject让react脚手架将本身的webpack全部显露出来,要么使用craco进行配置。
craco是一个库,我们可以install进行编写webpack的配置,编写完后默认会将craco的配置和脚手架默认的webpack配置进行合并。
npm install @craco/craco@alpha -D
使用less的话执行npm install [email protected]
这里配置别名,需要的是绝对路径,所以要使用node1的path模块进行拼接路径
不要忘了去package.json文件中修改script脚本为craco启动
CSS样式的重置
◼ 对默认CSS样式进行重置:
normalize.css
reset.css
Router配置
npm install react-router-dom
import React from "react";
import {
Navigate } from "react-router-dom";
const Home = React.lazy(() => import("@/views/home/index"))
const Entire = React.lazy(() => import("@/views/entire/index"))
const Detail = React.lazy(() => import("@/views/detail/index"))
const routes = [
{
path: '', element: <Navigate to='/home' /> },
{
path: '/home', element: <Home /> },
{
path: '/detail', element: <Detail /> },
{
path: '/entire', element: <Entire /> }
];
export default routes
Redux状态管理
下载toolkit和react-redux
npm install @reduxjs/toolkit react-redux
网络请求 - axios
npm install axios
展示数据第一次为undefined怎么办?
要么用三元表达式判断,有数据再渲染,要么使用可选链
import React, {
memo,useEffect,useState } from 'react'
import yhRequest from '@/services'
const Home = memo(() => {
const [a,seta]=useState({
})
useEffect(() => {
yhRequest.get({
url: '/home/highscore' }).then(res => {
seta(res)
},[])
})
return (
<div>
<ul>
{
a?.list?.map(item => {
return <li key={
item.id}>{
item.name}</li>
})}
</ul>
</div>
)
})
export default Home
布局问题:在header进行flex布局时本想左右固定宽度,中间自适应,但是左右固定宽度不一样导致中间无法居中
思路:左右flex:1,中间固定宽度,并且中间再进行一次flex布局设置justify-content: center;使其居中
svg图片使用问题
因为直接写svg标签,代码太多,所以我们写成一个函数组件,返回这个svg,我们在使用的时候直接用这个组件
我们想要在flex布局自定义样式,可以直接在flex布局的子元素中接flex布局
less中hover效果
transition: box-shadow 250ms ease;
&:hover{
box-shadow: 0px 2px 4px rgba(0,0,0,.18);
}
项目中多处有该动画,可以用抽离进行混入
使用
不设计蒙版解决弹出层问题
点击设置isShow为true,监听window的点击,设置为false,但是会冒泡,所以将addEventListener第三个参数设置为true,变为事件捕获状态即可
const [isShow, setisShow] = useState(false)
useEffect(() => {
window.addEventListener('click', function () {
setisShow(false)
},true)
},[])
解决通过…/形式动态引入图片失败
我们在设置backgorund-image时或者通过img的src引入时写类似这种…/的路径是无法显示的,因为webpack底层在打包的时候这种路径就会找不到了
img的正确使用
在backgorund中使用
material ui 的使用
官网链接:https://mui.com/zh/material-ui/
默认是用emtion/styled的方式,要是用styled-components要配一下
在craco.config.js中写上22行的代码即可
ant design使用
官网地址:https://ant.design/docs/react/use-with-create-react-app-cn
npm install antd
在index.js中引入
import ‘antd/dist/antd.less’
在craco中配一下options
点击动态添加className
npm install classnames
添加.item样式,并且当index===currentindex时加上.active类名
使用useState出现的问题
useState只会在组件第一次渲染的时候设置初始值,后面组件再次渲染不会改变useState的值。
比如一开始设置useState是1,然后父组件发了一个网络请求将initialName改为2,但是不会生效的,useState还是1
HomeSectionv2组件
解决方法:等网络请求有值了再去渲染这个组件,数据通过props传递。
方法2:
在useEffet中的影响数组中写想要的数据,因为第一次没数据,第二次网络请求有数据之后会调用这个回调函数,回调函数中setName来改useState的数据,不过这样不好,因为组件会被渲染多次
遮罩层渐变实现
.bg-cover {
position: absolute;
left: 8px;
right: 8px;
bottom: 0;
height: 60%;
background-image: linear-gradient(-180deg, rgba(0, 0, 0, 0) 3%, rgb(0, 0, 0) 100%)
}
封装复用的滑动tab组件
详见项目
点击选中加入数组
做浅拷贝
const [items,setitems]=useState([])
function itemClick(item) {
const newitems = [...items];
newitems.push(item);
setitems(newitems)
}
如果我在一个项目中使用了多个组件库,那么打包出来的文件会不会特别大呢?
不会,因为webpack在打包时有一个tree shaking,所以打包只会打包你引入的组件,其他是不会打包的
在totalCount有值后再加载某个数据显示0问题
因为一开时totalCount是0就会展示,解决方法是totalCount前面加上两个!!转为布尔值即可
{
totalCount && <div className="info">
<Pagination count={
10}></Pagination>
<div className='desc'>
第 {
start} - {
end} 个房源, 共超过 {
totalCount} 个
</div>
</div>
}
蒙版
在网络请求数据来之前加一个蒙版,结束后取消
28行加蒙版
发送的请求函数,吧isloading存到redux里,在请求函数中进行变换
antd怎么使用组件的方法
在引入轮播图组件后想进行点击调用next和prev方法
给组件绑定ref进行调用即可
less中的&
1.当前选择器的伪类或者伪元素。如 :hover :focus :after :before等
.demo {
&:after {
content: '.';
display: block;
background-color: aquamarine;
}
}
上面的代码编译后会变成
.demo:after {
content: '.';
display: block;
background-color: aquamarine;
}
2.多一个类名
上面的代码编译后会变成
两个页面复用同一个组件,但是只想在a页面点击触发事件b页面不触发
在a页面使用组件时传入一个方法,组件进行判断,如果有该方法就执行
11行拿到传来的方法,68行点击后调用,63行判断即可
redux状态持久化
前言
A页面和B页面都需要通过同一个接口获取的数据来展示页面;
B是A的子页面,从A页面可以跳转到B页面;
思考
从减少页面的http请求数来达到性能优化的方面来考虑:可不可以A跳到B页面后,不再ajax请求数据,而是直接获取A页面Model里的数据?
尝试
按上述思路可以达到需求。但同时也发现一个问题:在重新刷新B页面时,B页面上显示的数据为空。
分析
Redux一个用于应用程序状态管理的开源JavaScript库,本质是管理组件数据状态的。刷新浏览器,相当于重新开启应用,必定会从最初的状态重新开始。如果想获取值,要么向后台发送请求,要么将值本地存储,从本地存储去取值。单说刷新浏览器,肯定是回复到最初状态,相当于什么也没做。
这里也涉及到前端路由和后端路由的问题。前端路由分为hash模式和history模式,我们从A页面跳转到B页面,本质上并没有从后端重新请求数据,而是前端通过正则匹配路由规则从而显示相应的页面(例:网易云音乐网页版)
hover效果实现
利用css的层叠属性
当picture处于hover时,让当前的cover的透明度变成1,也就是显示遮罩层,但是让item的遮罩层隐藏即可。(cover就是个遮罩层)
图片播放器
思路:搞一个img标签,利用index改变url即可,然后加个过渡动画即可
<div className='picture'>
<SwitchTransition mode='in-out'>
<CSSTransition
key={
pictureUrls[currentIndex]}
classNames="pic"
timeout={
200}
>
<img src={
pictureUrls[currentIndex]} alt="" />
</CSSTransition>
</SwitchTransition>
</div>
下拉隐藏列表思路:改变height的大小,而不是直接设置display,然后加个height的transition即可
Suspense组件和Provider组件的顺序
如果Suspense在Provider的外面,假如a页面使用了usedispatch改变了redux的数据,在b页面有用selector使用redux的数据,但是数据改变后b页面不会发生刷新。原因是因为Suspense内部是一个异步加载的方式,默认是不会对Provider的store做监听的。
解决方法就是用Provider包裹Suspense组件即可。
使用懒加载,组件渲染两次的问题
因为使用了const Detail = React.lazy(() => import("@/views/detail/index"))
懒加载,所以组件是异步的加载方式被加载的,当组件还没被加载出来时显示的是Suspense组件的fallback函数,当加载完成Suspense组件会更新,导致被引入的组件重新渲染了一遍
解决方法就是把懒加载写成import的同步加载的方式即可
less中的&
.head{
height: 100px;
width: 100px;
border: 1px solid gainsboro;
background-color: #000000;
.content{
background-color: #fff;
}
&.body{
background-color: #72cc26;
}
}
这段less编译之后为:
.head {
height: 100px;
width: 100px;
border: 1px solid gainsboro;
background-color: #000000;
}
.head .content {
background-color: #fff;
}
.head.body {
background-color: #72cc26;
}
这里我们可以看到在类前面添加了&之后,编译之后的css变为且的关系,而没有使用&的css是父子的关系。
这里需要注意.a.b和.a .b之间的区别,.a.b 是且的关系意思就是2者必须都具备,而.a .b是上下级,父子关系
<!--.a.b-->
<div class="a b"></div>
<!--.a .b-->
<div class="a">
<div class="b"></div>
</div>
每次切换页面都让页面跳到顶部
在App.jsx中
const location = useLocation();
useEffect(() => {
window.scrollTo(0,0)
},[location.pathname])
nginx部署和自动化部署过程
CICD是什么?
CI
协同开发是目前主流的开发方式,也就是多位开发人员可以同时处理同一个应用的不同模块或者功能。
但是,如果企业计划在同一天,将所有开发分支代码集成在一起,最终可能会花费很多时间和进行很多重复劳动,费事费力。因为代码冲突是难以避免的。
如果开发人员本地的环境和线上不一致的话,那么这个问题就更加复杂了。
持续集成(CI)可以帮助开发者更加方便地将代码更改合并到主分支。
一旦开发人员将改动的代码合并到主分支,系统就会通过自动构建应用,并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。
如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。
CD
CI 在完成了构建、单元测试和集成测试这些自动化流程后,持续交付可以自动把已验证的代码发布到企业自己的存储库。
持续交付旨在建立一个可随时将开发环境的功能部署到生产环境的代码库。
在持续交付过程中,每个步骤都涉及到了测试自动化和代码发布自动化。
在流程结束时,运维团队可以快速、轻松地将应用部署到生产环境中。
传统的开发模式:
项目完成 => build => test => release => operate
DevOps:
模块完成 => build => test => release => operate