【项目】基于Vue2+Router+Vant 前端面经项目

news2024/11/16 6:50:09

环境配置

Vue脚手架的创建

  1. 在终端中打开输入
vue create 项目包名 -m npm

注意⚠️:项目名称不再允许包含大写字母。

  1. 选择第三项

image-20240808105439494

3.选择要安装的模块

image-20240808105618092

从上到下的功能模块:

  1. Babel - ES:降级处理
  2. Router-Vue:路由插件
  3. CSS预处理器
  4. ESLint代码格式检查工具
  1. 选择下载Vue2版本

image-20240808105630361

  1. 选择模式
  • 选择路由模式(输入n)
  • 选择Css预处理器(选择Less)
  • ESLint版本(选择Standard——标准版)

image-20240808105841368

  1. 选择配置

image-20240808105919169

从上到下依次是:

  1. Lint on save 代码保存的时候,检查代码格式
  2. 配置文件保存位置选择保存到单个文件中
    1. 如果选择package.json表示几种保存在package.json
  3. 是否将所有选择保存为预设(输入n)
  1. 等待安装完成,出现如下字样即表示创建成功。

image-20240808110048267

  1. 运行项目:cd到指定文件夹,执行npm run serve启动项目。

安装ESLint插件

ESLint 是一个用于检测和规范 JavaScript 代码风格及语法错误的工具。它可以帮助开发者保持代码的一致性和可读性,提高代码质量。例如,它能检查是否遗漏了分号,变量名是否符合规范等。

作用:检查代码格式,保证每个成员的代码风格应该一致。

安装方式:直接在vscode扩展商店中搜索ESLint,点击下载即可。

image-20240808144213396

当通过 @vue/cli 脚手架工具安装项目后,默认已经将 eslint 相关的包安装并配置好了。

我们将使用vue 的 eslint 插件规定的默认规则进行代码检查。如果需要查看规则,则可以查看 https://eslint.nodejs.cn/docs/latest/rules/%E3%80%82

ESLint报错提示:

image-20240808110940688

配置ESLint

  1. 关于ESLint的配置,需要放到配置文件 .eslintrc.js 中。

  2. 打开 .eslintrc.js 文件,找到里面的 rules 节点,这个 rules节点,用于自定义 ESLint规则

  3. 配置的每条语句结束,必须加分号。

例如:在 rules节点中加入如下规则

"rules": {
  "semi": ["error", "always"]
}

注意⚠️:修改了配置文件,需要重启项目。

安装自动化格式插件

自动化格式插件有很多种,比如 ESlint、Vue插件,都带这种格式化功能。

这边的话就跟我的配置来安装包,保证了配置统一性。

安装步骤:

  1. 在扩展商店查找以下👇插件

