前言

最近有一个想法,就是在使用rem适配大小的时候,往往我们会引入一个用于设置根元素大小的js脚本文件,但是对于这个脚本的处理根据不同情况会有不同的调用方式。

以插件:amfe-flexible为例:

比如我们是一个纯spa项目,一般就会在main.ts中import引入这个脚本:

import "amfe-flexible";

一般来说,这种做法没什么问题,因为spa的html渲染是js操作的,我们在渲染之前就引入了这个脚本,所以体验上会先通过amfe-flexible设置根元素大小,再进行html渲染,用户体验良好。

随着业务的变化,我们可能会有SSR的需求,首屏loading的需求,这种情况下都会存在非js渲染的html结构,这样用户一进来就能看到一些内容,但是我们使用了rem适配,如果此时amfe-flexible还在main文件中引入,就会导致用户进来后,内容会发生大小变化,其原因是:

一开始amfe-flexible没有被加载,rem的大小适配都是基于默认根元素html字体大小16px来展示的,当js脚本加载完成,根元素被设置为屏幕宽度/10的字体大小,此时元素重新计算大小重绘。

解决这个问题的做法就是将脚本直接丢在index.html的head中引入,不再main中引入。

<script src="/js/amfe-flexible.min.js"></script>

这种做法最简单,也很方便,但是我又不希望多一个脚本请求,这个脚本文件本身也不大,于是我想到把他做成内联的形式:

<script>
  ...
</script>

但是这就导致出现了一个很难受的问题,在开发阶段,Prettier 会自动格式化代码,导致index.html变得非常的长,非常不利于开发阅读,于是我萌生了一个想法,能不能让他在编译的时候将<script src="/js/amfe-flexible.min.js"></script>转换成内联的形式。

于是查了好多文档,都没有现成的插件,要么就是太偏的,不敢用,于是就自己写了一个vite插件来实现这个效果。

插件实现

编写一个脚本文件:_vite-plugins/vite-plugin-inline-js.ts_

内容如下:

import { readFileSync } from "fs";
import { resolve } from "path";
import type { Plugin } from "vite";

export default function vitePluginInlineJs() {
    /** 项目路径 */
    let rootPath: string = "";

    const plugin: Plugin = {
        enforce: "pre",
        name: "vite-plugin-inline-js",
        configResolved(config) {
            rootPath = config.root;
        },
        transformIndexHtml(html: string) {
            const reg = /<script src=".*" mode="inline"><\/script>/gi;
            const matchResult = html.match(reg);
            if (!matchResult) return html;
            matchResult.forEach((item) => {
                const itemMatchResult = item.match(/src="(.*?)"/);
                if (!itemMatchResult) return;
                let src = itemMatchResult[1];
                if (!src || src.startsWith("http")) return;
                if (src.startsWith("/")) src = src.slice(1);
                const pathPrefix = resolve(rootPath, "public");
                const srcPath = resolve(pathPrefix, src);
                const content = readFileSync(srcPath, "utf-8");
                html = html.replace(item, `<script>${content}</script>`);
            });
            return html;
        }
    };

    return plugin;
}

然后再vite中引入并使用

vite.config.ts

import vitePluginInlineJs from "./vite-plugins/vite-plugin-inline-js";

export default { 
  plugins: [
    vitePluginInlineJs()
  ] 
}

然后再index.html中根据格式要求引入脚本:

<script src="/js/amfe-flexible.min.js" mode="inline"></script>

保存后效果如图:

插件会去匹配mode="inline"的script标签,然后通过fs同步读取到项目public中的对应脚本文件,然后替换成内联的script标签。

当然这是一个很基础的插件,有想法的可以自己去改改。

分类: vue 项目实战 标签: vitepluginvite插件内联脚本

评论

暂无评论数据

暂无评论数据

目录