多页面打包

2.5k 词
多页面打包

多页面打包#

单页面 :单个html文件 ,切换DOM的方式实现不同业务逻辑展示,后续Vue/React再拓展学习

多页面:多个html文件,切换页面实现不同业务逻辑展示

实例#

需求:把黑马头条-数据管理平台-内容页面-发布页面一起引入打包使用

步骤:

  1. 准备源码(html、css、js)放入相应位置,并改用模块化语法导出
  2. 下载form-serialize包并导入到核心代码中使用
  3. 配置webpack.config.js多入口和多页面的设置
    // ...
    const config = {
        entry: {
            模块名1: path.resolve(__dirname, 'src/入口1.js'),
            模块名2: path.resolve(__dirname, 'src/入口2.js'),
        },
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: './[name]/index.js',
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: './public/页面1.html', // 模板文件
                filename: './路径/index.html', // 输出文件
                chunks: ['模块名1'],
            }),
            new HtmlWebpackPlugin({
                template: './public/页面2.html', // 模板文件
                filename: './路径/index.html', // 输出文件
                chunks: ['模块名2'],
            }),
        ],
    };
  4. 重新打包观察效果

内容管理页#

准备源码

将之前写的项目heima-Toutiao中的文章管理文件即page/content/index.html放入当前项目public目录下,为了避免冲突将其重命名为content.html

content.html头部引入的index.css需要自行处理,暂时取消引用

下方相对路径引入的js文件后续动态注入,先取消引入

将content页的资源文件放入src/content

content页资源request.js已有,将缺少的auth.js放入utils

接下来处理content页的index.js

首先用到了axios,所以需要导入一下(之前是login页的index.js导入了,这里页面不同另外导入)

当然,为了遵循模块化,我们从request.js中导入axios,而不是直接从node_modules中导入

模块化
// content/index.js
import axios from '@/utils/request.js';

接下来看auth.js

其中也用到了axios,那么也导入一下,用到了request.js请求/响应拦截器的功能,所以也从request.js中导入

至于form-serialize插件,当前内容管理页没用到,之后发布文章再使用

注意:没有导出和导入,只是为了让目标js代码被一起打包进来,参与html最后的运行,则直接导入目标文件的路径即可

所以对于content页要用到的auth.js和content/index.css,直接在content/index.js中导入路径

import '@/utils/auth.js';
import './index.css';

设置完成,之后需要在webpack.config.js中配置新的入口

而打包出口就让webpack按照入口的名字分配,用 [name] 占位符即可

至于打包的html和css也要在插件中new新的对象,插件生成css可以使用 [name] 占位符,但是html插件不可以,而是使用chunks来区分要引入哪些打包的模块

const config = {
    // entry: path.resolve(__dirname, 'src/login/index.js'),
    entry: {
        login: path.resolve(__dirname, 'src/login/index.js'),
        content: path.resolve(__dirname, 'src/content/index.js'),
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        // filename: './login/index.js',
        filename: './[name]/index.js',
        clean: true,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'public/login.html'), // 模板文件
            filename: path.resolve(__dirname, 'dist/login/index.html'), // 输出文件
            useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用cdn引入的地址
            chunks: ['login'], // 引入哪些打包后的模块(和entry的key一致)
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'public/content.html'), // 模板文件
            filename: path.resolve(__dirname, 'dist/content/index.html'), // 输出文件
            useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用cdn引入的地址
            chunks: ['content'],
        }),
        new MiniCssExtractPlugin({
            filename: './[name]/index.css',
        }), // 生成css文件
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
        }),
    ],
    // ...
}

最后一步,重新打包观察

运行打包后的content/index.html

未登录时先跳转到了登录页

点击登录后显示成功却未跳转,是因为在登录页的逻辑代码中跳转没加上,现在去加上再重新打包运行

document.querySelector('.btn').addEventListener('click', function () {
    // 验证
    // ...
    ({
    // axios登录请求
    })
    .then(res => {
        myAlert(true, '登录成功');
        // 添加跳转逻辑及设置token
        localStorage.setItem('token', res.data.token);
        location.href = '../content/index.html';
    })
    .catch(err => {
        myAlert(false, err.response.data.message);
    });
    console.log('提交到服务器登录...');
});

重新打包后点击登录就能正常跳转了,页面内容也显示无误

发布文章页#

需求:把发布文章页面一起打包

步骤:

  1. 准备发布文章页面源代码,改写成模块化的导出和导入方式
  2. 修改webpack.config.js的配置,增加一个入口和出口
  3. 打包观察效果

