前言
这是一个系列专栏,将会更新一系列 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 内容:
其中只有 css 样式文件的引入,并没有 JS 的逻辑
首先,将 html
里的内容和 css
里的内容全都 copy 过来,将 body 中的元素拷贝至 template 中。css 样式有两个,一个是公共样式,一个是页面单独的样式。我们将页面独立的 css 文件的内容拷贝至 style 标签中,公共样式在入口文件处引入。
这里有个地方需要注意,图片的路径需要更改,现在我们的路径是这样的:
除了 DOM 中的路径,css 中也有图片的应用,路径也需要更改:
二
其次创建入口文件main.js
这里的路径也需要注意。main.js
内容和 home
页面内容基本一致
到这里是不是感觉比较简单,但真正复杂的是 webpack 配置,需要为两个页面做单独处理
三
测试 login
首先我们先尝试将 login 页面跑起来,把它当作是一个单页应用:
javascript
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"),
}),
]
}
这里,我们需要修改三个地方:
- 构建入口为
src/pages/Login/main.js
- 添加图片的 loader 配置,因为 DOM 中引用了图片
- 修改 html 模版路径
启动看看:
和改造前的 login 页面对比下:
发现背景图片没有加载出来,打开控制台看看是什么情况,初步猜测是图片没有加载成功,但是打开 netWork 一看,图片正常获取的:
然后查看页面元素,发现 body 高度是 0:
但是改造前的页面 body 高度却不是 0:
而且有明显的 css 样式定义height:100%;
,
回到代码 style 标签处,发现这里有个 scope,应该是这个问题。把这个 scope 删掉看看:
果然有用!
scoped
会对当前组件内的所有 DOM 添加一个额外的 tag 属性,像这样: css 样式选择器生成的时候,就会带上这个 tag,像这样:这样就能实现组件内的独立样式了,不会污染其他组件的样式。
但是 scoped 只对组件内的 DOM 添加 tag 属性,所以刚才对 html,body 的样式设置失效了
OK,现在 login.html 改造测试成功,接下来就修改 webpack,将 login 以及 home 页面结合成一个项目--MAP
改造 webpack
现在文件夹结构是这个样子,两个页面的文件分别放在各自的文件夹内:
下面来修改配置文件的 entry 和 output:
javascript
{
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 配置:
javascript
{
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:
javascript
{
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
:
然后访问/login
完全符合预期!!
SPA 的实现
SPA
即 Single Page Application
单页应用,而这正是前端框架思想的最经典应用。单页应用意思是用一个 html 文件,效果上实现多个页面。页面之间的切换实际上只是页面的组件切换。
下面我们将 index.html
以及 login.html
合并,来实现一个单页应用
一
首先准备两个页面的组件,然后用 vue-router 进行切换。由于之前已经做好了两个页面的 vue 化,可以直接复用两个页面的 App.vue。App.vue 中包含了对应页面所有的内容
接下来准备 vue-router
的内容:
- 安装依赖:
npm i vue-router@3
, 由于我们使用的是 vue@2,所以要指定 vue-router 的版本 - 写路由文件:
router/routes.js
javascript
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
,采用history
mode
javascript
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
:
vue
<template>
<router-view></router-view>
</template>
<script>
export default {};
</script>
<style></style>
一个空文件,主要是给 vue-router
做 render 用
然后是 src/main.js
javascript
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%都是单页应用,所以我就不详细讲,只把重点部分标注出来
javascript
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
页面:
没问题,下面来看看/login
页面
也没问题!!
现在文件夹结构是这样子:
这个项目还有其他的页面,可以采用其中一个实现方案将其他的页面一并改造了
总结
老前端项目改造,从 webpack 的引入,再到 vue 框架的引入,再到多页应用或者单页应用的实现,对于一个老项目的改造到这里就差不多接近尾声了,看这文件夹就是个完完全全的现代前端项目。
但对于一个现代前端项目来说,项目中的 webpack 配置是不够看的,下篇文章来分享对项目性能优化的内容,敬请期待。