iconfont
iconfont也就是把icon当font来用,通过自定义font-face实现。线上有许多iconfont库可供使用。
一般iconfont解压缩后的文件夹都会放在整个项目的静态资源文件夹中,即src文件夹下的statics中,这涉及到了webpack在生产环境下打包静态资源的问题,与其他图片字体的引用一致,具体参考李熠 从React脚手架工具学习React项目的最佳实践(上):前端基础配置。
阿里图标库
- 在线链接引入
- 下载项目到本地
需要注意的是一次性最好把想要添加的图标全罗列进去,否则以后再添加还需要重新替换本地文件。
使用
由于历史因素使用i标签容纳无语义图标,但是自己在用的时候使用了span元素。
iconfont本意是将icon当作font来使用,所以也可以像字体一样设置颜色color,大小font-size。
需要注意的是下载的icon的空白部分是透明的还是有背景白色的;也就是要注意边框与实体。
icon+a
当iconfont和超链接a href一起使用的时候,一定要先给a元素width和height,否则a元素就是个没有宽高的空元素,显示不出来。
文字对齐
这里的文字除了一般文字,还包括iconfont。
以想要对齐的对象为基准。它的父元素,需要设置text-align;它本身,可能需要设置vertical-align。
盒子性质
由最基础的inline,block,flex,table衍生出了各种混合属性。这些基本盒子性质都是针对于盒子本身及其内容来说的。比如inline的性质就是内容决定盒子,padding和margin都不可以设置,通过设定内容的大小来决定盒子的大小;block的性质就是盒子就是一整行,通过设定盒子本身的大小以及盒子的margin和padding来决定盒子的大小。
而混合属性,比如inline-block,这个属性inline性质代表的是这个元素在上下文中是inline的性质,他不会占据一整行,可以和其他元素一起排列在一行;block性质代表的是这个元素对于自己内部的内容是block性质,可以设置padding,margin等属性把盒子撑开来。
从树的角度来理解,基础属性就只是从父节点出发去审视子节点,但是混合属性就设计了从该节点去向上看父节点,向下看子节点。我们在谈盒子性质,其实是在谈两个元素/标签之间的关系。
定位方式
此处视角以我们想要让它的位置从正常文档流中脱离的对象来看。应当涉及元素本身和元素的父元素。
absolute+float
第一步: 在父元素上使用position: absolute,同时确定top bottom left right等值。明确盒子性质最基础的inline/block还是较为复杂的inline-block/flex,以及盒子的属性/box-sizing。
第二步: 在元素上使用float属性。如果该元素和他的相邻的元素之间想要同在一行,那么他们之间是inline的关系;如果该元素和他的相邻元素不想在同一行,那么他们之间是block关系。
第三步: 如有必要,清除该浮动元素对后续元素的影响,使用clear。
flex布局
第一步: 在父元素上使用display: flex,决定是flex-direction是row还是columnn,是wrap还是nowrap,对于给定width和height的子元素使用align-items和vertical-align进行对齐。
第二步: 在每个子元素上使用flex: XX,这里的单位可以是百分比也可以是数字,用来说明子元素占用父元素的份额。子元素如果有内容的话(其是相对来说该内容也像子元素),还需要自己设置内容居中。
关于flex布局中flex父元素的子元素定位的问题,好像align-items和vertical-align只支持头尾中这些方式,如果想要特定高度的话,依据百分比自动调节的话还是需要传统定位方式。参考react简书项目login页面的下载app这个flex子元素。
0或负
span的图标想放到block的元素内部去,可以先将span设置为inline/inline-block,然后把margin取负值,至于是左还是右看需要。
bottom为0似乎也能实现底部效果。
去除浏览器默认样式
一般会在src下创建一个全局样式表,是为了消除浏览器客户端的user agent style。要将这个样式添加在APP组件中,然后在index.js中去渲染。
styled-component使用
教程Reference
#2: Why I prefer Styled components to build React apps
react-router-dom的Link样式
因为Link标签最终还是被解析为a标签,所以会自带下划线和点击过后的变蓝色,将Link样式化后变成StyledLink使用。
1 | import { Link } from 'react-router-dom'; |
需要记住a标签与Link标签本质上是行内元素,对他设置margin和padding都没有用,通过给a标签与Link标签添加display: block使它变成块级元素后才能实现padding和margin。
待解决的问题
代码优化与简洁。一开始创建主要是styled的基础使用与模版字符串,至于嵌套等高级性质还没有看,代码较为冗长。
数据流管理
slice书写规范
如果不使用createEntityAdapter来管理store中的数据,而选择手写数据的话,需要注意数据本身一定是数组而不是对象构成的,所以在对store中数据修改的时候才能用concat/push这一类的方法。由于数据本身可能还需要描述数据状态的元数据,所以就多了其他的field,从而构成了一个对象。
使用createSlice
API自动创建action以及action creators一定会有三个name: '', initialState, reducers:{}
这三个field,而extraReducers:{}
存在与否取决于slice本身以及slice负责的UI是否有异步数据管理的需求。
reducer
field中的数据一定要注意的是:
- 这里的state是指这个slice所管理的state,而不是store全体。
- reducer对象中的每一个函数其实都是对应该action/action creator的回调函数,一定要弄明白是在原state的基础之上增删改,还是通过直接return语句覆盖原state。在initialState定义为{posts: ‘’, id: ‘’, desc: ‘’}对象的情况下,直接的赋值语句state.posts = action.payload会在state对象下重新生成一了posts field来访数据,而不会覆盖初始initialState(当时因为这个调试了好久!)
- reducer对象中的回调函数一定要export对应的slice.actions。
- reducer对象本身一定也要export default slice到store中去生成全体store。
从数据store中随机选取数据
对应到项目中就是推荐作者一栏的换一换,采用的是洗牌算法,对store中的所有数据进行洗牌操作,然后选取(0,5)即可,也可以生成一个随机数,然后加上5。
采用Knuth-Durstenfeld Shuffle,每次从未处理的数组中随机取一个元素,然后把该元素放到数组的尾部,即数组的尾部放的就是已经处理过的元素。
算法步骤:
- 选取数组(长度n)中最后一个元素(arr[length-1]),将其与n个元素中的任意一个交换,此时最后一个元素已经确定
- 选取倒数第二个元素(arr[length-2]),将其与n-1个元素中的任意一个交换
- 重复第 1 2 步,直到剩下1个元素为止
es6实现:
1 | function shuffle(arr){ |
异步数据管理
本项目因为使用了Redux Toolkit,所以在操作异步数据的时候会使用createAsyncThunk
API,结合axios
一起使用的话流程如下:
在slice中定义异步操作:
1 | // productSlice.js |
在component中使用异步操作:
1 | import { useSelector, useDispatch } from "react-redux"; |
这里的定义与使用分别在slice和component中是因为异步数据的产生是在server端的,也就是我们要从服务器,或者在本项目中是提前准备好的public文件下的mock数据。如果异步数据的产生是通过用于与UI交互生成的,那么createAsyncThunk的生成就应该在UI中进行。
代码来源StackOverflow- redux toolkit and axios。
路由管理
两个基本模式
路由管理采用react-router-dom,基本模式有两个。
第一个模式就是在App顶层组件中使用BrowseRouter包裹Switch再包裹Router与Redirect。一般Router中会有参数传递。
1 | import { |
第二个模式就是在组件内部要进行跳转,使用Link标签包括所有能够通过点击到达另一个页面的内容,记得传递参数。
1 | // 某个UI的return部分 |
参数的传递与获取
Router的参数传递与Link的参数传递有所不同。
1 | <Route exact path="/posts/:postId" component={SinglePostPage} /> |
但是对接受这些参数传递的页面的组件来说,处理方式为:
1 | export const SinglePostPage = ( { match }) => { |
path和to指向的连接整体会作为一个对象传入UI的函数组件中作为参数,为了提取其中的参数,使用url.params方法,注意这里返回的对象都是字符串类型的。
一般得到链接里的参数后,就会将这个参数作为一个搜寻条件去匹配redux store中的所有数据。这里需要注意,如果redux中的数据id是number类型,而恰巧在useSelector中又使用了全等===符号,那么最后选择出来的结果绝对是undefined。所以一定要注意url参数和redux store的数据类型匹配。
1 | // UI中选择id匹配的数据 |
- 本文标题:简书项目复刻问题集合
- 本文作者:徐徐
- 创建时间:2020-12-02 17:37:55
- 本文链接:https://machacroissant.github.io/2020/12/02/react-project-summary/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!