Introduction
技术栈:react + redux + react-router + express + Nginx
练习点:
- redux 连接
- react-router 路由跳转
- scss 样式书写
- 容器组件与展示组件的设计
- express 脚手架项目结构设计
- 用户信息持久化(cookie + redis)
- 常见安全问题处理(xss sql 注入 cookie 跨域)
- Promise 封装数据库操作
- PM2 进程守护
线上体验地址:点我跳转
github:https://github.com/ChenMingK/blog-proj 欢迎 star?
Show

Design
前端
项目结构
|-- src
|-- api // 所有API请求(axios)
|-- assets // 字体图标、全局/混合样式
|-- components // 展示组件 / 作为某个页面的局部的组件
|-- common // 可复用的组件
|-- home // home 页面所用到的组件,即 home 页面由这些组件构成
|-- edit // edit 页面所用到的组件
|-- pages // 容器组件 / 该组件整体作为一个页面展示,与 redux 连接并将 store 中的数据传递给其子组件
|-- login // 登录页
|-- home // 首页
|-- Home.jsx // react 组件
|-- Home.scss // 该组件的样式文件
......
|-- store // redux
|-- home // home 页对应的 store
|-- action-type.js // action 类型
|-- actions.js // action 构造器
|-- index.js // 用于整体导出
|-- reducer.js // 该 module 的 reducer
|-- module2 // 这个文件夹只是为了说明如果有 redux 有新的 module 需要引入就和 home 文件夹下格式一样
|-- store.js // 合并 reducer,创建 store(全局唯一)并导出,如果需要应用中间件,在这里添加
|-- App.js // 根组件 / 定制路由
|-- index.js // 项目入口 / webpack 打包入口文件
react-router-dom 路由跳转的几种方式
1.引入<Link> 组件并使用,但是其有默认的样式(比如下划线),还要修改其默认样式
import <Link> from 'react-router-dom'
...
<Link to="/login" className="login-btn">
<span className="login-text">登录</span>
</Link>
2.导入 withRouter 使用 js 方式跳转
import { withRouter } from 'react-router-dom'
// 需要对该组件做如下处理(这是与 redux 连接的同时又使用 withRouter 的情况)
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Header))
// js 方法实现路由跳转
this.props.history.push('/')
// 简单来说就是通过某种方式(context?)把 history 给传递到这个组件了
回到顶部的过渡效果
1.利用 window.scrollTo(xpos, ypos)
方法加上 transition: all linear .2s
来实现 - 该方案无效
2.利用元素的 scrollTop 属性,点击回到顶部按钮时设置元素的 scrollTop: 0 再添加 transition 属性来实现 - 无法对 scrollTop 属性实现过渡
3.该元素的 css 中添加 scroll-behavior: smooth;
点击回到顶部按钮时设置元素的 scrollTop: 0 - OK
利用 <hr> 画分割线
hr {
border-color: #eaeaea;
border: 0; // 默认横线
border-top: 1px solid #eee; // 画条灰色横线
margin-left: 65px; // 这是块级元素,可以用 margin 来控制横线长度
margin-right: 15px;
}
利用 transform:scaleX() 实现下划线伸缩效果

.menu-item {
@include center;
width: 100px;
color: #969696;
font-weight: 700;
font-size: 16px;
position: relative;
.icon {
margin-right: 5px;
}
// 利用伪元素给这个 item 加 "下划线"
&:after {
content: '';
position: absolute;
width: 100%;
border-bottom: 2px solid #646464;
top: 100%;
transform: scaleX(0);
transition: all linear .2s;
}
// hover 时改变 scaleX
&:hover {
color: #646464;
cursor: pointer;
&:after { // 咋选的?
transform: scaleX(1);
}
}
}
利用 DOM 操作手动添加的事件处理程序要手动移除
// 假设在加载组件时这样添加事件处理程序
componentDidMount() {
let app = document.getElementsByClassName('App')[0]
app.addEventListener('scroll', this.handleScroll, false)
}
// 就需要这么移除,否则会报内存泄漏,另外注意这里的 this.handleScroll 必须是与上面的 addEventListener 相同的引用
componentWillUnmount() {
let app = document.getElementsByClassName('App')[0]
app.removeEventListener('scroll', this.handleScroll, false)
}
防止无意义的渲染
shouldComponentUpdate(nextProps, nextState) {
// ArticleList 组件是从父组件拿到的 articlelist,发现在内容没变的情况下页面向下滚动就会触发 render 函数
// 投机取巧......
return nextProps.articleList.length !== this.props.articleList.length
}
自己实现一个带边框的 tooltip
小三角就用我们熟悉的 css 画三角来画,如果我们想给这个 tooltip 外层加一个边框?可以再利用一次伪元素来画一个三角形,其颜色
就是边框颜色,利用高度差来实现这个边框效果。
&:after {
content: ''; // 记得加 content 才行
width: 0;
height: 0;
border-width: 10px;
border-style: solid;
border-color: #fff transparent transparent transparent;
position: absolute;
top: 100%;
left: 50%;
z-index: 101;
margin-left: -10px;
}
// 如果我想给小三角再加个边框?
&:before {
content: '';
width: 0;
height: 0;
border-width: 11px;
border-style: solid;
border-color: #f0f0f0 transparent transparent transparent;
position: absolute;
top: calc(100% + 1px); // calc 大法好
left: 50%;
z-index: 100;
margin-left: -11px;
}
后端
项目结构
|--bin
|-- www // 入口文件 / 启动文件
|-- conf // 配置项
|-- db.js // 数据库连接配置 / redis 连接配置
|-- controller
|-- blog.js // 处理 blog 路由相关逻辑(将逻辑操作封装为函数并导出由供路由处理部分使用)
|-- user.js // 处理 user 路由相关逻辑
|-- db
|-- mysql.js // 建立 mysql 连接,将执行 sql 操作封装为 Promise 并导出
|-- redis.js // 建立 redis 连接,封装 set、get 操作并导出
|-- middleware
|-- loginCheck.js // 自定义的中间件
|-- model
|-- resModel.js // 封装响应的格式
|-- routes