Vue-Element-UI-Vuex-Vue-router梳理前端知识点

2,243 阅读6分钟

Github来源: | 求星星 ✨ | 给个❤️关注,❤️点赞,❤️鼓励一下作者

前端工程师的职业

  • 初级工程师(对应薪资:5000-1w):初步掌握前端开发,技术栈比较局限;
  • 中级工程师(对应薪资:1w-2w):技术栈进一步丰富,对实现细节不清楚;
  • 高级工程师(对应薪资:2w-3w):清楚实现细节,不再局限某一技术栈;
  • 专家工程师(对应薪资:3w-4w):灵活运用各种技术,解决复杂的技术问题。

emitemit 和 on

<html>
  <head>
    <title>$emit 和 $on</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <button @click="boost">触发事件</button>
    </div>
    <script>
      new Vue({
        el: '#root',
        data() {
          return {
            message: 'hello vue'
          }
        },
        created() {
          this.$on('my_events', this.handleEvents)
        },
        methods: {
          handleEvents(e) {
            console.log(this.message, e)
          },
          boost() {
            this.$emit('my_events', 'my params')            
          }
        }
      })
    </script>
  </body>
</html>

directive 用法

<html>
  <head>
    <title>directive 用法</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <div v-loading="isLoading">{{data}}</div>
      <button @click="update">更新</button>
    </div>
    <script>
      Vue.directive('loading', {
        update(el, binding, vnode) {
          if (binding.value) {
            const div = document.createElement('div')
            div.innerText = '加载中...'
            div.setAttribute('id', 'loading')
            div.style.position = 'absolute'
            div.style.left = 0
            div.style.top = 0
            div.style.width = '100%'
            div.style.height = '100%'
            div.style.display = 'flex'
            div.style.justifyContent = 'center'
            div.style.alignItems = 'center'
            div.style.color = 'white'
            div.style.background = 'rgba(0, 0, 0, .7)'
            document.body.append(div)
          } else {
            document.body.removeChild(document.getElementById('loading'))
          }
        }
      })
      new Vue({
        el: '#root',
        data() {
          return {
            isLoading: false,
            data: ''
          }
        },
        methods: {
          update() {
            this.isLoading = true
            setTimeout(() => {
              this.data = '用户数据'
              this.isLoading = false
            }, 3000)
          }
        }
      })
    </script>
  </body>
</html>

Vue.component 用法

<html>
  <head>
    <title>Vue.component 用法</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <Test :msg="message"></Test>
    </div>
    <script>
      Vue.component('Test', {
        template: '<div>{{msg}}</div>',
        props: {
          msg: {
            type: String,
            default: 'default message'
          }
        }
      })
      new Vue({
        el: '#root',
        data() {
          return {
            message: "Test Component"
          }
        }
      })
    </script>
  </body>
</html>

Vue.extend 用法

<html>
  <head>
    <title>Vue.extend 用法</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <Test :msg="message"></Test>
    </div>
    <script>
      const component = Vue.extend({
        template: '<div>{{msg}}</div>',
        props: {
          msg: {
            type: String,
            default: 'default message'
          }
        },
        name: 'Test'
      })
      Vue.component('Test')
      new Vue({
        el: '#root',
        data() {
          return {
            message: "Test Extend Component"
          }
        }
      })
    </script>
  </body>
</html>
<html>
  <head>
    <title>Vue.extend 用法2</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      #loading-wrapper {
        position: fixed;
        top: 0;
        left: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: 100%;
        background: rgba(0,0,0,.7);
        color: #fff;
      }
    </style>
  </head>
  <body>
    <div id="root">
      <button @click="showLoading">显示Loading</button>
    </div>
    <script>
      function Loading(msg) {
        const LoadingComponent = Vue.extend({
          template: '<div id="loading-wrapper">{{msg}}</div>',
          props: {
            msg: {
              type: String,
              default: msg
            }
          },
          name: 'LoadingComponent'
        })
        const div = document.createElement('div')
        div.setAttribute('id', 'loading-wrapper')
        document.body.append(div)
        new LoadingComponent().$mount('#loading-wrapper')
        return () => {
          document.body.removeChild(document.getElementById('loading-wrapper'))
        }
      }
      Vue.prototype.$loading = Loading
      new Vue({
        el: '#root',
        methods: {
          showLoading() {
            const hide = this.$loading('正在加载,请稍等...')
            setTimeout(() => {
              hide()
            }, 2000)
          }
        }
      })
    </script>
  </body>
