【问题标题】:How to get Flow to properly work with Vue 2 (webpack)?如何让 Flow 与 Vue 2 (webpack) 一起正常工作?
【发布时间】:2017-02-27 12:56:18
【问题描述】:

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

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

我的第二次尝试是添加一个 webpack 加载器(即 flow-status-webpack-plugin)并在构建过程中运行 Flow check(例如 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“理解”Vue 组件的结构(我猜对指令也是如此)。

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

【问题讨论】:

    标签: webpack vue.js vuejs2 babeljs flowtype


    【解决方案1】:

    我已经用flowvue 实现了一个项目模板。 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. 配置.babelrc:https://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 store:状态、提交处理程序、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

    【讨论】:

      【解决方案2】:

      使用 eslint + 流

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

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

      以下是设置 flow 和 eslint 的方法:

      1. 安装依赖

        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 */。 见原帖here

      【讨论】:

      • 我无法让它工作。我全力以赴并重新安装了 Atom 以确保一切设置正确,但我仍然无法在我的 Vue 组件中显示任何错误。如果我强制 Atom 将其解析为 javascript 文件,它仍然可以工作。嗯..你用的是什么编辑器?使用 linter-eslint 包来支持 ESLint。有什么想法吗 ?把它弄起来真是太好了。
      • 搞定了!我必须将 linter-eslint 包激活为“Lint HTML 文件”,因为这将激活 .vue 文件的 linting。
      • 太棒了。让它在 Atom 中的 Sublime 中工作(供参考)。编码愉快!
      【解决方案3】:

      我认为这个问题已经解决了,现在你可以在没有 hack 的情况下使用 Flow 和 Vue 组件。有关配置详细信息,请参阅这篇相当精彩的文章: https://alligator.io/vuejs/components-flow/

      【讨论】:

      • 嘿@musicformellons。这是另一种方法,但请注意,如果您选择 eslint 方法,flow check 将不起作用。见detailed explanation。不过,一种有效的方法:-)
      【解决方案4】:

      除了Nik's answer,值得一提的是,将他的“评论”策略与运行时检查器相结合,使“包”更加完整。 一种方法是使用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 的注释中包含模板和样式块;注释掉脚本标签(here提到了这种方法):

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

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

      3. 在生产环境中禁用 tcomb

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

      【讨论】:

      • 问题:如果我像 vue 2.0 boilerplate example 那样在开头构建我的组件,其中 .vue 仅加载外部 .js 和 .html 并让流 cli 忽略 .vue 并仅检查 .js 它可以也可以在没有丑陋的修改的情况下工作(因为它在开始时是分开的)。你怎么看?
      • 是的,应该没问题。除非您在流程配置文件中明确声明,否则默认情况下不会检查 Vue 文件。
      【解决方案5】:

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

       /* @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 编译器仍会识别 &lt;template&gt;, &lt;style&gt; and &lt;script&gt; 部分,但 Flow 类型检查器将忽略它们并仅处理正确的 javascript 部分。

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

      【讨论】:

      • 这太糟糕了。我希望有一种方法可以获得 100% 的覆盖率,也许是一些 Flow 注释魔术,但我开始相信没有。关于注释掉这些部分,使用 babel-plugin-typecheck 不需要注释掉模板和样式,因为它只检查脚本中的内容。它的工作方式与流式 webpack 插件或通过 CLI 运行流式有很大不同,但到目前为止我没有任何问题。
      • 我再给它几天,如果没有其他关于如何实现这一点的建议,我会接受你的回答:-)
      • 好的@Nik。在开发期间通过babel-plugin-tcomb 进行运行时检查+flow check 作为CI 的一部分,设法让它按照我最初想要的方式运行。在新答案中编写了我的配置;也许它会帮助别人。我会保持你的接受。感谢您的帮助。
      猜你喜欢
      • 2017-06-27
      • 1970-01-01
      • 2019-11-04
      • 2017-07-06
      • 2019-10-31
      • 2018-11-03
      • 2018-04-09
      • 2019-04-27
      • 1970-01-01
      相关资源
      最近更新 更多