Skip to content

创建路由对应的组件与实现路由一(Lesson9 2/2) ​

本页对应 Lesson09,共 5 段视频,约 2 小时 21 分。

学习目标 ​

  1. 能说明跨域产生的原因,并在 Vue 开发服务器中配置代理,将指定前缀请求转发到目标服务,验证浏览器请求自身开发服务器且数据成功返回。
  2. 能正确使用 changeOrigin、pathRewrite 等代理选项处理来源与路径重写,并能排查代理不生效问题(如修改配置后重启服务、前缀匹配、路由与代理优先级)。
  3. 能解释 JavaScript 引用类型在数组/对象修改中的影响,复现通过 map/遍历修改对象属性对原数组的影响,并使用替换新数组或事件方式触发 Vue 更新,说明深度监听的性能代价。
  4. 能创建路由对应组件并配置 path、component、redirect、alias、通配 404 等路由规则,实现首页、新闻列表、商品列表、个人中心及重定向和别名访问。
  5. 能配置 hash/history 路由模式、base 基础地址,并使用命名视图与多路由出口实现单页面多路由渲染。
  6. 能将路由配置从 main.js 抽离到独立的 router/index.js 并导出使用,验证抽离后路由功能与抽离前一致。

知识点 ​

本节要做什么 ​

这一节的目标很明确:先不要急着写路由表,而是把路由要用到的页面组件先建出来。转写稿里先提出需求:访问 / 或 /home 显示首页,访问 /newslist 或 /news 显示新闻列表,访问 /goodslist 显示商品列表,访问 /my 显示个人中心,其他地址显示 404。需求拆完后,第一步就是“每个路由对应一个组件”。为什么需要先建组件?因为路由配置的本质是“地址匹配后使用哪个组件”,组件不存在,路由表就没法写。怎么想?先按页面维度拆成 5 个文件,统一放到 src/pages 下。怎么做?逐个创建并写最小模板。易错点:路由级页面组件不要和普通小组件混在一起;Vue 2 模板要保证单根节点;name 与文件名保持一致,调试时更清楚。

创建 Home 组件 ​

为什么需要:首页是默认入口,后续 / 要映射到它。怎么想:先写最小可识别内容,不堆业务。怎么做:在 src/pages/Home.vue 中写标题。易错点:<style scoped> 先留空可以,但不要漏掉 export default。

vue
<template>
    <div>
        <h3>Home</h3>
    </div>
</template>

<script>
export default {
    name: "Home"
}
</script>

<style scoped>

</style>

创建 NewsList 组件 ​

为什么需要:新闻列表是独立页面,后面可能对应 /newslist,转写稿还要求 /news 也能显示同一组件。怎么想:一个页面一个组件,地址别名后面在路由里配。怎么做:在 src/pages/NewsList.vue 写列表页占位。易错点:不要把“新闻列表”写成普通组件名,页面组件命名要能直接对应路由。

vue
<template>
    <div>
        <h3>新闻列表界面</h3>
    </div>
</template>

<script>
export default {
    name: "NewsList"
}
</script>

<style scoped>

</style>

创建 GoodsList 组件 ​

为什么需要:商品列表与新闻列表结构相似,但业务不同,必须拆成两个组件。怎么想:路由地址 /goodslist 对应 GoodsList。怎么做:在 src/pages/GoodsList.vue 写占位。易错点:文件名大小写要和 import 时一致,避免 Linux 环境下找不到文件。

vue
<template>
    <div>
        <h3>商品列表界面</h3>
    </div>
</template>

<script>
export default {
    name: "GoodsList"
}
</script>

<style scoped>

</style>

创建 My 组件 ​

为什么需要:个人中心是独立路由页面。怎么想:地址 /my 对应 My。怎么做:在 src/pages/My.vue 写占位。易错点:转写稿里曾出现 /my 被代理规则拦截,导致路由页面不出现;这不是组件写错,而是代理优先级问题,后面配置代理时要注意。

