uniapp vue 3 升级之旅

1,196 阅读4分钟

背景

之前在公司做了一个 uniapp vue2 项目的一个升级的工作,在此记录一下。vue 2 升级 vue3 的好处显而易见,在此不多赘述,直接开启升级之旅。

框架搭建

HBuilder 不好用,所以使用的是 cli 的方式开搭建项目

vue create -p dcloudio/uni-preset-vue my-project

统一请求

原来请求有两种方式 uni.request 直接请求,以及基于 uni.request 请求的封装,目前统一成基于 uni.request 请求的封装,代码路径从 service/http.js 改成 utils/http.js

import { getCurrentPage, merger, generateId } from '@/utils/common.js';
const baseUrl = import.meta.env.VITE_APP_BASE_URL;

const DEFAULT_CONFIG = {
  baseUrl: baseUrl,
  data: {},
  header: {},
  method: 'post',
  timeout: 150000,
  dataType: 'json',
  responseType: 'text',
  ...
};

// 屏蔽的页面
const blockLoginPopList = [...];

let loading = true;
const requestInterceptor = (opt) => {
  const { url, isLoading } = opt;
  const u = DEFAULT_CONFIG.baseUrl + url;
  const m = merger(DEFAULT_CONFIG, opt);
  const token = uni.getStorageSync('token') || '';
  if (token) {
    m.header.token = token;
  }
  const configs = {
    ...m,
    url: u,
  };

  if (loading && isLoading) {
    loadingMask();
  }
  return configs;
};

const responseInterceptor = (res) => {
  const { code } = res.data;
  const pages = getCurrentPage();
  if (pages && pages.route) {
    const r = !blockLoginPopList.includes(pages.route);
    if (['200000', '200006'].includes(code)) {
      uni.removeStorageSync('token');
    }
  }
  return res.data;
};

const loadingMask = (opt = {}) => {
  const o = {
    ...opt,
    mask: true,
  };
  loading = false;
  uni.showLoading(o);
};

const request = (options) => {
  const { method, url } = options;
  const requestConfig = requestInterceptor(options);

  let requestId = generateId(method, url);
  if (!requestId) {
    return false;
  }

  return new Promise((resolve, reject) => {
    uni.request({
      ...requestConfig,
      header: {
        token: uni.getStorageSync('token') || '',
      },
      success: (res) => {
        const inter_res = responseInterceptor(res);
        if (inter_res.code == '000000') {
          resolve(inter_res);
        } else {
          console.log('request fail ', request, inter_res);
          reject(inter_res);
        }
      },
      fail: (err) => {
        console.log('request fail ', request);
        reject(err);
      },
      complete: () => {
        setTimeout(() => {
          loading = true;
        }, 1000);
      },
    });
  });
};

export { request };

请求方式

import { request } from '@/utils/http.js';

export const getData = (id) => {
  return request({
    url: `/xxxxxx/url`,
    method: 'GET',
  });
};

全局状态

vuex -> pinia (vue 官方推荐的状态管理库,更好的兼容性、更轻量化,打包完只有 1kb ) Pinia 语法有两种方式,一种是组合式,一种是选项式的;选项式的更好理解,所以这里使用的是选项式的。

import { createPinia } from 'pinia';
import { defineStore } from 'pinia';

export const useExpressStore = defineStore('myData', {
  state: () => ({
    data: {},
  }),
  getters: () => {},
  actions: {
    setData(data) {
      this.data = data;
    },
  },
});

Mixins -> hooks

Mixins 存在很多缺点,比如说命名冲突,相互之间有依赖维护很麻烦等;所以摈弃 mixins,改成官方更推荐的 hooks

 // 使用方式
 import { useUserStore } from '@/store/index';
 const userStore = useUserStore();

全局样式

全局样式统一放 style 里面

环境变量

环境变量使用 env 文件来控制,不同的 env 文件对应不同的环境变量 .env.development -> 开发环境 .env.test -> 测试环境 .env.custom -> 自定义环境 .env.production -> 生产环境

