webpack5 + vue3快速开发html构建静态页面项目(快速开发html模板)

本文详细介绍了如何初始化一个Vue项目,配置webpack、安装依赖,创建HTML模板,实现SSR,热更新功能,以及CSS、JS的处理和代码格式化的步骤。
摘要由CSDN通过智能技术生成

demo 目录文件
在这里插入图片描述

1、初始化项目

npm init

2、安装依赖包 (注意只安装到开发环境中,因为依赖不需要在生产环境运行)

安装 webpackwebpack-cliwebpack-dev-serverhtml-webpack-pluginvue(SSR)

npm i webpack -D
npm i webpack-cli -D
npm i webpack-dev-server -D
npm i html-webpack-plugin -D
npm i vue -D

安装成功完成后 package.json 应该多出以下配置(版本号可不同)

"devDependencies": {
   "html-webpack-plugin": "^5.6.0",
   "vue": "^3.4.5",
   "webpack": "^5.89.0",
   "webpack-cli": "^5.1.4",
   "webpack-dev-server": "^4.15.1"
 }

3、新建src目录,并创建 index.html、tag.html

index.html 文件,注意这里用到了 html-webpack-plugin 模板语法、vue 模板语法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>About</title>
</head>
<body>
	<!-- 插入 html-webpack-plugin 模板, 参考文档:https://github.com/jantimon/html-webpack-plugin -->
    <div><%= htmlWebpackPlugin.options.tag %></div>
    <!-- vue 模板语法, 参考文档: https://cn.vuejs.org/ -->
    <p>我是index.html的内容 {{filename}}</p>
</body>
</html>

tag.html 文件,注意这里用到了 vue 的模板语法

<div>我是tag.html的内容 {{filename}}</div>

4、根目录新建 webpack.config.js、HotReload.js、VueLoad.js

webpack.config.js 配置文件

const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');

module.exports = {
    mode: 'development',
    entry: {},
    output: {
        path: path.resolve(__dirname, 'dist'),
        clean: true,
    },
    devServer: {
        static: 'dist',
        hot: true,
        process: false,
    },
    devtool: 'inline-source-map',
    plugins: [
        new HtmlWebpackPlugin({
            title: '首页',
            tag: fs.readFileSync('./src/tag.html', 'utf8'),
            template: './src/index.html',
            filename: 'index.html',
            minify: false,
        }),
        new VueLoad(),
        new HotReload(),
    ],
    optimization: {
        runtimeChunk: 'single',
    },
};

HotReload.js 热更处理文件,由于 webpack 热更监听js注入到 entry 的 js 文件bundle实现的;entry配置为空,所以html文件更新,浏览器无法自动刷新

const HtmlWebpackPlugin = require('html-webpack-plugin');

class HotReload {
    apply(compiler) {
        compiler.hooks.compilation.tap('HotReload', (compilation) => {
            // Static Plugin interface |compilation |HOOK NAME | register listener 
            HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
                'HotReload',
                (data, cb) => {
                    // 如果执行 npm run serve 命令 WebSocket 刷新浏览器监听注入到</body>
                    if (process.env.npm_lifecycle_event == 'serve') {
                        const i = data.html.lastIndexOf('</body>')
                        data.html = data.html.slice(0, i) +
                            '    <script>(function(){new WebSocket(`ws://${window.location.host}/ws`).addEventListener("message", function(event){if(event.data.indexOf("invalid")!= -1){window.location.reload()}})})()</script>\r\n' +
                            data.html.slice(i);
                    }
                    cb(null, data)
                }
            )
        })
    }
}

module.exports = HotReload

VueLoad.js 解析 vue 模板代码

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { createSSRApp } = require('vue');
const { renderToString } = require('vue/server-renderer');

class VueLoad {
    apply(compiler) {
        compiler.hooks.compilation.tap('VueLoad', (compilation) => {
            HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
                'VueLoad',
                async (data, cb) => {
                    // vue解析, 出入 data 数据,在index.html、tag.html中使用
                    data.html = await renderToString(createSSRApp({
                        data: () => ({ filename: data.plugin.options.filename }),
                        template: data.html
                    }))
                    cb(null, data)
                }
            )
        })
    }
}

module.exports = VueLoad

5、相关参考文档 webpackhtml-webpack-pluginvue

到这里就配置完成了,npm run serve 运行一下试试吧!

6、css、img、js 一起拷贝打包目录

npm i copy-webpack-plugin -D

在webpack.config.js配置引入使用

const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');
const CopyPlugin = require('copy-webpack-plugin');

plugins: [
      new HtmlWebpackPlugin({
          title: '首页',
          tag: fs.readFileSync('./src/tag.html', 'utf8'),
          template: './src/index.html',
          filename: 'index.html',
          minify: false,
      }),
      new VueLoad(),
      new HotReload(),
      new CopyPlugin({
	      patterns: [
	        { from: './src/css', to: 'css' },
	        { from: './src/images', to: 'images' },
	        { from: './src/js', to: 'js' },
	      ],
    }),
]

7、prettier 格式化打包后代码

安装 prettier

npm i prettier -D

根目录下创建 prettier.config.js

