news 2026/10/24 9:20:25

Vue3 HelloWorld工程模板:Vite脚手架与组件通信实战拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue3 HelloWorld工程模板:Vite脚手架与组件通信实战拆解

简介:这是一份面向前端初学者的Vue3入门示例资源,以经典HelloWorld形式演示Vue3相对Vue2的核心改进,帮助学习者快速认识Composition API、setup函数以及基于Proxy的响应式系统。压缩包内共7个文件,以2个js脚本和2个xml工程配置为主,辅以1个html入口文件、1个gitignore及1个iml工程标识,整体仅257KB,轻量精简可直接复用。目前已有784人下载学习,适合作为第一份Vue3实践素材。资源包含可直接在浏览器中运行测试的HTML页面与Vue3运行库,无需事先搭建复杂脚手架即可观察响应式效果;同时保留了项目级IDE配置,便于导入WebStorm等工具继续扩展。通过这个示例,读者可以掌握setup函数的基本写法、模板中的常用指令,以及组件化的组织方式,并为理解Teleport、Suspense等进阶特性打下基础。

1. Vue3 HelloWorld 工程模板:从脚手架到组件通信一次讲清

很多人第一次接触 Vue3 HelloWorld 项目时,最容易掉进两个坑:一个是还在用 Vue2 的 Options API 写法,另一个是跟着老教程配 Vue CLI,结果启动一次要等十几秒。实际上 Vue3 生态早就把重心移到了 Vite 和<script setup>语法糖上,一个 HelloWorld 跑通之后,组件通信、响应式数据、构建部署这些真实项目里天天用的东西,你基本都能无缝迁移过去。这篇文章要拆的这份 Vue3 HelloWorld 资源,就是一套基于 Vite 的完整最小工程,适合刚转 Vue3 的前端、要做课程设计的学生,以及想快速搭一个干净模板的开发者。下面直接从脚手架创建开始,把每一步的坑和参数都讲清楚。

2. 从零到跑通:Vite 脚手架与三个必调参数

2.1 为什么选 Vite 而不是 Vue CLI:启动速度和依赖预构建

在 Vue3 刚发布那阵,官方推荐的是 Vue CLI,底层走的是 Webpack。Webpack 在开发服务器启动时要把整个项目从头编译一遍,项目一超过几十个组件,冷启动就是五到十秒起步,改一行代码触发热更新也要等一两秒。Vite 的思路完全不同:开发模式下它不对整个项目做打包编译,而是利用浏览器原生的 ES Module 按需加载,启动时只做依赖预构建,所以一个 HelloWorld 工程基本是一秒内启动。

这里有个容易被忽略的概念叫依赖预构建。Vite 启动时会把 node_modules 里那些 CommonJS 或 UMD 格式的依赖,用 esbuild 预打包成 ESM 格式,并缓存在node_modules/.vite目录。这意味着你第一次跑npm install后启动项目,Vite 会做一次比较耗时的预构建,之后再启动就快很多。如果你改了某个依赖的版本,或者手动改了vite.config.js里的optimizeDeps.include,你会发现启动时会重新预构建,这是正常的,不是报错。

对于 Vue3 HelloWorld 这种规模的工程,Vite 和 Vue CLI 的最终产物差别不大,但开发体验差距巨大。我一般给新手推荐 Vite,就是因为热更新反馈快,能立刻看到自己改的代码在页面上变成什么样子,这对理解响应式原理有直观帮助。

2.2 创建 HelloWorld 工程:命令与关键参数逐条拆解

假设你已经装好了 Node.js 环境,版本最好在 16 以上,下一步就是创建工程。最常见的方式是直接使用 Vite 官方脚手架,命令如下:

# 用 Vite 官方脚手架创建 Vue3 工程 npm create vite@latest vue3-helloworld -- --template vue # 进入工程目录 cd vue3-helloworld # 安装依赖 npm install # 启动开发服务器 npm run dev

