【问题标题】:Vue3 Typescript breaks the webpack encore watcherVue3 Typescript 打破了 webpack encore watcher
【发布时间】:2021-10-16 19:59:33
【问题描述】:

我有一个 vue3/symfony 项目,我开始实现 typescript,但遇到了一个我无法解决的问题。对于构建资产,我使用的是 webpack encore,当我启动观察程序时,资产编译得很好,但是当我更改 .vue 文件中的任何内容时(甚至添加空格以强制 webpack 重新编译)我得到这个错误:

TS2614: Module '"resources/ts/helpers"' has no exported member 'TestClass'. Did you mean to use 'import TestClass from "resources/ts/helpers"' instead?

TS2339: Property '__file' does not exist on type '{}'.

重要提示:如果我在 helper.ts 上进行任何类型的更改(甚至是空格),编译将再次成功。

这只发生在 .ts 文件导入到 .vue 文件中。我导入的 .js 或 .vue(带或不带 typescript)文件都很好

helpers.ts:

export class TestClass {
  constructor(public test: string) {
  }
}

导入为import { TestClass } from "resources/ts/helpers";

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "noImplicitThis": true,
    "jsx": "preserve",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ],
    "baseUrl": ".",
    "paths": {
      "resources/*": [
        "resources/*"
      ]
    }
  },
  "include": [
    "resources/**/*.ts",
    "resources/**/*.tsx",
    "resources/**/*.vue",
  ],
  "exclude": [
    "node_modules"
  ]
}

webpack.config.js:

const Encore = require('@symfony/webpack-encore');
const path = require('path');
const webpack = require('webpack');

// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
  Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}

Encore
  // directory where compiled assets will be stored
  .setOutputPath('public/build/')
  // public path used by the web server to access the output path
  .setPublicPath('/build')
  // only needed for CDN's or sub-directory deploy
  //.setManifestKeyPrefix('build/')
  .copyFiles({
    from: './resources/assets/media',
    to: 'media/[path][name].[ext]',
    pattern: /\.(png|jpg|jpeg|svg)$/
  })
  .copyFiles({
    from: './resources/assets/fonts',
    to: 'fonts/[path][name].[ext]',
    pattern: /\.(ttf)$/
  })
  /*
   * ENTRY CONFIG
   *
   * Add 1 entry for each "page" of your app
   * (including one that's included on every page - e.g. "app")
   *
   * Each entry will result in one JavaScript file (e.g. main.js)
   * and one CSS file (e.g. app.css) if you JavaScript imports CSS.
   */
  .addEntry('main', './resources/main.js')

  // When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
  .splitEntryChunks()

  // will require an extra script tag for runtime.js
  // but, you probably want this, unless you're building a single-page app
  .enableSingleRuntimeChunk()

  /*
   * FEATURE CONFIG
   *
   * Enable & configure other features below. For a full
   * list of features, see:
   * https://symfony.com/doc/current/frontend.html#adding-more-features
   */
  .cleanupOutputBeforeBuild()
  .enableBuildNotifications()
  .enableSourceMaps(!Encore.isProduction())
  // enables hashed filenames (e.g. app.abc123.css)
  .enableVersioning(Encore.isProduction())

  // enables @babel/preset-env polyfills
  .configureBabel(() => {
  }, {
    useBuiltIns: 'usage',
    corejs: 3
  })

  // enables Sass/SCSS support
  .enableSassLoader()

  // enables Vue support
  .enableVueLoader(() => {
  }, {
    version: 3,
    runtimeCompilerBuild: false //if using only single file components, this is not needed (https://symfony.com/doc/current/frontend/encore/vuejs.html#runtime-compiler-build)

  })
  // uncomment if you use TypeScript
  .enableTypeScriptLoader()

  // uncomment if you're having problems with a jQuery plugin
  .autoProvidejQuery()
  .addAliases({
    'resources': path.resolve('./resources')
  })
;

module.exports = Encore.getWebpackConfig();

package.json:

{
  "devDependencies": {
    "@symfony/webpack-encore": "^1.5.0",
    "@types/jquery": "^3.5.5",
    "@vue/compiler-sfc": "^3.0.2",
    "babel-core": "^7.0.0-bridge.0",
    "file-loader": "^6.0.0",
    "https-proxy-agent": "^2.2.1",
    "lorem-ipsum": "^2.0.3",
    "sass": "^1.32.13",
    "sass-loader": "^10.2.0",
    "ts-loader": "^8.3.0",
    "tslib": "^2.3.0",
    "vue-loader": "^16.5.0",
    "vue-template-compiler": "^2.6.12",
    "webpack-notifier": "^1.6.0"
  },
  "license": "UNLICENSED",
  "private": true,
  "scripts": {
    "dev-server": "encore dev-server",
    "dev": "encore dev",
    "watch": "encore dev --watch",
    "build": "encore production --progress"
  },
  "dependencies": {
    "@babel/polyfill": "^7.12.1",
    "@ckeditor/ckeditor5-build-classic": "^25.0.0",
    "@fortawesome/fontawesome-free": "^5.15.3",
    "@popperjs/core": "^2.5.4",
    "@tinymce/tinymce-vue": "^4.0.0",
    "@vee-validate/rules": "^4.2.4",
    "@vueform/multiselect": "^2.0.1",
    "axios": "^0.21.1",
    "bootstrap": "^5.0.2",
    "chart.js": "^2.9.4",
    "core-js": "^3.6.5",
    "dropzone": "^5.9.2",
    "element-plus": "^1.0.2-beta.36",
    "es6-promise": "^4.2.8",
    "inputmask": "^5.0.6",
    "jquery": "^3.5.1",
    "lodash": "^4.17.20",
    "nprogress": "^0.2.0",
    "perfect-scrollbar": "^1.5.0",
    "select2": "^4.0.13",
    "sweetalert2": "^10.10.0",
    "typescript": "^4.3.4",
    "vee-validate": "^4.5.0-alpha.2",
    "vue": "^3.0.7",
    "vue-inline-svg": "^3.0.0-beta.2",
    "vue-router": "^4.0.3",
    "vuex": "^4.0.0-rc.1",
    "yup": "^0.29.3"
  }
}

