Slog101_使用React框架進行前端開發12

ArthurSlog

  • ArthurSlog

  • SLog-101

  • Year·1

  • Guangzhou·China

  • October 24th 2018

關注微信公衆號“ArthurSlog”

一時一局 我們需要見風使舵


開發環境MacOS(Mojave 10.14 (18A391))

信息源

開始編碼

  • 本次確定了後端的開發規範 和 工程結構
// 開發規範
// 所有數據的交互 都通過根目錄下的 main.js 
// 其他任何文件 禁止上下路徑的引用
// 例如:
// ./js/func1.js文件要引用 ./public/img/01.png文件的時候
// 禁止使用 require(../../public/img/o1.png) 或者 url(../../public/img/o1.png)
// 必須統一採用以下方式:
// const config = require('./main.js') 
  • 把 在main.js文件裏 創建一個對象 config作爲整個工程的配置文件

  • 爲了儘可能的整潔 不使用單獨的全局配置文件

  • 而其他文件都只需要引入 main.js文件裏的 config對象

  • 具體的代碼如下:

server/main.js

// 這裏面的後端接口 分爲四類
// 第一類是提供給 消費者客戶端的接口
// 第二類是提供給 商城管理者的接口
// 第三類是提供給 商城運營者的接口
// 第四類是提供給 商城開發者的接口

// 開發規範
// 所有數據的交互 都通過根目錄下的 main.js 
// 其他任何文件 禁止上下路徑的引用
// 例如:
// ./js/func1.js文件要引用 ./public/img/01.png文件的時候
// 禁止使用 require(../../public/img/o1.png) 或者 url(../../public/img/o1.png)
// 必須統一採用以下方式:
// const config = require('./main.js') 

// [] is array
// {} is object
// Arrays are Objects
// Arrays are a special type of objects
// The typeof operator in JavaScript returns "object" for arrays
// But, JavaScript arrays are best described as arrays
// Arrays use numbers to access its "elements". In this example, person[0] returns John:
// const person = ["John", "Doe", 46]
// Objects use names to access its "members". In this example, person.firstName returns John:
// const person = {firstName:"John", lastName:"Doe", age:46}
// 總結:
// {}裏面放的是object
// 而[]是特殊的object
// 所以 可以寫成{[], [], []}或{{}, {}, {}}這樣的形式
// 也可以寫成[{}, {}, {}]或[[], [], []]這樣的形式

// 引入路徑方案解決第三方包
// path.join()方法path使用特定於平臺的分隔符作爲分隔符將所有給定的段連接在一起 然後規範化生成的路徑
// 零長度path段會被忽略 如果連接的路徑字符串是零長度字符串 則返回'.' 表示當前工作目錄
const path = require('path')

// 這裏config作爲一個object 所以使用{}
const config = {
    jsUrl: path.join(__dirname, "/src/js"),
    imgUrl: path.join(__dirname, "/src/public/img"),
    // 公告欄顯示的信息
    BulletinBoardMsg: '[歡迎來到ArthurSlogStore]_當前時間是: '
}
// 導出工程的配置文件
module.exports = config

// 引入demo
const Issue = require(path.join(config.jsUrl, "/issue.js"))
// 引入測試函數
const Func1 = require(path.join(config.jsUrl, "/func1.js"))
// 引入‘公告欄’函數
const BulletinBoard = require(path.join(config.jsUrl, "/func00.js"))

// 引入express框架
const express = require('express')
// 因爲express無法解析POST的body數據,所以需要引入中間件body-parser來解析數據
const bodyParser = require('body-parser')

const app = express()

app.use(express.static('static'))
app.use(bodyParser.json())

// 引入mongodb驅動
const MongoClient = require('mongodb').MongoClient