逐条拆解一下命令里的关键点。第一行的npm create vite@latest是 npm 创建脚手架的统一语法,vue3-helloworld是你的项目目录名,--template vue指定使用 Vue 模板,注意这里有两个--,第一个--是 npm 的参数分隔符,第二个是让后面的参数原样传给 create-vite。如果漏掉-- --template vue,脚手架会进入交互式问答模式,问你选择原生 JS 还是 Vue、要不要 TS 等,虽然也不麻烦,但脚本化创建时不够干脆。

第四行的npm install会生成 package-lock.json 文件,这一步在网络不稳定时容易卡住,常见做法是用npm install --registry=https://registry.npmmirror.com切换镜像源,或者换用pnpm install,但混用包管理器容易把 node_modules 结构搞乱,这点后面避坑章会展开。

启动npm run dev之后,终端会输出一个本地访问地址,通常是http://localhost:5173/。这里有一个新手常见误区:Vite 默认端口是 5173,不是 Vue CLI 时代的 8080,也不是老教程里常写的 3000。如果你的 5173 端口被占用,Vite 会自动往后找 5174、5175,终端打印的地址会跟着变,以终端输出为准。

2.3 工程目录速读:HelloWorld 里每个文件在干什么

脚手架创建完成后,项目里会生成一批文件,很多人一看到十几个文件就懵了。实际上核心文件就那几个,我用下面的表格把职责列清楚:

文件路径作用在 HelloWorld 里你该关注什么
index.htmlVite 的入口 HTML<script type="module" src="/src/main.js">是挂载点
src/main.js创建应用实例引入 App.vue,调用createApp().mount('#app')
src/App.vue根组件所有视图的顶层容器
src/components/HelloWorld.vue示例子组件展示 props、响应式数据、事件绑定
vite.config.jsVite 配置文件端口、代理、打包路径都在这里改
package.json工程元信息scripts 里的 dev/build/preview 是最常用三个命令

打开index.html你可能会奇怪,为什么 Vue 单页应用的入口居然不是src/main.js而是index.html?这正是 Vite 与传统 Webpack 工程的核心差异之一。Vite 在开发环境下直接把index.html当作页面入口,浏览器解析到<script type="module">标签时,才去请求对应模块,所以启动速度才快。

src/main.js是 Vue3 应用的诞生点,代码非常短:

// main.js 是 Vue3 应用的最小编排入口 import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')

这段代码的逻辑是:从 vue 包中导入createApp函数,传入根组件 App,最后挂载到index.html里 id 为 app 的 DOM 节点上。对比 Vue2 的new Vue({ render: h => h(App) }).$mount('#app'),Vue3 用函数式创建应用的方式,好处是多个应用实例互不干扰,而且方便后续按需注册插件,比如app.use(router)。注意,main.js里没有默认全局引入 Vue,因为 Vue3 的 SFC 转译会自动注入必要的运行时导入,这也是为什么你在组件里写模板却不需要import Vue from 'vue'的原因。

3. 拆开 HelloWorld:setup 语法糖、ref/reactive 与模板渲染链路

3.1 Options API 与 setup 语法糖:同样的 HelloWorld 两种写法

Vue3 初期兼容 Vue2 的 Options API,也就是data、computed、methods、watch这些选项对象放在组件默认导出的对象里。但新项目里几乎看不到这种写法了,因为组合式 API 配合<script setup>语法糖,代码组织更紧凑。

用一个最经典的计数器对比两种写法:

<!-- 传统 Options API 写法:Vue2 时代延续下来的风格 --> <script> export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } } } </script> <template> <button @click="increment">点击了 {{ count }} 次</button> </template>
<!-- 现在推荐的 <script setup> 写法:Vue3 HelloWorld 的标准姿势 --> <script setup> import { ref } from 'vue' // ref 把基本类型包装成响应式对象 const count = ref(0) const increment = () => { count.value++ } </script> <template> <button @click="increment">点击了 {{ count }} 次</button> </template>

第二种写法里的逻辑区别值得细讲。<script setup>是单文件组件编译时的语法糖,它把顶层变量和函数直接暴露给模板,不需要return。这意味着你写在 setup 里的每一个ref、computed、普通函数,模板里都能直接用。ref(0)返回的是一个 Proxy 对象,访问它的值要用count.value,但在模板里 Vue 会自动解包,所以模板里直接写count而不是count.value。