vue
<template>
    <div>
        <h3>我的个人中心界面</h3>
    </div>
</template>

<script>
export default {
    name: "My"
}
</script>

<style scoped>

</style>

创建 NotFound 组件 ​

为什么需要:地址写错时不能白屏,要给出 404 提示。怎么想:最后用通配地址 * 匹配它。怎么做:在 src/pages/NotFound.vue 写提示。易错点:404 路由通常放在路由表最后,否则可能提前匹配。

vue
<template>
    <div>
        <h3>404,您找的界面不存在!</h3>
    </div>
</template>

<script>
export default {
    name: "NotFound"
}
</script>

<style scoped>

</style>

组件如何接到路由上 ​

为什么需要:只有组件文件,页面不会自动出现,必须让地址和组件建立映射。怎么想:转写稿里的做法是引入 VueRouter、Vue.use(VueRouter),再写 routes 数组,每项包含 path 和 component。怎么做:先按转写稿配置 /、/home 重定向、/newslist 与别名 /news、/goodslist、/my、*。易错点:component 的值是组件对象,不是字符串;redirect 是重定向,alias 是别名,两者不要混。

js
const routes = [
  { path: '/', component: Home },
  { path: '/home', redirect: '/' },
  { path: '/newslist', component: NewsList, alias: '/news' },
  { path: '/goodslist', component: GoodsList },
  { path: '/my', component: My },
  { path: '*', component: NotFound }
]

router-view 与调试过程 ​

为什么需要:路由匹配到组件后,必须告诉 Vue“在哪里渲染”。怎么想:转写稿中在 App.vue 里放 <router-view>,它占的位置就是路由组件的出口。怎么做:main.js 挂载 App,App.vue 中写 <router-view></router-view>。易错点:转写稿里一开始挂载写成了 #root,控制台报错,检查后发现模板里是 #app,要改为 #app;路由不显示时,先查 router-view 有没有写、路由是否注册、控制台是否报错。

模式、重定向、别名与抽离 ​

为什么需要:默认 hash 模式地址带 #,转写稿演示了 mode: 'history' 可去掉 #;重定向用于 /home 回 /;别名让 /news 和 /newslist 共用一个组件;base 可统一加前缀。怎么做:在 new VueRouter({ routes, mode: 'history' }) 中配置。转写稿最后把路由配置抽到 src/router/index.js,导出 router,再在 main.js 引入,避免主文件越来越乱。易错点:改 vue.config.js 或代理后要重启服务;代理规则可能优先于路由,如 /my 被代理拦截时页面就出不来。

先看目标:把地址映射到组件 ​

为什么需要:单页应用只有一张 HTML,页面切换不能靠刷新,而要靠“地址变化 → 显示不同组件”。这就是前端路由的核心。

怎么想:先把需求拆成地址:/ 首页、/newsList 新闻、/goodsList 商品、/my 个人中心、找不到则 404。每个地址先对应一个页面组件。

怎么做:在 src/pages 下准备 Home、NewsList、GoodsList、My、NotFound,以及单页面多路由要用的 MyHeader、MyMain、YouFooter。转写稿里先创建组件、再在 main.js 引入,顺序不要反。

易错点:转写稿调试时出现过 #root 报错,根实例挂载点应是项目里的 #app;组件路径、文件名大小写要一致。

安装路由并挂载根实例 ​

为什么需要:VueRouter 是插件,必须先 Vue.use 安装,才能使用 router-view、$route 等能力。

怎么想:固定三步:引入 vue-router → Vue.use(VueRouter) → 在根实例传入 router 配置。routes 是数组,每个对象就是一条路由配置。

怎么做:完整 src/main.js 如下:

js
import Vue from "vue";
// 1- 引入模块vue-router
import VueRouter from "vue-router";
import App from "@/App";
import Home from "@/pages/Home";
import NewsList from "@/pages/NewsList";
import GoodsList from "@/pages/GoodsList";
import My from "@/pages/My";
import NotFound from "@/pages/NotFound";
import MyHeader from "@/pages/MyHeader";
import MyMain from "@/pages/MyMain";
import YouFooter from "@/pages/YouFooter";
// 2- 安装路由
Vue.use(VueRouter);
new Vue({
	render: h => h(App),
	// 3- 应用配置
	// 接收的是一个对象,对象中的routes数组,数组中的每个元素(对象)即是路由配置信息对象
	router: new VueRouter({
		// 配置路由信息
		routes: [
			{
				path: "/",// 指定路由地址
				component: Home// 当请求地址与路由地址匹配时,那么会使用组件Home
			}, {
				// 支持的请求地址除/newsList以及,还支持/news
				path: "/newsList",
				alias: "/news",// 为地址起别名
				component: NewsList
			}, {
				path: "/goodsList",
				// 可以设置为数组,起多个别名
				alias: ["/first", "/daoLang", "/wangFei"],
				component: GoodsList
			}, {
				path: "/my",
				// 单页面多路由(了解)
				components: {
					one:MyHeader,
					main:MyMain,
					footer:YouFooter,
					default:My
				}
			}, {
				path: "*",
				component: NotFound
			}, {
				// redirect:重定向--》当请求地址为/home,被重定向至地址/
				path: "/home",
				redirect: "/"
			}
		],
		// 指定路由模式:默认为hash
		// 1- history
		// 2- hash
		mode: "history",
		// 支持地址以m开头
		// base:"m"
	})
}).$mount("#app");

易错点:routes 不是 routers;component 是单组件,components 才是多视图;改完配置要重启开发服务。

路由表细节:别名、重定向、404 ​

为什么需要:一个页面可能希望有多个访问地址;旧地址要回到首页;错误地址要有兜底页面。

怎么想:alias 是“同一组件多地址”;redirect 是“访问 A 跳到 B”;path:"*" 是“其余都匹配”。

怎么做:如 /news 与 /newsList 同组件;/first、/daoLang、/wangFei 都进商品列表;/home 重定向到 /;* 对应 NotFound。

易错点:别名可写字符串或数组;通配路由通常放在最后;重定向写目标 path,不要误写成组件。

用 router-view 指定出口与命名视图 ​

为什么需要:配置了路由还不够,必须告诉 Vue“匹配到的组件显示在哪里”。

怎么想:普通路由用 <router-view> 占位;单页面多路由用 components 配多个名字,再在 router-view 上写 name。

怎么做:完整 src/App.vue 如下:

vue
<template>
    <div>
        <!--指定路由呈现的位置:为路由组件占位-->
        <!--  components->one->组件-->
        <router-view name="one"></router-view>
        <!--        components->main->组件-->
        <router-view name="main"></router-view>
        <!--        components->footer->组件-->
        <router-view name="footer"></router-view>
        <hr/>
<!--在单页面多路由中,router-view省略name,那么会使用components->default->组件-->
        <router-view></router-view>
        <router-view></router-view>
        <router-view></router-view>
        <router-view></router-view>
    </div>
</template>

<script>
export default {
    name: "App.vue"
}
</script>

<style scoped>

</style>

易错点:name="one" 必须和 components 里的键一致;省略 name 时使用 default;多写多个 router-view 会重复渲染。

路由模式与 base,注意和代理的冲突 ​

为什么需要:默认 hash 地址带 #;history 地址更自然。base 可给地址加统一前缀。

怎么想:mode:"history" 后地址不再带 #;base:"m" 表示地址支持以 m 开头。

怎么做:在 new VueRouter({...}) 中配置 mode,需要时再打开 base。转写稿调试时发现:开发代理若以 /m 开头,会抢在路由前匹配,导致 /my 个人中心异常;应避免代理前缀覆盖路由,或调整代理前缀后重启。

易错点:代理匹配优先于前端路由;mode、base 改动后要重启;先保证 /、/newsList、/goodsList、/my、404 都能单独访问,再叠加多视图配置。

先把本节目标说清楚 ​

