vscode vue3 typescript开发环境搭建
引言
今天搞一下vue3环境,用这玩意看看好使不,工欲善其事、必先利其器。vue2早期的语法看起来真实够呛,嵌套比较深。以前看过一个妹子写的react代码,发现里面很多js在控制样式和html,三个东西揉在一块也是厉害了。不知道是不是react本身的风格。vue3本身风格要接近原生js了,提倡组合式API,但是还是有些距离。从语法使用上来说还是早期原生js风格统一,没有各种套路,都是拿来就用,纯组合。这组件方开发感觉看起来不直白,一个页面里面很多自定义组件,不看源码不知道是啥玩意。比如<MyComp/>这是啥玩意?你可能说这是自定义组件。那<scroll-nav>呢?你可能会说中间有-的是自定义的(这里就有两种写法了),那再来一个<nav>呢?这个是自定义标签还是原生html标签看容易造成困扰。thymleaf在这个方面做得比较好,使用的标准的或者或尽量靠近html的语法。个人认为除非很小的代码片段可以内联在一起,不然css、html、js不宜混在一起,如果自定义组件遵从html语法风格,或者从语法上完全符合html语法规范,看起来应该会爽很多,如果vue的文件可以直接用.html做后缀,符合html规则,那应该会很清晰,当然这只是我个人的一个看法。这个后面在和react对比看看。
技术框架主要涉及windows 10 + vite + vue3 + pnpm + typescript相关方面的介绍。觉得有用的朋友可以收藏转发,希望能给更多的人带来价值。点赞收藏多,是我更新的源动力。
本篇文档不会讲vue的基础知识,默认情况是你懂css,html,原生js用法。如果是0基础,建议先找个视频从vue2看起,然后再看vue3。我自己也看了一个185集的视频,讲的比较全面,拉起快进开都差不多要两个周,加上自己实践的时间践估计得画差不多一个月左右(可以先看看文章后面没技术栈预览)。vue基础了解之后,在来看这个文章会比较适合,这篇文章都是以生产为标准写的。不像有些博客,你看了你觉得懂了,后面发现你的东西根本不能再生产使用,躺了坑,还要重新学或者做技术调研。
1 nodejs安装
从官网下载需要windows版本的node-v24.11.1-x64.msi 安装包。一路点击安装即可。如果要自定义安装路径,则在安装界面中指定路径即可。
2 设置npm安装包路径
设置全局包路径
npm config set prefix "G:\programdata\nodejs\lib"
设置下载缓存路径
npm config set cache "G:\programdata\nodejs\cache"
这样做的目的是防止C盘默认用户目录下满。但是有很多安装软件的缓存和包下载地址都喜欢下载到用户目录下,所以创建一个新用户到其他比较大盘,是一个很有远见的决定,以后就不怕用户目录堆满,影响系统运行。
如果程序不是在C盘安装(或者说不与系统在一个盘),空间足够大,可以将两个路径和node js一个安装路径目录,方便管理如:
npm config set prefix "D:\programs\nodejs-24.11\global_install"
npm config set cache "D:\programs\nodejs-24.11\install_cache"
global_install路径需要需要手动添加到path,因为有些全局安装的包会放在这下面,命令中需要用到,比如pnpm。一般项目依赖都不会做全局安装,所以也不用担心这个安装目录会下很多东西。
3 配置镜像
设置好阿里镜像源 : npm config set registry https://registry.npmmirror.com
这个不是必须的,只是为了加快下载速度。
4 npm其他常用命令
| 功能分类 | 命令 | 描述 |
|---|---|---|
| 配置管理 | npm config list |
查看所有默认配置的详细信息 |
npm config ls -l |
查看所有默认配置的详细信息(包含默认值) | |
npm config get <key> |
查看某个特定配置项的值 | |
| 帮助与路径 | npm help install |
查看 install 命令的详细帮助文档 |
npm root -g |
查看全局包的安装路径 | |
| 列表与版本 | npm list -g --depth=0 |
查看全局安装的包(限制层级,仅显示顶层) |
npm list --depth=0 |
查看项目根路径下的直接依赖包 | |
npm list [packageName] |
查找/查看特定包的安装情况 | |
npm outdated |
检查项目中是否有过时的包(需要更新) | |
| 包信息 | npm info <package-name> |
查看指定包的详细信息(如版本、作者、仓库等) |
5 创建项目
执行命令:npm create vue@latest my-vue-app1,会出现如下面的界面
默认选这三个就可以。后面两个需要用的话可以通过IDE安装。后面用到ESLint和Prettier,建议ESLint不要配置成自动修复,可能会修复出问题来,或者不小心搞到别人代码。Prettier也是类似问题,可能一下格式化到不该格式化的带代码,特别是老项目。TypeScript可以使用,也可以不使用,有聊胜于无,但是确定要使用的话你就必须全盘了解他的规范.举个简单的例子就是js文件后缀都要是ts,这里我就不选了。js一出生就是弱类型语言,支持面向对象。类型声明,搞得有点不论不类的。python现在也是搞得有点不论不类的。语言一旦用广了,规模大了,强类型和面向对象就显得更有优势。弱类型语言到了后面没有办法,各种模仿,创造,美其名为简洁灵活,反正我就是记不住他们的简洁灵活。这让我想起当年scala,野心勃勃感觉要把java替换掉了,能和java能互调用,无缝集成 ,信了你就倒霉了。两种风格的东西搞在一起真实不伦不类。学了两遍scala,现在一点都记不住他那强大灵活的语法,估计不是经常用吧。

