前端电商项目

项目介绍:

该项目是基于vue-cli创建的一个综合性的电商购物网站,类似于淘宝,京东商城。登录后用户可以在该网站浏览商品,加入购物车,下订单以及各种各样的活动。未登录的用户可以不能进行下单,他们可以通过手机号注册或者qq登录的方式进行登录完成购物。

技术栈:

vue3.0(使用组合API的方式来开发),axios,vue-router(单页路由),vuex(状态管理, vuex数据持久化),@vueuse/core(组合api常用工具库), vee-validate(表单校验)

遇到的问题:

(1)后台接口请求完毕,但是数据缺乏(分类,商品),所以会有一些本地的moke数据

(2)绝大多数第三方UI组件库不支持vue3.0,大多数组件由自己封装(轮播图,复选框,对话框消息提示和消息确认组件,城市选择组件)

(3)第三方登录也是有点难度

vuex-持久化:

让在vuex中存储的状态和数据存储在本地。

在开发过程中,用户的信息(名字,头像,token等)需要在vuex中存储且需要存储在本地

购物车未登录状态下也支持,管理在vuex中的数据需要存储在本地

所以,通过安装vuex插件来支持vuex的状态持久化

请求工具:

基于axios封装的请求工具,调用接口时使用

(1)创建一个新的axios实例

(2)请求拦截器:如果有token进行头部携带

(3)相应拦截器:①剥离无效数据   ②处理token失效

(4)导出一个函数,调用当前的axios实例发请求,返回一个promise

请求拦截器:

instance.interceptors.request.use(config => {
  // 拦截业务逻辑
  // 进行请求配置的修改
  // 如果本地又token就在头部携带
  // 1. 获取用户信息对象
  const { profile } = store.state.user
  // 2. 判断是否有token
  if (profile.token) {
    // 3. 设置token
    config.headers.Authorization = `Bearer ${profile.token}`
  }
  return config
}, err => {
  return Promise.reject(err)
})

响应拦截器:

// res => res.data  取出data数据,将来调用接口的时候直接拿到的就是后台的数据
instance.interceptors.response.use(res => res.data, err => {
  // 401 状态码,进入该函数
  if (err.response && err.response.status === 401) {
    // 1. 清空无效用户信息
    // 2. 跳转到登录页
    // 3. 跳转需要传参(当前路由地址)给登录页码
    store.commit('user/setUser', {})
    // 当前路由地址
    // 组件里头:`/user?a=10` $route.path === /user  $route.fullPath === /user?a=10
    // js模块中:router.currentRoute.value.fullPath 就是当前路由地址,router.currentRoute 是ref响应式数据
    const fullPath = encodeURIComponent(router.currentRoute.value.fullPath)
    // encodeURIComponent 转换uri编码,防止解析地址出问题
    router.push('/login?redirectUrl=' + fullPath)
  }
  return Promise.reject(err)
})

首页:

(1)使用less自动化导入

使用less混入,就是声明一段css代码(选择器包裹的代码)或者函数,在其他css选择器调用,可服用包裹的代码

完成自动注入公用变量和混入遇到的问题:

每次使用公用变量和mixin的时候需要单独引入到文件中

解决方法:使用vuecli的style-resources-loader插件来完成自动注入到每个less文件或者vue组件中style标签中(安装完插件后再vue.config.js中进行配置,然后再重启服务器)

轮播图组价的封装:

暴露一个自动播放属性,通过设置一个定时器来控制自动播放,先设置默认显示图片的索引为0,

const index = ref(0),自动播放根据数组,如果它的索引大于传过来数据数组的长度,则重新把索引设置为0

通过监听传过来的数据,如果有数据开启自动播放,调用自动播放的函数

指示器切换上一张下一张:

就是给指示器添加click事件然后传递参数一个是1,一个是-1分别代表前进和后退

   // 上一张下一张
    const toggle = (step) => {
      const newIndex = index.value + step
      if (newIndex >= props.sliders.length) {
        index.value = 0
        return
      }
      if (newIndex < 0) {
        index.value = props.sliders.length - 1
        return
      }
      index.value = newIndex
    }

    return { index, stop, start, toggle }

vue动画:(Transition组件的使用)

在vue中,显示隐藏创建移除一个元素或一个组件的时候可以通过transition实现动画

进入:(显示,创建)

v-enter 进入前(vue3.0是 v-enter-form)

v-enter-active 进入中

v-enter-to 进入后

离开(隐藏,移除)

v-leave离开前(vue3.0是 v-leave-form)

v-leave-active 离开中

v-leave-to 离开后

多个transition使用不同的动画,可以添加name属性,name属性的值替换v即可

组件数据懒加载:(当前组件进入可视区再加载数据)

使用@vueuse/ core中的useIntersectionObserve函数来实现监听可视区域行为,必须配合vue3.0的组合API的方式才能实现

useIntersectionObserve函数:

// stop 是停止观察是否进入或移出可视区域的行为    
const { stop } = useIntersectionObserver(
  // target 是观察的目标dom容器,必须是dom容器,而且是vue3.0方式绑定的dom对象
  target,
  // isIntersecting 是否进入可视区域,true是进入 false是移出
  // observerElement 被观察的dom
  ([{ isIntersecting }], observerElement) => {
    // 在此处可根据isIntersecting来判断,然后做业务
  },
)

由于首页面板数据加载都需要实现,数据懒加载,所以通过封装好一个钩子函数来得到数据

