骑猪o0o找牛
骑猪o0o找牛
  • 发布:2026-01-27 10:28
  • 更新:2026-01-27 11:55
  • 阅读:22

h5端尺寸转换逻辑

分类:uni-app

vue3 + vite

vue3 下 , h5使用rpx 时, 会 编译成 rem

注册 postcss 的 plugin

在/node_modules/@dcloudio/uni-h5-vite/dist/plugin/config.js,
代码是, 用的 是config 这个钩子

 css: {  
    postcss: {  
        plugins: (0, uni_cli_shared_1.initPostcssPlugin)({  
            uniApp: (0, uni_cli_shared_1.parseRpx2UnitOnce)(inputDir, process.env.UNI_PLATFORM),  
        }),  
    },  
},

parseRpx2UnitOnce , 选定使用的单位

exports.parseRpx2UnitOnce = (0, uni_shared_1.once)((inputDir, platform = 'h5') => {  
    // 如果是  h5/app/鸿蒙 会使用 defaultRpx2Unit  
    const rpx2unit = platform === 'h5' || platform === 'app' || platform === 'app-harmony'  
        ? uni_shared_1.defaultRpx2Unit  
        : uni_shared_1.defaultMiniProgramRpx2Unit;  
    const manifestJson = (0, exports.parseManifestJsonOnce)(inputDir);  
    let platformOptions = getPlatformManifestJson(manifestJson, platform);  
    if (platformOptions && platformOptions.rpx) {  
        return (0, shared_1.extend)({}, rpx2unit, platformOptions);  
    }  
    return (0, shared_1.extend)({}, rpx2unit);  
});

都有啥呢 ?

const defaultRpx2Unit = {  
    unit: 'rem',  
    unitRatio: 10 / 320,  
    unitPrecision: 5,  
};  
const defaultMiniProgramRpx2Unit = {  
    unit: 'rpx',  
    unitRatio: 1,  
    unitPrecision: 1,  
};

但是 注意这里 let platformOptions = getPlatformManifestJson(manifestJson, platform);
还是 会从 mainfest中 获取 配置的, 如果有 , 就用 mainfest 中的,
也就是 你可以 在 manifest.json的 h5节点下 , 覆盖 一些 配置 , 例如

    "h5": {  
        "router": {  
            "mode": "history"  
        },  
        "unit": "px",  
        "unitRatio": 0.5,  
        "unitPrecision": 2,  
    }

但是 文档里 , 我没有 找到

哪里 修改的 rpx 2 rem 呢?

在这里 /node_modules/@dcloudio/uni-cli-shared/dist/postcss/plugins/uniapp.js

const uniapp = (opts) => {  
    const platform = process.env.UNI_PLATFORM;  
    const { unit, unitRatio, unitPrecision } = (0, shared_1.extend)({}, defaultUniAppCssProcessorOptions, opts);  
    const rpx2unit = (0, uni_shared_1.createRpx2Unit)(unit, unitRatio, unitPrecision);  
    return {  
        postcssPlugin: 'uni-app',  
        prepare() {  
            return {  
                OnceExit(root) {  
                    root.walkDecls(walkDecls(rpx2unit));  
                    const rewriteTag = transforms[platform];  
                    filterPrefersColorScheme(root);  
                    if (rewriteTag) {  
                        root.walkRules(walkRules({  
                            rewriteTag,  
                        }));  
                    }  
                },  
            };  
        },  
    };  
};

就是 遍历 然后 替换

function walkDecls(rpx2unit) {  
    return (decl) => {  
        const { value } = decl;  
        if (value.indexOf('rpx') === -1 && value.indexOf('upx') === -1) {  
            return;  
        }  
        // 如果有 rpx 或者upx, 就调用rpx2unit  
        decl.value = rpx2unit(decl.value);  
    };  
}
const unitRE = new RegExp(`"[^"]+"|'[^']+'|url\\([^)]+\\)|(\\d*\\.?\\d+)[r|u]px`, 'g');  

// 这里的  参数 就是  defaultRpx2Unit的三个参数  
function createRpx2Unit(unit, unitRatio, unitPrecision) {  
    // ignore: rpxCalcIncludeWidth  
    return (val) => val.replace(unitRE, (m, $1) => {  
        if (!$1) {  
            return m;  
        }  
        if (unitRatio === 1) {  
            return `${$1}${unit}`;  
        }  
        // 替换 为 rem  
        // 就是  * unitRatio, 然后 保留unitPrecision 位的小数  
        const value = toFixed(parseFloat($1) * unitRatio, unitPrecision);  
        return value === 0 ? '0' : `${value}${unit}`;  
    });  
}

我是 分割线----------------------------------------------------------------------------------------------------------------------------------------------------------------

vue2 + webpack

再 说一下 vue2的 处理, vue2 是 依托于 vue-cli-service

查看打包结果

