黑马头条案例

项目准备

前置需安装 vue脚手架 (如果你还没有安装 VueCLI,请执行下面的命令安装或是升级)

PowerShell
npm install --global @vue/cli

黑马头条接口文档下载

环境安装

在命令行中输入以下命令创建 Vue 项目

PowerShell
vue create toutiao-m

进行流程安装

PowerShell
# 选择Vue脚手架版本 - 手动选择特性,支持更多自定义选项
? Please pick a preset:
  Default ([Vue 2] babel, eslint)
  Default (Vue 3 Preview) ([Vue 3] babel, eslint)
> Manually select features

# 选择需要安装的插件 - 勾选 (*) 项目需要安装的插件
# |Babel:es6 转 es5
# |Router:路由
# |Vuex:数据容器,存储共享数据
# |CSS Pre-processors:CSS 预处理器,后面会提示你选择 less、sass、stylus 等
# |Linter / Formatter:代码格式校验
? Please pick a preset: Manually select features
? Check the features needed for your project:
 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 (*) Router
 (*) Vuex
 (*) CSS Pre-processors
>(*) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing

# 是否使用 history 路由模式(兼容差) n 不使用
? Use history mode for router? 
(Requires proper server setup for index fallback in production) (Y/n) n

# 选择 CSS 预处理器,这里选择 Less
? Pick a CSS pre-processor 
(PostCSS, Autoprefixer and CSS Modules are supported by default):
  Sass/SCSS (with dart-sass)
  Sass/SCSS (with node-sass)
> Less

# 选择校验工具,这里选择 ESLint + [Standard config](https://standardjs.com/)
? Pick a linter / formatter config:
  ESLint with error prevention only
  ESLint + Airbnb config
> ESLint + Standard config
  ESLint + Prettier
  
# 选择在什么时机下触发代码格式校验:
# Lint on save:每当保存文件的时候
# Lint and fix on commit:每当执行 `git commit` 提交的时候
? Pick additional lint features:
 (*) Lint on save
>(*) Lint and fix on commit

# Babel、ESLint 等工具会有一些额外的配置文件,询问将配置文件写入的位置:
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files
  In package.json
  
# 是否需要将刚才选择的一系列配置保存 选择 n
? Save this as a preset for future projects? (y/N) N

输入上述操作安装完成后,运行项目,环境安装完成

PowerShell
# 进入你的项目目录
cd toutiao-webapp

# 启动开发服务
npm run serve
加入Git管理

制作项目一开始时,通常建议对项目的改动进行备份。git 就适合对代码的版本进行管理。
如若需要进行多人协作,就需要创建远程仓库进行托管。

>>> 关于git 的知识

创建远程仓库

前置需要安装 git管理工具,并绑定远程仓库账号

远程仓库推荐有 GitHub码云Ciding

  • 以GitHub为例,创建关于本项目的区域
%title插图%num
  • 添加新项目仓库
%title插图%num
  • 配置项目名称、项目描述等
%title插图%num

本地 Git仓库 上传到 远程仓库

Vue脚手架默认给我们部署了Git仓库,并预先做了一次提交,因此 git init 操作可以忽略

PowerShell
# 创建本地仓库
git init

# 将文件添加到暂存区
git add 文件

# 提交历史记录
git commit -m "提交日志"

# 添加远端仓库地址
git remote add origin 你的远程仓库地址

# 重命名当前默认的 master 分支为 main
git branch -M main

# 推送提交 上传代码到该仓库节点 -u 是保存上传目标信息
git push -u origin main

# 再次提交 [需要有上述的 -u 参数]
git push

效果展示

%title插图%num
调整初始目录结构

默认生成的目录不满足我们的开发需求,需要进行一些自定义操作

  • 删除部分初始化默认文件
  • 新增需要的目录结构

修改 App.vue 文件

App.vue
<template>
  <div id="app">
    <h1>黑马头条</h1>
    <router-view />
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

修改 router/index.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

删除文件

  • src/views/About.vue
  • src/views/Home.vue
  • src/components/HelloWorld.vue
  • src/assets/logo.png

创建以下几个目录

  • src/api 目录
    • 存储接口封装
  • src/utils 目录
    • 存储一些工具模块
  • src/styles 目录
    • index.less 文件,存储全局样式
    • main.js 中加载全局样式 import './styles/index.less'

完成后的目录结构为

