likes
comments
collection
share

👊超给力的webpack实战三之vue单页面和多页面配置

作者站长头像
站长
· 阅读数 7

前言

这是一个系列专栏,将会更新一系列 webpack 的实战内容,主要围绕着旧前端项目现代化,以及现代化前端项目的再优化。

下面开始第三篇:vue单页面和多页面配置

上篇文章写到将 vue 框架引入老旧前端项目,完成了一个页面的改造。将其中一个页面用 vue 框架运行了起来。但是项目不止一个页面,其他页面也都需要改造成 vue 页面。

一旦改造第二个页面,就需要面临一个问题:第二个页面是和第一个页面放在一个 vue 项目里,还是分开成两个 vue 页面,做成两个 vue 项目呢?

前者是单页面应用 SPA,后者是多页面应用 MPA

SPA 全称 Single Page Application,即单页面应用。它所需的资源,如 HTML、CSS 和 JS 等,在一个页面中就加载完成,对于 SPA 来说,页面的切换就是组件或视图之间的切换。

MPA多页面应用 MultiPage Application ,指有多个独立页面的应用(多个html页面),每个页面必须重新加载js、css等相关资源。页面之间跳转,需要重新加载整页资源。页面跳转,是在不同的 HTML 之间跳转。

上面两种方案都行,都有其道理和存在的业务场景,所以这篇文章就将两个方案都去实现

MPA 的实现

MPA 是MultiPage Application,每个页面是单独的 html 文件,所以改造的第二个页面 login.html的思路是,第二个页面和第一个页面采用不同的 vue 入口,也就是说两个没有关系的页面。

MPA改造的方便之处是,在引入 vue 框架的操作,可以完全复制第一个页面的操作,比如新建 main.js, App.vue 等。

先看看 login.html 内容:👊超给力的webpack实战三之vue单页面和多页面配置其中只有 css 样式文件的引入,并没有 JS 的逻辑

首先,将 html 里的内容和 css 里的内容全都 copy 过来,将 body 中的元素拷贝至 template 中。css 样式有两个,一个是公共样式,一个是页面单独的样式。我们将页面独立的 css 文件的内容拷贝至 style 标签中,公共样式在入口文件处引入。👊超给力的webpack实战三之vue单页面和多页面配置这里有个地方需要注意,图片的路径需要更改,现在我们的路径是这样的:👊超给力的webpack实战三之vue单页面和多页面配置

除了 DOM 中的路径,css 中也有图片的应用,路径也需要更改:👊超给力的webpack实战三之vue单页面和多页面配置

其次创建入口文件main.js👊超给力的webpack实战三之vue单页面和多页面配置这里的路径也需要注意。main.js 内容和 home 页面内容基本一致

到这里是不是感觉比较简单,但真正复杂的是 webpack 配置,需要为两个页面做单独处理

测试 login

首先我们先尝试将 login 页面跑起来,把它当作是一个单页应用:

module.exports = {
  entry: resolve("./src/pages/Login/main.js"),
  module:{
    rules:[
      {
				test: /\.(png|jpe?g)$/i,
				type: "asset/resource",
			}
    ]
  },
  plugins:[
    new HtmlWebpackPlugin({
			template: resolve("./src/pages/Login/index.html"),
		}),
  ]
}

这里,我们需要修改三个地方:

  1. 构建入口为src/pages/Login/main.js
  2. 添加图片的 loader 配置,因为 DOM 中引用了图片
  3. 修改 html 模版路径

启动看看:👊超给力的webpack实战三之vue单页面和多页面配置👊超给力的webpack实战三之vue单页面和多页面配置和改造前的 login 页面对比下:👊超给力的webpack实战三之vue单页面和多页面配置发现背景图片没有加载出来,打开控制台看看是什么情况,初步猜测是图片没有加载成功,但是打开 netWork 一看,图片正常获取的:👊超给力的webpack实战三之vue单页面和多页面配置然后查看页面元素,发现 body 高度是 0:👊超给力的webpack实战三之vue单页面和多页面配置但是改造前的页面 body 高度却不是 0:👊超给力的webpack实战三之vue单页面和多页面配置而且有明显的 css 样式定义height:100%;,回到代码 style 标签处,发现这里有个 scope,应该是这个问题。把这个 scope 删掉看看:👊超给力的webpack实战三之vue单页面和多页面配置👊超给力的webpack实战三之vue单页面和多页面配置果然有用!

scoped会对当前组件内的所有 DOM 添加一个额外的 tag 属性,像这样: 👊超给力的webpack实战三之vue单页面和多页面配置 css 样式选择器生成的时候,就会带上这个 tag,像这样: 👊超给力的webpack实战三之vue单页面和多页面配置

这样就能实现组件内的独立样式了,不会污染其他组件的样式。

但是 scoped 只对组件内的 DOM 添加 tag 属性,所以刚才对 html,body 的样式设置失效了

OK,现在 login.html 改造测试成功,接下来就修改 webpack,将 login 以及 home 页面结合成一个项目--MAP

改造 webpack

现在文件夹结构是这个样子,两个页面的文件分别放在各自的文件夹内:👊超给力的webpack实战三之vue单页面和多页面配置

下面来修改配置文件的 entry 和 output:

{
  entry: {
		home: resolve("./src/pages/Home/main.js"),
		login: resolve("./src/pages/Login/main.js"),
	},
	output: {
		path: resolve("./dist"),
		filename: "[name].js",
	},
  
}

将入口改成两个,分别是两个页面的入口文件。并且输出的文件名称改成动态的,根据 entry 对象的 key 所决定。

然后增加一个 html-webpack-plugin 配置:

{
  plugins:[
    new HtmlWebpackPlugin({
			template: resolve("./src/pages/Home/index.html"),
			filename: "home.html",
			chunks: ["home"],
		}),
		new HtmlWebpackPlugin({
			template: resolve("./src/pages/Login/index.html"),
			filename: "login.html",
			chunks: ["login"],
		}),
  ]
}

一个 plugin 处理 index 页面,另一个处理 login 页面。其中 chunks 的作用是指定对应页面构建后的 js 文件。如果不配置 chunks,就会出现 login.html 同时引入 index.js 以及 login.js 的情况

最后一个,配置 devServer:

{
  devServer: {
		// 省略部分代码
		historyApiFallback: {
			rewrites: [
				{ from: /^(\/)?$/, to: "/home.html" },
				{ from: /^\/home$/, to: "/home.html" },
				{ from: /^\/login$/, to: "/login.html" },
			],
		},
}

因为有两个页面,如果不配置historyApiFallback,那么启动 webpack-dev-server 后,无论路径是什么,返回浏览器的永远是默认的 index.html。

其中 rewrites 的作用是识别路径,如果路径符合正则,那就返回对应的 html 文件。

上面配置的效果是,如果路径是/login,那就返回 login.html,其他的一律返回home.html先访问/``/home:👊超给力的webpack实战三之vue单页面和多页面配置👊超给力的webpack实战三之vue单页面和多页面配置然后访问/login👊超给力的webpack实战三之vue单页面和多页面配置

完全符合预期!!

SPA 的实现

SPASingle Page Application单页应用,而这正是前端框架思想的最经典应用。单页应用意思是用一个 html 文件,效果上实现多个页面。页面之间的切换实际上只是页面的组件切换。

下面我们将 index.html 以及 login.html 合并,来实现一个单页应用

首先准备两个页面的组件,然后用 vue-router 进行切换。由于之前已经做好了两个页面的 vue 化,可以直接复用两个页面的 App.vue。App.vue 中包含了对应页面所有的内容👊超给力的webpack实战三之vue单页面和多页面配置接下来准备 vue-router 的内容:

  1. 安装依赖:npm i vue-router@3, 由于我们使用的是 vue@2,所以要指定 vue-router 的版本
  2. 写路由文件:router/routes.js
import Home from "../pages/Home/App.vue";
import Login from "../pages/Login/App.vue";

const routes = [
	{
		path: "/",
		component: Home,
		children: [{ path: "home", component: Home }],
	},
	{
		path: "/login",
		component: Login,
	},
];

export default routes;

准备了三个路由,如果路径是//home,那就会渲染 Home 页面;如果路径是/login,就会渲染 Login 页面

路由出口文件:router/index.js,采用historymode

import VueRouter from "vue-router";
import Vue from "vue";
import routes from "./routes";

Vue.use(VueRouter);

export default new VueRouter({
	mode: "history",
	routes: routes,
});

然后准备 Vue 应用的入口文件

首先是 src/App.vue:

<template>
		<router-view></router-view>
</template>

<script>
export default {};
</script>

<style></style>

一个空文件,主要是给 vue-router 做 render 用

然后是 src/main.js

import Vue from "vue";
import router from "./router";
import App from "./App.vue";
import "../css/public.css"; // 这个公共样式文件别忘了

new Vue({
	render: (h) => h(App),
	router: router,
}).$mount("#root");

最后是 webpack 的单页应用配置文件,单页应用的配置文件比多页应用简单多了,大家在网上看到的配置 99%都是单页应用,所以我就不详细讲,只把重点部分标注出来

module.exports = {
  //入口和出口都是单页应用
  entry: resolve("./src/main.js"),
	output: {
		path: resolve("dist"),
		filename: "index.js",
	},
  devServer: {
		// 直接设置为true即可,不需要配置rewrite
		historyApiFallback: true,
	},
  plugins:[
    // html-webpack-plugin也只需要一个就可以
    new HtmlWebpackPlugin({
			template: resolve("./public/index.html"),
			filename: "index.html",
		})
  ]
}

搞定!!

下面来测试一下,启动项目:npm run dev

打开/以及/home页面:👊超给力的webpack实战三之vue单页面和多页面配置👊超给力的webpack实战三之vue单页面和多页面配置

没问题,下面来看看/login页面👊超给力的webpack实战三之vue单页面和多页面配置

也没问题!!现在文件夹结构是这样子:👊超给力的webpack实战三之vue单页面和多页面配置

这个项目还有其他的页面,可以采用其中一个实现方案将其他的页面一并改造了

总结

老前端项目改造,从 webpack 的引入,再到 vue 框架的引入,再到多页应用或者单页应用的实现,对于一个老项目的改造到这里就差不多接近尾声了,看这文件夹就是个完完全全的现代前端项目。

但对于一个现代前端项目来说,项目中的 webpack 配置是不够看的,下篇文章来分享对项目性能优化的内容,敬请期待。