课程资料

一、登录页

课程目标

  1. 复习vue脚手架
  2. 搭建vue大型项目框架
  3. 制作一级路由页面登录页面

知识点

1.v-model

登录页有两个输入框,分别是用户名和密码。用户在输入框里输入的内容可以通过v-model指令和data进行双向数据绑定。
可以带着学生回忆一下在没有vue的时候怎么做这个功能。在react里没有v-model指令,是通过受控组件来做的,两者有什么
区别?能否在vue里用受控组件代替v-model?

语法格式:

<input v-model="username">

2.axios

前后端通信的工具,如何安装axios?添加登录按钮,并绑定登录事件,在登录回调函数里如何获取输入框里的值?
获取到值后,使用axios把用户名和密码发送给后端。

语法格式:

<button @click="handleLogin">登录</button>

methods: {
  handleLogin() {
    Api.login({ username: this.username, password: this.password }).then(res => {
    })
  }
}

3.如何写后端接口

可以在vue.config.js里写,也可以利用在专高一时所学的知识,搭建后端项目,使用express写接口。
vue.config.js是vue脚手架的api,会随着项目的启动自动加载。建议安装body-parser解析post请求。

语法格式:

const bodyParser = require('body-parser')

let userList = [
  {
    id: 0,
    username: 'admin',
    password: '123456'
  },
  {
    id: 1,
    username: 'xu',
    password: '123'
  }
]

module.exports = {
  lintOnSave: false,
  devServer: {
    open: true,
    before(app) {

      app.use(bodyParser.json())

      app.post('/api/login', (req, res) => {
        let { username, password } = req.body
        let user = userList.find(item => item.username === username)
        if (user) {
          if (user.password === password) {
            res.send({
              code: 200,
              data: {
                username
              },
              message: '登录成功'
            })
          } else {
            res.send({
              code: 400,
              message: '密码错误'
            })
          }
        } else {
          res.send({
            code: 400,
            message: '用户名不存在'
          })
        }
      })
    }
  }
}

4.登录页面的优化

首页要做的是用户名输入框自动获取焦点,可以给学生看看百度首页,每次刷新时百度首页的搜索框都会自动获取焦点,这样
做对用户来说使用起来很方便!
语法格式:

<input v-model="username" placeholder="请输入用户名" autofocus >

还有一个需要优化的是回车登录。这个也可以举百度的例子,就是用户输入完成后,点击回车键即可搜索!
在vue里可以使用.enter修饰keyup事件做到这个功能。

语法格式:

<input @keyup.enter="handleLogin" >

授课思路

在这里插入图片描述

案例作业

1.练习v-model
2.制作登录页
3.前后端通信
4.可以包含多个用户
5.区分用户名错误和密码错误

在这里插入图片描述

二、登录页控制密码显示隐藏

课程目标

  1. 复习登录页主要知识点
  2. 复习vue组件化的思想
  3. 讲解父子组件传值
  4. 讲解iconfont的用法以及为什么要用iconfont
  5. 封装Icon组件,使用iconfont,控制密码输入框的密码显示和隐藏

知识点

1.组件化思想

组件化就是把传统的一张大网页拆分成很多小组件,有些组件可以反复使用,有些组件也许只使用一次,拆分成很多组件的目的就是使页面解构更清晰更好维护,也方便多人协作开发一个大项目。

2.vue组件

首先带学生回忆一下react里如何创建组件,然后再用快捷方式创建一个vue的组件。讲解vue组件的模板、js以及样式这三大模块在react组件里是如何实现的,还可以讲解一下jsx多么的方便,而vue又哪些弊端。再给学生展示一下vue和react在github上star数对比以及npm下载量对比。

3.iconfont

讲解iconfont和雪碧图的区别。现场演示登录iconfont网站,并添加眼睛睁开和眼睛闭上的图标,并下载到本地。打开demo页,调试网页,
介绍iconfont的原理。

4.制作Icon组件

引入iconfont文件,编写Icon组件。
重点内容:props的类型检查,如何动态绑定class,子组件触发父组件的方法

语法格式:

<template>
  <span :class="[ `icon iconfont icon-${name}` ]" @click="handleClick"></span>
</template>

<script>
export default {
 props: {
   name: String
 },
 methods: {
   handleClick() {
     this.$emit('onClick')
   }
 }
}
</script>

5.在登录页使用Icon组件

如果引入组件并使用

