建站四部曲之前端显示篇(React+上线)
本系列分為四篇:
- 建站四部曲之后端接口篇(SpringBoot+上線)
- 建站四部曲之Python數據爬蟲篇(selenium)
- 建站四部曲之前端顯示篇(React+上線)
- 建站四部曲之移動端篇(Android+上線)
零、前言
本系列為了總結一下手上的知識,致敬我的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.網頁效果(筆記本):已上線,可訪問:www.toly1994.com
手機端用媒體查詢簡單適配了一下
2.示意圖
這里的數據寫死在了IndexData.js里,當然也可以讓服務端提供數據,方便動態修改
只要格式和IndexData.js里的json對象保持一致就行了
3.路由的使用
由于主頁比較簡單,布局樣式就不貼了,這里講一下router的使用
3.1:安裝
npm i react-router-dom 復制代碼3.2:新建一個router.js管理路由
其實也不是非常復雜,一句畫來說就是:
http://http://192.168.43.60/Android可以訪問到Android組件頁面
3.3:使用
ReactDOM.render(router(), document.getElementById('root')); 復制代碼3.4:跳轉:
a標簽的href和Link組件的to都可以,如果跳到Android頁,寫上`/Android`就行了 復制代碼二、單條目的封裝:
1.組件狀態:
核心是itemInfo,字段名稱與接口數據保持一致
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.組件屬性和行為
//組件屬性 this.props.itemInfo:上層組件傳遞來的數據 this.props.isNew :是否加"新"字 this.props.css: 暴露樣式修改接口(主要為了修改寬高)//組件行為: 鼠標進入是遮罩層+介紹文字進入+圖片放大 復制代碼3.分析布局層級關系
4.標簽的書寫
使用top的變化來讓懸浮時文字移入
<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樣式書寫
//使用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.靜態界面組件化(屬性對接):
<div className={"ItemBox"} style={this.props.css}> 復制代碼componentDidMount() {this.setState({itemInfo: this.props.itemInfo}) } 復制代碼三、獲取數據,填充界面
1.數據的獲取(以Android界面為例)
1.1:添加依賴
這里使用axios發送請求
npm i axios 復制代碼1.2:獲取數據方法簡單封裝:DataFetcher.js
封裝一下是為了更符合接口的操作,以便復用
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:使用方法:
DataFetcher.get(data => {console.log(data); }, 'area/A'); 復制代碼2.Pager頁的實現
數據獲取了,就已經萬事具備
2.1.Pager的狀態與屬性:
//Pager的狀態 this.state = {data: [] }//Pager的狀態屬性 this.props.img 背景圖 this.props.type 類型 this.props.sub_title 副標題 this.props.title標題 復制代碼2.2.數據獲取,更新狀態
componentDidMount() {DataFetcher.get(data => {this.setState({data})}, this.props.type); } 復制代碼2.3.根據數據生成視圖
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就能加載不同類型的數據
class Android extends Component {render() {return (<div><Pagerpager={{img: Logic.loadImg("android.svg"),title: "Android 技術棧",sub_title: "A complete node and summary for Android.",type: "area/A"}}/></div>);} } 復制代碼3.懶加載的實現
3.1:問題所在:
問題所在:請求時是所以數據,遍歷時所有條目都會加載
解決方案:查詢范圍的接口,監聽滾動事件,快到底部時加載更多
3.2:滾動監聽:
this.state = {dataCount: 9,//默認加載9條data: [] } 復制代碼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); } 復制代碼四、搜索功能的實現:
折騰了好一會,總算擺弄處理了,期間犯了一個低級失誤,mark一下: 搜索時記得在條目的:componentWillReceiveProps(nextProps)里更新state
1.查找組件的封裝
很簡單,樣式上面的自己怎么好看怎么來吧
回顧一下按部分名稱查詢接口:http://192.168.43.60:8089/api/android/note/name/材料
2.樣式
.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
let type = '';componentDidMount() {type = this.props.pager.type;//為type賦值//.... }getData() {//抽取獲取數據函數DataFetcher.get(data => {this.setState({data})}, type, 0, this.state.dataCount); } 復制代碼4.搜索框的使用:
<Searchersearcher={{text: "張風捷特烈是誰?",doOnClick: (value) => {type = "name/" + value;this.getData();}}}/> 復制代碼5.最重要的一點:ItemBox.js
componentWillReceiveProps(nextProps) {this.setState({itemInfo: nextProps.itemInfo}); } 復制代碼其實搜索功能本身不難,有后臺接口配合就行了
五、添加操作:
1.使用axios發送post請求,封裝插入方法
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.測試插入數據的使用
DataFetcher.insert({type: "C",name: "hell0",localPath: "hell0",jianshuUrl: "hell0",juejinUrl: "hell0",createTime: "2018-12-13",info: "hell0",area: "A" }); 復制代碼3.使用axios上傳文件方法封裝
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.上傳方法的使用
<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> 復制代碼//執行上傳 handleSubmit(event) {let input = document.forms['add'].file;DataFetcher.upload("file", input.files[0]);event.preventDefault(); } 復制代碼六、React項目的上線
1.package.json配置homepage
"homepage": "http://toly1994.com" 復制代碼2.打包
build一下,將生成的build文件加拷貝到服務器 復制代碼3.運行:確保服務器上有node,并且有serve
沒有serve的話:npm i serve
serve -p 80 -s 復制代碼>那個jQuery隨意操縱dom的時代已經一去不復返了,React的思想非常符合Android 我經常把React自定義組件和Android自定義控件去比較: React組件接收的props就像Android自定義控件中的自定義屬性,并且React靈活很多 css的布局就像Android中的布局,相比而言,css強大很多 ES6的語法加持,更讓React寫起來符合Javaer的心情,所以React寫起來很舒心 復制代碼
終于打完收工,前端我是打醬油的,不當之處,還請海涵。
下一站,安卓移動端(命屬),敬請期待。
后記:捷文規范
1.本文成長記錄及勘誤表
| V0.1 | 2018-12-13 | [建站四部曲之前端顯示篇(React+上線)](www.jianshu.com/p/b0b4776cc… |
2.更多關于我
| 張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
| 我的github | 我的簡書 | 我的掘金 | 個人網站 |
3.聲明
1----本文由張風捷特烈原創,轉載請注明
2----歡迎廣大編程愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這里,我在此感謝你的喜歡與支持
總結
以上是生活随笔為你收集整理的建站四部曲之前端显示篇(React+上线)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Xshell利用Xftp传输文件,使用p
- 下一篇: elasticSearch6源码分析(6