单页应用转多页,你猜 webpack+react 怎么配

1,083 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

单页应用和多页应用,你分清楚了吗?

我们先来看单页应用。

百度百科介绍如下:

单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。

什么意思呢? 就是它只有一个 html 文件,页面内容的变化是通过 js 控制的。可以理解为,点击不同的菜单,js 就会控制 html 中加载的 dom 内容。

由于它第一次加载的时候,它需要加载一个比较大的 JS 文件,所以,它的缺点就是首页加载比较慢。但是切换界面的时候,它不需要重新发起请求,所以它切换很快。

就我目前的开发经验而言,单页应用应用于许多大型的管理系统,这些管理系统表单多、数据处理复杂、相似的部分多、需要更快的响应速度,这种情况下,单页应用更加合适。

那么多页应用呢?

多页应用(MultiPage Application,MPA),它有许多个 html 文件,每次页面跳转的时候,它都会重新从服务器获取一个新的 html,冰加载对应的 js、css。相应的,它的优点是首页加载快,页面切换相对较慢。

它主要应用是一些公司官网、新闻网站。这些网站设计要求高,相似度相对较低,交互少,对首页加载速度要求高,多页应用更加适合于这类场景。

我以前写多页应用,多是用 bootstrap + jquery,写起来并不舒服。 但是随着技术的发展,我们现在完全可以使用 react 来写,然后通过配置 webpack,顺利的写出我们想要的多页应用。

你知道怎么用 react 写多页应用吗?

如果你看过我 webpack 专栏的其他文章,你可能会发现,我已经配置好了一个基础的单页应用。点击此处获取代码

我们就在这个项目的基础上来完成从单页到多页应用的转变吧。

在了解多页应用概念的基础上,我们可以得出,如果想要实现一个多页应用,我们需要达到以下四个要求。

1.多个 html。

2.多个 js。

3.多个 html 能够引入对应的 js。

4.多个 html 能够跳转。

既然是使用多个 html 、多个 js ,那么单页应用的入口文件也不需要了,我们建立如下文件结构:

Snipaste_2022-04-21_15-09-03.png

我新建了 page 文件夹,并在 page 文件夹下新建了 firstPage、home、secondPage,代表每个界面。然后分别建立了如图四个文件。暂时我们用两个文件夹 home、 firstPage 来实现功能。

firstPage 文件结构如下:

index.html

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
    <div id="first_page"></div>
</body>
</html>

index.js


import React from 'react';
import ReactDOM from 'react-dom';
import App from './index.jsx';

ReactDOM.render(<App />,document.querySelector("#first_page"))

index.jsx

import React from 'react';
import Style from './index.less';

export default class Index extends React.Component{
    render(){
        return <div className={Style.first_page}>FIRST-PAGE</div>
    }
}

index.less

.first_page{
    color:blue
}

home 文件 结构如下:

index.html

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
    <div id="home"></div>
</body>
</html>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './index.jsx';

ReactDOM.render(<App />,document.querySelector("#home"))

index.jsx

import React from 'react';
import Style from './index.less';

export default class Index extends React.Component{
    render(){
        return <div className={Style.home}>
            <div><a href = "./firstPage.html">to FirstPage</a></div>
            HOME-1</div>
    }
}

index.less

.home{
    color:rgb(143, 24, 24);
}

这两个文件夹内容大致相似,我在 home/index.jsx 下添加了一个跳转链接,用于测试多页跳转。

将基础的测试文件添加完成后,我们需要修改 webpack 的配置。

