最近比较闲,学了一些杂七杂八的技术,但不知道怎么用,想的是作一个简单的博客项目来练手,不知道能不能坚持下去,如今把项目框架搭建好了javascript
项目技术选择css
后端:node+express+mongoose 前端:vue2+vue-router+vue-resource+vuex
mongodbhtml
启动:要使用MongoDB,须要指定一个文件夹让它存放数据,我在D:\MongoDB\ data下创建了一个名为db的文件夹 win+R打开cmd,进入D:\MongoDB\bin目录,执行“mongod –dbpath=D:\MongoDB\data\db”,就会启动MongoDB 启动了MongoDB,咱们就可使用mongo(交互式shell)来管理数据库了
项目结构前端
前端的目录直接用的vue-cli生成的,我以为超级方便的,不少配置都不用本身去写。不过可能会用到jquery的一些插件,查了资料,在webpack.base.config里面加入这段,就能够随时随地用jquery了vue
plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", jquery: "jquery", "window.jQuery": "jquery", }) ],
前端的主入口为main.jsjava
import Vue from 'vue' import router from "./router/router.js" import Vuex from 'vuex' import vueResource from 'vue-resource' import App from './App' import store from "./vuex/store.js" import {sync} from 'vuex-router-sync' import "bootstrap/dist/js/bootstrap.min.js" import "bootstrap/dist/css/bootstrap.min.css" window.$router = router; window.$resource = vueResource; sync(store, router); Vue.use(vueResource); new Vue({ router, store, render: h => h(App) }).$mount("#app");
前端路由node
/** * Created by canoe on 2016/11/10. */ import Vue from 'vue' import Router from 'vue-router' import NProgress from 'nprogress' import userLogin from './../components/login.vue' import home from './../components/home.vue' import myBlog from './../components/blog.vue' const routes = [{ path: '/login', name: 'login', component: userLogin }, { path:'/home', name:'home', component:home, },{ path:'/myBlog', name:'myBlog', component:myBlog, }, { path: '*', redirect: '/login' }]; Vue.use(Router); const router = new Router({ mode: 'history', base: '/', routes: routes }); /** * 登陆状态检查 * */ router.beforeEach((to, from, next) => { NProgress.start(); if (to.matched.some(to => to.meta.requiresAuth)){ //todo 鉴权 }else{ next() } }); router.afterEach(transition => { NProgress.done(); NProgress.remove(); }); module.exports = router;
vuex 是用来管理状态的,能够用来传递组件之间的通讯
vuex 目录jquery
数据流动是单向的webpack
组件能够调用 actionsweb
Actions 是用来分发 mutations 的
只有 mutations 能够修改状态
store 是反应式的,即,状态的变化会在组件内部获得反映
后端NODE
昨天就作了个简单的登陆注册。由于我用8080端口监听的前端页面,所以我另外开了个端口6600监听的服务,知道这种方法很差,可是没有经验,暂时这样作吧。所以在调本地接口的时候,遇到了跨域问题,查了下资料,在node里加上
//设置跨域访问 app.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin", "http://localhost:8080"); res.header("Access-Control-Allow-Credentials", "true"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 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"); next(); });
这里再记录下依赖Express 实现post 4种方式提交参数吧,由于当时我也遇到个小问题,就是我post的是application/json型的数据,可是node 里面req.body 为空,后来查了资料,须要bodyParser队请求包进行解析,具体的文章http://yijiebuyi.com/blog/90c...,感受讲的清晰易懂
后端入口server.js
var express = require('express'); var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var mongoose = require('mongoose'); var config = require('./config/config.js') var router = require('./router/router.js') var app = express(); app.use(express.static('dist')); app.set('port', 6600); app.use(bodyParser.json({limit: '1mb'})); //这里指定参数使用 json 格式 app.use(bodyParser.urlencoded({ extended: true })); //设置跨域访问 app.all('*', function(req, res, next) { console.log(req.body) res.header("Access-Control-Allow-Origin", "http://localhost:8080"); res.header("Access-Control-Allow-Credentials", "true"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 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"); next(); }); app.get('/getdata', function(req, res) { res.send({id:req.params.id, name: req.params.password}); }); app.get('/', function (req, res) { res.sendFile(__dirname + "/" + "index.html"); }); var db = mongoose.connect(config.database); db = db.connection; db.on('open', function (err, doc) { console.log('数据库链接成功') }); db.on('err', function (err, doc) { console.log('数据库链接失败') }); app.use('/api', router) app.listen(app.get('port'), function (req,res) { console.log('Server up: http://localhost:' + app.get('port')+new Date().getTime()); });
把项目框架搭建好了以后,第一步是作登陆模块。
界面设计
图片描述
前端实现
<template> <div class="login-wrap"> <div class="blog-desc"> <h1>蚂蚁窝</h1> <h4>记录一些感想而已</h4> </div> <div class="login-title"> <span> <a class="login-text" href='#' @click='type="login"'>登陆</a> <b>·</b> <a class="login-text" href='#' @click='type="reg"'>注册</a> </span> </div> <div class="login-box" v-if='type=="login"'> <form class="login-form" > <div class="form-group"> <div class="input-group"> <div class="input-group-addon"><i class="fa fa-user"></i></div> <input class="form-control" type="text" name='username' placeholder="输入用户名" v-model='login_params.username'> </div> </div> <div class="form-group"> <div class="input-group"> <div class="input-group-addon"><i class="fa fa-unlock-alt"></i></div> <input class="form-control" type="password" placeholder="输入密码" name="password" v-model='login_params.password'> </div> </div> <div class="tool-box"> <div class="checkbox remember"> <label> <input type="checkbox"> 记住我 </label> </div> <a href='#' class='forget-pwd'>忘记密码</a> </div> <div class="login-tip" v-text="err_login"></div> <button type="button" class="btn btn-lg btn-block" @click='login()'>登陆</button> </form> </div> <!--注册--> <div class="reg-box" v-if='type=="reg"'> <form class="reg-form" name='reg-form'> <div class="form-group"> <div class="input-group"> <div class="input-group-addon"><i class="fa fa-user"></i></div> <input class="form-control" name="username" type="text" placeholder="输入用户名" v-model='reg_params.username'> </div> </div> <div class="form-group"> <div class="input-group"> <div class="input-group-addon"><i class="fa fa-unlock-alt"></i></div> <input class="form-control" name="password" type="password" placeholder="输入密码" v-model='reg_params.password'> </div> </div> <div class="form-group"> <div class="input-group"> <div class="input-group-addon"><i class="fa fa-unlock-alt"></i></div> <input class="form-control" name="ensure_pwd" type="password" placeholder="确认密码" v-model='reg_params.ensure_pwd'> </div> </div> <div class="form-group"> <label class="radio-inline"> <input type="radio" name="gender" value="1" v-model='reg_params.gender'> 男 </label> <label class="radio-inline"> <input type="radio" name="gender" value="0" v-model='reg_params.gender'> 女 </label> </div> <button type="button" class="btn btn-lg btn-block" @click='userReg()'>注册</button> </form> </div> </template> <script type="text/javascript"> import {appLogin } from './../../vuex/action.js' export default{ data(){ return{ type:'login', err_login:'', login_params:{ }, reg_params:{} } }, methods:{ login(){ var _this=this; var login_params=_this.login_params; if(login_params.username==''){ _this.err_login='用户名不能为空' }else if(login_params.password==''){ _this.err_login='密码不能为空' }else{ _this.$store.dispatch('userLogin',_this.login_params).then(res=>{ //权限信息 localStorage.setItem('authorization',JSON.stringify({ token: res.token, time: new Date().getTime() })); _this.$router.replace({path:"article"}) },res=>{ _this.err_login='用户名或密码错误' }); } }, userReg(){ if(this.reg_params.ensure_pwd==this.reg_params.password){ this.$store.dispatch('userReg',this.reg_params); }else{ } } } } </script> <style lang="stylus"> @import "./user.styl"; </style>
action.js,主要是一些行为去触发mutations,而后mutations中去修改状态
import * as types from "./mutation_types.js"; import * as API from "../api/api.js"; import Vue from 'vue' export const setLoginState=({commit},state)=>{ commit(types.SET_LOGIN_STATUS, state); }; export const userLogin = ({ commit }, params) => { return new Promise((resolve, reject)=> { API.login(params).then(res=> { var data=res.data; Vue.http.headers.common['authorization'] = data.token; commit(types.SET_LOGIN_STATUS, true); commit(types.SET_USER_INFO, {userName : params.username}); resolve(data) }, res=> { reject(res.data); }) }) };
vuex module 中的 longin.js,
import { SET_LOGIN_STATUS,SET_USER_INFO } from './../mutation_types.js' const state = { isLogin: false, userInfo: {}, }; const mutations = { [SET_LOGIN_STATUS](state, status) { state.isLogin = !!status; }, [SET_USER_INFO](state,data){ state.userInfo = data; } }; export default { state, mutations }
api 设计,用的vue-resource nodejs 端提供restful接口
import Vue from "vue"; import VueResource from "vue-resource" Vue.use(VueResource); // HTTP相关 Vue.http.options.crossOrigin = true; Vue.http.options.credentials = true; Vue.http.interceptors.push((req, next) => { next((res) => { return res }); }); const api = { user : `http://localhost:6600/api/user`, article:'http://localhost:6600/api/article', comment:'http://localhost:6600/api/comment', favorite:'http://localhost:6600/api/favorite', }; const userResource = Vue.resource(api.user + '{/id}'); const articleResource = Vue.resource(api.article + '{/id}'); const commentResource = Vue.resource(api.comment + '{/id}'); const favoriteResource = Vue.resource(api.favorite + '{/id}'); export const login = function (params) { return userResource.save({id : 'userLogin'}, params) }; export const userReg = function (params) { return userResource.save({id : 'userReg'}, params) }; export const getArticleList = function (params) { return articleResource.get(params) }; export const createArticle=function(params){ return articleResource.save(params) }; export const getArticleDetail=function(articleId){ return articleResource.get({id:articleId}) }; export const commentArticle=function(params){ return commentResource.save({id:params.id},params) }; export const favoriteArticle=function(params){ return favoriteResource.save({id:params.id},params) };
nodejs 端 model 设计
var mongoose=require('mongoose'); var md5 = require("md5"); var Schema=mongoose.Schema;//建立模型 var userSchema=new Schema({ userName:{type:String, require:true}, passWord:{type:String, require:true}, ensure_pwd:{type:String, require:true}, gender:Boolean,//1 为男,0为女 created:{type:Date} }); var articleSchema=new Schema({ title:{type:String, require:true}, content: {type:String, require:true}, author: {type:Schema.Types.ObjectId, ref:'User'}, //category: {type:Schema.Types.ObjectId, ref:'Category'}, created: {type:Date}, hasRead:{type:Number}, slug: {type:String, required: true }, published: {type:Boolean, default: false }, meta: {type:Schema.Types.Mixed,favorites: 0 }, comments: [Schema.Types.Mixed ] }); var CategorySchema = new Schema({ name: {type:String, require:true}, slug: {type:String, require:true}, created: {type:Date} }); //MD5密码和原密码匹配 userSchema.methods.verifyPassword= function(password){ var isMatch= md5(password)=== this.password; //console.log('UserSchema.methods.verifyPassword: ', password, this.password, isMatch); return isMatch; }; var Models={ User:mongoose.model('User',userSchema), Article:mongoose.model('Article',articleSchema), Category:mongoose.model('Category',CategorySchema), } module.exports = Models;