前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >建站四部曲之前端显示篇(React+上线)

建站四部曲之前端显示篇(React+上线)

作者头像
张风捷特烈
发布2018-12-26 15:17:29
3.4K0
发布2018-12-26 15:17:29
举报
本系列分为四篇:

零、前言

本系列为了总结一下手上的知识,致敬我的2018 本篇的重点在于:用前两篇的数据使用React搭建一个简单网站 本篇总结的技术点: React的组件封装React实现简单的懒加载React中的网络请求搜索功能 React中form表单与接口的对接路由react-router-dom的使用React中文件上传


先回顾一下服务端的接口(以ip:192.168.43.60,端口8089为例)
查询接口:GET请求

----查询所有: http://192.168.43.60:8089/api/android/note ----查询偏移12条,查询12条(即12条为一页的第2页): http://192.168.43.60:8089/api/android/note/12/12 ----按区域查询(A为Android数据,SB为SpringBoot数据,Re为React数据) http://192.168.43.60:8089/api/android/note/area/A http://192.168.43.60:8089/api/android/note/area/A/12/12 ----按部分名称查询 http://192.168.43.60:8089/api/android/note/name/材料 http://192.168.43.60:8089/api/android/note/name/材料/2/2 ----按类型名称查询(类型定义表见第一篇) http://192.168.43.60:8089/api/android/note/name/ABCS http://192.168.43.60:8089/api/android/note/name/ABCS/2/2 ----按id名称查 http://192.168.43.60:8089/api/android/note/12

添改删接口

添-POST请求:http://192.168.43.60:8089/api/android/note 添-PUT请求:http://192.168.43.60:8089/api/android/note 删-DELETE请求:http://192.168.43.60:8089/api/android/note/1


一、首页的制作
1.网页效果(笔记本):已上线,可访问:http://www.toly1994.com

手机端用媒体查询简单适配了一下

首页效果.png

2.示意图

这里的数据写死在了IndexData.js里,当然也可以让服务端提供数据,方便动态修改 只要格式和IndexData.js里的json对象保持一致就行了

首页.png


3.路由的使用

由于主页比较简单,布局样式就不贴了,这里讲一下router的使用

3.1:安装
代码语言:javascript
复制
npm i react-router-dom
3.2:新建一个router.js管理路由

其实也不是非常复杂,一句画来说就是: http://http://192.168.43.60/Android可以访问到Android组件页面

代码语言:javascript
复制
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import React from 'react';
import Index from "./pagers/index/Index";
import Android from "./pagers/Android";
import SpringBoot from "./pagers/SpringBoot";
import ReactJS from "./pagers/ReactJS";
import Note from "./pagers/Note";

export default () => (
    <Router>
        <Switch>
            <Route path={'/index'} component={Index}/>
            <Route path={'/Android'} component={Android}/>
            <Route path={'/SpringBoot'} component={SpringBoot}/>
            <Route path={'/ReactJS'} component={ReactJS}/>
            <Route path={'/Note'} component={Note}/>
            <Route path={'/'} component={Index}/>
        </Switch>
    </Router>
)
3.3:使用
代码语言:javascript
复制
ReactDOM.render(router(), document.getElementById('root'));
3.4:跳转:
代码语言:javascript
复制
a标签的href和Link组件的to都可以,如果跳到Android页,写上`/Android`就行了

二、单条目的封装:

单条目的封装.gif


1.组件状态:

核心是itemInfo,字段名称与接口数据保持一致

代码语言:javascript
复制
this.state = {
    top: "100%",
    itemInfo: {
        type: "数据读写",
        name: "1-SI--安卓SQLite基础使用指南",
        jianshuUrl: "https://www.jianshu.com/p/58076ca06a33",
        imgUrl: "http://192.168.43.60:8089/imgs/android/f593dab6a21907dec2dfed6ffc39b7e4.png",
        createTime: "2018-08-26",
        info: "零、前言 [1]熟悉MySQL的学这个就像会西瓜的人去学吃哈密瓜一样简单。[2]如果对MySQL不太熟悉的童鞋,可以看一下我的这篇:Spring..."
    }
}

2.组件属性和行为
代码语言:javascript
复制
//组件属性
this.props.itemInfo:上层组件传递来的数据
this.props.isNew :是否加"新"字  
this.props.css: 暴露样式修改接口(主要为了修改宽高)

//组件行为:
鼠标进入是遮罩层+介绍文字进入+图片放大

3.分析布局层级关系

CSS层级关系.png

标签分级.png


4.标签的书写

使用top的变化来让悬浮时文字移入

