如何让 Flow 与 Vue 2 (webpack) 一起正常工作?

How to get Flow to properly work with Vue 2 (webpack)?

我正在尝试将 Flow 添加到 Vue 2 webpack 模板。作为记录,我只使用 运行 时间(文件遵循 .vue 格式/标准)。

我的第一次尝试是通过 cli 使用流程,我意识到它不会起作用,因为它不知道如何处理 .vue 文件。

我的第二次尝试是添加一个 webpack 加载器(即 flow-status-webpack-plugin)和 运行 流检查作为构建的一部分(例如 eslint 工作)。那没有成功,所以我研究了其他选择。

我的第三次尝试是使用 babel 插件,一开始还算成功。我用了babel-plugin-typecheck + babel-plugin-syntax-flow。 Webpack 中没有输出,但是类型错误会破坏应用程序。我对这种方法很好;它可以与 CI 一起正常工作并破坏构建。

这是我的 .babelrc 的样子:

{
  ...
  "plugins": [
    ...
    ["typecheck", {
      "disable": {
        "production": true
      }
    }],
    "syntax-flow",
    "transform-flow-strip-types"
  ],
  ...
}

此时,Flow 对于全局方法可以正常工作,但不能在 Vue 组件内部工作:

<template>...</template>

<script>
/* @flow */
const flowIt = (a: number): number => {
  return a * 10
}

flowIt(20)
flowIt('bah') // Uncaught TypeError: Value of argument "a" violates contract. Expected: number Got: string

export default {    
  mounted: function () {
    flowIt(20)
    flowIt('bah') // Sees nothing wrong here
  }
}
</script>

<style>...</style>

最重要的是,目标是不因为 Flow 而更改应用程序代码。理想情况下,我会像往常一样使用 Vue:

<template>...</template>

<script>
/* @flow */
export default {  
  methods: {
    flowIt (a: number): number {
      return a * 10
    }
  },

  mounted: function () {
    this.flowIt(20)
    this.flowIt('bah') // Should throw a type error.
  }
}
</script>

<style>...</style>

不确定这是否与 Vue 有很大关系,因为它与我使用 Flow 的经验有关(提示:没有那么多经验)。我在想我需要一些类型文件来使 Flow 'understand' Vue 组件的结构(我猜指令也是如此)。

对于那些有更多经验的人,您是如何让 Flow 正确地与 Vue + webpack 一起工作的?

您仍然可以将 Flow 用于 .vue 组件的 JS 部分,方法是注释掉 <template><style><script> 部分:

 /* @flow
 <style>
 ...style definitions here
 </style>
 <template>
 ...html...
 </template>
 */
 // <script>
 export default {  
   methods: {
      flowIt (a: number): number {
         return a * 10
      }
   },

   mounted: function () {
      this.flowIt(20)
      this.flowIt('bah') //Won't throw error, as flowIt is attached to
                         //this.
   }
}
// </script>

即使有注释,vue 编译器仍会识别 <template>, <style> and <script> 部分,但 Flow 类型检查器会忽略它们,只处理正确的 javascript 部分。

不幸的是,这不会让您获得 100% 的类型覆盖率,因为 Flow 将无法检查附加到 this(Vue 组件本身)的函数和对象,但是,您仍然可以从Flow 对外部函数调用的类型检查(例如 Vuex 动作和 getter,其他 javascript 导入模块),如果您在组件的方法中扩展了业务逻辑,则在使用方法参数时可以获得一些类型安全.

除了 之外,值得一提的是,将他的 'comment' 策略与 运行 时间检查器相结合,使 'the package' 更加完整。 一种方法是使用 babel-plugin-tcomb。这将使 运行 时间检查器成为 webpack / 构建过程的一部分(保存时)+ flow check 作为 CI 脚本的一部分。

对于开发,tcomb 将进行 运行时间检查并抛出异常(控制台)。它不做静态检查,所以下面

<script>
/* @flow */
const flowIt = (a: number): number => {
  return '' // Sees nothing wrong here, should be a number
}

// Vue component
export default {    
  ...
}
</script>

不会按预期工作。但是,以下将:

<template>{{ foo('bar') }} <!-- Type error --></template>
<script>
/* @flow */
const flowIt = (a: number): number => {
  return '' // Type error
}