├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
└── src
├── api
├── App.vue
├── assets
├── components
├── main.js
├── router
├── utils
├── styles
├── store
└── views

%title插图%num
导入图标素材

上传图标

通过将素材中的svg图标上传至阿里图标库,得到了图标CSS样式文件

素材下载

导入配置

完成上传后,配置项目文件;

  • 在src/styles 下创建 icon.less 文件 (将阿里云图标库得到的 css 样式粘贴到此处)
  • 在 src/styles 下创建 index.less 文件

在 mian.js 中引入 less入口文件 (index.less)

mian.js
// 加载全局样式
import './styles/index.less'

在 index.less 文件中 引入 icon.less

index.less
// 加载图标库 图标样式
@import './icon.less';

替换 vue默认图标

vue的 项目图标在根目录的 public 文件夹内,同名文件直接覆盖即可。其他图片素材放置在 src\assets 下

素材下载

完成效果如下

%title插图%num
引入Vant组件库

Vant 是一个轻量、可靠的移动端 Vue组件库,开箱即用

支持移动项目中大多数使用场景的 Vue组件库,包含了组件所需的 js 和 css

相关文档:https://www.w3cschool.cn/vantlesson/
Vant组件库:gitee地址

特点

  • 提供60多个高质量组件,覆盖移动端各类场景
  • 性能极佳,组件平均体积不到1kb
  • 完善的中英文文档示例
  • 支持 Vue2 & Vue3
  • 支持按需引入和主题定制
%title插图%num

安装

下载包

PowerShell
# yarn 安装 vue2 版本的 vant
yarn add vant@latest-v2
# npm 安装 vue2 版本的 vant
npm install vant@latest-v2

全部导入

main.js
import Vue from 'vue';
// 引入 vant 所有组件
import Vant from 'vant';
import 'vant/lib/index.css';
// 全局注册 vant组件
Vue.use(Vant);

移动端REM适配

如果需要使用 rem 单位进行适配,推荐使用以下两个工具:

安装 lib-flexible

下载 lib-flexible 包

PowerShell
# yarn安装
yarn add amfe-flexible
# npm安装
npm i amfe-flexible

在 mian.js 中加载执行该模块

main.js
import 'amfe-flexible'

安装 postcss、postcss-pxtorem

下载包

PowerShell
# 下载 postcss postcss-pxtorem
yarn add postcss postcss-pxtorem@5.1.1

创建 postcss.config.js 文件到根目录 并写入以下参数

postcss.config.js
module.exports = {
    plugins: {
            // 能够把所有元素的 px 转换成 rem
            // rootValue 转换的基准值 (例如一个元素宽是75px 则转换成 rem 为 2rem)
        'postcss-pxtorem': {
            // lib-flexible 的 REM 适配方案为界面分成十份
           // 所以 rootValue 设置为设计稿的十分之一 (vant基于375写的)
           // rootValue 除了可以传递传入数值,还可以传入函数,并动态改变
            rootValue: 37.5,
            // 配置要转换的 CSS属性 (* 为所有,其他则表示只转该属性)
            propList: ['*'],
        }
    }
}

传入的vant样式默认是基于375的尺寸,因此 用户自定义样式的 px 尺寸 会与设计稿的 px 尺寸有所不同

如若需要让 postcss-pxtorem 进行一个判断。可采用下方代码:

它使得 用户 自定义CSS样式 与 vant组件样式 进行了一个区分。
当使用 vant 组件样式时使用 37.5 数值,使用自定义样式 75 来转换

JavaScript
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue({ file }) {
        return file.indexOf('vant') !== -1 ? 37.5 : 75
      },
      propList: ['*'],
    },
  },
};

使用上方策略,让 vant 灵活分配 rem 的比例。方便后续写 用户自定义样式 时,可以直接填入设计稿对应 px 数值。

JavaScript
<template>
  <div id="app">

    <!-- 路由的出口 -->
    <router-view />
    <h1>黑马头条</h1>
    <div>
      <!-- 粘贴字体图标 -->
      <i class="toutiao toutiao-shanchu"></i>
    </div>
    <van-button type="primary">主要按钮</van-button>
    <van-button type="success">成功按钮</van-button>
    <van-button type="default">默认按钮</van-button>
    <van-button type="warning">警告按钮</van-button>
    <van-button type="danger">危险按钮</van-button>
    <van-cell-group>
      <van-cell title="单元格" value="内容" />
      <van-cell title="单元格" value="内容" label="描述信息" />
    </van-cell-group>
    <div class="box">helloword</div>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style lang="less">