image-20240808112017577

  1. 打开 vscode,按 Ctrl + , 快捷键,打开设置界面。
  2. 点击右上角的 打开设置(json)image-20240808112252922
  3. 将下面的代码复制进去(复制到最后一个大括号之前
// 代码缩进 2 个空格
"editor.tabSize": 2,
// 保存(包括自动保存),自动格式化
"editor.formatOnSave": true,
// 按Ctrl + S保存时,自动修复
"editor.codeActionsOnSave": {
  "source.fixAll.eslint": true
},
// 编辑器窗口失去焦点时,自动保存代码【可选】
"files.autoSave": "onFocusChange",
// 配置vue默认的格式化程序【必须的配置】
"[vue]": {
  "editor.defaultFormatter": "numso.prettier-standard-vscode"
},
// 设置JS文件的默认格式化程序【必须的配置】
"[javascript]": {
  "editor.defaultFormatter": "numso.prettier-standard-vscode"
},
"[jsonc]": {
  "editor.defaultFormatter": "numso.prettier-standard-vscode"
},

image-20240808112418716

注意⚠️:如果出现 黄色的下划线,说明有重复的配置。去掉前面的(你自己的)。

使用方法:

右键–>找到使用…格式化文档–>选择Prettier-Standard

image-20240808112101589

Vant组件库

因为本次项目是一个移动端项目,所以移动端上使用的组件库目前比较不错的一个流行库就是Vant啦🥳。

官方网站:Vant 2 - 轻量、可靠的移动端组件库 (vant-ui.github.io)

安装步骤:

打开终端输入以下👇命令
npm i vant@latest-v2
安装如果有报错用👇
npm i vant@latest-v2 --legacy-peer-deps

检查是否安装完成:

image-20240808113706755

引入组件

  1. 按需引入:只引入项目中实际用到的组件,能减小项目体积。通常通过配置或特定的模块加载方式实现。
  2. 全局引入:将组件库整体加载到项目中,在任何地方都能直接使用。比如在入口文件中一次性引入。
  3. 手动导入:把组件库当作一个模块,在需要的模块中单独引入相关组件。

⚠️ 注意三种方式不能混用。

⚠️ 我会讲按需引入全局引入这两种方式的引入步骤,但是本项目使用的是全局引入的方式来制作。所以按需引入方式教学大家看看就好🙏。

全局引入

步骤:直接在main.js中粘贴以下👇代码:

import Vant from 'vant'
import 'vant/lib/index.css'
Vue.use(Vant)

按需引入

步骤:

  1. 安装一个插件(插件配置后,就可以实现自动导入)

    npm i babel-plugin-import -D --legacy-peer-deps
    
  2. babel.config.js中配置

    module.exports = {
      presets: [
        '@vue/cli-plugin-babel/preset'
      ],
      plugins: [
        ['import', {
          libraryName: 'vant',
          libraryDirectory: 'es',
          style: true
        }, 'vant']
      ]
    }
    
  3. 按需引入main.js

    import { Button, Icon } from 'vant'
    
    Vue.use(Button)
    Vue.use(Icon)
    
  4. 在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>
    

再次强调⚠️

完整导入和按需导入Vant,二者只能用一个;

  • 如果你用完整导入,请将按需导入的配置都去掉,然后重启项目。

实现vw适配

使用Vant组件的适配方案就非常的简单😆。

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

步骤:

  1. 安装命令

    npm i postcss-px-to-viewport@1.1.1 -D
    安装报错就用👇
    npm i postcss-px-to-viewport@1.1.1 -D --legacy-peer-deps
    
  2. 在项目的根目录,新建一个配置文件pocstcss.config.js

    // postcss.config.js
    module.exports = {
      plugins: {
        'postcss-px-to-viewport': {
          // 设计稿如果是2倍图,宽是750,则 750/2 = 375,下面就写375
          // 设计稿如果是3倍图,宽是 1080,则 1080/3 = 360,下面就写360
          viewportWidth: 375
        }
      }
    }
    

路由配置

总体路由结构图:

image-20240808150928700

我们根据上面的结构图分析一下,这个项目呢——是由4个页面组成的。

一共有4个一级目录:App.vueLogin.vueRegister.vueDetall.vue 和1个二级目录:Home.vue。所以我们先根据结构创建好项目目录先。

怎么在src文件夹下的views新建以下👇文件:

  • Home.vue:用来做主页

    <template>
      <div id="app">主页</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    
  • Login.vue:用来做登录页

    <template>
      <div id="app">登录页</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    
  • Register.vue:用来做注册页

    <template>
      <div id="app">登录页</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    
  • Detall.vue:用来做详情页

    <template>
      <div id="app">登录页</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    

创建完上面四个文件之后我们还有在views文件夹下再创建一个Layout包,继续创建我们的二级目录文件:

  • Article.vue:文章列表

    <template>
      <div id="app">文章列表</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    
  • Collect.vue:收藏界面

    <template>
      <div id="app">收藏界面</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    
  • Like.vue:喜欢界面

    <template>
      <div id="app">喜欢界面</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    
  • User.vue:用户界面

    <template>
      <div id="app">用户界面</div>
    </template>
    
    <script>
    	export default {}
    </script>
    
    <style lang="less" scoped></style>
    

配置路由

前置知识:路由

路由模版:

const routes =	[
	{
		path:'/',	//url地址
		component: () => import('文件路径'),	//指定文件
	}
]

image-20240808154320984

二级路由模版:

const routes =	[
	{
		path:'/',	//url地址
		component: () => import('文件路径'),	//指定文件
		redirect: '/二级默认展示url地址',	// 重定向,指向二级路由默认展示的页面
		children: [
   {
     path:'/',	//url地址
     component: () => import('文件路径'),	//指定文件
   }
		]
	}
]

直接在router文件夹下的index.js文件下书写我们的路由代码。

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

Vue.use(VueRouter)

const routes = [
  // 主页
  {
    path: '/',
    component: () => import('@/views/Home.vue'),
    redirect: '/article', // 重定向,指向二级路由默认展示的页面
    children: [
      // 列表页
      { path: 'article', component: () => import('@/views/Layout/Article') },
      // 藏页
      { path: 'collect', component: () => import('@/views/Layout/Collect') },
      // 喜欢页
      { path: 'like', component: () => import('@/views/Layout/Like') },
      // 用户页
      { path: 'user', component: () => import('@/views/Layout/User') }
    ]
  },
  // 登录页
  { path: '/login', component: () => import('@/views/Login.vue') },
  // 详情页
  { path: '/detail', component: () => import('@/views/Detail.vue') },
  // 注册页
  { path: '/register', component: () => import('@/views/Register.vue') }
]

const router = new VueRouter({
  routes
})

export default router

之后在App.vue中和Home.vue中,分别加入 <router-view></router-view>

<template>
  <div class="layout-page">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'layout-page'
}
</script>

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

最后我们进而可以通过url栏手动修改值来测试一下页面跳转。

image-20240808155525768

登录页面搭建

静态布局

效果展示:image-20240809152042999

登录页面的搭建主要用到Vant组件中的导航栏组件

image-20240809140047929

所以我们只需要保留title属性即可:

image-20240809140830274

<!-- 登录login页面 -->
<template>
  <div class="login-page">
    <van-nav-bar title="面经项目登录" />
  </div>
</template>

<script>
export default {
  name: 'login-page',
  methods: {}
}
</script>

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

image-20240809140929614

然后我们就要写一个表单来实现账号密码输入框,同样的在Vant组件中找到表单组件。

image-20240809141130268

我先来对一些代码字段解析一下让大家更好的理解:

<van-field
        v-model="username"
        name="username"
        label="账号"
        placeholder="请输入账号"
        :rules="[{ required: true, message: '请填写用户名' }]"
      />
  • v-model="username": 将输入字段的值绑定到 Vue 实例的 username 数据属性上。
  • name="username": 设置输入字段的 name 属性,通常用于表单提交时标识字段。
  • label="账号": 设置输入字段的标签文本为“账号”。
  • placeholder="请输入账号": 设置输入字段的占位符文本为“请输入账号”。
  • :rules="[{ required: true, message: '请填写用户名' }]": 设置输入字段的验证规则,这里要求输入字段必须填写,否则会显示错误消息“请填写用户名”。

然后就是修改提交按钮的样式,我这里也是先对代码进行解释:

