react抽離配置文件、配置@符號、調整src文件夾---配置scss、編寫項目的頁面結構、建立各個頁面 src/views、開始路由、入口文件處修改代碼、修改App.js佈局文件、添加底部的導航佈局

一、回顧

二、react項目的配置

react默認建立的項目配置文件在 node_modules/react-scripts 文件夾內部css

2.1 抽離配置文件

cnpm run ejecthtml

cnpm run startvue

2.2 配置@符號

打開config/webpack.config.js,ctrl + f 搜索 alias,添加配置node

alias: {
  'react-native': 'react-native-web',
  // ++++++++++++++++++++++++++++++++++++++++++++++++++
  '@': path.join(__dirname, '../', 'src'),
  ...(isEnvProductionProfile && {
    'react-dom$': 'react-dom/profiling',
    'scheduler/tracing': 'scheduler/tracing-profiling',
  }),
  ...(modules.webpackAliases || {}),
},

2.3 調整src文件夾---配置scss

src
lib
App.js
index.js
logo.svg
serviceWorker.js
main.scssreact

  • index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './main.scss'; // +++++++++++++++++++++++++++++++
import App from '@/App';
import * as serviceWorker from '@/serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
  • App.js
import React from 'react';

function App() {
  return (
    <div className="App">
     
    </div>
  );
}

export default App;
  • main.scss
@import '@/lib/reset.scss';

html {
  @include background-color(#f66);
}

三、編寫項目的頁面結構

jsx中的class須要寫成classNamewebpack

App.jsios

import React from 'react';

function App() {
  return (
    <div className="container">
      <div className="box">
        <header className="header"></header>
        <div className="content"></div>
      </div>
      <footer className="footer"></footer>
    </div>
  );
}

export default App;

編寫樣式es6

@import '@/lib/reset.scss';

html, body, #root, .container {
  @include rect(100%, 100%);
}

