B2C-02-项目基础架构

260 阅读4分钟

项目起步

创建项目

目的:使用vue-cli创建一个vue3.0项目。


第一步:打开命令行窗口。
1603852293974.png

  • 注意,所在目录将会是你创建项目的目录。


第二步:执行创建项目命令行

1603852444971.png

第三步:选择自定义创建
1603852670808.png
第四步:选中vue-router,vuex,css Pre-processors选项
1603852792935.png
第五步:选择vue3.0版本
1603852839087.png
第六步:选择hash模式的路由
1603852990684.png
第七步:选择less作为预处理器
1603853031917.png

第八步:选择 standard 标准代码风格
1603853095879.png
第九步:保存代码校验代码风格,代码提交时候校验代码风格
1603853195466.png
第十步:依赖插件或者工具的配置文件分文件保存
1603853269591.png
第十一步:是否记录以上操作,选择否
1603853342559.png
第十二步:等待安装...
1603853394460.png
最后:安装完毕
1603853818009.png

目录调整

目的:对项目功能模块进行拆分。


大致步骤:

  • 删除无用代码和文件
  • 完善项目的基础结构
  • 读懂默认生成的代码


落的代码:

1605596588487.png

注意:以上结构目录及供参考

需要注意的一些文件:

  • router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'

const routes = [

]

// 创建路由实例
const router = createRouter({
  // 使用hash方式实现路由
  history: createWebHashHistory(),
  // 配置路由规则,写法和之前一样
  routes
})

export default router

vue3.0中createRouter来创建路由实例,createWebHashHistory代表使用hash模式的路由。

  • store/index.js
import { createStore } from 'vuex'

// 创建vuex仓库并导出
export default createStore({
  state: {
    // 数据
  },
  mutations: {
    // 改数据函数
  },
  actions: {
    // 请求数据函数
  },
  modules: {
    // 分模块
  },
  getters: {
    // vuex的计算属性
  }
})

vue3.0中createStore来创建vuex实例。

  • main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// 创建一个vue应用实例
createApp(App).use(store).use(router).mount('#app')

vue3.0中createApp来创建应用app。


额外增加两个配置文件:

  • jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
    }
  },
  "exclude": ["node_modules", "dist"]
}

当我们使用路径别名@的时候可以提示路径。

  • .eslintignore
/dist
/src/vender

eslint在做风格检查的时候忽略 dist 和 vender 不去检查。

基于git管理项目

  • git init
  • git add .
  • git commit -m '初始化仓库'
  • 创建远程仓库
  • 添加远程仓库的别名 git remote add origin https://gitee.com/wzj1031/erabbit-128.git
  • 推送代码 git push -u origin master

vuex-基础

目的:知道每个配置作用,根模块vue3.0的用法,带命名空间模块再vue3.0的用法

  1. 根模块的用法


定义

import { createStore } from 'vuex'
// vue2.0 创建仓库 new Vuex.Store({})
// vue3.0 创建仓库 createStore({})
export default createStore({
  // 数据
  state: {
    info: '王浩'
  },
  // vuex的计算属性
  getters: {
    fullInfo (state) {
      return state.info + '😁😁😁'
    }
  },
  // 改数据函数
  mutations: {
    updateInfo (state, payload) {
      state.info = payload
    }
  },
  // 请求数据函数
  actions: {
    updateInfo (context, payload) {
      setTimeout(() => {
        context.commit('updateInfo', payload)
      }, 1000)
    }
  },
  // 分模块
  modules: {}
})


使用

<template>
  <!-- vue2.0需要根元素,vue3.0可以是代码片段 Fragment -->
  <div>
    <!-- 1. 使用根模块state的数据   -->
    <div>{{ $store.state.info }}</div>
    <!-- 2. 使用根模块getters的数据   -->
    <div>{{ $store.getters.fullInfo }}</div>
    <button @click="handleClick">点击</button>
  </div>
