第八章-通行凭证-权限管理

63 阅读2分钟

8-1登录一部分获取token

const postAndCommit=async(url:string,mutation:){

}

login({commit},payload){

postAndCommit('/user/login','login'){

}

const onFormSubmit = (result: boolean) => {
      if (result) {
        const payload = {
          email: emailVal.value,
          password: passwordVal.value
        }
        store.dispatch('loginAndFetch', payload).then(data => {
          createMessage('登录成功 2秒后跳转首页', 'success', 2000)
          setTimeout(() => {
            router.push('/')
          }, 2000)
        }).catch(e => {
          console.log(e)
        })
      }
    }

8-2jwt的运行机制

HTTP 200OKname:'biking'

POST/login->使用JWT算法生成对应的token

8-3登录第二部分axios设置通用header

访问loginurl获取token

设置header设置通用的authorzation

访问userprofileapi获取用户信息并且等更新store返回到页面上来

设置header

login(store,rawData){

const{token}=rawData.data

state.token=token

axios.defaults.headers.common.Authorization=Baearer${token}

fetchCurrentUser({commit}){

getAndCommit(`/uswr/current,fetchCurrentUser');

fetchCurrentUser(state,rawData){

state.user=

loginAndFetch({dispatch},loginData){

return dispatch('login',loginData)then(()=>dispatch('fetchCurrentUser')})

持久化登录状态

token不支持跨域

login(state,rawData){

const token=computed(()=>store.state.token)

import {GlobalDataProps)

onMounted (()=>{

if(!current User。value.isLogin && token.value){

axios.defualts.

8-5通用错误处理

state{

error:{

setErrOR(STATE,E:gLOBALeRRORpRops){}

axios.interceptors.response.use(config => {
  setTimeout(() => {
    store.commit('setLoading', false)
  }, 1000)
  return config
}, e => {
  const { error } = e.response.data
  store.commit('setError', { status: true, message: error })
  store.commit('setLoading', false)
  return Promise.reject(e.response.data)
})

8-6创建message组件

<template>
  <teleport to="#message">
    <div class="alert message-info fixed-top w-50 mx-auto d-flex justify-content-between mt-2"
      :class="classObject" v-if="isVisible">
      <span>{{message}}</span>
      <button type="button" class="btn-close" aria-label="Close"></button>
    </div>
  </teleport>
</template>


<script lang="ts">
import { defineComponent, PropType, ref } from 'vue'
import useDOMCreate from '../hooks/useDOMCreate'
export type MessageType = 'success' | 'error' | 'default'
export default defineComponent({
  props: {
    message: String,
    type: {
      type: String as PropType<MessageType>,
      default: 'default'
    }
  },
  emits: ['close-message'],
  setup(props, context) {
    useDOMCreate('message')
    const isVisible = ref(true)
    const classObject = {
      'alert-success': props.type === 'success',
      'alert-danger': props.type === 'error',
      'alert-primary': props.type === 'default'
    }
    const hide = () => {
      isVisible.value = false
      context.emit('close-message', true)
    }
    return {
      classObject,
      isVisible,
      hide
    }
  }
})


</script>

8-7message组件改称为函数调用形式

import { h, render } from 'vue'
import Message from './Message.vue'
export type MessageType = 'success' | 'error' | 'default'


const createMessage = (message: string, type: MessageType, timeout?: number) => {
  const messageVnode = h(Message, {
    message,
    type
  })
  const mountNode = document.createElement('div')
  document.body.appendChild(mountNode)
  render(messageVnode, mountNode)
  const destory = () => {
    render(null, mountNode)
    document.body.removeChild(mountNode)
  }
  if (timeout) {
    setTimeout(() => {
      destory()
    }, timeout)
  }
}


export default createMessage

watch(()=>error.value.statsuc.()=>{

const{status,message}=error.value

if(status && message){

createMessage(messgae,'error')}

8-9创建vnode以及使用renderfunction

return()=>{

return j('div');}}

Vnode.vue

setup(props){

const count=ref(1)

return()=>[h('1'

vue add babel

8-11注册页面的编写

<template>
  <div class="signup-page mx-auto p-3 w-330">
    <h5 class="my-4 text-center">注册者也账户</h5>
    <validate-form @form-submit="onFormSubmit">
      <div class="mb-3">
        <label class="form-label">邮箱地址</label>
        <validate-input
          :rules="emailRules" v-model="formData.email"
          placeholder="请输入邮箱地址"
          type="text"
        />
      </div>
      <div class="mb-3">
        <label class="form-label">昵称</label>
        <validate-input
          :rules="nameRules" v-model="formData.nickName"
          placeholder="请输入昵称"
          type="text"
        />
      </div>
      <div class="mb-3">
        <label class="form-label">密码</label>
        <validate-input
          type="password"
          placeholder="请输入密码"
          :rules="passwordRules"
          v-model="formData.password"
        />
      </div>
      <div class="mb-3">
        <label class="form-label">重复密码</label>
        <validate-input
          type="password"
          placeholder="请再次密码"
          :rules="repeatPasswordRules"
          v-model="formData.repeatPassword"
        />
      </div>
      <template #submit>
        <button type="submit" class="btn btn-primary btn-block btn-large">注册新用户</button>
      </template>
    </validate-form>
  </div>
</template>


<script lang="ts">
import { defineComponent, reactive } from 'vue'
import { useRouter } from 'vue-router'
import axios from 'axios'
import ValidateInput, { RulesProp } from '../components/ValidateInput.vue'
import ValidateForm from '../components/ValidateForm.vue'
import createMessage from '../components/createMessage'


export default defineComponent({
  name: 'Signup',
  components: {
    ValidateInput,
    ValidateForm
  },
  setup() {
    const formData = reactive({
      email: '',
      nickName: '',
      password: '',
      repeatPassword: ''
    })
    const router = useRouter()
    const emailRules: RulesProp = [
      { type: 'required', message: '电子邮箱地址不能为空' },
      { type: 'email', message: '请输入正确的电子邮箱格式' }
    ]
    const nameRules: RulesProp = [
      { type: 'required', message: '昵称不能为空' }
    ]
    const passwordRules: RulesProp = [
      { type: 'required', message: '密码不能为空' }
    ]
    const repeatPasswordRules: RulesProp = [
      { type: 'required', message: '重复密码不能为空' },
      {
        type: 'custom',
        validator: () => {
          return formData.password === formData.repeatPassword
        },
        message: '密码不相同'
      }
    ]
    const onFormSubmit = (result: boolean) => {
      if (result) {
        const payload = {
          email: formData.email,
          password: formData.password,
          nickName: formData.nickName
        }
        axios.post('/users/', payload).then(data => {
          createMessage('注册成功 正在跳转登录页面', 'success', 2000)
          setTimeout(() => {
            router.push('/login')
          }, 2000)
        }).catch(e => {
          console.log(e)
        })
      }
    }
    return {
      emailRules,
      nameRules,
      passwordRules,
      repeatPasswordRules,
      onFormSubmit,
      formData
    }
  }
})
</script>


<style>
.w-330 {
  max-width: 330px;
}
</style>

第8章 通行凭证 - 权限管理

#8-1 登录第一部分 获取token

代码提交详情:git.imooc.com/coding-449/…

#8-2 JWT 的运行机制

JWT 以及 Session 的运行原理图

JWT 的官方网站,可以去试试看:jwt.io/

#8-3 登录第二部分 axios 设置通用 header

Axios Default Header 设置的文档:axios-http.com/docs/config…

// 示例代码
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

代码提交详情:git.imooc.com/coding-449/…

#8-4 登录第三部分 持久化登录状态

LocalStorage 文档地址:developer.mozilla.org/en-US/docs/…

注意这里为什么不选择 Cookie?

代码提交详情:git.imooc.com/coding-449/…

#8-5 通用错误处理

Axios 拦截器的文档地址:axios-http.com/docs/interc…

示例代码:

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
  }, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
  });

代码提交详情: git.imooc.com/coding-449/…

#8-6 创建 Message 组件 8-7 Message 组件改进为函数调用形式

代码提交详情:git.imooc.com/coding-449/…

关于 Vue createApp 实例相关的文档地址:v3.cn.vuejs.org/api/applica…

#8-8 了解 Vnode 以及 vue 的简单工作原理

文档地址:vuejs.org/guide/extra…

Virtual DOM:一种虚拟的,保存在内存中的数据结构,用来代表 UI 的表现,和真实 DOM 节点保持同步。Virtual DOM是由一系列的 Vnode 组成的。

// 模拟一个简单的 Vnode
const vnode = {
  type: 'div',
  props: {
    id: 'hello'
  },
  children: [
    /* more vnodes */
  ]
}

#Render Pipeline

  • Compile, Vue 组件的 Template 会被编译成 render function,一个可以返回 Virtual DOM 树的函数。
  • Mount,执行 render function,遍历虚拟DOM 树,生成真正的 DOM 节点。
  • Patch,当组件中任何响应式对象(依赖)发生变化的时候,执行更新操作 。生成新的虚拟节点树,Vue 内部会遍历新的虚拟节点树,和旧的树做对比,然后执行必要的更新。

虚拟DOM 的优点

  • 可以使用一种更方便的方式,供开发者操控 UI 的状态和结构,不必和真实的DOM 节点打交道。
  • 更新效率更高,计算需要的最小化操作,并完成更新。

#看一下 Render Functions

// 在 main.ts 中
console.log(App)
// 返回
Object
  components: {HelloWorld: {…}}
  name: "App"
  render: ƒ render(_ctx, _cache, $props, $setup, $data, $options)
  setup: ƒ ()
// 原始的 template
<template>
  <HelloWorld msg="axyz"/>
  {{ hello }}
</template>

// template 会被转换成这样的 function
  const _component_HelloWorld = _resolveComponent("HelloWorld")!

  return (_openBlock(), _createElementBlock(_Fragment, null, [
    _createVNode(_component_HelloWorld, { msg: "axyz" }),
    _createTextVNode(" " + _toDisplayString(_ctx.hello), 1 /* TEXT */)
  ], 64 /* STABLE_FRAGMENT */))
  • Template 比 render function 更接近 html,更好懂,更容易修改。
  • Template 更容易做静态优化,Vue 的 compiler 在编译过程中可以做很多自动的性能优化。

在实践中,templates适应大多数的情况,但是在少数情况下,还是需要学习使用 render function。因为它本身是 javascript 语法,要更灵活多变。Vue 提供对应的 API 可以不使用 templates,而是直接使用 render function。

#8-9 创建 Vnode 以及使用 render function

#创建 Vnode

h 和 createVnode 都可以创建 vnode,h 是 hyperscript 的缩写,意思就是 “JavaScript that produces HTML (hypertext markup language)”,很多 virtualDOM 的实现都使用这个函数名称。还有一个函数称之为 createVnode,更形象,两个函数的用法几乎是一样的。

import { h, createVnode } from 'vue'

const vnode = h(
  'div', // type
  { id: 'foo', class: 'bar' }, // props
  [
    /* children */
  ]
)

#声明 Render Function

当使用 composition API 的时候,在 setup 当中直接返回一个对象,代表着给模版使用的数据,当要使用 render function 的时候,可以直接返回一个函数。

setup() {
  const message = ref(1)
  // 使用 template
  return {
    message
  }
  // 使用 render function
  return () => {
    return h('div')
  }
}

使用 JSX

JSX 是一种类似XML 的语法,如果大家使用过 React 对它应该特别熟悉。它就是 h 函数的一种语法糖。可以将这种类似 HTML 的语法转换成 h 函数的写法。

// 创建 vnode
const vnode = <div>hello</div>
// 使用变量
const vnode = <div id={dynamicId}>hello, {userName}</div>

添加 JSX 支持

vue add babel