Appearance
多页路由与模板解析
本节目标:理解“多页路由与模板解析”的核心思路,并能把它用于实际开发或面试表达。
说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 ==上篇文章中我们成功打包并输出了多页文件,而构建一个多页应用能够让我们进一步了解项目配置的可拓展性,可以对学习== ==Vue== ==和== ==webpack== ==起到强化训练的效果,本文将在此基础上主要针对多页路由及模板的配置进行系列的介绍。== 路由配置1. 跳转 ==在配置路由前,首先我们要明确一点就是,多页应用中的每个单页都是相互隔离的,即如果你想从== ==page1== ==下的路由跳到== ==page2== ==下的路由,你无法使用== ==vue-router== ==中的方法进行跳转,需要使用原生方法:====location.href== ==或== ==location.replace====。== ==此外为了能够清晰的分辨路由属于哪个单页,我们应该给每个单页路由添加前缀,比如:==
==index== ==单页:====/vue/====page1== ==单页:====/vue/page1/====page2== ==单页:====/vue/page2/====其中== ==/vue/== ==为项目的二级目录,其后的目录代表路由属于哪个单页。因此我们每个单页的路由配置可以像这样:==
/* page1 单页路由配置 */
import Vue from 'vue'
import Router from 'vue-router'
// 首页
const Home = (resolve => {
require.ensure(['../views/home.vue'], () => {
resolve(require('../views/home.vue'))
})
})
Vue.use(Router)
let base = `${process.env.BASE_URL}` + 'page1'; // 添加单页前缀
export default new Router({
mode: 'history',
base: base,
routes: [
{
path: '/',
name: 'home',
component: Home
},
]
})
==我们通过设置路由的== ==base== ==值来为每个单页添加路由前缀,如果是== ==index== ==单页我们无需拼接路由前缀,直接跳转至二级目录即可。==
==那么在单页间跳转的地方,我们可以这样写:==
==<template>====== ==<div== ==id========="app"====>====== ==<div== ==id========="nav"====>====== ==<a @====click========="goFn('')"====>====Index====</a>== ==|====== ==<a @====click========="goFn('page1')"====>====Page1====</a>== ==|====== ==<a @====click========="goFn('page2')"====>====Page2====</a>== ==|====== ==</div>====== ==<router-view/>====== ==</div>========</template>======
==<script>======**export** **default** =={====== ==methods: {====== ==goFn(name) {====== ==location.href === ==`====${process.env.BASE_URL}====`== ==+ name====== ==}====== ==}========}========</script>======
==但是为了保持和== ==Vue== ==路由跳转同样的风格,我可以对单页之间的跳转做一下封装,实现一个== ==Navigator== ==类,类的代码可以查看本文最后的示例,封装完成后我们可以将跳转方法修改为:==
**this**==.$openRouter({====== ==name: name,== ==//== ==跳转地址====== ==query: {====== ==text:== =='hello'== ==//== ==可以进行参数传递====== ==},========})======
==使用上述== ==$openRouter== ==方法我们还需要一个前提条件,便是将其绑定到== ==Vue== ==的原型链上,我们在所有单页的入口文件中添加:==
**import** =={ Navigator }== **from** =='../../common'== ==//== ==引入== ==Navigator======
==Vue.prototype.$openRouter = Navigator.openRouter;== ==//== ==添加至== ==Vue== ==原型链======
==至此我们已经能够成功模仿== ==vue-router== ==进行单页间的跳转,但是需要注意的是因为其本质使用的是== ==location== ==跳转,所以必然会产生浏览器的刷新与重载。==
**2.** **重定向**
==当我们完成上述路由跳转的功能后,可以在本地服务器上来进行一下测试,你会发现== ==Index== ==首页可以正常打开,但是跳转== ==Page1====、====Page2== ==却仍然处于== ==Index== ==父组件下,这是因为浏览器认为你所要跳转的页面还是在== ==Index== ==根路由下,同时又没有匹配到== ==Index== ==单页中对应的路由。这时候我们服务器需要做一次重定向,将下方路由指向对应的== ==html== ==文件即可:==
==/vue/page1 -> /vue/page1.html========/vue/page2 -> /vue/page2.html======
==在== ==vue.config.js== ==中,我们需要对== ==devServer== ==进行配置,添加== ==historyApiFallback== ==配置项,该配置项主要用于解决== ==HTML5 History API== ==产生的问题,比如其== ==rewrites== ==选项用于重写路由:==
==/* vue.config.js */======
**let** ==baseUrl === =='/vue/'====;======
==module====.exports = {====== ==...====== ==== ==devServer: {====== ==historyApiFallback: {====== ==rewrites: [====== =={ from:== **new** ==RegExp====(baseUrl +== =='page1'====), to: baseUrl +== =='page1.html'== ==},====== =={ from:== **new** ==RegExp====(baseUrl +== =='page2'====), to: baseUrl +== =='page2.html'== ==},====== ==]====== ==}====== ==}====== ==== ==...========}======
==上方我们通过== ==rewrites== ==匹配正则表达式的方式将== ==/vue/page1== ==这样的路由替换为访问服务器下正确== ==html== ==文件的形式,如此不同单页间便可以进行正确跳转和访问了。最后需要注意的是如果你的应用发布到正式服务器上,你同样需要让服务器或者中间层作出合理解析,参考:====HTML5 History== ==模式== ==#== ==后端配置例子==
==而更多关于== ==historyApiFallback== ==的信息可以访问:====connect-history-api-fallback==
**模板配置**
==上篇文章我们已经介绍了关于多模板的读取和配置,在配置== ==html-webpack-plugin== ==的时候我们提到了自定义配置,这里我将结合模板渲染的功能来进行统一介绍。==
**1.** **模板渲染**
==这里所说的模板渲染是在我们的== ==html== ==模板文件中使用== ==html-webpack-plugin== ==提供的== ==default template== ==语法进行模板编写,比如:==
**<!DOCTYPE html>**======<html>====== ==<head>====== ==<meta== ==charset========="utf-8"====>====== ==<meta== ==http-equiv========="X-UA-Compatible"== ==content========="IE=edge"====>====== ==<meta== ==name========="viewport"== ==content========="width=device-width,initial-scale=1.0"====>====== ==<title>====模板====</title>====== ==<%== ==for== ==(====var== ==chunk== ==in== ==htmlWebpackPlugin.files.css====) { %>====== ==<%== ==if====(====htmlWebpackPlugin.files.css====[====chunk====]) {%>====== ==<link== ==href========="<%= htmlWebpackPlugin.files.css[chunk] %>"== ==rel========="stylesheet"== ==/>====== ==<%}%>====== ==<% } %>====== ==</head>====== ==<body>====== ==<div== ==id========="app"====></div>====== ==<!-- built files will be auto injected -->======
==<%== ==for== ==(====var== ==chunk== ==in== ==htmlWebpackPlugin.files.js====) { %>====== ==<%== ==if====(====htmlWebpackPlugin.files.js====[====chunk====]) {%>====== ==<script== ==type========="text/javascript"== ==src========="<%= htmlWebpackPlugin.files.js[chunk] %>"====></script>====== ==<%}%>====== ==<% } %>====== ==</body>========</html>======
==以上我们使用模板语法手动获取并遍历== ==htmlWebpackPlugin== ==打包后的文件并生成到模板中,其中的== ==htmlWebpackPlugin== ==变量是模板提供的可访问变量,其有以下特定数据:==
=="htmlWebpackPlugin": {====== =="files": {====== =="css": [ "main.css" ],====== =="js": [ "assets/head_bundle.js", "assets/main_bundle.js"],====== =="chunks": {====== =="head": {====== =="entry": "assets/head_bundle.js",====== =="css": [ "main.css" ]====== ==},====== =="main": {====== =="entry": "assets/main_bundle.js",====== =="css": []====== ==},====== ==}====== ==}========}======
==我们通过== ==htmlWebpackPlugin.files== ==可以获取打包输出的== ==js== ==及== ==css== ==文件路径,包括入口文件路径等。==
==需要注意的是如果你在模板中编写了插入对应== ==js== ==及== ==css== ==的语法,你需要设置== ==inject== ==的值为== ==false== ==来关闭资源的自动注入:==
==/* utils.js */========...======
==let conf = {====== ==entry: filePath,== ==// page== ==的入口====== ==template: filePath,== ==//== ==模板路径====== ==filename: filename +== =='.html'====,== ==//== ==生成== ==html== ==的文件名====== ==chunks: [===='manifest'====,== =='vendor'====, filename],====== ==inject:== ==false====,== ==//== ==关闭资源自动注入========}======
==...======
==否则在页面会引入两次资源,如下图所示:==**2.** **自定义配置**
==在模板渲染中,我们只能够使用== ==htmlWebpackPlugin== ==内部的一些属性和方法来进行模板的定制化开发,那么如果遇到需要根据不同环境来引入不同资源,同时不同模板间的配置还可能不一样的需求情况的话,我们使用自定义配置会比较方便。比如我们需要在生产环境模板中引入第三方统计脚本:==
==/* vue.config.js */======
==module====.exports = {====== ==...====== ==== ==pages: utils.setPages({====== ==addScript() {====== **if** ==(process.env.NODE_ENV ===== =='production'====) {====== **return** ==`====== ==<script src="https://s95.cnzz.com/z_stat.php?id=xxx&web_id=xxx" language="JavaScript"></script>====== ==`====== ==}======
**return** ==''====== ==}====== ==}),====== ==== ==...========}======
==然后在页面模板中通过== ==htmlWebpackPlugin.options== ==获取自定义配置对象并进行输出:==
==<%== ==if====(====htmlWebpackPlugin.options.addScript====){ %>====== ==<%=== ==htmlWebpackPlugin.options.addScript====() %>========<%}%>======
==同时你也可以针对个别模板进行配置,比如我想只在== ==Index== ==单页中添加统计脚本,在== ==Page1== ==单页中添加其他脚本,那么你可以给== ==addScript== ==传入标识符来进行判断输出,比如:==
==<%== ==if====(====htmlWebpackPlugin.options.addScript====){ %>====== ==<%=== ==htmlWebpackPlugin.options.addScript====('====index====') %>========<%}%>======
==同时为== ==addScript== ==方法添加参数== ==from====:==
==addScript(==**from**==) {====== **if** ==(process.env.NODE_ENV ===== =='production'====) {====== **let** ==url === =="====https://xxx===="====;====== ==== **if** ==(==**from** ======= =='index'====) {====== ==url === =="====https://s95.cnzz.com/z_stat.php?id=xxx&web_id=xxx===="====;====== ==}====== ==== **return** ==`====== ==<script src=====${url}== ==language="JavaScript"></script>====== ==`====== ==}======
**return** ==''========}======
==这样我们就完成了自定义配置中的模板渲染功能。当然根据实际项目需求你的自定义配置项可能会更加复杂和灵活。==
**结语**
==通过== ==2== ==小节的学习,相信大家对== ==Vue== ==多页应用的构建已经有所了解。本文在第== ==1== ==节的基础上重点介绍了多页路由及模板的配置,阐述了其与单页应用的不同之处,同时针对模板自定义配置的使用场景给出了简单的实例,希望大家在了解的基础上将下方的实例代码作为参考,进行相应的实战。==
==本案例代码地址:====multi-page-project==
**思考** **&** **作业**==多页应用中各自的== ==Vuex Store== ==信息能实现共享吗?====html-webpack-plugin== ==如何解析非== ==.html== ==的模板,比如== ==.hbs====,应该如何配置?==> 来自
<https://juejin.im/book/5b23a5aef265da59716fda09/section/5b4dea8df265da0f9e58930a>