Vue

ts+vue3+vite+pinia+vue-router 踩坑

2022-07-07  本文已影响0人  剁椒先生

前言

1. 预处理器

安装sass以及配置全局scss

npm install node-sass
npm install sass-loader

node 版本不能过高; 目前成功版本为10.15.0; 可通过安装"n"来管理node版本

配置全局mixin.scss
根目录找到vite.config.js

// vite.config.js 
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    // 配置别名
    alias: [
      { find: '@', replacement: resolve(__dirname, './src') },
      { find: 'views', replacement: resolve(__dirname, './src/views') },
    ],
  },
  css: {
    // 添加全局mixin.scss
    preprocessorOptions: {
      scss: {
        additionalData: '@import "@/style/libs.scss";',
      },
    },
  },
});

scss参与计算 (内置模块)

原来的写法直接计算是不行的, 需要使用内置模块math.div()方法参与计算

//libs.scss
//使用math 需要先声明使用; 添加在文件顶部
@use "sass:math" 

// 原来的写法
border-width: $w/2;

// 新写法
border-width: math.div($w/2)

新写法; 切记math需要声明使用(https://sass-lang.com/documentation/modules); 不然就会提示:
Error: There is no module with the namespace "math".

2. require 使用

const file = require.context('@/views/', true, /\.vue$/)

结果提示找不到名称“require”。是否需要为节点安装类型定义? 请尝试使用 npm i --save-dev @types/node,然后将 “node” 添加到类型字段。

解决办法:
按照提示安装库
npm i --save-dev @types/node
然后在tsconfig.json types字段中添加node; 然后重启一下; 就行了

// tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": false,
    "jsx": "preserve",
    "moduleResolution": "node",
    "types": [
      "vite/client",
      "node",
    ],
     ...
    }
  }
}

3.别名配置

import router from '@/router/index';

解决办法:
vite.config.js 配置别名

// vite.config.js 
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    // 配置别名
    alias: [
      { find: '@', replacement: resolve(__dirname, './src') },
      { find: 'views', replacement: resolve(__dirname, './src/views') },
    ],
  },
  css: {
    // 添加全局mixin.scss
    preprocessorOptions: {
      scss: {
        additionalData: '@import "@/style/libs.scss";',
      },
    },
  },
});

很多人以为这样就结束了;在js 文件中确实就已经可以用了; 但是在.vue 文件中还是不行,其实还需要在tsconfig.json里面配置对应的别名; 详情见https://www.jianshu.com/p/1477b68b2d69

// tsconfig.json 
{ 
  "compilerOptions": {
      ... 
      "paths": { 
        "@/*":[ "./src/*" ], 
        "views/*":[ "./src/views/*" ], 
      } 
    } 
}

4.动态路由

1. 如果你用JSON.stringify()转为string 后存储; 因为JSON.stringify()在序列化的过程中function、undefined、symbol这几种类型是不可枚举属性; 序列化后这个键值对会消失; addRoute()的时候就会发现没有这个component.

2. 使用component:()=>import('views/Order/index.vue')懒加载方式引入;也会提示Invalid route component;

tips: 调试路由时可通过router.getRoutes(); 查看动态路由是否已经完整注入进去

解决办法1. 使用Glob Import 动态导入

// src/mockjs/mock.routes.ts
export default routes = [
    {
      path: '/',
      name: 'home',
      component: 'views/Home/index.vue',
      meta: {
        // 页面标题
        title: '首页',
        // 身份认证
        userAuth: false,
      },
    },
]
// src/router/index.ts
import routes from "@/mockjs/mock.routes"// 路由元信息
const modules = import.meta.golb("../views/**/**.vue") //使用golb ++

routes.forEach(item=>{
    router.addRoute("home", {
          path: item.path,
          name: item.name,
          meta: ...item.meta,
          component:
             //本地能使用,上生产直接GG
            //()=>import(/* @vite-ignore */ `/@views/${itemRouter.component}`),
        //使用modules
            modules[/* @vite-ignore */ `../views/${item.component}`],
        })
})

解决办法2 : 在声明路由数据时使用 RouteRecordRaw; 下面是RouteRecordRaw的注解

当用户通过 routes option 或者 router.addRoute() 来添加路由时,可以得到路由记录。 有三种不同的路由记录:

  1. 单一视图记录:有一个 component 配置
  2. 多视图记录 (命名视图) :有一个 components 配置
  3. 重定向记录:没有 component 或 components 配置,因为重定向记录永远不会到达。
// src/mockjs/mock.routes.ts 
export default routes:RouteRecordRaw[] = [ 
  { 
    path: '/', 
    name: 'home', 
    component:  component: () => import("views/Home/index.vue"),
    meta: { 
        // 页面标题 
        title: '首页', 
        // 身份认证 
        userAuth: false, 
    }, 
  }, 
]

