美文网首页
Vue.js实现多人共享博客

Vue.js实现多人共享博客

作者: kzc爱吃梨 | 来源:发表于2019-11-26 14:12 被阅读0次

    1. 项目初始化

    老套路了..使用vue-cli 创建项目骨架,创建路由。
    在 vue 项目中使用 scoped&less

    npm install --save-dev less-loader less
    
    <style scoped lang="less" src="./template.less"></style>
    

    markdown转换

    npm install --save marked
    
    //在js中引入,然后在计算属性中使用
    import marked from 'mark'
    
    computed: {
      markdown() {
        return marked(this.rawContent)
      }
    }
    //最后在vue在写入,使用v-html
     <section class="article" v-html="markdown"></section>
    

    ElementUI 的使用

    ElementUI 的有很详细的安装使用文档

    主要步骤:

    1. 安装
    npm i element-ui -S
    
    1. 在 main.js 中写入以下内容:
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    Vue.use(ElementUI);
    

    数据请求接口封装
    前期还对 axios 底层请求做了进一步的定制和封装,其中一些技巧很值得学习。使得使用更加的方便。

    1. 先把 axios 请求封装成了输入参数更简洁明了、报错信息更「人性化」的 Promise 对象。
    //  /helpers/request.js
    axios.defaults.headers.post[‘Content-Type‘] = ‘application/x-www-form-urlencoded‘
    axios.defaults.baseURL = ‘http://blog-server.hunger-valley.com‘
    axios.defaults.withCredentials = true
    
    export default function request(url, type = ‘GET‘, data = {}) {
      return new Promise((resolve, reject) => {
        let option = {
          url,
          method: type
        }
        if(type.toLowerCase() === ‘get‘) {
          option.params = data
        }else {
          option.data = data
        }
        axios(option).then(res => {
          console.log(res.data)
          if(res.data.status === ‘ok‘) {
            resolve(res.data)
          }else{
            Message.error(res.data.msg)
            reject(res.data)
          }
        }).catch(err => {
          Message.error(‘网络异常‘)
          reject({ msg: ‘网络异常‘ })
        })
      })
    }
    
    1. 再把获取数据的 API 进行封装,使其更易用:
    import request from '@/helpers/request'
    
    const URL = {
      REGISTER: '/auth/register',
      LOGIN: '/auth/login',
      LOGOUT: '/auth/logout',
      GET_INFO: '/auth'
    }
    
    export default {
      register({username, password}) {
        return request(URL.REGISTER, 'POST', { username, password })
      },
    
      login({username, password}) {
        return request(URL.LOGIN, 'POST', { username, password })
      },
    
      logout() {
        return request(URL.LOGOUT)
      },
    
      getInfo() {
        return request(URL.GET_INFO)
      }
    }
    

    这样子处理的话,登录请求就可以不使用 axios(‘http://blog-server.hunger-valley.com/auth/login‘,‘POST‘,{username,password}) 那么繁琐了,直接 auth.login({username,password}) 就完事了~

    可以查看此 commit


    grid

    使用 grid 进行布局。

    关于 grid 布局之前有了解过,grid 通过在页面上划分 columns 和 rows ,然后把内容分别放进不同区域来建立布局,也写过 demo,但真正在项目中使用还是第一次。关于 grid 的教程可以参考这里

    如在项目中的使用:

    #app {
      display: grid;
      // 分成三列,左右列宽度分别是页面的12%,中间内容宽度自适应
      grid-template-columns: 12% auto 12%;
      // 分成三行,上下行高度自适应,中间内容占满剩余宽度
      grid-template-rows: auto 1fr auto;
      // 划分区域
      grid-template-areas: "header header header"
                           ".      main   .     "
                           "footer footer footer";
      #header{
        grid-area: header;
        padding-left: 12%;
        padding-right: 12%;
      }
      #main{
        grid-area: main;
      }
      #footer{
        grid-area: footer;
        padding-left: 12%;
        padding-right: 12%;
      }
    }
    

    可以查看此 commit


    async/await

    在完成项目的过程中接触到了async/await

    async/await是异步编程的一种解决方案。

    async 声明一个函数为异步函数,这个函数返回的是一个 Promise 对象;

    await用于等待一个 async 函数的返回值(注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。)

    以项目中用户注册为例:

    // 声明 register 为一个异步函数,他会返回一个 Promise 对象
    async register({commit},{username,password}){
        
        // 用户注册成功后后会返回的一个 Promise 对象,其中包含了用户的信息,let res 就是异步 auth.register 获取的结果
        let res = await auth.register({username,password})
        commit(‘setUser‘,{user:res.data})
        commit(‘setLogin‘,{isLogin:true})
        
        // 把 res.data 返回出去,使用 register() 后就可以用 then 来处理这个结果
        return res.data
    },
    

    对于 async/await,我参考了 边城 在 segmentfault 中的这边文章


    Vuex

    如何在项目中使用 vuex 管理状态?(以登录为例)

    1. 创建store,定义 state/getters/mutations/actions
      • 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
      • 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

    在这里针对不同的内容状态管理划分到不同的文件,保持可读性:

    store
    |
    │  index.js // 引入子模块
    │  
    └─modules
            auth.js   // 负责用户注册、登录状态
            blog.js   // 负责用户获取、发布、修改博客等
    

    把负责用户注册登录的 state 写在了 auth.js:

    // auth.js
    import auth from ‘@/api/auth‘
    
    const state = {
        // 先定义一个默认的用户状态
        user:null,
    
        //登录状态
        isLogin:false
    }
    const getters = {
        // 获取 state 数据
        user:state => state.user,
        isLogin:state => state.isLogin
    }
    const mutations = {
        // 更新用户数据
        setUser(state,payload){
            state.user = payload.user
        },
    
        // 更新用户登录状态
        setLogin(state,payload){
            state.isLogin = payload.isLogin
        }
    }
    const actions = {
        ...
        // 检测用户是否登录
        async checkLogin({commit,state}){
    
            // 先从本地store的state去看用户是否登录,如果登录了 就返回true
            if(state.isLogin) return true
            let res = await auth.getInfo()
            commit(‘setLogin‘,{isLogin:res.isLogin})
    
            // 如果本地没有这个状态,就发ajax请求去服务器,服务器会返回一个isLogin的响应,根据这个值来确定是否登录
            if(!res.isLogin) return false
            commit(‘setUser‘,{user:res.data})
    
            // 最后的 return true 是为了在实例中then拿到这个true,方便做下一步处理
            return true
        },
        // 用户登录  {commit} 是默认参数,相当于 context.commit,使用了 ES6 的参数结构
        login({commit},{username,password}){
    
            // 调用底层接口,返回的是一个 Promise 对象
            return auth.login({username,password})
                .then(res => {
    
            // 把通过 axios 获取回来的用户数据提交 mutation,更新到 state: commit -> setUser -> state
                    commit(‘setUser‘,{user:res.data})
                    commit(‘setLogin‘,{isLogin:true})
                })
        },
        ...
    }
    export default {
        state,
        getters,
        mutations,
        actions
    }
    

    2.在 /store/index.js 把模块引入进来:

    // index.js
    import Vue from ‘vue‘
    import Vuex from ‘vuex‘
    import auth from ‘./modules/auth‘
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        modules:{
            auth,
        }
    })
    
    1. 在相关组件中映射属性,commit 更新
      在 Login.vue 中,我们要做的事情就是:点击按钮后,调用 auth.js 中的 login() 方法,完成登录,更新 state 中的数据,并给需要的组件更新状态(如 header.vue)

    首先映射 login 方法到此组件,这样此组件就可以通过 this.login 来调用这个 auth.js 中的方法了:

    //Login.vue
    import {mapActions} from ‘vuex‘
    
    
    export default {
      name:‘Login‘,
      methods:{
        ...mapActions([
            ‘login‘
          ]),
      },
    }
    

    接着设置点击事件,点击按钮会执行 onLogin,调用 this.login ,发送 axios 请求 auth.login({username,password}),成功注册后 commit mutation,更新 state 数据,跳转到首页:

    <el-button size="medium" @click="onLogin">立即登录</el-button>
    
    //Login.vue
    methods:{
        ...mapActions([
            ‘login‘
          ]),
          onLogin(){
            this.login({username:this.username,password:this.password})
              .then(()=>{
                console.log(`${this.username},${this.password}`)
                this.$router.push({path:‘/‘})
              })
          }
      },
    
    1. 在相关组件中关联 state 数据,实现状态切换
    • 在 Header 中,登录和未登录,他的样式在两种状态下是不一样的:

    • 未登录的时候,header 会显示提示登录和注册的按钮;登录之后,header 会显示用户头像及其他操作选项。

    • 而这两种状态的切换,就要依靠我们的 state 了,首先引入映射 {mapGetters,mapActions},在页面还未渲染的时候检查 state 中用户登录状态,用户已登录,则返回 isLogin = true ,获取用户信息,渲染到页面上;用户未登录,则返回 isLogin = false。

    <header :class="{login:isLogin,‘no-login‘:!isLogin}">
    
    // Header.vue
    import {mapGetters,mapActions} from ‘vuex‘
    
    export default {
      name:‘Header‘,
      // 把 store 中 getter 属性映射到此组件
      computed:{
        ...mapGetters([
          ‘isLogin‘,
          ‘user‘
        ])
      },
      //在页面没有渲染之前检查用户是否登录
      created(){
        this.checkLogin()
      },
      methods:{
        // 把 auth.js 中的 checkLogin 方法映射到此组件
        ...mapActions([
          ‘checkLogin‘
        ]),
      },
    

    这就是 vuex 在在项目中管理登录状态了。


    完善路由

    • 添加路由元信息

    • 项目中有一些页面,比如添加文章、编辑文章等等,都需要先确认用户是否登录才能操作,否则将会自动跳转到登录页。

    • 路由元信息做的就是这样一件事情,我们给某段路由添加一个 meta 字段 meta:{ requiresAuth:true },这段路由路由匹配到的所有路由记录会暴露为$route对象 (还有在导航守卫中的路由对象) 的$route.matched数组。通过遍历 $route.matched 来检查路由记录中的 meta 字段,对访问的路径做一个状态检查,从而确定是否允许访问。

    const router = new Router({
      routes: [
        ...
        {
          path: ‘/Create‘,
          component: () =>import (‘@/pages/Create/Create‘),
          // 路由添加 meta 字段
          meta:{ requiresAuth:true }
        },
        ...
      ]
    })
    router.beforeEach((to, from, next) => {
      if (to.matched.some(record => record.meta.requiresAuth)) {
        // 如果 store.dispatch(‘checkLogin‘) 返回的结果 isLogin 为 false,则说明用户没有登录,就会跳转到 /login
        store.dispatch(‘checkLogin‘).then(isLogin=>{
          if (!isLogin) {
            next({
              path: ‘/login‘,
              query: { redirect: to.fullPath }
            })
          } else {
            next()
          }
        })
      } else {
        next() // 确保一定要调用 next()
      }
    })
    

    懒加载

    小技巧 - 按需加载,节约性能:

    按需加载的适用场景,比如说「访问某个路由的时候再去加载对应的组件」,用户不一定会访问所有的路由,所以没必要把所有路由对应的组件都先在开始的加载完;更典型的例子是「某些用户他们的权限只能访问某些页面」,所以没必要把他们没权限访问的页面的代码也加载。

    // before
    import Index from ‘@/pages/Index/Index‘
    const router = new Router({
      routes: [
        {
          path: ‘/‘,
          component: Index
        },
        ...
      ]
    })
    
    // after
    const router = new Router({
      routes: [
        {
          path: ‘/‘,
          component: () =>import (‘@/pages/Index/Index‘)
        },
        ...
      ]
    })
    

    相关文章

      网友评论

          本文标题:Vue.js实现多人共享博客

          本文链接:https://www.haomeiwen.com/subject/xqlewctx.html