<van-button round block type="info" native-type="submit">提交</van-button>
  • round: 使按钮具有圆角。
  • block: 使按钮宽度充满其父容器。
  • type="info": 设置按钮的类型为 info,通常表示按钮具有提示或信息性质。
  • native-type="submit": 将按钮的 type 属性设置为 submit,使其在表单中可以提交表单数据。

我们还需要修改一下圆角按钮的样式,首先将提交改为登录,然后去掉小圆角,将按钮颜色改为橙色。

如何修改颜色呢?Vant 的 <van-button> 组件支持 color 属性,你可以直接在组件上设置颜色。例如,如果你想将按钮颜色改为橙色。

<div style="margin: 16px">
        <van-button block type="info" native-type="submit" color="#FF5500"
          >登录</van-button
        >

下面还有一个超链接用于跳转到注册页面,可以使用a标签或者是直接使用<router-link>标签。我这里就用link标签方便一点。

<router-link to="/register">还没有账号,去注册吧!</router-link>

测试一下是否能跳转:

PixPin_2024-08-09_14-41-27

然后再给link添加一个class写样式:

<style lang="less" scoped>
.link {
  float: right;
  font-size: 14px;
  color: #ff5500;
  margin-right: 10px;
}
</style>

最后就完成了我们想要的效果啦🥳!

image-20240809144757686

但是我们还可以把代码复制给Ai做一下界面美化。下面我就贴上我完成的代码:

<!-- 登录login页面 -->
<template>
  <div class="login-page">
    <!-- 标题栏 -->
    <van-nav-bar title="面经项目登录" />
    <!-- 表单 -->
    <div class="font-div">
      <van-form @submit="onSubmit">
        <van-field
          v-model="username"
          name="username"
          label="账号"
          placeholder="请输入账号"
          :rules="[{ required: true, message: '请填写用户名' }]"
        />
        <van-field
          v-model="password"
          type="password"
          name="password"
          label="密码"
          placeholder="请输入密码"
          :rules="[{ required: true, message: '请填写密码' }]"
        />
        <div style="margin: 16px">
          <van-button block type="info" native-type="submit" color="#FF5500"
            >登录</van-button
          >
        </div>
      </van-form>
      <!-- 超链接-注册按钮 -->
      <router-link class="link" to="/register"
        >还没有账号,去注册吧!</router-link
      >
    </div>
  </div>
</template>

<script>
export default {
  name: 'login-page',
  data () {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    onSubmit (values) {
      console.log('submit', values)
    }
  }
}
</script>

<style lang="less" scoped>
.font-div {
  position: absolute;
  top: 26%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 300px;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  // box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  // background-color: #fff;
}

.van-nav-bar {
  // background-color: #ff5500;
  margin-top: 10px;
  /deep/ .van-nav-bar__title {
    color: #ff5500;
    font-weight: 700;
    font-size: 25px;
  }
}

.van-button {
  border-radius: 4px;
}

.link {
  display: block;
  text-align: right;
  font-size: 14px;
  color: #ff5500;
  margin-top: 10px;
}
</style>

表单验证

Vant组件也有告诉我们,通过 rules 定义表单校验规则

使用 Field 的 rules 属性可以定义校验规则,可选属性如下:

键名说明类型
required是否为必选字段,当值为空字符串、空数组、undefinednull 时,校验不通过boolean
message v2.5.3错误提示文案string | (value, rule) => string
validator v2.5.3通过函数进行校验(value, rule) => boolean | Promise
pattern v2.5.3通过正则表达式进行校验RegExp
trigger v2.5.2本项规则的触发时机,可选值为 onChangeonBlurstring
formatter v2.5.3格式化函数,将表单项的值转换后进行校验(value, rule) => any

我们这里主要用到required`message \pattern `。通过正则表达式校验可以让我们对输入的信息进行数字、字母、长度的要求。

v-model="password"
          type="password"
          name="password"
          label="密码"
          placeholder="请输入密码"
          :rules="[
            { required: true, message: '请输入密码' },
            {
              pattern: /^(?=.*[a-zA-Z]).{2,6}$/,
              message: '密码必须为2-6位且包含至少一个字母'
            }
          ]"
        />

在这个正则表达式中:

  • ^(?=.*[a-zA-Z]) 是一个正向先行断言,确保字符串中至少包含一个字母(不区分大小写)。
  • .{2,6}$ 确保整个字符串的长度在2到6个字符之间。

如果规则比较多,可以在data里面写一个变量,

<van-field
  v-model="username"
  name="username"
  label="账号"
  placeholder="请输入账号"
  :rules="userRules"
/>


export default {
  name: 'login-page',
  data () {
    return {
      username: '',
      password: '',
      userRules: [
        { required: true, message: '请输入账号' },
        { pattern: /^\d{2,6}$/, message: '账号必须为2-6位数字' }
      ]
    }
  },
}

其中 pattern 键名对应的正则表达式 /^\d{2,6}$/ 用于验证输入是否全为数字且长度为2-6位。如果输入不符合这个正则表达式,将会显示 message 键名对应的错误信息。

注册页面布局

注册页面和登录页面大多相似,我们直接复制登录页面的代码到注册页面中,然后稍微做一些修改就好了。

  1. 修改标题image-20240809155218288
  2. 修改注册按钮image-20240809155249577
  3. 修改超链接,别忘了还有文本也要修改哦🤗image-20240809155317785
  4. 修改组件名称image-20240809155139029
  5. 修改主题颜色,然他和注册页面有所区分image-20240809155510835

整体效果:

image-20240809155616841

发送请求

首先我们要注册一个账号,所以就要对后台数据发送请求,使用axios来完成这个注册操作。

首先安装一个axios包

npm i axios
如果报错了就用以下👇安装
npm install axios --legacy-peer-deps

axios前置知识

