Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目

day1

接口文档地址:https://www.apifox.cn/apidoc/project-934563/api-20384515

一、项目功能演示

1.目标

启动准备好的代码,演示移动端面经内容,明确功能模块

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第1张图片

2.项目收获

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第2张图片

二、项目创建目录初始化

vue-cli 建项目

1.安装脚手架 (已安装)

npm i @vue/cli -g

2.创建项目

vue create hm-vant-h5
  • 选项
Vue CLI v5.0.8
? Please pick a preset:
  Default ([Vue 3] babel, eslint)
  Default ([Vue 2] babel, eslint)
> Manually select features     选自定义
  • 手动选择功能

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第3张图片

  • 选择vue的版本
  3.x
> 2.x
  • 是否使用history模式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 选择css预处理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 选择eslint的风格 (eslint 代码规范的检验工具,检验代码是否符合规范)
  • 比如:const age = 18; => 报错!多加了分号!后面有工具,一保存,全部格式化成最规范的样子

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第4张图片

  • 选择校验的时机 (直接回车)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 选择配置文件的生成方式 (直接回车)

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第5张图片

  • 是否保存预设,下次直接使用? => 不保存,输入 N

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第6张图片

  • 等待安装,项目初始化完成

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第7张图片

  • 启动项目
npm run serve

三、ESlint代码规范及手动修复

代码规范:一套写代码的约定规则。例如:赋值符号的左右是否需要空格?一句结束是否是要加;?…

没有规矩不成方圆

ESLint:是一个代码检查工具,用来检查你的代码是否符合指定的规则(你和你的团队可以自行约定一套规则)。在创建项目时,我们使用的是 JavaScript Standard Style 代码风格的规则。

1.JavaScript Standard Style 规范说明

建议把:https://standardjs.com/rules-zhcn.html 看一遍,然后在写的时候, 遇到错误就查询解决。

下面是这份规则中的一小部分:

  • 字符串使用单引号 – 需要转义的地方除外
  • 无分号 – 这没什么不好。不骗你!
  • 关键字后加空格 if (condition) { ... }
  • 函数名后加空格 function name (arg) { ... }
  • 坚持使用全等 === 摒弃 == 一但在需要检查 null || undefined 时可以使用 obj == null

2.代码规范错误

如果你的代码不符合standard的要求,eslint会跳出来刀子嘴,豆腐心地提示你。

下面我们在main.js中随意做一些改动:添加一些空行,空格。

import Vue from 'vue'
import App from './App.vue'

import './styles/index.less'
import router from './router'
Vue.config.productionTip = false

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


按下保存代码之后:

你将会看在控制台中输出如下错误:

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第8张图片

eslint 是来帮助你的。心态要好,有错,就改。

3.手动修正

根据错误提示来一项一项手动修正。

如果你不认识命令行中的语法报错是什么意思,你可以根据错误代码(func-call-spacing, space-in-parens,…)去 ESLint 规则列表中查找其具体含义。

打开 ESLint 规则表,使用页面搜索(Ctrl + F)这个代码,查找对该规则的一个释义。

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第9张图片

四、通过eslint插件来实现自动修正

  1. eslint会自动高亮错误显示
  2. 通过配置,eslint会自动帮助我们修复错误
  • 如何安装

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 如何配置
// 当保存的时候,eslint自动帮我们修复错误
"editor.codeActionsOnSave": {
    "source.fixAll": true
},
// 保存代码,不自动格式化
"editor.formatOnSave": false
  • 注意:eslint的配置文件必须在根目录下,这个插件才能才能生效。打开项目必须以根目录打开,一次打开一个项目
  • 注意:使用了eslint校验之后,把vscode带的那些格式化工具全禁用了 Beatify

settings.json 参考

{
    "window.zoomLevel": 2,
    "workbench.iconTheme": "vscode-icons",
    "editor.tabSize": 2,
    "emmet.triggerExpansionOnTab": true,
    // 当保存的时候,eslint自动帮我们修复错误
    "editor.codeActionsOnSave": {
        "source.fixAll": true
    },
    // 保存代码,不自动格式化
    "editor.formatOnSave": false
}

五、调整初始化目录结构

强烈建议大家严格按照老师的步骤进行调整,为了符合企业规范

为了更好的实现后面的操作,我们把整体的目录结构做一些调整。

目标:

  1. 删除初始化的一些默认文件
  2. 修改没删除的文件
  3. 新增我们需要的目录结构