很多新手在这块会翻车:在<script setup>里导入的组件,不需要写components: { HelloWorld }注册,直接<HelloWorld />就能用。因为编译阶段 Vite 会把导入的变量自动视为组件引用。顺手提一句,文件名大小写敏感,import HelloWorld from './components/HelloWorld.vue'里的路径如果写错大小写,本地 Windows 上可能能跑,但 Linux 服务器上直接报错。

3.2 计数器 Demo:ref.value 为什么模板里不用写点

继续用计数器深入理解渲染链路。Vue3 的响应式核心是 Proxy,ref的功能是把内部值包裹成带有 getter 和 setter 的对象。修改count.value时,dep 依赖收集器会通知所有依赖它的 effect 函数重新执行,模板编译出来的渲染函数自然也被重新执行,于是页面更新。

有一个细节特别容易让新手困惑:count.value++和++count.value看起来没啥区别,但如果把计数逻辑拆到普通函数里,比如写一个const logAndIncrement = () => { console.log(count.value); count.value++ },你会发现console.log打印的是自增前的值。这不是 bug,而是 JS 本身的执行顺序问题,跟 Vue 无关。排查这类问题时先回到原生 JS 语义,别急着怀疑框架。

对于对象和数组这类复杂结构,ref内部其实会自动调用reactive来包裹深层数据。所以ref({ name: 'Vue3' })之后,obj.name也是响应式的。但规范用法是对象用reactive、基本类型用ref,这样代码语义更清晰。

3.3 列表与条件渲染:HelloWorld 里最常用的流程控制

计数器只是起步,HelloWorld 工程里通常还包含列表渲染和条件渲染,这两个几乎每个页面都会用。下面这段是一个带状态切换的待办示例:

<script setup> import { ref, computed } from 'vue' const todos = ref([ { id: 1, text: '学习 Vite 脚手架', done: true }, { id: 2, text: '理解 ref 和 reactive', done: false }, { id: 3, text: '跑通第一个 HelloWorld', done: false } ]) // computed 会根据 done 状态自动衍生数据 const unfinished = computed(() => todos.value.filter(item => !item.done)) const toggleDone = (id) => { const target = todos.value.find(item => item.id === id) if (target) { target.done = !target.done } } </script> <template> <ul> <li v-for="item in unfinished" :key="item.id"> {{ item.text }} <button @click="toggleDone(item.id)">标为完成</button> </li> </ul> <p v-if="unfinished.length === 0">所有任务都完成了</p> <p v-else>还有 {{ unfinished.length }} 个任务未完成</p> </template>

这里有三个重点参数。第一个是v-for的:key,必须绑定稳定且唯一的标识,用item.id而不是数组下标。如果拿下标做 key,当列表头部插入一项时,Vue 会认为每个位置的元素没变,只是内容变了,导致复用错误的 DOM,出现复选框状态串位之类的诡异问题。第二个是v-if与v-else必须紧挨着,中间不能插入其他元素,否则 Vue 无法正确匹配分支。第三个是computed的缓存特性:它只在todos.value变化时重新计算,而不是每次渲染都重新 filter 一遍,这也是它比普通函数调用高效的原因。

3.4 样式绑定与事件处理:模板里的两个顺手技巧

HelloWorld 工程里还经常演示动态样式和事件对象,这两个技巧在真实项目中几乎天天要用。

先看动态样式绑定:

<script setup> import { ref } from 'vue' const isActive = ref(true) const activeColor = ref('#42b983') const toggleActive = () => { isActive.value = !isActive.value } </script> <template> <div class="demo-box" :class="{ active: isActive }" :style="{ color: isActive ? activeColor : '#999' }" @click="toggleActive($event)" > 点击切换状态,当前颜色 {{ isActive ? activeColor : '灰色' }} </div> </template>

:class绑定对象时,对象键决定是否添加这个 class,值决定条件,这里isActive为 true 时 div 会同时拥有demo-box和active两个类。:style绑定的对象键对应 CSS 属性名,注意驼峰命名,background-color要写成backgroundColor。@click事件处理方法里手动传入$event,是为了拿到原生事件对象,比如event.clientX做弹窗定位。如果是纯标记性点击,不传$event也行,Vue 默认会传入事件对象作为第一个参数。

