史上最详细!看了学不会就找我!使用VUE + Element-UI完成一个后台管理系统项目,从项目搭建到技术分析,外加项目经验。

Vue后台管理系统

此项目是 vue + element-ui 构建的后台管理系统(单页面应用),所有的数据都是从服务器实时获取的真实数据,具有真实的登录、数据显示、新增数据、修改数据、删除数据等功能。

前端技术栈:vue + vuex + vue-cli + vue-r outer + ES6 + sass + element-ui
调试工具:devtools + postman

vue-cli搭建项目结构解析:

  • node_modules 需要下载的所有依赖包
  • public 静态资源服务器
  • src 源码文件目录
    • assets 静态资源
    • main.js 入口文件,代码开始运行的地方(创建根容器、挂载路由系统)
    • router.js 路由系统,用于实现单页面应用程序
    • components 公共组件
    • pages 页面组件
    • store 仓库(包含应用中状态)
    • utils 工具(联调接口)
  • App.vue 根组件
  • package.json 项目信息(记录依赖包)
  • readme 项目解析笔记
  • vue.config.js 解决跨域配置文件

路由系统解析:

1. 安装路由
cnpm install vue-router -S  
2. src 根目录中新建一个 router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)  // 路由注册

export default new VueRouter({
  routes: []
})
3.在 main.js 入口文件中,把路由系统挂载到Vue系统中去
import router from './router.js'

new Vue({
  router
})
4.定义路由匹配规则
// 定义路由匹配规则
const routes = [
    { path: '/home', component: Home },
    { path: '/user', component: User },
    { path: '/find', component: Find }
]
5、 显示url匹配成功的视图(组件)

6.如何改变URL?
  • **声明式路由导航:**建议使用vue-router内置 组件来实现。
    • 注:默认用a标签显示,使用tag属性可以实现改变标签
  • **编程式路由导航:**使用 路由api【$router.push()/replace()/back()】来实现页面跳转。
    • 【编程式路由跳转的三种写法】
    • 1、使用path来跳转 this.$router.push(’/useroiwioewoieoioiewoiewoiewoiewi’)
    • 2、使用路由别名 this.$router.push(’/u’)
    • 3、命名路由命名 this.$router.push({name:‘me’})
7、动态路由

路由匹配规则的写法:{path: ‘/detail/:id’, component: ‘’, props: true }

两种路由参数传参的方式:$route / props

8、三个命名
  • 视图命名:当加了name属性时,在路由匹配规则定义时要使用 components 字段。

    实际应用:登录页和Layout(系统内页)是有你无我的。他俩敌人(兄弟)关系。
    但是Login页面也需要通过 /#/login 路由的方式进行访问,也需要把 Login加入到路由系统中。
    为了避免Login 和 其它所谓的 pages 页面抢那个名字叫default的椅子(router-view)。
    所以,当我们把Login加入到路由系统中时,给它一个VIP专座(名字叫login的router-view)来展示,放在App.vue中。

        components: {
           abc: Home,
           efg: Find,
           default: MusicList
         }
  • 路由别名:给路由匹配规则中的复杂path,取一个容易记忆的小名。
        name: 'me'
  • 路由命名:给路由匹配规则取一个名字。
        alias: '/u'
9.路由懒加载

是一种性能优化的方案,是导入组件的一种写法,使用webpack代码分割功能和异步组件的特点来实现

const Home = ()=>import('./home/Home.vue')
const User = ()=>import('./user/User.vue')
const Find = ()=>import('./home/Find.vue')

异步组件

 Vue.component('qf-async', (resolve, reject)=>{
   setTimeout(()=>{
     resolve({
       template: `
`, methods: {} }) }, 2000) })
10.嵌套视图

即,被 承载的组件中还可以再使用 ,形成嵌套关系。 在路由匹配规则中,使用 children 属性来实现。

{
  path: '/find',
  component: Find,
  // 嵌套视图
  children: [
    { path: '', component: FindPanelA },
    { path: 't2', component: FindPanelB }
  ]
}

状态管理工具解析

1. 如何理解“状态”?

在应用程序中,状态就是数据。 状态管理工具在Vue项目架构中是可选的。但是,从项目发展角度看,最好还安装、集成一下。 Vuex,它是Vue全家桶中最流利使用的状态工具。

2. 作用
  • 组件之间数据共享
  • 实现数据缓存
3. Vuex中的五大概念
  • state 存储中心,所有需要被共享或缓存的数据,都在这里定义
  • getters 相当于计算属性,过滤出来一些值,当它所关系的state变量发生变化时,会自动重新计算
  • mutations mutations是Vuex中专门用于更新state
  • actions 异步操作方法,与后端api打交道
  • mudules 模块化
5. Vuex使用
  • 安装Vuex
