Skip to content

Latest commit

 

History

History
309 lines (252 loc) · 10.5 KB

开发指南.md

File metadata and controls

309 lines (252 loc) · 10.5 KB

开发指南

vue 博客的教程及上线指南

上线指南

首先先提下提交我们的代码上线的过程,线上服务器只需要我们最后的 server 文件以及构建后的前端文件,即根目录下的 public 文件夹下的东东。即在完成每次的开发内容后,记得在 frond-end 前端项目中,执行 npm run build 代码构建。

可以注意到根目录下的 server.js 文件,将代码 push 上去后,会自动执行 node server.js 启动 node 服务器,配置的地方见 package.json 里面的 scripts 部分,当然你也可以根据自己喜欢的方式进行定义。

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },

再看看 server.js 需要做些什么,

'use strict';
var AV = require('leanengine');

AV.init({
  appId: process.env.LEANCLOUD_APP_ID,
  appKey: process.env.LEANCLOUD_APP_KEY,
  masterKey: process.env.LEANCLOUD_APP_MASTER_KEY
});

// 如果不希望使用 masterKey 权限,可以将下面一行删除
AV.Cloud.useMasterKey();

var app = require('./server-modules/app');

// 端口一定要从环境变量 `LEANCLOUD_APP_PORT` 中获取。
// LeanEngine 运行时会分配端口并赋值到该变量。
var PORT = parseInt(process.env.LEANCLOUD_APP_PORT || 3000);
app.listen(PORT, function () {
  console.log('Node app is running, port:', PORT);

  // 注册全局未捕获异常处理器
  process.on('uncaughtException', function(err) {
    console.error("Caught exception:", err.stack);
  });
  process.on('unhandledRejection', function(reason, p) {
    console.error("Unhandled Rejection at: Promise ", p, " reason: ", reason.stack);
  });
});

其实就是一些启动 node 服务器以及连接 leancloud 的一些验证信息等各种配置,如果出现异常将错误信息打印等工作。

最后说的就是部署的工作,由于是用的免费测试应用,所以只有一个生产环境,不用管环境的问题。我们使用的部署方式使用官方提供的一套命令行工具即可完成,安装方式:(执行下面的命令即可,需要提前装好 brew)

brew install lean-cli

装好后,如果在命令行执行 lean,后可以看到使用说明,说明已经安装成功了,接着进行下一步。在项目根目录下,登录自己的 leancloud 账号,并完成当前项目与线上创建应用的绑定。如果项目是第一次绑定,请先执行 lean init 初始化。

lean login
// 输入邮箱 & 密码
// 登录成功后
lean init
// 可以看到已经创建的应用,选择即可完成绑定,已绑定可键入 lean switch 切换应用

绑定好我们的应用后,为了防止部署后出现问题,可以先执行 lean up 尝试在本地启动服务,检查应用是否启动正常。如果有报错,请先检查是否本地端口被占用,换一个即可。

如果前面一切顺利,在根目录下执行

lean deploy

即完成可对代码进行线上部署。由于是免费的测试服务器,所以会出现时不时的服务无法访问的情况,且一段时间不访问,也会自动进入休眠状态,即再次启动时需要等待服务被唤醒,时间较长。

部署完成后,访问我们线上填写的 url 即可在线看到我们的应用。

文件结构

首先观察我们的文件结构,主要设置如下

├── front-end 前端代码
    ├── build 构建脚本
    ├── config 通用配置
    ├── node_modules 
    ├── src 页面编写的代码
    ├── static
    └── test
├── node_modules
├── public
└── server-modules 后台服务调用
    └── modules 模块拆分

前端部分的文件结构直接使用 vue-cli 生成即可,根目录下的需要的模块主要是为了提供 node.js 跑服务使用,用到了如下几个:

"dependencies": {
    "body-parser": "1.12.3", // 解析body参数
    "cookie-parser": "^1.3.5", // 解析 cookie
    "express": "4.12.3", // express 服务框架
    "leanengine": "^1.0.0-beta" // leancloud 提供的 SDK,提供我们本地对云存储的操作
  }

注意确保已经安装配置好了 node.js 与 npm 等环境。

设置好我们的文件结构即可进入到后面的实战开发了。

前端部分

Vuex 数据状态管理

先说明这一部分,主要是考虑到本应用涉及到的逻辑并不复杂,所以讲基本所有的数据请求以及交互都存放在了 Vuex 的 store 中,也是直接请求我们服务端的地方,所以理清楚这一块对于整体应用也基本了解了。

import createLogger from 'vuex/dist/logger'
import actions from './actions'

// modules 模块分类
import contentList from './modules/contentList'
import article from './modules/article'
import commentsList from './modules/commentsList'
import tags from './modules/tags'
import tagContentList from './modules/tagContentList'
import user from './modules/user'

Vue.use(Vuex)

Vue.config.debug = true

 // 非生产环境下开启严格模式,用以检测是否有在 mutation 外改变 store