</html>

Vue.use 用法

<html>
  <head>
    <title>Vue.use 用法</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      #loading-wrapper {
        position: fixed;
        top: 0;
        left: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: 100%;
        background: rgba(0,0,0,.7);
        color: #fff;
      }
    </style>
  </head>
  <body>
    <div id="root">
      <button @click="showLoading">显示Loading</button>
    </div>
    <script>
      const loadingPlugin = {
        install: function(vm) {
          const LoadingComponent = vm.extend({
            template: '<div id="loading-wrapper">{{msg}}</div>',
            props: {
              msg: {
                type: String,
                default: 'loading...'
              }
            }
          }, 'LoadingComponent')
          function Loading(msg) {
            const div = document.createElement('div')
            div.setAttribute('id', 'loading-wrapper')
            document.body.append(div)
            new LoadingComponent({
              props: {
                msg: {
                  type: String,
                  default: msg
                }
              } 
            }).$mount('#loading-wrapper')
            return () => {
              document.body.removeChild(document.getElementById('loading-wrapper'))
            }
          }
          vm.prototype.$loading = Loading
        }
      }
      Vue.use(loadingPlugin)
      new Vue({
        el: '#root',
        methods: {
          showLoading() {
            const hide = this.$loading('正在加载,请稍等...')
            setTimeout(() => {
              hide()
            }, 2000)
          }
        }
      })
    </script>
  </body>
</html>

组件通信 provide 和 inject

<html>
  <head>
    <title>组件通信 provide 和 inject</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <Test></Test>
    </div>
    <script>
      function registerPlugin() {
        Vue.component('Test', {
          template: '<div>{{message}}<Test2 /></div>',
          provide() {
            return {
              elTest: this
            }
          }, // function 的用途是为了获取运行时环境,否则 this 将指向 window
          data() {
            return {
              message: 'message from Test'
            }
          },
          methods: {
            change(component) {
              this.message = 'message from ' + component
            }
          }
        })
        Vue.component('Test2', {
          template: '<Test3 />'
        })
        Vue.component('Test3', {
          template: '<button @click="changeMessage">change</button>',
          inject: ['elTest'],
          methods: {
            changeMessage() {
              this.elTest.change(this.$options._componentTag)
            }
          }
        })
      }
      Vue.use(registerPlugin)
      new Vue({
        el: '#root'
      })
    </script>
  </body>
</html>

过滤器 filter

<html>
  <head>
    <title>过滤器 filter</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      {{message | lower}}
    </div>
    <script>
      new Vue({
        el: '#root',
        filters: {
          lower(value) {
            return value.toLowerCase()
          }
        },
        data() {
          return {
            message: 'Hello Vue'
          }
        }
      })
    </script>
  </body>
</html>

监听器 watch

<html>
  <head>
    <title>监听器 watch</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h3>Watch 用法1:常见用法</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root2">
      <h3>Watch 用法2:绑定方法</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root3">
      <h3>Watch 用法3:deep + handler</h3>
      <input v-model="deepMessage.a.b">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root4">
      <h3>Watch 用法4:immediate</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root5">
      <h3>Watch 用法5:绑定多个 handler</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root6">
      <h3>Watch 用法6:监听对象属性</h3>
      <input v-model="deepMessage.a.b">
      <span>{{copyMessage}}</span>
    </div>
      
    <script>
      new Vue({
        el: '#root',
        watch: {
          message(value) {
            this.copyMessage = value
          }
        },
        data() {
          return {
            message: 'Hello Vue',
            copyMessage: ''
          }
        }
      })
      new Vue({
        el: '#root2',
        watch: {
          message: 'handleMessage'
        },
        data() {
          return {
            message: 'Hello Vue',
            copyMessage: ''
          }
        },
        methods: {
          handleMessage(value) {
            this.copyMessage = value
          }
        }
      })
      new Vue({
        el: '#root3',
        watch: {
          deepMessage: {
            handler: 'handleDeepMessage',
            deep: true
          }
        },
        data() {
          return {
            deepMessage: {
              a: {
                b: 'Deep Message'
              }
            },
            copyMessage: ''
          }
        },
        methods: {
          handleDeepMessage(value) {
            this.copyMessage = value.a.b
          }
        }
      })
      new Vue({
        el: '#root4',
        watch: {
          message: {
            handler: 'handleMessage',
            immediate: true,
          }
        },
        data() {
          return {
            message: 'Hello Vue',
            copyMessage: ''
          }
        },
        methods: {
          handleMessage(value) {
            this.copyMessage = value
          }
        }
      }),
      new Vue({
        el: '#root5',
        watch: {
          message: [{
            handler: 'handleMessage',
          },
          'handleMessage2',
          function(value) {
            this.copyMessage = this.copyMessage + '...'
          }]
        },
        data() {
          return {
            message: 'Hello Vue',
            copyMessage: ''
          }
        },
        methods: {
          handleMessage(value) {
            this.copyMessage = value
          },
          handleMessage2(value) {
            this.copyMessage = this.copyMessage + '*'
          }
        }
      })
      new Vue({
        el: '#root6',
        watch: {
          'deepMessage.a.b': 'handleMessage'
        },
        data() {
          return {
            deepMessage: { a: { b: 'Hello Vue' } },
            copyMessage: ''
          }
        },
        methods: {
          handleMessage(value) {
            this.copyMessage = value
          }
        }
      })
    </script>
  </body>