这一节是在“实现路由一”的基础上,把 src/main.js 里的路由配置补完整。为什么需要这一步?因为只会写 path + component 只能完成最基础跳转;真实项目里还会遇到“同一个页面多个地址”“旧地址跳新地址”“一个地址同时渲染多块组件”“404 页面”“去掉地址栏的 #”等需求。怎么想?先把路由实例的配置项按职责拆开:routes 管匹配规则,mode 管地址形式,base 管统一前缀。怎么做?直接看本节笔记代码:

js
import Vue from "vue";
// 1- 引入模块vue-router
import VueRouter from "vue-router";
import App from "@/App";
import Home from "@/pages/Home";
import NewsList from "@/pages/NewsList";
import GoodsList from "@/pages/GoodsList";
import My from "@/pages/My";
import NotFound from "@/pages/NotFound";
import MyHeader from "@/pages/MyHeader";
import MyMain from "@/pages/MyMain";
import YouFooter from "@/pages/YouFooter";
// 2- 安装路由
Vue.use(VueRouter);
// 3- 应用配置
// 接收的是一个对象,对象中的routes数组,数组中的每个元素(对象)即是路由配置信息对象
const router =  new VueRouter({
	// 配置路由信息
	routes: [
		{
			path: "/",// 指定路由地址
			component: Home// 当请求地址与路由地址匹配时,那么会使用组件Home
		}, {
			// 支持的请求地址除/newsList以及,还支持/news
			path: "/newsList",
			alias: "/news",// 为地址起别名
			component: NewsList
		}, {
			path: "/goodsList",
			// 可以设置为数组,起多个别名
			alias: ["/first", "/daoLang", "/wangFei"],
			component: GoodsList
		}, {
			path: "/my",
			// 单页面多路由(了解)
			components: {
				one:MyHeader,
				main:MyMain,
				footer:YouFooter,
				default:My
			}
		}, {
			path: "*",
			component: NotFound
		}, {
			// redirect:重定向--》当请求地址为/home,被重定向至地址/
			path: "/home",
			redirect: "/"
		}
	],
	// 指定路由模式:默认为hash
	// 1- history
	// 2- hash
	mode: "history",
	// 支持地址以m开头
	// base:"m"
});
new Vue({
	render: h => h(App),
	// 3- 应用配置
	// 接收的是一个对象,对象中的routes数组,数组中的每个元素(对象)即是路由配置信息对象
	router
}).$mount("#app");

引入、安装、创建实例 ​

为什么需要?Vue.use(VueRouter) 是把路由能力挂到 Vue 上;new VueRouter({...}) 才是创建路由实例。怎么想?固定三步:引入、安装、把实例交给根 Vue。怎么做?注意 router 要作为根实例选项传入,否则 <router-view> 不工作。易错点:转写稿里反复强调“固定写法,不要想太多”;漏写 Vue.use 或根实例没传 router,页面不会按地址切换。

routes 数组与 path/component ​

为什么需要?路由本质是“地址匹配哪个组件”。怎么想?routes 是数组,每个元素是一个配置对象。怎么做?path 写匹配地址,component 写对应组件。易错点:笔记里是 component 单数组件;后面 /my 用的是 components 多视图,两个单词别混。转写稿调试时发现页面不出现,先检查控制台和 router-view 是否写了。

alias:同一组件多个地址 ​

为什么需要?比如新闻列表既要 /newsList 又要 /news。怎么想?别名不是跳转,而是“这个地址也匹配我”。怎么做?alias: "/news",也可写成数组:alias: ["/first", "/daoLang", "/wangFei"]。易错点:转写稿里“编名”是同音,正确是“别名”;别名不会改变地址栏,也不会像 redirect 那样把 A 改成 B。

redirect:地址改道 ​

为什么需要?旧地址 /home 要回到首页 /。怎么想?redirect 是“请求 A,最终去 B”。怎么做?{ path: "/home", redirect: "/" }。易错点:redirect 和 alias 容易混:alias 是同一组件的另一个名字;redirect 是换地址。转写稿说“重钉项”应纠正为“重定向”。