补一下前置知识有关axios的内容!

import axios from 'axios'

export default{
	name:'',
	date(){},
	method:{
		async onSubmit(values){
		//定义一个变量接受结果
			const {data:res} = await axios({
				method:'',
				url:'',
				data:value
			})
			console.log(res)
		}
	}
}

因为黑马那边的数据接口关闭了,所以我们没办法调用他们接口里面的数据。既然别人不给我们,那么我们就自己造!因为大家都是前端朋友,使用json-server来搭建接口会更加友好一点,不会涉及后端知识,也不用mysql数据库。

搭建数据大家可以看我这一篇文章:

然后我们这里的话也是用的是json-server,我这里给大家放上json数据供大家搭建

{
  "users": [
    {
	"id": 1,
      "name": "李雷",
      "UID": "01a1"
    },
    {
	"id": 2,
      "name": "韩梅梅",
      "UID": "02b2"
    },
    {
	"id": 3,
      "name": "张伟",
      "UID": "03c3"
    },
    {
	"id": 4,
      "name": "王芳",
      "UID": "04d4"
    },
    {
	"id": 5,
      "name": "赵敏",
      "UID": "05e5"
    },
    {
	"id": 6,
      "name": "马云",
      "UID": "06f6"
    },
    {
	"id": 7,
      "name": "马东",
      "UID": "07a7"
    }
  ],
  "register": [
    {
	"id": 1,
      "username": "01",
      "password": "a1"
    },
    {
	"id": 2,
      "username": "02",
      "password": "a2"
    },
    {
	"id": 3,
      "username": "03",
      "password": "a3"
    },
    {
	"id": 4,
      "username": "04",
      "password": "a4"
    },
    {
	"id": 5,
      "username": "05",
      "password": "a5"
    },
    {
	"id": 6,
      "username": "06",
      "password": "a6"
    },
    {
	"id": 7,
      "username": "07",
      "password": "a7"
    }
  ]
}

封装axios

为了让整体项目更加的规整,我们需要将axios单独封装到一个文件当中,方便后面管理。

我们在src目录下新建一个utils文件夹,里面再新建一个文件一般叫做request.js

// 封装axios请求
// 1.导入axios
import axios from 'axios'
// 2.配置
const requerst = axios.create({
  // 配置请求根路径
  baseURL: 'http://localhost:5000'
  // 超时时间
  // timeout:5000
})
// request配置拦截器
// 3.导出
export default requerst

导入request.js文件

import request from '@/utils/request'

配置axios