1.删除文件

  • src/assets/logo.png
  • src/components/HelloWorld.vue
  • src/views/AboutView.vue
  • src/views/HomeView.vue

2.修改文件

main.js 不需要修改

router/index.js

删除默认的路由配置

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
]

const router = new VueRouter({
  routes
})

export default router

App.vue

<template>
  <div id="app">
    <router-view/>
  div>
template>

3.新增目录

  • src/api 目录
    • 存储接口模块 (发送ajax请求接口的模块)
  • src/utils 目录
    • 存储一些工具模块 (自己封装的方法)

目录效果如下:

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第10张图片

六、vant组件库及Vue周边的其他组件库

组件库:第三方封装好了很多很多的组件,整合到一起就是一个组件库。

https://vant-contrib.gitee.io/vant/v2/#/zh-CN/

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第11张图片

比如日历组件、键盘组件、打分组件、登录组件等

组件库并不是唯一的,常用的组件库还有以下几种:

pc: element-ui element-plus iview ant-design

移动:vant-ui Mint UI (饿了么) Cube UI (滴滴)

七、全部导入和按需导入的区别

目标:明确 全部导入按需导入 的区别

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第12张图片

区别:

1.全部导入会引起项目打包后的体积变大,进而影响用户访问网站的性能

2.按需导入只会导入你使用的组件,进而节约了资源

八、全部导入

  • 安装vant-ui
yarn add vant@latest-v2
// 或者 npm i vant@latest-v2
  • 在main.js中
import Vant from 'vant';
import 'vant/lib/index.css';
// 把vant中所有的组件都导入了
Vue.use(Vant)
  • 即可使用
主要按钮
信息按钮

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第13张图片

vant-ui提供了很多的组件,全部导入,会导致项目打包变得很大。

九、按需导入

  • 安装vant-ui
npm i vant@latest-v2  或  yarn add vant@latest-v2
  • 安装一个插件
npm i babel-plugin-import -D
  • babel.config.js中配置
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
}
  • 按需加载,在main.js
import { Button, Icon } from 'vant'

Vue.use(Button)
Vue.use(Icon)
  • app.vue中进行测试
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
  • 把引入组件的步骤抽离到单独的js文件中比如 utils/vant-ui.js
import { Button, Icon } from 'vant'

Vue.use(Button)
Vue.use(Icon)

main.js中进行导入

// 导入按需导入的配置文件
import '@/utils/vant-ui'

十、项目中的vw适配

官方说明:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/advanced-usage

yarn add postcss-px-to-viewport@1.1.1 -D
  • 项目根目录, 新建postcss的配置文件postcss.config.js
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 375,
    },
  },
};

viewportWidth:设计稿的视口宽度

  1. vant-ui中的组件就是按照375的视口宽度设计的
  2. 恰好面经项目中的设计稿也是按照375的视口宽度设计的,所以此时 我们只需要配置375就可以了
  3. 如果设计稿不是按照375而是按照750的宽度设计,那此时这个值该怎么填呢?

十一、路由配置-一级路由

但凡是单个页面,独立展示的,都是一级路由

路由设计:

  • 登录页 (一级) Login
  • 注册页(一级) Register
  • 文章详情页(一级) Detail
  • 首页(一级) Layout
    • 面经(二级)Article
    • 收藏(二级)Collect
    • 喜欢(二级)Like
    • 我的(二级)My

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第14张图片

一级路由

router/index.js配置一级路由, 一级views组件于准备好的中直接 CV 即可

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '@/views/Login'
import Register from '@/views/Register'
import Detail from '@/views/Detail'
import Layout from '@/views/Layout'
Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    { path: '/login', component: Login },
    { path: '/register', component: Register },
    { path: '/article/:id', component: Detail },
    {
      path: '/',
      component: Layout
    }
  ]
})
export default router

清理 App.vue




十二、路由配置-tabbar标签页

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第15张图片

https://vant-contrib.gitee.io/vant/v2/#/zh-CN/tabbar

vant-ui.js 引入组件

import { Button, Icon, Tabbar, TabbarItem } from 'vant'
Vue.use(Tabbar)
Vue.use(TabbarItem)

layout.vue

  1. 复制官方代码
  2. 修改显示文本及显示的图标

十三、路由配置-配置主题色

整体网站风格,其实都是橙色的,可以通过变量覆盖的方式,制定主题色

https://vant-contrib.gitee.io/vant/v2/#/zh-CN/theme

babel.config.js 制定样式路径

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      // 指定样式路径
      style: (name) => `${name}/style/less`
    }, 'vant']
  ]
}