.box{
width: 555px;
height: 64px;
padding: 20px;
background-color: red;
}
</style>

效果展示

iPhone 12机型尺寸

%title插图%num
封装请求函数

安装

PowerShell
# npm 下载
npm i axios
# yarn 下载
yarn add axios

配置

项目根目录中创建 utils 文件夹,并在该文件夹中创建 request.js

request.js
import axios from 'axios';

const request = axios.create({
    // 创建基础路径
    baseURL: 'http://toutiao.itheima.net'
})
// 请求拦截器 暂无

// 响应拦截器 暂无

// 导出模块
export default request;

挂载

  • 方式1:(简单方便、但是不利于接口维护)

我们可以把请求对象挂载到 Vue.prototype 原型对象中,然后再组件中通过 this.xxx 直接访问

  • 方式2: (推荐)

我们把每一个请求都封装成每一个独立的功能函数,在需要的适合加载调用,这种做法更便于接口的管理和维护

登录注册

完成登录项目的功能,包括样式布局、接口请求、短信验证等功能

配置路由

在 src/views 文件夹中创建 login 文件夹,并在文件夹中创建 index.vue 文件

login/index.vue
<template>
    <div class="login-container">登录页面</div>
</template>

<script>
export default {
    name: 'LoginIndex',
    components: {},
    props: {},
}
</script>

<style></style>

修改 src/router 文件夹中 index.js 的路由配置,使用懒加载方式加载 login组件

router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

// 路由表
const routes = [
  {
    path: '/login',
    name: 'login',
    // [懒加载] vue 会自动加载 views/login 下的 index.vue
    component: () => import('@/views/login')
  }
] 

const router = new VueRouter({
  routes
})

export default router

效果展示

通过 /#/login 路由路径成功访问

%title插图%num
布局结构搭建

使用 vant 组件 实现登录界面的铺设

基础铺设

配置并调整 vant 组件中的 From表单 组件样式

Vue
<template>
  <div class="login-container">
    <!-- 导航栏 -->
    <van-nav-bar title="登录" />
    <!-- 登录表单 -->
    <van-form @submit="onSubmit">
      <van-field 
      name="用户名" 
      placeholder="请输入手机号" />
      <van-field 
      type="text" 
      name="密码" 
      placeholder="请输入验证码" />
      <div style="margin: 16px;">
        <van-button 
        block 
        type="info" 
        native-type="submit">登录</van-button>
      </div>
    </van-form>
  </div>
</template>

<script>
export default {
  name: "LoginIndex",
  components: {},
  props: {},
  methods: {
    onSubmit(values) {
      console.log("submit", values);
    }
  }
};
</script>

<style scoped lang="less"></style>

效果展示

%title插图%num

自定义化样式 – 全局样式

vant组件的基础配置不满足设计稿的需求,因此需要去 index.less 全局样式中覆写对应类的样式
* 通过 F12 审查元素,找到 vant组件 中对应组件的类名,并在 index.less 替换样式 *

1. 在全局样式中设置类名,并覆写vant组件中类的样式
[公共样式将作用于全局]

index.less
// ...

body {
    background-color: #f5f7f9;
}

.page-nav-bar {
    background-color: #3296fa;

    .van-nav-bar__title {
        color: #fff;
    }
}

2. 在vant对应组件中添加该类,不破坏原样式情况下达到覆写样式效果

Vue
<template>
  <div class="login-container">
    <!-- 导航栏 添加类名 -->
    <van-nav-bar class="page-nav-bar" title="登录" />
    ...
  </div>
</template>

效果展示

%title插图%num

修改自定义样式 – 自定义图标等

完善缺失的内容:icon图标、右侧按钮;使用 less 修改一些默认样式,方便达成设计稿的样式

1. vant输入框组件中 提供了图标属性 left-icon 并内置了 icon图标库
如若仍不满足业务需要,可使用 组件提供的 具名插槽 left-icon 插入标签

2. 右侧 “发送验证码” 按钮采用 button插槽 给输入框尾部插入按钮。
并在当前组件的局部样式 <style> 中使用 less 做需求样式调整