写一个 最简单的 项目,
查看 打包后的 结果

 ___CSS_LOADER_EXPORT___.push([  
            module.id,  
            '@charset "UTF-8";\n/**\n  */.ggdxd[data-v-4551d3b2]{height:%?600?%;background-color:pink}',  
            "",  
        ]);

是 内联css ,
而 可以 修改么? 可以 在 vue.config.js中 设置css.extract么?

不可以

查看代码, uni 是 使用 自定义 的 vue-cli-service 插件,
/node_modules/@dcloudio/vue-cli-plugin-uni/index.js

h5 强制要 内联css...

if (process.env.UNI_PLATFORM === 'h5' || process.env.UNI_USING_V3) {  
    options.css.extract = false  
} else {  
    options.css.extract = true  
}

height:%?600?%

在 cli 项目中, 根目录下会有postcss.config.js
实际上 使用了 uni的 一个自定义 插件require('@dcloudio/vue-cli-plugin-uni/packages/postcss')
在 parseWord方法中

if (process.env.UNI_PLATFORM === 'h5') {  
    if (u === 'upx' || u === 'rpx') {  
          // 这里 变成了很奇怪的东西  
      node.value = `%?${num}?%`  
    }  
}

也就是 在编译的产物中, rpx/upx 都转成了 %?${num}?%的 格式

啥时候 转成 px 哦?

是 运行时!

运行时的 转换代码 是哪里来的?

是 通过 webpack的 loader 处理的

我写了一个 webpack 插件, 获取了 resolve 之后的 样式使用的loader

[  
  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/loaders/pitcher.js",  

  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue-style-loader/index.js",  

  "/node_modules/@vue/cli-service/node_modules/css-loader/dist/cjs.js",  
  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/index.js",  
  "/node_modules/postcss-loader/dist/cjs.js",  
  "/node_modules/postcss-loader/dist/cjs.js",  
  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/sass-loader/dist/cjs.js",  
  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/index.js",  
  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/index.js",  
  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-scoped-loader/index.js",  
  "/node_modules/@dcloudio/vue-cli-plugin-uni/packages/wrap-loader/index.js",  
]

为什么会使用这个 h5-vue-style-loader?

用了 alias

/node_modules/@dcloudio/vue-cli-plugin-uni/lib/h5/index.js文件中配置了webpack

resolveLoader: {  
    alias: {  
      'vue-style-loader': resolve('packages/h5-vue-style-loader')  
    }  
  }

在 h5-vue-style-loader中 处理 css模块时,
编译源码是

var code = [  
  '// add the styles to the DOM',  
  'var add = require(' + addStylesClientPath + ').default',  
  'var update = add(' + id + ', content, ' + isProduction + ', ' + JSON.stringify(options) + ');'  
]

动态 插入了 add/update 方法
执行后的返回示例:

// style-loader: Adds some css to the DOM by adding a <style> tag  

// load the styles  
var content = require("........!../node_modules/@dcloudio/vue-cli-plugin-uni/packages/wrap-loader/index.js??clonedRuleSet-45[0].rules[0].use!./App.vue?vue&type=style&index=0&lang=scss&");  

if(content.__esModule) content = content.default;  

if(typeof content === 'string') content = [[module.id, content, '']];  
if(content.locals) module.exports = content.locals;  

// add the styles to the DOM  
var add = require("!../node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue-style-loader/lib/addStylesClient.js").default  
var update = add("43b7a677", content, true, {"sourceMap":false,"shadowMode":false});

也就是 这个 css 模块 变成了 上面的样子

运行时呢?

css模块 代码 注入了
var add = require("!../node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue-style-loader/lib/addStylesClient.js").default

源码是:

var UPX_RE = /%\?([+-]?\d+(\.\d+)?)\?%/g;  
function processCss(css) {  
    var page = getPage();  
    if (typeof uni !== "undefined" && !uni.canIUse("css.var")) {  
        //不支持 css 变量  
        var offset = getWindowOffset();  
        css = css  
            .replace(VAR_STATUS_BAR_HEIGHT, "0px")  
            .replace(VAR_WINDOW_TOP, offset.top + "px")  
            .replace(VAR_WINDOW_BOTTOM, offset.bottom + "px")  
            .replace(VAR_WINDOW_LEFT, "0px")  
            .replace(VAR_WINDOW_RIGHT, "0px");  
    }  
    return css  
        .replace(BODY_SCOPED_RE, page)  
        .replace(BODY_RE, "")  
        .replace(PAGE_SCOPED_RE, "body." + page + " uni-page-body")  
        .replace(/\{[\s\S]+?\}|@media.+?\{/g, function (css) {  
            if (typeof uni === "undefined") {  
                return css;  
            }  
            // 这里 把  %?数值?% 格式的  数字样式 使用 upx2px 计算成px  
            return css.replace(UPX_RE, function (a, b) {  
                return uni.upx2px(b) + "px";  
            });  
        });  
}
0 关注 分享

要回复文章请先登录注册

r***@chn-das.com

r***@chn-das.com

mark
2026-01-27 11:55