npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

fpx-webpack-loader

v1.0.3

Published

viewport(rem、vw适配)

Readme

前言

前面我们分析了webpack,最后还实战了一个vue的项目:

强烈推荐大家去阅读一下前面的文章哈!

今天我们带来点干货,我们利用前面的知识撸一个h5移动端适配的框架,我们取名为"fpx-css-loader"

说到h5移动端适配的,大家都会想到remvw,我们去caniuse看一下这两个方案的兼容性:

rem

在这里插入图片描述

可以看到,绝大多数的浏览器是兼容的,平时项目用它完全是没毛病!

vw

在这里插入图片描述

vm跟rem比就差多了,不过当下大部分手机是可以覆盖的。

思路

  • 我们利用caniuse的数据判断当前项目环境是否支持vw适配,如果支持就用vw适配,不支持就用rem适配
  • webpack插件做rem适配兼容
  • webpak loader做css中的单位转换("fpx"转“vw”、“rem”)

开始

caniuse

/**
 * 判断当前环境是否支持vw适配
 * @returns {boolean}
 */
exports.supportVw = function () {
  //  支持浏览器环境
  const supportList = require('browserslist')(); //获取当前项目的浏览器列表
  // vw所支持的浏览器环境
  const vw = require('caniuse-lite/data/features/viewport-units');//vw在caniuse数据库中的位置
  const unpack = require('caniuse-lite').feature; //caniuse数据库数据解析工具
  // 默认支持
  let support = true;
  function browsersSort(a, b) {
    a = a.split(' ');
    b = b.split(' ');
    if (a[0] > b[0]) {
      return 1;
    }
    if (a[0] < b[0]) {
      return -1;
    }
    return Math.sign(parseFloat(a[1]) - parseFloat(b[1]));
  }
  // 转换caniuse的数据
  function f(data, opts, callback) {
    data = unpack(data);
    if (!callback) {
      [callback, opts] = [opts, {}];
    }
    const need = [];
    // eslint-disable-next-line guard-for-in,no-restricted-syntax
    for (const browser in data.stats) {
      const versions = data.stats[browser];
      // eslint-disable-next-line guard-for-in,no-restricted-syntax
      for (const version in versions) {
        const suppor = versions[version];
        //筛选出不支持的浏览器
        if (suppor === 'n') {
          need.push(`${browser} ${version}`);
        }
      }
    }
    callback(need.sort(browsersSort));
  }
  f(vw, (browsers) => {
    browsers.forEach((item) => {
      //如果当前项目浏览器列表中包含不支持vw的浏览器的时候
      if (supportList.includes(item)) {
        support = false;
      }
    });
  });

  return support;
};

plugin

/**
 * fpx-css-loader webpack插件
 * 自动给入口文件注入flexible.js代码
 */
const { supportVw } = require('./utils');

class FpxWebpackPlugin {
  constructor(options) {
    this.options = options || {};
  }

  apply(compiler) {
    //如果设置了强制使用rem或者不支持vw并且不是强制使用vw的时候,自动注入amfe-flexible/index.min.js做rem适配
    if ((this.options.forceRem || !supportVw()) && !this.options.forceVw) {
      //获取webpack中配置的所有入口
      Object.keys(compiler.options.entry).forEach((key) => {
        if (!(compiler.options.entry[key] instanceof Array)) {
          compiler.options.entry[key] = [compiler.options.entry[key]];
        }
        //给每个入口加上一个“amfe-flexible/index.min.js”文件
        compiler.options.entry[key] = [`!!${require.resolve('amfe-flexible/index.min.js')}`, ...compiler.options.entry[key]];
      });
    }
  }
}

FpxWebpackPlugin.NAME = 'FpxWebpackPlugin';
module.exports = FpxWebpackPlugin;

loader

const loaderUtils = require('loader-utils');
const plugin = require('./plugin');
const webParse = require('./parser/web');

const defaultOpts = { //默认配置
  rootValue: {
    fpx: 750, //ui基准
  },
  forceRem: false, //是否强制使用rem
  forceVw: false, //是否青汁使用vw
  platform: 'web', //平台选择
  unitPrecision: 5, //计算过后的值保留的小数位
};

module.exports = function (source, options) {
  options = { //获取配置的loader参数
    ...defaultOpts,
    ...loaderUtils.getOptions(this) || {},
  };
  if (source) {
    let result;
    switch (options.platform) {
      case 'web':
        result = webParse(source, options); //解析css
        break;
      default:
        result = webParse(source, options);
        break;
    }
    return result;
  }
  return source;
};
module.exports.FoxCssPlugin = plugin;

parse

/**
 * web端fpx单位适配
 */