// Vue component
export default {    
  methods: {
    foo: (x) => { flowIt(x) // Type error }
  },

  mounted: () => {
    flowIt([]) // Type error
  }
}
</script>

这并不理想,但它会在每次保存后进行检查,这会发现大多数类型错误。 值得一提的是:tcomb 使用相同的注释(在内部使用 Flow),因此它开箱即用。

Ofc,这还不够好,有点违背了 Flow 的要点。如前所述,解决方案是在 CI 上仍然 运行 flow check。这需要进行一些更改:

  1. 更新 .flowconfig 以加载 .vue 文件:

    ...
    [options]
    module.file_ext=.vue
    module.file_ext=.js
    ...
    
  2. 在包含@flow pragma 的注释中包含模板和样式块;注释掉脚本标签(这种方法被提到):

    /* @flow
    <template>...</template>
    
    <style>...</style>
    */
    
    // <script>
    ...
    // </script>
    

    这有点尴尬,但我找不到更好的方法。理想情况下,Flow 能够处理 HTML 文档中的 <script> 标签,但这只是目前的愿望清单 (see issue).

  3. 在生产中禁用 tcomb

    {
      ...
      "plugins": [
        ...
        "syntax-flow",
        "transform-flow-strip-types"
      ],
      "env": {
        "development": {
          "plugins": ["tcomb"]
        }
      }
    }
    

我认为这已同时解决,现在您可以将 Flow 与 Vue 组件一起使用而无需 hack。有关配置详细信息,请参阅这篇相当精彩的文章: https://alligator.io/vuejs/components-flow/

使用 eslint + 流

这是另一种集成 Flow 和 Vue 的方法。 与此同时,flow来到了eslint。因此,我们可以直接将流错误作为 lint 错误。这是一种更简洁的方法,但是流程会与您的构建过程相结合(您不能独立 运行 flow check,但需要通过 webpack 运行 整个构建管道来获取错误) .仍在等待 this issue 得到解决,以在 .vue 文件中提供完整的流程支持,截至 2017 年 5 月 10 日

在大多数情况下这很好,但有些人可能仍然想要 运行ning flow check 的灵​​活性(和速度)。这也可能取决于您的 CI 设置。

以下是设置流程和 eslint 的方法:

  1. 安装 deps

    yarn add \
      babel-plugin-syntax-flow \
      babel-plugin-transform-class-properties \
      babel-plugin-transform-flow-strip-types \
      eslint \
      babel-eslint \
      eslint-plugin-html \
      eslint-plugin-flowtype-errors \
      eslint-plugin-vue \
      eslint-config-vue \
      flow-bin \
    -D
    
  2. 配置.babelrc

    {
      ...
      "plugins": [
        "babel-plugin-transform-class-properties",
        "babel-plugin-syntax-flow",
        "babel-plugin-transform-flow-strip-types"
      ]
    }
    
  3. 配置.eslintrc

    {
      "parser": "babel-eslint",
    
      "plugins": [
        "html",
        "flowtype-errors"
      ],
    
      "extends": [
        "vue"
      ],
    
      "rules": {
        "flowtype-errors/show-errors": 2
      }
    }
    
  4. 创建一个 .flowconfig 文件。如果您没有任何配置,它可以为空。

在这种情况下不需要其他解决方法,然后您可以在任何 .vue 文件的脚本标记中使用 /* @flow */。 见原文posthere.

我已经为 vueflow 实施了一个项目模板。 https://github.com/wemake-services/wemake-vue-template 它支持单文件组件、linting、jest 测试、构建和服务器端渲染。

vue

您的组件如下所示:

<template>
...
</template>

<script>
// @flow

import Vue from 'vue'
import { Store } from 'vuex'
import Component from 'nuxt-class-component'
import { Getter, State } from 'vuex-class'
import AppLogo from '~/components/AppLogo'
import Comment from '~/components/Comment'
import type { CommentType, StateType } from '~/types'

@Component({
  components: {
    AppLogo,
    Comment
  }
})
export default class Index extends Vue {
  @State('comments') comments: Array<CommentType>
  @Getter('hasComments') hasComments: boolean

  fetch (
    { store, app }: { store: Store<StateType>, app: Vue }
  ): Promise<Array<CommentType>> {
    // Uncomment the next line to test flow types:
    // console.log(this.comments + 12)
    return store.dispatch('fetchComments', app)
  }
}
</script>

它需要配置几件事:

  1. 依赖关系。名称中带有 flow 的所有内容来自此处:https://github.com/wemake-services/wemake-vue-template/blob/master/template/package.json
  2. 正在创建有效 .flowconfig。这可能很棘手:https://github.com/wemake-services/wemake-vue-template/blob/master/template/.flowconfig
  3. 正在配置 .babelrchttps://github.com/wemake-services/wemake-vue-template/blob/master/template/.babelrc
  4. 正在配置 eslinthttps://github.com/wemake-services/wemake-vue-template/blob/master/template/.eslintrc
  5. 每次正常安装后触发 flow-typed installhttps://github.com/wemake-services/wemake-vue-template/blob/master/template/package.json#L12

vuex

您还可以注释 vuex 存储:状态、提交处理程序、getter 和操作。这部分你可以使用@vue-flow-typed/vuex

看起来是这样的:

type StateType = {
  comments: string[]
}

function state (): StateType {
  return {
    comments: null
  }
}

const getters = {
  hasComments (state: StateType): boolean {
    return Boolean(state.comments && state.comments.length > 0)
  }
}

const mutations = {
  'SET_COMMENTS': (
    state: StateType, comments: string[]
  ) => {
    state.comments = comments
  }
}

const actions = {
  async fetchComments (
    { commit, state }: ActionContext<StateType>
  ) {
    const data = await Promise.resolve(['good', 'nice'])
    commit('SET_COMMENTS', data)
    // Uncomment next line to see typing in action:
    // console.log(state.comments, state.fake)

    return data
  }
}

但请注意,有些部分仍然无法注释。 在此处阅读有关已知问题的更多信息:https://github.com/sobolevn/vue-flow-typed#known-problems