本系列分為四篇:
零、前言
本系列為了總結一下手上的知識,致敬我的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
元件頁面
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:使用
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>
<
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:問題所在:
問題所在:請求時是所以資料,遍歷時所有條目都會載入
解決方案:查詢範圍的介面,監聽滾動事件,快到底部時載入更多
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/材料
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.樣式
.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.搜尋框的使用:
<
Searcher searcher={{
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—-看到這裡,我在此感謝你的喜歡與支援