</html>

class 和 style 绑定的高级用法

<html>
  <head>
    <title>class 和 style 绑定的高级用法</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <div :class="['active', 'normal']">数组绑定多个class</div>
      <div :class="[{active: isActive}, 'normal']">数组包含对象绑定class</div>
      <div :class="[showWarning(), 'normal']">数组包含方法绑定class</div>
      <div :style="[warning, bold]">数组绑定多个style</div>
      <div :style="[warning, mix()]">数组包含方法绑定style</div>
      <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">style多重值</div>
    </div>
    <script>
      new Vue({
        el: '#root',
        data() {
          return {
            isActive: true,
            warning: {
              color: 'orange'
            },
            bold: {
              fontWeight: 'bold'
            }
          }
        },
        methods: {
          showWarning() {
            return 'warning'
          },
          mix() {
            return {
              ...this.bold,
              fontSize: 20
            }
          }
        }
      })
    </script>
  </body>
</html>

Vue.observable

<html>
  <head>
    <title>Vue.observable</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      {{message}}
      <button @click="change">Change</button>
    </div>
    <script>
      const state = Vue.observable({ message: 'Vue 2.6' })
      const mutation = {
        setMessage(value) {
          state.message = value
        }
      }
      new Vue({
        el: '#root',
        computed: {
          message() {
            return state.message
          }
        },
        methods: {
          change() {
            mutation.setMessage('Vue 3.0')
          }
        }
      })
    </script>
  </body>
</html>

插槽 slot

<html>
  <head>
    <title>插槽 slot</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <div>案例1:slot的基本用法</div>
      <Test>
        <template v-slot:header="{user}">
          <div>自定义header({{user.a}})</div>
        </template>
        <template v-slot="{user}">
          <div>自定义body({{user.b}})</div>
        </template>
      </Test>
    </div>
    <div id="root2">
      <div>案例2:Vue2.6新特性 - 动态slot</div>
      <Test>
        <template v-slot:[section]="{section}">
          <div>this is {{section}}</div>
        </template>
      </Test>
      <button @click="change">switch header and body</button>
    </div>
    <script>
      Vue.component('Test', {
        template: 
          '<div>' +
            '<slot name="header" :user="obj" :section="'header'">' +
              '<div>默认header</div>' +
            '</slot>' +
            '<slot :user="obj" :section="'body'">默认body</slot>' +
          '</div>',
        data() {
          return {
            obj: { a: 1, b: 2 }
          }
        }
      })
      new Vue({ el: '#root' })
      new Vue({ 
        el: '#root2',
        data() {
          return {
            section: 'header'
          }
        },
        methods: {
          change() {
            this.section === 'header' ?
              this.section = 'default' :
              this.section = 'header'
          }
        }
      })
    </script>
  </body>
</html>

Element-UI

vue create element-test
vuex+router ([Vue 2] router, vuex, dart-sass, babel, eslint)
  Default ([Vue 2] babel, eslint)
  Default (Vue 3) ([Vue 3] babel, eslint)
  Manually select features