components:单页面多路由 ​

为什么需要?/my 页面要同时出现头部、主体、底部和个人中心默认内容。怎么想?一个地址可以对应多个命名视图。怎么做?用 components: { one, main, footer, default: My },模板里用 <router-view name="one"> 等占位。易错点:这是“了解”项,普通页面仍用 component;命名视图要和 name 对上。

404、mode 与 base ​

为什么需要?未知地址要兜底;地址栏想不要 #。怎么想?path: "*" 放最后匹配 404;mode: "history" 使用 history 模式,默认是 hash。怎么做?按笔记写 NotFound 和 mode。base: "m" 是注释项,表示统一前缀,启用后地址要以 m 开头。易错点:改完配置要重启开发服务;转写稿还提到代理前缀可能先匹配,若 /my 被代理拦截,检查 Network 请求地址,必要时调整代理前缀,别误判成路由坏了。

为什么要把路由从 main.js 抽离 ​

为什么需要:路由配置一多,main.js 会同时承担“创建根实例”和“配置地址与组件映射”两件事,文件越来越长,阅读性变差,后面再加路由也不方便定位。
怎么想:转写稿里的推导是,先把 new VueRouter(...) 的返回值抽成一个 router 变量,让 main.js 只负责引入;再把与路由相关的 Vue.use(VueRouter)、routes、mode、base 整体挪到 src/router/index.js,最后导出给入口使用。
怎么做:新建 src/router/index.js,在其中创建并 export default 路由实例;main.js 只 import router from "@/router",再挂到 Vue 实例上。
易错点:export default 和 import router from "@/router" 要对应;Vue.use(VueRouter) 应放在路由模块中执行一次;不要把 routes 写成 routers。

抽离后的入口文件 main.js ​

为什么需要:入口文件应尽量薄,只做根实例创建和挂载,路由细节交给路由模块。
怎么想:转写稿中抽离后,main.js 里不再出现 routes 数组,只保留 router 的引入和挂载。
怎么做:

js
import Vue from "vue";
import App from "@/App";
import router from "@/router";
new Vue({
	render: h => h(App),
	router
}).$mount("#app");

这里的 @/router 会找到 src/router/index.js。new Vue 配置里的 router 是让整个应用接入路由,$mount("#app") 对应页面中的挂载点。
易错点:忘记在 new Vue 中写 router,会导致路由组件无法正常显示;#app 要和 index.html 中的 id 一致。

路由配置文件 router/index.js 的完整写法 ​

为什么需要:把地址与组件的对应关系集中管理,后续新增页面只改路由文件。
怎么想:先安装插件 Vue.use(VueRouter),再创建 new VueRouter({ routes, mode, base })。routes 中每一项描述“什么地址使用什么组件”。
怎么做:

js
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "@/pages/Home";
import NewsList from "@/pages/NewsList";
import GoodsList from "@/pages/GoodsList";
import MyHeader from "@/pages/MyHeader";
import MyMain from "@/pages/MyMain";
import YouFooter from "@/pages/YouFooter";
import My from "@/pages/My";
import NotFound from "@/pages/NotFound";

Vue.use(VueRouter);
export default new VueRouter({
	// 配置路由信息
	routes: [
		{
			path: "/",// 指定路由地址
			component: Home// 当请求地址与路由地址匹配时,那么会使用组件Home
		}, {
			// 支持的请求地址除/newsList以及,还支持/news
			path: "/newsList",
			alias: "/news",// 为地址起别名
			component: NewsList
		}, {
			path: "/goodsList",
			// 可以设置为数组,起多个别名
			alias: ["/first", "/daoLang", "/wangFei"],
			component: GoodsList
		}, {
			path: "/my",
			// 单页面多路由(了解)
			components: {
				one: MyHeader,
				main: MyMain,
				footer: YouFooter,
				default: My
			}
		}, {
			path: "*",
			component: NotFound
		}, {
			// redirect:重定向--》当请求地址为/home,被重定向至地址/
			path: "/home",
			redirect: "/"
		}
	],
	// 指定路由模式:默认为hash
	// 1- history
	// 2- hash
	mode: "history",
	// 支持地址以m开头
	// base:"m"
});

