前端多页面打包通用方案

前端多页面打包通用方案

一、多页面应用(MPA)概念

每⼀次⻚⾯跳转的时候,后台服务器都会给返回⼀个新的html⽂档,
这种类型的⽹站也就是多⻚⽹站,也叫做多⻚应⽤。

二、多⻚⾯打包基本思路

每个⻚⾯对应⼀个 entry,⼀个 html-webpack-plugin

module.exports = {
entry: {
index: './src/index.js',
search: './src/search.js ‘ }
};

缺点:每次新增或删除⻚⾯需要改 webpack 配置

三、多⻚⾯打包通⽤⽅案

动态获取 entry 和设置 html-webpack-plugin 数量
利⽤ glob.sync

// 安装
 npm i glob -D

 // 使用
entry: glob.sync(path.join(__dirname, './src/*/index.js'))

四、多⻚⾯打包实操

1.划分好目录
src目录下建立页面模块名称,页面目录下建立同名称子文件index.htmlindex.js(方便查找)
在这里插入图片描述

2.安装并引入globHtmlWebpackPlugin

// 安装
 npm i glob -D
 npm i html-webpack-plugin -D

 // 引入
const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');

3.编写设置多页面函数(在webpack.js文件中)

const setMPA = () => {
    const entry = {};
    const htmlWebpackPlugins = [];
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'));
    Object.keys(entryFiles)
        .map((index) => {
            const entryFile = entryFiles[index];
            const match = entryFile.match(/src\/(.*)\/index\.js/);
            const pageName = match && match[1];
            entry[pageName] = entryFiles;
            htmlWebpackPlugins.push(
                new HtmlWebpackPlugin({
                    template: path.join(__dirname, `src/${pageName}/index.html`),
                    filename: `${pageName}.html`,
                    chunks: [pageName],
                    inject: true,
                    minify: {
                        html5: true,
                        collapseWhitespace: true,
                        preserveLineBreaks: false,
                        minifyCSS: true,
                        minifyJS: true,
                        removeComments: false
                    } 
                }),
            )
        })
    return {
        entry,
        htmlWebpackPlugins
    }
}

4.使用多页面参数及配置入口参数

// 获取参数
const { entry, htmlWebpackPlugins } = setMPA();
// 配置入口参数
module.exports = {
   // 多页配置
    entry: entry,
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]_[chunkhash:8].js'
    },
}
// 使用htmlWebpackPlugins插件
 plugins: [
  new MiniCssExtractPlugin({
      filename: '[name]_[contenthash:8].css'
  }),
  new CleanWebpackPlugin(),
  new HTMLInlineCSSWebpackPlugin(),
].concat(htmlWebpackPlugins) // 把获取到的插件加进来

五、项目中的多页面方案