语法格式:

<template>
  <div>
    <div>
      <input v-model="username" placeholder="请输入用户名" autofocus >
    </div>
    <div>
      <input v-model="password" placeholder="请输入密码" :type=" visible ? 'text' : 'password'" @keyup.enter="handleLogin" >
      <Icon :name="visible ? 'xianshimima' : 'buxianshimima'" class="m-login-icon" @onClick="handleVisible" ></Icon>
    </div>
    <button @click="handleLogin">登录</button>
  </div>
</template>

<script>
import axios from 'axios'
import Icon from '../components/Icon'
import Api from '../api'

export default {
  data() {
    return {
      username: 'admin',
      password: '123456',
      visible: false
    }
  },
  components: {
    Icon
  },
  methods: {
    handleLogin() {
      Api.login({ username: this.username, password: this.password }).then(res => {
        if (res.code === 200) {
          localStorage.setItem('token', res.data.username)
          this.$router.push('/index/home')
        } else {
          this.$message({ message: res.message, duration: 1000 })
        }
      })
    },
    handleVisible() {
      this.visible = !this.visible
    }
  }
}
</script>

<style>

</style>

授课思路

在这里插入图片描述

案例作业

1.完善登录页
2.研究iconfont的用法
3.编写vue组件
4.控制密码的显示和隐藏

在这里插入图片描述

三、封装Api

课程目标

  1. 讲解为什么要封装Api
  2. 首先封装url
  3. 其次要复习复习Promise
  4. 讲解拦截器
  5. 讲解封装本身代码

知识点

1.为啥要封装Api呢

为了好维护,减少代码量!把前后端通信的url单独写的一个文件里,有多少接口就一目了然了。把所有前端发起请求的方法也写到一个文件里,方法名抛出,可以减少代码量。另外还可以给axios添加拦截器,对报错信息统一处理,统一添加请求头等等。

2.首先封装url

封装url并没有用到啥新技术,js模块化而已!把url归归类,也可以写写注释,甚至还可以继续拆分成多个文件,多人协同维护。

语法格式:

const urls = {
  login: '/api/login',
  list: '/api/list',
  myBooks: '/api/my_books'
}

export default urls

3.复习Promise

axios请求本身就是一个promise,如果想对axios进行二次封装,要好好研究一下。promise对象是可以通过.then处理成功的回调的,在封装之前大家一直也都这么用,封装之后也需要继续保持。async函数本身也是一个promise,且async函数内部可以通过await等待异步函数执行。

4.拦截器

拦截器只是一个概念,其实很好理解。可以类比之前讲的express中间件,其实二者的语法格式都是一致的。重点不是语法,重点是拦截器能帮咱干啥。可以类比高速公路的上高速前领一张卡和下高速前把卡交回并缴费。回到拦截器里就是请求前可以在header里添加token等信息,请求后可以帮忙这一些消息框的提示拦截。

5.封装axios

这块是核心代码,会有点多,核心思想就是把请求的核心部分封装成一个函数,抛出多个函数对应多个接口。

语法格式:

import axios from 'axios'
import message from '../components/message'
import urls from './urls'

if (process.env.NODE_ENV === 'development') {
  axios.defaults.baseURL = 'http://localhost:81'
}

axios.interceptors.request.use((config) => {
  config.headers.token = localStorage.getItem('token')
  return config
})

axios.interceptors.response.use((res) => {
  if (res.data.code === 400) {
    message({ message: res.data.message, duration: 2000 })
  }
  return res
})

const common = async (config) => {
  let resolve = await axios(config)
  return resolve.data
}

const Api = {
  login: (data) => common({ url: urls.login, data, method: 'post' }),
  list: () => common({ url: urls.list }),
  myBooks: (data, method) => common({ url: urls.myBooks, data, method })
}

export default Api

授课思路

在这里插入图片描述

案例作业

1.登录页调用封装后的api
2.在后端添加对一个数组的增删查改的接口
3.前端封装对应的接口并调用
4.研究get请求和post请求的区别

四、二级路由

课程目标

  1. 登录后跳转到新页面
  2. 新页面是个三段式
  3. header、footer拆分成组件
  4. 中间部分是二级路由,动态切换

知识点

1.如何跳转路由

如果用户名和密码都是正确的,后端会返回登录成功,前端就需要跳转页面了。可以给学生扩展一下路由的原理,history模式和hash模式到底啥区别?