alias 用于给同一路由起别名,可写字符串或数组;components 用于单页面多路由,配合命名视图使用;redirect 是访问 /home 时跳到 /;path:"*" 兜底 404。转写稿调试时还提到:mode:"history" 后地址不再带 #,默认是 hash;base 可给地址加统一前缀,如 base:"m" 后地址以 /m 开头。
易错点:单个组件用 component,多个命名视图用 components;alias 不要写成 alias 数组却仍按单值理解;redirect 写目标路径;若 base 前缀与开发代理前缀冲突,转写稿中明确代理优先级更高,可能把路由请求先拦走,需检查代理配置。

调试与检查清单 ​

转写稿的调试过程是:先把路由配置留在 main.js,验证 /、/newsList、/news、/goodsList、/my、404 和 /home 重定向;再把 new VueRouter 抽成 const router,最后整体抽到 src/router/index.js 并导出。抽离后刷新页面,地址与组件仍能对应,说明导出和引入正确。
易错点检查:src/router/index.js 是否默认导出;main.js 是否引入并挂载;Vue.use(VueRouter) 是否执行;routes 拼写;mode 与 base 是否符合预期;使用 base:"m" 时留意与代理前缀的优先级。

笔记中记录的接口地址原样保留:

https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=上海&ci=10&channelId=4

https://api.github.com/search/repositories?q=r&sort=stars

https://api.github.com/search/users?q=r&sort=stars

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

代码示例 ​

示例一:创建路由对应的 5 个页面组件 ​

vue
<!-- src/pages/Home.vue -->
<template>
    <div>
        <h3>Home</h3>
    </div>
</template>

<script>
export default {
    name: "Home"
}
</script>

<style scoped>

</style>
vue
<!-- src/pages/NewsList.vue -->
<template>
    <div>
        <h3>新闻列表界面</h3>
    </div>
</template>

<script>
export default {
    name: "NewsList"
}
</script>

<style scoped>

</style>
vue
<!-- src/pages/GoodsList.vue -->
<template>
    <div>
        <h3>商品列表界面</h3>
    </div>
</template>

<script>
export default {
    name: "GoodsList"
}
</script>

<style scoped>

</style>
vue
<!-- src/pages/My.vue -->
<template>
    <div>
        <h3>我的个人中心界面</h3>
    </div>
</template>

<script>
export default {
    name: "My"
}
</script>

<style scoped>

</style>
vue
<!-- src/pages/NotFound.vue -->
<template>
    <div>
        <h3>404,您找的界面不存在!</h3>
    </div>
</template>

<script>
export default {
    name: "NotFound"
}
</script>

<style scoped>

</style>

要点解释与易错点

  • 路由级组件统一放在 src/pages(或 src/views)下,和普通业务组件区分开,这是笔记中明确强调的目录习惯。
  • name 属性与文件名保持一致,便于调试时在 Vue Devtools 中定位组件。
  • 每个组件模板必须是单根节点(外层 <div>),Vue 2 中多根节点会直接报编译错误。
  • <style scoped> 加 scoped 是为了让样式只作用于当前组件,避免多个页面组件样式互相污染。
  • 组件里先只写占位标题即可,路由跑通后再填业务内容,符合转写稿中“先创建组件,再实现路由”的顺序。

示例二:路由配置文件 src/router/index.js(含 alias、redirect、404、history 模式、单页面多路由) ​

js
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "@/pages/Home";
import NewsList from "@/pages/NewsList";
import GoodsList from "@/pages/GoodsList";
import MyHeader from "@/pages/MyHeader";
import MyMain from "@/pages/MyMain";
import YouFooter from "@/pages/YouFooter";
import My from "@/pages/My";
import NotFound from "@/pages/NotFound";

