使用Vite重构Vue3项目(下)

简介: 使用Vite重构Vue3项目(下)

require不存在


一切准备就绪后,按下了项目启动按钮,很快啊,651ms项目就启动了,不愧是vite速度就是快,嘴角疯狂上扬。


640.png

                            image-20220804230003937


浏览器加载完项目后,我傻眼了,我的登陆界面呢🌚?顺势打开控制台,发现报错require is not defined


640.png

                            image-20220804230914786


解决方案


打开Login.vue文件后,发现我用require导入了一些图片文件,在VueCLI环境下的require会交给webpack处理。在vite中是不存在的,那么我们就需要查看vite是怎么处理静态文件了。


翻了下文档后,在静态资源处理章节发现他有两种处理方法:


  • 通过import语句直接导入图片
  • 通过new URL来导入图片


我打算将所有组件都重构为setup形式,因此直接使用import方式来导入图片可以保持组件的一致性,可以大大提升可读性。


我们写个简单的demo来尝试下,如下所示:


<template>
  <img :src="loginUndo" alt="" />
</template>
<script lang="ts" setup>
import loginUndo from "@/assets/img/login/LoginWindow_BigDefaultHeadImage@2x.png";
</script>
<style scoped></style>


已经可以正确解析出图片的路径了。


640.png

                         image-20220804234223781


注意:本文不会过多讲解setup的语法,对此不了解的开发者请移步:单文件组件 - script setup

new URL方式可以用来引入一个动态资源,例如:你有一份json配置文件,里面描述了图片的文件名,这些图片是放在项目中的,他们的访问前缀都一样,此时你就可以通过遍历json文件通过此方式来引入这些图片。


vue相关模块不存在


我试图从vue的包中导入shallowRef时,编辑器报错: TS2305: Module 'xxx' has no exported member 'shallowRef'.


640.png

                          image-20220806102302026


解决方案


经过一番排查后,是因为项目typescript版本是3.x,跟3.2版本的vue不兼容,需要将其升级至4.x版本。


打开package.json文件,作出如下所示的修改,重新执行yarn install命令即可。


{
  "devDependencies": {
-    "typescript": "~3.9.3",
+    "typescript": "~4.7.4",
  }
}


setup中的变量警告未被使用


当我在setup中声明了一个函数或者导入了一个文件,在template中已经使用了,但是他却报错ESLint: 'xx' is assigned a value but never used.(@typescript-eslint/no-unused-vars)


640.png

                      image-20220806231446097


解决方案


eslint-plugin-vue 插件的Issues中看到有人遇到了跟我同样的问题,在v9.0.0: regression in unused variables in script setup中我找到了解决方案。


我们需要升级下@vue/eslint-config-typescripteslint-plugin-vue的版本号,如下所示:


{
  "devDependencies": {
    "@vue/eslint-config-typescript": "^11.0.0",
    "eslint-plugin-vue": "^9.0.0"
  }
}


随后在eslint的配置文件中,添加parser属性,重新执行yarn install命令即可。


module.exports = {
+  parser: 'vue-eslint-parser'
}


模块隔离


Vite 使用 esbuild 来转译 TypeScript,并受限于单文件转译的限制,因此需要在ts的配置文件中将isolatedModules属性设置为true。


{
    "compilerOptions": {
       "isolatedModules": true
    }
}


process不存在


在路由配置文件中,我们需要从process中获取BASE_URL,此时编辑器报错: TS2591: Cannot find name 'process'. Do you need to install type definitions for node? Try npm i --save-dev @types/node and then add 'node' to the types field in your tsconfig.


640.png

                             image-20220806105226383


解决方案


由于vite中已经没有process了,需要用import.meta来代替,那么上述的路由配置文件就应该改为:


const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL), // 地址栏不带#
  routes
});


无法导入json文件


在表情面板模块,我将每个表情都放入了json文件中。在vite中引入文件需要使用import,改了写法后,发现它报错:Cannot find module 'xx.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.


640.png

                                     image-20220806111708308


解决方案


我们需要在ts的配置文件中添加resolveJsonModule属性,如下所示:


{
  "compilerOptions": {
+   "resolveJsonModule": true
  }
}


使用vite提供的对象


当我想使用vite所提供的glob属性时,发现编辑器报错: TS2339: Property 'glob' does not exist on type 'ImportMeta'.

解决方案也很简单,我们只需要在ts的配置文件中添加vite/client即可,如下所示:


{
 "compilerOptions": {
   "types": [
+     "vite/client"
    ]
 }
}


获取全局属性


当我们使用一些第三方库的时候它会在globalProperties挂载一些方法,当在ts+setup环境下使用时,会出现类型无法推导问题,如下所示:


  • 第三方库提供了一个$connect方法
  • 我们通过proxy来访问


<script lang="ts" setup>
  import {
    getCurrentInstance,
    onMounted,
    ComponentInternalInstance
  } from "vue";
  onMounted(() => {
    const { proxy } = getCurrentInstance() as ComponentInternalInstance;
    proxy.$connect();
  })
</script>


他会出现报错: TS2339: Property 'xx' does not exist on type 'ComponentPublicInstance{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase >'.


640.png

                               image-20220809103616969


解决方案


我们可以在type目录下新建一个global文件夹,在这里存放一些我们扩展出来的全局方法。


如下所示,我们:


  • 创建了一个useCurrentInstance方法
  • globalProperties属性暴露出去