image.png

  1. 初始化项目
vue create element-test
  1. 安装
npm i element-ui -S
  1. Vue 插件
import ElementUI from 'element-ui'

Vue.use(ElementUI)
  1. 引用样式
import 'element-ui/lib/theme-chalk/index.css'

按需加载

Entrypoints:
  app (1.03 MiB)
      css/chunk-vendors.a16c4353.css
      js/chunk-vendors.29976e71.js
      css/app.601d2ada.css
      js/app.29e7d705.js
For more info visit https://webpack.js.org/guides/code-splitting/

  File                                   Size              Gzipped

  dist\js\chunk-vendors.29976e71.js      845.63 KiB        225.63 KiB
  dist\js\app.29e7d705.js                2.77 KiB          1.30 KiB
  dist\css\chunk-vendors.a16c4353.css    209.55 KiB        33.63 KiB
  dist\css\app.601d2ada.css              0.25 KiB          0.19 KiB

  Images and other types of assets omitted.

按需加载的用法如下:

  1. 安装 babel-plugin-component
npm install babel-plugin-component -D
  1. 修改 babel.config.js:
{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

Vuex 原理解析

Vuex 的原理关键:使用 Vue 实例管理状态

<html>
  <head>
    <title>vuex 原理解析</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="root">{{data}}</div>
    <div id="root2">{{data2}}</div>
    <div id="root3">
      <button @click="change">change</button>
    </div>
    <script>
      function registerPlugin(Vue) {
        const vuex = {}
        vuex._vm = new Vue({
          data: {
            message: 'hello vue.js'
          }
        })
        vuex.state = vuex._vm
        vuex.mutations = {
          setMessage(value) {
            vuex.state.message = value
          }
        }
        function init() {
          this.$store = vuex
        }
        Vue.mixin({
          beforeCreate: init
        })
      }
      Vue.use(registerPlugin)
      new Vue({
        el: '#root',
        computed: {
          data() {
            return this.$store.state.message
          }
        }
      })
      new Vue({
        el: '#root2',
        computed: {
          data2() {
            return this.$store.state.message
          }
        }
      })
      new Vue({
        el: '#root3',
        methods: {
          change() {
            const newValue = this.$store.state.message + '.'
            this.$store.mutations.setMessage(newValue)
          }
        }
      })
    </script>
  </body>
</html>

vue-router 实现原理

vue-router 实例化时会初始化 this.history,不同 mode 对应不同的 history

constructor (options: RouterOptions = {}) {
    this.mode = mode
    
    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
}

这里以 HashHistory 为例,vue-router 的 push 方法实现如下:

push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    // $flow-disable-line
    if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        this.history.push(location, resolve, reject)
      })
    } else {
      this.history.push(location, onComplete, onAbort)
    }
}

HashHistory 具体实现了 push 方法:

function pushHash (path) {
  if (supportsPushState) {
    pushState(getUrl(path))
  } else {
    window.location.hash = path
  }
}

对路由的监听通过 hash 相应的事件监听实现:

window.addEventListener(
  supportsPushState ? 'popstate' : 'hashchange',
  () => {
    const current = this.current
    if (!ensureSlash()) {
      return
    }
    this.transitionTo(getHash(), route => {
      if (supportsScroll) {
        handleScroll(this.router, route, current, true)
      }
      if (!supportsPushState) {
        replaceHash(route.fullPath)
      }
    })
  }
)

除此之外,vue-router 还提供了两个组件:

Vue.component('RouterView', View)
Vue.component('RouterLink', Link)

vue-router 路由守卫

创建 router.js:

import Vue from 'vue'
import Route from 'vue-router'
import HelloWorld from './components/HelloWorld'

Vue.use(Route)
const routes = [
  { path: '/hello-world', component: HelloWorld }
]
const router = new Route({
  routes
})

export default router

在 main.js 中引用 router,并加入 vue 实例:

import router from './router'

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

全局守卫

router.beforeEach((to, from, next) => {
  console.log('beforeEach', to, from)
  next()
})

router.beforeResolve((to, from, next) => {
  console.log('beforeResolve', to, from)
  next()
})

router.afterEach((to, from) => {
  console.log('afterEach', to, from)
})

局部守卫