3. 调整 登录按钮 的颜色和外边距等,方便达到设计稿需求

Vue
<template>
  <div class="login-container">
    <!-- 导航栏 -->
    <van-nav-bar class="page-nav-bar" title="登录" />
    <!-- 登录表单 -->
    <van-form @submit="onSubmit">
      <van-field 
      name="用户名" 
      placeholder="请输入手机号">
      <i slot="left-icon" class="toutiao toutiao-shouji"></i>
      </van-field>
      <van-field 
      name="验证码" 
      placeholder="请输入验证码" >
      <i slot="left-icon" class="toutiao toutiao-yanzhengma"></i>
        <template #button>
           <van-button class="send-sms-btn" round size="small" type="default">
            发送验证码
           </van-button>
        </template>
      </van-field>
      <div class="login-btn-wrap">
        <van-button class="login-btn" block type="info" native-type="submit">
          登录
        </van-button>
      </div>
    </van-form>
  </div>
</template>

<script>
export default {
  name: "LoginIndex",
  ...
};
</script>

<style scoped lang="less">
   .login-container{
    .toutiao{
      font-size: 37px;
    }
    .send-sms-btn{
      font-size: 22px;
      color: #666;
      width: 152px;
      height: 46px;
      background-color: #ededed;
    }
    .login-btn-wrap{
      padding: 53px 33px;
      .login-btn{
        background-color: #6db4fb;
        border: none;
      }
    }
   }
</style>

效果展示

%title插图%num
实现登录功能

完成用户在界面中输入账号后,发起 ajax请求,并从接口返回成功或失败的信息。

步骤

  • 获取表单数据
  • 表单验证
  • 提交表单请求登录,使用 try{}catch(err){} 捕获错误
  • 根据请求响应结果处理后续操作

实际操作

1. 在 data 中定义并使用 v-mode 绑定手机号、验证码数据

login/index.vue
<template>
     ...
    <!-- 登录表单 -->
     ...
      <van-field 
      v-model="user.mobile"
      name="手机号" 
      placeholder="请输入手机号">
      ...
      </van-field>
      <van-field 
      v-model="user.code"
      name="验证码" 
      placeholder="请输入验证码" >
      ...
      </van-field>
      ...
  </div>
</template>

<script>
export default {
  data(){
    return{
      user:{
        mobile:'', // 手机号
        code:'' // 验证码

      }
    }
  },
  ...
};
</script>

2. 在 src/user 中创建 user.js 文件,作为登录接口文件,写入接口请求

api/user.js
// 引用公共接口
import request from '@/utils/request';

export const login = data => {
    return request({
        method: 'POST',
        url: '/v1_0/authorizations',
        data
    })
}

3. 引入登录请求文件,并使用绑定数据发起请求
使用接口测试账号[13911111111] 验证码 [246810]

login/index.vue
// ...
import { login } from "@/api/user.js";
export default {
  // ...
  data() {
    return {
      user: {
        mobile: "", // 手机号
        code: "" // 验证码
      }
    };
  },
  methods: {
    // 登录按钮单击事件
    async onSubmit() {
      const user = this.user;
      // 尝试捕获错误
      try {
        // 等待接收请求数据
        const res = await login(user);
        // 完成登录
        console.log("登录成功", res);
      } catch (err) {
        if (err.response.status === 400) {
          // 捕获失败结果 返回失败信息
          console.log("手机号或验证码错误", err);
        } else {
          console.log("登录失败,请稍后再试", err);
        }
      }
    }
  }
};

效果展示

%title插图%num

F12 控制台提示登录成功

%title插图%num
登录状态提示

使用vant组件中 轻提示 方式在前端页面中提示用户请求登录后返回的 登录的状态

属性介绍

特性

  • 使用 Toast.loading 方法展示加载提示,通过 forbidClick 属性可以禁用背景点击
  • Toast 默认采用单例模式,即同一时间只会存在一个 Toast
  • Toast 轻提示中目前使用有 loading [加载提示] => success [成功提示] || fail [失败提示]

API介绍

方法名 说明 参数 返回值
Toast 展示提示 options | message toast 实例
Toast.loading 展示加载提示 options | message toast 实例
Toast.success 展示成功提示 options | message toast 实例
Toast.fail 展示失败提示 options | message toast 实例
Toast.clear 关闭提示 clearAll: boolean void
Toast.allowMultiple 允许同时存在多个 Toast void
Toast.setDefaultOptions 修改默认配置,对所有 Toast 生效。
传入 type 可以修改指定类型的默认配置
type | options void
Toast.resetDefaultOptions 重置默认配置,对所有 Toast 生效。
传入 type 可以重置指定类型的默认配置
type void