const glob = require('glob')
const HtmlWebpackInlinePlugin = require('html-webpack-inline-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const titles = require('./titles.js')
const OfflinePackagePlugin = require('@vivo/offline-pkg')

// 取得相应的页面路径,因为之前的配置,所以是 src 文件夹下的 pages 文件夹
const isProd = process.env.NODE_ENV === 'production'
const isTest = process.env.NODE_ENV === 'test'
const isAnalyz = process.env.analyz === 'true'

// 取所有入口文件
let entryFiles = glob.sync('./src/views/**/main.js')
let pages = {}
entryFiles.forEach(filePath => {
  let template = filePath.substring(0, filePath.lastIndexOf('/'))
  let filename = template.replace('./src/views/', '')
  let conf = {
    // page 的入口
    entry: filePath,
    // 模板来源
    template: template + '/index.html',
    // 在 dist的输出
    filename: filename + '.html',
    title: titles[filename],
    // 页面模板需要加对应的js脚本,如果不加这行则每个页面都会引入所有的js脚本
    chunks: ['vendors', 'common', filename],
    inject: true,
    // 自定义版本
    version: '0.0.2',
    env: isProd, // 环境
    dnsPrefetch: ['//topicstatic.vivo.com.cn/', '//mstatic.vivojrkj.com/'],
    cdn: {
      js: [
        '//wwwstatic.vivojrkj.com/assets/__cdn/js/vue/v2.6.10/vue.runtime.min.js',
        '//wwwstatic.vivojrkj.com/assets/__cdn/js/axios/v0.18.0/axios.min.js',
        '//wwwstatic.vivojrkj.com/assets/__cdn/js/jsencrypt/v3.0.0/jsencrypt.min.js',
      ],
    },
  }
  pages[filename] = conf
})

module.exports = {
  pages: pages,
  // 是否生成sourcemap文件,生成环境不生成以加速生产环境构建
  productionSourceMap: !isProd,
  // 静态资源文件的目录
  assetsDir: 'static',
  publicPath: process.env.VUE_APP_PUBLIC_PATH,
  // css是否开启sourcemap,生成环境不生成
  css: {
    sourceMap: !isProd,
  },
  devServer: {
    open: true,
    index: '/',
    before: app => {
      app.get('/', (req, res, next) => {
        for (let i in pages) {
          res.write(`<a target="_self" href='${i}.html'>/${i}</a></br>`)
        }
        res.end()
      })
    },
    proxy: {
      '/': {
        target: 'http://vivopay.vivo.com.cn/',
        changeOrigin: true,
        pathRewrite: {
          '^': '',
        }
      },
      '/api/subscription/': {
        target: 'http://vivopay.vivo.com.cn/',
        changeOrigin: true,
        pathRewrite: {
          '^': '',
        }
      },
    }
  },
  configureWebpack: config => {
    config.resolve.extensions = ['.js', '.vue', '.json']
    config.plugins.push(new HtmlWebpackInlinePlugin())
    config.plugins.push(
      new HtmlWebpackPlugin({
        minify: {
          removeComments: true,
        },
      })
    )
    if (isProd) {
      config.plugins.push(
        new OfflinePackagePlugin({
          htmlUrl: 'https://h5.vivo.com.cn/wallet/member/',
          baseUrl: 'https://h5.vivo.com.cn/wallet/member/',
          fileTypes: ['html', 'js', 'css', 'png'],
        })
      )
    }
    if (isTest) {
      config.plugins.push(
        new OfflinePackagePlugin({
          htmlUrl: 'https://h5-inside.vivo.com.cn/wallet/member/',
          baseUrl: 'https://h5-inside.vivo.com.cn/wallet/member/',
          fileTypes: ['html', 'js', 'css', 'png'],
        })
      )
    }
    if (isAnalyz) {
      config.plugins.push(
        new BundleAnalyzerPlugin({
          analyzerMode: 'server',
          analyzerHost: '127.0.0.1',
          analyzerPort: 8888,
          reportFilename: 'report.html',
          defaultSizes: 'parsed',
          openAnalyzer: true,
          generateStatsFile: false,
          statsFilename: 'stats.json',
          statsOptions: null,
          logLevel: 'info',
        })
      )
    }
  },
  chainWebpack: config => {
    // 压缩代码
    config.optimization.minimize(true)
    // 分割代码
    config.optimization.splitChunks({
      cacheGroups: {
        vendors: {
          chunks: 'all',
          test: /(core-js)/,
          priority: 100,
          name: 'vendors',
        },
        'async-commons': {
          chunks: 'async',
          minChunks: 2,
          name: 'async-commons',
          priority: 90,
        },
        common: {
          chunks: 'all',
          minChunks: 2,
          name: 'common',
          priority: 80,
        },
      },
    })

    if (process.env.NODE_ENV === 'development') {
      config.output.filename('[name].[hash].js').end();
    }
    // 删除preload
    Object.keys(pages).forEach(key => {
      config.plugins.delete(`preload-${key}`)
    })
    // 用cdn方式引入
    config.externals({
      vue: 'Vue',
      axios: 'axios',
      jsencrypt: 'JSEncrypt',
    })
  },
}
// 引用 npm html-webpack-inline-plugin包,用来将html中inline标识的<script>,<link>,<img>标签的元素内容压缩进html中
const path = require('path');
const HtmlWebpackInlinePlugin = require('html-webpack-inline-plugin');

const pluginOptions = {
    // 项目名,定义成我们在云平台申请的应用名,类似 ***.vivo.com.cn
    projectName: 'vivopay.vivo.com.cn'
};
const isProd = process.env.NODE_ENV === 'production';

function resolve(dir) {
    return path.join(__dirname, dir);
}

module.exports = {
    pages: {
        index: {
            entry: 'src/pages/index/main.js',
            template: 'public/index.html',
            filename: 'index.html',
            chunks: ['libs', 'index']
        },
        bankCard: {
            entry: 'src/pages/bankCard/main.js',
            template: 'public/bankCard.html',
            filename: 'bankCard.html',
            chunks: ['libs', 'bankCard']
        }
    },
    pluginOptions,
    lintOnSave: false,
    // 是否生成sourcemap文件,生成环境不生成以加速生产环境构建
    productionSourceMap: !isProd,
    // 静态资源文件的目录
    assetsDir: 'static',
    publicPath: process.env.VUE_APP_PUBLIC_PATH,
    // css是否开启sourcemap,生成环境不生成
    css: {
        sourceMap: !isProd
    },
    devServer: {
        open: true,
        port: 8200,
        disableHostCheck: true,
        proxy: {
            '/h5': {
                target: process.env.VUE_APP_API_URL,
                pathRewrite: { '^/proxy': '' }
            },
            '/user_center': {
                target: process.env.VUE_APP_API_URL2,
                pathRewrite: { '^/proxy': '' }
            },
            '/security_center': {
                target: process.env.VUE_APP_API_URL2,
                pathRewrite: { '^/proxy': '' }
            },
            '/api': {
                target: process.env.VUE_APP_API_URL2,
                pathRewrite: { '^/proxy': '' }
            }
        },
        historyApiFallback: {
            rewrites: [
                { from: /^\/bankCard/, to: '/bankCard.html' }
            ]
        }
    },
    configureWebpack: config => {
        config.plugins.push(
            new HtmlWebpackInlinePlugin()
        );
        if (process.env.VUE_APP_ENV === 'prod') {
            config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true;
        }
    },
    chainWebpack: config => {
        // 压缩代码
        config.optimization.minimize(true);
        // 分割代码
        config.optimization.splitChunks({
            cacheGroups: {
                libs: {
                    name: 'libs',
                    test: /[\\/]node_modules[\\/]/,
                    chunks: 'initial',
                    priority: 10
                }
            }
        });
        // 用cdn方式引入
        config.externals({
            vue: 'Vue',
            'vue-router': 'VueRouter',
            axios: 'axios'
        });
        // alias
        config.resolve.alias
            .set('@', resolve('src'));
        // 设置全局 less 变量
        const oneOfsMap = config.module.rule('less').oneOfs.store;
        oneOfsMap.forEach(item => {
            item
                .use('sass-resources-loader')
                .loader('sass-resources-loader')
                .options({
                    resources: ['./src/style/vars.less']
                })
                .end();
        });
    },
    // 很重要,默认不会babel nodemodules
    transpileDependencies: ['@vivo/buffett', 'asn1.js']
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值