import { ComponentInternalInstance, getCurrentInstance } from "vue";
export default function useCurrentInstance() {
  const { appContext } = getCurrentInstance() as ComponentInternalInstance;
  const proxy = appContext.config.globalProperties;
  return {
    proxy
  };
}


我们在组件中使用暴露出来的proxy即可,如下所示:


<script lang="ts" setup>
 import useCurrentInstance from "@/type/global/UseCurrentInstance";
  onMounted(() => {
    const { proxy } = useCurrentInstance();
    proxy.$connect();
  })
</script>


无法识别NodeJS类型


我们在给setintervalsetTimeout指定类型时,会用到NodeJS模块,会出现报错:ESLint: 'NodeJS' is not defined.(no-undef)


这个问题的解决方案是:打开eslint的配置文件在globals对象中添加NodeJS选项,如下所示:


{
  globals: {
    NodeJS: true
  }
}


除了将类型声明为NodeJS.Timeout外,我们还可以将其声明为number类型,但是需要携带window前缀(window.setinterval/window.setTimeout)


管理静态资源


当我们在组件中使用import导入很多静态资源时,组件看起来会很杂乱。此时我们可以将其按照功能类型进行拆分。我的做法如下:


  • 在src下创建resource文件夹
  • 根据功能类型创建ts文件,将其导出


import defaultAvatar from "@/assets/img/login/LoginWindow_BigDefaultHeadImage@2x.png";
import defaultLoginBtnIcon from "@/assets/img/login/icon-enter-undo@2x.png";
import loginUndo from "@/assets/img/login/icon-enter-undo@2x.png";
import loginBtnHover from "@/assets/img/login/icon-enter-hover@2x.png";
import loginBtnDown from "@/assets/img/login/icon-enter-down@2x.png";
export {
  defaultAvatar,
  defaultLoginBtnIcon,
  loginUndo,
  loginBtnHover,
  loginBtnDown
};



640.png

                            image-20220808212416992


分离模版与逻辑代码


我的项目中有一个很复杂的组件,有上千行代码,去年我用CompositionAPI优化了一版,将组件中所有的方法都拆分成了一个个独立的ts文件,做到了逻辑代码与模版代码分离,模版需要什么方法我就通过import导入进来,最后return给模版。


在拆分出来的文件中,是没有办法访问vue提供的一些内置属性的,比如:


defineProps、defineEmits、getCurrentInstance。因此我想了一个奇妙的方法:将这些无法访问的属性都存起来。具体的做法请移步我另一篇文章:使用Vue3的CompositionAPI来优化代码量-创建InitData.ts文件


适配方案


vue3.2的setup语法糖支持import进来的方法都能在模版中直接使用,那我们的组件又可以精简下了,我花了亿点点时间对其进行了适配🤒


之前我们想获取组件的emit需要从context中拿,props声明并从setup函数的参数中获取,如下所示:


<script>
export default defineComponent({
  name: "message-display",
  props: {
    listId: String, // 消息id
    messageStatus: Number, // 消息类型
    buddyId: String, // 好友id
    buddyName: String, // 好友昵称
    serverTime: String // 服务器时间
  },
  setup(props, context) {
  // 访问emit
  context.emit
 }
})
</script>


现在我们就不用这么麻烦了,直接通过defineProps、defineEmits获取即可,如下所示:


<script lang="ts" setup>
// 获取父组件传递值
const props = defineProps<{
  listId: string; // 消息id
  messageStatus: number; // 消息类型
  buddyId: string; // 好友id
  buddyName: string; // 好友昵称
  serverTime: string; // 服务器时间
}>();
const emit = defineEmits<{
  (
    e: "update-last-message",
    msgObj: {
      text: string;
      id: string;
      time: string;
    }
  ): void;
}>();
// 事件监听函数,传入props和emit将其存储到initData中
const { userID, onlineUsers } = eventMonitoring(props, emit) as {
  userID: ComputedRef<string>;
  onlineUsers: ComputedRef<number>;
};
</script>


此组件重构后的完整代码请移步:


  • message-display.vue
  • EventMonitoring.ts


项目地址


至此,项目的重构工作就结束了。本文重构好的项目代码地址:


  • chat-system


写在最后


至此,文章就分享完毕了。


我是神奇的程序员,一位前端开发工程师。


如果你对我感兴趣,请移步我的个人网站,进一步了解。


  • 公众号无法外链,如果文中有链接,可点击下方阅读原文查看😊
相关文章
|
12天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
24天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
21天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
50 7
|
23天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
41 3
|
21天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
39 1
|
21天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
44 1
|
24天前
|
前端开发 JavaScript 容器
在 vite+vue 中使用@originjs/vite-plugin-federation 模块联邦
【10月更文挑战第25天】模块联邦是一种强大的技术,它允许将不同的微前端模块组合在一起,形成一个统一的应用。在 vite+vue 项目中,使用@originjs/vite-plugin-federation 模块联邦可以实现高效的模块共享和组合。通过本文的介绍,相信你已经了解了如何在 vite+vue 项目中使用@originjs/vite-plugin-federation 模块联邦,包括安装、配置和使用等方面。在实际开发中,你可以根据自己的需求和项目的特点,灵活地使用模块联邦,提高项目的可维护性和扩展性。
|
24天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
24天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
6月前
|
JavaScript API
【vue实战项目】通用管理系统:api封装、404页
【vue实战项目】通用管理系统:api封装、404页
77 3
下一篇
无影云桌面