01-创建项目

目的:使用vue-cli创建一个vue3.0项目。
第一步:打开命令行窗口。
image.png

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

第二步:执行创建项目命令行
image.png
第三步:选择自定义创建
image.png
第四步:选中vue-router,vuex,css Pre-processors选项
image.png
第五步:选择vue3.0版本
image.png
第六步:选择hash模式的路由
image.png
第七步:选择less作为预处理器
image.png
第八步:选择 standard 标准代码风格
image.png
第九步:保存代码校验代码风格,代码提交时候校验代码风格
image.png
第十步:依赖插件或者工具的配置文件分文件保存
image.png
第十一步:是否记录以上操作,选择否
image.png
第十二步:等待安装…
image.png
最后:安装完毕
image.png

02-目录调整

目的:对项目功能模块进行拆分。
大致步骤:

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

落的代码:
image.png
注意:以上结构目录及供参考
需要注意的一些文件有:

  • router/index.js ```javascript import { createRouter, createWebHashHistory } from ‘vue-router’

const routes = [

]

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

export default router

  1. vue3.0createRouter来创建路由实例,createWebHashHistory代表使用hash模式的路由。
  2. - store/index.js
  3. ```javascript
  4. import { createStore } from 'vuex'
  5. // 创建vuex仓库并导出
  6. export default createStore({
  7. state: {
  8. // 数据
  9. },
  10. mutations: {
  11. // 改数据函数
  12. },
  13. actions: {
  14. // 请求数据函数
  15. },
  16. modules: {
  17. // 分模块
  18. },
  19. getters: {
  20. // vuex的计算属性
  21. }
  22. })

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

  • main.js ```javascript 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’)

  1. vue3.0createApp来创建应用app。<br />**额外增加两个配置文件:**
  2. - jsconfig.json
  3. ```javascript
  4. {
  5. "compilerOptions": {
  6. "baseUrl": ".",
  7. "paths": {
  8. "@/*": ["./src/*"],
  9. }
  10. },
  11. "exclude": ["node_modules", "dist"]
  12. }

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

  • .eslintignore

    1. /dist
    2. /src/vender

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

  • 调整目录,解释生成代码

  • 添加配置文件,路径提示,忽略风格校验的。

    03-vuex-基础

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

定义

  1. vue2.0 创建仓库 new Vuex.Store({})
  2. vue3.0 创建仓库 createStore({})
  3. export default createStore({
  4. state: {
  5. username: 'zs'
  6. },
  7. getters: {
  8. newName (state) {
  9. return state.username + '!!!'
  10. }
  11. },
  12. mutations: {
  13. updateName (state) {
  14. state.username = 'ls'
  15. }
  16. },
  17. actions: {
  18. updateName (ctx) {
  19. // 发请求
  20. setTimeout(() => {
  21. ctx.commit('updateName')
  22. }, 1000)
  23. }
  24. },
  25. modules: {
  26. }
  27. })

使用

  1. <template>
  2. <!-- vue2.0需要根元素,vue3.0可以是代码片段 Fragment -->
  3. <div>
  4. App
  5. <!-- 1. 使用根模块state的数据 -->
  6. <p>{{$store.state.username}}</p>
  7. <!-- 2. 使用根模块getters的数据 -->
  8. <p>{{$store.getters['newName']}}</p>
  9. <button @click="mutationsFn">mutationsFn</button>
  10. </div>
  11. </template>
  12. <script>
  13. import { useStore } from 'vuex'
  14. export default {
  15. name: 'App',
  16. setup () {
  17. // 使用vuex仓库
  18. const store = useStore()
  19. // 1. 使用根模块state的数据
  20. console.log(store.state.username)
  21. // 2. 使用根模块getters的数据
  22. console.log(store.getters.newName)
  23. const mutationsFn = () => {
  24. // 3. 提交根模块mutations函数
  25. // store.commit('updateName')
  26. // 4. 调用根模块actions函数
  27. store.dispatch('updateName')
  28. }
  29. return { mutationsFn }
  30. }
  31. }
  32. </script>
  1. modules (分模块)
  • 存在两种情况
    • 默认的模块,state 区分模块,其他 getters mutations actions 都在全局。
    • 带命名空间 namespaced: true 的模块,所有功能区分模块,更高封装度和复用。 ```javascript 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 === ‘男’) } } })

  1. 使用:
  2. ```javascript
  3. <template>
  4. <div>APP组件</div>
  5. <ul>
  6. <li v-for="item in $store.getters.boys" :key="item.id">{{item.name}}</li>
  7. </ul>
  8. <!-- 使用模块A的username -->
  9. <p>A的username --- {{$store.state.a.username}}</p>
  10. <p>A的changeName --- {{$store.getters.changeName}}</p>
  11. <hr>
  12. <p>B的username --- {{$store.state.b.username}}</p>
  13. <p>B的changeName --- {{$store.getters['b/changeName']}}</p>
  14. <button @click="$store.commit('b/update')">修改username</button>
  15. <button @click="$store.dispatch('b/update')">异步修改username</button>
  16. </template>