这后面一路按回车,其他的一样都不要选,保持项目干净,尽量保持用到啥就安装啥。创建项目命令底层会调用create-vue,不再需要vue客户端了,所以不需要安装。
然后执行命令 npm install 安装项目本地依赖
如果需要初始化到git:git init && git add -A && git commit -m "initial commit"
6 安装VSCode
独立前端我建议用这个,当然IDEA也可以。如果是全栈开发,就建议用IDEA,Eclipse。
VSCode 去官网下载安装,是开源免费的软件。
这里有个比较重要的快捷键要记住Ctrl+Shift+P 调出命令面板,然后在输入命令检索,然后设置。这个用起来非常反人类,考记忆。
7 导入项目
1 启动VSCode
启动VSCode,然后在左上角File-->Open Folder 找到刚才创建的文件夹my-vue-app1打开即可

下面有个vue插件支持,点击install即可。旁边有个Coplit机器人,有钱的可以买个账号玩玩,在公司的最好不要玩这个东西,免得泄露信息,要合规。
2 配置折叠
下面这个是根据个人习惯处理,不是必须
Settings->explorer.compactFolders 限制折叠空文件夹,
项目->.vscode->settings.json ->"explorer.fileNesting.enabled": false,禁止分类折叠文件
8 关键配置说明

package.json sctipt下面的dev就是开发环境下运行服务的命令
9 运行服务与验证
在VSCode最上面一排的工具栏Terminal-->new terminal打开一个新终端,运行npm run dev 服务就起来了,你可以在浏览器熵输入地址 http://localhost:5173/ 就可以看到欢迎页面了。到此为止,整个开发环境就搞起来了。

10 配置模板快捷方式
1 点击配置按钮
左下角齿轮
图标->选择snippets

2 创建配置文件
初次的时候是没有配置文件的,创建的文件有全局模式和项目本地模式,建议配置文件为全局模式。选择全局模式后,再输入框输入vue,按下回车,就会创建一个名为vue.code-snippets配置文件

3 编写配置
这个配置是有 相关规范的,可以去网上查看使用说明,prefix前缀的作用就是在vue文件中输入vue前缀时会出现提示选择模板代码
{
"Vue3 template": {
// "scope": "javascript,typescript",
"prefix": "vue3",
"body": [
"<!-- note: -->",
"<!-- Author: 隔壁老王 -->",
"<!-- Created: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} -->",
"",
"<script setup>",
"$1",
"</script>",
"",
"<template>",
"$2",
"</template>",
"",
"<style scoped>",
"$3",
"</style>"
],
"description": "Vue3 template with script setup and scoped style"
}
}
4 使用
在vue文件中输入vue3 就会出现模板选项,效果如下图,比较方便

11 禁止更新设置
一般可以设置禁用更新,设置按钮-settings 然后输入update mode 设置成none

12 调整目录折叠设置
这个玩意也是折腾了一会才再到设置,vs code这玩意和idea界面和设置风格很像,找个配置项不太好找。eclipse 一般都是在preferrance下去设置或者右键项目目录做设置。新建立的VsCode默认有一个目录折叠设置,这个玩意当时也把我看麻木了。

看见上面那个箭头没有,实际上两个文件都是在同级目录,项目中那个settings.json有个开关是控制这个规则的

下面是折叠规则,如果把true变成false,就会变成正常的和目录对应的层次结构,这个折叠方式配置也不知道是那个天才想出来的,反正我还看不习惯,还是改成了false

13 vue文件命名规范
vue这玩意学起来不难,但是要整的想怎么搞好工程化真的是有点凌乱。vue的命名规范感觉有点恶心到了,越是只有求助百度,给出了如下答案:
|
Vue3 中 Vue 文件的命名规范解析 一、命名场景与核心逻辑 二、命名风格选择(大驼峰 vs 短横线) 大驼峰命名法(PascalCase): 模板中使用时,无需加 .vue 后缀,直接写 <MyComponent /> 即可。 模板中使用时,需加 .vue 后缀,写法为 <my-component.vue />。 组件文件名与组件名一致性: 局部组件:文件名需与 export default 中的 name 属性一致(如 MyComponent.vue 中 name: 'MyComponent')。 不同组件的文件名需唯一,防止加载时冲突。 四、命名规范的延伸价值 降低新人学习成本:统一命名让新成员快速理解组件结构。 |
你一看,这有啥问题,人家都不是告诉你my-component.vue或者MyComponent.vue不就行了了么!其实就是因为这个可以二选一,导致了vue文件的的组件会出现<my-component>或者<MyComponent>标签。你可能说你只要要求项目成员都统一成一种风格不就行了,我只能说你太年轻了。如果你选择了MyComponent组件命名方式,一旦你引入了的第三方组件使用my-component命名方式,就会导致你代码里面出现了两种风格(比如element-ui),就像下面这样

vue的脚手架代码是MyComponent,看来官方就是推荐用MyComponent模式,但是推荐总会有人不这样干或者以前根本就没有规范。不知道什么时候曾听人说过这个东西要是项目大了比较难维护。这里我还是建议使用MyComponent,用框架推荐的方式,如果你其他好的理由可以自行决策,这里还涉及另外一个问题,就是文件名不一定就是组件,请看后面章节说明。
14 文件名与组件名
在vue中vue文件的名字和组件的名字是一样的.比如有一个WelcomeItem.vue文件,那个在另外一个vue文件中就可以向下图一样使用WelcomeItem组件,