</template>
<script>
import { useStore } from 'vuex'
export default {
  name: 'App',
  setup () {
    // 获取store对象不可以写到事件函数中
    const store = useStore()
    const handleClick = () => {
      // 获取store对象
      // 触发mutation
      // store.commit('updateInfo', '你好啊')
      // 触发action
      store.dispatch('updateInfo', 'hi')
    }
    return { handleClick }
  }
}
</script>
  1. modules  (分模块)
  • 存在两种情况
    • 默认的模块,state 区分模块,其他 getters mutations actions  都在全局。
    • 带命名空间  namespaced: true  的模块,所有功能区分模块,更高封装度和复用。
import { createStore } from 'vuex'

const moduleA = {
  // 子模块state建议写成函数
  state: () => {
    return {
      username: '模块A'
    }
  },
  getters: {
    changeName (state) {
      return state.username + 'AAAAAA'
    }
  }
}

const moduleB = {
  // 带命名空间的模块
  namespaced: true,
  // 子模块state建议写成函数
  state: () => {
    return {
      username: '模块B'
    }
  },
  getters: {
    changeName (state) {
      return state.username + 'BBBBBB'
    }
  },
  mutations: {
    // 修改名字的mutation
    update (state) {
      state.username = 'BBBB' + state.username
    }
  },
  actions: {
    update ({ commit }) {
      // 假设请求
      setTimeout(() => {
        commit('update')
      }, 2000)
    }
  }
}

// 创建vuex仓库并导出
export default createStore({
  state: {
    // 数据
    person: [
      { id: 1, name: 'tom', gender: '男' },
      { id: 2, name: 'lucy', gender: '女' },
      { id: 3, name: 'jack', gender: '男' }
    ]
  },
  mutations: {
    // 改数据函数
  },
  actions: {
    // 请求数据函数
  },
  modules: {
    // 分模块
    a: moduleA,
    b: moduleB
  },
  getters: {
    // vuex的计算属性
    boys: (state) => {
      return state.person.filter(p => p.gender === '男')
    }
  }
})


使用:

<template>
 <div>APP组件</div>
 <ul>
   <li v-for="item in $store.getters.boys" :key="item.id">{{item.name}}</li>
 </ul>
 <!-- 使用模块A的username -->
 <p>A的username --- {{$store.state.a.username}}</p>
 <p>A的changeName --- {{$store.getters.changeName}}</p>
 <hr>
 <p>B的username --- {{$store.state.b.username}}</p>
 <p>B的changeName --- {{$store.getters['b/changeName']}}</p>
 <button @click="$store.commit('b/update')">修改username</button>
 <button @click="$store.dispatch('b/update')">异步修改username</button>
</template>

vuex-持久化

目的:让在vuex中管理的状态数据同时存储在本地。可免去自己存储的环节。

  • 在开发的过程中,像用户信息(名字,头像,token)需要vuex中存储且需要本地存储。
  • 再例如,购物车如果需要未登录状态下也支持,如果管理在vuex中页需要存储在本地。
  • 我们需要category模块存储分类信息,但是分类信息不需要持久化。


1)首先:我们需要安装一个vuex的插件vuex-persistedstate来支持vuex的状态持久化。

npm i vuex-persistedstate


2)然后:在src/store 文件夹下新建 modules 文件,在 modules 下新建 user.js  和 cart.js

src/store/modules/user.js

// 用户模块
export default {
  namespaced: true,
  state () {
    return {
      // 用户信息
      profile: {
        id: '',
        avatar: '',
        nickname: '',
        account: '',
        mobile: '',
        token: ''
      }
    }
  },
  mutations: {
    // 修改用户信息,payload就是用户信息对象
    setUser (state, payload) {
      state.profile = payload
    }
  }
}


src/store/modules/cart.js

// 购物车状态
export default {
  namespaced: true,
  state: () => {
    return {
      list: []
    }
  }
}


