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

Posted

技术标签:

【中文标题】如何让 Flow 与 Vue 2 (webpack) 一起正常工作?【英文标题】:How to get Flow to properly work with 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 正常工作的?

【问题讨论】:

【参考方案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>

它需要配置几件事:

    依赖关系。名称中带有flow 的所有内容都来自这里:https://github.com/wemake-services/wemake-vue-template/blob/master/template/package.json 创建有效的.flowconfig。这可能很棘手:https://github.com/wemake-services/wemake-vue-template/blob/master/template/.flowconfig 配置.babelrc:https://github.com/wemake-services/wemake-vue-template/blob/master/template/.babelrc 配置eslint:https://github.com/wemake-services/wemake-vue-template/blob/master/template/.eslintrc 每次正常安装后触发flow-typed install:https://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 的方法:

    安装依赖

    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
    

    配置.babelrc

    
      ...
      "plugins": [
        "babel-plugin-transform-class-properties",
        "babel-plugin-syntax-flow",
        "babel-plugin-transform-flow-strip-types"
      ]
    
    

    配置.eslintrc

    
      "parser": "babel-eslint",
    
      "plugins": [
        "html",
        "flowtype-errors"
      ],
    
      "extends": [
        "vue"
      ],
    
      "rules": 
        "flowtype-errors/show-errors": 2
      
    
    

    创建一个.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。这需要进行一些更改:

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

    ...
    [options]
    module.file_ext=.vue
    module.file_ext=.js
    ...
    

    在包含@flow pragma 的注释中包含模板和样式块;注释掉脚本标签(here提到了这种方法):

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

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

    在生产环境中禁用 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 的一部分,设法让它按照我最初想要的方式运行。在新答案中编写了我的配置;也许它会帮助别人。我会保持你的接受。感谢您的帮助。

以上是关于如何让 Flow 与 Vue 2 (webpack) 一起正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 react-hot-loader 与 webpack 2 和 webpackDevMiddleware 一起工作?

如何在 Vue-cli 中配置 Webpack?

旧版vue-cli脚手架Webpack3项目如何升级Webpack4

Honeybadger projectRoot 与 webpack

如何在 Vue2 的 webpack 中加载 signalr.js

如何将 vue-cli 与同一个项目中现有的不相关 webpack 设置集成?