// 接收到一個請求之後,返回一些數據
app.all('/api/issues/GET', (req, res) => {

    db.collection('issues').find().toArray()
        .then(issues => {
            const metadata = { total_count: issues.length }

            res.header("Access-Control-Allow-Origin", "*")
            res.header("Access-Control-Allow-Headers", "X-Requested-With")
            res.header("Access-Control-Allow-Headers", "Content-Type")
            res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
            res.header("X-Powered-By", ' 3.2.1')
            res.header("Content-Type", "application/json;charset=utf-8")

            res.json({ _metadata: metadata, records: issues })
        })
        .catch(err => {
            console.log('Error', err)

            res.header("Access-Control-Allow-Origin", "*")
            res.header("Access-Control-Allow-Headers", "X-Requested-With")
            res.header("Access-Control-Allow-Headers", "Content-Type")
            res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
            res.header("X-Powered-By", ' 3.2.1')
            res.header("Content-Type", "application/json;charset=utf-8")

            res.status(500).json({ message: `Internal Server Error: ${error}` })
        })

    console.log('GET: have a req from client')
})

// 因爲跨域請求,客戶端每一次完整的通信都會發送兩次請求
// 第一次請求是查看 "Access-Control-Allow-Origin" 等header的狀態
// 狀態允許才進行正式的通訊
// 所以,第一次的請求的裏面是不包含我們定義的數據的,第二次纔是
//
// 雖然在客戶端定義的是POST方法,但由於跨域請求
// 客戶端實際上發起了兩個請求,第二個請求需要第一個請求的確認
// 如果第一次確認失敗,那麼第二個請求就不會發出
// 在這個過程中,第一次請求並不是POST方法,而是OPTIONS方法,所以這裏使用all方法
// 當第一次請求成功後,第二次請求才是POST方法
// 這裏直接用一個all來響應兩次請求,不過分成兩個請求應該也是可以
// 接收到一個請求之後,返回一些數據
app.all('/api/issues/POST', (req, res) => {
    console.log(req.body)
    const newIssue = req.body
    if (newIssue.owner) {
        console.log('Accept: ', newIssue)
        // newIssue.id = issues.length + 1
        newIssue.created = new Date()


        if (!newIssue.status)
            newIssue.status = 'New'

        // issues.push(newIssue)

        const err = Issue.validIssueStatus(newIssue)
        if (err) {
            res.status(422).json({ message: `Invalid request: ${err}` })
            return
        }

        if (!newIssue) {
            console.log('newIssue is null')
        }
        else if (newIssue) {
            db.collection('issues').insertOne(newIssue)
                .then(result => {
                    return db.collection('issues').find({ _id: result.insertedId }).limit(1).next()
                })
                .then(newIssue => {
                    if (newIssue) {
                        res.header("Access-Control-Allow-Origin", "*")
                        res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
                        res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
                        res.header("X-Powered-By", ' 3.2.1')
                        res.header("Content-Type", "application/json;charset=utf-8")
                        res.json(newIssue)
                    } else {
                        console.log('newIssue is null')
                        return
                    }
                })
                .catch(err => {
                    console.log(err)
                    res.header("Access-Control-Allow-Origin", "*")
                    res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
                    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
                    res.header("X-Powered-By", ' 3.2.1')
                    res.header("Content-Type", "application/json;charset=utf-8")
                    res.status(500).json({ message: `Internal Server Error: ${err}` })
                    console.log('newIssue is error')
                })
        }
    }
    else {
        res.header("Access-Control-Allow-Origin", "*")
        res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
        res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
        res.header("X-Powered-By", ' 3.2.1')
        res.header("Content-Type", "application/json;charset=utf-8")
        res.json(newIssue)
        console.log('Client request is null!')
        return
    }

    console.log('POST: have a req from client')
})