module.exports = {
  useTabs: false,
  singleQuote: true,
  semi: false,
  printWidth: 200,
  htmlWhitespaceSensitivity: 'ignore', /* <span>、<a>、<b>等行内元素换行处理按钮块执行 */
};

htmlWhitespaceSensitivity 配置后 “行内元素” 默认为 display: block ,特殊代码处理如下:

<!-- input -->
<!-- display: inline -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
<!-- output -->
<span class="dolorum atque aspernatur"
  >Est molestiae sunt facilis qui rem.</span
>

<!-- input -->
<!-- display: block -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
<!-- output -->
<!-- display: block -->
<span class="dolorum atque aspernatur">
  Est molestiae sunt facilis qui rem.
</span>


<!-- 忽略代码块 -->
<!-- input -->
<!-- prettier-ignore-start -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
<!-- prettier-ignore-end -->

<!-- output -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>

根目录下创建 Format.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const prettier = require('prettier');
const config = require('./prettier.config');

/**
 * 代码格式化
 */
class Format {
  apply(compiler) {
    compiler.hooks.compilation.tap('Format', (compilation) => {
      HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync('Format', async (data, cb) => {
        // prettier格式化
        data.html = await prettier.format(data.html, { ...config, parser: 'html' });
        cb(null, data);
      });
    });
  }
}

module.exports = Format;

webpack.config.js 配置

const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');
const CopyPlugin = require('copy-webpack-plugin');
const Format = require('./Format.js');

plugins: [
      new HtmlWebpackPlugin({
          title: '首页',
          tag: fs.readFileSync('./src/tag.html', 'utf8'),
          template: './src/index.html',
          filename: 'index.html',
          minify: false,
      }),
      new VueLoad(),
      new HotReload(),
      new Format(),
      new CopyPlugin({
	      patterns: [
	        { from: './src/css', to: 'css' },
	        { from: './src/images', to: 'images' },
	        { from: './src/js', to: 'js' },
	      ],
    }),
]

npm run build 运行一下试试,看看dist目录html文件被格式化后的效果吧!
如果格式化失败,.gitignore.prettierignore.eslintignore 是否忽略了 dist 目录,请先移除忽略在试试(此处不知道 prettier 为什么不执行 git 忽略项)prettier忽略规则;其他文件需要格式化请参考 prettier 文档

注意:<%= htmlWebpackPlugin.options.tag %> 因为不属于html规范,格式化出现缩进问题,可以加入prettier忽略代码块,如下:

<!-- prettier-ignore-start -->
<%= htmlWebpackPlugin.options.tag %>
<!-- prettier-ignore-end -->

8、打包后vue中对v-if产生的 <!----> 空注释,及 prettier-ignore 注释

可在 VueLoad.js 中加入一下正则去除

class VueLoad {
    apply(compiler) {
        compiler.hooks.compilation.tap('VueLoad', (compilation) => {
            HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
                'VueLoad',
                async (data, cb) => {
                    // vue解析, 出入 data 数据,在index.html、tag.html中使用
                    data.html = await renderToString(createSSRApp({
                        data: () => ({ filename: data.plugin.options.filename }),
                        template: data.html
                    }))
                    // 去掉多余注释信息
			        data.html = html.replace(/<!-- prettier-ignore-start -->|<!-- prettier-ignore-end -->|<!---->/g, '')
                    cb(null, data)
                }
            )
        })
    }
}

npm run build 运行一下再试试

9、遍历所有html模板和页面

创建模板存放目录 src/template,把 tag.html 放入template目录中
根目录下创建 BuildHtml.js

const fs = require('fs');
const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 获取所有模板
function getTemplate() {
  const files = glob.sync('src/template/**.html');
  const templates = {};
  files.forEach(async (filePath) => {
    const arr = filePath.split('/');
    const templateName = arr[arr.length - 1];
    const name = templateName.split('.')[0];
    templates[name] = fs.readFileSync(filePath, 'utf8');
  });
  return templates;
}

// 构建所有页面
function BuildHtml() {
  const files = glob.sync('src/**.html');
  const htmlWebpack = [];
  const templates = getTemplate();

  files.forEach((filePath, index) => {
    const arr = filePath.split('/');
    const fileName = arr[arr.length - 1];
    let main = fs.readFileSync(filePath, 'utf8');
    htmlWebpack.push(
      new HtmlWebpackPlugin({
        inject: false,
        tags: templates,
        filename: fileName,
        template: filePath,
        minify: false,
      })
    )
  })
  return htmlWebpack;
}

module.exports = BuildHtml;

webpack.config.js 配置

const VueLoad = require('./VueLoad.js');
const HotReload = require('./HotReload.js');
const CopyPlugin = require('copy-webpack-plugin');
const Format = require('./Format.js');
const BuildHtml = require('./BuildHtml.js');

plugins: [
      ...BuildHtml(),
      new VueLoad(),
      new HotReload(),
      new Format(),
      new CopyPlugin({
	      patterns: [
	        { from: './src/css', to: 'css' },
	        { from: './src/images', to: 'images' },
	        { from: './src/js', to: 'js' },
	      ],
    }),
]

使用模板

<div><%= htmlWebpackPlugin.options.tags.tag %></div>

此文章属于个人使用整理,有坑请大佬勿喷。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值