// hooks 封装逻辑,提供响应式数据。
import { useIntersectionObserver } from '@vueuse/core'
import { ref } from 'vue'
// 数据懒加载函数
export const useLazyData = (apiFn) => {
  // 需要
  // 1. 被观察的对象
  // 2. 不同的API函数
  const target = ref(null)
  const result = ref([])
  const { stop } = useIntersectionObserver(
    target,
    ([{ isIntersecting }], observerElement) => {
      if (isIntersecting) {
        stop()
        // 调用API获取数据
        apiFn().then(data => {
          result.value = data.result
        })
      }
    }
  )
  // 返回--->数据(dom,后台数据)
  return { target, result }
}

面包屑组件的封装:

先准备一个xtx-bread.vue组件,定义props暴露parentPath和parentName属性,默认插槽,动态渲染组件,然后再注册组件全局使用

由于一个一个注册组件有点麻烦,要注册的组件很多,所以就采用了自动批量注册组件的方式:

(1)使用require提供的函数context加载某一目录下的所有.vue后缀的文件

(2)然后context函数会返回一个导入函数importFn(它又有一个属性keys()获取所有的文件路径)

(3)通过文件路劲数组,通过遍历数组,再使用importFn根据路径导入对象

(4)然后再遍历的同时进行全局注册

// 批量导入需要使用一个函数 require.context(dir,deep,matching)
// 参数:1. 目录  2. 是否加载子目录  3. 加载的正则匹配
const importFn = require.context('./', false, /\.vue$/)
// console.dir(importFn.keys()) 文件名称数组
export default {
  install (app) {
    // 批量注册全局组件
    importFn.keys().forEach(key => {
      // 导入组件
      const component = importFn(key).default
      // 注册组件
      app.component(component.name, component)
    })
  }
}

复选框组件的封装:

主要是通过v-mode指令来实现这个组件的封装


import { ref, watch } from 'vue'
// v-model  ====>  :modelValue  +   @update:modelValue
export default {
  name: 'XtxCheckbox',
  props: {
    modelValue: {
      type: Boolean,
      default: false
    }
  },
  setup (props, { emit }) {
    const checked = ref(false)
    const changeChecked = () => {
      checked.value = !checked.value
      // 使用emit通知父组件数据的改变
      emit('update:modelValue', checked.value)
    }
    // 使用侦听器,得到父组件传递数据,给checked数据
    watch(() => props.modelValue, () => {
      checked.value = props.modelValue
    }, { immediate: true })
    return { checked, changeChecked }
  }
}

也可以用@vueuse/ core中的useVModel来实现数据双向绑定

setup (props, { emit }) {
    // 使用useVModel实现双向数据绑定v-model指令
    // 1. 使用props接收modelValue
    // 2. 使用useVModel来包装props中的modelValue属性数据
    // 3. 在使用checked.value就是使用父组件数据
    // 4. 在使用checked.value = '数据' 赋值,触发emit('update:modelvalue', '数据')
    const checked = useVModel(props, 'modelValue', emit)
    const changeChecked = () => {
      const newVal = !checked.value
      // 通知父组件
      checked.value = newVal
      // 让组件支持change事件
      emit('change', newVal)
    }
    return { checked, changeChecked }
  }

表单校验:使用vee-valideta插件来进行表单校验

导入:

import { Form, Field } from 'vee-validate'

 定义一个校验规则的js文件提供给vee-validate使用:(提取出来的目的是为了将来在其他表单校验的时候复用)

  // 校验account
  account (value) {
    // value是将来使用该规则的表单元素的值
    // 1. 必填
    // 2. 6-20个字符,需要以字母开头
    // 如何反馈校验成功还是失败,返回true才是成功,其他情况失败,返回失败原因。
    if (!value) return '请输入用户名'
    if (!/^[a-zA-Z]\w{5,19}$/.test(value)) return '字母开头且6-20个字符'
    return true
  }

在组件中,使用form组件,使用vee-validate-schema校验规则

把form表单换成这样:

...

校验:

  setup () {
    // 控制短信登录切换的
    const isMsgLogin = ref(false)
    // 表单对象数据
    const form = reactive({
      isAgree: true,
      account: null,
      password: null,
      mobile: null,
      code: null
    })
    // 校验规则对象
    const mySchema = {
      account: schema.account,
      password: schema.password,
      mobile: schema.mobile,
      code: schema.code,
      isAgree: schema.isAgree
    }
    return { isMsgLogin, form, scheam:mySchema, submit }
 } 

使用field组件,添加表单项目校验:就是把所有的input标签换成field,默认解析成input,Field添加name属性,作用是指定schema中哪个校验规则

整体进行表单验证:

登 录
    // 需要在点击登录的时候对整体表单进行校验
    const login = async () => {
      // Form组件提供了一个 validate 函数作为整体表单校验,当是返回的是一个promise
      const valid = await formCom.value.validate()
      console.log(valid)
    }

    return { isMsgLogin, form, schema: mySchema, formCom, login }

登录:

手机号和账号登录都是通过vee-validate表单验证

重难点是QQ登录:

(1)在登录页面,QQ登录图片处,赋予其打开QQ登录页面的功能

在index.html页面添加(appid,登录页面,回跳页面,QQ互联的链接)

在vue.config.js中添加设置外部扩展,模块为qc的设置为QC,导入qc将不做打包 

看页面新生成的QQ登录按钮,点击后新窗口打开,登录成功后也无法跳转到登录页面窗口

通过审查元素,找到跳转链接,自己来控制

(2)回跳的页面得到QQ给的唯一标识openId,根据openId去后台查询是否已经绑定过账户

如果绑定过,完成登录

如果没有绑定:

①有账号的绑定手机号,即为登录

②没账号的,完善账户信息,即为登陆

(3)登录成功后,跳转首页,或者来源页面

。。。

你可能感兴趣的:(javascript,开发语言)