const debug = process.env.NODE_ENV !== 'production'

export default new Vuex.Store({
  strict: debug,
  actions,
  modules: {
    contentList,
    article,
    commentsList,
    tags,
    tagContentList,
    user
  },
  plugins: process.env.NODE_ENV !== 'production'
    ? [createLogger()]
    : []
})

可以看到我们将应用的不同部分分成了多个模块去管理,包括 首页的文章列表、详情、评论、标签、标签下文章列表、用户等, 也将我们的具体工作分成对应的模块,按模块开发。Action 内则进行异步请求处理。注意这里用到了 createLogger 插件,这是一个 Vuex 插件,能够追踪 Vuex 状态的变化,并在控制台打印,在开发模式下启动即可,便于我们开发中即时看到状态的变化过程。

具体模块内容参考源代码即可,不过多啰嗦了,可以看到每个模块中我们使用 state 存放每个模块的基本数据属性、mutations 放置对 state 的修改操作,并供 Actions 调用。

Vue router 前端路由配置

对于路由的配置,将我们应用需要的页面全部设置在这里,可以得到应用的一个大概了。

Vue.use(VueRouter)

export default new VueRouter({
    mode: 'history',
    routes: [{
        path: '/',
        name: 'Index',
        component: function(resolve) {
            require(['@/views/Index'], resolve)
        }
    }, {
        path: '/article/:id',
        name: 'article',
        component: function(resolve) {
            require(['@/views/Article'], resolve)
        }
    },  {
        path: '/tags',
        name: 'tags',
        component: function(resolve) {
            require(['@/views/Tags'], resolve)
        }
    }, {
        path: '/about',
        name: 'about',
        component: function(resolve) {
            require(['@/views/About'], resolve)
        }
    }, {
        path: '/post',
        name: 'post',
        component: function(resolve) {
            require(['@/views/Post'], resolve)
        },
        // meta: {
        //   requiresAuth: true
        // }
    }, {
        path: '/login',
        name: 'login',
        component: function(resolve) {
            require(['@/views/Login'], resolve)
        }
    }, {
      path: '/home',
      name: 'home',
      component: function (resolve) {
        require(['@/views/pages/Index'], resolve)
      }
    }],

    scrollBehavior(to, from, savedPosition) {
        // return 期望滚动到哪个的位置
        // 路由切换时滚动到顶部
        if (savedPosition) {
            return savedPosition
        } else {
            return {  y: 0 }
        }
    }
})

注意到每个路由组件的应用方式均采用了异步调用的方式,是一种路由懒加载的手段,使我们在访问到对应路由时采取加载对应的模块代码,可以用于提高首屏加载的时间。

服务端部分

通过 leanengine SDK 可以很方便的与云端存储通信,并将返回的最新数据通过 api 接口提供出来供我们前台显示。

const router = require('express').Router();

// 添加路由模块
const content = require('./modules/content');
const comments = require('./modules/comment');
const tags = require('./modules/tags');
const user = require('./modules/user');

// 路由设置
router.get('/hello', content.hello);

// Content
router.get('/contentAll', content.contentList);
router.get('/contentList/:page', content.getTenContent);
router.get('/content/backgroundImg', content.getImgUrl);
// Article
router.get('/article/:id', content.article);
router.post('/article/submitArticle', content.submitArticle);
// Comment
router.get('/comments/:articleId', comments.commentsList);
router.post('/comments/submitComment', comments.submitComment);
// Tag
router.get('/tags', tags.tags);
router.get('/tags/:tagId', tags.tagList);
// User
router.post('/login', user.login);
router.post('/logout', user.logout);

module.exports = router;

路由按照模块划分,将我们应用中需要的接口,在这里提供出来。

const AV = require('leanengine');
let Content = {};
// 获取10篇文章
Content.getTenContent = async(req, res) => {
    const page = req.params.page || 1

    const queryTenContent = (page) => {
        const query = new AV.Query('ContentList') // 创建查询实例
        query.descending('createdAt') // 创建时间->降序查询
        query.skip((page - 1)*10) // 跳过指定项
        query.limit(10) // 限制返回项数量
        return query.find();
    }

    try {
        const data = await queryTenContent(page)

        if (data) {
            let arr = []
            for (let item of data) {
                let result = {}
                result.objectId = item.get('objectId')
                result.title = item.get('title')
                result.abstract = item.get('abstract')
                result.author = item.get('author')
                result.createdAt = item.get('createdAt').Format("yyyy-MM-dd hh:mm:ss")
                arr.push(result)
            }
            let final_result = {};
            final_result.page = page;
            final_result.data = arr;
            res.send(final_result)
        } else {
            throw new Error('Can\'t find the data-Content')
        }
    } catch (err) {
        console.error(err)
    }
}
module.exports = Content;

有了云服务提供的存储支持,我们只用像上面这样编写相应的类似 controller 函数即可完成各类交互,具体方法,查阅请官方文档。