[dcloudio/uni-app]编译到小程序时,无法使用@vue/composition-api

2023-12-09 829 views
7

在小程序中,初始化小程序组件实例的data时,只使用了vue component data函数返回的model,导致setup中定义的state无法绑定到小程序的view,希望能兼容composition-api,用着太舒服回不去了😂

function initData (vueOptions, context) {
  let data = vueOptions.data || {};
  const methods = vueOptions.methods || {};

  if (typeof data === 'function') {
    try {
     //只取了data函数返回的model
      data = data.call(context); // 支持 Vue.prototype 上挂的数据
    } catch (e) {
      if (process.env.VUE_APP_DEBUG) {
        console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data);
      }
    }
  } else {
    try {
      // 对 data 格式化
      data = JSON.parse(JSON.stringify(data));
    } catch (e) {}
  }

  if (!isPlainObject(data)) {
    data = {};
  }

  Object.keys(methods).forEach(methodName => {
    if (context.__lifecycle_hooks__.indexOf(methodName) === -1 && !hasOwn(data, methodName)) {
      data[methodName] = methods[methodName];
    }
  });

  return data
}

回答

3

你可以手动替换下 mp.runtime.esm.js 文件,验证composition-api相关功能,不排除部分特性仍有问题

2

我在尝试的时候发现在页面中使用 composition-ap 是可行的,但组件中使用却不行,相关代码执行了但ComponentData 中却没有setup 返回的变量, 我天真了,开发版页面中是有了但编译后确实没有了

6

已经放弃了在uni中使用composition-api,编译到h5时都会有一些问题,坑太多 想用composition-api只能等官方支持vue3.0了

1

我替换了该文件为你提交的那个,但是ComponentData 中仍然没有setup 返回的变量,dev 模式没问题,build就不行

检查发现 这个 rawBindings 一直是 undefined 无论 dev 还是 build 模式 图片

请问您代码中的 vm.__secret_vfa_state__ 是否使用错误,或者这个属性现有的发布版本中还没有? 我通过查看对象将 var rawBindings = vm.__secret_vfa_state__ && vm.__secret_vfa_state__.rawBindings 替换为 var rawBindings = (vm.__secret_vfa_state__ && vm.__secret_vfa_state__.rawBindings)|| (vm.__composition_api_state__ && vm.__composition_api_state__.rawBindings); 后在编译版可以正常的查看到 setup 返回的变量了, @dnt1996 您可以尝试一下

2

@hbcui1984 可以更新一下关于 composition-api 的包吗,我现在是在本地改了vue-cli-plugin-uni 的包,这样不太好。

1

@fxy060608 还存在一个表现不一致的地方

<template>
  <div id="app">{{test}}</div>
</template>

<script>
import {
  defineComponent,
  ref,
  onBeforeUpdate,
  reactive,
  computed
} from "@vue/composition-api";
export default defineComponent({
  name: "App",
  setup() {
    const s = reactive({ test: 1 });
    const test = computed(() => s.test);
    setInterval(() => {
      s.test += 1;
    }, 1000);

    onBeforeUpdate(() => {
      console.log("onBeforeUpdate", test.value);
    });
    return { test }; // uniapp 微信小程序中 无法触发beforeUpdate 也就无法更新值
    // return { test , s }; // 用例2  这个才能在uniapp 微信小程序中正确执行
  },
  beforeUpdate() {
    console.log("options.beforeUpdate", this.test);
  }
});
</script>

这段代码在浏览器中是没有问题的,但是如果 setup 只返回计算值 test 微信小程序中就不不会更新视图,连beforeUpdate都没有触发

代码 codesandbox 地址

1

请问现在(2021-3)uni-app对composition-api的支持如何了?是否已经能在各小程序(主要是微信小程序、百度小程序、支付宝小程序)中完美运行? 想在生产环境使用这一新特性~

2

可以用了, 只不过uni-app的生命周期onLoad, onHide和template ref功能等等需要自己封装

5

怎么封装onLoad生命周期方法,有示例么

9
import {onUnmounted, onMounted, getCurrentInstance, reactive, computed} from '@vue/composition-api';
import Vue from "vue";

function createUseOn(name) {
    return (fun: Function) => {
        const vm = getCurrentInstance()!;
        vm.proxy.$root.$on(`hooks:${name}`, fun);
        vm.proxy.$on('hook:beforeDestroy', () => vm.proxy.$root.$off(`hooks:${name}`, fun));
    }
}

export const useOnShow = createUseOn('onShow');

export const useOnHide = createUseOn('onHide');

export const useOnLoad = createUseOn('onLoad');

export const useOnUnload = createUseOn('onUnload');

export const useOnReachBottom = createUseOn('onReachBottom');

