资讯动态

Vue文件模板到底放哪?四类模板存放规则与工程实践解析

发布时间:2026/9/26 2:26:12 来源:尧图企业网站定制
刚入坑 Vue 的前端同事经常问我同一个问题“项目里的文件模板到底该放哪”起初我以为他问的是.vue文件里的template标签怎么写后来发现他翻遍了整个项目目录是想找一个叫template的文件夹。这种困惑其实非常典型——在 Vue 的世界里“文件模板”是个多义词组件模板、入口 HTML 模板、脚手架预设模板、编辑器里的代码片段模板四者的存放位置和命名规则完全不同。如果你也正在纠结“模板放哪”这件事先把这四个概念分开理解再对应到你的实际场景就基本不会迷路了。这篇文章我会从项目目录、构建机制、脚手架预设、VS Code 片段、动态渲染这几个层面逐一拆解既讲清“放哪里”也讲清“为什么必须这样放”最后附带一些我在真实项目里踩过的坑和处理经验。1. 先拆词Vue语境里的“文件模板”至少有四副面孔同一个“模板”说法放在不同的上下文里含义天差地别。我先把最常见的四类场景列出来后文再逐个展开。模板类型典型位置由谁使用核心作用单文件组件模板src/components/、src/views/下的.vue文件Vue 编译器vue-loader / Vite 插件描述组件 UI 结构入口 HTML 模板public/index.htmlVue CLI、项目根目录index.htmlVitehtml-webpack-plugin / Vite 构建提供页面外壳、挂载点、meta 信息脚手架项目模板本机~/.vuerc、团队 Git 模板仓库vue-cli / create-vue初始化新项目的目录与依赖编辑器代码片段VS Code 的 snippets 配置目录编辑器本身提升编码效率快速生成代码结构一张表看完你应该发现了它们之间几乎没有交集。很多人问“模板放哪里”其实是在问第一类也就是.vue文件里的template区块放在哪一层目录、怎么组织组件文件。少数人在问第二类即index.html应该改哪里。问第三类和第四类的人往往是看了某个教程里提到的“项目模板”或“代码模板”才产生的疑问。1.1 先分清概念再动手找路径我见过一个刚转前端的小伙把一段后台返回的 HTML 字符串拆到了src/templates/目录下想用v-html渲染出来结果整个项目结构变得不伦不类。问题不在技术而在对“模板”的理解发生了偏移。Vue 的核心主张是组件化一个.vue文件就是一个自包含的单元它内部自带template、script、style。模板不是独立存在的文件而是组件的一个属性区域。你非要把它从组件里抽出去单独维护等于亲手拆散组件的内聚性。后续维护时你改模板要跳文件改逻辑跳文件改样式还要再跳一次想定位一个小改动要来回切三个地方效率直线下降。所以理解完概念之后结论其实很朴素99% 的场景下组件模板就放在组件文件里跟 script 和 style 在一起。后面几个问题都是围绕“组件文件放哪”和“例外情况放哪”展开的。1.2 不是所有“模板文件”都需要你手动建还有一层需要澄清Vue 项目运行时根本不需要你把模板文件“放”在任何地方让浏览器直接读取。构建工具会在编译阶段把.vue文件里的template编译成render函数最终产物是 JS 而不是 HTML 模板文件。所以你不需要建立一个templates静态目录更不需要在public里放.vue模板——那不会被正确解析。理解了这一点后面所有目录规划才有意义。2. 组件模板的停放准则views、components与就近原则框架本身不强制规定组件模板必须放哪个目录但社区经过多年实践沉淀出了一套被广泛接受的默认工程结构。遵守它团队协作时会省很多沟通成本。2.1 标准目录结构与“模板跟着组件走”一个典型的 Vue 项目基于 Vue CLI 或 Vite 创建核心结构如下my-project/ ├── public/ │ ├── index.html │ └── favicon.ico ├── src/ │ ├── assets/ │ ├── components/ │ ├── router/ │ ├── stores/ │ ├── views/ │ ├── App.vue │ └── main.js ├── .gitignore ├── package.json └── vite.config.js模板存放在哪取决于你写的组件是什么角色src/views/或src/pages/放页面级组件也就是路由直接渲染的顶层组件。每个文件对应一个页面文件名通常与路由名称保持一致。一个HomeView.vue负责一个路由入口的完整页面骨架它的template描述整页布局。src/components/放可复用组件比如按钮、弹窗、表格、卡片等。这类组件文件同样自带模板。如果组件内部又拆了子组件可以就近建一个子目录子组件的模板跟着父组件文件走形成独立小单元。src/App.vue根组件是所有组件的入口壳子它的模板一般只保留全局性的布局层内容例如router-view挂载点、公共 header/footer。这个结构背后是一个经典原则就近原则。组件模板应该离逻辑最近放在组件文件内部组件文件放在语义清晰的目录里。不要为了“找模板”而把模板单独抽到一个叫template的目录——除非你在做编辑器拖拽生成代码之类的高级二次开发那不在常规工程讨论范围内。2.2 容器组件与展示组件的拆分决定了模板的归属很多人问“模板放哪”时其实真正纠结的是一个表单页面到底把整块表单写成一个大组件还是拆成多个小组件模板要怎么跟着拆我一般建议按“容器组件 展示组件”的思路拆。容器组件负责数据请求和业务状态管理模板里渲染的是逻辑骨架展示组件负责 UI 呈现模板里写死结构通过 props 接收数据、通过 emit 抛事件。举例src/views/order/ ├── OrderList.vue // 容器组件路由入口拉数据、处理翻页 ├── OrderListItem.vue // 展示组件单行订单渲染 ├── OrderFilterBar.vue // 展示组件筛选条件栏OrderList.vue的模板里写的是OrderFilterBar :filtersfilters changeonFilterChange /这类组合关系OrderFilterBar.vue的模板里才是具体的input、select结构。每个组件的模板都紧跟自身文件互不越界。判断准则如果你发现一个组件文件里的 template 超过 300 行大概率它承担了太多职责需要拆。拆出来的每个模板都放到对应角色的组件文件里而不是丢进一个公共的templates目录。2.3 明确划分作用域组件模板不受全局样式“连坐”组件模板放在.vue文件内部还有一个隐藏福利可以用style scoped把样式作用域限制在当前模板内。构建工具会给模板元素加上带有 hash 的 data 属性实现样式隔离。如果你把模板 HTML 抽出去写成公共片段再通过某种方式拼进组件你会瞬间失去scoped 样式的自动属性匹配构建时的模板编译检查比如标签未闭合、表达式写错组件内模板 ref、指令绑定的类型推导。这些都是在真实项目中反复出现的返工原因。所以我的态度一直很明确常规业务页面里模板不存在“放哪”的争议它就该老老实实待在组件文件里。3. 入口HTML模板public/index.html为什么是特例如果说组件模板是 Vue 领域的事那入口 HTML 模板就是构建工具的事。它的位置在不同脚手架下有明显差异这也是最容易让人困惑的点。3.1 Vue CLI 与 Vite 对 index.html 的处理不一样Vue CLI 生成的项目里public/index.html是以模板身份存在的html-webpack-plugin会读取它往里注入编译后的 JS/CSS 标签再输出到dist目录。此时public/下的其他静态资源会被原样拷贝不出现在构建产物中。Vite 的默认结构与 Vue CLI 不同index.html直接放在项目根目录它是 Vite 的入口文件。项目启动或构建时Vite 解析这个 HTML找到script src/src/main.js作为应用入口再编译后续的.vue文件。Vite 项目也有public/目录但它只存放静态资源不承担 HTML 模板角色。脚手架入口 HTML 模板位置底层机制Vue CLIwebpackpublic/index.htmlhtml-webpack-plugin 注入打包资源Vite根目录index.htmlVite 将 HTML 作为模块图起点如果你在 Vite 项目里把index.html塞进public启动后大概率出现白屏因为 Vite 根本找不到入口文件。这个坑我见过不止一个人踩进去。3.2 修改 title、meta 的正确姿势确定了入口模板位置后“怎么改页面标题”就成了下一个高频问题。最直接的方式是改index.html里的title标签。但 SPA 项目往往希望不同路由显示不同标题这时应该用vue-router的路由 meta 配合afterEach钩子动态设置router.afterEach((to) { document.title to.meta?.title ? ${to.meta.title} - 管理系统 : 默认标题 })不要为了多个标题去复制多份 HTML 模板那是多页应用才需要考虑的事。SPA 的本质是单页外壳页面内容交给 Vue 组件模板去渲染HTML 模板只提供最外层骨架。3.3 多页面应用与多个 HTML 模板的摆放如果确实要做多页应用比如后台管理系统要同时输出管理端和用户端两个入口Vue CLI 可以在vue.config.js中通过pages字段配置多模板module.exports { pages: { admin: { entry: src/admin/main.js, template: public/admin.html, filename: admin.html }, client: { entry: src/client/main.js, template: public/client.html, filename: client.html } } }此时每个入口对应一个独立的 HTML 模板文件都放在public/下通过构建配置关联。Vite 侧则通过在根目录放多个 HTML 文件并在入口脚本里用script标签关联实现。这类场景比较少见但如果遇到了记住一条HTML 入口模板和业务组件模板是两条线前者放在公共外壳层后者放在组件模块内部。4. 脚手架模板与团队preset新项目的初始文件从哪来“文件模板”还有一层意思是指创建新项目时用到的那套初始文件模板。很多团队每次建项目都重复配置 ESLint、Prettier、路由、状态管理其实这些都可以固化成模板。4.1 vue create 的 preset 机制用 Vue CLI 创建项目时如果你手动选过一遍特性CLI 会提示是否保存为预设。保存后配置会写入用户目录下的~/.vuerc文件{ useConfigFiles: true, plugins: { vue/cli-plugin-babel: {}, vue/cli-plugin-router: { historyMode: true }, vue/cli-plugin-vuex: {} }, vueVersion: 3 }以后执行vue create my-project选择对应预设就会按照这套配置生成项目模板。这个文件的位置是Windows 在C:\Users\用户名\.vuercmacOS/Linux 在~/.vuerc。如果你用的是新版官方脚手架create-vue交互式生成的是 Vite Vue 3 模板它本身没有 preset 文件但支持通过命令行参数直接指定特性组合比如npm create vuelatest my-project -- --ts --router --pinia这类模板化的思路是把“初始化配置”从人的记忆里挪到配置文件里。新建项目时直接复用脚本比每次手动点十几次选项要稳定得多。4.2 团队模板仓库与初始化脚本preset解决的只是 CLI 层面的选项预设但很多团队还想统一目录结构、工具链版本、基础组件库封装。更实际的做法是维护一个模板 Git 仓库。我的习惯是在 Git 平台建一个vue-project-template仓库存放基础项目代码开发新项目时克隆模板仓库删除.git目录重新初始化用一个初始化脚本统一处理克隆、安装依赖、修改项目名、替换README、提交首次 commit。脚本核心逻辑大致是git clone gitgithub.com:your-team/vue-project-template.git new-project cd new-project rm -rf .git npm install node scripts/rename.js --name new-project git init git add . git commit -m init project这样团队每个项目的“模板”都是同源的模板仓库放在哪、由谁维护也一目了然。比散落在个人电脑里的~/.vuerc更适合团队协作。4.3 模板初始化时的两个高频坑第一模板仓库里的依赖版本会快速过期。我建议模板仓库使用模糊版本号并在模板中配置统一的依赖升级脚本比如ncu而不是锁死 patch 版本。不然三个月后新建的项目从第一天起就带着一堆安全漏洞。第二模板仓库里的.gitignore容易被忽略。node_modules、dist、.env.local这些必须提前写好否则第一次提交就会把构建产物和依赖目录推上远程仓库后续想清理会很头疼。5. 编辑器片段模板VS Code里的Vue速写存放在哪“模板文件”的第四副面孔是编辑器里的代码片段snippet。这玩意儿和项目无关但能极大提升你写 Vue 组件时的效率特别是在重度重复的初始化结构上。5.1 用户代码片段的存放路径VS Code 的用户代码片段有两个级别全局用户片段对所有项目生效配置文件存放在用户设置的snippets目录里。Windows 路径是%APPDATA%\Code\User\snippets\macOS 是~/Library/Application Support/Code/User/snippets/Linux 是~/.config/Code/User/snippets/。项目级片段只在当前项目生效放在项目根目录的.vscode/snippets/下。打开方式很简单命令面板搜索“配置用户代码片段”选择vue或vue-html就会打开对应的 JSON 文件。如果你希望团队共享同一套 Vue 片段就放在项目的.vscode/snippets/目录里一起提交到 Git 仓库。5.2 一份可以直接抄的Vue文件片段配置这里给出一份我实际在用的 Vue 3 单文件模板片段{ Vue3 SFC Base: { scope: vue, prefix: vue3sfc, body: [ template, div class\$1\$0/div, /template, , script setup, import { ref, computed } from vue, , const $1 ref(), /script, , style scoped, /style ], description: Vue3 组合式单文件组件基础模板 } }在.vue文件里输入vue3sfc按 Tab 就会展开。$1是第一个光标位置$0是最后停靠位置。把类名和状态名绑定为同一个占位符可以减少重复输入。类似的片段还可以配置vif展开v-if、vfor展开v-for、importref展开import { ref } from vue。这些片段文件存放的位置就是上面说的两个目录属于纯编辑器行为不进项目构建链路。5.3 Volar、Vetur 与自动生成模板Vue 官方推荐的 Volar 插件即 Vue Language Features内置了一些代码能力但它不负责生成整套 SFC 骨架。如果你装了Vue Volar extension pack新建.vue文件后它可能自动给出基础结构这属于扩展自带的 file template 机制位置在扩展安装目录里一般不需要手动维护。有一点需要提醒如果你的团队混用了 Vetur 和 VolarVue 2 时代的 Vetur 在 Vue 3 项目里可能会提示一堆格式问题。模板相关能力建议统一走 Volar别在同一个工作区里同时启用两者不然代码片段、语法高亮、跳转都会互相干扰。6. 越界场景动态模板、render函数与运行时编译有些需求会让你产生“把模板动态拼出来”的冲动——比如后台返回一段 HTML 片段比如根据 JSON 配置动态渲染表单比如做一个低代码页面。这时“模板放哪”会变成一个真正有深度的工程问题。6.1 字符串模板何时才会浮出水面Vue 的 SFC 模板是在构建期被编译成 render 函数的。也就是说运行时你手里拿到的组件里根本不存在待编译的模板字符串。如果你在业务代码里写const template div{{ msg }}/div然后把这段字符串丢给某个组件去渲染默认情况下运行时构建 未引入编译器是跑不起来的控制台会报“You are using the runtime-only build of Vue where the template compiler is not available”。此时你只有三条路改用带编译器的构建版 Vue把vue别名指到vue/dist/vue.esm-bundler.js性能差、包体大一般只在特殊调试场景用用render函数或 JSX 直接描述动态结构不依赖模板编译使用defineComponent配合() import()异步加载真正的.vue文件让模板一直停留在 SFC 文件内。我的建议是优先走 2 和 3。低代码类需求可以用动态组件component :is...配合映射表去加载不同组件文件模板仍然放在各自的.vue文件里只是由运行时去做切换。6.2 render函数与JSX的取舍当你要做高度动态的渲染比如根据配置生成表单字段列表模板会变得极其臃肿。此时把“模板”换写成 render 函数反而更清晰import { h } from vue const FieldRenderer { props: { field: { type: Object, required: true } }, setup(props) { return () { switch (props.field.type) { case input: return h(input, { value: props.field.value }) case select: return h(select, props.field.options.map(opt h(option, opt.label))) default: return h(span, props.field.value) } } } }这样写模板不是消失了而是变成了 h 函数组成的结构树。它的“存放位置”就在组件内部跟 SFC 模板属于同一个地方——它仍是组件描述的一部分。团队里如果已经普及 JSX把FieldRenderer写成.tsx文件放到src/components/下也行只是要注意统一转译配置。6.3 安全边界模板不是拼接HTML最后必须提醒一句不要把“模板”和“HTML 字符串拼接”混为一谈。有人看到需求是动态渲染富文本就顺手把后台返回的 HTML 字符串用v-html直接塞进模板div v-htmlcontentFromServer/div这是极其危险的。v-html是明文插入 HTML不是 Vue 模板编译。外部输入的 HTML 一旦包含恶意脚本XSS 风险直接拉满。如果你的业务必须渲染富文本可以使用经过消毒的富文本渲染方案比如先通过 DOMPurify 清洗再交给内容区组件渲染不要把后端返回的原始 HTML 当成“模板”直接用。动态渲染表单也好、渲染后端配置也好核心边界都一致结构性内容走组件化模板可信静态 HTML 才考虑直接注入不可信内容一律先清洗再处理。我个人在实际项目里维护过一套基于配置的表单引擎最初也试图把“模板”抽到一个公共目录做了两个迭代后发现越抽越难维护。后来彻底转向动态组件 render 函数模板还是老老实实待在各自组件里只是由配置去驱动发起哪个组件、传什么参数。项目结构清爽了很多新人上手也不需要再理解“模板层”这种自定义概念。如果你现在也在规划 Vue 项目的目录先问自己一句你所说的“文件模板”是组件模板、入口 HTML、脚手架模板还是编辑器片段确认好这个再去对照目录结构几乎不会走弯路。项目里真的没有一个叫template的文件夹等着你创建——好的模板都活在它们该在的组件里。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