支持在Vue.js中使用可选链。

29

我使用@vue/cli-service 4.2创建了Vue和Electron应用程序,但我遇到了“可选链”问题。

我无法使用“?”来验证条件,例如(@babel/plugin-proposal-optional-chaining)。

例如:a?.b?.c,它的意思是先检查a是否存在,然后检查b,否则返回false,类似于Angular中的模板表达式。

有人知道如何在Vue.js中配置可选链吗?


你是在说支持在不支持可选链的浏览器中使用可选链吗?还是说即使浏览器支持,Vue中的可选链也会失败? - Flimm
@Flimm:不是针对浏览器的。我只是在谈论Vue.js中的可选链。在React和Angular中,我们有可选链支持,但在Vue.js中没有支持。 - Hardik Kothari
9个回答

13

一个快速的更新是Vue 3自带了对可选链的支持。

你可以尝试编译以下Vue组件代码来测试。

<template>
  <div id="app" v-if="user?.username">
    @{{ user?.username }} - {{ fullName }} <strong>Followers: </strong>
    {{ followers }}
    <button style="align-self: center" @click="followUser">Follow</button>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'App',
  props: {
    test: Object
  },
  data() {
    return {
      followers: 0,
      user: {
        id: 1,
        test: {},
        username: '_sethAkash',
        firstName: undefined,
        lastName: 'Seth',
        email: 'sethakash007@gmail.com',
        isAdmin: true
      }
    }
  },
  computed: {
    fullName(): string {
      //
      return `${this?.test?.firstName} ${this?.user?.lastName}`
    }
  },
  methods: {
    followUser: function () {
      this.followers += 1
    }
  },
  watch: {
    followers(newFollowerCount, oldFollowerCount) {
      if (oldFollowerCount < newFollowerCount) {
        console.log(`${this?.user?.username} has gained a follower!`)
      }
    }
  },
  mounted() {
    this.followUser()
  }
})
</script>

11

试试vue-template-babel-compiler

它使用Babel来使Vue.js SFC支持可选链(?.),空值合并运算符(??)和许多新的ES语法。

Github 仓库: vue-template-babel-compiler

演示

演示图片

用法

1. 安装

npm install vue-template-babel-compiler --save-dev

2. 配置

1. Vue-CLI

Vue-CLI DEMO project

// vue.config.js
module.exports = {
    chainWebpack: config => {
        config.module
            .rule('vue')
            .use('vue-loader')
            .tap(options => {
                options.compiler = require('vue-template-babel-compiler')
                return options
            })
    }
}

2. Nuxt.js

Nuxt.js的DEMO项目

// nuxt.config.js
export default {
  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
    loaders: {
      vue: {
        compiler: require('vue-template-babel-compiler')
      }
    },
  },
  // ...
}

请查看REAMDE以获取详细使用说明

支持Vue-CLI,Nuxt.js,Webpack,任何使用vue-loader v15+的环境。


3
webpack的配置对我没用,这是有任何要求吗?我有vue-loader 14和已经过时的webpack 3,其他东西大体上都是最新的。 - Maxym Kopych
1
@MaxymKopych,抱歉,它不兼容vue-loader <= 15.0.0。这可能是您升级项目的机会。 - Junior Tour
1
@JuniorTour 我使用的是 vue-loader 的版本是 15.9.6,但它仍然无法正常工作。 - Loc Truong
1
此库不会破坏任何现有的东西,它与所有 Vue.js 语法兼容。如果遇到任何错误,请创建一个 GitHub 问题,它仍在维护中,错误将会被修复。 - Junior Tour
我将我的 Node 更新到 v16 后,它开始无法工作。是否需要进行任何更改以支持该版本?@JuniorTour - Bravo
显示剩余2条评论

8
根据这里的评论,您可以创建一个全局mixin并使用eval函数来评估表达式。
例如:
Vue.mixin({
  methods: {
    $evaluate: param => eval('this.'+param)
  }
});

在模板中:
<template>
  <p>{{ $evaluate('user?.name') }}</p>
</template>


他们还说这可能不完美:

虽然它仍然不能替代真正的操作员,特别是如果你有很多它的出现


编辑

如上所述,使用eval可能会带来一些意想不到的问题,我建议您改用计算属性。

在SFC中:

<template>
  <p>{{ userName }}</p>
</template>

<script>
export default {
  data(){
    return { 
      user: {
        firstName: 'Bran'
      }
    }
  },
  computed: {
    userName(){
      return this.user?.firstName
    }
  }
}
</script>

8
不应使用eval,因为它在一些浏览器中会引发错误,而且这显然是不良实践。 - Michał Styś

