开学的时候学习了一个老师简书的项目,一直没总结,前两天想起来,这里总结一下。自己学的也不怎么好,可能内容有些错误的地方,大家有看到的在评论里尽管说,哈哈。
- 下面进入正题
项目预期
- 可以登录,并设置是否登陆才能使用某个功能的权限
- 主要为首页部分的布局
- 有路由的相关知识,跳转到不同页面
- 异步数据获取
项目成果
项目实施过程
1. 创建React项目
使用 create-react-app 创建
进入React官网–Get Started–右侧Create a new React App,或者点击这里,向下就能找到安装React以及创建项目的npm方法
npm install -g create-react-app
create-react-app my-app
cd my-app
npm start 或者 yarn start
第一行就是安装 create-react-app
,这是个脚手架工具,用于创建React项目的。类似于webpack
。
第二行这里npm-app
就是要写的React项目名称了。
然后进入这个项目文件夹,运行项目便可以了。
如果可以出现页面,有React的logo之类的表示项目创建成功
React项目结构
刚创建完应该应该把react自带的一些页面删掉或者改一下,自己再用,这里展示一下最后完成的项目的目录结构
build
最上面的build 文件夹是项目最后结束后,打包之后的结果,这个包放到后端的服务器,就能访问了。
node_modules
这个里面是React运行时需要用到的各种包,组件,模块(包括第三方的)之类的,比如后面会用到的redux-thunk
等等。
public
public 文件夹里的 index.html 就是整个项目页面的入口页面,可以看到和普通的HTML页面结构一模一样,<body>
中只有一个 id='root'
的<div>
,这个<div>
便是以后项目的整个页面了。
favicon.ico 是浏览器标签栏上那个小图标。
最后的json里是一些设置文件,这个项目并没有改变这里。
src
这是整个项目最重要的部分了,几乎所有页面、逻辑、需要的文件都是在这里写的。
先说该文件夹最后面三个文件:
App.js 称为根组建,是连接所有页面的,路由的设置也在这里,可以进入不同的页面
index.js 这个是整个大的React页面的入口,其实也就是前面说的那个 id='root'
的div
,这里基本没有什么逻辑,主要是引入了 App.js ,然后返回给 id='root'
的div
style.js 其实就是CSS文件,这里面相当于给页面一个初始化的设定
common
这里面是头部导航的部分,包括logo,搜索框,右边登陆,写文章等等
里面包括了完整的store,store相当于是一个单独的空间,专门为了处理数据函数等等,同样是为了降低耦合的组件化思想。其中的对象都是imutable对象,这些后面再详细说明
pages
这里面是所有页面了,包括detail 详情页面(文章页面);home 刚进入的主页,这个页面基本连接了所有的别的页面;login 登录页面;write 写文章页面,该页面只是为了表示一下写文章需要登陆,没有其他作用。
statics
这里是一些不需要动的东西,比如引入的iconfont
,需要的图片、logo等等
store
这是整个项目总体的store ,作用其实也只是为了连接所有子store。
package.json
这里是项目的一些版本信息等,在创建按项目时便会产生。包括项目名,版本号,使用的模块的版本等等,还有一些其他的项目属性的东西
其他的也都是创建项目的时候会产生的,暂时不用去管。
项目详细介绍
这里就以项目的经过来介绍
整体初始化样式
引入
styled-components
,使用Reset.css 见官网,对项目整体样式进行初始化
styled-components
这里使用了 styled-components
将其封装成了JS文件,原因是如果使用通常的CSS,会出现一个问题,给一个组件设置了CSS,这个组件引入的组件的某个元素如果也使用相同的className
那么该元素也会有这个CSS效果,而往往我们希望每一个组件都是单独 ,不能有较多的耦合。,所以在这里使用 styled-components
这个第三方模块,来加强组件化的思想,降低耦合 ,安装方法
yarn add styled-components
文件里的样式是通过 ES6 的语法的
首先引入 styled-components
对于全局样式
export const GlobalStyle = createGlobalStyle``;
createGlobalStyle 可以自己定义的,这个由于是全局样式,所以是需要在 App.js 中引入,使用的时候,用其包裹所有内容,或者放在最上层,用单括号。
其他的样式就直接写名称,一对 反引号 包裹就可以了
具体的还是看styled-components
的官方文档,需要的时候先看看,可能会有更新。
Reset.css
由于不同的浏览器对文档的渲染是不同的,使用Reset.css 可以统一格式。
简书头部区块布局
依然使用 styled-components 进行布局
在 common 文件夹下 header 中新建,index.js 用来书写头部区块的代码,这个文件其实相当于是一个 HTML 文件,只是使用的是React写
styled-components
在该部分需要注意的:
-
每个变量都需要出去,比如
export const HeaderWrapper = styled.div` `;
-
对图片的引用问题,由于项目在打包的时候,不知道路径,所以需要在
styled-components
中先声明某个图片的位置,后面再进行使用,比如import logoPic from '../../statics/nav-logo.png'; export const Logo = styled.div` background: url(${logoPic}); `;
可以看到基本都是ES6 的写法。
-
在style.js 中定义元素的属性,看例子
export const NavSearch = styled.input.attrs({ placeholder: '搜索' })` &::placeholder { color: #999; } `;
这样就可以让index.js 中少一些关于属性的定义了,而改变这些属性的样式就用上面这个方法便可以。
iconfont
在React中的使用
下载下来的iconfont.css 文件需要改为 JS 的格式
首先在所有iconfont
的 url
上加一个相对路径 ./
一般都会把iconfont
当作一个全局样式,所以也可以使用styled-components
对其处理,然后将其引入到 App.js 中去,位置放在初始化的全局样式里面(下面)。
使用的时候就在要加入的页面,按照iconfont
中提供的方法加入即可。
input框动画部分
操作数据 focused 的bool 值来改变元素的 className
动画的部分使用了
react-transition-group
-
改变
className
思想 ,先定义一个变量focused
,默认给false
,也就相当于鼠标失焦状态,然后给搜索框绑定鼠标聚焦和失焦的函数,聚焦函数让focused
的值变为``true,而这个值
className中会有一个三元表达式来给元素变换
className`。 -
react-transition-group
安装方法
yarn add react-transition-group
相关文档在github中,但是说实话这个模块并不太好用
使用方法
index.js import { CSSTransition } from 'react-transition-group'; <CSSTransition in={focused} timeout={400} classNames="slide"> <NavSearch> </NavSearch> </CSSTransition> style.js export const NavSearch = styled.input` &.slide-enter { //动画开始 transition: all .4s ease-in-out; } &.slide-enter-active{ //动画执行结束 width: 240px; } &.slide-exit{ //动画结束 transition: all .4s ease-in-out; } &.slide-exit-active{ //结束动画 width: 160px; } `;
头部数据的管理——redux
头部目前基本只有 focused
这一个数据,就以这个为例吧
-
redux
的安装这里顺便写一下
react-redux
这个是方便在 react 中使用 redux 的模块yarn add redux yarn add react-redux
-
使用流程(之前笔记也有过)
这里是一个比较大的项目,所以会有很多个
store
,也会有一个大的store
用来连接分支store
。-
src 目录下创建一个 store 文件夹,里面有index.js 和 reducer.js
-
index.js 中引入
redux
和reducer
-
在 App.js 中引入
store
-
在 App.js 中引入
Provider
(来自于react-redux) 。用法是用<Provider store={store}></Provider>
包裹所有的页面,或者会使用到store
的内容的页面。作用是可以让其包裹的所有页面都可以使用到引入的store
中的数据,或者说Provider
可以把store
中的数据提供给包裹在内的所有页面。 -
在 header 的index.js 中引入
connect
来自于 react-redux。 用法是在最后面使用这么一句export default connect(mapStateToProps, mapDispatchToProps)(Header);
便可以将
store
中的数据和Header
组件连接在一起mapStateToProps
中写的是从store
中取数据的方法mapDispatchToProps
中写的是组件要改变store
中数据的方法 -
将数据移到
reducer
中,将处理聚焦改变focused
的值的方法也移到reducer
中 -
将函数的派发写到
mapDispatchToProps
-
将数据的调用写到
mapStateToProps
由于用了immutable
所以这里也需要注意一下写法 -
然后在使用数据的时候一定注意
this
的指向,现在应该都要变成this.props
了,不过在这个项目中是将所有的都进行了聚合,在render
下一句,做了整体处理 -
redux-devtools
的使用,这是配合redux
插件使用的,可以很清楚的看到整个项目redux的数据和逻辑-
官网地址 点击进入官网
-
安装方法
插件的在应用商店就能找到
在代码中还有一些要注意的,在网站下面,有个 Advanced store setup 中有安装方法,复制
在代码最外层的store 中的 index.js 中粘贴,使用方法注意以前的代码或者官网的介绍,以为涉及到多个组件。
-
-
store
的拆分已经知道,在最外层有一个
store
,在每一个页面也有一个小的store
所以我们一般会把每个页面的逻辑放在自己的store
中,而把连接所有分支store
的总体代码写到外层那个大的store
中。链接的方法就是在外面大的
store
中引入小的,逻辑写在 reducer.js 中这里需要引入一个combineReducers
,来自于redux-immutable
;然后其他的都需要reducer as headerReducer
的形式来引入,这句是 ES6 中该名字的语法,原因是多个reducer
的名字一样,会出现重名的错误。最后再导出去便可以了,由于这里使用了
redux-immutable
,所以直接看下面代码import { combineReducers } from "redux-immutable"; import { reducer as headerReducer } from '../common/header/store'; const reducer = combineReducers({ header: headerReducer, // 前面header 是自己起的名字 }); export default reducer;
其他的页面只要添加就可以了。
这个代码还有一点就是第二行引入
headerReducer
的时候我们看到并没有指向 header 中的store 中的 reducer ,这是因为 header 中的 store 中的 index.js 将其导出出去,这样,这个 index.js 就相当于这个 header 的 reducer 出口文件,而大的 store 便可以直接从那里去链接了。 -
使用 actinCreater
使用 actinCreater 有一个好处就是可以将所有使用的
action
都放在其中,这样业务逻辑会更加清晰。actinCreater 是一个函数,可以返回一个对象。
使用actinCreater 创建了action,就在 header 的index.js 中引入,这里要注意写法。
使用 actinCreater 会出现一些字符串,会使得出错不易发现,所以再新建一个 constants.js ,将字符串都改成常量。
最后在使用这个常量的地方都引入就可以用了。
-
-
到这里,header 中基本使用 redux 的流程就差不多了,再来总结一下
header 中,style.js是组件的所有样式;idnex.js 中是header 部分整体的组件,有UI组件(样式部分),有 connect 组件(逻辑组件)用来存取数据等等操作;而所有的数据都存放在 store 下面的 reducer.js 中,包括后面数据库中的,需要异步请求的都在这个里面引入或者存放;
-
immutable对象
-
安装
immutable
yarn add immutable
-
使用方法
- 首先将
reducer
中的数据使用fromJS
变成一个immutable
对象 - 在
reducer
中使,用的时候就需要使用immutable
的方法,比如使用state.set('focused', true)
将focused
的值改变为true
。 - 在 index.js 中也就不能使用一般的方法去获取或者发送数据了,也需要使用
immutable
的方法,比如获取数据就需要使用state.get()
或者state.getIn()
来获取数据,一般推荐使用后者,写起来比较少一些。
- 首先将
-
其他的用法可以去 Github 查找
-
热门搜索异步请求
-
需求:通过AJAX获取数据,而且只在鼠标第一次聚焦搜索框的时候获取,往后只是使用这个数据就可以了。
-
实现步骤
-
reducer 中添加这个数据,为数组
-
使用
redux-thunk
实现异步操作-
redux-thunk
的安装yarn add redux-thunk
-
引入方法
在最外面的
store
中引入redux-thunk
。然后使用redux
中的applyMiddleware
方法,在composeEnhancers()
中引入thunk()
中间件。 -
引入 axios 来实现异步操作
yarn add axios
在需要的页面引入
-
整体使用方法即使,在 actionCreators.js 中实现异步请求的操作,大概就是导出一个函数,函数中返回一个带有
dispatch
参数的函数,函数就利用asiox.get( )
来获取后端的数据 -
在 reducer.js 中引入数据,但是由于是
immutable
的对象,所以这使用的方法有所不同 -
在index.js 中,派发函数(
mapDispatchToProps
),获取数据(mapStateToProps)。在要展示数据的地方调用一个方法,方法就是从获取数据的地方取数据,做循环,加到该显示的地方,这里还有一个细节,比如key
值,immutable
类型需要转换成普通数组。还有对数据分页显示,才能做到“换一换”的目的。
-
-
设置只在第一次鼠标聚焦搜索框的时候获取热门搜索的列表信息
- 在鼠标聚焦的时候,调用了
headleInputFocus()
方法,这个方法里派发了getList
的action
. - 可以通过列表信息的
list
数据来判断是否是第一次聚焦,在调用headleInputFocus()
的时候将list
传递进去,然后在headleInputFocus()
方法实现的地方传入,打印一下list
,发现每次聚焦会有不同的size
,size
为0的时候是第一次聚焦的时候,所以这里进行一个if
判断,便可以了
- 在鼠标聚焦的时候,调用了
-
路由的使用
-
路由安装
yarn add react-router-dom
-
使用方法
-
在 App.js 中引入
react-router-dom
的其中两个组件BrowserRouter
和Route
-
在需要使用路由的地方用
BrowserRouter
包裹Route
import { BrowserRouter, Route } from 'react-router-dom'; import Home from './pages/home'; import Detail from './pages/detail/loadable'; <BrowserRouter> <div> <Header/> <Route path='/' exact component={Home}> </Route> <Route path='/detail/:id' exact component={Detail}> </Route> </div> </BrowserRouter>
-