美文网首页Vue
ts+vue3+vite+pinia+vue-router 踩坑

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

作者: 剁椒先生 | 来源:发表于2022-07-07 16:11 被阅读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.别名配置

    • 问题: 当你使用别名@, 你会发现别名框架并没有配置 会出现提示 找不到模块“@/router/index”或其相应的类型声明
    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.动态路由

    • 问题: Invalid route component at extractComponentsGuards ? 这个问题不陌生吧!! 有俩个因素;

    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)
    }
    
    未完待续, 后续还会更新; 这些只是一部分; 欢迎大家参与留言讨论

    相关文章

      网友评论

        本文标题:ts+vue3+vite+pinia+vue-router 踩坑

        本文链接:https://www.haomeiwen.com/subject/wrvybrtx.html