VUE3 电商项目浅浅总结一下

背景:

vue3.0 / 自己封装组件 / 电商购物网站

亮点:

  1. 自己封装的一些基础组件(骨架屏、checkbox、下拉刷新、分页组件、消息提示、消息确认框)
  2. SKU业务
  3. 自己封装的message提示消息 全局函数式调用

学到了

  1. 购物车操作通常分两种:本地和后端,操作用promise封装兼容同步和异步操作
  2. 批量获取路径,注册组件require.context(文件夹路径,是否获取子目录,要匹配的文件正则regExp)
  3. @change="handle()" --> @change=($event)=>handle($event,anyParams) 既拿到事件change传出来的$event又灵活传入自己想要的参数
  4. 懒加载:可视区加载数据,可视区加载图片
  5. actions(ctx) ctx属性模块中的还包含rootState,可以跨模块拿到其他命名空间下的vuex数据
例如:现在为cart模块,去拿user模块下的list
actions(ctx){
 ctx.rootState.user.list
}

getters:{
A(){
    return 'aaa'
}
B(state, getters){
   getters.A  //拿到同getters下其他函数 
} 

总结

批量获取路径,注册组件

// import XtxSkeleton from './xtx-skeleton.vue'
// import XtxCarousel from './xtx-carousel.vue'
const importFn = require.context('./', false, /\.vue$/)
// 【importFn 2个功能:
//(1)importFn.key()获取所有符合条件的路径数组,
//(2)导入单个文件】
import XtxMessageFn from './Message'

export default {
  install (app) {
    // app.component(XtxSkeleton.name, XtxSkeleton)
    // app.component(XtxCarousel.name, XtxCarousel)
    importFn.keys().forEach(key => {
      const component = importFn(key).default
      app.component(component.name, component)// 组件注册
    })
    defineDirective(app)
    app.config.globalProperties.$message = XtxMessageFn
  }
} 

全局函数调用组件

  1. 准备好组件(props接受数据)
  2. 准备js文件
    • 引入组件
    • createVnode(组件,{属性}) 来创建虚拟节点
    • 准备容器 div
    • render(vnode,div) 渲染进页面
    • 根据不同组件 特殊处理
  3. 将js文件挂载app.config.globalProperties.$xxx=js文件就能全局this.$xxx调用
/** 
全局函数式调用的消息确认框
使用方法: Confirm({ text: '您确定从购物车删除该商品吗?' }).then().catch()
返回的要是一个promise 。点击取消的时候触发catch,点击确认的时候触发then
*/

import { createVNode, render } from 'vue'
import XtxConfirm from './xtx-confirm.vue' //1.引入

const div = document.createElement('div')  // 3. 准备容器,插入body
div.setAttribute('class', 'xtx-confirm-container')
document.body.appendChild(div)

export default ({title, text}) => {
 // 2.单文件组件渲染成虚拟dom :createVNode(要渲染的组件,组件上的属性对象props) 

 return new Promise((resolve, reject) => {
    // 确认时触发 then 所以resolve
    const submitFn = () => {
      render(null, div)
      resolve()
    }

    // 取消时触发catch,所以reject
    const cancelFn = () => {
      render(null, div)
      reject(new Error('点击取消'))
    }

    //【组件中点击取消/确认,触发js文件promise的reject/resolve】
    //【既然参数可以传递props传递给确认框组件,那么函数Fn也可以】
    const vnode = createVNode(XtxConfirm, { title, text, submitFn, cancelFn })
    // 4. 容器种放入虚拟dom
    render(vnode, div)//确认框什么时候渲染进页面?创建promise的时候 
  })
} 

SKU业务部分

商品信息接口返回的数据如下:

VUE3 电商项目浅浅总结一下_第1张图片

思路:

  1. 提取每条组合SKU里的 [规格:值,规格:值]

  2. 通过算法获得数组,再转化为字典 {key:skuId,key:skuId}

  3. 获得点击的规格数组,一行拿一个点击的值,没有的放入undefined

    e.g 比如有三个规格。那么点击的数组 是每一行规则中点击的值 对应 [红色,undefined,undefined]

specs.item item.values[0] item.values[1]
颜色 红色 绿色
尺寸 1米 2米
产地 中国 日本
  1. 初始化/点击规格的按钮的时候调用更新函数fn

    fn 把所有按钮信息,一行行循环,一行中,排除已经点击的放入对应位置[绿色,undefined,undefined] 过滤掉undefined ,join转化为key,去字典里找是否有,再判断 [红色,1米,undefined]

VUE3 电商项目浅浅总结一下_第2张图片

const spliter = '★'
import getPowerSet from '@/vendor/power-set'
const getPathMap = (skus) => {
  // 2.1 目标格式:每个skus下的specs数组中的valueName提取成:['红色','1米'] 
  const dictionary = {}
  skus.forEach(sku => {
    const skuArr = sku.specs.map(obj => obj.valueName)
    // powerSet是幂集算法后的结果 数组  [[红色],[1米],[红色,1米]...] 
    getPowerSet(skuArr).forEach(arr => {
      const key = arr.join(spliter)
      // 2.2 设置给路径字典对象,把数组下的每个小数组拼接成字符串作为key,把skuId作为value 
      dictionary[key] ? dictionary[key].push(sku.id) : dictionary[key] = [sku.id]
    }
    )
  })
  return dictionary
}
export default {
  name: 'GoodsSku',
  props: {
    goods: {
      type: Object,
      default: () => { }
    }
  },
  setup (props, { emit }) {
    // goods.specs 里面每个obj都是一个规格属性,obj.values是次规格下的可选项
    // 1.按钮点击事件 效果:点击选中,点击取消。点击别的本按钮取消选中
    const clickSpecs = (specsObj, btnObj) => {
      // btnObj是点击的那个具体按钮信息(红色)。specsObj是这一行的规格信息{颜色,valuse:[]}
      // 点击的那一项btn添加selected属性
      if (btnObj.disabled) return
      if (btnObj.selected) {
        btnObj.selected = false
      } else {
        specsObj.values.forEach(item => {
          item.selected = false
        })
        btnObj.selected = true
      }
      updateDisabledStatus(props.goods.specs, pathMap)
    }

    // 2.拿到有效路径字典
    const pathMap = getPathMap(props.goods.skus)
   
    // 3.拿到点击的每个规格名字 selectedArr:[红色,undefined] 都是已经选择的属性名字
    const getSelectedArr = (specs) => {
      // 一行里就拿被点击的那个,都没有就undefined占位
      const selectedArr = []
      specs.forEach(obj => {//
        const targetBtn = obj.values.find(btn => btn.selected)
        targetBtn ? selectedArr.push(targetBtn.name) : selectedArr.push(undefined)
      })
      return selectedArr
    }


    // 4 更新按钮状态的函数 触发时机:初始化 或者 点击按钮
    const updateDisabledStatus = (specs, pathMap) => {
      // 约定每个按钮的能否点击的状态由 【disabled属性】来决定
      // a.循环规则属性下的每个按钮,如果已经选择 则忽略 
      // b.满足要求的每一个都依次放入对比数组selectedArr中 按照顺序 
      // c.剔除undefined的 [红色,M,undefined]==> [红色,1米] 再拼接成key
      // d. 根据key去字典里查询 是否存在。
     // const selectedArr = getSelectedArr(specs) //放这最后一次循环会替换掉[绿色,undefined]从匹配错
      specs.forEach((item, i) => {
       const selectedArr = getSelectedArr(specs) //点击了 [白色,undefined] ,每次对比都拿这个对比
        item.values.forEach(btn => {
          if (btn.selected) return
          selectedArr[i] = btn.name
          const key = selectedArr.filter(item => item).join(spliter)
          btn.disabled = !pathMap[key]
        })
      })

    }
    // 4.1 组件初始化的时候更新禁用状态
    updateDisabledStatus(props.goods.specs, pathMap)

    return { clickSpecs }
  }
} 

你可能感兴趣的:(前端,javascript,vue.js)