商城项目整体构建

Posted Coding With you.....

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了商城项目整体构建相关的知识,希望对你有一定的参考价值。

一、

1.项目结构分配、模块的定义、组件封装调用(先定义、然后导入 注册 使用)、路由拦截、cookie理解、token权限、路由权限管理、动态路由、打包部署、vuex状态管理、接口文档

对于跳转,要先设置路由确保组件可以正常使用、路由可以正常跳转,再充实页面

对于左边导航与上边面包屑的联动,可以通过this.$route.matched获取该路由的所有信息,然后在每一个路由中写一个meta对应路由的内容,在面包屑那块通过遍历当前路由信息展示meta的值

对于导航栏的多级菜单,可以通过递归进行展示

父子组件传值的时候,子组件中通过props接收父组件传过来的值

二、流程

1.脚手架创建项目,并且 关闭eslint校验以防写代码时没错也报错

node_modules:放置项目依赖的地方。
public:一般放置一些共用的静态资源,打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面。
src:程序员源代码文件夹:
assets:经常放置一些静态资源(公用的图片(即很多组件都用此图)),assets文件夹里面资源webpack会进行打包为一个模块(js文件夹里面)
components:一般放置非路由组件(如共用的组件)
App.vue:唯一的根组件
main.js:入口文件【程序最先执行的文件】
babel.config.js:babel配置文件
package.json:项目描述、项目依赖、项目运行
README.md:项目说明文件

2.在开发项目的时候:

非路由组件:

  • 书写静态页面(html + CSS)
  • 拆分组件
  • 获取服务器的数据动态展示
  • 完成相应的动态业务逻辑

路由组件

  • 创建组件:Vue.component(tagName, options)                   

                         var 组件内容 = Vue.extend(template: '<div>自定义全局组件,使用Vue.extend</div>')Vue.component("组件名称",组件内容)

  • 在router中创建并配置具体路由
  • 在main.js中引入进行全局注册路由
  • 在app.vue中:<!-- 路由组件出口的地方、路由组件展示 --> <router-view></router-view>
  • 在需要的地方引入标签

3.mockjs模拟数据 

使用Mock.js插件,生成随机数据,拦截 Ajax 请求。

  • 前后端分离:让前端攻城师独立于后端进行开发。
  • 增加单元测试的真实性:通过随机数据,模拟各种场景。
  • 开发无侵入:不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据。
  • 用法简单:符合直觉的接口。
  • 数据类型丰富:支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等。
  • 方便扩展:支持支持扩展更多数据类型,支持自定义函数和正则。

4.购物车的管理

1.逻辑:点击加入购物车,将详情页的数据加入购物车中;

此时需要对state中的cartList进行修改,点击加入购物车就会执行store文件中actions对象里定义的加入购物车方法,actions对象中对加入的商品进行判断,若之前商品已存在,则数量加1,若商品不存在,则将商品添加进cartList。其返回一个promise对象。

2.组件:在购物车模块里可以查看详细信息 更新数量,全选反全选  取消加购 去结款

  • 将加入购物车中的商品对象放到cartList中了,商品对象中包括需要在购物车中展示的详细信息。首先将通过store中的getters属性接收vuex中的数据,通过v-for来对cartList中的商品对象进行遍历,同时展示单个商品信息的组件通过props来接收父组件传来的单个商品对象;在计算属性computed中接收vuex中挂载的数据。
  • 通过数组的filter方法找出选中的商品 ,然后通过数组的reduce方法对选中商品的价值总额进行计算;
  • 全选的逻辑:若部分商品或者全部商品未被选中,则利用forEach使cartList中的每个商品为选中状态;若全部选中,则使cartList中的每个商品为未选中状态

3.数据交互:通过vuex状态管理机制来实现购物车的数据交互,创建store文件并挂载在vue实例上,在store文件中定义一个可以挂载数据的state,在其中定义一个数组cartList来存放商品信息,其他组件就可以获取并使用这个数据了。

store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
 