npm i vuex --save
  • 引用Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
  • 创建仓库Store
 const store = new Vuex.Store({});
  • 在 main.js 中挂载
import store from './store/index.js'
new Vue({
  store
}).$mount('#app')

6. 如何优雅地使用Vuex来管理应用程序中的外部数据?

把外部数据定义在Vuex的state中,页面中就可以通过 $store.state来使用了。 封装api接口(参考utils/api.js) 在Vuex导入api方法,在actions中定义与后端交互的逻辑,把处理完成的数据通过mutations方法提交到state 在mutations中定义 更新state的方法。当state变化时,页面自动更新 那么actions在哪里被触发呢?在页面逻辑中触发它。

【温馨提示】:建议在组件使用 map* 系列方法来使用Vuex中的数据和函数 mapState 和 mapGetters 在写在 computed 中 mapActions 和 mapMutations 写在 methods 中

axios解析

是一个HTTP工具,用于与后端进行数据交互。 特点: 1、基于Promise封装的库 2、在客户端、Node端都可以使用

在项目中怎么使用呢? 1、cnpm install axios -S 2、一定要封装请求拦截器和响应拦截器。参考 utils/axios.js 3、最好把所有api统一封装可以复用的方法。参考 utils/api.js

浏览器同源策略阻止了ajax,怎么解决? 在项目根目录里新建vue.config.js文件,代码如下:

module.exports = {
  devServer: {
    // 用代理的方式来解决浏览器同源策略对ajax的限制
    proxy: {
      '/soso': {
        target: 'https://c.y.qq.com',  // 目标远程服务器
        ws: true,
        changeOrigin: true   // 允许改变“域”
      }
    }
  }
}

生命周期解析

  1. beforeCreate 这是第一个生命周期函数;此时,组件的data和methods以及页面DOM结构,都还没有初始化;所以此阶段,什么都做不了。
  2. created 这个组件创建阶段第二个生命周期函数,此时,组件的data和methods已经可以用了;但是页面还没有渲染出来;在这个生命周期函数中,一般会发起Ajax请求。
  3. beforeMount 当模板在内存中编译完成,会立即执行实例创建阶段的第三个生命周期函数beforeMount,此时内存中的模板结构,还没有真正渲染到页面上;此时,页面上看不到真实的数据,用户看到的只是一个模板页面而已。
  4. mounted mounted是组件创建阶段最后的一个生命周期函数;此时,页面已经真正的渲染好了,用户可以看到真实的页面数据了;当这个生命周期函数执行完,组件就离开了创建阶段,进入到了运行中的阶段;如果使用到一些第三方的UI插件,而且这个插件还需要被初始化,那么,必须在mounted中来初始化插件。
  5. beforeUpdate 在执行beforeUpdate运行中的生命周期函数的时候,数据肯定是最新的;但是页面上呈现的数据还是旧的。
  6. updated 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以这时可以执行依赖于 DOM 的操作。
  7. beforeDestroy 当执行beforeDestroy的时候,组件即将被销毁,但是还没有真正开始销毁,此时组件还是正常可用的;data、methods等数据或方法,依旧可以被正常访问。
  8. destroyed 组件已完成了销毁,组件无法使用,data和methods都不可使用。

项目开发

页面渲染

此项目为单页面应用,一切皆组件,路由系统原理是当路由发生变化,切换main内容组件,可以通过watch监听路由系统url的变化。
  • 布局渲染::在omponents/common文件夹里写父组件QfLayout 子组件QfAsaide(左侧菜单栏)、QfHeader(右侧顶部)、QfMain(右侧主体显示url匹配组件),然后统一在index.js里抛出。在App.vue里导入并应用QfLayout组件完成基本布局渲染。
  • 菜单栏: 遍历pages/index.js里的路由数组,拿到text完成渲染,在路由数组里添加isNotNav键实现控制是否渲染成菜单,添加exact键控制是否精准匹配实现高亮显示。
  • 主体: 通过 渲染在QfMain,再导入到QfLayout,通过App.vue使用(椅子)渲染到页面。
  • 登录页 登录页和Layout(系统内页)是(兄弟)关系,不同时存在。为了避免Login 和 其它所谓的 pages 页面抢那个名字叫default的椅子(router-view)。所以,当我们把Login加入到路由系统中时,给它一个VIP专座(名字叫login的router-view)来展示,放在App.vue中。它俩的胜负关系,由$route.path===’/login’来决定胜负。


功能技术分析

使用嵌套路由实现find页面tab选项卡切换效果
    //定义路由规则
      {
        id: 1002,
        path: '/find',
        component: Find,
        text: '公司新闻',
        exact: false,
        // 嵌套路由
        children: [
          { path: '', component: FindPanelA },   // /find/
          { path: 't2', component: FindPanelB }    // /find/t2
        ],
      }
     
    //渲染
    今日
    昨日