【问题讨论】:

    标签: typescript webpack vuejs3 webpack-encore


    【解决方案1】:

    您似乎正在将 Vue Javascript 项目迁移到 Vue typescript 项目中。因此,我们必须明智地配置 webpack 和 tsconfig。我们必须按照一些步骤来正确配置它。

    1. 将打字稿添加到我们的 ts 项目中
    vue add typescript
    
    1. 配置 tsconfig 文件以支持 javascript 和 typescript 模块

      我建议你必须添加

      • allowJS 为 true 以便允许将 js 文件导入到我们的 ts 模块中。

      • importHelper 为真。

      • allowSyntheticDefaultImports 为真,它允许像下面这样默认导出,我认为编译器选项的这个属性将帮助您完成当前情况的工作。在我看来,它无法正确重新编译导入或导出语句。

    import TestClass from "resources/ts/helpers";
    
    1. 在你的项目目录下添加shims-vue.d.tsshim-tsx.d.ts文件,它将让typescript理解*.vue文件和代码风格的JSX语法。如果您想进一步了解两者之间的区别,请阅读此answer
    //shims-vue.d.ts
    declare module "*.vue" {
      import Vue from 'vue';
      export default Vue;
    }
    
    //shims-tsx.d.ts
    import Vue, { VNode } from 'vue';
    
    declare global {
      namespace JSX {
        // tslint:disable no-empty-interface
        interface Element extends VNode {}
        // tslint:disable no-empty-interface
        interface ElementClass extends Vue {}
        interface IntrinsicElements {
          [elem: string]: any;
        }
      }
    }
    

    为了让 typescript 能够读取这两个文件,我们需要将这些文件添加到 tsconfig 的 files 属性中。

    // tsconfig.ts
      "files": [
         "shims-vue.d.ts",
         "shims-tsx.d.ts"
       ] 
    

    您的整个 tsconfig 文件如下所示

    {
      "compilerOptions": {
        "target": "esnext",
        "module": "esnext",
        "noImplicitThis": true,
        "jsx": "preserve",
        "moduleResolution": "node",
        "esModuleInterop": true,
    
     // my recommendation
        "importHelpers": true,
        "allowJS": true,
        "allowSyntheticDefaultImports": true,
     // -------------------------
    
        "skipLibCheck": true,
        "lib": [
          "esnext",
          "dom",
          "dom.iterable",
          "scripthost"
        ],
        "baseUrl": ".",
        "paths": {
          "resources/*": [
            "resources/*"
          ]
        }
      },
     // my addition
      "files": [
        "shims-vue.d.ts",
        "shims-tsx.d.ts"
      ] 
     // -------------
      "include": [
        "resources/**/*.ts",
        "resources/**/*.tsx",
        "resources/**/*.vue",
      ],
      "exclude": [
        "node_modules"
      ]
    }
    
    1. 现在,最后一步是配置我们的 webpack encore。 我检查了你的配置,我发现了一些你缺少的配置。
      • 首先,将main.js 转换为main.ts
      • 要让 ts-loader 解析 .vue 文件中的 <script lang="ts"> 块,您需要添加 appendTsSuffixTo 配置。
    // webpack.config.js
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
     // ...
     Encore
         // ...
         .addEntry('main', './resources/main.ts')
    
         .enableTypeScriptLoader(function (tsConfig){
           tsConfig.appendTsSuffixTo = [/\.vue$/]; 
           tsConfig.appendTsxSuffixTo = [/\.vue$/]; 
         })
    
         // don't know the reason why you didn't add HtmlWebpackPlugin
         .addPlugin(new HtmlWebpackPlugin({
           template: './src/main.html',
         }))    
    

    如果您遵循所有这些步骤,我认为您不会遇到任何问题。最后,您还需要@babel/typescript 或许多打字稿插件来支持项目中的打字稿。

    【讨论】:

    【解决方案2】:

    我可以看到的一个可能的问题是这一行:

    .addEntry('main', './resources/main.js')
    

    在使用 typescript 的 Encore docs 中,他们将条目设置为 .ts 文件:

    .addEntry('main', './assets/main.ts')
    

    如果您有一个main.ts 文件,那么这可以解释为什么编辑 helper.ts 会导致它再次工作。编辑文件会触发 typescript 编译器重新编译(从 .ts -> .js 编译),并创建一个 main.js 文件(之前可能不存在)。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-30
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    相关资源
    最近更新 更多