beforeRouteEnter (to, from, next) {
  // 不能获取组件实例 `this`
  console.log('beforeRouteEnter', to, from)
  next()
},
beforeRouteUpdate (to, from, next) {
  console.log('beforeRouteUpdate', to, from)
  next()
},
beforeRouteLeave (to, from, next) {
  console.log('beforeRouteLeave', to, from)
  next()
}

路由元信息

通过 meta 定义路由元信息

const routes = [
  { path: '/a', component: A, meta: { title: 'Custom Title A' } }
]

使用 meta 信息动态修改标题

router.beforeEach((to, from, next) => {
  console.log('beforeEach', to, from)
  if (to.meta && to.meta.title) {
    document.title = to.meta.title
  } else {
    document.title = 'default title'
  }
  next()
})

路由 API

使用 router.addRoutes 动态添加路由

addRoute() {
    this.$router.addRoutes([{
      path: '/b', component: B, meta: { title: 'Custom Title B' },
    }])
}

此时可以访问到 B 组件

<router-link to='/b'>to B</router-link>

项目初始化

git clone https://github.com/PanJiaChen/vue-element-admin
cd vue-element-admin
npm i
npm run dev

项目精简

  • 删除 src/views 下的源码,保留:

    • dashboard:首页
    • error-page:异常页面
    • login:登录
    • redirect:重定向
  • 对 src/router/index 进行相应修改

  • 删除 src/router/modules 文件夹

  • 删除 src/vendor 文件夹

vue-element-admin 初始化项目

vue-element-admin 实现了登录模块,包括 token 校验、网络请求等

项目配置

通过 src/settings.js 进行全局配置:

  • title:站点标题,进入某个页面后,格式为:
页面标题 - 站点标题
  • showSettings:是否显示右侧悬浮配置按钮
  • tagsView:是否显示页面标签功能条
  • fixedHeader:是否将头部布局固定
  • sidebarLogo:菜单栏中是否显示LOGO
  • errorLog:默认显示错误日志的环境

源码调试

如果需要进行源码调试,需要修改 vue.config.js:

config
  // https://webpack.js.org/configuration/devtool/#development
  .when(process.env.NODE_ENV === 'development',
    config => config.devtool('cheap-source-map')
  )

将 cheap-source-map 改为 source-map,如果希望提升构建速度可以改为 eval

通常建议开发时保持 eval 配置,以增加构建速度,当出现需要源码调试排查问题时改为 source-map

项目结构

  • api:接口请求

  • assets:静态资源

  • components:通用组件

  • directive:自定义指令

  • filters:自定义过滤器

  • icons:图标组件

  • layout:布局组件

  • router:路由配置

  • store:状态管理

  • styles:自定义样式

  • utils:通用工具方法

    • auth.js:token 存取
    • permission.js:权限检查
    • request.js:axios 请求封装
    • index.js:工具方法
  • views:页面

  • permission.js:登录认证和路由跳转

  • settings.js:全局配置

  • main.js:全局入口文件

  • App.vue:全局入口组件

后端框架搭建

npm init -y

安装依赖

npm i -S express

创建 app.js

const express = require('express')

// 创建 express 应用
const app = express()

// 监听 / 路径的 get 请求
app.get('/', function(req, res) {
  res.send('hello node')
})

// 使 express 监听 5000 端口号发起的 http 请求
const server = app.listen(5000, function() {
  const { address, port } = server.address()
  console.log('Http Server is running on http://%s:%s', address, port)
})

Express 三大基础概念

中间件

中间件是一个函数,在请求和响应周期中被顺序调用

const myLogger = function(req, res, next) {
  console.log('myLogger')
  next()
}

app.use(myLogger)

提示:中间件需要在响应结束前被调用

路由

应用如何响应请求的一种规则

响应 / 路径的 get 请求:

app.get('/', function(req, res) {
  res.send('hello node')
})

响应 / 路径的 post 请求:

app.post('/', function(req, res) {
  res.send('hello node')
})

异常处理

通过自定义异常处理中间件处理请求中产生的异常

app.get('/', function(req, res) {
  throw new Error('something has error...')
})

const errorHandler = function (err, req, res, next) {
  console.log('errorHandler...')
  res.status(500)
  res.send('down...')
}

app.use(errorHandler)

使用时需要注意两点:

  • 第一,参数一个不能少,否则会视为普通的中间件
  • 第二,中间件需要在请求之后引用