1
/*
 * Where to use: Use in vue templates to determine deeply nested undefined/null values
 * How to use: Instead of writing parent?.child?.child2 you can write
 *            isAvailable(parent, 'child.child2')
 * @author    Smit Patel
 * @params    {Object} parent
 *            {String} child
 * @return    {Boolean}     True if all the nested properties exist
 */
export default function isAvailable(parent, child) {
  try {
    const childArray = String(child).split('.');
    let evaluted = parent;
    childArray.forEach((x) => {
      evaluted = evaluted[x];
    });
    return !!evaluted;
  } catch {
    return false;
  }
}

使用:
<template>
  <div>
    <span :v-if="isAvailable(data, 'user.group.name')">
      {{ data.user.group.name }}
    <span/>
  </div>
</template>
<script>
import isAvailable from 'file/path';
export default {
   methods: { isAvailable }
}
</script>

1

这并不完全相同,但我认为在这种情况下,对于大多数情况来说,可能会更好。

我使用了 Proxy 来实现魔法方法效果。您只需要调用对象的 nullsafe 方法,然后从那里开始,就可以正常链接。

在某些版本的 VueJs 中,您无法指定默认值。它将我们的空安全值视为对象(有充分的理由),并将其 JSON.stringify,绕过了 toString 方法。我可以覆盖 toJSON 方法,但无法返回字符串输出。它仍然将您的返回值编码为 JSON。因此,您最终会得到带引号的字符串。

const isProxy = Symbol("isProxy");
Object.defineProperty(Object.prototype, 'nullsafe', {
  enumarable: false,
  writable: false,
  value: function(defaultValue, maxDepth = 100) {
    let treat = function(unsafe, depth = 0) {
      if (depth > maxDepth || (unsafe && unsafe.isProxy)) {
        return unsafe;
      }
      let isNullish = unsafe === null || unsafe === undefined;
      let isObject = typeof unsafe === "object";
      let handler = {
        get: function(target, prop) {
          if (prop === "valueOf") {
            return target[prop];
          } else if (typeof prop === "symbol") {
            return prop === isProxy ? true : target[prop];
          } else {
            return treat(target[prop], depth + 1);
          }
        }
      };
      let stringify = function() {
        return defaultValue || '';
      };
      let dummy = {
        toString: stringify,
        includes: function() {
          return false;
        },
        indexOf: function() {
          return -1;
        },
        valueOf: function() {
          return unsafe;
        }
      };

      return (isNullish || isObject) ? (new Proxy(unsafe || dummy, handler)) : unsafe;
    };

    return treat(this);
  }
});


new Vue({
  el: '#app',
  data: {
    yoMama: {
      a: 1
    }.nullsafe('xx'),
    yoyoMa: {
      b: 1
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  {{ yoMama.yoyoMa.yoMama.yoyoMa.yoMama }}
  <hr> {{ yoyoMa.nullsafe('yy').yoMama.yoyoMa.yoMama.yoyoMa }}
</div>


0

在模板和js文件中使用getSafe()方法:

<template><div>
  {{getSafe(() => obj.foo.bar)}} <!-- returns 'baz' -->
  {{getSafe(() => obj.foo.doesNotExist)}} <!-- returns undefined -->
</div></template>

<script>
export default {
    data() {
        return {obj: {foo: {bar: 'baz'}}};
    },
    methods: {getSafe},
};
function getSafe(fn) {
    try { return fn(); }
    catch (e) {}
}
</script>

0
在搜索了许多可能性后,我创建了一个函数来帮助自己。
创建一个js文件来保存这个帮助函数并导出它。
const propCheck = function (obj = {}, properties = ""){

    const levels = properties.split(".");
    let objProperty = Object.assign({}, obj);

    for ( let level of levels){
        objProperty =  objProperty[level];
        if(!objProperty) 
            return false;
    }

   return true;

}
export default propCheck;

并在Vue实例中全局安装此函数

Vue.prototype.$propCheck = propCheck;

在你的模板中使用后
<span>{{$propCheck(person, "name")}}</span>

或者

<span>{{$propCheck(person, "contatcs.0.address")}}</span>

或者

<span>{{$propCheck(person, "addres.street")}}</span>

我认为在Vue3中,vue-loader包将支持运算符“可选链”,因此将不再需要此功能。 - Alison Rodrigues

0

在这种情况下,您可以使用loadash的get方法:

_.get(object, path, [defaultValue])

获取对象路径上的值。如果解析的值未定义,则返回默认值。

https://lodash.com/docs/4.17.15#get

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// => 3

_.get(object, 'a.b.c', 'default');
// => 'default'

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接