src/store/modules/category.js

// 分类模块
export default {
  namespaced: true,
  state () {
    return {
      // 分类信息集合
      list: []
    }
  }
}


3)继续:在 src/store/index.js 中导入 user cart 模块。

import { createStore } from 'vuex'

import user from './modules/user'
import cart from './modules/cart'
import cart from './modules/category'

export default createStore({
  modules: {
    user,
    cart,
    category
  }
})


4)最后:使用vuex-persistedstate插件来进行持久化

import { createStore } from 'vuex'
+import createPersistedstate from 'vuex-persistedstate'

import user from './modules/user'
import cart from './modules/cart'
import cart from './modules/category'

export default createStore({
  modules: {
    user,
    cart,
    category
  },
+  plugins: [
+    createPersistedstate({
+      key: 'erabbit-client-pc-store',
+      paths: ['user', 'cart']
+    })
+  ]
})


注意:

===> 默认是存储在localStorage中

===> key是存储数据的键名

===> paths是存储state中的那些数据,如果是模块下具体的数据需要加上模块名称,如user.token

===> 修改state后触发才可以看到本地存储数据的的变化。

测试: user模块定义一个mutation在main.js去调用下,观察浏览器application的localStorage下数据。

src/App.js

<template>
  <div class="container">
    <!-- 修改数据,测试是否持久化 -->
    App {{$store.state.user.profile.account}}
    <button @click="$store.commit('user/setUser',{account:'zhousg'})">设置用户信息</button>
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>

请求工具

目的:基于axios封装一个请求工具,调用接口时使用。

  • 安装 axios
npm i axios
  • 新建 src/utils/request.js 模块,代码如下
// 封装通用的接口调用模块----------------------
import axios from 'axios'
import store from '@/store'
import router from '@/router'

// ------------------请求的基准路径-----------------------------
export const baseURL = ''

// 创建独立的axios的实例
const instance = axios.create({
  baseURL: baseURL,
  timeout: 5000
})

// ------------请求拦截器:统一添加请求头--------------------
instance.interceptors.request.use((config) => {
  // 判断Vuex中是否有token,如果有就添加到请求头
  const token = store.state.user.profile.token
  // 2. 判断是否有token
  if (token) {
    // 3. 设置token
    config.headers.Authorization = 'Bearer ' + token
  }
  return config
}, (err) => {
  return Promise.reject(err)
})

// ------------响应拦截器:处理后端返回的数据,把data属性去掉----------------
instance.interceptors.response.use((response) => {
  return response.data
}, (err) => {
  // 处理token的过期操作
  if (err.response && err.response.status === 401) {
    // token过期了,清空过期的用户信息,跳转到登录页面
    store.commit('user/updateUserInfo', {})
    router.push('/login')
  }
  return Promise.reject(err)
})

// ----------------封装一个通用的请求方法----------------------------
export default (options) => {
  return instance({
    // 如果没有传递请求方式,默认是使用get请求
    method: options.method || 'GET',
    // 请求地址
    url: options.url,
    // 对象的键可以是动态的变量
    // get请求,添加params属性,其他请求添加data属性
    [options.method.toUpperCase() === 'GET' ? 'params' : 'data']: options.data
    // data用于传递请求体数据(POST/PUT/DELETE)
    // data: options.data,
    // params用于传递get请求数据(查询字符串)
    // params: options.data
  })
}

路由设计

目的:知道项目路由层级的设计


1605599713484.png

路径组件(功能)嵌套级别
/首页布局容器1级
/首页2级
/category/:id一级分类2级
/category/sub/:id二级分类2级
/product/:id商品详情2级
/login登录1级
/login/callback第三方登录回调1级
/cart购物车2级
/member/checkout填写订单2级
/member/pay进行支付2级
/member/pay/result支付结果2级
/member个人中心布局容器2级
/member个人中心3级
/member/order订单管理3级
/member/order/:id订单详情3级