vue.config.js 覆盖变量

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  css: {
    loaderOptions: {
      less: {
        lessOptions: {
          modifyVars: {
            // 直接覆盖变量
            'blue': '#FA6D1D',
          },
        },
      },
    },
  },
})

重启服务器生效!

十四、路由配置-二级路由

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第16张图片

1.router/index.js配置二级路由

在准备好的代码中去复制对应的组件即可

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '@/views/Login'
import Register from '@/views/Register'
import Detail from '@/views/Detail'
import Layout from '@/views/Layout'

import Like from '@/views/Like'
import Article from '@/views/Article'
import Collect from '@/views/Collect'
import User from '@/views/User'
Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    { path: '/login', component: Login },
    { path: '/register', component: Register },
    { path: '/article/:id', component: Detail },
    { 
      path: '/',
      component: Layout,
      redirect: '/article',
      children: [
        { path: 'article', component: Article },
        { path: 'like', component: Like },
        { path: 'collect', component: Collect },
        { path: 'user', component: User }
      ]
    }
  ]
})

export default router

2.layout.vue 配置路由出口, 配置 tabbar


十五、登录静态布局

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第17张图片

使用组件

  • van-nav-bar
  • van-form
  • van-field
  • van-button

vant-ui.js 注册

import Vue from 'vue'
import {
  NavBar,
  Form,
  Field
} from 'vant'
Vue.use(NavBar)
Vue.use(Form)
Vue.use(Field)

Login.vue 使用




login.vue添加 router-link 标签(跳转到注册)


login.vue调整样式


十六、登录表单中的细节分析

  1. @submit事件:当点击提交按钮时会自动触发submit事件
  2. v-model双向绑定:会自动把v-model后面的值和文本框中的值进行双向绑定
  3. name属性:收集的key的值,要和接口文档对应起来
  4. label:输入的文本框的title
  5. :rules: 表单的校验规则
  6. placeholder: 文本框的提示语

十七、注册静态布局

Register.vue






十八、request模块 - axios封装

接口文档地址:https://apifox.com/apidoc/project-934563/api-20384515

基地址:http://interview-api-t.itheima.net/h5/

目标:将 axios 请求方法,封装到 request 模块

我们会使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址,请求响应拦截器等等)

一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用

  1. 安装 axios
npm i axios
  1. 新建 utils/request.js 封装 axios 模块

    利用 axios.create 创建一个自定义的 axios 来使用

    http://www.axios-js.com/zh-cn/docs/#axios-create-config

/* 封装axios用于发送请求 */
import axios from 'axios'

// 创建一个新的axios实例
const request = axios.create({
  baseURL: 'http://interview-api-t.itheima.net/h5/',
  timeout: 5000
})

// 添加请求拦截器
request.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response.data
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})

export default request
  1. 注册测试
// 监听表单的提交,形参中:可以获取到输入框的值
async onSubmit (values) {
  console.log('submit', values)
  const res = await request.post('/user/register', values)
  console.log(res)
}

十九、封装api接口 - 注册功能

1.目标:将请求封装成方法,统一存放到 api 模块,与页面分离

2.原因:

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第18张图片

以前的模式:

  • 页面中充斥着请求代码,
  • 可阅读性不高
  • 相同的请求没有复用请求没有统一管理

3.期望:

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第19张图片

  • 请求与页面逻辑分离
  • 相同的请求可以直接复用请求
  • 进行了统一管理

4.具体实现

新建 api/user.js 提供注册 Api 函数

import request from '@/utils/request'

// 注册接口
export const register = (data) => {
  return request.post('/user/register', data)
}

register.vue页面中调用测试

methods: {
  async onSubmit (values) {
    // 往后台发送注册请求了
    await register(values)
    alert('注册成功')
    this.$router.push('/login')
  }
}

二十、toast 轻提示

https://vant-contrib.gitee.io/vant/v2/#/zh-CN/toast

两种使用方式

  1. 组件内js文件内 导入,调用
import { Toast } from 'vant';
Toast('提示内容');
  1. **组件内 **通过this直接调用

main.js

import { Toast } from 'vant';
Vue.use(Toast)
this.$toast('提示内容')

代码演示

this.$toast.loading({
    message:'拼命加载中...',
    forbidClick:true
})
try{
    await register(values)
    this.$toast.success('注册成功')
    this.$router.push('/login')
}catch(e){
    this.$toast.fail('注册失败')
}