源码准备#

首先准备源码,之前编写的发布页publish中index.html放入当前项目public下并更名为publish.html,另外的资源文件index.css和index.js放入资源目录src/publish下

第一步来处理publish.html

头部的css文件除了我们自己编写的index.css需要引入到入口index.js中处理,其余的直接使用cdn的方式引入即可,所以先取消index.css的引入

底部的js文件:axios、form-serialize、wangeditor三个可以在生产环境下用cdn,开发环境下载本地

其余的以相对路径引入的工具包需要我们自己引入到入口js中处理,所以可以先取消引入

<%if(htmlWebpackPlugin.options.useCdn){%>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.4/axios.min.js"></script>
<script src="http://unpkg.com/form-serialize@0.7.2/index.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/wangeditor5/5.1.23/index.min.js"></script>
<%}%>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/js/bootstrap.min.js"></script>
<script src="../../utils/request.js"></script>
<script src="../../utils/editor.js"></script>
<script src="../../utils/auth.js"></script>
<script src="../../utils/alert.js"></script>
<script src="./index.js"></script>

库下载#

之后来把三个库下载到本地

axios已经下载过了,而对于另外两个:form-serialize和wangeditor,我们不确定它们用npm下载时的名字,可以去 https://www.npmjs.com/ 查找插件的名字进行下载

工具导出导入#

下载完成后往下继续,request.js每个页面都会引入,已经处理好了,跳过

editor.js放入utils,剩下的三个也已经准备好

注意导入时'@'后面不要少'/'

然后对index.js进行处理,首先要导入request.js暴露的axios

// index.js
import axios from '@/utils/request.js';
// ...

在下面的逻辑代码中需要使用wangeditor,所以在editor.js进行处理

// editor.js
// 顶部导入wangeditor
const wangEditor = require('@wangeditor/editor');
// 底部导出要暴露的editor对象
export default editor;

editor.js暴露出editor之后,index.js就可以导入以使用editor

import editor from '@/utils/editor.js';
// ...

auth.js用于判断操作权限,有无token,除了登录页都需要,所以直接在入口index.js最顶部导入即可,无需暴露什么变量

import '@/utils/auth.js';
// ...

alert.js之前使用过,采用了命名导出的方式将myAlert函数导出,入口index.js用命名方式导入即可

import { myAlert } from '@/utils/alert.js';
// ...

index.css直接导入即可

import './index.css';
// ...

补充:还有serialize函数需引入

import serialize from 'form-serialize';
// ...

webpack.config.js配置#

与内容管理页一样,设置入口-出口-插件生成页面

const config = {
    // entry: path.resolve(__dirname, 'src/login/index.js'),
    entry: {
        login: path.resolve(__dirname, 'src/login/index.js'),
        content: path.resolve(__dirname, 'src/content/index.js'),
        publish: path.resolve(__dirname, 'src/publish/index.js'),
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        // filename: './login/index.js',
        filename: './[name]/index.js',
        clean: true,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'public/login.html'), // 模板文件
            filename: path.resolve(__dirname, 'dist/login/index.html'), // 输出文件
            useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用cdn引入的地址
            chunks: ['login'], // 引入哪些打包后的模块(和entry的key一致)
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'public/content.html'), // 模板文件
            filename: path.resolve(__dirname, 'dist/content/index.html'), // 输出文件
            useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用cdn引入的地址
            chunks: ['content'],
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'public/publish.html'), // 模板文件
            filename: path.resolve(__dirname, 'dist/publish/index.html'), // 输出文件
            useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用cdn引入的地址
            chunks: ['publish'],
        }),
        new MiniCssExtractPlugin({
            filename: './[name]/index.css',
        }), // 生成css文件
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
        }),
    ],
    // ...
}

配置完成,打包!

小问题 :在打包时,花费时间长,生产环境无需打包的form-serialize和wangeditor被打包了

这是因为webpack.config.js中的外部扩展我们忘记配置了,现在加上这两项再重新打包

// 生产环境下使用相关的配置
if (process.env.NODE_ENV === 'production') {
    // 外部扩展(让webpack防止import的包被打包进来)
    config.externals = {
        // key:import from 语句后面的字符串
        // value:留在原地的全局变量(最好和cdn在全局暴露的变量一致)
        'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
        axios: 'axios',
        'form-serialize': 'serialize',
        '@wangeditor/editor': 'wangEditor',
    };
}

打包打包看效果!

测试功能完整