使用状态管理实现TodoList效果
  • 添加任务: 在todo.vue里的input标签添加回车事件confirm,然后在methods里定义回车事件confirm,在confirm事件里向Vuex提交添加任务的申请并传参,在todo.js的state里定义list空数组,在mutations里写定义的任务并接受参数,每当页面confirm事件触发,就会提交添加任务申请,就会触发mutation定义好的任务,在任务里向list空数组里添加页面v-model双向绑定的数据,循环list拿到数据即可渲染在页面上。
    // 向状态管理中添加一条任务
    confirm() {
      // 向Vuex提交添加任务的申请
      // this.$store.commit('addTask', this.task)
      this.addTask(this.task)
      this.task = ''
    },
    
    
    mutations: {
    addTask(state, payload) {  // 负荷
      console.log('addTask', state, payload)
      state.list.push({task: payload, id: Date.now()})
    },
  }
  • 删除任务: 在todo.vue组件里的span标签添加点击事件remove,然后在methods里定义remove事件向Vuex提交删除任务的申请并传ID作为参数,在todo.js的mutations里写定义的删除任务并接受参数,使用数组的filter方法删除指定ID的任务。
    // 删除一条任务
    remove(item) {
      // 向Vuex提交删除任务的申请
      // this.$store.commit('delTask', item.id)
      this.delTask(item.id)
    }
    
    mutations: {
        delTask(state, payload) {
        state.list = state.list.filter(ele=>ele.id!==payload)
        }
    },

  • 计算任务数量: 在getters里定义total方法并传state为参数计算list数组长度(任务数量),state相关变量发生变化,getters里跟state相关的变量就会重新计算,用v-text把tatol渲染在页面上完成计算功能。
  getters: {
    total(state) {
      return state.list.length
    }
  },
调取接口实现简版QQ音乐歌曲搜索
  • src目录里新建utils文件表示工具文件,新建axios.js文件,在里面导入axios模块,并且创建一个axios实例,封装请求拦截器(在请求被send出去之前)跟响应拦截器(当后端返回数据,抵达客户端之前时)。
  • 新建api.js,导入axios实例,定义fetchQqMusic方法联调接口并抛出。
  • 创建子store(music.js)文件并抛出,然后在实例上导入。
export function fetchQqMusic(params) {
  return fetch({
    url: '/soso/fcgi-bin/client_search_cp',
    method: 'GET',
    params
  })
}

export default {
  fetchQqMusic,
}
  • 状态管理流程
    1. 在MusicList.vue使用定义好的getMusic方法并传接口地址参数。难点:查询字符串转换成json对象,附代码。
    2. 在子store(music.js)中,用getMusic方法接收接口地址参数,并用fetchQqMusisc方法调接口拿到音乐列表数据,在actions里commit一个updateMusicList方法并传音乐列表参数,用来更新音乐列表。
    3. 在mutations里定义updataMusicList方法更新音乐列表。
    //查询字符串转换成json对象
      let params = {}
      str.split('&').map(ele=>{
        let arr = ele.split('=')
        params[arr[0]] = arr[1]
      })

项目经验

  • 工作中不用把所有文件搞明白,先弄懂全局的东西(vue.config.js/README.md/package.json/main.js/App.vue/及utils封装的api)
  • 工作中为了调试效果一般先需要假数据
  • 管理系统中不要用浮动操作
  • 要写低耦合(动态)代码,即改一个地方就把所有需要用的地方都改,要学会改造代码,该循环循环,该封装的地方封装。
  • 做导航页面跳转,如果需要做高亮样式就用声明式导航(router-link)做,如果需要做业务逻辑,就用编程式导航做(this.$router.history.push/replace/back(’’))。
  • 在vue中封装方法不要用delete,deletey是一元操作符,定义删除方法用remove。
  • 使用状态管理时,不要直接在页面中操作state,要使用mutations更新state,这样Devtools才能有记录。
  • 在传递参数的时候,中文会被编译成字符串,可以使用decodeURI反编码
  • 在computed里导入并使用vuex中的mapState/mapGetters方法,可以把子store中的需要用到的变量作为json对象映射进当前页面,在当前页面直接用this.变量名就可以调用,达到简化代码的目的。
  • 在methods里导入并使用vuex中的mapActions/mapMutations方法,可以把子store中的需要用到的方法作为json对象映射进当前页面,在当前页面直接用this.方法名就可以调用,达到简化代码的目的。
  • 在methods里导入并使用vuex中的mapActions方法,可以把子store中的需要用到的方法作为json对象映射进当前页面,在当前页面直接用this.方法名就可以调用,达到简化代码的目的。

你可能感兴趣的:(elementui,vue,vue,elementui,大前端,javascript,项目架构)