.container {
  @include flexbox();
  @include flex-direction(column);
  .box {
    @include flex();
    @include rect(100%, auto);
    @include flexbox();
    @include flex-direction(column);
    .header {
      @include rect(100%, 0.44rem);
      @include background-color(#f66);
    }
    .content {
      @include flex();
      @include rect(100%, auto);
      @include overflow();
    }
  }
  .footer {
    @include rect(100%, 0.5rem);
    @include background-color(#efefef);
  }
}

四、建立各個頁面 src/views

views/home/index.jsxweb

views/kind/index.jsxnpm

views/cart/index.jsx

views/user/index.jsx

以首頁爲例

import React from 'react';

class Com extends React.Component {
  render () {
    return (
      <div className="box">
        <header className="header">首頁頭部</header>
        <div className="content">首頁內容</div>
      </div>
    )
  }
}

export default Com;
  • 測試頁面模塊 App.js
import React from 'react';
import Home from '@/views/home'; // +++++++++++++++++
function App() {
  return (
    <div className="container">
      {
        // +++++++++++++++++
      }
      <Home />
      <footer className="footer"></footer>
    </div>
  );
}

export default App;

五、開始路由

入口找佈局,佈局找頁面,頁面找組件

https://reacttraining.com/react-router/web/guides/quick-start

cnpm i react-router-dom -S

之前 react-router

5.1 入口文件處修改代碼

import React from 'react';
import ReactDOM from 'react-dom';
import './main.scss';
import App from '@/App';
import * as serviceWorker from '@/serviceWorker';
// HashRouter ---- vue中的hash   /#/home
// BrowserRouter ---- vue中的history /home
//  BrowserRouter as Router 把BrowserRouter起名爲Router
// Route 路由
// Switch多個只能選中一個  ------ BrowserRouter 只能有一個子元素
// 一個應用有不少佈局 --- Switch
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';

// 入口找佈局,佈局找頁面,頁面找組件
// App 就是一個佈局文件
ReactDOM.render(
  <Router>
    <Switch>
      <Route path="/">
        <App />
      </Route>
    </Switch>
  </Router>
  , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

5.2 修改App.js佈局文件

import React from 'react';
import Home from '@/views/home';
import Kind from '@/views/kind';
import Cart from '@/views/cart';
import User from '@/views/user';
// 一種佈局有不少的頁面 --- Switch
import { Switch, Route } from 'react-router-dom';
// 佈局找頁面
function App() {
  return (
    <div className="container">
      <Switch>
        <Route path = "/home"><Home /></Route>
        <Route path = "/kind"><Kind /></Route>
        <Route path = "/cart"><Cart /></Route>
        <Route path = "/user" component = { User }/ >
        {
          //<Route path = "/user"><User /></Route>
        }
      </Switch>
      <footer className="footer"></footer>
    </div>
  );
}

export default App;

六、添加底部的導航佈局

<footer className="footer">
  <ul>
    <li>
      <span className="iconfont icon-fonts-shouye"></span>
      <p>首頁</p>
    </li>
    <li>
      <span className="iconfont icon-icon"></span>
      <p>分類</p>
    </li>
    <li>
      <span className="iconfont icon-gouwuche"></span>
      <p>購物車</p>
    </li>
    <li>
      <span className="iconfont icon-wode"></span>
      <p>個人</p>
    </li>
  </ul>
</footer>

main.scss

.footer {
  @include rect(100%, 0.5rem);
  @include background-color(#efefef);
  ul {
    @include rect(100%, 100%);
    @include flexbox();
    li {
      @include flex();
      @include rect(auto, 100%);
      @include flexbox();
      @include flex-direction(column);
      @include justify-content();
      @include align-items();
      span {
        @include font-size(0.24rem);
      }
      p {
        @include font-size(0.12rem);
      }
    }
  }
}

七、聲明式跳轉路由

Link / NavLink

  • Link 跳轉沒法設置樣式
    https://reacttraining.com/react-router/web/api/Link

  • NavLink 能夠給選中的項設置樣式
    https://reacttraining.com/react-router/web/api/NavLink

App.js ---- NavLink標籤會自動解析爲 a標籤,須要更改樣式表,

經過activeClassName給選中的路由添加樣式

// NavLink 必須導入
import { Switch, Route, NavLink } from 'react-router-dom';

<footer className="footer">
  <ul>
    <NavLink to="/home" activeClassName="active">
      <span className="iconfont icon-fonts-shouye"></span>
      <p>首頁</p>
    </NavLink>
    <NavLink to="/kind" activeClassName="active">
      <span className="iconfont icon-icon"></span>
      <p>分類</p>
    </NavLink>
    <NavLink to="/cart" activeClassName="active">
      <span className="iconfont icon-gouwuche"></span>
      <p>購物車</p>
    </NavLink>
    <NavLink to="/user" activeClassName="active">
      <span className="iconfont icon-wode"></span>
      <p>個人</p>
    </NavLink>
  </ul>
</footer>

main.scss

.footer {
  @include rect(100%, 0.5rem);
  @include background-color(#efefef);
  ul {
    @include rect(100%, 100%);
    @include flexbox();
    a { // +++++++++++++++++++++++++
      @include color(#666);
      @include flex();
      @include rect(auto, 100%);
      @include flexbox();
      @include flex-direction(column);
      @include justify-content();
      @include align-items();
      span {
        @include font-size(0.24rem);
      }
      p {
        @include font-size(0.12rem);
      }
      &.active { // ++++++++++++++++++++++++++
        @include color(#f66);
      }
    }
  }
}

八、使用React UI庫

PC: element-ui 、 ant design (antd)

移動端: ant design mobile (antd-mobile)

https://mobile.ant.design/index-cn

https://mobile.ant.design/docs/react/use-with-create-react-app-cn

8.1 修改public/index.html

引入 FastClick 而且設置 html meta (更多參考 #576)

引入 Promise 的 fallback 支持 (部分安卓手機不支持 Promise)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
  <script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
  <script>
    if ('addEventListener' in document) {
      document.addEventListener('DOMContentLoaded', function() {
        FastClick.attach(document.body);
      }, false);
    }
    if(!window.Promise) {
      document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>');
    }
  </script>
    <link rel="apple-touch-icon" href="logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
    <link rel="stylesheet" href="//at.alicdn.com/t/font_1476238_uph8zgimp3.css">
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

8.2 安裝模塊

cnpm i antd-mobile -S

cnpm i babel-plugin-import -D

8.3 修改package.json文件中的babel選項

"babel": {
  "presets": [
    "react-app"
  ],
  // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  "plugins": [
    ["import", { "libraryName": "antd-mobile", "style": "css" }]
  ]
},

九、請求渲染首頁數據

9.1 解決跨域問題

cnpm i http-proxy-middleware -D

src/setupProxy.js

const proxy = require('http-proxy-middleware');

module.exports = function (app) {
  app.use(proxy('/api', {
    target: 'http://47.92.152.70', // 代理哪個服務器
    changeOrigin: true, // 代理
    pathRewrite: {
      '^/api': '' // 以 /api 開頭的請求,認爲就是請求的代理
      // /api/pro   ===> http://47.92.152.70/pro
    }
  }))
  // 純屬湊數
  app.use(proxy('/test', {
    target: 'https://www.baidu.com', // 代理哪個服務器
    changeOrigin: true, // 代理
    pathRewrite: {
      '^/test': ''
    }
  }))
}

9.2 封裝數據請求模塊

cnpm i axios -S

utils/request.js + utils/api.js

9.3 首頁使用UI庫的 走馬燈 ---- 輪播圖

https://mobile.ant.design/components/carousel-cn/

import React from 'react';
// ++++++++++++++++++++++++++++++++++++++++++++
import { Carousel } from 'antd-mobile';
// +++++++++++++++++++++++++++++++++++++++++++++++++
import { getBannerlist, getProlist } from '@/utils/api';
class Com extends React.Component {
  constructor (props) {
    super(props);
    this.state = { // +++++++++++++++++++++++++++++++++++++++++
      bannerlist: [{ bannerid: 1, img: 'images/1.jpg'}],
      prolist: []
    }
  }

  componentDidMount () { // +++++++++++++++++++++++++++++++++++
    getBannerlist().then(data => {
      console.log(data.data)
      this.setState({
        bannerlist: data.data
      })
    })
    getProlist().then(data => {
      this.setState({
        prolist: data.data
      })
    })
  }

  render () {
    return (
      <div className="box">
        <header className="header">首頁頭部</header>
        <div className="content">
          {
            // +++++++++++++++++++++++++++
          }
          <Carousel
            autoplay={ true }
            infinite
            beforeChange={(from, to) => console.log(`slide from ${from} to ${to}`)}
            afterChange={index => console.log('slide to', index)}
          >
            {this.state.bannerlist.map(item => (
              <a
                key={ item.bannerid }
                href="https://www.baidu.com"
                style={{ display: 'inline-block', width: '100%', height: '176px' }}
              >
                <img
                  src={`http://47.92.152.70/${item.img}`}
                  alt=""
                  style={{ width: '100%', verticalAlign: 'top' }}
                  onLoad={() => {
                    // fire window resize event to change height
                    window.dispatchEvent(new Event('resize'));
                    this.setState({ imgHeight: 'auto' });
                  }}
                />
              </a>
            ))}
          </Carousel>
        </div>
      </div>
    )
  }
}

export default Com;

main.scss中添加

* { touch-action: none; }

9.4 封裝Prolist.jsx組件,首頁列表的渲染

  • components/Prolist/index.jsx
import React from 'react';
import './style.scss';
// class組件獲取數據 使用 this.props
// 函數式組件含有默認的參數 props   ----- 等同與 class 組件中的this.props
const Com = (props) => {
  return (
    <ul className="prolist">
      <li className="proitem">
        <div className="proimg">
          <img src="" alt="" />
        </div>
        <div className="proinfo">
          111111111
        </div>
      </li>
    </ul>
  )
}

export default Com;
  • components/Prolist/style.scss
@import '@/lib/reset.scss';
.prolist {
  @include rect(100%, auto);
  .proitem {
    @include rect(100%, 1rem);
    @include border(0 0 1px 0, #efefef, solid); // 設定的是一個物理像素
    @include flexbox();
    .itemimg {
      @include rect(1rem, 1rem);
      img {
        @include rect(0.9rem, 0.9rem);
        @include border(1px, #f66, solid);
        @include margin(0.05rem);
        @include display(block);
      }
    }
    .iteminfo {
      @include flex();
    }
  }
}
  • 首頁面引入 Prolist 組件
import Prolist from '@/components/Prolist';
<Prolist />
  • 首頁傳值給列表
<Prolist prolist = { this.state.prolist }/>
  • 列表組件渲染數據
import React from 'react';
import './style.scss';
// class組件獲取數據 使用 this.props
// 函數式組件含有默認的參數 props   ----- 等同與 class 組件中的this.props
const Com = (props) => {
  return (
    <ul className="prolist">
      {
        props.prolist.map(item => {
          return (
            <li className="proitem" key = { item.proid }>
              <div className="proimg">
                <img src={ item.proimg } alt="" />
              </div>
              <div className="proinfo">
                { item.proname }
              </div>
            </li>
          )
        })
      }
    </ul>
  )
}

export default Com;

十、構建我的中心

// 設置變量,使用三木運算符斷定 用戶是否是登錄狀態
import React from 'react';

class Com extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      flag: false
    }
  }
  render () {
    return (
      <div className="box">
        <header className="header">我的中心頭部</header>
        <div className="content">
          {
            this.state.flag ? 
            <div>
              歡迎您......
            </div>
            :
            <div>
              <button>登錄</button>
              <button>註冊</button>
            </div>
          }
        </div>
      </div>
    )
  }
}

export default Com;
  • 斷定用戶有沒有登錄
componentDidMount () {
    // if (localStorage.getItem('isLogin') === 'ok') {
    //   this.setState({
    //     flag: true
    //   })
    // } else {
    //    this.setState({
    //      flag: false
    //    })
    // }
    let flag = localStorage.getItem('isLogin') === 'ok' ? true : false
    this.setState({
      flag
    })
  }
  • 給登錄按鈕設置點擊事件,跳轉到登錄頁面

十一、構建登錄頁面

views/login/index.jsx

import React from 'react';

class Com extends React.Component {
  render () {
    return (
      <div className="box">
        <header className="header">登錄</header>
        <div className="content">登錄</div>
      </div>
    )
  }
}

export default Com;

入口找佈局,佈局找頁面,頁面找組件 --- 登錄沒有底部的頁面佈局

  • 添加新的佈局文件

src/Other.js

import React from 'react';
import Login from '@/views/login';
import { Switch, Route } from 'react-router-dom';
// 佈局找頁面 /o/login --- 自定義  o 表示新的佈局
function Other () { 
  return (
    <div className="container">
      <Switch>
        <Route path="/o/login"><Login /></Route>
      </Switch>
    </div>
  );
}

export default Other;
  • 入口添加新的佈局 / 所對應的佈局在最下面
import React from 'react';
import ReactDOM from 'react-dom';
import './main.scss';
import App from '@/App';
import Other from '@/Other'; // 新的佈局文件 +++++++++++++++++++++++++++++++
import * as serviceWorker from '@/serviceWorker';
import { HashRouter as Router, Switch, Route } from 'react-router-dom';

// 入口找佈局,佈局找頁面,頁面找組件
// App 就是一個佈局文件
/**
<Route path="/o">
  <Other />
</Route>
*/

ReactDOM.render(
  <Router>
    <Switch>
      <Route path="/o">
        <Other />
      </Route>
      <Route path="/">
        <App />
      </Route>
    </Switch>
  </Router>
  , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
  • 點擊登錄 使用編程式跳轉 到 登錄頁面

this.props.history.push() / replace() / goBack()

// views/user/index.js
<button onClick = { () => {
  // console.log(this.props)
  this.props.history.push('/o/login')
}}>登錄</button>

12 編輯登錄的表單

views/login/index.js

import React from 'react';
import './style.scss'
class Com extends React.Component {
  render () {
    return (
      <div className="box">
        <header className="header">登錄</header>
        <div className="content">
          <input type="text" placeholder="手機號碼"/>
          <p className="tip"></p>
          <input type="password" placeholder="密碼" />
          <p className="tip"></p>
          <button className="userBtn" >登錄</button>
          <p className="tip"></p>
        </div>
      </div>
    )
  }
}

export default Com;

views/login/style.scss

input {
  outline: none;
  border: 0;
  display: block;
  width: 96%;
  margin: 5px 2%;
  border-bottom: 1px solid #efefef;
  line-height: 36px;
  text-indent: 10px;
}
.tip {
  color: #f66;
  text-align: center;
  height: 20px;
}
.userBtn {
  outline: none;
  border: 0;
  display: block;
  background-color:#f66;
  width: 96%;
  margin: 15px 2%;
  line-height: 40px;
  font-size: 18px;
  color: #fff;
}
  • 檢驗表單信息 --- 手機號
this.state = { 
  tel: '18813007814',
  telTip: ''
}

<input type="text" placeholder="手機號碼" value={ this.state.tel } onChange={ (event) => {
  // 輸入框綁定value值
  console.log(event.currentTarget.value)
  // 經過事件對象獲取輸入框的值
  let val = event.currentTarget.value
  // 提示標識變量
  let tip = ''
  // 輸入時,若是輸入的值爲空,標識爲空
  // 若是輸入的語法錯誤,標識爲手機號碼格式錯誤
  // 不然 標識爲ok
  tip = val === '' ? '' : val.length !== 11 ? '手機號碼格式錯誤' : 'ok'
  // 修改狀態 --- 視圖二次渲染
  this.setState({
    tel: val,
    telTip: tip
  })
} }/>
<p className="tip">{ this.state.telTip }</p>
  • 校驗表單信息 --- 密碼
this.state = { 
  tel: '18813007814',
  telTip: '',
  password: '123456',
  passwordTip: ''
}


<input type="password" placeholder="密碼" value= { this.state.password } onChange = { this.validPassword.bind(this)}/>
<p className="tip">{ this.state.passwordTip }</p>


validPassword (event) {
  // console.log(event)
  let val = event.currentTarget.value;
  let tip = ''
  tip = val === '' ? '' : val.length < 6 ? '密碼格式錯誤' : ''
  this.setState({
    password: val,
    passwordTip: tip
  })
}
  • 登錄功能
<button className="userBtn" onClick= { this.login.bind(this) }>登錄</button>
  • 封裝登錄的接口

utils/api.js

/**
 * 登錄接口
 * @param {tel} String 
 * @param {password} String 
 */
const login = (tel, password) => {
  return new Promise(resolve => {
    request.post('/users/login', { tel, password }).then(res => {
      resolve(res.data)
    })
  })
}

// 三、暴露接口
export {
  getProlist,
  getBannerlist,
  getCartlist,
  login // ++++++++++++++++++++++++
}
  • 實現登錄功能
login () {
  // 點擊登錄驗證手機號碼輸入是否正確
  if (this.state.tel === '' || this.state.telTip === '手機號碼格式錯誤') {
    this.setState({
      tip: '請輸入合法的電話號碼'
    })
    return
  }
  // 點擊登錄驗證密碼輸入是否正確
  if (this.state.password === '' || this.state.passwordTip === '密碼格式錯誤') {
    this.setState({
      tip: '請輸入合法的密碼'
    })
    return
  }
  // 請求接口
  login(this.state.tel, this.state.password).then(data => {
    console.log(data)
    let tip = '' // 顯示的是 後端返回的數據的標識
    if (data.code === '10086') {
      tip = '用戶未註冊,請先註冊'
    } else if (data.code === '10100') {
      tip = '密碼錯誤'
    } else {
      tip = '登錄成功'
      localStorage.setItem('token', data.token)
      localStorage.setItem('userid', data.userid)
      localStorage.setItem('username', data.username)
      localStorage.setItem('isLogin', 'ok')
      this.props.history.goBack()
    }
    this.setState({
      tip
    })
  })
}

1三、發送短信驗證碼

一、申請簽名和短信模板

二、獲取用戶標識

id: LTAIZQoVVoPuBjU9

secret: GfJuI2dLsCQh7Q56TmFxPTniXjkVnB

三、編寫代碼

cnpm i @alicloud/pop-core -S

相關文章
相關標籤/搜索