一、知识回顾-Vue2项目基础操作与环境配置
1.1 项目启动
- 项目打开方式:直接将项目文件夹(如
my-app
)拖拽到 Visual Studio Code(推荐编辑器),避免拖拽父级文件夹,防止路径混乱。 - 启动命令:
- 进入项目根目录(必须与
package.json
同级),打开终端执行启动命令。 - 默认命令:
npm run serve
(命令定义在package.json
的scripts
字段中)。 - 命令变体:部分项目可能使用
npm run dev
或npm run start
,需以实际项目package.json
中的配置为准(例:若scripts: {"dev": "vue-cli-service serve"}
,则执行npm run dev
)。
- 进入项目根目录(必须与
- 服务管理:
- 关闭服务:终端中按
Ctrl+C
(Mac 为Command+C
),输入y
确认。 - 重启服务:需先关闭现有服务,再重新执行启动命令,不可直接开新终端启动(避免端口占用)。
- 终端切换:VS Code 可通过右侧标签页切换多个终端实例。
- 关闭服务:终端中按
1.2 淘宝镜像配置
- 作用:解决从国外 npm 仓库下载依赖速度慢的问题(国内服务器定期同步国外仓库代码,形成镜像副本)。
- 切换逻辑:
- 自动切换:当系统检测到下载速度过慢时,会提示是否切换至淘宝镜像。
- 适用场景:仅在下载速度慢时配置,正常网络环境可无需设置(镜像内容与原仓库完全一致,不影响功能)。
- 主动配置步骤:
- 打开终端,执行配置命令:
npm config set registry https://registry.npm.taobao.org
。 - 验证配置:执行
npm config get registry
,若返回淘宝镜像地址则配置成功。 - 注意事项:配置后下载地址会永久变更,如需恢复默认仓库,执行
npm config set registry https://registry.npmjs.org
。
- 打开终端,执行配置命令:
1.3 项目访问与常见问题
- 访问方式:启动成功后,终端会输出本地地址(如
http://localhost:8080/
),在浏览器中输入该地址即可访问项目。 - 禁止直接访问 HTML:项目运行在集成服务器环境,直接双击
public/index.html
无法正常加载(依赖 Webpack 打包的模块化资源)。 - 常见启动错误排查:
- 错误场景 1:在父目录或子目录(如
src
)执行启动命令 → 解决方案:切换到package.json
所在的根目录。 - 错误场景 2:命令不存在(如
npm run dev
报错)→ 解决方案:查看package.json
的scripts
字段,确认正确命令。 - 错误场景 3:端口占用 → 解决方案:终端按
Ctrl+C
关闭占用端口的服务,或重启终端。
- 错误场景 1:在父目录或子目录(如
二、项目文件结构与 SPA 概念
2.1 核心目录解析
目录/文件 | 作用与说明 | 注意事项 |
---|---|---|
node_modules |
存放通过npm install 下载的所有依赖包(如 Vue、Vue Router) |
通常无需修改内部代码,若需修改源码需谨慎(升级依赖可能覆盖修改) |
public |
静态文件目录,存放项目唯一的 HTML 文件(单页应用核心) | 包含index.html (挂载点)和favicon.ico (浏览器标签图标) |
src |
核心开发目录,所有业务代码在此编写 | 开发的核心区域,需熟练掌握子目录功能 |
src/assets |
存放静态资源(图片、字体文件等) | 建议按类型分类(如assets/images 、assets/fonts ) |
src/components |
存放公共复用组件(如导航栏、按钮、弹窗等) | 组件文件建议用 PascalCase 命名(如Navbar.vue ) |
src/store |
与 Vuex 相关的状态管理目录(用于多组件数据共享,后续章节讲解) | 初期可忽略,需使用 Vuex 时再配置 |
src/App.vue |
项目根组件,所有页面组件最终嵌套在该组件中 | 单文件组件的典型示例,包含template 、script 、style 三部分 |
src/main.js |
项目入口文件,建立所有 JS 文件的依赖关系链 | 所有自定义 JS 必须直接/间接被该文件引入,否则无法生效 |
2.2 SPA 与 MPA 对比(单页应用 vs 多页应用)
对比维度 | 单页应用(SPA) | 多页应用(MPA) |
---|---|---|
核心特点 | 只有 1 个 HTML 文件,页面切换不刷新整体 | 多个独立 HTML 文件,跳转时加载新页面 |
切换方式 | 通过前端路由(如 Vue Router)动态替换内容 | 通过超链接(<a> 标签)跳转新页面 |
用户体验 | 流畅(类似桌面/移动端 App) | 跳转时有加载延迟,体验较割裂 |
开发复杂度 | 需处理路由、状态管理,复杂度较高 | 无需路由,仅需处理页面间数据传递(如 URL 参数) |
SEO 优化 | 较差(内容动态渲染,搜索引擎难抓取) | 较好(每个页面独立 HTML,易被抓取) |
适用场景 | 后台管理系统、移动端 App、数据看板 | 新闻网站、电商平台(如京东)、博客 |
三、核心文件深度解析
3.1 入口文件 main.js
main.js
是项目的“入口大门”,所有 JS 依赖最终通过它关联,其核心作用是创建 Vue 实例并挂载到页面。
核心代码解析(Vue 2 示例)
// 1. 导入 Vue 核心包(从 node_modules 中查找)
import Vue from 'vue'
// 2. 导入根组件 App.vue(相对路径,需加 ./)
import App from './App.vue'
// 3. 导入路由实例(后续路由章节配置,可选)
// import router from './router'
// 4. 关闭生产环境的错误提示(上线时隐藏控制台报错,避免暴露代码问题)
Vue.config.productionTip = false
// 5. 创建 Vue 实例
new Vue({
// router, // 路由实例(可选,需配置路由时添加)
render: h => h(App) // 渲染根组件 App(h 是 createElement 的简写,用于创建虚拟 DOM)
}).$mount('#app') // 挂载到 index.html 中 id 为 app 的元素(等效于配置 el: '#app')
关键知识点
- 依赖链规则:若自定义 JS 文件(如
src/a.js
)未被main.js
直接/间接引入(例:main.js
引入c.js
,c.js
引入a.js
),则该文件功能不会生效(Webpack 打包时会忽略未引入的文件)。 render
函数作用:Vue 根组件需通过render
函数渲染子组件(非根组件可通过components
注册后直接使用),h(App)
表示将App
组件转换为虚拟 DOM 并渲染。- 挂载方式对比:
- 传统方式:
new Vue({ el: '#app' })
(直接指定挂载点)。 - 推荐方式:
$mount('#app')
(更灵活,可动态控制挂载时机)。
- 传统方式:
3.2 静态文件 public/index.html
该文件是项目唯一的 HTML 载体,所有 Vue 组件最终都会渲染到这个文件中。
核心代码解析
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<!-- 适配移动端 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 页面标题(由 Webpack 动态注入,配置在 package.json 中) -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 1. Vue 应用的挂载点:所有组件内容最终渲染到这里 -->
<div id="app"></div>
<!-- 2. 兼容性提示:浏览器禁用 JS 时显示 -->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- 3. JS 自动注入:Webpack 打包时会将编译后的 JS 文件(如 main.js)自动插入这里,无需手动加 <script> 标签 -->
</body>
</html>
关键知识点
- 自动注入机制:HTML 中未显式引入
main.js
,但 Webpack 会在打包时将所有依赖的 JS 合并并注入到<body>
末尾,确保 DOM 加载完成后执行 JS。 - 挂载点作用:
id="app"
的div
是 Vue 实例的“容器”,Vue 会将虚拟 DOM 渲染为真实 DOM 并替换该容器内的内容。
四、单文件组件(.vue)详解
单文件组件(SFC)是 Vue 的核心特性,将组件的模板(HTML)、逻辑(JS)、样式(CSS) 封装在一个.vue
文件中,结构清晰且便于维护。
4.1 基本结构与要求
一个完整的单文件组件必须包含template
、script
,style
可选(若无需样式可省略)。
标准结构示例(Demo.vue
)
<template>
<!-- 模板:必须有且只有一个根元素(通常用 div 包裹) -->
<div class="demo-container">
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</div>
</template>
<script>
// 逻辑:通过 export default 导出组件配置对象
export default {
// 组件名称(建议与文件名一致,PascalCase 命名,非必需但便于调试)
name: 'Demo',
// 数据定义:必须是函数(避免组件复用时数据污染),返回对象
data() {
return {
title: '单文件组件示例',
content: '这是组件的动态内容'
}
},
// 生命周期钩子(可选,如组件创建后执行)
created() {
console.log('Demo 组件已创建')
}
}
</script>
<style scoped>
/* 样式:scoped 表示样式仅作用于当前组件(避免全局污染) */
.demo-container {
width: 80%;
margin: 0 auto;
padding: 20px;
border: 1px solid #eee;
}
h1 {
color: #42b983; /* Vue 主题色 */
}
</style>
核心要求
template
规则:- 必须包含一个根元素(若有多个同级元素,需用
div
、template
等标签包裹),否则会触发 ESLint 错误。 - 支持 Vue 指令(如
v-for
、v-if
、v-bind
)和插值语法({{ 变量 }}
)。
- 必须包含一个根元素(若有多个同级元素,需用
script
规则:- 必须通过
export default
导出组件配置对象(否则无法被其他组件引入)。 data
必须是函数(返回对象),而非直接定义对象(原因:组件复用后,每个实例会拥有独立的数据副本,避免数据共享污染)。
- 必须通过
style
规则:- 若需样式隔离,添加
scoped
属性(原理:Webpack 会给组件内所有 DOM 元素添加唯一data-v-xxx
属性,样式选择器会自动匹配该属性,如.demo-container[data-v-123]
)。 - 支持预处理器(如 SCSS):需安装对应依赖(如
node-sass
、sass-loader
),并在style
标签中指定语言(例:<style lang="scss" scoped>
)。
- 若需样式隔离,添加
4.2 组件引入与注册
组件需先引入、再注册,最后才能在模板中使用,流程如下(以App.vue
引入Demo.vue
为例):
步骤 1:引入组件
在父组件(如App.vue
)的script
中,通过import
语法引入子组件:
// App.vue 的 script 部分
import Demo from './components/Demo.vue' // 相对路径:从当前目录下的 components 文件夹引入 Demo.vue
步骤 2:注册组件
在export default
的components
选项中注册引入的组件:
export default {
name: 'App',
components: {
Demo // 等价于 Demo: Demo(ES6 简写,键和值同名时可省略值)
}
}
步骤 3:使用组件
在父组件的template
中,使用组件标签(支持两种写法):
<template>
<div id="app">
<!-- 写法 1:kebab-case(推荐,符合 HTML 标签命名规范) -->
<demo></demo>
<!-- 写法 2:PascalCase(与组件名一致,Vue 支持但不推荐在 HTML 中使用) -->
<!-- <Demo></Demo> -->
</div>
</template>
命名规范
- 组件文件名:PascalCase(如
UserProfile.vue
),便于区分组件和普通文件。 - 组件标签:在模板中使用kebab-case(如
<user-profile>
),符合 HTML 标签的小写命名习惯。 - 避免使用保留字(如
div
、button
)作为组件名,防止与原生标签冲突。
4.3 样式作用域与深度选择器
1. scoped
的局限性
- 当组件中使用
scoped
后,样式仅作用于当前组件的 DOM,无法修改子组件的样式(子组件的 DOM 没有当前组件的data-v-xxx
属性)。 - 例:若
App.vue
中用了scoped
,无法直接修改Demo.vue
中.demo-container
的样式。
2. 深度选择器(解决子组件样式修改)
若需修改子组件样式,可使用深度选择器(不同预处理器语法不同):
- 原生 CSS:使用
>>>
(例:>>> .demo-container { color: red; }
)。 - SCSS/LESS:使用
/deep/
(例:/deep/ .demo-container { color: red; }
)。 - Vue 3 中:使用
:deep()
(例::deep(.demo-container) { color: red; }
)。
示例(App.vue
修改Demo.vue
样式)
<!-- App.vue 的 style 部分 -->
<style scoped>
/* 使用深度选择器修改 Demo 组件的 .demo-container 样式 */
>>> .demo-container {
border-color: red;
}
</style>