语法格式:

  this.$router.push('/index/home')

2.三段式

盒子需要覆盖整个浏览器,flex纵向布局。头和尾高度固定,中间部分flex:1

.m-wrap{display: flex;flex-direction: column;position: absolute;top: 0;left: 0;right: 0;bottom: 0;}
.m-header{height: 40px;line-height: 40px;background: red;color: #fff;text-align: center;}
.m-main{flex: 1;overflow-y: auto;}
.m-footer{display: flex; height: 40px;box-shadow: 0 -5px 5px rgba(0,0,0,0.1);border-top: 1px solid #ddd; text-align: center;z-index: 9;}

3.拆分组件

组件化依然是主体,header和footer两个组件。这个两个组件还是有很多文章可以做的,可以简单讲一下先不展开,先把二级路由讲清楚!

<template>
  <div class="m-wrap">
    <Header></Header>
    <router-view></router-view>
    <Footer></Footer>
  </div>
</template>

4.二级路由

所谓二级,就是普通的页面里包含小页面。其实多少级都是一样的,学会二级就都学会了。router-view是页面渲染的地方,是可以嵌套。路由配置是数组对象,也可以通过children字段进行嵌套,和router-view形成对应。

语法格式:

const routes = [
  {
    path: '/index',
    component: Index,
    children: [{
      path: '/index/home',
      component: () => import('../views/Home')
    }]
  }
]

授课思路

在这里插入图片描述

案例作业

1.登录成功后跳转新页面
2.新页面是三段式,头和尾是组件,中间是二级路由
3.研究路由懒加载
4.预习vuex

五、引入VueX

课程目标

  1. 通过案例引入VueX
  2. 让学生掌握VueX的基本语法
  3. 了解路由守卫

知识点

1.VueX

VueX是难点、重点。可以多举一些有趣的例子加深学生理解。仓库好比是杂货铺,又好比是手机里的应用商店等等。还可以带着学生安装一下VueX在chrome里的插件,这样学习起来更直观些。这里把Vuex的最精简、最易上手的语法展示一下,学会这些就足够了,不必全面学习官方Api。

语法格式:

import Vue from 'vue'
import Vuex from 'vuex'
import Api from '../api'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    title: '小米书城'
  },
  mutations: {
    setState(state, payload) {
      state[payload.key] = payload.value
    }
  },
  actions: {
    getList({ commit }) {
      Api.list().then(res => {
        if (res.code === 200) {
          commit({ type: 'setState', key: 'list', value: res.data })
        }
      })
    }
  }
})

title是仓库里的值,mutations里的setState方法负责修改仓库里的值,actions只是一个写异步函数的地方。

2.在Header里获取仓库里的值

通过计算属性获取,语法很简单,获取之后的使用也和data里的数据完全一样。当然也可以使用辅助函数获取,不过我不喜欢使用。

<template>
  <div class="m-header">
    {{title}}
  </div>
</template>

<script>
export default {
  computed: {
    title() {
      return this.$store.state.title
    }
  }
}
</script>

3.完善Footer组件

Footer组件包含首页、书包和我的三个菜单,除了文字外,应该添加上小图标,这样会更加有趣!之前的课程中已经封装了Icon组件在密码输入框里使用,这里是第二次是否Icon组件,Icon组件还会再接下来的课程中继续使用。

<template>
  <div class="m-footer">
    <router-link to="/index/home" class="m-footer-item">
      <Icon name="shouye" class="m-footer-icon"></Icon>
      <div class="m-footer-text">首页</div>
    </router-link>
    <router-link to="/index/my_books" class="m-footer-item">
      <Icon name="shubao" class="m-footer-icon"></Icon>
      <div class="m-footer-text">书包</div>
    </router-link>
    <router-link to="/index/me" class="m-footer-item">
      <Icon name="wodedangxuan" class="m-footer-icon"></Icon>
      <div class="m-footer-text">我的</div>
    </router-link>
  </div>
</template>

4.点击底部菜单动态修改Header里的文本

如果直接在底部菜单上面绑定click事件,点击的时候修改仓库里的title值,确实可以实现这个需求,但是刷新后会出bug,你可以试一试!原因很简单,store里的数据刷新后就回到初始值了。这里推荐一个新的解决方案,通过路由守卫实现!