async onSubmit (values) {
      const { data: res } = await request({
        method: 'POST',
        url: '/register',
        data: values
      })
      console.log(res)

抽离注册的API方法

在src目录下新建一个Api文件夹,里面再新建一个文件一般叫做user.js。用来存放与用户相关的请求。

// 和用户相关的请求
import request from '@/utils/request'

export function registerAPI (data) {
  return request({
    method: 'POST',
    url: '',
    data: data
  })
}

在注册组件中导入api方法

// <!-- 导入封装的axios -->
// import request from '@/utils/request'
// 导入API方法
import { registerAPI } from '@/Api/user'

记得把上次导入axios语句注释掉⚠️

修改调用方法

const { data: res } = await registerAPI()
// 之前的都注释掉了
      // request({
      //   method: 'POST',
      //   url: 'register',
      //   data: values
      // })

image-20240810201557459

我们可以发现调用的registerAPI依旧是原来的request

最后再给registerAPI()方法传入values参数:

const { data: res } = await registerAPI(values)

还有user.js里面的url写上我们的路径:

// 和用户相关的请求
import request from '@/utils/request'

export function registerAPI (data) {
  return request({
    method: 'POST',
    url: 'register',	//user.js里面的url写上我们的路径
    data: data
  })
}

之所以要这样写,就是为了我们这个方法能够复用起来,以后要使用这个直接调用这个方法就可以了。

注册、登录页面跳转

下面我们就要写一个注册跳转功能,让我们注册完之后跳转到登录界面 。使用axios的trycatch方法来实现。

async onSubmit (values) {
      try {
        const { data: res } = await registerAPI(values)
        console.log(res) // 其实这个res这个变量没有用到过,可以删掉也可以的
        this.$toast('注册成功')
        this.username = this.password = '' // 重置表单
        this.$router.push('/login')	// 跳转到登录页面
      } catch (err) {}
    }

同样的我们也在登录页面设置一些,来实现登录成功跳转到首页当中。这一次我们只需要在login.vue文件中进行设置就可以了,具体代码和注册跳转是同样滴。

methods: {
    // 点击登录的时候执行
    async onSubmit (values) {
      // values={username:'',password:''}
      try {
        await registerAPI(values)	//这里我就把没有用到的data删掉了
        this.$toast('登录成功')
        this.username = this.password = ''
        this.$router.push('/article')	//这里url要变成主页的地址
      } catch (err) {}
      console.log('submit', values)
    }
  }

⚠️前面别忘记导入封装好的registerAPI方法。

主页搭建

面经列表数据

封装面经方法

在Api包中新建一个article.js

// 存放和面经内容相关的请求
import request from '@/utils/request'

export function articleAPI (params) {
  return request({
    url: '',
    params: params
  })
}

这里因为params先待定。

然后我们在article.vue组件中的脚本区域获取数据。

import { articleAPI } from '@/Api/article'
export default {
  name: 'article-page',
  data () {
    return {
      curent: 1, // 页码、获取第一页的数据
      sorter: 'weight_desc'
    }
  },
  methods: {
    async article () {
      const { data: res } = await articleAPI({
        current: this.current,
        sorter: this.sorter
      })
      console.log(res)
    }
  },
  created () {
    this.article() // 创建生命周期,使其一开始就加载页面
  }
}
{
    "code": 10000,
    "message": "请求成功",
    "data": {
        "current": 1,
        "pageTotal": 10,
        "pageSize": 20,
        "total": 1000,
        "rows": [
            {
                "id": "40821",
                "stem": "广东紫云平台数据服务有限公司前端面经,未通过",
                "content": "<p><b>面试公司:</b>广东紫云平台数据服务有限公司<br></p><p><b>面试岗位
                "createdAt": "2022-01-20 00-00-00",
                "creator": "中二苏北陌",
                "avatar": "http://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.png",
                "likeCount": 1,
                "views": 32
            }
        ]
    }
}
axios前置知识——get请求
  1. 省略method属性(默认GET):

    javascriptexport function articleAPI(data) {
      return request({
        url: '', // 这里应填写实际的API URL
        params: data // 使用params来传递URL查询参数
      });
    }
    
  2. 明确指定method'GET':

    javascriptexport function articleAPI(data) {
      return request({
        method: 'GET',
        url: '', // 同样需要填写实际的API URL
        params: data
      });
    }
    

在这两种方式中,params属性用于传递URL查询参数,这是GET请求中常见的做法。例如,如果你需要根据用户ID获取文章详情,你可以这样调用articleAPI函数:

javascript复制
articleAPI({ userId: 123 });

面经页面搭建

页面搭建就是用Vant组件库里面的Cell单元格,通过循环遍历的方式,来渲染出列表。

image-20240810233132353

<van-cell title="单元格" value="内容" />

但是单元格现实的内容太少了,我们还需要用到插槽来搭建更多的内容。

<van-cell value="内容" is-link>
  <!-- 使用 title 插槽来自定义标题 -->
  <template #title>
    <span class="custom-title">单元格</span>
    <van-tag type="danger">标签</van-tag>
  </template>
</van-cell>

<van-cell title="单元格" icon="shop-o">
  <!-- 使用 right-icon 插槽来自定义右侧图标 -->
  <template #right-icon>
    <van-icon name="search" class="search-icon" />
  </template>
</van-cell>

<style>
  .custom-title {
    margin-right: 4px;
    vertical-align: middle;
  }

  .search-icon {
    font-size: 16px;
    line-height: inherit;
  }
</style>
给插槽传值

在Vue 2中,给插槽(slot)传值通常使用slot-scope属性。slot-scope属性允许你将数据传递给具名插槽或作用域插槽。

全写形式:<template v-slot:title>
简写形式:<template #title>

静态结构我去直接提供给大家啦,大家可以试一试调一调。

<template>
  <div class="article-page">
    <nav class="my-nav van-hairline--bottom">
      <a href="javascript:;">推荐</a>
      <a href="javascript:;">最新</a>
      <div class="logo"><img src="@/assets/logo.png" alt=""></div>
    </nav>

    <van-cell class="article-item" >
      <template #title>
        <div class="head">
          <img src="@/assets/logo.png" alt="" />
          <div class="con">
            <p class="title van-ellipsis">宇宙头条校招前端面经</p>
            <p class="other">不风流怎样倜傥 | 2022-01-20 00-00-00</p>
          </div>
        </div>
      </template>
      <template #label>
        <div class="body van-multi-ellipsis--l2">
          笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端, 总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题&nbsp;一面
        </div>
        <div class="foot">点赞 46 | 浏览 332</div>
      </template>
    </van-cell>
  </div>
</template>

样式:

<style lang="less" scoped>
.article-page {
  margin-bottom: 50px;
  margin-top: 44px;
  .my-nav {
    height: 44px;
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    z-index: 999;
    background: #fff;
    display: flex;
    align-items: center;
    > a {
      color: #999;
      font-size: 14px;
      line-height: 44px;
      margin-left: 20px;
      position: relative;
      transition: all 0.3s;
      &::after {
        content: '';
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        bottom: 0;
        width: 0;
        height: 2px;
        background: #222;
        transition: all 0.3s;
      }
      &.active {
        color: #222;
        &::after {
          width: 14px;
        }
      }
    }
    .logo {
      flex: 1;
      display: flex;
      justify-content: flex-end;
      > img {
        width: 64px;
        height: 28px;
        display: block;
        margin-right: 10px;
      }
    }
  }
}
.article-item {
  .head {
    display: flex;
    img {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      overflow: hidden;
    }
    .con {
      flex: 1;
      overflow: hidden;
      padding-left: 10px;
      p {
        margin: 0;
        line-height: 1.5;
        &.title {
          width: 280px;
        }
        &.other {
          font-size: 10px;
          color: #999;
        }
      }
    }
  }
  .body {
    font-size: 14px;
    color: #666;
    line-height: 1.6;
    margin-top: 10px;
  }
  .foot {
    font-size: 12px;
    color: #999;
    margin-top: 10px;
  }
}
</style>

将单个单元格封装成组件

因为不止在主页中会用到这个单元格列表,其二级目录下的收藏页,或者喜欢页也都会有这个列表的展示,所以我们不妨直接将他封装成一个组件,后面需要用到的时候直接调用即可ヾ(◍°∇°◍)ノ゙。

在src下面新建一个components文件夹用来存放组件,然后新建一个.vue文件,就叫做Articleitem.vue吧。

然后把上面写的单元格列表和样式全部丢进去(≧∇≦)ノ。

<!-- 封装单元格组件 -->
<template>
  <div>
    <van-cell class="article-item">
      <template #title>
        <div class="head">
          <img src="@/assets/logo.png" alt="" />
          <div class="con">
            <p class="title van-ellipsis">宇宙头条校招前端面经</p>
            <p class="other">不风流怎样倜傥 | 2022-01-20 00-00-00</p>
          </div>
        </div>
      </template>
      <template #label>
        <div class="body van-multi-ellipsis--l2">
          笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端,
          总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题&nbsp;一面
        </div>
        <div class="foot">点赞 46 | 浏览 332</div>
      </template>
    </van-cell>
  </div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.article-item {
  .head {
    display: flex;
    img {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      overflow: hidden;
    }
    .con {
      flex: 1;
      overflow: hidden;
      padding-left: 10px;
      p {
        margin: 0;
        line-height: 1.5;
        &.title {
          width: 280px;
        }
        &.other {
          font-size: 10px;
          color: #999;
        }
      }
    }
  }
  .body {
    font-size: 14px;
    color: #666;
    line-height: 1.6;
    margin-top: 10px;
  }
  .foot {
    font-size: 12px;
    color: #999;
    margin-top: 10px;
  }
}
</style>

注册全局组件

main.js中导入:

import ArticleItem from '@/components/ArticleItem.vue'

注册:

Vue.component('ArticleItem', ArticleItem)

回到Article.vue文件使用组件

<ArticleItem></ArticleItem>
[eslint]Component name "Articleitem" should always be multi-word vue/multi-word-component-names

循环遍历列表数据

在data中创建list用来存储面经列表项目

data () {
    return {
      curent: 1, // 页码、获取第一页的数据
      sorter: 'weight_desc'
      list:[] //存储要展示的面经列表项目
    }
  },

v-for循环遍历

 <ArticleItem 
 	v-for="item in list" :key="item.id" 
 	:item="item"	//父传子
 	>
 </ArticleItem>

子组件接受传值

  props: {
    item: {
      type: Object,
      //默认值如果是数组或对象,则而要写成函数,函数中返回数组或对象
      default: () =>({})
    }
  }

使用差值表达式渲染数据

image-20240811010056292

推荐和最新栏目

类名切换

接口文档中是规定了排序字段通过这个来区分最新还是推荐

image-20240811172323860

sorter属性通过发请求数据到之前定义好的onLoad方法中:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用到的是切换类名,通过绑定类名的值,在模版字符串里面进行判断接口中的数据是哪一种,吐过是推荐就传weight,如果是最新页面就不要传值。

然后还有添加一个点击事件,作用是为了通过点击事件实现两个a标签切换。

image-20240811172229122

点击方法:

image-20240811172213425

数据切换

类名变换实现了,那么我们的样式就会改变。image-20240811172541065

继续调用上面的Onload方法,来获取到数据。

image-20240811173046718

最不好理解的问题,如果在推荐页面滚到比较低的地方,然后切换到最新页面,这个时候滚动条依旧还在先前推荐页面滚动的位置,而不是重新再最顶部。为了避免重复发送请求,就要加上这句代码:

this.loading=true//避免自动发送请求

image-20240811173225587

实现详情页

数据获取

我们希望在列表中点击标题的时候,能够进入到详细页面中观看内容。

localhost:8080/#/detail

这个时候就要用到路由传参

前置知识——路由传参

传参方式

地址栏

  • 查询参数:/xx/xx?id=2
    • path: /xx/xx
    • 组件中获取参数值:$route.query.id

动态路由

  • 动态路由:/xx/xx/2
    • path: /xx/xx/:id
    • 组件中获取参数值:$route.params.id
  1. 设置传参方式,在封装好的articleitem.vue组件中添加一个点击事件

    @click="$router.push(/detail?id=${item.id})
    
  2. 路由规则不需要改动

  3. 组件中获取参数值image-20240811175243599

内容渲染

article.js文件中封装axios组件

image-20240811234029430

导入封装好的axios请求

image-20240811234254021

组件中处理请求数据


收藏点赞功能

在文章中,我们会有两个小按钮来实现收藏和点赞两个功能。

image-20240811234650976

主要是通过的在接口里面的这两个数据:

image-20240811234755315

在组件中使用:

  1. 绑定数据image-20240811234847569

  2. 添加点击事件image-20240811234918980

  3. 编写方法image-20240811235050275

  4. 处理细节image-20240811235125240
    当我们点赞的时候,这里也要加上“1”

    • image-20240811235205343
  5. 提示功能就用咱们得轻提示就好啦。

    this.$toast.success('点赞成功')
    
  6. 修改服务器数据,因为我们做的都是页面上的数据,还没有涉及到服务器请求。一刷新就回到原来的数据了。

    
    

实现主题定制

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

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

注意 ⚠️下面的操作会涉及到修改配置文件,所以修改完配置文件之后都要重启一下项目。

引入样式源文件

main.js 引入less:

import Vant from 'vant'
import 'vant/lib/index.css'
import 'vant/lib/index.less'
Vue.use(Vant)

修改样式变量

vue.config.js 覆盖变量,直接全部复制我们的覆盖掉原来代码就可以了。

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  css: {
    loaderOptions: {
      less: {
        // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
        lessOptions: {
          modifyVars: {
            // 直接覆盖变量
            blue: '#ff5500',
            // 'text-color': '#111',
            // 'border-color': '#eee'
            // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
            // hack: 'true; @import "your-less-file-path.less";'
          }
        }
      }
    }
  }
})

