12/08/2018, 17:35

Tạo Single Page App giống Medium bằng Reactjs+Redux kết hợp với Nodejs

Các công nghệ chính sử dụng trong project Redux Nodejs Cloudinary MongoDB Expressjs Ở project này ta sẽ sử dụng javascript để code cho cả client-side và server-side Server setup Client setup Ở đây backend của app sẽ sử dụng nodejs còn front end sẽ sử dụng react để render ...

alt Các công nghệ chính sử dụng trong project

  • Redux
  • Nodejs
  • Cloudinary
  • MongoDB
  • Expressjs Ở project này ta sẽ sử dụng javascript để code cho cả client-side và server-side
  • Server setup
  • Client setup

Ở đây backend của app sẽ sử dụng nodejs còn front end sẽ sử dụng react để render html.Và chắc chắn bạn phải cài Nodejs và Mongodb Ở đây mình sử dụng npm create-react-app để khởi tạo react thay vì code từ đầu đến cuối :v.

alt text

Sau khi cài đặt thành công thì đây là thành quả =))) Tiếp theo mình sẽ setup server, toàn bộ code server mình sẽ code hết ở trong folder server

alt text

  • mongoose
  • cloudinary(package để base64 ảnh cho ông nào lười làm chức năng upload ảnh)
  • helmet
  • express
  • cors
  • connect-multiparty
  • body-parser
  • compression

Đây là một đống bùi nhùi dependencies của nó. Và cài đặt thì sẽ one hit với 1 câu lệnh thôi ko có gì cao siêu cả

npm i mongoose cloudinary helmet express cors connect-multiparty body-parser compression Với backend thì mình sẽ build theo mô hình MVC cũ rích thôi. Thì trong folder server mình create thêm 3 thư mục là controller,route,models

alt text

  • Route sẽ chứa tất cả cái đường link(trả api, CRUD user, articel)

Ở phần này bạn nhớ chạy mongodb , ở đây để call data từ DB thì mình import mongoose Ở dưới là model của article. thì có bao nhiêu chức năng cho article thì có bấy nhiêu function thôi.Mình phát hiện ra cái view code của kialog nó hơi khó nhìn thì ở cuối bài mình sẽ gửi link github project để các bạn view cho dễ ~~

// server/models/Article.js
const mongoose = require('mongoose')
let ArticleSchema = new mongoose.Schema(
    {
        text: String,
        title: String,
        description: String,
        feature_img: String,
        claps: Number,
        author: {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        },
        comments: [
            {
                author: {
                    type: mongoose.Schema.Types.ObjectId,
                    ref: 'User'
                },
                text: String
            }
        ]
    }
);
ArticleSchema.methods.clap = function() {
    this.claps++
    return this.save()
}
ArticleSchema.methods.comment = function(c) {
    this.comments.push(c)
    return this.save()
}
ArticleSchema.methods.addAuthor = function (author_id) {
    this.author = author_id
    return this.save()
}
ArticleSchema.methods.getUserArticle = function (_id) {
    Article.find({'author': _id}).then((article) => {
        return article
    	})
}
module.exports = mongoose.model('Article', ArticleSchema)

Và tương tự cho model article thì ta cũng có code của model user. model user sẽ có các function thực hiện các chức năng như call data từ DB, add follow. follow user(như medium ấy mà ~~). Để trách dài bài viết mình sẽ ko paste code lên đây :v

/** server/controllers/article.ctrl.js*/
const Article = require('./../models/Article')
const User = require('./../models/User')
const fs = require('fs')
const cloudinary = require('cloudinary')
module.exports = {
    addArticle: (req, res, next) => {
        let { text, title, claps, description } = req.body
        if (req.files.image) {
            cloudinary.uploader.upload(req.files.image.path, (result) => {
                let obj = { text, title, claps, description, feature_img: result.url != null ? result.url : ' }
                saveArticle(obj)
            },{
                resource_type: 'image',
                eager: [
                    {effect: 'sepia'}
                ]
            })
        }else {
            saveArticle({ text, title, claps, description, feature_img: ' })
        }
        function saveArticle(obj) {
            new Article(obj).save((err, article) => {
                if (err)
                    res.send(err)
                else if (!article)
                    res.send(400)
                else {
                    return article.addAuthor(req.body.author_id).then((_article) => {
                        return res.send(_article)
                    })
                }
                next()
            })
        }
    },
    getAll: (req, res, next) => {
        Article.find(req.params.id)
        .populate('author')
        .populate('comments.author').exec((err, article)=> {
            if (err)
                res.send(err)
            else if (!article)
                res.send(404)
            else
                res.send(article)
            next()            
        })
    },
    /**
     * article_id
     */
    clapArticle: (req, res, next) => {
        Article.findById(req.body.article_id).then((article)=> {
            return article.clap().then(()=>{
                return res.json({msg: "Done"})
            })
        }).catch(next)
    },
    /**
     * comment, author_id, article_id
     */
    commentArticle: (req, res, next) => {
        Article.findById(req.body.article_id).then((article)=> {
            return article.comment({
                author: req.body.author_id,
                text: req.body.comment
            }).then(() => {
                return res.json({msg: "Done"})
            })
        }).catch(next)
    },
    /**
     * article_id
     */
    getArticle: (req, res, next) => {
        Article.findById(req.params.id)
        .populate('author')
        .populate('comments.author').exec((err, article)=> {
            if (err)
                res.send(err)
            else if (!article)
                res.send(404)
            else
                res.send(article)
            next()            
        })
    }
}

Trong đoạn code này thì mình sẽ export các một object . Trong object đó có các method như addArticle,getAll,clapArticle,commentArticle,getArticle . Mỗi method là một chức năng của bài viết.Nói chung vẫn quanh quẩn lại cái CRUD :v.Tương tự thế Ta cũng sẽ code cho User Controller ....

Ở Route thì mình sẽ sử dụng restful api.Vẫn theo rìa CRUD mà code thôi. delete,update thì post còn add, hoặc get thì request get.Vào folder route create 3 file là article.js, user.js và index.js. Mình thêm file index.js để tí nữa mình sẽ import code của cả 2 file article,user.js vào rồi export một lúc cho nó tiện :v.

alt text

code file article.js(trong folder routes nha : ))))