事件处理还有一个容易被忽略的细节:内联语句里直接调用toggleActive()不传参时,事件对象不会自动传进去,收到的是 undefined。所以需要在模板里手动传$event,示例里就是这么处理的。

4. 组件通信:props、emit 与 defineExpose 的配合

4.1 props 单向数据流:父组件给子组件传值的标准写法

HelloWorld 工程一般会带一个子组件,用来演示父组件向子组件传值。Vue3 中定义 props 的推荐写法是直接用defineProps宏:

<!-- ChildComp.vue --> <script setup> // 用 defineProps 声明组件接收哪些属性 const props = defineProps({ title: { type: String, default: '默认标题' }, count: { type: Number, required: true } }) // props 是只读的,不能直接修改 console.log(props.title) </script> <template> <div class="child-box"> <h3>{{ title }}</h3> <p>收到的计数是:{{ count }}</p> </div> </template>

defineProps是编译宏,不需要从 vue 里 import,直接在<script setup>里调用就行。传 props 的父组件写法:

<!-- App.vue --> <script setup> import { ref } from 'vue' import ChildComp from './components/ChildComp.vue' const appCount = ref(3) </script> <template> <ChildComp title="父组件传来的标题" :count="appCount" /> </template>

这里有个单向数据流的概念要反复强调:子组件不能直接修改props.count,如果写了props.count = 5,Vue 会在控制台告警。正确做法是子组件把 props 的值拷贝到自己的本地 ref,或者通过 emit 事件通知父组件去修改。实际上 Vue3 的底层并没有真正冻结 props 对象,但直接修改会破坏单向数据流,导致数据来源混乱,这是设计约定不是技术限制。

4.2 emit 自定义事件:子组件把操作结果交回父组件

子组件要改变父组件的数据,标准姿势是触发自定义事件。还是用计数器场景:

<!-- CounterChild.vue --> <script setup> const props = defineProps({ start: { type: Number, default: 0 } }) // 声明组件能触发哪些自定义事件 const emit = defineEmits(['update:count']) const localCount = props.start const plusOne = () => { // 第一个参数是事件名,第二个是要传给父组件的值 emit('update:count', localCount + 1) } </script> <template> <button @click="plusOne">子组件按钮:当前 {{ localCount }}</button> </template>

父组件监听方式:

<!-- App.vue --> <script setup> import { ref } from 'vue' import CounterChild from './components/CounterChild.vue' const total = ref(10) const handleUpdate = (newCount) => { total.value = newCount } </script> <template> <p>父组件维护的数值:{{ total }}</p> <CounterChild :start="total" @update:count="handleUpdate" /> </template>

defineEmits的作用不只是声明,还有一个容易被忽略的好处:如果子组件发了一个未声明的事件名,比如emit('foo')而defineEmits里没有列出foo,在开发模式下 Vue 会给出警告。这在多人协作时能快速发现事件拼写错误。另外注意update:count这种带冒号的事件名是 Vue 提供的一种约定,配合v-model:count="total"可以实现类似v-model的双向绑定效果,本质上是:count="total"加@update:count="total = $event"的语法糖。

4.3 defineExpose 与 ref:拿到子组件内部方法的另一种路径

props 和 emit 覆盖了大多数场景,但有一种情况它们不好使:父组件想在某个时机主动调用子组件内部的方法,比如点一个按钮让子组件的弹窗打开。这时候可以用ref获取子组件实例,并通过defineExpose控制暴露哪些内容给父组件。

子组件侧代码:

<!-- ModalChild.vue --> <script setup> import { ref } from 'vue' const visible = ref(false) const open = () => { visible.value = true } const close = () => { visible.value = false } // vue3 中 setup 里的变量默认不暴露给父组件,需要显式声明 defineExpose({ open, close }) </script> <template> <div v-if="visible" class="modal-mask"> <div class="modal-content"> <p>子组件的弹窗内容</p> <button @click="close">关闭</button> </div> </div> </template>

父组件侧获取实例:

<!-- App.vue --> <script setup> import { ref } from 'vue' import ModalChild from './components/ModalChild.vue' // 模板里 ref="modalRef" 的名字需要和这个变量一致 const modalRef = ref(null) const callOpen = () => { if (modalRef.value) { modalRef.value.open() } } </script> <template> <button @click="callOpen">打开子组件弹窗</button> <ModalChild ref="modalRef" /> </template>

关键点在于ref="modalRef"和const modalRef = ref(null)的名字必须保持一致,否则拿不到组件实例。也不是每个组件实例的所有内部属性都能访问的,因为<script setup>默认是关闭的,父组件只能拿到defineExpose显式暴露的成员。如果你发现modalRef.value是 null,最常见的原因是子组件还没挂载完成,或者你在onMounted之前就去访问了。

5. 避坑指南:从 HelloWorld 到真实项目最常见的七个坑

5.1 端口被占用后刷新失效

现象:启动npm run dev后终端提示 5173 被占用,自动换到 5174,但浏览器访问 5174 一直白屏,控制台报Failed to fetch dynamically imported module。

原因:Vite 重启后换了端口,但内存里的模块映射还引用旧端口,或者浏览器缓存了旧的模块请求。

解决:先彻底停掉终端里跑着的 Vite 进程,然后清一遍浏览器缓存再重新npm run dev。更稳的做法是在vite.config.js里固定端口:

// vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, strictPort: true } })

strictPort设为 true 后,端口被占用时会直接报错而不是自动切换到下一个端口,避免端口漂移带来的各种缓存不一致问题。

5.2 安装了依赖但 import 就报错

现象:npm install正常完成,但npm run dev后页面报Cannot find module 'vue'或'@vitejs/plugin-vue' is not defined。

原因:大概率是 package.json 里依赖版本不对,或者 npm 用了旧缓存安装了一半的依赖。

解决:第一步rm -rf node_modules package-lock.json,注意 Windows 下用rmdir /s node_modules。第二步重新安装,并确认 package.json 里 Vue 版本是^3.x而不是^2.7,因为 Vue2 的包名也叫 vue,装上后代码里createApp不存在直接崩。我一般会用npm view vue version先确认最新的 3.x 主版本号再写进依赖。

5.3 热更新失效:改代码页面毫无反应

现象:改了模板或样式,浏览器不自动刷新,必须手动刷新才能看到变化。

原因:Vite 的文件监听机制在某些环境下失效,比如 IDE 自动保存时写入的是软链接路径、WSL 跨文件系统访问、或者项目目录里有符号链接。

解决:如果项目在 WSL 或 Docker 挂载目录里运行,把项目挪到本地文件系统再试。如果实在挪不了,改 Vite 配置里的监听选项:

server: { watch: { usePolling: true, interval: 100 } }

usePolling会强制 Vite 用轮询方式检测文件变化,代价是 CPU 占用上升,只作为开发阶段的临时方案。

5.4 生产构建后打开是白屏

现象:npm run build成功,npm run preview本地预览正常,但把dist目录部署到服务器子目录后,访问页面白屏,控制台报资源 404。

原因:Vite 默认base是/,即产物里的资源引用路径都是从域名根目录开始。部署在https://example.com/myapp/这种二级目录下时,资源请求会跑到https://example.com/assets/...去。

解决:在vite.config.js里把base改为相对路径:

export default defineConfig({ base: './' })

base: './'会让产物里的路径变成相对路径,这样部署在任何目录下都能正常加载。注意改了base之后,preview本地预览也要用相对路径环境,否则带入旧配置。

5.5 依赖预构建缓存不更新

现象:改了 npm 某依赖的版本,重新npm install后启动 Vite,还是加载旧版本的依赖,页面表现与依赖版本不符。

原因:Vite 的依赖预构建缓存存在node_modules/.vite,它基于 package-lock.json 内容和依赖版本生成 hash,但某些场景下 hash 计算不准,比如用npm install升级了 patch 版本后锁文件缺失。

解决:强制清除缓存再启动:

# 删除预构建缓存 rm -rf node_modules/.vite # 重启开发服务器 npm run dev