修改配置文件后,需要重启服务。

实现收藏和喜欢页面

收藏和喜欢的页面结构都是一样的,就像登录注册一样。所以我们只需要写一个页面,后面再稍微做一下修改就完成了这两个页面。

写一个标题栏:

1.收藏页标题去登录页复制

<template>
  <div class="collect-page">
    <van-nav-bar fixed title="我的收藏" />
    <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
      <article-item v-for="(item, i) in list" :key="i" :item="item" />
    </van-list>
  </div>
</template>

封装API请求方法:

export const collectAndLikeListAPI = (params) => {
  return request.get('/h5/interview/opt/list', {
    params: params
  })
}

script内容:

去Article.vue复制<van-list></van-list>,然后去Article.vue复制data和methods
,去掉sorter变量:去掉changeSorter方法
,最后在api/articlejs中,新封装一个获取收藏列表的方法

<script>
import { collectAndLikeListAPI } from '@/api/article'
export default {
  name: 'collect-page',
  data () {
    return {
      list: [],
      loading: false,
      finished: false,
      page: 1
    }
  },
  methods: {
    async onLoad () {
      // 异步更新数据
      const { data: res } = await collectAndLikeListAPI({
        page: this.page,
        optType: 2
      })
      this.list.push(...res.data.rows)
      this.loading = false
      if (this.page === res.data.pageTotal || !res.data.rows.length) {
        this.finished = true
      } else {
        this.page++
      }
    }
  }
}

