vite2 + vue2 + tsx + pinia开发todo-list实战总结分享

4,443 阅读3分钟

概述

之前搞了vue3+vite2的实战,踩了不少坑,既然vite2也支持vue2并且原生支持ts,想尝试能否将vue2比较平滑过渡到vue3,看看两者之间的差异,尽量采用之前写vue3的方式来写vue2,踩踩坑,前两天给vite-plugin-components 提了两个pr支持了vue2的组件库view-uielement-ui,恰巧看看真实项目中使用会不会有什么问题

项目搭建

vite2支持vue2

安装依赖:

    "vite-plugin-vue2": "^1.7.2",

配置vite.config:

import { createVuePlugin } from "vite-plugin-vue2";
export default defineConfig({
  plugins: [createVuePlugin()]
})

安装模板编译器

用于解析vue2文件,版本和vue2对应

 "vue-template-compiler": "^2.6.14",

支持jsx/tsx

插件支持

只需要在createVuePlugin的插件中配置一下jsx开启

 createVuePlugin({
      jsx: true,
 })

使用时声明

.vue文件中使用的时候要声明为<script lang="tsx">

添加声明文件

在src目录下 新建一个shim-tsx.d.ts,

// file: shim-tsx.d.ts
import Vue, { VNode } from 'vue';
import { ComponentRenderProxy } from '@vue/composition-api';