某些特殊依赖如在运行时动态 import 的模块,还需要在optimizeDeps.include里显式声明,Vite 才会在预构建时把它们打包进去。

5.6 混用包管理器导致依赖版本不一致

现象:同事用pnpm install后项目能跑,你用npm install后报错,或者反过来。尤其在公司协作项目中,这种问题很致命。

原因:pnpm和npm的依赖解析策略不同,pnpm基于内容寻址的链接结构,npm是平铺的 node_modules,两者生成的 lock 文件互不兼容。混用后 node_modules 里会残留大量不属于当前包管理器策略的重复依赖。

解决:项目里统一只用一个包管理器。如果当前项目已经混用了,彻底清场重装:

# 清掉所有依赖和锁文件,windows 请用对应命令 rm -rf node_modules package-lock.json pnpm-lock.yaml yarn.lock # 选定一个包管理器重装,推荐 pnpm 或 npm 二选一 npm install

从那以后我每次接手新项目,第一件事就是看仓库根目录有没有pnpm-lock.yaml或yarn.lock,确认锁文件到底属于哪个包管理器,再决定自己用哪个命令。

5.7 子组件不渲染也没有报错

现象:页面没有报错,模板也写了<HelloWorld />,但子组件内容不显示。

原因:最常见的是子组件导入路径写错了后缀,./components/HelloWorld.vue写成了./components/HelloWorld,Vite 默认允许省略.vue后缀,但省略后它按 ESM 去解析时可能匹配不到文件;另一种可能是子组件根元素只有一个注释节点,Vue 渲染结果为空。

解决:统一带.vue后缀导入,并确认子组件模板根节点是真实 DOM 元素。如果用了<Fragment>或多个根节点,确认没有把<template>的注释当渲染结果。检查技巧是临时在子组件里加一行静态文本,如果还是不显示,再往导入路径上排查。

6. 进阶技巧:从 HelloWorld 到多页面与生产构建的三步提升

HelloWorld 跑通之后,最自然的进阶路径是往里面加路由,让页面从单组件变成多视图。

# 装 vue-router,注意当前安装的是 vue-router 4.x npm install vue-router@4

然后在src下建router/index.js:

// src/router/index.js import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' const routes = [ { path: '/', name: 'home', component: HomeView }, { path: '/about', name: 'about', component: () => import('../views/AboutView.vue') } ] const router = createRouter({ history: createWebHistory(), routes }) export default router

createWebHistory()基于 HTML5 History API,URL 里没有#,好看但部署到服务器时需要配置前端路由回退到index.html,否则刷新页面会 404。不想配服务器回退就改createWebHashHistory(),url 变成/#/about,兼容性更好,代价是略丑。

然后在main.js里挂载路由:

// main.js import { createApp } from 'vue' import App from './App.vue' import router from './router/index.js' createApp(App).use(router).mount('#app')

第二步是区分环境变量。HelloWorld 阶段往往写死接口地址,真实项目里要把开发环境和生产环境的 api 地址拆开。在项目根目录建.env.development和.env.production:

# .env.development 开发环境变量 VITE_API_BASE=/api # .env.production 生产环境变量 VITE_API_BASE=https://api.example.com

组件里通过import.meta.env.VITE_API_BASE读取,注意变量必须以VITE_开头,Vite 才会把它暴露给客户端代码。改完环境变量要重启开发服务器才生效,这是环境变量加载时机决定的,不是 Vite 的 bug。

第三步是生产构建的验证流程。npm run build之后,除了看构建结果,我固定会做三个自查动作:第一,npm run preview本地起一个生产环境预览服务器,因为开发环境表现和构建产物不完全一致;第二,打开浏览器开发者工具的网络面板,确认静态资源路径是相对路径还是绝对路径,对应判断base配置是否有问题;第三,如果页面有路由,把preview服务的 URL 手动改成/about刷新一次,验证服务器是否配置了路由回退,如果 404 就说明部署环境还缺一条 try_files 规则。

关于环境变量有个常见误区:.env.production在本地npm run build时生效,但部署到服务器后又发现接口地址不对,这种情况往往是构建时没把生产环境变量打包进去,或者服务器上的部署目录和base配置不匹配。处理办法是回本地重新npm run build,用vite preview验证后再上传,别在服务器上直接改 dist 文件。