这个看起来还是很方便和直白的,但是问题有随之而来了,如果文件需要一个单词就够了,比如Home.vue或者home.vue。这里有两个问题使用大写还是小写,如果按上面的标准你,我们会选择大写,你以为问题解决了,但是实际上只有一个单词可能会有问题。如果你安装了eslint就会提示你vue文件必须用两个单词命名文件。它为啥会有这个要求呢,其实我在文章开头说了,这个vue组件开发模式不好识别标签是html原生的还是vue自定义组件,eslint子所以限制两个单词就是为了防止和html标签冲突(当然具体是不是这个原因没做考究,但是肯定会出现冲突情况),所以后面命名组件最好用两个单词,避免和html标签冲突。实际上我背不完html标签,所以遇到一些长得像的,你问我有没有冲突,我智能百度了。下面的脚手架文件加了View应该就是为了满足两个子要求。这应该不是为了表达意思,如果是,那么components下面的文件是不是都要加components

这个显得有点冗余了,但是没有办法,你不按规范来,其他团队成员学习,坏榜样很快会有其他团队成员模仿的。我也不确定html标签以后就不会用两个单词来做标签,这个有谁知道html绝对不会用两个单词做标签的可以告诉我一下。
Vue脚手架中App.vue这个文件是没有必要去改变名字的,因为这个文件不会用着组件,所以如果ESlint报错可以配置白名单,其他的没有特殊情况,都要以两个或者以上单词命名。
说了半天,转到正题。Vue的文件名可以和组件名不一样,比如MyComponent.vue文件,你可以在MyComponent.vue的代码中给起一个名字叫xx(文件中设置一个name=xx的选项),然后你就可以在其他文件使用这个组件<xx>,这里问价就来了,你知道xx是在那个文件中吗?这玩意就扯蛋。上面我提到可以配置ESlint白名单防止单文件名对应的组件名报错,如果你的组件名叫Home.vue,你在组件中设置了name=HomeView,这个组件就不会报错了。这破玩意看是灵活,实际上是让代码乱了套,不好跟踪问题。中玩意如果没有全盘很细致的把握,对于长期大型一点的项目来说是一个灾难。
在我们以前的经验看来,应该是vue文件可以包含html内容,引入js和css等资源,js可以操作dom,js可以引入其他js,但是js中不会引入html或者css文件。但是在vue中你可能会看到在js中导入vue组件,在js中导入css,vue文件在js中导入组件。看起来关系非常混乱。
这些新奇的技术和思路看起来都是那么不可思议。
15 ESLint与Prettier
ESLint是代码检查工具,如果不符合规范,文件就会报错。如果需要,创建项目时勾选ESLint和Prettier即可,然后导入项目,VS Code会提示安装推荐的插件,点击install安装即可。这个两个玩意新项目可以用,如果不安装了发现代码不规范的又不能改的就直接禁用。
ESLint默认时不开启自动修复,我建议不开启自动修复,报错就自己改,免得不小心修改到不改的,特别是老项目。格式化默认是保存时格式化,新项目可以开启,来项目最好是配置成手动格式格式化。
1 分号处理
格式化插件推荐使用无分号模式,实际上自动加分号还是都不要分号,其实不是很好,自动加减分号出都可能出问题,虽然统一是理想的,但一旦遇到问题就不好处理。看下面TypeScript代码:
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
console.log('first'+id);//这里不加分号TypeScript编译要报错
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
console.log('second'+id);
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
实际使用发现,perttier 配置semi的值只有true,false是合法的,没有其他选择,有点弱智。百度查了一下,说可以设置一个不合法的字符,然后就能起到不处理分号的作用:

确实是起作用了,但是发现是整个perttier都不能工作了,百度AI还真是个坑货。其实使用默认的无分号即可,如果遇到分号,像上面的代码分号会换到下一行开头。有分号方式就是保持不变。如果后面遇到特殊情况,可以设置白名单文件。或者直接禁用自动格式化,但是这样会有点麻烦。
2 eslint校验规则开关
eslint.config.ts
import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import pluginVue from 'eslint-plugin-vue'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript'
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
export default defineConfigWithVueTs(
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
skipFormatting,
{
rules: {
'@typescript-eslint/no-explicit-any': 'off', // 关闭 any 类型检查
},
},
)
3 诡异的插件bug
Version: 1.104.1 (user setup)

Vue3框架中出现代码中的 /* eslint-disable-next-line no-unused-vars */ 一保存就消失。经过验证就是插件有问题,禁用后就不会消失
var aa
/* eslint-disable-next-line no-unused-vars */
export default aa = 'kkk'
4 不可控的自动格式化
自动保存时自动格式化,大多数情况下,能省很多事情,但是一旦你遇到了格式化问题,相当棘手,比如格式化成下面这个样子

前面一个箭头错开我都可以忍了,第二个箭头错开就相当炸裂了,可以设置成手动格式化或者
配置.prettierrc.json printWidth让行宽宽一些,默认值太小了。
16 框架结构调整
一个框架在使用前,需要理解每个文件夹作用,如果不合理的,能调整的需要重新规划。我这里说一下我的个人看法和规划,抛砖引玉。下面是默认框架生成的目录结构:

1 public目录
据说这个文件夹是存放静态资源:如图片、Favicon、HTML文件等,这些文件不会被等构建工具处理。直接访问:文件可通过浏览器直接访问,无需经过Vue的构建流程。这个大家可以验证一下,这个就保持默认即可,这个目录应该很少用。
2 src源码目录
这个目录一看就是存放源码的目录,凭经验来说,开发的js,css,vue文件都应该放在该目录下。大目录没啥问题,这里也不要动,需要考虑的主要是以下图中的几个目录:

assets目录专门用于存放需要经过构建工具处理的静态资源文件:
1 存放项目中的图片资源(PNG、JPG、SVG等格式)
2 存储样式文件,如CSS、SASS、LESS等
3 放置字体文件,包括自定义或第三方字体
4 组织其他需要模块化处理的静态资源
这个目录根据实际情况建立文件夹如css,img、font、js等。以前一些开源组件比如日期控件datepicker等这种一坨的就可以直接放在这下面,这里css、js可以上升一层和vue评级。因为这两个属于源码级别,可能需要经常调整。
这里可以有个比较值得讨论的地方是这个router下面放了一个页面路由的js文件,如果这个js就只有一个文件,那么可以直接将index.js重命名成router.js。直接扔到js文件夹下,或者把router移动到js目录下。
store文件夹主要是pinia状态js,可能会有很多个,这个也可以考虑将stores移动到js目录下。
component下都是vue文件,view下面也是vue文件,我的理解是这两个文件夹是不是可以统一放在一个叫vue的文件加下。
以上就是我的目录规划。因为某些目录移动或者文件发生变化,可能会影响框架潜规则,原始的目录规划有些可能和默认规则有关联,这里需要自行验证。
整理目录划分的一个标准就是同一类型的文件,应该归类到同一个顶层文件夹,这样找东西会更容易些。顶层文件夹之下i,如果文件多的就按需建立文件夹,如果只会有一个文件的,可以不用文件夹。
实际上这个脚手架生成的的东西有点多了,很多文件需要删除和修改,这点有点烦。如果使用的组件多,比如引入TypeScript工程会更复杂,配置文件会更多。前后端工程师分离也是有原因的,为啥以前基本都是全栈,因为以前学习和维护前端成本很低。现在也开始卷起来了,要全栈工程师,还要会java、c\c++、python、人工智能、go 这是准备一个人干几个人的活的节奏。
3 参考目录结构
这是我最终确定的一个大的目录结构,仅供参考。

这个就先这样子了,是我想要的样子,我个人建议不要让目录嵌套太深,一般情况下也不要一个文件夹下就一个文件,遇到这种情况就不要用文件夹,直接用文件就行。尽量保持目录树简短。好了,就这样,后面可以按需到相应目录夹文件夹。
17 依赖管理
1 npm
依赖管理从npm->yarn->到pnpm。pnpm据说现在是最流行的。工程化项目,依赖管理是一个大头,想当年Java没有maven的时候是多么痛苦。依赖的重要性不言而喻。我相信没有人一个在愿意回到没有依赖管理工具的年代,下面是一个与npm的对比
|
安装速度:pnpm 通过硬链接和全局存储机制,安装速度比 npm 快 2-3 倍 迁移: 全局安装 pnpm:npm install -g pnpm |
执行pnpm install esbuild会被拦截,输入pnpm approve-builds会生成一个配置文件pnpm-workspace.yaml,内容如下:
| ignoredBuiltDependencies: - esbuild |
有了这个配置后,重新安装,就不需要在输入pnpm approve-builds命令了。
2 缺点
1 有可能会输错命令,比如应该输入pnpm的,惯性思维输入了npm。这个有点恶心。解决办法是在下面配置文件中加两项:

第一项不能阻止npm install,第二项虽然能阻止安装,但是还是会产生package-lock.json。会不会还有其他副作用,这个就需要大家去验证了。终极解决这个恶心问题的是删除node_modules和相关文件,重新pnpm install。或者还有有一个办法,是卸载npm,哈哈。
2 pnpm完全替代npm还是持怀疑态度,不知道会不会有些地方不支持出问题。当然大家都说这个好用,我们就认为它好用。毕竟能解决一些npm不能解决的依赖问题。好了就先记录到这里了。
18 关于TypeScript
TypeScript不是一门纯粹的语言,需要编译成js后再执行,下面的这些问题可能都和不纯粹有很大关系。vue2的性能和可维护性都不好,vue3使用vite构建据说时能提升两三倍,vue3开始的时候确实启动很快,毫秒级别。一集成了TypeScript,有时候启动七八秒。实际上我就写了一个demo页面,什么都没干。vue3语法比vue2上进步的就是更接近js语法,更少的vue套路。TypeScript搞上来,什么都白干
1 原生js面向对象
听说最近TypeScript比较流行,于是看看要不要把vue3基础框架搭建的时候就支持TypeScript。趁机复习了一下JavaScript 类型系统。其实我已经看过两遍了,就是记不住,但是像C++和java我都是能记住的,JavaScript早期类型系统是有点恶心,基于prototype。风格有点像GO。下面是一个示例示例代码:
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上添加方法
Person.prototype.greet = function() {
return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
};
// 创建Person的实例
const person1 = new Person('Alice', 30);
console.log(person1.greet()); // 输出: Hello, my name is Alice and I am 30 years old.
Person是构造函数,你可看它的greet方法是写在下面,这个有啥坏处呢?显然是这个东西搞得很麻烦,不直观,不好维护。有可能在Person 和greet定义之间写很多东西,我不能清楚地看到这个Person具有的属性和方法,写法也很怪,我也是不能理解天才的想法,读者可以看下面的新语法对比看看。今天学到了新知识ES6引入了新的关键字:
class Car {
constructor(brand) {
this.carname = brand;
}
present() {
return 'I have a ' + this.carname;
}
}
let myCar = new Car ("Ford");
myCar.present()
看看这个语法是将数据和方法合并到一个类Car下了。不是傻子都应该看出来第二个风格更好,当然比java还查差了一些。记住上面这个是ES6的语法,不是TypeScript的语法,TypeScript这样写你是会报错,意不意外!
为啥说这像go呢,go语言我可以这么说,就是一个具有垃圾回收器的C,GO语言的类是结构体和this指针的结合,和C逻辑一样。写法上是结构体和方法是分开的,有个限制是要求在一个包下,我也是醉了。go语言和js差不多也能操作函数,还可以返回多个值。类型声明就像现在的TypeScript,一个方法上类型参数占用一大片空间,感兴趣的可以去看看。在我的印象里面向对象的语言就C++和Java搞得像个人样。很多语言都吹简单灵活,其实简单的东西通常都不太灵活,灵活的东西学习成本和维护代价一般都高。我记得我了解的语言就只有Lua是真的简单。
2 TypeScript面向对象
下面走到正题,上一个TypeScript代码,官网:
//因此,我们应该直接操作类的静态部分。 看下面的例子,我们定义了两个接口, ClockConstructor为构造
//函数所用和ClockInterface为实例方法所用。 为了方便我们定义一个构造函数 createClock,它用传入的
//类型创建实例。
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
//
//因为createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)里,会
//检查AnalogClock是否符合构造函数签名
//////----------------------
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control {
select() { }
}
// 错误:“Image”类型缺少“state”属性。 让人费解
class Image implements SelectableControl {
select() { }
}
//错误 让人费解
class Location {
}
//在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state。 因为 state是
//私有成员,所以只能够是Control的子类们才能实现SelectableControl接口。 因为只有 Control的子
//类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。
//在Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。 实际上,
//SelectableControl接口和拥有select方法的Control类是一样的。 Button和TextBox类是
//SelectableControl的子类(因为它们都继承自Control并有select方法),但Image和Location类并不是
//这样的
我记得我学java和c++面向对象,接口、继承很轻松。这玩意把我正不会了,绕来绕去的,要是项目都是这种代码以后还整么看。
这里当时有个比较绕人的地方是Control有一个私有属性,Image实现了SelectableControl,SelectableControl extends Control。首先是类可以实现接口,类可以继承类。接口可以继承类,接口可以继承接口,就问你晕不晕。接口继承类完全是一个颠覆认知的设计。不是我对新东西反感,相互之间要避免循环依赖。看到这里我就会想了如果出现了下面代码会怎么样。一个设计思想如果不容易推断结果,需要写代码分析,那么这个设计就是糟糕的。class Image和class Location的报错让你很莫名其妙,不信你去问问百度AI,你们可以相互搞疯。如果你最后没有搞明白,联系我告诉你答案。
//假如有个class a {}
//假如有个 接口interface b {},下面会出现什么状况?
class a implements b {}
interface b extends a {}
//以下Car继承类实现还是一个名字,这个有点倒反天罡了
//为啥这样说呢,理论上类应该遵守接口,应该是现有接口再有实现
class Greeter {
greeting
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
interface Car extends Greeter{
}
3 TypeScript是JavaScript超集?
我虽然不是大牛,但是我还是知道很多东西的,看过很多,也忘记过很多,现在有点时间,除了自己研究,也记录下来,可以为后来者提供一些参考,发一份自己的光和热。
如果你是一个老司机,一看这个TypeScript是JavaScript超集,就满心欢喜,信以为真,那你估计迟早就要被坑一把。因为两个不同的语言体系,肯定有自己的语法体系,虽然TypeScript最终翻译成JavaScript执行,但是不意味着你在ts里面写的JavaScript都是正确的,所以说TypeScript是JavaScript超集,这个说法是不正确的,除非它完全兼容JavaScript语法。你可以看看18.1说的类定义,看起来像,实际上不一样。所以说如果你是一个js项目,想改成ts,你简单的以为把文件名改成ts,那么我说你想多了(这让我想到了当年scala鼓吹和java可以无缝集成,你可以调用我,我可以调用你,一个工程里面一部分是scala,一部分是java代码,真是不敢想象)。所以,如果你像要用TypeScript,你得全盘按TypeScript思路和套路、语法来,不要夹杂两种风格,不然容易踩坑,把项目搞得不伦不类的。至于JavaScript语法在TypeScript上执行结果不一样的那些情况,这种就只能在网上找了,鼓吹的人说你们多踩踩就好了,我只是说了一句。就像公交车上的座位是脏的,司机说坐坐就干净了一样。
typescript这后面肯定有推手的。发明一门语言如果要火起来,都有一些推动手段。
一是 语言本身高效简洁、易学易懂,自身优秀。
二是 我就用这个语言开发一些牛逼的开源软件(这个是惯用伎俩)或者是应用生态。比如python 数据分析、AI。python这个东西在使用广了之后自己的问题就暴露出来,工程化、高并发、弱类型。现在python已支持类型限制了。不过它比TypeScript要平滑,高版本的python是直接支持类型限制,在类型限制的地方去掉类型限制就和原来的一样,这个从理论上讲就是完全兼容以前语法。说白了python主要用在学习研究领域,因为大家都在用,所以生态可库就很丰富,其他语言就不占优势。scala当年也有一个伴生的spark,这玩意让scala火了一把。Go嘛,实际上就是习惯C的人在那里搞,docker云原生,不是一个好的面向对象语言。如果TypeSctipt能像python那样兼容就好了,但是这玩意要在浏览器上运行,就没法那样搞了。下面是我对超集不认同的一个地方说明代码,当然还有其他的需要读者自己发现和上网查了。
//标准JavaScript代码,能被浏览器正常解析执行,但是段代码不能被TypeScript编译通过
//Code-1
class Greeter {
constructor(message) {
this.greeting = message;
}
greet() {
console.log("Hello, " + this.greeting);
}
}
var g = new Greeter('dd');
g.greet();
//修改后代码
//Code-2
class Greeter {
greeting//这里必须新增这个字段才能被TypeScript编译通过
constructor(message) {
this.greeting = message;
}
greet() {
console.log("Hello, " + this.greeting);
}
}
var g = new Greeter('dd')
g.greet()
//下面这段代码是Code-2编码编码后的结果,实际上和Code-1并不一致
//Code-3
var Greeter = /** @class */ (function () {
//greeting
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
console.log("Hello, " + this.greeting);
};
return Greeter;
}());
var g = new Greeter('dd');
g.greet();
分析:
1 TypeScript语法并不能完全兼容javascript语法
2 从Code-2通过TypeScript编译后生成的代码与Code-1一致,可以看出javascript代码
如果直接复制成TypeScript,编译后的代码可能会和以前逻辑不一致,容易造成bug
结论:
如果再浏览器能正常运行的javascript代码,如果typescript不能正常编译通过,或者编译后
代码与编译前内容不一样(这个的容易一致是代码一样,不能运行结果一致),我就有确定的理由
说TypeScript不是javascript超集。你不能因为TypeScript能编译成javascript代码运行或者说
TypeScript语法和javascript长得像,就说TypeScript是javascript超集。记住超集一定包含子集,
在子集能正确运行的,都应该能源封不动的在超集中运行。
4 选择
js如果是做框架级别的工具可能会面向对象应用,如果是做前端业务可能会几乎不涉及,如果涉及,我还是选择那些看起来容易理解的是方式处理。可以搞个新工程,谨慎使用或者少用那些看起来奇葩的东西。取其精华,丢其糟粕,使用一些简简单的方式。
另外一点是如果你引用的库可能都是ts的,那么最好就用ts了,因为如果大家都被带进去了,你的一些依赖组件都被搞成ts版本了,你也能只能用了,不然你就找不到组件,或者需要修改代码,也不知道还有那些没有修改到位,TypeScript这招比较狠。老项目没有余力就不要随便升级。
Element-Plus 饿了么vue3的组件是ts,非ts项目做一下修改,去掉类型限制可能也能用,为了避免麻烦使用这个组件的项目最好用ts结构。安装命令pnpm install element-plus 组件地址。
按需自动导入需要安装插件 pnpm install -D unplugin-vue-components unplugin-auto-import
下面是工程截图,如果选择了测试支持,配置文件相当多。这玩意是越搞越复杂,比后端配置还要多,以前可不是这样的,几乎没有配置文件。这和现在卷文化非常像,一边喊提高效率,一边是加班时间越来越长、做了很多无用功。