declare global {
  namespace JSX {
    interface Element extends VNode {}
    interface ElementClass extends ComponentRenderProxy {}
    interface ElementAttributesProperty {
      $props: any; // specify the property name to use
    }
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}

支持ts

安装依赖

安装typescript依赖和vue编译器

 "typescript": "^4.1.3",
 "vue-tsc": "^0.1.7"

文件名替换ts

更改入口文件js为ts,index.html入口文件为main.ts

 <script type="module" src="/src/main.ts"></script>

添加类型声明文件

  • 添加.vue文件ts声明,和main.ts同级目录新建shims-vue.d.ts,
declare module "*.vue" {
  import Vue from "vue";
  // eslint-disable-next-line prettier/prettier
  export default Vue;
}

支持vue3特性(引入compositionApi)

安装依赖

"@vue/composition-api": "^1.0.3",

主函数引入并使用

import VueCompositionAPI from "@vue/composition-api";
Vue.use(VueCompositionAPI);

遇到的问题

这个api需要在最前面引入,因为有的依赖库也使用了compositionApi如果顺序颠倒则会报错

配置组件库

 "view-design": "^4.6.1",
 "element-ui": "^2.15.3",

额外依赖

由于view-ui中有用到commonjs的语法require,vite无法识别,需要安装一个插件解决

"@originjs/vite-plugin-commonjs": "^1.0.0-beta6",
import { viteCommonjs } from "@originjs/vite-plugin-commonjs";
export default defineConfig({
    plugins: [
         viteCommonjs(),
    ]
})

配置vite

import ViteComponents, {
  ElementUiResolver,
  ViewUiResolver,
} from "vite-plugin-components";

export default defineConfig({
    plugins: [
         ViteComponents({
               dirs: ["src/views"],//扫描自定义组件的路径,可配置为相对路径  ./
               customComponentResolvers: [
                   ViewUiResolver(),
                   ElementUiResolver(),
               ]
         })
    ]
})

设置alias别名

配置vite.config

import path from "path";
export default defineConfig({
  // 配置别名
  resolve: {
    alias: [
      {
        find: "@",
        replacement: path.resolve(__dirname, "src"),
      },
    ],
  },
})

更改tsconfig.json

配置添加如下两个配置,避免ts文件中引入口提示如下错误

Cannot find module '@/hooks/useForm' or its corresponding type declarations.Vetur(2307)

{
  "compilerOptions": {
    //...略
    "baseUrl": "src",
    "paths": {
      "@/*": ["./*"]
    },
  },
  //...略
}

遇到的问题

开始配置好上述两项依旧不起作用,还是报错,重启vscode才好

vue-router

vue2只能使用vue-router3

  • 安装依赖
 "vue-router": "^3.5.2",
  • 配置使用 和以前使用一样
import Vue from 'vue'
Vue.use(Router);
export default new Router({
  // mode:'history',
  routes: [
    {
      path: '/',
      component: ()=>  import("@/views/index.vue"),
    },
  ],
});

使用pinia

安装依赖:

vue2版本只能使用pinia1,官网是这样说的:

pinia@next install Pinia v2 for Vue 3. If your app is using Vue 2, you need to install Pinia v1: pinia@latest and @vue/composition-api. If you are using Nuxt, you should follow these instructions.

但是我使用了pinia@latest这个让我选择版本,没有找到1.0的版本,应该指的就是0.x的版本吧,如果使用2.x版本就会报错

    "pinia": "^0.5.2",

按需加载和引入组件库

使用vite-plugin-components实现按需加载,在0.13.0这个版本支持了view-uielement-ui,比较主流的两个组件库

安装依赖:

"vite-plugin-components": "^0.13.0",

项目中使用

//main.ts
import { createPinia, PiniaPlugin } from 'pinia'
Vue.use(PiniaPlugin)
const store = createPinia();
new Vue({
  el: "#app",
  router,
  pinia:store,//使用了插件才会有这个属性
  render: h => h(App),
});

遇到的问题

之前一般是新建一个store的文件夹然后新建一个index.ts导入后在main.ts中import后使用,但是总是提示没有引入compositionAPi,即使我在main.ts最上面已经引入了,还是报,无奈只能是将pinia也在main中引入了,不多也才一句话

项目开发中踩到的坑

tsx的vetur报错

描述: 在view-ui的table使用tsx动态渲染列的时候给一个button设置时候设置了size属性,结果提示报错如下:

Property 'size' does not exist on type 'ComponentOptions<Vue, DefaultData, DefaultMethods, DefaultComputed, PropsDefinition, DefaultProps>'.Vetur(2769)

但是运行没有任何的报错,看ts的定义size属性也是定义的非必须属性

解决: 今天更新了一下vscode发现就不报错了,挺坑的~

vue2在setup中定义tsx报错

描述: 报错信息如下:

Cannot read property '$createElement' of undefined 解决:发现render函数在setup中应该是注入不进来,查看了setup传入参数的定义没有发现render函数的定义,无奈只能写在data函数中,render函数可以成功注入进来

动态添加的属性不会立即生效

描述: 这是个老问题,只是vue3中解决了,退回到vue2就忽略了这个,向一个对象中添加一个之前没有的属性,不会响应变化 解决:使用$set添加响应式, 例如: this.$set(item,'editing',!!item.editing)

在hooks中 使用共享数据不具备响应式

描述:类似const data = todoStore.total;这种形式,然后再用data进行computed出其他的变量,此时在vue2中将失去响应式,但是vue3是正常的

//vue3
  const data: UnwrapRef<DataItem[]> = todoStore.total;
  const unfinish = computed(() =>
    data.filter((item: DataItem) => item.finish != true)
  );

  const finishes = computed(() => data.filter((item: DataItem) => item.finish));

解决:就是直接使用store的state变量属性进行computed

//vue2
  const unfinish = computed(() =>
  todoStore.total.filter((item: DataItem) => item.finish != true)
  );
  const finishes = computed(() =>  todoStore.total.filter((item: DataItem) => item.finish));

pinia删除数据不触发拦截器

描述: 使用vue3的时候使用如下方式即可删除数组数据并且进入拦截器,但是在vue2的时候失效了

//vue3
  const clearAll = () => {
    todoStore.$state.total.length = 0; // 如果使用$reset  不触发拦截器
  };

解决:经过测试发现只有调用splice才会触发pinia的拦截器 测试结果如下:

//vue2
  const clearAll = () => {
    // todoStore.$state.total=[]//可清空store 不走拦截器
    // todoStore.$state.total.length = 0//没有作用
    // Vue.set(todoStore.$state.total,'length',0)//没有作用
    todoStore.$state.total.splice(0,todoStore.$state.total.length)//只能通过这种方式,触发全部删除,重置length为0 不管用
  
  };

参考文档

vue2环境下成功使用vite的实践总结!!

代码地址

主项目 github: github.com/nabaonan/to…

vue2项目目录是:vue2-vite-ts

欢迎star~

结语

总的来说,vue2的ts版本如果配合compositionApivue3基本很接近了,vue2element版本就是直接把vue3的代码粘贴过来,改动很少,由于使用了hooks的方式,所以逻辑基本不用动,然后页面的话,element-plus和element-ui也都是一个东西,页面都能复用,只是有个小bug是popconfirm中没有引入popover的样式,这个在element-plus中已经解决,element-ui没人管了,只能自己引入一下样式解决,其他都还好。 之前听说vue2.7应该会内置compositionApi,那样的话,vue3的代码直接复制过来连改都不用改了,但是vue2compositionApi也有一些不被支持的功能,比如emit函数就不支持,需要从外部将emit传入到hooks函数中。

最近发现一个大佬在vue2里提了一个将vue2重构为ts的巨型pr,实在佩服这种十倍工程师的大佬,希望能早日并入,没准那时template的ts语法提示能更进一步了~

----更新20220203-----

pinia2支持vue2了 升级依赖版本

    "@vue/composition-api": "^1.4.5",
    "pinia": "^2.0.11",

main函数也要做出微调,之前的PiniaPlugin变了名称为PiniaVuePlugin

import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)