代码语言:javascript
复制
<div className={"ItemBox"} style={{width: "300px", height: "200px"}}>
    <div className={"box-img-bg"}
         style={{backgroundImage: `url(${this.state.itemInfo.imgUrl})`}}>
    </div>
    <div className="mask-with-text"
         onMouseEnter={() => {
             let itemInfo = this.state.itemInfo;
             this.setState({top: 0, itemInfo})
         }}
         onMouseLeave={() => {
             let itemInfo = this.state.itemInfo;
             itemInfo.text = "";
             this.setState({top: "100%", itemInfo})
         }}>
        <div className="tag">
            <a href="">{this.state.itemInfo.type}</a>
        </div>
        <div className={"text"} style={{
            paddingTop: this.state.top
        }}>
            <a href={this.state.itemInfo.jianshuUrl} target={"_blank"}>
                {this.state.itemInfo.info}
            </a>
        </div>
    </div>
    <div className={"box-info"}>
        <div className={ "new"}>
        </div>
        <div className={"text-info"}>
            <a href={this.state.itemInfo.jianshuUrl} target={"_blank"}>
                {this.state.itemInfo.name}
            </a>
        </div>
    </div>
</div>

5.scss样式书写
代码语言:javascript
复制
//使用flex布局并内容居中
@mixin flexCenter() {
  display: flex;
  justify-content: center;
  align-items: center;
}

//宽高同父控件
@mixin match-parent() {
  width: 100%;
  height: 100%;
}