还有个小技巧值得提醒:构建产物里的assets文件夹如果文件名带了很长的 hash,比如index.8f3d2c.js,这是 Vite 基于内容生成的指纹,作用是强制浏览器加载新版本而不是命中旧缓存。如果你发现线上改了代码但用户还是看到旧页面,优先检查服务器是否设置了index.html的no-cache响应头,而不是怀疑构建有问题。

这套流程跑通之后,Vue3 的工程化底座就算立住了。从那以后我每次新建 Vue3 工程,都强制走一遍创建脚手架、确认端口、跑通组件通信、构建预览的完整流程,哪怕只是写个测试页也绝不跳过构建这一步。踩过太多次开发环境好好的、生产环境白屏的坑,这习惯帮我提前拦掉了至少一半的部署事故。希望这篇 Vue3 HelloWorld 的拆解能帮你在起步阶段少绕几个弯,把基础工程搭得干净一点,后面加路由、加状态管理、接后端接口的时候,你会感谢自己没在起步阶段引入一堆看不懂的配置。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 9:19:38

OpenMontage:视频素材自动拼接成片的流水线实践

做内容这几年&#xff0c;我剪得最多的不是那种需要精修的片子&#xff0c;反而是把一堆零散素材拼成一条完整成片的活。活动花絮、会议快剪、口播合集&#xff0c;几乎每周都会来几批&#xff0c;时间还卡得死。以前全靠人力在时间轴上一段一段拖&#xff0c;直到我上手 OpenM…

作者头像 李华
网站建设 2026/10/24 9:20:20

Azure DevOps Pipelines 实战:从 YAML 配置到自托管 Agent 避坑指南

简介&#xff1a;这份Azure DevOps Pipelines中文操作手册聚焦持续集成与持续交付&#xff0c;面向DevOps工程师、开发人员和运维人员。内容先介绍Azure Pipelines是什么、经典发布管道、快速入门及创建或导入管道&#xff0c;再围绕代理、条件、表达式、环境、作业、运行时参数…

作者头像 李华
网站建设 2026/10/18 12:44:59

Apollo-11软着陆仿真:从零手写动力学与制导控制

把一个项目叫做 Apollo-11&#xff0c;并不代表我要去复刻某段特定历史事件。这串编号在我这里代表一种工程气质&#xff1a;大量子系统必须紧密协作&#xff0c;推进、测量、制导、显示一环扣一环&#xff0c;任何一处失配都可能导致整体失控。我需要一个能够反复验证着陆过程…

作者头像 李华
网站建设 2026/10/23 9:24:31

嗖嗖移动大厅:可运行的移动端工程切片与卡片调度实践

简介&#xff1a;本资源是一份面向Java初学者与Web开发入门者的轻量级移动应用大厅教学案例&#xff0c;聚焦MVC架构实践与代码可读性培养。项目提供完整可运行的HTML静态页面&#xff08;含内嵌JavaScript交互逻辑&#xff09;&#xff0c;配合详尽的中文注释&#xff0c;清晰…

作者头像 李华
网站建设 2026/10/23 15:12:16

PP-OCRv5 Ubuntu 20.04 服务化部署实战指南

简介&#xff1a;本资源是面向Linux开发者与AI部署工程师的PP-OCRv5 Ubuntu 20.04 OCR识别服务完整部署包&#xff0c;解决图像文字批量识别、服务化封装及端侧/服务端模型推理等实际落地问题&#xff0c;适用于文档数字化、票据识别、自动化数据录入等工业级OCR场景。压缩包共…

作者头像 李华
网站建设 2026/10/22 10:29:00

Rust迭代器原理与实战:从基础到高级应用

1. 迭代器特质&#xff08;Iterator Trait&#xff09;的本质解析在Rust的标准库中&#xff0c;Iterator trait是处理集合类数据的核心抽象。它通过将集合元素的访问操作标准化&#xff0c;实现了算法与数据结构的解耦。这种设计模式在C中被称为"迭代器模式"&#xf…

作者头像 李华