vue3.0 / 自己封装组件 / 电商购物网站
require.context(文件夹路径,是否获取子目录,要匹配的文件正则regExp)
@change="handle()"
--> @change=($event)=>handle($event,anyParams)
既拿到事件change传出来的$event又灵活传入自己想要的参数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
}
}
createVnode(组件,{属性})
来创建虚拟节点div
render(vnode,div)
渲染进页面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里的 [规格:值,规格:值]
通过算法获得数组,再转化为字典 {key:skuId,key:skuId}
获得点击的规格数组,一行拿一个点击的值,没有的放入undefined
e.g 比如有三个规格。那么点击的数组 是每一行规则中点击的值 对应 [红色,undefined,undefined]
specs.item | item.values[0] | item.values[1] |
---|---|---|
颜色 | 红色 | 绿色 |
尺寸 | 1米 | 2米 |
产地 | 中国 | 日本 |
初始化/点击规格的按钮的时候调用更新函数fn
fn 把所有按钮信息,一行行循环,一行中,排除已经点击的放入对应位置[绿色,undefined,undefined]
过滤掉undefined ,join转化为key,去字典里找是否有,再判断 [红色,1米,undefined]
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 }
}
}