打开 webpack.common.js 文件,配置如下:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    mode:'development',
    // entry: ["@babel/polyfill",path.resolve(__dirname,'./src/index.js')],
    entry:{firstPage:'./src/page/firstPage/index.js',home:'./src/page/home/index.js'},
    output:{
        filename: '[name].[chunkhash].js',
        path:path.resolve(__dirname,'./dist'),
    },
    //...
    plugins:[ // 配置插件
        new CleanWebpackPlugin() ,
        new MiniCssExtractPlugin({ // 添加插件
            filename: '[name].[chunkhash].css'
        }),
        new HtmlWebpackPlugin({
            template: './src/page/firstPage/index.html',
            filename:'firstPage.html',// 输出的 html 文件名称
            chunks:['firstPage'],// 引入内容的入口文件,和 entry 的 firstPage 对应。
            scriptLoading: 'blocking',// 加载方式
        }),
        new HtmlWebpackPlugin({
            template: './src/page/home/index.html',
            filename:'index.html',// 设置为默认访问页
            chunks:['home'],
            scriptLoading: 'blocking',
        }),
    ],
   //... 
}

主要修改的点为: 入口文件,以及 new HtmlWebpackPlugin 相关配置。

核心思想很简单,我们通过配置多个 入口文件来获得多个 js 文件,然后将多个 html 文件引入对应的 js 和 css 即可。由于 css 文件是在 js 文件中引入的,所以如果想让 html 文件引入对应的 js,指定对应的入口 js 文件 key 即可。

这个时候我们使用 npm run build 打包一下,可以在 dist 目录中发现如下文件: Snipaste_2022-04-21_15-25-36.png

查看 index.html 及其引入的 css 文件,发现它与 page 文件中的内容一致。

Snipaste_2022-04-21_15-27-09.png

ctrl + C 停掉项目后,我们使用 npm run start 启动项目,得到如下界面:

Snipaste_2022-04-21_15-27-57.png

点击 to FirstPage

Snipaste_2022-04-21_15-29-00.png

可以看到,成功跳转了,和我们写原生的差不多嘛。这个时候,我们就可以愉快的使用 react 开发多页应用了。

让我们再减少一点重复的工作量吧!

我们发现,entry 的配置和 new HtmlWebpackPlugin 的配置实在是太罗嗦了!太多相似点了!

而多个文件引入的路径也是非常相似的,唯一不同的只是它的的文件夹名字不同!

这,有破绽啊!

以下内容参照博客:blog.csdn.net/scorpio_h/a…

npm i glob -D

在根目录下添加 config.js。

const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const setMPA = () =>{
    const entry = {};
    const hemlWebpackPlugins = [];

    const entryFiles = glob.sync("./src/page/**/*.js");// 拿到符合条件的文件路径数组

    console.log('entryFiles',entryFiles);

    Object.keys(entryFiles).map(index=>{
        const entryFile = entryFiles[index];
        const match = entryFile.match(/src\/page\/(.*)\/index\.js/);// 匹配到文件名
        const pageName = match && match[1];

        entry[pageName] = entryFile;

        hemlWebpackPlugins.push(
            new HtmlWebpackPlugin({
                template: `./src/page/${pageName}/index.html`,
                filename:`${pageName === 'home'?'index':pageName}.html`,// 这里让 home.html 输出为 index.html
                chunks:[pageName],
                scriptLoading: 'blocking',
            }),
        )
    })
    console.log(entry,hemlWebpackPlugins)
    return { entry,hemlWebpackPlugins };
}


module.exports = setMPA;

修改 webpack.common.js 如下:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const setMPA = require('./config.js');
const { entry,hemlWebpackPlugins } = setMPA();

module.exports = {
    mode:'development',
    entry:entry,
   //...
    
    plugins:[ // 配置插件
        new CleanWebpackPlugin() ,
        new MiniCssExtractPlugin({ // 添加插件
            filename: '[name].[chunkhash].css'
        }),
        ...hemlWebpackPlugins,
    ],
   //...
}

运行后,发现于之前的代码效果一致! 这个时候,我们只要直接按照固定的文件形式去 page 下新建文件夹,即可对应相应界面,无需额外配置啦。

示例代码

以上配置内容,都已经上传到线上仓库。

https://gitee.com/is-wang-fugui-rich/learn_mpa.git

如果对你有帮助,记得给我点个赞噢~