export const useOnReady = createUseOn('onReady');

// 下拉刷新回调
export const useOnPullDownRefresh = createUseOn('onPullDownRefresh');

Vue.mixin({
    ...['onShow', 'onHide', 'onUnload', 'onLoad', 'onReachBottom', 'onReady', 'onPullDownRefresh'].reduce((previousValue, currentValue) => {
        previousValue[currentValue] = function (this: any) {
            if (this !== this.$root) {
                return;
            }
            // 解决uni-app 傻逼报错
            this.$scope.triggerEvent = this.$scope.triggerEvent ?? (() => {
            });
            this.$emit(`hooks:${currentValue}`, ...arguments);
        }
        return previousValue;
    }, {})
});

// 上拉加载, 下拉刷新
export function useReachBottomLoad(opts: { getData: () => void, immediate?: boolean, pullToRefresh?: boolean }) {
    const state = reactive(new class {
        currentPage = 1;
        pageSize = 20;
        loading = false;
        finished = false;
        refreshing = false;
        form = computed(() => {
            return {
                startIndex: state.pageSize * (state.currentPage - 1),
                row: state.pageSize
            }
        });
        loadingStatus = computed(() => state.finished ? 'nomore' : state.loading ? 'loading' : 'loadmore');
        refresh = () => {
            state.currentPage = 1;
            state.loading = false;
            state.finished = false;
            state.refreshing = true;
            return state.nextPage();
        };
        nextPage = async () => {
            if (state.loading || state.finished) {
                return;
            }
            state.loading = true;
            try {
                await opts.getData();
                state.currentPage++;
            } finally {
                state.loading = false;
                state.refreshing = false;
                opts.pullToRefresh && uni.stopPullDownRefresh();
            }
        }
    });

    useOnReachBottom(state.nextPage);
    opts.immediate && onMounted(opts.getData);
    opts.pullToRefresh && useOnPullDownRefresh(state.refresh);
    return state;
}
1

建议用官方的vue2 开发,不要整这些东西来搞自己

4

引入主要是简化开发的

7

封装onLoad

import { getCurrentInstance } from '@vue/composition-api'

export function onLoad(callback: (options: Record<string, string | undefined>) => void) {
    getCurrentInstance()?.proxy.$on('hook:onLoad', callback)
}

export function onShow(callback: () => void) {
    const $this = getCurrentInstance()?.proxy
    if ($this) {
        const hook = $this.$mp.page ? 'onShow' : 'onPageShow'
        $this.$on(`hook:${hook}`, callback)
    }
}

封装哪些hook就在全局混入里加入哪些hook, 否则自己封装的hook可能不会触发

import Vue from 'vue'
import { noop } from 'lodash'

Vue.mixin({
    onLoad: noop,
    onShow: noop
})

封装template ref

import Vue, { ComponentOptions } from 'vue'
import { CombinedVueInstance } from 'vue/types/vue'
import { getCurrentInstance, onMounted, onUpdated, Ref, ref } from '@vue/composition-api'

type VueInstance = Vue extends {$refs: { [key: string]: infer R }} ? R : never
type CombinedVueInstanceFromOptions<
        Options extends ComponentOptions<Vue>,
    > = Options extends ComponentOptions<
        infer Vue,
        infer Data,
        infer Methods,
        infer Computed,
        {},
        infer Props
    > ? CombinedVueInstance<Vue, Data, Methods, Computed, Props> : never

/**
 * @description 接收一个字符串返回对应的ref名称的组件实例
 * @example
 * setup() {
 *     //如果是class组件
 *     const input = useVueRef<Input>('refName')
 *     //如果是选项式组件
 *     const input = useVueRef<typeof Input>('refName')
 *
 *     onMounted(() => input.value?.focus())
 * }
 * */
function useVueRef<
    Instance extends ComponentOptions<Vue> | ComponentOptions<Vue>[] | VueInstance = VueInstance,
    VueRef = Instance extends VueInstance ? Instance :
            Instance extends ComponentOptions<Vue>[] ? CombinedVueInstanceFromOptions<Instance[number]>[] :
            Instance extends ComponentOptions<Vue> ? CombinedVueInstanceFromOptions<Instance> :
            never
>(refName: string): Ref<VueRef | undefined> {
    const $vm = getCurrentInstance()?.proxy
    const vueRef = ref<VueRef | undefined>()
    if ($vm) {
        const refreshRef = () => {
            vueRef.value = $vm.$refs[refName] as unknown as VueRef | undefined
        }
        onMounted(refreshRef)
        onUpdated(refreshRef)
    }
    return vueRef
}

export default useVueRef
3

image 这样并不会执行对应的回调方法

3

使用composition-api在安卓设备上容易触发onMemoryWarning警告,你们有遇到么