然后我们在首页收藏一些页面,就有最终的效果啦!image-20240812001541699

喜欢页面也是一样的,直接复制上面的代码,修改请求参数optType: 1即可。

前置知识——vuex的使用

Vuex是vue项目中实现大范围数据共享的技术方案。

作用:能够方便、高效的实现组件之间的数据共享。

  1. 安装vuex

    npm i vuex@3.6.2
    
  2. 配置vuex

    1. 封装一个store模块,里面新建一个index.js
    2. 在man.js导入store

基本使用:

state用于存储数据。

state:{
	name:'tom',
	age:20
}

组件中直接使用数据,组件中可通过$store.state.属性名

getters是vuex中的计算属性,和组件中的计算属性意义一样,但是不支持set修改。

getters:{
	abc(state){
		return s
state.age * state.age
	}
}

为了方便取state中的数据:插件作者会给每个计算属性方法,传递一个state参数。

使用和state用法一致$store.getters.属性名

导航守卫

前置知识——导航守卫

加入导航守卫,目的是如果在没有登录的情况下,是不允许访问到其他的页面。

  • to.path — 要访问的地址
  • from.path — 你从哪里来的
  • next() — 放行
  • next(‘/url’) — 不放行,并且跳转到url中。

在router.index.js路由文件中使用以下👇代码:

router.beforeEach((to,from,next)=>{
  if (to.path !=='/login'&& store.state.user.token === ''){
    next('/login')
    return
  }
	next()
})

打包项目

在项目终端中运行npm run build

打包的时候可能会有一点点久,大家稍等一会。

当再次出现终端路径,就表示打包完成了。

打包完毕之后就会生成一个dist文件夹,文件夹里面的内容如下:

image-20240812235835279

然后我们发现点击index.html,项目是展示不出来的。因为我们的一些数据都在接口服务器上,需要在服务器上打来才行。如果要本地打开也不是没有办法,我们在vue.config.js文件中添加下面一句话:

//表示打包的结果,允许以文件的形式打开
publicPath:'./'	

我们还会发现在生成的文件当中会出现非常的.map文件,这个作用就是映射原始代码对应打包后的代码位置,一般可以不要的。在vue.config.js文件中添加下面一句话:

//去掉mao文件
productionSourceMap:false

然后还可以添加一个方法——生成打包报告,来看看是那些文件占用的体积比较大。

image-20240813000649386

对于一些外部组件库,可以使用CDN(引入网络中的文件)的方式引入组件。

image-20240813001857374

然后再重新npm run build打包文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2034263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于STM32开发的智能家居照明控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码控制代码应用场景 家庭照明自动化节能照明管理常见问题及解决方案 常见问题解决方案结论 1. 引言 智能家居照明控制系统通过整合各种传感器和控制器&#xff0c;能够实现对家居照…

[全文]买椟还珠和坏事变好事?《分析模式》漫谈19

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 “Analysis Patterns”的第一章有这么一句&#xff1a; Modeling in a programming language also presents the danger of tying the models to that language. The model may use f…

pythonUI自动化008::allure测试报告(安装及应用)

allure报告预览 1 下载jdk&#xff0c;配置jdk Path变量&#xff1a; https://www.cnblogs.com/FBGG/p/15103119.html&#xff08;这里不作阐述&#xff0c;请看该偏文章配置即可&#xff09; 2 下载allure驱动&#xff0c;配置allure Path变量&#xff1a; 下载allure驱动&a…

Springboot 开发之 Quartz 任务调度框架简介

引言 Quartz Scheduler是一个功能丰富的开源作业调度库&#xff0c;它允许开发人员以灵活的方式创建和管理定时任务。以下是对Quartz Scheduler的详细解析&#xff1a; 官网地址&#xff1a;https://www.quartz-scheduler.org/w3cschool 官方文档&#xff1a;https://www.w3c…

离子交换技术:助力电池材料纯度提升的环保解决方案

在新能源技术迅猛发展的今天&#xff0c;电池正极材料的生产和性能成为了科研与工业界的关注焦点。特别是锂离子电池&#xff0c;其广泛运用于电动汽车和各类便携式电子设备中&#xff0c;对电池性能的要求日益严苛。电池正极材料中的球形氢氧化镍直接影响着电池的充放电效率、…

PhotoShop - 初级抠图

1. 调整边缘 1.1 快速选择工具 使用快速选择工具&#xff0c;photoshop会智能的去识别图像中的边缘部分并以此选区 1.2 参数调整 使用快速选择工具选区之后&#xff0c;实际上很多边缘部分是识别不明确的&#xff0c;所以我们需要进一步调节参数使得选区中的边缘识别更加明…

