vite打包构建多页面(纯html)

 vite打包构建多页面(纯html)
分类:前端

项目目录


web/
├── index.html              # 首页入口
├── other.html           		# 其他页面入口(根目录 *.html 均为 MPA 入口)
├── common/                 # 公共 HTML 片段(不参与构建入口扫描)
│   ├── common-link.html    # 公共 CSS / rem 脚本 / CDN
│   ├── common-script.html  # 公共 JS CDN + common.js
│   ├── header.html
│   └── footer.html
├── css/                    # 全局样式(全站复用)
│   ├── global.css          # 样式入口(@import 聚合)
│   ├── base.css            # 重置与基础
│   ├── var.css             # CSS 变量(颜色等)
│   ├── font.css            # @font-face
│   ├── font-size.css       # --font-N 字号变量
│   ├── layout.css          # 响应式 --p-x(按需引入)
│   ├── components.css      # 通用组件(按钮、section 公共块等)
│   ├── header.scss
│   └── footer.scss
├── styles/                 # 页面级 SCSS(按页引入)
│   └── index.scss          # 首页区块样式
├── js/
│   ├── common.js           # 全站公共逻辑(jQuery)
│   └── home.js             # 单页入口(ES Module)
├── assets/
│   ├── images/             # 图片资源
│   └── fonts/              # 字体
├── vite.config.js 					# vite.config
├── package.json 					
└── postcss.config.js 			# postcss

项目依赖 package.json

{
  "name": "web-template",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vite": "^8.0.12",
    "vite-plugin-html-inject": "^1.1.2"
  },
  "devDependencies": {
    "sass-embedded": "^1.99.0",
    "postcss": "^8.5.0",
    "postcss-pxtorem": "^6.1.0"
  }
}

创建公共的header.htmlfooter.html,或者引入其他公共代码html

页面引入

 <!-- 引入公共头部 -->
  <load src="./common/header.html" />
  ...
  <!-- 引入首页底部 -->
  <load src="./common/home-footer.html" />

vite.config.js配置vite-plugin-html-inject处理

import { readdirSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
import htmlInject from 'vite-plugin-html-inject'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
export default defineConfig({
  plugins: [htmlInject()],
  server: {
    // 允许外部访问
    host: '0.0.0.0',
  },
   build: {
    rollupOptions: {
      input:{
         index: 'index.html',
         job: 'job.html',
      },
    },
  },
)}

还可以自定义插件实现自动获取根目录的 .html文件,新的页面不用手动添加


/** 扫描项目根目录下的 *.html 作为 MPA 构建入口 */
function getPageInputs(rootDir) {
  return Object.fromEntries(
    readdirSync(rootDir, { withFileTypes: true })
      .filter((entry) => entry.isFile() && entry.name.endsWith('.html'))
      .map((entry) => [
        path.basename(entry.name, '.html'),
        path.resolve(rootDir, entry.name),
      ])
  )
}


export default defineConfig({
  plugins: [htmlInject()],
  server: {
    // 允许外部访问
    host: '0.0.0.0',
  },
  build: {
    rollupOptions: {
      input: getPageInputs(__dirname),
    },
  },
})

配置输出目录static/

export default defineConfig({
  plugins: [htmlInject()],
  server: {
    // 允许外部访问
    host: '0.0.0.0',
  },
  build: {
    rollupOptions: {
      input: getPageInputs(__dirname),
      output: {
        entryFileNames: 'static/js/[name]-[hash].js',
        chunkFileNames: 'static/js/[name]-[hash].js',
        assetFileNames: (assetInfo) => {
          const name = assetInfo.names[0]
          const ext = path.extname(name ?? '')
          if (ext === '.css') return 'static/css/[name]-[hash][extname]'
          if (/\.(png|jpe?g|gif|svg|webp|ico|avif)$/i.test(ext)) {
            return 'static/images/[name]-[hash][extname]'
          }
          if (/\.(woff2?|eot|ttf|otf)$/i.test(ext)) {
            return 'static/fonts/[name]-[hash][extname]'
          }
          return 'static/assets/[name]-[hash][extname]'
        },
      },
    },
  },
})

通过postcss-pxtorem实现rem响应式方案

安装依赖

pnpm i postcss postcss-pxtorem -D

配置文件postcss.config.js


export default {
  plugins: [
    postcssPxtorem({
      rootValue: 16,//基准换算基数 1rem = 16px
      propList: ['*'],//需要转换 px → rem 的 CSS 属性列表
      selectorBlackList: [],//选择器黑名单:匹配到的 CSS 选择器内 px 不转 rem
      minPixelValue: 2,//最小转换像素值:小于该数值的 px 不做转换
      mediaQuery: false,//@media (max-width: 576px)<==不处理这个px,不是media内部的px
    }),
  ],
}

如何处理media内部的px不转换?

使用Px代替px

使用自定义插件自动转换 px->Px

/** 将 @media 块内声明值中的 `px` 改为 `Px`,postcss-pxtorem 只匹配小写 `px`,故不转换;浏览器仍按 px 解析 */
function skipPxToRemInsideMedia() {
  return {
    postcssPlugin: 'skip-px-to-rem-inside-media',
    AtRule(atRule) {
      if (atRule.name !== 'media') return
      atRule.walkDecls((decl) => {
        if (!decl.value.includes('px')) return
        decl.value = decl.value.replace(/(\d*\.?\d+)px/g, '$1Px')
      })
    },
  }
}
skipPxToRemInsideMedia.postcss = true

export default {
  plugins: [
    skipPxToRemInsideMedia(),
    postcssPxtorem({
      rootValue: 16,
      propList: ['*'],
      selectorBlackList: [],
      minPixelValue: 2,
      mediaQuery: false,
    }),
  ],
}