语法格式:

  children: [{
    path: '/index/home',
    component: () => import('../views/Home'),
    meta: {
      title: '小米书城'
    }
  }, {
    path: '/index/my_books',
    component: () => import('../views/MyBooks'),
    meta: {
      title: '我的书包'
    }
  }, {
    path: '/index/me',
    component: () => import('../views/Me'),
    meta: {
      title: '个人中心'
    }
  }]

router.beforeEach((to, from, next) => {
  store.commit({ type: 'setState', key: 'title', value: to.meta.title })
  next()
})

授课思路

在这里插入图片描述

案例作业

1.学习VueX语法
2.实现底部菜单切换顶部Header文本
3.学习Icon组件
4.学习actions

在这里插入图片描述

六、初识楼层

课程目标

  1. 深入掌握VueX
  2. 深入掌握组件化思想
  3. 搭建楼层页面框架

知识点

1.VueX之actions

actions是写异步函数的地方,通过异步请求获取数据。函数的第一个参数是context,context里包含state、commit等。通过commit把获取的数据提交给mutations,然后更新state。这是VueX给咱们设计出来的一套语法格式。

import Vue from 'vue'
import Vuex from 'vuex'
import Api from '../api'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    title: '小米书城',
    list: [],
  },
  mutations: {
    setState(state, payload) {
      state[payload.key] = payload.value
    }
  },
  actions: {
    getList({ commit }) {
      Api.list().then(res => {
        if (res.code === 200) {
          commit({ type: 'setState', key: 'list', value: res.data })
        }
      })
    }
  }
})

actions里的函数并不会自动触发,可以在组件挂载后这个生命周期里手动触发。

  mounted() {
    this.$store.dispatch({ type: 'getList' })
  }

2.初始楼层页面

通过计算属性可以获取仓库里的数据。无论是哪个组件,都可以随时随地的获得仓库里的数据。我们在首页里使用两个组件,一个是侧边栏,另一个是列表。点击侧边栏的菜单可以控制列表滚动到对应的数据位置,同时右侧数据滚动时,侧边栏菜单的高亮也会有所变化,其实就是楼层的效果!

这是首页的代码,可以清醒的看到使用了侧边栏组件和列表组件,以及在挂载完这个生命周期派发了getList这个action去请求数据

<template>
  <div class="m-main m-home">
    <Sidebar></Sidebar>
    <List></List>
  </div>
</template>

<script>
import Sidebar from '../components/Sidebar'
import List from '../components/List'

export default {
  components: {
    Sidebar,
    List
  },
  mounted() {
    this.$store.dispatch({ type: 'getList' })
  }
}
</script>

3.侧边栏组件

侧边栏里有菜单,菜单的数据需要从仓库里获得,仓库里的list数据是一个数组。

在这里插入图片描述

我们需要使用v-for把数组里的第一层的title字段渲染成菜单,并给每一个菜单菜单绑定点击事件,通过点击事件控制仓库里的currentId,该字段和菜单的高亮有关。

<template>
  <div class="m-sidebar">
    <div 
      v-for="item in list" 
      :key="item.id" 
      class="m-sidebar-item" 
      :class="currentId === item.id ? 'active' : ''"
      @click="handleNav(item.id)">{{item.title}}</div>
  </div>
</template>

<script>
let timer

export default {
  computed: {
    list() {
      return this.$store.state.list
    },
    currentId() {
      return this.$store.state.currentId
    }
  },
  methods: {
    handleNav(id) {
      this.$store.commit({ type: 'setState', key: 'currentId', value: id })
    }
  }
}
</script>

4.List组件

List组件这节课并不讲太多内容,先渲染一下列表即可!渲染的时候需要注意一下这是一个两层的数组,第一层是分类,第二层是分类对应的列表。

<template>
  <div class="m-list">
    <div v-for="categroy in list" :key="categroy.id" :id="categroy.id" class="m-category js-category">
      <div class="m-category-title">{{categroy.title}}</div>
      <div v-for="book in categroy.list" :key="book.id" class="m-list-item">
        <div class="m-info">
          {{book.title}}
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    list() {
      return this.$store.state.list
    }
  },
}
</script>

授课思路

在这里插入图片描述

案例作业

1.编辑actions函数并把返回的数据保存到仓库
2.搭建楼层页面框架
3.开发侧边栏组件并添加高亮
4.开发列表组件

在这里插入图片描述

原文地址:https://www.cnblogs.com/xutongbao/p/14876300.html