5 文件配置继承
新版的TypeScript继承了tsconfig.node.json一个配置,需要安装pnpm install @tsconfig/node24
{
"extends": "@tsconfig/node24/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}
6 vite.config.ts
该配置文件中能使用.env环境变量,但是类型识别有问题

import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
server: {
host: env.VITE_FT_SERVICE_DOMAIN,
// 已经声明了数字,这里还是要报错,识别成了string
port: Number(env.VITE_FT_SERVICE_PORT),
// proxy: {
// '/manage.center.api': {
// target: 'http://localhost:80', // 后端服务器的地址
// changeOrigin: true, // 是否启用跨域
// rewrite: (path) => path.replace(/^\/manage.center.api/, ''), // 重写路径,去掉/api前缀
// },
// },
},
}
})
7 Element plus提示组件
使用自动按需导入后,不要在代码中再导入,否则组件不能正常工作(所以需要去掉官网的导入语句),d.ts中声明即可,要确保d.ts文件被检查到,这里我就直接放在env.d.ts中
//env.d.ts
declare global {
const ElMessage: MessageApi
const ElNotification: NotificationApi
const ElMessageBox: MessageBoxApi
}

8 项目实战案例
下面是使用Elements-Plus结合日志采集管理后端(可去我的日志采集管理中心博客参看界面效果,那是使用springboot+thymeleaf+jquery做的,也可以作为一个更简单的选择)开发的一个样板项目,工程结构如下图

左边是有很多配置文件,玩意看起来却是有些头大,不过大部分情况下不需要关注,有时间的的话再仔细研究。
下面是依赖配置package.json
{
"name": "manage-center-vue",
"version": "0.0.0",
"private": true,
"type": "module",
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint": "eslint . --fix --cache",
"format": "prettier --write --experimental-cli src/"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@types/js-cookie": "^3.0.6",
"axios": "^1.13.2",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.19",
"element-plus": "^2.12.0",
"i": "^0.3.7",
"js-cookie": "^3.0.5",
"pinia": "^3.0.4",
"vue": "^3.5.25",
"vue-router": "^4.6.3"
},
"devDependencies": {
"@tsconfig/node24": "^24.0.3",
"@types/crypto-js": "^4.2.2",
"@types/node": "^24.10.1",
"@vitejs/plugin-vue": "^6.0.2",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/tsconfig": "^0.8.1",
"dotenv": "^17.2.3",
"eslint": "^9.39.1",
"eslint-plugin-vue": "~10.5.1",
"jiti": "^2.6.1",
"npm-run-all2": "^8.0.4",
"prettier": "3.6.2",
"typescript": "~5.9.0",
"unplugin-auto-import": "^20.3.0",
"unplugin-vue-components": "^30.0.0",
"vite": "^7.2.4",
"vite-plugin-vue-devtools": "^8.0.5",
"vue-tsc": "^3.1.5"
}
}
下面是界面效果,分页组件做了一定的调整,原始的不太好看。ElmentPlus偏浅色系列,普通后端应用基本够了。