项目框架搭建

路由

安装 boom 依赖:

npm i -S boom

创建 router 文件夹,创建 router/index.js:

const express = require('express')
const boom = require('boom')
const userRouter = require('./user')
const {
  CODE_ERROR
} = require('../utils/constant')

// 注册路由
const router = express.Router()

router.get('/', function(req, res) {
  res.send('欢迎学习管理后台')
})

// 通过 userRouter 来处理 /user 路由,对路由处理进行解耦
router.use('/user', userRouter)

/**
 * 集中处理404请求的中间件
 * 注意:该中间件必须放在正常处理流程之后
 * 否则,会拦截正常请求
 */
router.use((req, res, next) => {
  next(boom.notFound('接口不存在'))
})

/**
 * 自定义路由异常处理中间件
 * 注意两点:
 * 第一,方法的参数不能减少
 * 第二,方法的必须放在路由最后
 */
router.use((err, req, res, next) => {
  const msg = (err && err.message) || '系统错误'
  const statusCode = (err.output && err.output.statusCode) || 500;
  const errorMsg = (err.output && err.output.payload && err.output.payload.error) || err.message
  res.status(statusCode).json({
    code: CODE_ERROR,
    msg,
    error: statusCode,
    errorMsg
  })
})

module.exports = router

创建 router/use.js:

const express = require('express')

const router = express.Router()

router.get('/info', function(req, res, next) {
  res.json('user info...')
})

module.exports = router

创建 utils/constant:

module.exports = {
  CODE_ERROR: -1
}

验证 /user/info:

"user info..."

验证 /user/login:

{"code":-1,"msg":"接口不存在","error":404,"errorMsg":"Not Found"}

登录

  • 用户名密码校验
  • token 生成、校验和路由过滤
  • 前端 token 校验和重定向

Nginx 服务器搭建

修改配置文件

打开配置文件 nginx.conf:

  • windows 位于安装目录下
  • macOS 位于:/usr/local/etc/nginx/nginx.conf

修改一:添加当前登录用户为da

user sam da;

修改二:在结尾大括号之前添加:

include /Users/sam/upload/upload.conf;

这里 /Users/sam/upload 是资源文件路径,/Users/sam/upload/upload.conf 是额外的配置文件,当前把 /Users/sam/upload/upload.conf 配置文件的内容加入 nginx.conf 也是可行的!

修改三:添加 /Users/sam/upload/upload.conf 文件,配置如下:

server
{ 
  charset utf-8;
  listen 8089;
  server_name http_host;
  root /Users/sam/upload/;
  autoindex on;
  add_header Cache-Control "no-cache, must-revalidate";
  location / { 
    add_header Access-Control-Allow-Origin *;
  }
}

如果需要加入 https 服务,可以再添加一个 server:

server
{
  listen 443 default ssl;
  server_name https_host;
  root /Users/sam/upload/;
  autoindex on;
  add_header Cache-Control "no-cache, must-revalidate";
  location / {
    add_header Access-Control-Allow-Origin *;
  }
  ssl_certificate /Users/sam/Desktop/https/da.pem;
  ssl_certificate_key /Users/sam/Desktop/https/da.key;
  ssl_session_timeout  5m;
  ssl_protocols  SSLv3 TLSv1;
  ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
  ssl_prefer_server_ciphers  on;
}
  • https证书:/Users/sam/Desktop/https/da.pem
  • https:私钥:/Users/sam/Desktop/https/da.key

启动服务

启动 nginx 服务:

sudo nginx

重启 nginx 服务:

sudo nginx -s reload

停止 nginx 服务:

sudo nginx -s stop

检查配置文件是否存在语法错误:

sudo nginx -t

访问地址:

  • http: http://localhost:8089
  • https: https://localhost

📚掘金文章

❤️关注+点赞+收藏+评论+转发❤️

点赞、收藏和评论

我是Jeskson(达达前端),感谢各位人才的:点赞、收藏和评论,我们下期见!(如本文内容有地方讲解有误,欢迎指出☞谢谢,一起学习了)

我们下期见!

文章持续更新,可以微信搜一搜「 程序员哆啦A梦 」第一时间阅读,回复【资料】有我准备的一线大厂资料,本文 www.1024bibi.com 已经收录

github收录,欢迎Stargithub.com/webVueBlog/…