04-vuex-持久化

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

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

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

  1. npm i vuex-persistedstate

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

  1. // 用户模块
  2. export default {
  3. namespaced: true,
  4. state () {
  5. return {
  6. // 用户信息
  7. profile: {
  8. id: '',
  9. avatar: '',
  10. nickname: '',
  11. account: '',
  12. mobile: '',
  13. token: ''
  14. }
  15. }
  16. },
  17. mutations: {
  18. // 修改用户信息,payload就是用户信息对象
  19. setUser (state, payload) {
  20. state.profile = payload
  21. }
  22. }
  23. }

src/store/modules/cart.js

  1. // 购物车状态
  2. export default {
  3. namespaced: true,
  4. state: () => {
  5. return {
  6. list: []
  7. }
  8. }
  9. }

src/store/modules/category.js

  1. // 分类模块
  2. export default {
  3. namespaced: true,
  4. state () {
  5. return {
  6. // 分类信息集合
  7. list: []
  8. }
  9. }
  10. }

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

  1. import { createStore } from 'vuex'
  2. import user from './modules/user'
  3. import cart from './modules/cart'
  4. import cart from './modules/category'
  5. export default createStore({
  6. modules: {
  7. user,
  8. cart,
  9. category
  10. }
  11. })

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

  1. import { createStore } from 'vuex'
  2. +import createPersistedstate from 'vuex-persistedstate'
  3. import user from './modules/user'
  4. import cart from './modules/cart'
  5. import cart from './modules/category'
  6. export default createStore({
  7. modules: {
  8. user,
  9. cart,
  10. category
  11. },
  12. + plugins: [
  13. + createPersistedstate({
  14. + key: 'erabbit-client-pc-store',
  15. + paths: ['user', 'cart']
  16. + })
  17. + ]
  18. })

注意:
===> 默认是存储在localStorage中
===> key是存储数据的键名
===> paths是存储state中的那些数据,如果是模块下具体的数据需要加上模块名称,如user.token
===> 修改state后触发才可以看到本地存储数据的的变化。
测试: user模块定义一个mutation在main.js去调用下,观察浏览器application的localStorage下数据。
src/App.js

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

05-请求工具

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

  • 安装 axios

npm i axios

  • 新建 src/utils/request.js 模块,代码如下 ```javascript // 1. 创建一个新的axios实例 // 2. 请求拦截器,如果有token进行头部携带 // 3. 响应拦截器:1. 剥离无效数据 2. 处理token失效 // 4. 导出一个函数,调用当前的axsio实例发请求,返回值promise

import axios from ‘axios’ import store from ‘@/store’ import router from ‘@/router’

// 导出基准地址,原因:其他地方不是通过axios发请求的地方用上基准地址 export const baseURL = ‘http://pcapi-xiaotuxian-front-devtest.itheima.net/‘ const instance = axios.create({ // axios 的一些配置,baseURL timeout baseURL, timeout: 5000 })

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) })

// 请求工具函数 export default (url, method, submitData) => { // 负责发请求:请求地址,请求方式,提交的数据 return instance({ url, method, // 1. 如果是get请求 需要使用params来传递submitData ?a=10&c=10 // 2. 如果不是get请求 需要使用data来传递submitData 请求体传参 // [] 设置一个动态的key, 写js表达式,js表达式的执行结果当作KEY // method参数:get,Get,GET 转换成小写再来判断 // 在对象,[‘params’]:submitData ===== params:submitData 这样理解

  1. [method.toLowerCase() === 'get' ? 'params' : 'data']: submitData

}) } ```

06-路由设计

目的:知道项目路由层级的设计
image.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级