// src/router/index.ts 
import routes from "@/mockjs/mock.routes"
// 路由元信息  确保自己的地址是否正确
routes.forEach(item=>{ router.addRoute(item}) })

5.pinia 使用以及动态路由持续存储踩坑

注释: pinia跟vuex一样也是一个基于vue的状态管理库; 使用pinia时,可以理解为它每一个store 都是动态的、独立的; 不像vuex 一样可嵌套,因为没有modules!

pinia 使用:
npm install pinia
or
yarn add pinia

// src/store/index.ts 
import {createPinia} from "pinia"
const pinia = createPinia() 
export default pinia
//main.ts 
.... 
import pinia from "./store/index"
createApp(App).use(router).use(pinia).mount('#app')

定义单个store

// src/store/createRouteInfo.ts 
import {defineStore} from "pinia" 
import router from "@/router/index" 
// 封装的路由
// defineStore的第一个参数是应用程序中 store 的唯一id;这些注解官网都有 
const useStore = defineStore('useStore',{
    state:()=>({ routeModules:[], menu:[] ..... }),
    getterL:{ 
      routeModules: state=>state.routeModules, 
      menu: state=>state.menu 
   }, 
   // 上面那俩个参数没变; 没有mutations!! actions类似于组件中methods,也可以定义一些业务逻辑; 
   actions: { 
      addAsyncRoutes(routes){
        // 业务逻辑
        //设置动态路由
        routes.forEach(el=>{ router.addRoute(el) })
      } 
   } 
}) 
export default useStore

使用useStore

// src/components/menu.vue 
<script lang="ts" setup> 
  import useStore from "@/store/createRouteInfo"
  import {storeToRefs} from "pinia" 
  const store = useStore() 
  // 使参数变为响应式 
  const {routeModules,menu} = storeToRefs(store)
  onMounted(()=>{ getMenu() }) 
  const getMenu = ()=>{ let result = getMenuData(params)
 // set store value 
  store.$patch(state=>{ state.menu = result.data.menu }) } 
</script>
 <template>
   <div class="menu"> 
      <ul> 
          <li v-for="li in menu" :key="li.id">{{li.text}}</li>
     </ul>
   </div> 
  </template>

动态路由权限踩坑

很多人做动态路由时 数据存在store; 页面刷新后页面空白或者进入404页面; 并且使用router.getRoutes() 查看新增的路由已经消失; 是因为store是存在浏览器的堆栈内存里面; 刷新后页面数据被释放掉; 那么如何解决呢? 想一想 页面刷新后我们的框架是会重新挂载的! so! main.js? 试试就试试!!!!

// src/store/asyncRoute.ts 
import routes from "@/mockjs/mock.routes"
// 路由元信息 i
mport useStore from "@/store/createRouteInfo" 
// 封装的路由 
const store = useStore() 
const {role} = localStorage.getItem('user')||{} 
// 是否已经登陆, 初级判断 可增加路由权限判断
 if(role === 'admin') { 
  // 此方法在 上面“定义单个store” createRouteInfo.ts 文件中定义了
     store.addAsyncRoutes(routes)
 }
// main.js
 ..... 
import "./store/asyncRoute"
 ..... 
createApp(App).use(router).use(pinia).mount('#app')

页面更新后提示报错信息:
问题: Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
什么意思呢? 你当前正在使用pinia; 但是pinia 还没有挂载成功!!!! what? 我使用vuex就不会这样呀!!!! 戏精

解决方案:

无意间在廖雪峰的官网看到过一句评论!!所谓的对象、函数在浏览器内存中就是一个地址;找个地址只想某个函数或者对象或者方法、谁拿到这个地址就拥有操作的权利; 所谓的函数作为参数传入别的函数; 其实就是把地址给了它; 让它拥有的操作的权利! 我觉得挺精辟!!

使用时引入pinia

// src/store/index.ts 
import {createPinia} from "pinia"
const pinia = createPinia() 
export default pinia 
//_______________分割线__________________ // 

//src/store/asyncRoute.ts 
import routes from "@/mockjs/mock.routes"
// 路由元信息 
import useStore from "@/store/createRouteInfo"
// 封装的路由 
import pinia from "./index"
// 引入pinia 
const store = useStore(pinia) // 把pinia 传进去 就可以任意任意使用里面的属性了
const {role} = localStorage.getItem('user')||{} 
// 是否已经登陆, 初级判断 可增加路由权限判断 
if(role === 'admin') {
 // 此方法在 上面“定义单个store” createRouteInfo.ts 文件中定义了
   store.addAsyncRoutes(routes)
}
未完待续, 后续还会更新; 这些只是一部分; 欢迎大家参与留言讨论
上一篇下一篇

猜你喜欢

热点阅读