19 测试框架
一般不用,用习惯的可以加。如果是要用,最好提前规划目录,验证默认框架模板。
| 工具名称 | 主要定位 | 核心用途 |
|---|---|---|
| Cypress | 端到端测试 (E2E) 和集成测试 | 模拟真实用户操作,从浏览器视角测试整个应用流程,如登录、表单提交、页面导航等。12 |
| Playwright | 端到端测试 (E2E) 和集成测试 | 同样用于模拟真实用户行为,但由微软开发,强调多浏览器支持和高性能。23 |
| Vitest | 单元测试和组件测试 | 针对代码的最小单元(如函数、组件)进行快速、隔离的验证,通常用于开发阶段的即时反馈。23 |
20 开发调试
1 命令模式
VSCode调试有一种模式是在头部输入框输入>debug:OpenLink 启动调试,我没有采用这种方式。

2 配置模式
点击标红的地方

点击标红的地方,然后会在.vscode目录下产自动产生一个配置文件launch.json,确保和实际服务启动端口一致。这里有个不好的地方是launch.json不能使用.env环境变量

启动服务,然后点击下面的图标开启调试模式,会自动打开浏览器

记得先运行pnpm dev启动服务,不会自动启动
3 配置快捷键
这个应该是需要配置快捷键的,不然你调试时不能使用左手,默认调试快捷键时F10旁边的一些按键。Ctrl+K Ctrl+S 打开快捷键设置。

| Step over | f1 |
| Step into | f2 |
| Step over | f3 |
主要是这三个,其他的没有变
4 环境配置
全局的时.evn文件,开发.env.development。命名请参考vite官网,这里有个问题时当值是_开头取不出来,这个感觉有点坑。
.env
#16字节 128位置
VITE_AES_KEY=12345678egrf5iuf
#16字节 128位置
VITE_AES_IV=12345678frghjupf
#后台登录cookie
VITE_AES_BG_CK_KEY=wyplgck
#前端判断cookie
VITE_AES_FT_CK_KEY=ftlj
#过期设置 -1 关闭浏览器消后删除
VITE_LOGIN_CK_MAX_AGE=-1
#前端服务协议
VITE_FT_SERVICE_PROTOCOL=http
#前端服务域名
VITE_FT_SERVICE_DOMAIN=vuetest.wyp.test
#前端端口
VITE_FT_SERVICE_PORT=5173
#管理中心后端服务协议
VITE_BG_SERVICE_PROTOCOL=http
#管理中心后端服务域名
VITE_BG_SERVICE_DOMAIN=mc.wyp.test
#管理中心后端端口
VITE_BG_SERVICE_PORT=80
#path
VITE_BG_SERVICE_PATH=/wt/mc/login/loginPage
.env.development
NODE_ENV=development
21 跨域请求
1 后端跨域配置
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // 是否允许发送Cookie config.setAllowedOrigins(Arrays.asList("http://vuetest.wyp.test:5173")); // 允许的源列表,这里使用*表示接受所有域的请求,生产环境应指定具体的域名
config.setAllowedHeaders(Collections.singletonList("*")); // 允许的头部列表,这里使用*表示接受所有头部信息,生产环境应指定具体的头部信息
config.setAllowedMethods(Collections.singletonList("*")); // 允许的方法列表,这里使用*表示接受所有方法,生产环境应指定具体的HTTP方法
2 前端设置
const mcBgAxiosJson = axios.create({
baseURL: bgBaseUrl, // 替换为实际 API 地址
withCredentials: true, // 关键配置:允许携带 Cookie
timeout: 30000, // 可选:设置请求超时时间
})
前后端都做了配置后,只是axios能发送请求到后端请求数据,但是如果不同域名,或者在同一顶级域名下,还是不能顺利的携带cookie
3 分析验证
如果后台除了提供接口,还提供登录认证,如果基于cookie,那么最好是在同一个域名下,不然携带cookie会成为严重问题。google新版允许将Samesite设置成None,但是secure必须为ture,但是火狐不支持。httpOnly最好为true,增加安全性,httpOnly最好是后台设置返回前端。不管如何,如果不在同一域名下,最好不要依赖浏览器cookie的特殊属性来满足要求,这个东西规则容易被修改。
基于session或者js缓存不能跨窗口,基于localStorege过期和浏览器关闭不好出处理。
在顶级域名相同的情况下,如果后台使用https协议(域名:mc.w.test 8443端口),前端使用http协议(域名:vuetest.w.test mc.w.test,5173端口),cookie域名设置.w.test,经过验证firefox 携带cookie,chrome、和edge不行。
如果后端是http协议,前端也是http协议,firefox、chrome、edge都可以传输顶级域名的cookie。端口并不检查。
比较容错的方案是考虑使用不支持cookie的场景。但是检查客户端是否支持cookie,以及支持情况是一个比较难的问题。如果要考虑兼容性强,就最好不要使用cookie认证方式。
cookie应该不会检查端口,不然除了在同域名下,cookie基本无用。
总结一下:不在同一域名下,最好不要使用cookie。
4 关于js-cookie库
1 Cookies.set和Cookies.remove是无法读写httpOnly属性的cookie的.所以只能依赖后端处理
2 Cookies.set和Cookies.remove不会报错,要自己通过浏览器查看是否操作成功
22 技术栈预览
22.1 PC
Vue3+Vite+TypeScript+ElementsPlus+VueRouter+Axios+Pinia
标绿部分是UI组件库,可以根据需要选择,其他基本都是最佳搭配
22.2 手机
Vue3+Vite+TypeScript+Vant 4+VueRouter+Axios+Pinia
标绿部分是UI组件库,可以根据需要选择,其他基本都是最佳搭配
22.3 对比
| 组件库 | 核心优势 | 适用框架 | 推荐指数 |
|---|---|---|---|
| Vant 4 | 行业标准、文档最全、轻量级 | Vue 3 (H5/小程序) | ⭐⭐⭐⭐⭐ |
| NutUI | 电商组件多、京东出品 | Vue 3 (Taro/uni-app) | ⭐⭐⭐⭐ |
| uView Pro | uni-app 生态最强、功能大而全 | uni-app (Vue 3) | ⭐⭐⭐⭐⭐ |
| Varlet | 颜值高、Material Design 风格 | Vue 3 (H5) | ⭐⭐⭐ |
22.4 uni-app简介
它让你只需编写一套代码,就可以将应用发布到 iOS、Android、Web(H5) 以及各种小程序平台(如微信、支付宝、抖音等)。它由 DCloud(数字天堂)公司推出,是目前国内跨端开发领域非常主流的选择。
为了让你更全面地了解它,我为你整理了以下核心要点:
核心能力:一套代码,多端运行
uni-app 的核心价值在于“高效率”。你不需要为了 iOS 学 Swift,为了 Android 学 Kotlin,也不需要为了微信小程序单独写一套代码。
- 支持平台: iOS、Android、鸿蒙 Next、Web(H5)、以及微信、支付宝、百度、抖音、飞书、QQ、快手、钉钉、淘宝、京东、小红书等小程序平台。
- 开发体验: 它的语法基于 Vue.js,如果你熟悉 Vue,几乎可以零成本上手。同时,它结合了微信小程序的 API 风格(如
uni.request),让前端开发者倍感亲切。
🛠️ 工作原理
uni-app 通过编译器和运行时两部分配合工作:
- 编译器: 运行在你的电脑上(通常内置于 HBuilderX 编辑器中)。它将你编写的
.vue文件编译成各个平台能识别的代码。例如,编译成微信小程序的wxml/wxss,或者编译成 App 端的 JavaScript 和原生代码。 - 运行时: 运行在用户的手机或浏览器上。在 App 端,它内置了一个小程序引擎,让你的代码像在浏览器或小程序里一样运行,同时支持调用原生能力。
核心特点对比
| 特点 | 说明 |
|---|---|
| 跨平台能力强 | 真正实现了“一次开发,到处运行”,覆盖了目前几乎所有的主流前端平台。 |
| 性能接近原生 | 在 App 端支持 原生渲染(nvue),可以解决 WebView 渲染的性能瓶颈,适合做复杂的动画或长列表。 |
| 生态丰富 | 拥有庞大的插件市场,提供了数千款插件(如登录、支付、地图等),可以快速集成第三方功能。 |
| 开发工具 | 官方推荐使用 HBuilderX,这是一款专为 uni-app 优化的 IDE,内置了打包、运行、调试等全套功能。 |
优点与局限
优点:
- 降本增效: 极大地降低了多端开发的成本和人力投入。
- 上手简单: 基于 Vue 语法,学习曲线平缓。
- 灵活性: 支持条件编译,你可以在同一份代码中,针对特定平台(如只在微信小程序中)写专属代码,而不影响其他平台。
局限与注意事项:
- 平台差异: 虽然框架抹平了大部分差异,但不同平台(特别是 iOS 和 Android)在底层机制上仍有不同(如键盘弹起、导航栏高度、返回键逻辑),有时仍需手动处理兼容性问题。
- 包体积: 相比纯原生开发,App 的初始包体积会稍大一些(Hello World 模板约 6-8MB)。
- 复杂场景: 在极度复杂的图形处理或重度 3D 动画场景下,性能可能不如纯原生方案。
23 .vscode目录
这里补充说明一下目录.vscode。.vscode目录是 Visual Studio Code 用来存放项目特定配置的地方。.vscode目录默认是没有的。一种方式是通过IDE执行某些操作生成配置文件时自动产生该目录,另一种是手动创建。为了不出错,最好不要手动创建。这个目录下的文集应该提交是否需要提交到git