二十一、响应拦截器统一处理错误提示

响应拦截器是咱们拿到数据的第一个“数据流转站”

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第20张图片

import { Toast } from 'vant'

...

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response.data
}, function (error) {
  if (error.response) {
    // 有错误响应, 提示错误提示
    Toast(error.response.data.message)
  }
  // 对响应错误做点什么
  return Promise.reject(error)
})

二十二、封装api接口 - 登录功能

api/user.js 提供登录 Api 函数

// 登录接口
export const login = (data) => {
  return request.post('/user/login', data)
}

login.vue 登录功能

import { login } from '@/api/user'

methods: {
  async onSubmit (values) {
    const { data } = await login(values)
    this.$toast.success('登录成功')
    localStorage.setItem('vant-mobile-exp-token', data.token)
    this.$router.push('/')
  }
}

二十三、local模块 - 本地存储

新建 utils/storage.js

const KEY = 'vant-mobile-exp-token'

// 直接用按需导出,可以导出多个
// 获取
export const getToken = () => {
  return localStorage.getItem(KEY)
}

// 设置
export const setToken = (newToken) => {
  localStorage.setItem(KEY, newToken)
}

// 删除
export const delToken = () => {
  localStorage.removeItem(KEY)
}

登录完成存储token到本地

import { login } from '@/api/user'
import { setToken } from '@/utils/storage'

methods: {
  async onSubmit (values) {
    const { data } = await login(values)
    setToken(data.token)
    this.$toast.success('登录成功')
    this.$router.push('/')
  }
}

day2

一、全局前置守卫-语法认识

这个 面经移动端 项目,只对 登录用户 开放,如果未登录,一律拦截到登录

  1. 如果访问的是 首页, 无token, 拦走

  2. 如果访问的是 列表页,无token, 拦走

  3. 如果访问的是 详情页,无token, 拦走

分析:哪些页面,是不需要登录,就可以访问的! => 注册登录 (白名单 - 游客可以随意访问的)

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第21张图片

路由导航守卫 - 全局前置守卫

  • 访问的路径一旦被路由规则匹配到,都会先经过全局前置守卫

  • 只有全局前置守卫放行,才会真正解析渲染组件,才能看到页面内容

router/index.js

router.beforeEach((to, from, next) => {
  // 1. to   往哪里去, 到哪去的路由信息对象  
  // 2. from 从哪里来, 从哪来的路由信息对象
  // 3. next() 是否放行
  //    如果next()调用,就是放行
  //    next(路径) 拦截到某个路径页面
})

二、全局前置守卫-访问拦截处理

拦截或放行的关键点? → 用户是否有登录权证 token

核心逻辑:

  1. 判断用户有没有token, 有token, 直接放行 (有身份的人,想去哪就去哪~)
  2. 没有token(游客),如果是白名单中的页面,直接放行
  3. 否则,无token(游客),且在访问需要权限访问的页面,直接拦截到登录

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第22张图片

// 全局前置守卫:
// 1. 所有的路由一旦被匹配到,在真正渲染解析之前,都会先经过全局前置守卫
// 2. 只有全局前置守卫放行,才能看到真正的页面

// 任何路由,被解析访问前,都会先执行这个回调
// 1. from 你从哪里来, 从哪来的路由信息对象
// 2. to   你往哪里去, 到哪去的路由信息对象
// 3. next() 是否放行,如果next()调用,就是放行 => 放你去想去的页面
//    next(路径) 拦截到某个路径页面
import { getToken } from '@/utils/storage'

const whiteList = ['/login', '/register'] // 白名单列表,记录无需权限访问的所有页面

router.beforeEach((to, from, next) => {
  const token = getToken()
  // 如果有token,直接放行
  if (token) {
    next()
  } else {
    // 没有token的人, 看看你要去哪
    // (1) 访问的是无需授权的页面(白名单),也是放行
    //     就是判断,访问的地址,是否在白名单数组中存在 includes
    if (whiteList.includes(to.path)) {
      next()
    } else {
      // (2) 否则拦截到登录
      next('/login')
    }
  }
})

三、面经列表-认识Cell组件-准备基础布局

1.认识静态结构Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第23张图片

2.注册组件:

  • van-cell
import Vue from 'vue'
import { Cell } from 'vant'
Vue.use(Cell)

3.静态结构 Article.vue






四、封装 ArticleItem 组件

说明:每个文章列表项,其实就是一个整体,封装成一个组件 → 可阅读性 & 复用性