// 本地开发,mode 后加不同的环境,可以拿到不同的环境变量: development、test、prod
"dev": "uni -p mp-weixin --mode development",

获取环境变量

vite 不支持 process 环境变量获取方式如下: const baseUrl = import.meta.env.VITE_APP_BASE_URL 相关的环境变量统一 VITE_ 开头

// 相关变量统一 VITE 开头
VITE_APP_BASE_URL=https://xxx.com
VITE_APP_ENV=dev
...

升级过程遇到的问题

  • uni wx 等 not find
    • eslint.config.mjs 文件配置 { globals: uni: true, wx: true }
  • vue3 使用的是 vite ,不支持 commonJS, 所有的 require/module.export 方式都需要改成 import/export 的形式
  • 下图原因一般是组件未注册造成的 image.png
  • 在uniapp中vue3报错Cannot read property ‘route‘ of undefined
    • 因为vue3没有this上下文,所以使用 uni.createSelectorQuery().in(this)时会出这个错误
import {getCurrentInstance} from 'vue'
const instance = getCurrentInstance(); // 获取组件实例 
const query = uni.createSelectorQuery().in(instance);
  • uniapp小程序的全局弹窗解决方法 (基于 vite)
// vite.config.js
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni';

export default defineConfig({
    plugins: [uni(), {
        name: 'any name', // 插件名称
        enforce: 'pre', // 插件的执行顺序, 我们需要再vite的核心插件前执行
        // vite提供的钩子, 处理每个文件的时候都会触发一次改钩子
        // code: string 为文件数据
        // id: string 为文件路径
        transform(code, id) {
            // 过滤出page文件
            if (/\.vue$/.test(id) && /\/pages\//.test(id)) {
                /**
                * 匹配开头的第一个标签 例:
                * <template>
                *  <view>
                *
                * 然后在下面添加需要的全局组件
                */
                code = code.replace(/[\s\S]+?<[\d\D]+?>/, _ => `
                    ${_}
                    <YourComponent />
                `)

                return code
            }
        },
    }],
})
  • eslint 配置不生效
    • 执行 npx eslint --debug . 命令,查看当前配置是否正确
    • 查看eslint 版本,我执行了npm init @eslint/config@latest自动升级了相关版本,然后配置 eslint.config.mjs文件(vite 所以用的 mjs 文件),配置完之后重启就生效了
    • 建议看官网文档
import globals from 'globals';
import pluginJs from '@eslint/js';
import vueParser from 'vue-eslint-parser';
import pluginVue from 'eslint-plugin-vue';

export default [
  {
    files: ['**/*.{js,mjs,cjs,vue}'],
    languageOptions: {
      sourceType: 'module',
      globals: { ...globals.browser, ...globals.node, uni: true, wx: true },
      parser: vueParser,
    },
  },
  pluginJs.configs.recommended,
  ...pluginVue.configs['flat/essential'],
  {
    rules: {
      'vue/multi-word-component-names': 'off',
      'vue/no-unused-vars': [
        'error',
        {
          ignorePattern: '^_',
        },
      ],
    },
  },
];

升级完前后对比:

vue3 包体积:

  • dev: 3.37MB 主包: 2.54MB
  • build:2.01MB 主包 1.52MB

vue2 包体积:

  • dev: 11.50MB 主包: 5.52MB
  • build:3.52MB 主包 1.99MB

原文地址

参考:

uni-app官网

如何使用ARMS前端监控开始监控微信小程序_应用实时监控服务(ARMS)-阿里云帮助中心

Uni App + Vue3 引入高德小程序插件Js文件失败_plugins: [″],引入高德失败-CSDN博客

vue3引入wxcomponents中有带import字样会报错 - DCloud问答

如何解决“Error: xxx.js 已被代码依赖分析忽略,无法被其他模块引用”报错? | 微信开放社区

uni-app+vue3会遇到哪些问题 - 唯之为之 - 博客园