区分恶意加密货币地址:基于西里尔字母伪装的安全隐患分析

引言 在当前的网络环境中&#xff0c;安全威胁变得越来越复杂&#xff0c;特别是在涉及加密货币交易时&#xff0c;攻击者常常利用各种手段来欺骗用户。最近&#xff0c;研究人员发现了一种利用西里尔字母&#xff08;Cyrillic alphabet&#xff09;伪装的恶意手法&#xff0c…

前端学习笔记-JS篇-03

循环for语句 for 是JavaScript提供的另一种循环控制的话句&#xff0c;它和 while只是语法上存在差异。 for循环基本使用 作用:重复执行代码 好处:把声明起始值、循环条件、变化值写到一起&#xff0c;让人一目了然&#xff0c;它是最常使用的循环形式 for(变量起始值;终止…

猫头虎 分享已解决Bug || TypeError: Cannot read property ‘map‘ of undefined 解决方案

&#x1f42f; 猫头虎 分享已解决Bug || TypeError: Cannot read property map of undefined 解决方案 摘要&#xff1a; 今天猫头虎带大家深入探讨在前端开发中常见的一个令人头疼的问题&#xff1a;TypeError: Cannot read property map of undefined。这个错误通常出现在我…

【Python机器学习】树回归——树回归与标准回归的比较

模型树、回归树和一般的回归方法&#xff0c;为了测试哪个模型最好&#xff0c;可以设计一些函数&#xff0c;它们可以在树构建好的情况下对给定的输入进行预测&#xff0c;之后利用那个这些函数来计算三种回归模型的测试误差。这些模型将在某个数据上进行测试&#xff0c;该数…

DC系列靶场---DC 9靶场的渗透测试

信息收集 地址探测 使用arp-scan 对目标主机做地址探测 arp-scan -l -I eth0 目标主机IP地址为172.30,1,134 端口扫描 使用nmap对目标主机做端口扫描 nmap -sS -sV -T4 -p- -O 172.30.1.134 目标主机开放了80端口&#xff0c;但是22端口是过滤。这个22端口应该是关闭的&a…

95% 向量资源节省,火山引擎云搜索 RAG 技术体系演进

采访嘉宾 | 火山引擎云搜索团队 鲁蕴铖、李杰辉、余炜强 编辑 | Tina InfoQ 2023 年&#xff0c;大模型惊艳了世界。2024 年&#xff0c;RAG 技术如日中天。 RAG 使得大模型能够在不更新模型参数的情况下&#xff0c;获得必要的上下文信息&#xff0c;从而减少大模型的幻觉。…

pytorch,用lenet5识别cifar10数据集(训练+测试+单张图片识别)

目录 LeNet-5 LeNet-5 结构 CIFAR-10 pytorch实现 lenet模型 训练模型 1.导入数据 2.训练模型 3.测试模型 测试单张图片 代码 运行结果 LeNet-5 LeNet-5 是由 Yann LeCun 等人在 1998 年提出的一种经典卷积神经网络&#xff08;CNN&#xff09;模型&#xff0c;主要…

dos命令获取java进程的pid并停止 bat脚本 第二篇

最近要做一个java程序一键重启的功能,主要思路是用批处理命令先将java程序停止,然后重新启动,研究了一把dos命令, taskkill /?取得帮助, taskkill /FI是筛选器: 然后很happy的写好停止脚本如下: taskkill /f /fi "IMAGENAME eq javaw.exe"是不是这样就行了…

spring中使用到的设计模式有哪些

Spring 框架是一个高度模块化和灵活的框架&#xff0c;广泛使用了各种设计模式来实现其核心功能和架构。这些设计模式帮助 Spring 提供了高可配置性、可扩展性和可维护性。以下是 Spring 框架中使用到的一些关键设计模式&#xff1a;

linux 安装jdk步骤

建议用免安装版的&#xff0c;安装方法如下&#xff1a; 一、软件下载 查看系统多少位 getconf LONG_BIT 下载JDK&#xff08;下面分别是32位系统和64位系统下的版本&#xff09; # 32位 http://download.oracle.com/otn-pub/java/jdk/7u9-b05/jdk-7u9-linux-i586.tar.gz?…

来电、消息提醒延时很久,该如何解决

使用华为穿戴设备且同时使用三方安卓手机的朋友们&#xff0c;是否发现自己的华为手表经常接不到电话&#xff0c;接到消息提醒也是延时很久&#xff1f;不是手表有问题&#xff0c;而是因为三方安卓手机系统管控华为运动健康App&#xff0c;导致推动来电和消息有延迟。 若您使…

《实现 DevOps 平台(2) · GitLab CI/CD 交互》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

猫头虎 分享:Python库 Flask 的简介、安装、用法详解入门教程

&#x1f42f; 猫头虎 分享&#xff1a;Python库 Flask 的简介、安装、用法详解入门教程 这是猫头虎带给大家的一篇关于Flask框架的入门教程&#xff01;&#x1f389; 今天猫头虎要跟大家聊聊Python中的一个非常重要且流行的库——Flask。如果你正在寻找一个轻量级、易上手、…

基于CANopen的LabVIEW同步与PDO通信示例

该程序展示了在LabVIEW中使用CANopen协议实现同步消息&#xff08;SYNC&#xff09;和PDO&#xff08;过程数据对象&#xff09;通信的流程。 以下是程序各部分的详细解释&#xff1a; 接口创建 (Interface Create)&#xff1a; 创建一个CANopen接口&#xff0c;并设定通信的波…