步骤:

  • 新建 components/ArticleItem.vue 组件,贴入内容
  • 注册成全局组件
  • Article.vue 页面中应用

新建 components/ArticleItem.vue 组件







注册成全局组件使用

import ArticleItem from '@/components/ArticleItem.vue'
Vue.component('ArticleItem', ArticleItem)

Article.vue页面中


五、封装 api 接口-获取文章列表数据

接口:https://apifox.com/apidoc/project-934563/api-20384521

1.新建 api/article.js 提供接口函数

import request from '@/utils/request'

export const getArticles = (obj) => {
  return request.get('/interview/query', {
    params: {
      current: obj.current,
      sorter: obj.sorter,
      pageSize: 10
    }
  })
}

2.页面中调用测试

import { getArticles } from '@/api/article'
export default {
  name: 'article-page',
  data () {
    return {

    }
  },
  async created () {
    const res = await getArticles({
      current: 1,
      sorter: 'weight_desc'
    })
    console.log(res)
  },
  methods: {

  }
}

3.发现 401 错误, 通过 headers 携带 token

注意:这个token,需要拼上前缀 Bearer token标识前缀

// 封装接口,获取文章列表
export const getArticles = (obj) => {
  const token = getToken()

  return request.get('/interview/query', {
    params: {
      current: obj.current, // 当前页
      pageSize: 10, // 每页条数
      sorter: obj.sorter // 排序字段 =>  传"weight_desc" 获取 推荐, "不传" 获取 最新
    },
    headers: {
      // 注意 Bearer 和 后面的空格不能删除,为后台的token辨识
      Authorization: `Bearer ${token}`
    }
  })
}

六、请求拦截器-携带 token

utils/request.js

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第24张图片

每次自己携带token太麻烦,通过请求拦截器统一携带token更方便

import { getToken } from './storage'

// 添加请求拦截器
request.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  const token = getToken()
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

七、响应拦截器-处理token过期

说明:token 是有过期时间的 (6h),一旦 过期 或 失效 就无法正确获取到数据!

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第25张图片

utils/request.js

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response.data
}, function (error) {
  if (error.response) {
    // 有错误响应, 提示错误提示
    if (error.response.status === 401) {
      delToken()
      router.push('/login')
    } else {
      Toast(error.response.data.message)
    }
  }
  // 对响应错误做点什么
  return Promise.reject(error)
})

八、面经列表-动态渲染列表

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第26张图片

article.vue

存储数据

import {getArticles} from '@/api/article'
data () {
  return {
    list: [],
    current: 1,
    sorter: 'weight_desc'
  }
},
async created () {
  const { data } = await getArticles({
    current: this.current,
    sorter: this.sorter
  })
  this.list = data.data.rows
},

v-for循环展示


子组件接收渲染






九、面经列表-响应拦截器-简化响应

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response.data
}, function (error) {
  // console.log(error)
  // 有错误响应,后台正常返回了错误信息
  if (error.response) {
    if (error.response.status === 401) {
      // 清除掉无效的token
      delToken()
      // 拦截到登录
      router.push('/login')
    } else {
      // 有错误响应,提示错误消息
      // this.$toast(error.response.data.message)
      Toast(error.response.data.message)
    }
  }
  // 对响应错误做点什么
  return Promise.reject(error)
})

Login.vue

setToken(data.token)

Article.vue

async created () {
  // 获取推荐的,第1页的10条数据
  const res = await getArticles({
    current: this.current,
    sorter: this.sorter
  })
  this.list = res.data.rows
},

十、面经列表-分页加载更多

https://vant-contrib.gitee.io/vant/v2/#/zh-CN/list


  


data () {
  return {
    list: [],
    current: 1,
    sorter: 'weight_desc',
    loading: false,
    finished: false
  }
},
    
methods: {
  async onLoad () {
    const { data } = await getArticles({
      current: this.current,
      sorter: this.sorter
    })
    this.list = data.rows
  }
}

加载完成,重置 loading, 累加数据,处理 finished

async onLoad () {
  const { data } = await getArticles({
    current: this.current,
    sorter: this.sorter
  })
  this.list.push(...data.rows)
  this.loading = false
  this.current++

  if (this.current > data.pageTotal) {
    this.finished = true
  }
}

十一、面经列表-推荐和更新

1.切换推荐和最新 获取不同的数据

2.切换推荐和最新 点击的tab页签应该高亮

Vue2+Vue3基础入门到实战项目(前接六 副线一)—— 面经 项目_第27张图片