export default new Vuex.Store(
    state: 
        carList: [] //购物车的商品
    ,
    mutations: 
        // 加
        addCar(state, params) 
            let CarCon = state.carList;
            // 判断如果购物车是否有这个商品,有就只增加数量,否则就添加一个
            // some 只要有一个isHas为true,就为true
            let isHas = CarCon.some((item) => 
                if (params.id == item.id) 
                    item.num++;
                    return true;
                 else 
                    return false;
                
            )
 
            if (!isHas) 
                let obj = 
                    "id": params.id,
                    "title": params.title,
                    "price": params.price,
                    "num": 1,
                
                this.state.carList.push(obj)
            
        ,
        // 减
        reducedCar(state,params)
            let len=state.carList.length;
            for(var i=0;i<len;i++)
                if(state.carList[i].id==params.id)
                    state.carList[i].num--
                    if(state.carList[i].num==0)
                        state.carList.splice(i,1);
                        break;
                    
                
            
        ,
        //移出
        deleteCar(state,params)
            let len=state.carList.length;
            for(var i=0;i<len;i++)
                if(state.carList[i].id==params.id)
                    state.carList.splice(i,1);
                    break;
                
            
        ,
 
         // 初始化购物车,有可能用户一登录直接进入购物车
        // initCar(state, car) 
        //     state.carList = car
        // ,
 
    ,
    actions: 
        // 加
        addCar( commit , params) 
            // console.log(params) //点击添加传过来的参数
            // 使用setTimeout模拟异步获取购物车的数据
            setTimeout(function () 
                let result = 'ok'
                if (result == 'ok') 
                    // 提交给mutations
                    commit("addCar", params)
                
            , 100)
        ,
        // 减
        reducedCar( commit , params) 
            // console.log(params) //点击添加传过来的参数
            // 使用setTimeout模拟异步获取购物车的数据
            setTimeout(function () 
                let result = 'ok'
                if (result == 'ok') 
                    // 提交给mutations
                    commit("reducedCar", params)
                
            , 100)
        ,
        // 移出
        deleteCar( commit , params) 
            // console.log(params) //点击添加传过来的参数
            // 使用setTimeout模拟异步获取购物车的数据
            setTimeout(function () 
                let result = 'ok'
                if (result == 'ok') 
                    // 提交给mutations
                    commit("deleteCar", params)
                
            , 100)
        
        // initCar( commit ) 
        //     setTimeout(function () 
        //         let result = 'ok'
        //         if (result == 'ok') 
        //             // 提交给mutations
        //             commit("initCar", [
        //                 "id": 20193698,
        //                 "title": '我是购物车原来的',
        //                 "price": 30,
        //                 "num": 100,
        //             ])
        //         
        //     , 100)
        // 
    ,
    getters: 
        //返回购物车的总价
        totalPrice(state) 
            let Carlen = state.carList;
            let money = 0;
            if (Carlen.length != 0) 
                Carlen.forEach((item) => 
                    money += item.price * item.num
                )
                return money;
             else 
                return 0;
            
        ,
        //返回购物车的总数
        carCount(state) 
            return state.carList.length
        
    ,
)


list.vue

<template>
  <!-- 商品列表 -->
  <div id="listBox">
    <!--  -->
    <router-link :to="path:'/car'" style="line-height:50px">跳转到购物车</router-link>
    <el-table :data="tableData" border style="width: 100%">
      <el-table-column fixed prop="id" align="center" label="商品id"></el-table-column>
      <el-table-column prop="title" align="center" label="商品标题"></el-table-column>
      <el-table-column prop="price" align="center" label="商品价格"></el-table-column>
      <el-table-column label="操作" align="center">
        <template slot-scope="scope">
          <el-button @click="addCar(scope.row)" type="text" size="small">加入购物车</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
 
<script>
export default 
  name: "listBox",
  data() 
    return 
      tableData: [] //商品列表
    ;
  ,
  methods: 
    // 初始化商品列表
    initTable()
      this.$gAjax(`../static/shopList.json`)
        .then(res => 
          console.log(res)
          this.tableData=res;
        )["catch"](() => );
    ,
    // 加入购物车
    addCar(row)
      // console.log(row)
      // 提交给store里面actions 由于加入购物车的数据要同步到后台
      this.$store.dispatch('addCar',row)
    
    
  ,
  mounted () 
    this.initTable()
  
;
</script>
<style>
#listBox 
  width: 900px;
  margin: 0 auto;

</style




cart.vue
<template>
  <!-- 购物车 -->
  <div id="carBox">
    <!-- 商品总数 -->
    <h2 style="line-height:50px;font-size:16px;font-weight:bold">合计:总共count个商品,总价totalPrice元</h2>
    <p v-if="count==0">空空如也!·······</p>
    <div v-else>
      <el-table :data="carData" border style="width: 100%">
        <el-table-column fixed prop="id" align="center" label="商品id"></el-table-column>
        <el-table-column prop="title" align="center" label="商品标题"></el-table-column>
        <el-table-column prop="price" align="center" label="商品价格"></el-table-column>
        <el-table-column label="操作" align="center">
          <template slot-scope="scope">
            <el-button @click="reduceFun(scope.row)" type="text" size="small">-</el-button>
            <span >scope.row.num</span>
            <el-button @click="addCar(scope.row)" type="text" size="small">+</el-button>
 
            <el-button @click="deleteFun(scope.row)" type="text" size="small">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
 
<script>
export default 
  name: "carBox",
  data() 
    return ;
  ,
  computed: 
    //购物车列表
    carData() 
      return this.$store.state.carList;
    ,
    //商品总数
    count() 
      return this.$store.getters.carCount;
    ,
    //商品总价
    totalPrice() 
      return this.$store.getters.totalPrice;
    
  ,
  methods: 
    // 增加数量
    addCar(row)
       this.$store.dispatch('addCar',row)
    ,
    // 减数量
    reduceFun(row)
       this.$store.dispatch('reducedCar',row)
    ,
    // 删除
    deleteFun(row)
        this.$store.dispatch('deleteCar',row)
    
 
    // 用户首次登录请求购物车的数据
    // initCar()
    //   this.$store.dispatch('initCar')
    // 
  ,
  created () 
    // this.initCar();
  ,
  mounted() 
