使用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


写在最后


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


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


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


  • 公众号无法外链,如果文中有链接,可点击下方阅读原文查看😊
相关文章
|
2天前
|
JSON 数据可视化 前端开发
vue3+threejs+koa可视化项目——模型文件上传(第四步)
vue3+threejs+koa可视化项目——模型文件上传(第四步)
15 7
|
2天前
|
JSON 数据可视化 数据库
vue3+threejs+koa可视化项目——实现登录注册(第三步)
vue3+threejs+koa可视化项目——实现登录注册(第三步)
18 5
|
2天前
|
JavaScript 数据可视化 算法
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
17 6
|
2天前
|
JavaScript 安全 前端开发
Vue 项目中的权限管理:让页面也学会说“你无权访问!
Vue 项目中的权限管理:让页面也学会说“你无权访问!
11 3
|
5天前
|
JavaScript
Vue 如何新建一个项目(如何安装依赖)
Vue 如何新建一个项目(如何安装依赖)
10 0
|
11天前
|
JavaScript 算法 前端开发
vue3和vue2的区别都有哪些
【4月更文挑战第15天】Vue3与Vue2在响应式系统(Proxy vs. Object.defineProperty)、组件模块化(Composition API vs. Options API)、数据变化检测(Reactive API vs. $watch)、虚拟DOM算法(基于迭代 vs. 基于递归)及Tree-Shaking支持上存在显著差异。Vue3的改进带来了更好的性能和灵活性,适合追求新技术的项目。Vue2则因其成熟稳定,适合维护大型项目。选择版本需根据项目需求、团队情况和技术追求来决定。
13 0
|
12天前
|
JavaScript
vue3+vite项目配置ESlint
vue3+vite项目配置ESlint
14 0
|
JavaScript Java 物联网
现有vue项目seo优化
现有vue项目seo优化
|
JavaScript 前端开发
重读vue电商网站45之项目优化上线
重读vue电商网站45之项目优化上线
96 0
重读vue电商网站45之项目优化上线
|
3天前
|
JavaScript 前端开发
【vue】iview如何把input输入框和点击输入框之后的边框去掉
【vue】iview如何把input输入框和点击输入框之后的边框去掉
10 0