const postcss = require('postcss');
const px2rem = require('postcss-plugin-px2rem');
const fvw = require('../postcss/fvw');
const { supportVw } = require('../utils');
module.exports = function (source, options) {
  //如果设置了强制使用rem或者不支持vw并且不是强制使用vw的时候,利用postcss的px2rem插件做rem单位转换
  if ((options.forceRem || !supportVw()) && options.forceVw) { 
    return postcss([px2rem({
      ...options,
      rootValue: {
        fpx: options.rootValue.fpx / 10,
      },
    })]).process(source).css;
  }
  //当为vw适配方案的时候,使用自定义postcss插件进行vm单位转换
  return postcss([fvw(options)]).process(source).css;
};

fvw

const postcss = require('postcss');

module.exports = postcss.plugin('postcss-plugin-fvm', (options) => {
  const { unitPrecision, rootValue } = options;
  const pxRegex = /(\d*\.?\d+)fpx/gi;
	//替换fpx为vw单位
  const vwReplace = function (value, $1) {
    // eslint-disable-next-line no-restricted-properties
    const fixed = Math.pow(10, unitPrecision);
    // eslint-disable-next-line no-mixed-operators
    return `${Math.round((parseFloat($1) / (rootValue.fpx / 100)) * fixed) / fixed}vw`;
  };
	//开始遍历csstree
  return function (css) {
    css.walkDecls((decl, i) => {
      // eslint-disable-next-line no-bitwise
      if (~decl.value.indexOf('fpx')) { // 当遍历的css属性值中包换“fpx”的时候进行替换
        const value = decl.value.replace(pxRegex, vwReplace);
        decl.value = value;
      }
    });

    css.walkAtRules('media', (rule) => {
      if (!rule.params.indexOf('fpx')) { // 当遍历的css属性值中包换“fpx”的时候进行替换
        rule.params = rule.params.replace(pxRegex, vwReplace);
      }
    });
  };
});

使用

我们利用vue-cli创建一个简单的vue项目叫fpx-demo:

➜  vue create fpx-demo

安装

➜ npm install  fpx-webpack-loader -D

配置

参数(默认参数)

{ //默认配置
  rootValue: {
    fpx: 750, //ui基准
  },
  forceRem: false, //是否强制使用rem
  forceVw: false, //是否青汁使用vw
  platform: 'web', //平台选择
  unitPrecision: 5, //计算过后的值保留的小数位
};

vue-cli项目

vue.config.js:

module.exports = {
    chainWebpack: config => {
        ["css"].forEach((r) => {
            config.module.rule(r).oneOf('vue').use("fpx-loader").before("postcss-loader").loader(require.resolve("fpx-webpack-loader")).options({ //默认配置
  rootValue: {
    fpx: 750, //ui基准
  },
  forceRem: false, //是否强制使用rem
  forceVw: false, //是否青汁使用vw
  platform: 'web', //平台选择
  unitPrecision: 5, //计算过后的值保留的小数位
});
            config.module.rule(r).oneOf('normal').use("fpx-loader").before("postcss-loader").loader(require.resolve(" fpx-webpack-loader")).options({ //默认配置
  rootValue: {
    fpx: 750, //ui基准
  },
  forceRem: false, //是否强制使用rem
  forceVw: false, //是否青汁使用vw
  platform: 'web', //平台选择
  unitPrecision: 5, //计算过后的值保留的小数位
});;
            config.plugin("fpx-plugin").use(require("fpx-css-loader").FoxCssPlugin, [{}]);
        });
    }
};

普通项目webpack配置

module.exports = {
  ...
    module: {
        rules: [
            {
                test: /\.(sass|scss)$/,
                use: [
                    "style-loader",
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            config: {
                                path: path.resolve(__dirname, "./postcss.config.js")
                            }
                        }
                    },
                    "fpx-webpack-loader", //配置loader,不传就使用默认参数
                    "sass-loader"
                ],
            }
        ]
      ...
    },
    plugins: [
        new (require("fpx-webpack-loader").FoxCssPlugin)(), //配置plugin
    ]
  ...
};

大概是这样,大家具体按照自己项目配置。

css

<template>
  <div id="app">
    <div class="fpx-375">fpx-375</div>
    <div class="fpx-750">fpx-750</div>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'app',
  components: {
    HelloWorld
  }
}
</script>

<style>
  html, body {
    margin: 0;
    padding: 0;
  }

  #app {
    font-size: 24fpx;//使用fpx
    color: white;
  }

  .fpx-375 {
    width: 375fpx;//使用fpx
    height: 100fpx;//使用fpx
    background: red;
  }

  .fpx-750 {
    width: 750fpx; //使用fpx
    height: 100fpx;//使用fpx
    background: green;
  }
</style>

效果

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

项目已上传github:https://github.com/913453448/fpx-css-loader, 欢迎start、欢迎fork!!