;
</script>
 
<style>
#carBox 
  width: 900px;
  margin: 0 auto;

</style>

三、知识点记录

路由跳转的两种方式:

  • 声明式导航:router-link,可以进行路由的跳转<router-link to="/login">登录</router-link>
  • 编程式导航:利用组件实例的 $router.push | replace,可以进行路由跳转

编程式导航:声明式导航能做的,编程式导航都能;但是编程式导航除了可以进行路由跳转,还可以做一些其他的业务逻辑。

路由元信息:

将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过$route 的 meta属性 来实现,并且它可以在路由地址和导航守卫上都被访问到。

路由参数传递

params参数: 属于路径当中的一部分,需要注意,在配置路由的时候,需要占位

query参数: 不属于路径当中的一部分,类似于ajax中的queryString /home?k=v&kv=,不需要占位

路由传递参数(对象写法) path是否可以结合 params参数一起使用? 即:

this.$router.push( path: '/search', params: keyword: this.keyword , query: k: this.keyword.toUpperCase() , );

答:报错,不能。

如何指定 params参数 可传可不传? 即:

this.$router.push( name: "search", query: k: this.keyword.toUpperCase() , );

答:配置路由时,path上加个 ? 号,代表可传参数也可不传;若不加 ? ,则URL会出现问题。
params参数 可以传递也可以不传递,但是如果传递是空串,如何解决? 

可以使用 undefined 来解决params参数可以传递也可不传递(空的字符串)

路由组件能不能传递 props数据?

可以采用三种写法

 
            path: "/search/:keyword",
            component: Search,
            meta:  show: true ,
            // 对象形式路由传递参数
            name: "search",
            // 路由组件能不能传递 props数据?
            // 1、布尔值写法,但是这种方法只能传递params参数
            // props: true,
            // 2、对象写法:额外给路由组件传递一些props
            // props:  a: 1, b: 2 ,
            // 函数写法(常用):可以params参数、query参数,通过props传递给路由组件
            props: ($route) => 
                return keyword: $route.params.keyword, k: $route.query.k;
            
        ,

为什么进行axios 二次封装

为了请求拦截器、响应拦截器。

请求拦截器:在发请求之前可以处理一些业务;

响应拦截器:当服务器数据返回以后,可以处理一些事情。

// 对于axios进行二次封装
import axios from "axios"

// 利用axios对象的方法create,去创建一个axios实例
// 这里的request 就是 axios,在这里配置一下
const request = axios.create(
    // 配置对象
    // 基础路径,发请求的时候,路径当中会默认有/api,不用自己写了
    baseURL: "/api",
    // 请求超时5s
    timeout: 5000,
)

// 请求拦截器:在发请求之前,请求拦截器可以检测到,在请求发出之前做一些事情;
requests.interceptors.request.use((config) => 
    // config:配置对象,其有一个重要属性:header请求头

)
// 响应拦截器:当服务器数据返回以后,可以处理一些事情。
requests.interceptors.response.use(((res) => 
    // 服务器响应成功的回调函数
    return res.data;
, (error) => 
    // 服务器响应失败的回调函数
    return Promise.reject(new Error('faile'));
))


// 对外暴露
export default requests;

API接口统一管理

若项目很小,可以在组件的生命周期函数中发请求

但项目大,组件多,若有更改,将麻烦。所以API接口统一管理。比如跨域的代理可以统一管理

nprogress进度条的使用

在响应拦截器使用

// 请求拦截器:
requests.interceptors.request.use((config) => 
    // config:配置对象,其有一个重要属性:header请求头
    // 进度条开始动
    nprogress.start();
    return config;

)
// 响应拦截器:
requests.interceptors.response.use((res) => 
    // 服务器响应成功的回调函数
    // 进度条结束
    nprogress.done();
    return res.data;
, (err) => 
    // 服务器响应失败的回调函数
    return Promise.reject(new Error('faile'));
)

vuex 模块式开发

vuex 是官方提供的插件, 状态管理库,集中式管理项目中组件共用的数据 。

切记,并不是全部项目都需要 Vuex,如果项目很小,完全不需要Vuex,如果项目很大,组件很多、数据很多,数据维护很费劲,用Vuex

图片懒加载

// 引入图片懒加载插件
import VueLazeload from 'vue-lazyload';
// 引入懒加载默认图片(即真实图片没加载好之前,加载时显示的图片)
import tp from '@/assets/images/1.png';
// 注册插件
Vue.use(VueLazeload, 
  // 懒加载默认图片,(即真实图片没加载好之前,加载时显示的图片)
  loading: tp,
)




<!-- v-lazy自定义指令图片懒加载 -->
<img v-lazy="good.defaultImg" />

以上是关于商城项目整体构建的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记:Vue+Node+Mongodb构建简单商城系统

前端构建效率优化之路

微信服务号+Yii 2.0构建商城系统全栈应用

前端实现模拟购物商城案例实现

普歌-前端实现模拟购物商城案例实现

Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面,使用域名访问本地项目)