title: react 第一次實戰(zhàn)項目知識點記錄
基礎知識點項目地址:https://github.com/yangxinjian/reactPractice.git
完整項目地址(主要是這個)
https://github.com/yangxinjian/reactAntBike.git


準備階段-基層環(huán)境
安裝node.js(官網(wǎng)下載即可)
node -v (查看是否安裝node完成)
安裝yarn新一代的包管理工具facebook開發(fā),你也可以選擇cnpm
- yarn的速度會比npm快
- 安裝版本統(tǒng)一,更安全
- 更簡潔的輸出
- 更好的語義化
sudo cnpm install yarn -g
yarn -v(查看是否安裝yarn完成)
使用git在馬云上進行托管,并在本地clone下來項目
git clone 你項目的地址
cd 你的項目
配置gitignore(git命令忽視)
vim .gitignore
i(編輯命令)
.DS_Store (Mac自帶)
node_modules (node包)
dist (打包的壓縮文件)
*.log(錯誤信息等)
初始化項目
yarn init / cnpm init
提交項目
git add . (保存到暫存區(qū))
git commit -m '備注信息' (把暫存區(qū)內(nèi)容保存到分支)
git pull (拉取其他分支代碼)
git push (將更新內(nèi)容提交進入遠程分支)
安裝webpack打包工具(配置規(guī)則查看webpack章節(jié))
yarn add webpack --dev / cnpm install webpack --dev
在根目錄下創(chuàng)建一個webpack.config.js文件
1. 需要處理的文件類型
html => html-webpack=plugin
js es6 => babel + babel-preset-react
css => css-loader + sass-loader
img => url-loader + file-loader
2. 常用模塊
html-webpack-plugin => html單獨打包成文件
extract-text-webpack-plugin => 樣式打包成單獨文件
CommonsChunkPlugin => 提出通用模塊
3. webpack-dev-server
(1) 為webpack項目提供web服務
(2) 更改代碼自動刷新,路徑轉(zhuǎn)發(fā)
(3) yarn add webpack-dev-server --dev
(4) 解決多版本共存
1.配置webpack,在創(chuàng)建好的webpack.config.js中配置
-
添加模塊輸出口
const path = require('path') module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, 'dist'), // __dirname代表根目錄 filename: '你想輸出的文件名字.js' } } -
添加html插件
yarn add html-webpack-plugin --dev // 生成html5文件插件 在webpack.config.js中設置 const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件 module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, 'dist'), filename: '你想輸出的文件名字.js' }, plugins: [ // 使用插件 new HtmlWebpackPlugin({ template: '.src/index.html' // 引用模板自定義html文件,使打包出來的html與此文件一致 }) ] } -
添加babel插件 (將es6語言轉(zhuǎn)換成es5)
yarn add babel-core@6.26.0 babel-preset-env@1.6.1 babel-loader@7.1.2 --dev // 安裝 在webpack.config.js配置 const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'app.js' }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/,// 將不需要裝換的文件夾排除 use: { loader: 'babel-loader', options: { presets: ['env'] } } } ] }, plugins: [ new HtmlWebpackPlugin({ template: '.src/index.html'// 引用自定義html文件 }) // 生成html5文件 ] } -
安裝react的插件
yarn add babel-preset-react --dev / cnpm install babel-preset-react --devyarn add html-webpack-plugin --dev // 生成html5文件插件 在webpack.config.js中設置 const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件 module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, 'dist'), filename: '你想輸出的文件名字.js' }, plugins: [ // 使用插件 new HtmlWebpackPlugin({ template: '.src/index.html' // 引用模板自定義html文件,使打包出來的html與此文件一致 }) ] } -
添加babel插件 (將es6語言轉(zhuǎn)換成es5)
yarn add babel-core@6.26.0 babel-preset-env@1.6.1 babel-loader@7.1.2 --dev // 安裝 在webpack.config.js配置 const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'app.js' }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/,// 將不需要裝換的文件夾排除 use: { loader: 'babel-loader', options: { presets: ['env', 'react'] // 只需要在這里引用react } } } ] }, plugins: [ new HtmlWebpackPlugin({ template: '.src/index.html'// 引用自定義html文件 }) // 生成html5文件 ] } -
安裝樣式的插件
> 安裝css yarn add style-loader@0.19.1 css-loader@0.28.8 --dev 在項目src中新建一個index.css頁面,并在app.jsx中引入頁面 import React from 'react' import ReactDOM from 'react-dom' import './index.css' 安裝webpack打包css成獨立文件的插件 yarn add extract-text-webpack-plugin@3.0.2 --dev 在webpack.config.js中更改對css的配置配置 引入 const ExtractTextPlugin = require('extract-text-webpack-plugin') { test: /\.css$/, use: ExtractTextPlugin.extract({ // 這里改變 fallback: 'style-loader', use: 'css-loader' }) } 由于這是一個插件,需要在plugin中配置 > 安裝sass yarn add sass-loader --dev yarn add node-sass --dev 在webpack.config.js中rules css配置下添加 { test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: ['css-loader', 'sass-loader'] }) } -
對圖片的處理
yarn add url-loader --sev 在webpack.config.js中配置 { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options:{ limit: 8192 // 文件大于8k被當做文件 } } ] } -
對字體圖標的處理
yarn add font-awesome { test: /\.(eot|svg|ttf|woff|woff2|otf)$/, use: [ { loader: 'url-loader', options:{ limit: 8192 // 文件大于8k被當做文件 } } ] } 對公共模塊的處理
在plugin中處理
const path = require('path')
const webpack = require('webpack') //為了引用webpack自帶方法
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
entry: './src/app.jsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/app.js'
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /(node_modules)/, // 將不需要裝換的文件夾排除
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react'] // 自動根據(jù)環(huán)境打包
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader']
})
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader']
})
},
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options:{
limit: 8192, // 文件大于8k被當做文件
name: 'resource/[name].[ext]'
}
}
]
},
{
test: /\.(eot|svg|ttf|woff|woff2|otf)$/,
use: [
{
loader: 'url-loader',
options:{
limit: 8192, // 文件大于8k被當做文件
name: 'resource/[name].[ext]'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'// 引用自定義html文件
}), // 生成html5文件
new ExtractTextPlugin('css/[name].css'), // 將樣式單獨打包出來生成新的css頁面
// 提出公共模塊,webpack自帶
new webpack.optimize.CommonsChunkPlugin({
name: 'common',// 手動指定的通用木塊
filename: 'js/base.js'
})
]
}
-
webpack 自動刷新處理webpack-dev-server
yarn add webpack-dev-server 在config下與plugin同級加上 devServer: { } 為了防止打包后的圖片在根目錄下找不到 output: { path: path.resolve(__dirname, 'dist'), publicPath: '/dist/', //在這里添加路徑 filename: 'js/app.js' }, 編譯打包就用webpack-dev-server就可以了
使用react環(huán)境搭建項目
yarn add react react-dom
在app.js中引用react結構,并將app.js的后綴更改為jsx,webpack.config.js中的js配置也要變成jsx,入口文件的js也要更改為jsx
import React from 'react'
import ReactDOM from 'react-dom'
package.json的配置
"scripts": {
"dev": "node_modules/.bin/webpack-dev-server",
"dist": "node_modules/.bin/webpack -p"
},
這樣就可以使用yarn dev啟動項目
yarn dist 打包項目
react正式開始咯(上訴方法均為自己手打搭建一個react項目,了解每一步,接下來是react提供的項目創(chuàng)建方法)
react => 視圖層框架 組件化 JSX表達式 虛擬DOM
- Facebook 開源的一個JavaScript庫
- React結合生態(tài)庫構成一個MVC框架(vue也是mvc)
- 特點:Declarative(聲明式編碼:只需要聲明在哪里做什么,無需關心如何實現(xiàn));Component-Based(組件化編碼);高效-高效的DOM Diff算法,最小化頁面重繪
- 單向數(shù)據(jù)流
生態(tài)介紹
vue生態(tài):vue + vue-router + vuex + axios + babel + webpack
react生態(tài): react + react-router + redux + axios + babel + webpack
項目的創(chuàng)建(另一種方式,但是本人喜歡上面的手動配置,看得懂)
yarn add / cnpm install global/-g create-react-app (全局安裝)
在工作區(qū)創(chuàng)建項目
create-react-app 你的項目名稱
cd 你的項目名稱
cnpm / yarn start 啟動
JSX語法
-
添加html
let jsx = <div>this is a jsx programmer</div> ReactDOM.render( jsx, // 將jsx組件放到渲染的dom下 document.getElementById('app) ) -
添加樣式
行內(nèi)樣式 let style = { color: 'red' } let jsx = <div style={style}></div> 引入class樣式 在index.scss中設置 body { .jsx { font-size: 16px; } } let jsx = <div className="jsx"></div> // react中使用className引用樣式名稱 -
添加邏輯
(1)使用變量let name = "pig" let jsx = <div>I am a {pig}</div> ReactDom.render( jsx, document.getElementById('app') )(2)條件判斷
let truth = true let jsx = (//加括號的目的是為了換行的時候,編輯器不會給我們自動填補分號 <div> { // 條件判斷需要加上{} truth ? <p>I am a pig</p> : <p>I am not a pig</p> } </div> )(3)jsx中插入注釋
{/*我是一個注釋*/}(4)數(shù)組的使用
let names = ['pig', 'dog', 'chicken'] let jsx = ( { names.map((name,index) => <p key={index}>I am {name}</p>) } ) -
添加組件
(1)基礎function Component () { return <h1>I am a pig</h1> } ReactDom.render( <Component />, // 如果是變量直接飲用,如果是組件需要加上標簽 document.getElementById('app') )(2)es6組件寫法
class ES6Component extends React.Component{ render () { return <h1>I am a pig in es6</h1> } } ReactDom.render( <div> // 兩個組件共存需要包裹在一個div中 <Component /> <ES6Component> </div>, document.getElementById('app') )(3)組件初始化變量
class Component extends React.Component { constructor (props) { super(props); this.state = { name: 'pig' } } render () { return <h1>I am a {this.state.pig}</h1> } }(4)更改組件初始化變量
class Component extends React.Component { constructor (props) { // props在子組件中只能被使用不能被改變 super(props); this.state = { name: 'pig' } } render () { setTimeOut(() => { this.setState({ name: 'Pi g Pig' }) }, 2000) return <h1>I am a {this.state.pig}</h1> } }(5)父組件傳值
class Component extends React.Component { constructor (props) { super(props) } render () { return <h1>I am a {this.props.name}</h1> } } ReactDom.render( <Component name="pig" />, docuement.getElementById('app') )(6)組件添加點擊事件(方法一)
class Component extends React.Component { constructor (props) { super(props); this.state = { age: 18 } this.addAge = this.addAge.bind(this) } addAge () { this.setState({ age : this.state.age + 1 }) } render () { return ( <div> <h1>I am {this.state.age} years old </h1> <button onclick={this.addAge}><button /> </div> ) } } ReactDom.render( <Component />, docuement.getElementById('app') )
(7)組件添加點擊事件(方法二)
class Component extends React.Component {
constructor (props) {
super(props);
this.state = {
age: 18
}
}
addAge () {
this.setState({
age : this.state.age + 1
})
}
render () {
return (
<div>
<h1>I am {this.state.age} years old </h1>
<button onclick={(e) => {this.addAge(e)}}><button />
</div>
)
}
}
ReactDom.render(
<Component />,
docuement.getElementById('app')
)
(8)組件添加輸入框更改事件
class Component extends React.Component {
constructor (props) {
super(props);
this.state = {
age: 18
}
}
changeValue (e) {
this.setState({
age : e.target.value
})
}
render () {
return (
<div>
<h1>I am {this.state.age} years old </h1>
<input type="text" onChange={(e) => {this.changeValue(e)}} />
</div>
)
}
}
ReactDom.render(
<Component />,
docuement.getElementById('app')
)
(9)容器性組件嵌套組件
class Component extends React.Component {
constructor (props) {
super(props);
this.state = {
age: 18
}
}
changeValue (e) {
this.setState({
age : e.target.value
})
}
render () {
return (
<div>
<h1>I am {this.state.age} years old </h1>
<input type="text" onChange={(e) => {this.changeValue(e)}} />
</div>
)
}
}
class Title extends React.Component{
constuctor (props) {
super(props)
}
render (props) {
return <h1>{this.props.title}</h1>
}
}
class App extends React.Component{
render () {
return (
<div>
<Title title="app" />
<Component /> // 在這里引用小組件component
</div>
)
}
}
ReactDom.render(
<App />, // 這里換成App
docuement.getElementById('app')
)
(10)組件嵌套組件
class Component extends React.Component {
constructor (props) {
super(props);
this.state = {
age: 18
}
}
changeValue (e) {
this.setState({
age : e.target.value
})
}
render () {
return (
<div>
<h1>I am {this.state.age} years old </h1>
<input type="text" onChange={(e) => {this.changeValue(e)}} />
</div>
)
}
}
class App extends React.Component{
render () {
return (
<div>
<h1>app</h1>
<Component /> // 在這里引用小組件component
</div>
)
}
}
ReactDom.render(
<App />, // 這里換成App
docuement.getElementById('app')
)
(11)容器性組件嵌套組件,傳值可以為任何html形式
class Component extends React.Component {
constructor (props) {
super(props);
this.state = {
age: 18
}
}
changeValue (e) {
this.setState({
age : e.target.value
})
}
render () {
return (
<div>
<h1>I am {this.state.age} years old </h1>
<input type="text" onChange={(e) => {this.changeValue(e)}} />
</div>
)
}
}
class Title extends React.Component{
constuctor (props) {
super(props)
}
render (props) {
return <h1>{this.props.children}</h1> // 這里變成獲取子children
}
}
class App extends React.Component{
render () {
return (
<div>
<Title>
<span>我是spanspan</span>
<a href="">link</a>
</Title> //更改為html形式
<Component />
</div>
)
}
}
ReactDom.render(
<App />, // 這里換成App
docuement.getElementById('app')
)
(12)子組件給父組件傳值
class Father extends React.Component {
constructor (props) {
super(props);
this.state = {
bgColor: 'red'
}
}
changeMyBgColors (color) {
this.setState({
bgColor: color
})
}
render () {
return (
<div style={{background: this.state.bgColor}}>
<h1>我是爸爸</h1>
<Child bgColor={this.state.bgColor} changeColor={(color) => {this.changeMyBgColors(color)}}/>
</div>
)
}
}
class Child extends React.Component {
constructor (props) {
super(props)
}
changeMyBgColor () {
this.props.changeColor('blue')
}
render () {
return (
<div>
<h2>我是baby</h2>
<button onClick={(e) => {this.changeMyBgColor(e)}}>我想改變我爸爸的背景顏色</button>
</div>
)
}
}
ReactDOM.render(
<Father />,
document.getElementById('app')
)
(13)兄弟之間的組件通信(子1傳父,父在傳子2)
class Child1 extends React.Component{
constructor (props) {
super(props)
this.state = {
color1: 'red'
}
}
changeMyBrotherColor (props) {
this.props.change2Color(this.state.color1)
}
render () {
return (
<div>
<h2>我是孩子1</h2>
<button onClick={(e) => {this.changeMyBrotherColor(e)}}>我要改變我弟弟的顏色咯</button>
</div>
)
}
}
class Child2 extends React.Component{
constructor (props) {
super(props)
}
render () {
return (
<div>
<h2 style={{color: this.props.color22}}>我是孩子2</h2>
</div>
)
}
}
class Father extends React.Component{
constructor (props) {
super(props)
this.state = {
color2: 'yellow'
}
}
changColor (colorsss) {
this.setState({
color2: colorsss
})
}
render () {
return (
<div>
<h1>這是我的孩子們</h1>
<Child1 change2Color={(color) => {this.changColor(color)}}/>
<Child2 color22={this.state.color2}/>
</div>
)
}
}
ReactDOM.render(
<Father />,
document.getElementById('app')
)
react生命周期
getDefaultProps // 初始化props屬性,props來自其他組件
getInitialState // 初始化組件的狀態(tài)
componentWillMount // 組件加載之前
render // 渲染
componentDidMount // 組件dom插入之后
componentWillReceiveProps // 接受父組件的傳遞
shouldComponentUpdate // 組件的更新處罰
componentWillUpdate // 組件要更新前
componentDidUpdate // 組件更新后
componentWillUnmount // 組件的銷毀
Mounting : 掛載階段
Updating: 運行時階段
Unmounting: 卸載階段
-
Error Handling: 錯誤處理
import React from 'react' import ReactDOM from 'react-dom' import './index.scss' class Child extends React.Component{ // 構造函數(shù) constructor (props) { super(props) this.state = { data: 'oldzhuzhu' } console.log('這里是初始化數(shù)據(jù)constructor') } componentWillMount () { // 組件渲染前 console.log('componentWillMount') } componentDidMount () { // 組件渲染結束 console.log('componentDidMount') } componentWillReceiveProps () { // 將要接受父組件傳來的props觸發(fā) console.log('componentWillReceiveProps') } shouldComponentUpdate () { // 子組件是不是應該更新 console.log('shouldComponentUpdate') return true // 如果是false,后面的update就不會跟著更新 } componentWillUpdate () { // 組件將要更新 console.log('componentWillUpdate') } componentDidUpdate () { // 組件更新完成 console.log('componentDidUpdate') } componentWillUnmount () { // 組件將要銷毀調(diào)用 console.log('componentWillUnmount') } // 點擊事件 handleClick () { console.log('這里是更新數(shù)組') this.setState({ data: 'zhuzhuzhu' }) } // 渲染 render () { console.log('render') return ( <div> {this.state.data} 接收到的props: {this.props.data} <button onClick={() => {this.handleClick()}}>更新組件</button> </div> ) } } class Father extends React.Component{ constructor (props) { super(props) this.state = { data: 'old props' } } changeData () { this.setState({ data: 'new props', show: true }) } byeChild () { this.setState({ show: false }) } render () { return ( <div> { this.state.show ? <Child data={this.state.data}/> : null } <button onClick={() => {this.changeData()}}>改變子組件的props</button> <button onClick={() => {this.byeChild()}}></button> </div> ) } } ReactDOM.render( <Father />, document.getElementById('app') )
按順序輸出值:
constructor // 構造函數(shù)初始化
componentWillMount // 組件渲染前
render // 組價渲染
componentDidMount // 選件渲染完成
componentWillReceiveProps // 子組件接收到父組件傳來的props
shouldComponentUpdate // 是否組件進行更新,true更新,false接下來的周期不觸發(fā)
componentWillUpdate // 組件更新前
render // 更新
componentDidUpdate // 組件更新結束
componentWillUnmount // 組件被摧毀之前
react-router
1.router幾種方式
-
頁面路由
window.location.href = '地址' // www.baidu.com history.back() //回退 -
hash 路由
window.location = '#hash' window.onhashchange = function () { console.log('current hash' + window.location.hash) } -
h5路由
history.pushState('name', 'title', '/path') // 推進一個狀態(tài) history.replaceState('name', 'title', '/path') // 更換一個狀態(tài) window.onpopstate = function () { console.log(window.location.href) console.log(window.location.pathname) console.log(window.location.hash) console.log(window.location.search) }
2. react-router幾種方式
-
hash
import React from 'react' import ReactDOM from 'react-dom' import {HashRouter as Router,Route,Link} from 'react-router-dom' // 這里是Hash import './index.scss' class A extends React.Component { constructor (props) { super(props) } render () { return ( <div>Component A</div> ) } } class B extends React.Component { constructor (props) { super(props) } render () { return ( <div>Component B</div> ) } } class Wrapper extends React.Component { constructor (props) { super(props) } render () { return ( <div> <Link to="/a" >組件A</Link> <br /> <Link to="/b" >組件B </Link> {this.props.children} </div> ) } } ReactDOM.render( <Router> <Wrapper> <Route path="/a" component={A}/> <Route path="/b" component={B}/> </Wrapper> </Router> , document.getElementById('app') ) -
browser
如果改成 import {BrowserRouter as Router,Route,Link} from 'react-router-dom' 路徑地址都會變成 http://localhost:8080/a, 此時請求的是后端代碼,在復制這個鏈接在其他頁面打開時,會報成404,因為后臺并沒有生成這個地址
3.router傳參,組件接受不同組件傳參
- 引入switch
import {HashRouter as Router,Route,Link,Switch} from 'react-router-dom'
class A extends React.Component {
constructor (props) {
super(props)
}
render () {
return (
<div>
Component A
<Switch>
<Route
exact // 必須完全符合path
path={`${this.props.match.path}`}
render={(route) => {
return <div>當前組件是不帶參數(shù)的A</div>
}}
/>
<Route >
path={`${this.props.match.path}/sub`}
render={(route) => {
return <div>當前組件是sub</div>
}}
/>
<Route
path={`${this.props.match.path}/:id`} // 通過路由地址解析
render={(route) => {
return <div>當前組件是帶參數(shù)的A,參數(shù)是 : {route.match.params.id}</div>
}}/>
</Switch>
</div>
)
}
}
class B extends React.Component {
constructor (props) {
super(props)
}
render () {
return (
<div>Component B</div>
)
}
}
class Wrapper extends React.Component {
constructor (props) {
super(props)
}
render () {
return (
<div>
<Link to="/a" >組件A</Link>
<br />
<Link to="/a/123" >帶參數(shù)的組件A</Link>
<br />
<Link to="/a/sub" >/a/sub子路徑</Link>
<br />
<Link to="/b" >組件B </Link>
{this.props.children}
</div>
)
}
}
ReactDOM.render(
<Router>
<Wrapper>
<Route path="/a" component={A}/>
<Route path="/b" component={B}/>
</Wrapper>
</Router>
,
document.getElementById('app')
)
開始正式共享單車項目知識點(react全家桶+ant ui 組件+公共機制封裝)
地址:https://github.com/yangxinjian/reactAntBike.git
- react基礎知識,生命周期(見上部分的基礎知識點)
- router4.0
- redux集成開發(fā)
- ant最實用基礎組件
- ant柵格系統(tǒng)
- Etable組件封裝
- BaseForm組件封裝
- 表格內(nèi)嵌單選,復選封裝
- axios請求插件封裝
- api封裝
- 錯誤攔截
- 權限,菜單封裝等
- 地圖,圖表的使用
安裝腳手架(上述有步驟,可選擇自己搭建,也可以使用官方腳手架)
sudo cnpm install -g create-react-app
初始化項目
create-react-app bikesystem (你項目的名稱:注意不用大寫字母)
安裝react-router
yarn add react-router
啟動項目
yarn start
項目所需要的插件
- 安裝react-router,axios
- 安裝AntD
- 暴露webpack配置文件
- 安裝less-loader
- 修改less-loader
sudo yarn add react-router-dom axios less-loader
sudo yarn add less
sudo yarn antd (支付寶做的ui組件)
引用antd樣式
import {Button} from 'antd' // 引用某一個組件
<Button>使用組件</Button>
import 'antd/dist/antd.css'
- 想要按需加載對應組件css,需要安裝babel,安裝后就無需引用上面的css
sudo yarn add babel-plugin-import
sudo yarn add less@^2.7.3
在package.json 的babel下加入
"plugins": [
[
"import",
{
"libraryName": "antd",
"style": "css"
}
]
]
暴露webpack文件,得到config文件夾
sudo yarn eject
更改config/webpack.config.js ,使項目能識別less文件,配置后重啟生效
- 在 // style files regexes下 添加
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
- 在 cssRegex引用后面下 添加less配置對象
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3, // 這里不要與其他配置的數(shù)字一樣
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader'
),
sideEffects: true,
},
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3, // 這里不要與其他配置的數(shù)字一樣
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'less-loader'
),
},
項目架構
-
在src中新建組件文件夾,取名component,分別根據(jù)項目搭建出header頭部文件夾,footer底部文件夾,NavLeft左菜單文件夾,以及在src下新建admin.js
Jietu20190401-163524.jpg
在admin.js中配置主結構,用row,col,并引入三個外部組件,注意?。。∪齻€外部組件的內(nèi)容不能為空,否則會報錯
import React from 'react' import { Row, Col} from 'antd'; import Header from './components/header'; import Footer from './components/footer'; export default class Admin extends React.Component{ render () { return ( <Row> <Col span="3"> left </Col> <Col span="21"> <Header> header </Header> <Row> contet </Row> <Footer> footer </Footer> </Col> </Row> ) } } 作為主要的菜單結構,單獨封裝出一個js,寫在了config下的menuConfig.js中,請大家自行git clone 我的項目地址,本項目是開源的項目
public里文件是將要被打包部署到服務器上。一般是圖片等元素信息
項目中使用的功能方法
1. 請求外部的百度地圖下的天氣API
先在百度地圖開放平臺,創(chuàng)建應用,選擇瀏覽器,生成自己的AK碼
-
在代碼中安裝jsonp
sudo yarn add jsonp --save -
封裝jsonp方法
import JsonP from 'jsonp' export default class Axios { // 鏈式調(diào)用 static jsonp (options) { return new Promise((resolve, reject) => { JsonP(options.url,{ param: 'callback' },function(err, response){ if (response.status == 'success') { // 成功回調(diào) resolve(response); } else { reject(response.messsage); // 失敗回調(diào) } }) }) } } -
在組件中調(diào)用
import axios from '../../axios' state={} componentWillMount () { this.getWeatherAPIData() // 調(diào)用方法 } // 獲取天氣api,encodeURIComponent編碼對中文的處理 getWeatherAPIData () { let city = '北京' axios.jsonp({ url: 'http://api.map.baidu.com/telematics/v3/weather?location='+encodeURIComponent(city)+'&output=json&ak=3p49MVra6urFRGOT9s8UBWr2' // api地址 , ak是你自己申請的應用ak }).then((res) => { if(res.status == 'success'){ let data = res.results[0].weather_data[0]; // 取當前的天氣 this.setState({ dayPictureUrl:data.dayPictureUrl, weather:data.weather }) } }) } // 在render中渲染 <span className="weather-img"> <img src={this.state.dayPictureUrl} alt="" /> </span> <span className="weather-detail"> {this.state.weather} </span>
2.react-router4.0(不需要路由配置,一切皆組件,瀏覽器端)
-
核心用法:
-
HashRouter和BrowserRouter
hash: http://localhost:3000/#/admin browser: http://localhost:3000/admin
2.Route:path,exact,component,render --- 參考項目中的pages下的router-demo下的router2文件夾的內(nèi)容
<Router> <Home> // home 是主界面,包含的都是他的子組件路由 <Route path="/" component={Main} exact></Route> <Route path="/about" component={About}></Route> <Route path="/topics" component={Topics}></Route> </Home> </Router> 主界面加載子組件的地方,添加{this.props.children}3.NavLink,Link ------ 通過this.props.match接受路由帶的內(nèi)容--- 參考項目中的pages下的router-demo下的router1文件夾的內(nèi)容
<HashRouter> {/* 需要根節(jié)點 */} <div> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> <li> <Link to="/topics">Topics</Link> </li> </ul> {/* /about會檢索到/和/about,所以防止加載兩次,加上exact精確匹配 */} <Route path="/" component={Main}></Route> <Route path="/about" component={About}></Route> <Route path="/topics" component={Topics}></Route> </div> </HashRouter>4.Switch
<Switch> {/* switch只會加載一次,按順序 */} <Route path="/" component={Main}></Route> <Route path="/about" component={About}></Route> <Route path="/topics" component={Topics}></Route> </Switch>5.Redict
<Redirect to="/admin/home" />- withRouter 直接跳轉(zhuǎn)路由
-
import {withRouter} from 'react-router-dom' 引入
使用
this.props.history.push({
pathname: '/admin'
})
渲染,在export default 中增添
withRouter(Form.create()(OrganizeManage))
3.項目中運用router實現(xiàn)菜單導航路由
- 根路由的編寫
在src目錄下建立router.js文件 , 并在全局index.js中更換Router
-
在router.js中引用app.js作為主框架
import React from 'react' import {HashRouter, Route, Switch} from 'react-router-dom' import App from './App' import Login from './pages/login' import Admin from './admin' export default class IRouter extends React.Component{ render () { return ( <HashRouter> <App> <Route path="/login" component={Login}/> (一級路由--登錄界面) <Route path="/admin" component={Admin}/> (一級路由--登錄進來的頁面) </App> </HashRouter> ) } } -
在app中引用子路由
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div> {this.props.children} </div> ); } } export default App; -
在一級路由下嵌套二級路由,更改的admin界面下的路由配置
<Route path="/admin" render={() => // 二級路由 <Admin> <Route path="/admin/ui/button" component={Button}/> </Admin> }/> 在admin.js中更改 <Row className="content" > {this.props.children} // 這里引用 </Row> -
菜單路由link配置
引用路由 在NavLeft頁面下 import {NavLink} from 'react-router-dom' 在menu-item中包裹路由 <Menu.Item title={item.title} key={item.key}> <NavLink to={item.key}>{item.title}</NavLink> // 這里跳轉(zhuǎn)地址 </Menu.Item> 配置路由加載錯誤的情況下的展示頁面
先建立文件夾nomatch包裹404展示文件,再在路由里面引用
<Switch>
<Route path="/admin/ui/buttons" component={Buttons}/>
<Route component={NoMatch}></Route>
</Switch>
Ant design UI組件的基本使用
- 在項目地址下的ui文件夾有基本的使用展示,你可以選擇clone我的項目下到本地進行查看,接下來的筆記介紹幾種初學者會遇到的使用
1. form組件實現(xiàn)登錄
-
創(chuàng)建用戶名和密碼以及登錄摁鈕
需要引用FormItem const FormItem = Form.item <Form> <FromItem> <Input prefix={<Icon type="user" />} placeholder="請輸入用戶名"/> </FormItem> <FromItem> <Input prefix={<Icon type="lock" />} placeholder="請輸入密碼"/> </FormItem> <FromItem> <Button type="primary">登錄</Button> </FormItem> </Form> -
添加輸入驗證
第一步:在render中創(chuàng)建getFieldDecorator對象const { getFieldDecorator } = this.props.form;第二步:給標簽內(nèi)嵌入js語法實現(xiàn)驗證
<FormItem> { getFieldDecorator('userName',{ // username是定義的用戶名變量,不同輸入框的命名要是分別的 initialValue:'', rules:[ { required:true, message:'用戶名不能為空' }, { min:5,max:10, message:'長度不在范圍內(nèi)' }, { pattern:new RegExp('^\\w+$','g'), // 正則表達式都可以 message:'用戶名必須為字母或者數(shù)字' } ] })( <Input prefix={<Icon type="user"/>} placeholder="請輸入用戶名" /> ) } </FormItem>第三步:通過Form創(chuàng)建表單
class FormLogin extends React.Component 將前面的export default 去掉 export default Form.create()(FormLogin); 在結尾中加入 -
添加摁鈕點擊校驗
第一步: 給摁鈕綁定事件<Button type="primary" onClick={this.handleSubmit}>登錄</Button>第二步:編寫事件
handleSubmit = ()=>{ let userInfo = this.props.form.getFieldsValue(); this.props.form.validateFields((err,values)=>{ if(!err){ // 如果沒有錯的情況下 message.success(`${userInfo.userName} 恭喜你,您通過本次表單組件學習,當前密碼為:${userInfo.userPwd}`) } }) }
2. datePicker日期控件的引用
想要設置默認值,需要引用moment插件
sudo yarn add moment --save
import moment from 'moment
<FormItem label="生日" >
{
getFieldDecorator('birth', {
initialValue: moment('2018-08-08') , // 獲取moment
rules: [
{
required: true
}
]
})(
<DatePicker
showTime
format="YYYY-MM-DD"
/>
)
}
</FormItem>
3.柵格左右大小分配格式提取
const formItemLayout = { // 柵格樣式 復用
labelCol: {
xs: 24,
sm: 4
},
wrapperCol: {
xs: 24,
sm: 8
}
}
<FormItem {... formItemLayout}> 使用es6結構賦值
</FormItem>
4.上傳文件
<Upload
listType="picture-card"
howUploadList={false}
// axtion是服務器的地址
action="http://jsonplaceholder.typicode.com/posts/"
nChange={this.handleChange}
>
{this.state.userImg?<img src={this.state.userImg} />:<Icon type="plus"></Icon>}
</Upload>
handleChange = (info) => { // 上傳圖像
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// 正常是調(diào)用服務器的上傳地址
}
}
5. easy mock,語法參考mock.js
是一款可以適用于前端編寫線上接口,以便測試調(diào)用,自動創(chuàng)建項目,編寫接口,放入數(shù)據(jù)
' result|10': [ { // 生成十條數(shù)據(jù)
"code": 0,
"message": '失敗',
"result|10": [{
'id|+1': 0, // 遞增+1
'userName': '@cname', // 隨機的名字
'sex|1-2': 1, // 隨機在1-2中選擇
'telephone': '888888',
'state|1-5': 1,// 隨機在1-5中選擇
'interest|1-8': 1,// 隨機在1-8中選擇
'birth': '1995-06-30',
'address': '地球',
}]
6. axios使用與封裝
-
最簡單的引用
// 動態(tài)獲取mock數(shù)據(jù),根據(jù)上步創(chuàng)建的接口獲取 import axios from 'axios request = () => { let baseUrl = 'https://www.easy-mock.com/mock/5caffee40ee3ff114ba3e62b/yxjapi' axios.get(`${baseUrl}/table/list`).then((res) =>{ if (res.data.code == '0') { this.setState({ dataSource2: res.data.result }) } }) } -
封裝axios請求
static ajax(options){ const baseURL = 'https://www.easy-mock.com/mock/5caffee40ee3ff114ba3e62b/yxjapi' // 接口地址 return new Promise((resolve,reject) => { axios({ url: options.url, method: 'get', baseURL: baseURL, timeout: 7000, params: (options.data.params && options.data) || '' }).then((response) => { if (response.status == '200') {// http請求自帶status let res = response.data if (res.code == '0') { // 項目服務器后臺接口業(yè)務層面的判斷 resolve(res) }else { Modal.info({ title: '提示', content: res.data.messsage }) } }else { reject(response.data) } }) }) } -
使用
axios.ajax({ url: '/table/list', data: { params: { page: 1 } } }).then((res) => { if (res.code == '0') { this.setState({ dataSource2: res.result }) } })
7. 表格分頁的封裝
// 分頁的封裝
pagination (data, callback) {
let page = {
onChange: (current) => {
callback(current)
},
current: data.result.page,
pageSize: data.result.page_size,
total: data.result.total,
showTotal: () => {
return `共${data.result.total}條`
},
showQuickJumper: true
}
return page
}
-
使用
request = () => { axios.ajax({ url: '/table/list', data: { params: { page: 1 }, isShowLoading: true } }).then((res) => { if (res.code == '0') { this.setState({ dataSource2: res.result.list, selectedRowKeys: [], // 刪除后的重置 selectedRows: null, // 刪除后的重置 pagination: util.pagination(res, (current)=>{ // 根據(jù)點擊的頁瑪進行數(shù)據(jù)變化處理 // to-do _this.params.page = current _this.request() message.success(`當前頁${current}`) }) }) } }) <Table bordered dataSource={this.state.dataSource2} columns={columns} pagination={this.state.pagination} />
8 . 組件使用外部class的form表單,獲取表單對象
綁定wrappedComponentRef方法輸出表達對象給dataform自定義,通過
let drgInfo = this.dataForm.props.form.getFieldsValue() 獲取數(shù)據(jù)
<OpenFormDatas wrappedComponentRef={(datas) => {this.dataForm = datas}} />
9. 地圖功能的實現(xiàn)(基于項目中的騎車路線)
- 創(chuàng)建ak,加載百度地圖sdk
- 地圖初始化,添加地圖控件
- 繪制路線(通過坐標點)
- 繪制服務區(qū)地圖
(1) 創(chuàng)建ak,加載百度地圖sdk
第一步:打開map.baidu.com,找到地圖開放平臺,點擊進入
第二步:點擊開發(fā)文檔,點擊web開發(fā)下的JavaScript API
第三步:在產(chǎn)品簡介中,點擊申請秘鑰ak,創(chuàng)建應用,選擇瀏覽器 端,白名單中設置*號,點擊創(chuàng)建即可獲取ak值
第四步:在public文件夾下的index.html的head部分中引用以下代碼
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=您的ak秘鑰"></script>
(2)初始化地圖
第一步:創(chuàng)建一個渲染地圖的容器,取一個id值
<div id="orderDetailMap" className="order-map"></div>
第二步:為獲取地圖對象添加方法,并且添加控件,同時在接口數(shù)據(jù)返回后調(diào)用方法
renderMap = (result)=>{
this.map = new window.BMap.Map('orderDetailMap');
// 添加地圖控件
this.map.centerAndZoom('北京',11); // 設置地圖的中心點
this.addMapControl();
}
// 添加地圖控件
addMapControl = ()=>{
let map = this.map;
map.addControl(new window.BMap.ScaleControl({ anchor: window.BMAP_ANCHOR_TOP_RIGHT}));
map.addControl(new window.BMap.NavigationControl({ anchor: window.BMAP_ANCHOR_TOP_RIGHT }));
}
(3)繪制路線
// 繪制用戶的行駛路線
drawBikeRoute = (positionList)=>{
let map = this.map;
let startPoint = '';
let endPoint = '';
// lon 經(jīng)度 lat 緯度
if (positionList.length>0){ // 判斷行駛軌跡是否存在
let first = positionList[0];
let last = positionList[positionList.length-1];
startPoint = new window.BMap.Point(first.lon,first.lat);// 獲取地圖點
// 創(chuàng)建開始坐標點的圖標
let startIcon = new window.BMap.Icon('/assets/start_point.png',
new window.BMap.Size(36,42),{// 整個圖標
imageSize:new window.BMap.Size(36,42), // 圖片的大小
anchor: new window.BMap.Size(18, 42)
})
// marker是為了將icon圖標嵌入到地圖里面,并且要設置坐標點實現(xiàn)點
let startMarker = new window.BMap.Marker(startPoint, { icon: startIcon});
this.map.addOverlay(startMarker);
// 創(chuàng)建結束坐標點的圖標
endPoint = new window.BMap.Point(last.lon, last.lat);
let endIcon = new window.BMap.Icon('/assets/end_point.png', new window.BMap.Size(36, 42), {
imageSize: new window.BMap.Size(36, 42),
anchor: new window.BMap.Size(18, 42)
})
// marker是為了將icon圖標嵌入到地圖里面,并且要設置坐標點實現(xiàn)點
let endMarker = new window.BMap.Marker(endPoint, { icon: endIcon });
this.map.addOverlay(endMarker);
// 連接路線圖
let trackPoint = [];
for(let i=0;i<positionList.length;i++){
let point = positionList[i];
trackPoint.push(new window.BMap.Point(point.lon, point.lat));
}
// 設置線的樣式
let polyline = new window.BMap.Polyline(trackPoint,{
strokeColor:'#1869AD',
strokeWeight:3,
strokeOpacity:1
})
this.map.addOverlay(polyline);
this.map.centerAndZoom(endPoint, 11);
}
}
(4) 繪制服務區(qū)地圖
// 繪制服務區(qū)
drwaServiceArea = (positionList)=>{
// 連接路線圖
let trackPoint = [];
for (let i = 0; i < positionList.length; i++) {
let point = positionList[i];
trackPoint.push(new window.BMap.Point(point.lon, point.lat));
}
// 繪制服務區(qū)
let polygon = new window.BMap.Polygon(trackPoint, {
strokeColor: '#CE0000',
strokeWeight: 4,
strokeOpacity: 1,
fillColor: '#ff8605',
fillOpacity:0.4
})
this.map.addOverlay(polygon);
}
10. echart的使用
-
安裝
sudo yarn add echarts-for-react --save sudo yarn add echarts --save -
引用
import ReactEcharts from 'echarts-for-react'; import echarts from 'echarts/lib/echarts' 可以按需引用echart中某一個組件 // 引入餅圖和折線圖 import 'echarts/lib/chart/pie' 使用
getOption() {
let option = {
title: {
text: '用戶騎行訂單',
x : 'center'
},
legend : {
orient: 'vertical',
right: 10,
top: 20,
bottom: 20,
data: ['周一','周二','周三','周四','周五','周六','周日']
},
tooltip: {
trigger : 'item',
formatter : "{a} <br/> : {c} (u0z1t8os%)"
},
series: [
{
name : '訂單量',
type : 'pie',
radius : '55%',
center : [
'50%', '60%'
],
data:[
{
value:1000,
name:'周一'
}
],
itemStyle : {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
return option;
}
// render中渲染
<ReactEcharts
option={this.getOption()}
style={{ height: 500 }}/>
11. 富文本編輯器的使用 ------ react-draft-wysiwyg
-
安裝富文本組件以及將組件內(nèi)容裝換成html格式的draftjs
sudo yarn add react-draft-wysiwyg draftjs-to-html --save 使用 參考項目代碼把。。。文字過長了,不讓發(fā)了,我都提交了