// 這個函數 接收客戶端傳過來的數據 
// 判斷數據裏面是否含有 bootomTabBar的值
// 根據四個bootomTabBar的值 來返回每個基礎界面的數據
// 上面表述的不好理解 下面舉個栗子
// 比如 這個函數 接收到一個數據data data裏面包含有數據‘count’ 和 ‘tabBar’
// 並且‘count’等於1 ‘tabBar’也等於1
// 這代表了 客戶端想要得到的 bootomTabBar數量是一個 想要得到的bootomTabBar是第一個(商城首頁)
// 所以 通過這個函數
// 可以客戶端可以請求到 第一個界面的數據、第二個界面的數據、第三個界面的數據、第四個界面的數據
// {
//     count: int, // 這個就是代表了本次要修改的bootomTabBar的數量
//     tabBar: []  // 這個就是代表了本次要修改的bootomTabBar
// }
//
// 這裏其實還有另一種方式 就是全部的方法統一一個函數
// 具體是這麼做的 例如 app.all('/api/POST', ...
// 那麼 怎麼判斷客戶端請求的是哪個功能呢?
// 是這樣子的 是通過判斷客戶端傳過來的數據data裏面的特定的字段來判斷的
// 可以這麼看 就是根據客戶端傳過來的data裏的‘func’的值 進行路由的
// 路由什麼呢? 路由到指定的功能函數
// 例如 客戶端傳過來的數據data裏的‘func’==01
// 那麼就路由到函數 func1 這個func1函數返回第一個界面的數據
// 如果 ‘func’==02
// 那麼就路由到函數 func2 這個func2函數返回第二個界面的數據
// 以此類推
// 那麼 想要這麼做呢 就需要在後端進行嚴格的檢查判斷 需要一套工業級的機制
// 這麼看來 有點像在寫底層協議了 哈哈
// 包頭+包體 的模式
//
// 客戶端傳過來的數據結構
// {
//     func: int, // 這個就相當於包頭
//     object: [] // 這個就相當於包體
// }

app.all('/api/client', (req, res) => {
    console.log(req.body)
    // 現在採用第二種方式
    // 首先接收到來自客戶端的數據 並保存在 recDatas對象裏
    const recDatas = req.body
    // 接着 我們約定好的數據結構 就是 包頭+包體
    // 也就是 func + object 這樣的數據結構
    // 所以 首先判斷func的值 然後根據不同的值 執行不同的函數
    switch (recDatas.func) {
        // 這個是前後端接口的測試功能
        case 0:
            console.log('接收到了func等於0的數據')
            res.header("Access-Control-Allow-Origin", "*")
            break;
        // 這個來寫第一個正式功能
        // 這個寫個什麼功能好?給前端推點界面的數據好了
        // 這個就推給‘商城首頁’裏面‘公告欄’組件的數據好了
        // 首先 把功能寫出來 單獨一個js文件
        case 1:
            console.log('客戶端請求公告欄的數據')
            res.header("Access-Control-Allow-Origin", "*")
            res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
            res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
            let result = BulletinBoard.ReturnBBInfo(recDatas.object)
            res.json(result)
            break;
        default:
            res.header("Access-Control-Allow-Origin", "*")
            res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
            res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
            res.json('error');
    }
})

let db

MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true })
    .then(connection => {
        db = connection.db('issuetracker')
        app.listen(3000, () => {
            console.log('Server start on port 3000 ')
        })
    })
    .catch(err => {
        console.log('Error: ', err)
    })
  • 重新調整了一下 引用的路徑

  • 經過測試 後端正常服務

  • 工程文件已經上傳至Github:https://github.com/BlessedChild/ArthurSlogStore

  • 項目持續開發中 記錄跟不上開發速度

  • 有部分註解在源碼的註釋裏

  • 從源碼註釋可以看到 開發過程是一個實現與時間的權衡工作

  • 前期花更多的時間去做好開發規範 後期維護和拓展會大大降低成本

  • 但是在前期開發中 卻需要耗費大量的成本

  • 所以需要根據業務需求 和 收益期望來進行權衡

  • 至此,制定了暫行的後端開發規範 並對工程結構進行了相應的調整。


  • 歡迎關注我的微信公衆號 ArthurSlog

關注微信公衆號“ArthurSlog”

  • 如果你喜歡我的文章 歡迎點贊 留言

  • 謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章