// 安装路由插件,必须在 new VueRouter 之前执行
Vue.use(VueRouter);

export default new VueRouter({
    // 配置路由信息:routes 是数组,数组中的每个元素(对象)即是一条路由配置信息
    routes: [
        {
            path: "/",              // 指定路由地址
            component: Home         // 地址匹配时使用 Home 组件
        }, {
            // 支持的请求地址除 /newsList 以外,还支持 /news
            path: "/newsList",
            alias: "/news",         // 为地址起别名
            component: NewsList
        }, {
            path: "/goodsList",
            // 可以设置为数组,起多个别名
            alias: ["/first", "/daoLang", "/wangFei"],
            component: GoodsList
        }, {
            path: "/my",
            // 单页面多路由(了解)
            components: {
                one: MyHeader,
                main: MyMain,
                footer: YouFooter,
                default: My
            }
        }, {
            // 通配符,匹配不到任何已配置地址时走这里
            path: "*",
            component: NotFound
        }, {
            // redirect:当请求地址为 /home,被重定向至地址 /
            path: "/home",
            redirect: "/"
        }
    ],
    // 指定路由模式:默认为 hash
    // 1- history  2- hash
    mode: "history",
    // 支持地址以 m 开头
    // base: "m"
});

要点解释与易错点

  • Vue.use(VueRouter) 是固定写法,漏写会在 new VueRouter 时报 “not a constructor” 类错误。
  • 每个路由对象用 path 指定地址、component 指定组件,component 是单数;只有单页面多路由才用 components 复数,写成复数却配单个组件是常见错误。
  • alias 是“同一个组件多地址”,不是重定向:地址栏仍然显示 /news;redirect 是地址栏会变成目标地址,两者别混用。
  • path: "*" 必须写在数组末尾(Vue Router 3 按顺序匹配,写在前面会吃掉所有路由)。
  • mode: "history" 去掉 URL 中的 #,但上线后需要服务端把 404 回退到 index.html,否则刷新子路径会 404;base 用于给所有地址加统一前缀。
  • 转写稿中提到的一个坑:如果 devServer.proxy 里配了 /my 之类的代理,代理优先级高于路由,会出现“路由配了但页面出不来”,需要换代理前缀。

示例三:入口 src/main.js + 根组件 src/App.vue(抽出 router 后的最终形态) ​

js
// src/main.js
import Vue from "vue";
import App from "@/App";
import router from "@/router";

new Vue({
    render: h => h(App),
    router
}).$mount("#app");
vue
<!-- src/App.vue -->
<template>
    <div>
        <!-- 指定路由呈现的位置:为路由组件占位 -->
        <!-- components -> one -> 组件 -->
        <router-view name="one"></router-view>
        <!-- components -> main -> 组件 -->
        <router-view name="main"></router-view>
        <!-- components -> footer -> 组件 -->
        <router-view name="footer"></router-view>
        <hr/>
        <!-- 单页面多路由中,router-view 省略 name,会使用 components -> default -> 组件 -->
        <router-view></router-view>
    </div>
</template>

<script>
export default {
    name: "App.vue"
}
</script>

<style scoped>

</style>

要点解释与易错点

  • router 是 router: router 的 ES6 简写,new Vue({ router }) 把路由实例注入根实例,所有子组件才能用 router-view。
  • router-view 是占位符:地址匹配到哪条路由,就在这个位置渲染对应组件;不写 router-view 页面永远是空白,这是新手最常见的“路由配好了却没反应”。
  • router-view 的 name 必须和路由配置里 components 的 key 严格对应,省略 name 就走 default。
  • 笔记里 App.vue 重复写了 4 个无名 router-view,是为了演示“同一个组件会在每个无名占位处重复渲染”;实际项目只保留一个即可,多写会导致页面内容重复出现。
  • 从笔记 15-3 到 15-5 的演进路径:路由配置先写在 main.js → 抽成 const router 变量 → 再抽到 src/router/index.js 并 export default,main.js 只负责 import router from "@/router",这样后续新增路由只改路由文件,main.js 不用动。

