Skip to content

👍 A CRUD-like Blog SPA Demo, with Dva.js, Roadhog.js...

Notifications You must be signed in to change notification settings

Amecy/dva-blog-crud-demo

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

My Blog App

OverView

overview1

项目原要求

Homework 12 Blog

如何启动项目?

$ git clone https://github.com/HeskeyBaozi/dva-blog-crud-demo.git
  • 请确保你的node.js版本在6.5以上, 否则可能会报错
  • 默认的npm包地址使用国内淘宝镜像,可以在根目录下.npmrc内修改镜像源
$ npm install

在这个项目根目录下

$ npm install  // 安装服务端依赖
$ npm run db  // 开启mongoDB 默认路径为./data
$ npm start

此时服务器会在5858端口开启

  • 在保证后端服务器开启情况下
$ npm start
  • 其中代理设置如下 (此设置将所有/api请求代理到后端API, 这里后端监听端口为5858)
{
  "proxy": {
        "/api": {
            "target": "http://localhost:5858/api/v1",
            "changeOrigin": true,
            "pathRewrite": {
                "^/api": "/api"
            }
        }
    }
}
  • 终端在正常打开后如下显示,并且会自动打开浏览器标签访问 http://localhost:8000

compile

  • 内置一个管理员

username: admin password: admin

  • 内置两个普通用户

username: hezhiyu password: hezhiyu

username: normal password: normal

数据设计

登录流

flow

models/app负责全局的登录状态管理。

在路由控制中,使用react-routeronEnter钩子保证在进入需要授权的页面中登录状态是保持的。

function requireAuth(nextState, replace, callback) {
    app._store.dispatch({
        type: 'app/enterAuth',
        payload: {},
        onComplete: callback // enter the component
    });
}

function* enterAuth({payload, onComplete}, {put, take}) {
    yield [put({type: 'checkToken'}), put({type: 'queryUser'})];
    yield [take('app/hasToken'), take('app/queryUserSuccess')]; // promise the logged state
    onComplete();
}

总体思想

所有的组件都尽量是stateless, 所有的状态connect组件一般都是路由组件。所有的分发dispatch都交给了路由组件来完成。

这样我可以保证我可以复用一些Dumb组件,比如PostsListBody这个组件,既可以在文章列表页面使用,也可以在用户页面查看自己的文章列表使用。

数据的获取

有两种方式。

一种是dva.js官方推荐的, 使用在models/posts

"订阅"数据源。这封装了react-redux-router增强的history

这样可以监听路由的变化,比如说下面在进入/posts时,会发起一个获取文章列表的action.

app.model({
    subscriptions: {
        setup: function ({history, dispatch}) {
            history.listen(location => {
                if (pathToRegExp('/posts').exec(location.pathname)) {
                    dispatch({
                        type: 'fetchPostsList',
                        payload: {pageInfo: {limit: 5, page: 1}}
                    });
                }
            });
        }
    }
});

还有一种是进入一些页面时,要保证一些数据已经在state中了。这时我还是使用了react-routeronEnter钩子。

比如说在进入文章详细页面时,需要知道文章的基本元信息,标题作者等等。等到元信息加载完,再进入页面。

语法层面上上,多亏了有saga的各种effects创建器。可以很爽地写出各种异步代码

function requirePostPrepared(nextState, replace, callback) {
    app._store.dispatch({
        type: 'post_detail/initializePostDetail',
        payload: {post_id: nextState.params.post_id},
        onComplete: callback
    });
}

function* initializePostDetail({payload, onComplete}, {put, call}) {
    yield put({type: 'clear'});
    const {post_id} = payload;
    const {data} = yield call(fetchPostInfo, {post_id});
    if (data) {
        yield put({
            type: 'saveInitialPostDetailInfo',
            payload: {postInfo: data}
        });
        onComplete(); // enter the component
        
        // then fetch the data
        yield [
            put({type: 'fetchPostContent'}),
            put({type: 'fetchPostComments'})
        ];
    }
}

文章列表

使用normalizr将获取到的文章数组扁平化,方便后续修改visible可见状态等。

原理如图:

normalizr

这样在获取数据源展示数据时,即可使用一条语句

const dataSource = postsList.map(post_id => postsById[post_id]).filter(post => post);

About

👍 A CRUD-like Blog SPA Demo, with Dva.js, Roadhog.js...

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 94.0%
  • CSS 5.7%
  • HTML 0.3%