23.1 settings.json
这是最重要的文件。它用于覆盖编辑器的全局默认设置,仅对当前项目生效。
常见用途:
- 默认格式化程序:强制项目使用 Prettier 而不是 VS Code 自带的格式化器。
- 保存时自动格式化:确保每个人保存文件时都会自动运行格式化。
- Tab 大小:统一缩进是 2 个空格还是 4 个空格。
- 隐藏文件:在资源管理器中隐藏特定文件(如
.DS_Store)。
例子
{
"explorer.fileNesting.enabled": false,
"explorer.fileNesting.patterns": {
"tsconfig.json": "tsconfig.*.json, env.d.ts",
"vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .oxlint*, oxlint*, .prettier*, prettier*, .editorconfig"
},
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
这里我介绍一下使用界面操作生成的方法(快捷键可以自己看)
1 点击齿轮图标
齿轮一般代表设置

2 选择settings


3 配置保存
有用户级别和工作空间级别的配置,通过这个界面改配置。最好不好直接编写settings.json文件。如果是复制粘贴也可以直接修改文件。

23.2 launch.json
这个看前面调试操作,可以产生这个文件
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}"
}
]
}
23.3 extension.json
它的主要作用是统一团队开发环境,确保所有成员在处理同一个项目时,安装相同的必备插件,或者禁用可能产生冲突的插件。当你把这个文件放入项目的 .vscode 目录下并提交到代码仓库后,任何拉取代码的团队成员打开项目时,VS Code 都会自动检测并弹出通知,提示他们安装推荐的插件。
{
"recommendations": [
"Vue.volar",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode"
]
}
这个应该是脚手架创建项目的时候弄进去的,或者是安装某些插件搞的,没做记录,可以自己验证一下。需要的可以手动创建。
总结
先学习html、css、js、vue、typescript基础,然后再学习工程框架。
更多推荐


所有评论(0)