// server/routes/article.js
const articlecontroller = require('./../controllers/article.ctrl')
const multipart = require('connect-multiparty')
const multipartWare = multipart()
module.exports = (router) => {
    /**
     * get all articles
     */
    router
        .route('/articles')
        .get(articlecontroller.getAll)
    /**
     * add an article
     */
    router
        .route('/article')
        .post(multipartWare, articlecontroller.addArticle)
    /**
     * comment on an article
     */
    router
        .route('/article/comment')
        .post(articlecontroller.commentArticle)
    /**
     * get a particlular article to view
     */
    router
        .route('/article/:id')
        .get(articlecontroller.getArticle)
}

Ở dưới là code file index.js. Thì như mình bảo ở trên là nó sẽ import code cả 2 thằng article.js và user.js. À Bạn để ý thì trong arow function thấy có một đối số router . Thì thực ra tí nữa mình sẽ đẩy cái objecr route của package route express.Mục đích viết thế cho code nó sáng thôi

// server/routes/index.js
const user = require('./user')
const article = require('./article')
module.exports = (router) => {
    user(router)
    article(router)
}
// server/app.js
require dependencies 
const express = require("express")
const routes = require('./routes/')
const mongoose = require('mongoose')
const cors = require('cors')
const bodyParser = require('body-parser')
const helmet = require('helmet')
const cloudinary = require('cloudinary')
const app = express()
const router = express.Router()
// đoạn củ chuối này là connect đến database thôi
const url = process.env.MONGODB_URI || "mongodb://localhost:27017/medium"
/** configure cloudinary */
cloudinary.config({
    cloud_name: 'YOUR_CLOUDINARY_NAME_HERE',
    api_key: 'YOUR_CLOUDINARY_API_KEY_HERE',
    api_secret: 'YOUR_CLOUDINARY_API_SECRET_HERE'
})
/** connect to MongoDB datastore */
try {
    mongoose.connect(url, {
        //useMongoClient: true
    })    
} catch (error) {
    }
let port = 5000 || process.env.PORT
/** set up routes {API Endpoints} */
// đẩy object route vào này :v
routes(router)
/** set up middlewares */
app.use(cors())
app.use(bodyParser.json())
app.use(helmet())
//app.use('/static',express.static(path.join(__dirname,'static')))
app.use('/api', router)
/** start server */
app.listen(port, () => {
    console.log(`Server started at port: ${port}`);
});

Đến đoạn này đã xong phần backend . Ai muốn test mình có thể gợi ý sử dụng trình duyệt để get api .vd như: http:localhost::5000/api/articles. hoặc bạn có thể sử dụng postman thì nó sẽ trả về như thế này thì đã ok rồi : )))) alt text Với những gì mình hiểu thì web mình đang xây dựng thì backend h nó chỉ có nhận request và trả về api, Còn thằng front end nó sẽ vipro hơn trước là sẽ đảm nhiệm việc fetch api , login, authcation, .... Và Mình sẽ chuyển sang phần code front end ngay bây giờ @@

Phần backend coi như xong, đến front end. Ở đây ta sẽ sử dụng reactjs để ren html.Phần front cũng sẽ phải view ăn khớp với các chức năng của backend nên sẽ có các list view như sau

  • View articles
  • Write article
  • View article
  • Social sign in
  • Clap article
  • Follow user
  • View user

Ở đây bạn nào chưa học react nên vào link học react đi đã            </div>
            
            <div class=

0