Vue3 Typescript 打破了 webpack encore watcher
Posted
技术标签:
【中文标题】Vue3 Typescript 打破了 webpack encore watcher【英文标题】:Vue3 Typescript breaks the 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"
【问题讨论】:
【参考方案1】:您似乎正在将 Vue Javascript 项目迁移到 Vue typescript 项目中。因此,我们必须明智地配置 webpack 和 tsconfig。我们必须按照一些步骤来正确配置它。
-
将打字稿添加到我们的 ts 项目中
vue add typescript
配置 tsconfig 文件以支持 javascript 和 typescript 模块
我建议你必须添加
allowJS
为 true 以便允许将 js 文件导入到我们的 ts 模块中。
importHelper
为真。
allowSyntheticDefaultImports
为真,它允许像下面这样默认导出,我认为编译器选项的这个属性将帮助您完成当前情况的工作。在我看来,它无法正确重新编译导入或导出语句。
import TestClass from "resources/ts/helpers";
-
在你的项目目录下添加
shims-vue.d.ts
和shim-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"
]
-
现在,最后一步是配置我们的 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
或许多打字稿插件来支持项目中的打字稿。
【讨论】:
感谢您的工作,但是@joachimwedin 先发布了它,他的回答就足够了。尽管您的答案看起来很完整,但为了构建 vue3/symfony + ts 项目,您的某些观点并不是强制性的,如此处所述 (symfony.com/doc/current/frontend/encore/typescript.html) 和 (v3.vuejs.org/guide/…) 这很公平。 :) 这对其他人会有所帮助:)【参考方案2】:我可以看到的一个可能的问题是这一行:
.addEntry('main', './resources/main.js')
在使用 typescript 的 Encore docs 中,他们将条目设置为 .ts 文件:
.addEntry('main', './assets/main.ts')
如果您有一个main.ts
文件,那么这可以解释为什么编辑 helper.ts 会导致它再次工作。编辑文件会触发 typescript 编译器重新编译(从 .ts -> .js 编译),并创建一个 main.js
文件(之前可能不存在)。
【讨论】:
是的,这就是问题所在......不太清楚我是怎么错过的,这里说得很清楚(symfony.com/doc/current/frontend/encore/typescript.html)以上是关于Vue3 Typescript 打破了 webpack encore watcher的主要内容,如果未能解决你的问题,请参考以下文章