//文字单行加省略号
@mixin text-single() {
  font-weight: bold;
  text-align: center;
  display: inline-block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

//a标签的统一处理
@mixin handleA() {
  a {
    color: #fff;
    &:hover {
      color: #4B86FF;
      text-decoration: underline;
    }
  }
}

.ItemBox {
  margin-top: 16px;
  border-radius: 10px;
  position: relative;
  overflow: hidden;
  box-shadow: rgba(214, 214, 214, .8) 1px 1px 2px 2px;

  &:hover {
    .mask-with-text {
      transition: background-color .5s cubic-bezier(0, 0.51, 1, 1);
      background-color: rgba(0, 0, 0, .5);
    }

    .box-img-bg {
      transition: transform .5s cubic-bezier(0, 0.51, 1, 1);
      transform: scale(1.2);
    }
  }

  .box-img-bg {
    border-radius: 10px;
    position: relative;
    background-size: 100%;
    background-repeat: no-repeat;
    @include match-parent;
  }

  .mask-with-text {
    .tag {
      background-image: url("../static/imgs/tag.svg");
      font-size: 10px;
      text-align: center;
      width: 65px;
      height: 65px;
      position: absolute;
      background-size: 100% 100%;
      right: -2px;
      top: -20px;
      @include flexCenter;
      @include handleA;

    }

    border-radius: 10px 0 0 10px;
    position: absolute;
    left: 0;
    top: 0;
    @include match-parent;
    @include flexCenter;
    .text {
      transition: padding-top .6s;
      padding-left: 20px;
      padding-right: 20px;
      @include handleA;
    }
  }

  .box-info {
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 25%;
    background-color: rgba(0, 0, 0, .5);
    @include flexCenter;
    .new {
      background-image: url("../static/imgs/new.svg");
      align-self: flex-start;
      width: 30px;
      height: 30px;
      position: absolute;
      left: 0;
      background-size: 30px 30px;
    }
    .text-info {
      @include handleA;
      width: 80%;
      @include text-single()
    }
  }
}

6.静态界面组件化(属性对接):
代码语言:javascript
复制
 <div className={"ItemBox"} style={this.props.css}>
代码语言:javascript
复制
componentDidMount() {
    this.setState({
        itemInfo: this.props.itemInfo
    })
}

三、获取数据,填充界面

Page页的抽取与数据的流入.png


1.数据的获取(以Android界面为例)
1.1:添加依赖

这里使用axios发送请求

代码语言:javascript
复制
npm i axios
1.2:获取数据方法简单封装:DataFetcher.js

封装一下是为了更符合接口的操作,以便复用

代码语言:javascript
复制
const axios = require('axios');
const BASE_URL = 'http://192.168.43.60:8089';
const API = '/api/android/note/';

export default class DataFetcher {
    static findAll(callback, style = '', offset = 0, num = 10000) {
        let s = BASE_URL + API + style + "/" + offset + "/" + num;
        console.log(s);
        axios.get(s).then(rp => {
            callback(rp.data.data)
        });
    }

    static findAndroid(callback, offset = 0, num = 10000) {
        DataFetcher.findAll(callback, 'area/A', offset, num)
    }

}
1.3:使用方法:

获取数据.png

代码语言:javascript
复制
DataFetcher.get(data => {
    console.log(data);
}, 'area/A');

2.Pager页的实现

数据获取了,就已经万事具备

2.1.Pager的状态与属性:
代码语言:javascript
复制
//Pager的状态
this.state = {
    data: []
}

//Pager的状态属性
this.props.img 背景图
this.props.type 类型
this.props.sub_title 副标题
this.props.title标题
2.2.数据获取,更新状态
代码语言:javascript
复制
componentDidMount() {
    DataFetcher.get(data => {
        this.setState({data})
    }, this.props.type);
}
2.3.根据数据生成视图
代码语言:javascript
复制
renderBody() {
    return (
        this.state.data.map((i, index) => {
                return (
                    <ItemBox key={index} itemInfo={i}
                             isNew={index < 3}
                             css={{width: "30%", height: "100%"}}>
                    </ItemBox>);
            }
        )
    )
}

2.4.使用

只要改变: pager就能加载不同类型的数据

代码语言:javascript
复制
class Android extends Component {
    render() {
        return (
            <div>
                <Pager
                    pager={{
                        img: Logic.loadImg("android.svg"),
                        title: "Android 技术栈",
                        sub_title: "A complete node and summary for Android.",
                        type: "area/A"
                    }}/>
            </div>
        );
    }
}

3.懒加载的实现
3.1:问题所在:

问题所在:请求时是所以数据,遍历时所有条目都会加载 解决方案:查询范围的接口,监听滚动事件,快到底部时加载更多

图片全部加载.gif


3.2:滚动监听:
代码语言:javascript
复制
this.state = {
    dataCount: 9,//默认加载9条
    data: []
}
代码语言:javascript
复制
componentDidMount() {
    let self = this;
    window.onscroll = () => {
        let scrollHeight = document.body.scrollHeight;
        let top = document.documentElement.scrollTop || document.body.scrollTop;
        if (scrollHeight - (top + document.body.clientHeight) < 80) {
            self.state.dataCount += 6;//每次多加载6条
            DataFetcher.get((data) => {
                this.setState({data})
            }, this.props.type, 0, this.state.dataCount);
        }
    };
    DataFetcher.get(data => {
        this.setState({data})
    }, this.props.type, 0, this.state.dataCount);
}

懒加载.gif


四、搜索功能的实现:

搜索功能.gif

折腾了好一会,总算摆弄处理了,期间犯了一个低级失误,mark一下: 搜索时记得在条目的:componentWillReceiveProps(nextProps)里更新state


1.查找组件的封装

很简单,样式上面的自己怎么好看怎么来吧 回顾一下按部分名称查询接口:http://192.168.43.60:8089/api/android/note/name/材料

代码语言:javascript
复制
export default class Searcher extends Component {
    constructor() {
        super();

        this.state = {
            text: ""
        }
    }

    render() {
        return (
            <div className={"pager-search"}>
                <input className="input-search" defaultValue={this.props.searcher.text}
                       onInput={(e) => {
                           this.setState({
                               text: e.target.value
                           });
                       }}>
                </input>
                <img src={Logic.loadImg('search3.svg')} alt=""
                     onClick={() => {
                         this.props.searcher.doOnClick(this.state.text)
                     }}/>
            </div>
        )
    }
}

2.样式
代码语言:javascript
复制
.pager-search {
  position: absolute;
  right: 0;
  top: 0;
  padding: 10px;
  display: flex;
  justify-content: space-around;
  
  input {
    padding: 6px;
    box-shadow: #EAEAEA 1px 1px 30px 1px;
    width: 60%;
    color: #cccccc;
    border-bottom: transparent;
    border-width: 1px;
    background-color: rgba(195,243,231,.5);
    border-radius: 10px;
    &:focus {
      color: black;
    }
  }

  img {
    width: 50px;
    &:hover {
      transition: transform .5s;
      transform: scale(1.2);
      fill: blue;
    }
  }
}

3.请求方法的提取

这里定义了一个变量盛放type

代码语言:javascript
复制
let type = '';


componentDidMount() {
    type = this.props.pager.type;//为type赋值
    //....
}

getData() {//抽取获取数据函数
    DataFetcher.get(data => {
        this.setState({data})
    }, type, 0, this.state.dataCount);
}

4.搜索框的使用:
代码语言:javascript
复制
<Searcher
    searcher={{
            text: "张风捷特烈是谁?",
            doOnClick: (value) => {
                type = "name/" + value;
                this.getData();
            }
        }
    }/>

5.最重要的一点:ItemBox.js
代码语言:javascript
复制
componentWillReceiveProps(nextProps) {
    this.setState({
        itemInfo: nextProps.itemInfo
    });
}

其实搜索功能本身不难,有后台接口配合就行了


五、添加操作:
1.使用axios发送post请求,封装插入方法

使用post请求插入数据.png

代码语言:javascript
复制
static insert(obj) {
    let s = BASE_URL + API;
    let params = new URLSearchParams();
    params.append("type", obj.type);
    params.append("name", obj.name);
    params.append("imgUrl", obj.name);
    params.append("localPath", obj.localPath);
    params.append("jianshuUrl", obj.jianshuUrl);
    params.append("juejinUrl", obj.juejinUrl);
    params.append("createTime", obj.createTime);
    params.append("info", obj.info);
    params.append("area", obj.area);
    axios.post(s, params).then(function (response) {
        alert(response.data.data);
    }).catch(function (error) {
        console.log(error);
    });
}

2.测试插入数据的使用
代码语言:javascript
复制
DataFetcher.insert({
    type: "C",
    name: "hell0",
    localPath: "hell0",
    jianshuUrl: "hell0",
    juejinUrl: "hell0",
    createTime: "2018-12-13",
    info: "hell0",
    area: "A"
});

3.使用axios上传文件方法封装
代码语言:javascript
复制
static upload(name,file) {
    let s = BASE_URL + "/api/android/upload";
    let fd = new FormData();
    fd.append(name, file);
    let config = {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    };
    axios.post(s, fd, config).then(res => {
        console.log(res)
    }).catch(res => {
        console.log(res)
    })
}

4.上传方法的使用
代码语言:javascript
复制
<form id={"add-form"} onSubmit={this.handleSubmit.bind(this)} method={"post"} name={"add"}
    <label>上传图片:<input type="file" name={"file"}/>
    </label>
    <input type="submit" value="提交"/>
</form>
代码语言:javascript
复制
//执行上传
handleSubmit(event) {
    let input = document.forms['add'].file;
    DataFetcher.upload("file", input.files[0]);
    event.preventDefault();
}

文件上传成功.png


六、React项目的上线
1.package.json配置homepage
代码语言:javascript
复制
 "homepage": "http://toly1994.com"
2.打包
代码语言:javascript
复制
build一下,将生成的build文件加拷贝到服务器
3.运行:确保服务器上有node,并且有serve

没有serve的话:npm i serve

代码语言:javascript
复制
serve -p 80 -s

上线.png


代码语言:javascript
复制
>那个jQuery随意操纵dom的时代已经一去不复返了,React的思想非常符合Android  
我经常把React自定义组件和Android自定义控件去比较:  
React组件接收的props就像Android自定义控件中的自定义属性,并且React灵活很多    
css的布局就像Android中的布局,相比而言,css强大很多   
ES6的语法加持,更让React写起来符合Javaer的心情,所以React写起来很舒心

终于打完收工,前端我是打酱油的,不当之处,还请海涵。

下一站,安卓移动端(命属),敬请期待。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.12.13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本系列分为四篇:
  • 零、前言
    • 先回顾一下服务端的接口(以ip:192.168.43.60,端口8089为例)
      • 查询接口:GET请求
      • 添改删接口
  • 一、首页的制作
    • 1.网页效果(笔记本):已上线,可访问:http://www.toly1994.com
      • 2.示意图
        • 3.路由的使用
          • 3.1:安装
          • 3.2:新建一个router.js管理路由
          • 3.3:使用
          • 3.4:跳转:
      • 二、单条目的封装:
        • 1.组件状态:
          • 2.组件属性和行为
            • 3.分析布局层级关系
              • 4.标签的书写
                • 5.scss样式书写
                  • 6.静态界面组件化(属性对接):
                  • 三、获取数据,填充界面
                    • 1.数据的获取(以Android界面为例)
                      • 1.1:添加依赖
                      • 1.2:获取数据方法简单封装:DataFetcher.js
                      • 1.3:使用方法:
                    • 2.Pager页的实现
                      • 2.1.Pager的状态与属性:
                      • 2.2.数据获取,更新状态
                      • 2.3.根据数据生成视图
                    • 2.4.使用
                      • 3.懒加载的实现
                        • 3.1:问题所在:
                        • 3.2:滚动监听:
                    • 四、搜索功能的实现:
                      • 1.查找组件的封装
                        • 2.样式
                          • 3.请求方法的提取
                            • 4.搜索框的使用:
                              • 5.最重要的一点:ItemBox.js
                              • 五、添加操作:
                                • 1.使用axios发送post请求,封装插入方法
                                  • 2.测试插入数据的使用
                                    • 3.使用axios上传文件方法封装
                                      • 4.上传方法的使用
                                      • 六、React项目的上线
                                        • 1.package.json配置homepage
                                          • 2.打包
                                            • 3.运行:确保服务器上有node,并且有serve
                                            相关产品与服务
                                            云数据库 MySQL
                                            腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                                            领券
                                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档