实际操作

1. 引入 vant轻提示 函数,通过 this.$toast.loading({}) 调用提示
[如若输入 0 ,则 loading 状态持续,并加载提示动画持续展示]

login/index.vue
  methods: {
    // 登录按钮单击事件
    async onSubmit() {
      // ...
      // 轻提示
      this.$toast.loading({
        // 提示内容
        message: "加载中...",
        // 禁用背景点击
        forbidClick: true,
        // 持续实际 默认为 2000 毫秒
        // 如果该值为 0 则持续展示
        duration: 2000
      });
      // ...
    }
  }

2. 通过 this.$toast.success() 返回调用成功提示,会结束之前 toast 提示
[任何一个新的 toast提示 被调用,都会结束上一个 任何的 toast提示]

login/index.vue
  methods: {
    // 登录按钮单击事件
    async onSubmit() {
      const user = this.user;
      // 轻提示
      this.$toast.loading({
        // 提示内容
        message: "加载中...",
        // 禁用背景点击
        forbidClick: true,
        // 持续实际 默认为 2000 毫秒
        // 如果该值为 0 则持续展示
        duration: 2000
      });
      // 尝试捕获错误
      try {
        // 等待接收请求数据
        const res = await login(user);
        // 完成登录
        this.$toast.success("登录成功");
      } catch (err) {
        if (err.response.status === 400) {
          // 捕获失败结果 返回失败信息
          this.$toast.fail("手机号或验证码错误");
        } else {
          this.$toast.fail("登录失败,请稍后再试");
        }
      }
    }
  }

效果展示

%title插图%num
表单验证

使用 vant-from 中自带是表单验证属性来验证用户的输入的错误内容,并作出操作

属性介绍

:rules=”[{ pattern, message: ‘请输入正确内容’ }]”

vant-from 中的 rules 属性可以实现验证的操作,可在它数组参数中传入验证规则等配置对象,
当配置多个验证对象,rules 会从上到下方式依次尝试完成验证。

[它自动配置了拦截,当表单验证通过时才会触发 submit 事件,如果验证失败不会触发 submit 事件]

rules 属性值

键名 说明 类型
required 是否为必选字段,当值为空值时(空字符串、空数组、falseundefinednull ),校验不通过 boolean
message 错误提示文案,可以设置为一个函数来返回动态的文案内容 string | (value, rule) => string
validator 通过函数进行校验,可以返回一个 Promise 来进行异步校验 (value, rule) => boolean | string | Promise
pattern 通过正则表达式进行校验,正则无法匹配表示校验不通过 RegExp
trigger 设置本项规则的触发时机,优先级高于 Form 组件设置的 validate-trigger 属性,可选值为 onChangeonBluronSubmit string | string[]
formatter 格式化函数,将表单项的值转换后进行校验 (value, rule) => any
validateEmpty v3.6.0 设置 validatorpattern 是否要对空值进行校验,默认值为 true,可以设置为 false 来禁用该行为 boolean

实际操作

1. 为 vant-from 中的 van-field 标签添加 rules 验证属性,配置验证规则
[在 data 中创建验证对象,并引用在 van-field 的 rules 验证规则参数中]

login/index.vue
 
 
 <template>
    // ...
    <van-form @submit="onSubmit">
      <van-field
        // ...
        :rules="userFromRules.mobile"
        type="number"
        maxlength="11"
      >
      // ...
      </van-field> 
      <van-field
       // ...
        :rules="userFromRules.code"
        type="number"
        maxlength="6"
      // ...
      </van-field>
      // ...
    </van-form>
  </div>
</template>
  data() {
    //...
      userFromRules: {
        mobile: [
          {
            required: true,
            message: "手机号不能为空"
          },
          {
            pattern: /^1[3|5|7|8]\d{9}$/,
            message: "手机号格式错误"
          }
        ],
        code: [
          {
            required: true,
            message: "验证码不能为空"
          },
          {
            pattern: /^\d{6}$/,
            message: "验证码格式错误"
          }
        ]
      }
    };

效果展示

%title插图%num