article.vue

推荐
最新

提供methods

changeSorter (value) {
  this.sorter = value

  // 重置所有条件
  this.current = 1 // 排序条件变化,重新从第一页开始加载
  this.list = []
  this.finished = false // finished重置,重新有数据可以加载了
  // this.loading = false

  // 手动加载更多
  // 手动调用了加载更多,也需要手动将loading改成true,表示正在加载中(避免重复触发)
  this.loading = true
  this.onLoad()
}

十二、面经详情-动态路由传参-请求渲染

1.跳转路由传参

核心知识点:跳转路由传参

准备动态路由 (已准备)

const router = new VueRouter({
  routes: [
	...,
    { path: '/article/:id', component: Detail },
    {
      path: '/',
      component: Layout,
      redirect: '/article',
      children: [
       	...
      ]
    }
  ]
})

点击跳转 article.vue


页面中获取参数

this.$route.params.id

2.动态渲染 (页面代码准备)

准备代码:

导入图标组件:

Vue.use(Icon)

静态结构:







3.代码实现

3.1封装api接口函数

api/article.js

export const getArticleDetail = (id) => {
  return request.get('interview/show', {
    params: {
      id
    }
  })
}

3.2动态渲染

Detail.vue




十三、面经详情-点赞收藏

封装准备接口

api/article.js

export const updateLike = (id) => {
  return request.post('interview/opt', {
    id,
    optType: 1 // 喜欢
  })
}

export const updateCollect = (id) => {
  return request.post('interview/opt', {
    id,
    optType: 2 // 收藏
  })
}

Detail.vue

调用接口实现点赞收藏






十四、我的收藏 (实战)

提供api方法

  • page: 表示当前页
  • optType:2 表示获取我的收藏数据

api/article.js

// 获取我的收藏
export const getArticlesCollect = (obj) => {
  return request.get('/interview/opt/list', {
    params: {
      page: obj.page, // 当前页
      pageSize: 5, // 可选
      optType: 2 // 表示收藏
    }
  })
}

collect.vue准备结构






十五、我的喜欢 (快速实现)

准备api函数

  • page: 表示当前页
  • optType:1 表示获取我的喜欢数据

api/article.js

// 获取我的喜欢
export const getArticlesLike = (obj) => {
  return request.get('/interview/opt/list', {
    params: {
      page: obj.page, // 当前页
      pageSize: 5, // 可选
      optType: 1 // 表示喜欢
    }
  })
}

Like.vue请求渲染






十六、个人中心 (快速实现)

准备代码:

1 注册组件

import {
  Grid,
  GridItem,
  CellGroup
} from 'vant'

Vue.use(Grid)
Vue.use(GridItem)
Vue.use(CellGroup)

2 准备api

api/user.js

// 获取用户信息
export const getUserInfo = () => {
  return request('/user/currentUser')
}

3 页面调用渲染






十七、打包发布

vue脚手架只是开发过程中,协助开发的工具,当真正开发完了 => 脚手架不参与上线

参与上线的是 => 打包后的源代码

打包:

  • 将多个文件压缩合并成一个文件
  • 语法降级
  • less sass ts 语法解析, 解析成css

打包后,可以生成,浏览器能够直接运行的网页 => 就是需要上线的源码!

打包命令

vue脚手架工具已经提供了打包命令,直接使用即可。

yarn build

在项目的根目录会自动创建一个文件夹dist,dist中的文件就是打包后的文件,只需要放到服务器中即可。

配置publicPath

module.exports = {
  // 设置获取.js,.css文件时,是以相对地址为基准的。
  // https://cli.vuejs.org/zh/config/#publicpath
  publicPath: './'
}

十八、路由懒加载

路由懒加载 & 异步组件, 不会一上来就将所有的组件都加载,而是访问到对应的路由了,才加载解析这个路由对应的所有组件

官网链接:https://router.vuejs.org/zh/guide/advanced/lazy-loading.html#%E4%BD%BF%E7%94%A8-webpack

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

const Detail = () => import('@/views/detail')
const Register = () => import('@/views/register')
const Login = () => import('@/views/login')
const Article = () => import('@/views/article')
const Collect = () => import('@/views/collect')
const Like = () => import('@/views/like')
const User = () => import('@/views/user')

PS: 如果想要手机上看到效果,可以将打包后的代码,上传到 gitee,利用 git pages 进行展示

你可能感兴趣的:(Vue学习,前端,javascript,vue.js,less,npm)