常见错误 ​

  1. 现象:改完 vue.config.js 代理后仍跨域或请求未走代理→原因:devServer 未重启,代理配置未生效→修法:停止服务后重新 npm run serve,并确认改的是 devServer.proxy。
  2. 现象:请求仍跨域或代理不匹配→原因:Ajax 地址没有以代理键前缀开头→修法:统一改为 /api/... 等前缀,在 Network 面板核对最终请求 URL。
  3. 现象:代理后最终地址多前缀/少前缀导致 404→原因:pathRewrite 与请求前缀不匹配,或 changeOrigin 未按目标服务要求设置→修法:先看 Network 最终 URL,再按前缀写 pathRewrite,必要时设 changeOrigin: true。
  4. 现象:mode: "history" 后直接刷新 /newsList 等子路由 404→原因:history 模式需要服务端回退到入口页→修法:开发环境配 historyApiFallback,生产环境配 Nginx try_files,或暂用 hash 模式。
  5. 现象:访问别名地址以为会跳转,结果 URL 不变或误判失败→原因:alias 只是同一组件的可访问地址,不是重定向→修法:需要 URL 改变用 redirect;只需多地址访问用 alias。
  6. 现象:/my 页面空白或只显示部分区域→原因:components 的键与 router-view 的 name 不对应,或 default 没有无名 router-view 承接→修法:逐一核对 name,保留一个无名 router-view 承接 default。
  7. 现象:代理前缀与路由路径冲突,访问 /my 被代理走→原因:devServer 代理前缀优先匹配,代理前缀覆盖了路由路径→修法:代理改用 /api、/proxy 等独立前缀,避免与路由前缀重合。
  8. 现象:map 中直接改 item.checked 后原数组数据也被改→原因:item 是原数组对象的引用→修法:返回新对象或生成新数组后再替换/emit,不要直接改原对象属性。

练习 ​

  1. 在 vue.config.js 配置 /api 代理到猫眼热门接口,页面用 Axios 请求 /api/mmdb/movie/v3/list/hot.json?...;验收:Network 中请求本机 /api,响应有数据,无 CORS 报错。
  2. 将路由配置从 main.js 抽离到 src/router/index.js,main.js 只引入 router;验收:/、/newsList、/goodsList、/my、任意错误地址均正常。
  3. 用 alias 让 /news 与 /newsList 使用同一组件,用 redirect 让 /home 跳到 /;验收:说明哪些地址 URL 会变、哪些不会变。
  4. 为 /my 实现单页面多路由:one/main/footer/default 四个命名视图;验收:四个区域分别渲染对应组件,删掉无名 router-view 后能解释 default 消失现象。
  5. 给商品列表加“选中”功能:用 map 生成新数组修改指定 id 的选中状态;验收:原数组不被污染,视图更新,并说明直接改 item.checked 为什么会改原数据。

原视频对照 ​

#视频时长
1自定义代理14:45
2代理配置mp41:06:02
3引用10:41
4实现路由一mp438:48
5实现路由二三11:18

小结 ​

  1. 路由配置先跑通再抽离,抽离后要保证 export default 与 import router from "@/router" 一致。
  2. history 模式不是前端单独能完全解决的,必须考虑服务端回退配置。
  3. alias 解决“多地址访问同一组件”,redirect 解决“地址跳转”,两者不要混用。
  4. 单页面多路由的核心是 components 键与 router-view name 一一对应,default 必须有无名视图承接。
  5. 代理配置修改后必须重启 devServer;调试代理先看 Network 最终请求 URL,再看响应。
  6. pathRewrite 决定最终转发地址,changeOrigin 只影响目标服务看到的来源,不要指望它替代 CORS 处理。
  7. 代理前缀应避开路由前缀,否则代理会优先拦截页面路由请求。
  8. 修改数组内对象属性会污染原数据;需要可追踪更新时,优先生成新对象/新数组再替换。