最近比較閒,學了一些雜七雜八的技術,但不知道怎麼用,想的是作一個簡單的博客項目來練手,不知道能不能堅持下去,如今把項目框架搭建好了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;