webpack-qc-iconfont-plugin

webpack-qc-iconfont-plugin是一个webpack插件,可以轻松地帮你将阿里icon的图标项目下载至本地

开发初衷

  • 之前已经发布过gulp的版本了,但是在webpack流行的时代,我还是觉得webpack插件版还是很有必有的,于是在我加班加点的研究下,我终于实现了webpack插件版
  • 为啥子要加班加点呢,因为我很懒,到目前为止都没有完整的看过官方的文档,所以基础很差,因此连更半夜的才把这个插件完成了,请原谅我的懒,真的,我一看文档就感觉要睡着了,不知道有没有同感的同学

实现原理

  • 鉴于之前gulp版本的,有同学说看不懂,于是乎我决定在该版进行一个原理讲解,大佬勿喷,小女子只是分享下学习心得。

  • 实现这个插件首先你得研究阿里提供的css代码,这里我提供一个供大家伙学习使用的 //at.alicdn.com/t/font_1425510_3v068prmkkw.css

  • 研究这个你会发现,它其实就是一个css文件,保存下来就可以了。因此我们可以发起请求将css文件下载到本地,npm官方提供了相当多请求封装包,什么request/http/download...,你可以随便挑一个你喜欢的。

  • 文件请求成功后,因为我们想要可以自定义前缀,删除部分不需要的代码,这个时候需要用到正则,这里以插件源码来说明:

    let rawData = body;
    if (!isDev) rawData = rawData.replace(new RegExp(urlPrefix, 'g'), fontPath) // 当为生成环境时,将css中的在线url css地址替换为本地字体文件路径 fontPath
    var result = '/* 字体图标,来源路径:"' + url + '"*/ \r\n'; // 添加字体图标来源url注释,以便于快速定位样式来源
    var delUnnecessary = rawData.replace(/\.iconfont[\s\S]*?\}/, ''); // 利用正则删除 .iconfont { ... } 图标初始化代码
    var iconCss = delUnnecessary.match(/\.icon\-[\s\S]*?\}/g); // 利用正则匹配出所有 .icon-XXX { ... } 的图标样式代码
    var handlerData = keepIconFontStyle ? rawData : delUnnecessary; // 根据配置的 keepIconFontStyle 识别是否需要删除 .iconfont { ... } 图标初始化代码
    result += handlerData.replace(/\.icon\-[\s\S]*?\}/g, ''); // 利用正则删除原有的 .icon-XXX { ... } 的图标样式代码 // 在循环匹配出的 iconCss 重新生成正确前缀的 .icon-XXX { ... } 的图标样式代码
    for (var i in iconCss) {
    var item = iconCss[i];
    if (iconPrefix) item = item.replace(/\.icon\-/, iconPrefix);
    result += item + '\r\n';
    } // 最后删除多余的空行
    result = result.replace(/\r{2,}/g, '\r');
    result = result.replace(/\n{2,}/g, '\n');
  • 这样css文件就生成结束了,接下来就根据开发或生产环境决定是否需要下载css中引用的字体图标文件,这里通过对阿里提供的css文件分析,找到字体图标文件路径,如下:

//at.alicdn.com/t/font_1425510_3v068prmkkw.eot
//at.alicdn.com/t/font_1425510_3v068prmkkw.woff
//at.alicdn.com/t/font_1425510_3v068prmkkw.woff2
//at.alicdn.com/t/font_1425510_3v068prmkkw.ttf
//at.alicdn.com/t/font_1425510_3v068prmkkw.svg 与提供的css文件比较:
//at.alicdn.com/t/font_1425510_3v068prmkkw.css
  • 经过对比,发现字体图标文件路径,就是提供的css文件换个扩展名而已,因此,我们可以把需要下载的字体图标扩展名写成一个数组,利用递归请求文件路径下载字体图标文件到本地

  • 最后,webpack版有模板文件的说法,为此需要将我们生成的css文件注入到模板文件中,这里以插件源码讲解:

    if (template) { // 判断 template 配置存在的时候进行注入
    compiler.hooks.emit.tap(pluginName, compilation => { // 注册webpack插件compiler emit hook
    for (var filename in compilation.assets) { // 循环 compilation.assets 准备输出的资源列表
    if (filename === template) { // 找到模板文件
    const htmlData = compilation.assets[filename].source() // 获取模板文件字符串数据
    if (!htmlData) return const headLinkCss = '<link rel="stylesheet" href="./' + cssName + '">' // 生成本地路径的 head link css标签 // 询问该数据中已是否存在head link css标签,这里的判断是由于该插件代码会被先后执行两次,这需要你对webpack的compiler 与 compilation有初步的认识,compiler会在整个webpack生命周期中存在,而compilation是在每次编译时执行,因此前后会执行一次compiler 和 compilation,就是两次,因此未避免向生产环境注入两次head link css,我们需要进行过滤
    const findHeadLinkCss = htmlData.includes(headLinkCss) const htmlArr = htmlData.split('</head>') // 根据 '</head>' 分割数据为数组
    let htmlHeadBefore = htmlArr[0] // 获取第一个数据 if (isDev) {
    // 如果是开发环境,则将生成的css以 '<style> ... </style>' 形式注入到模板文件中,以便开发调试
    const iconCss = compilation.assets[cssName].source()
    if (!iconCss) return
    htmlHeadBefore += '<style>' + iconCss + '</style>'
    } else if (!findHeadLinkCss) {
    // 如果是生成环境,且模板文件中不存在head link css标签,便将前面生成的head link css标签注入到数据中
    htmlHeadBefore += headLinkCss
    } // 最后链接前后代码
    const handledHtml = htmlHeadBefore + '</head>' + htmlArr[1] // 替换掉准备输出的模板资源文件
    compilation.assets[template] = {
    source: function () {
    return handledHtml;
    },
    size: function () {
    return handledHtml.length;
    }
    };
    }
    }
    })
    }
    1. 代码逻辑基本理顺了,下面就是webpack插件一些简单知识了
    • 涉及知识点:

      • ES6 class 构造函数,什么是构造函数这里不多讲,类似于后端类
      • webpack 事件钩子 tapable ,这个看下官方文档,初步认识即可,类似于后端的委托代{过}{滤}理
      • webpack 的 compilercompilation, 这个webpack官方提供的事件,主要基于 tapable 编写
    • 源码解析:
    // 声明插件构造函数
    class WebpackQcIconfontPlugin { // 构造函数本身
    constructor(options) {
    // 用来对传入的options进行处理,统一的处理便于日后的维护,也是你自己后面编写文档是查看options 属性一个非常好的窗口
    this.options = options || {};
    if (!this.options.url) throw new Error('[' + pluginName + '] Missing options url!');
    ... ...
    } // 构造函数的原型函数apply,webpack插件需要
    apply(compiler) {
    // 这里我们获取到 webpack 插件为我们提供的事件对象 compiler // 对即将使用到的options进行获取,我通常习惯将他们重新获取赋值,在这里,而不是直接在代码中使用大量的 options.XXX,原因是当我需要去除或修改一个options属性时我找的难受,其次是我可以清晰知道这个函数使用了那些options属性,我的options属性将影响到哪些函数
    const options = this.options
    const isDev = options.isDev
    const fontExtList = options.fontExtList
    const cssName = options.cssName
    const template = options.template // 注册一个 compiler.hooks.compilation 钩子
    compiler.hooks.compilation.tap(pluginName, compilation => {
    // 执行声明的 IconfontDownloadCss 与 IconfontDownloadFontFile 构造函数,之所以将他们分别构造是为了逻辑清晰,增强代码可读性
    new IconfontDownloadCss().apply(compilation, options);
    if (!isDev && fontExtList && fontExtList.length > 0) new IconfontDownloadFontFile().apply(compilation, options);
    })
    }
    } module.exports = WebpackQcIconfontPlugin;
  1. 这样一个简单的webpack插件基架就完成了,剩下的就是根据我刚才分析写出 IconfontDownloadCssIconfontDownloadFontFile 的逻辑
  2. 最后为了便于其他开发者可以个性化的使用,我们应该为我们的插件提供钩子事件,这里就需要用到 tapable 了,详细的还请小白移步官网查阅,我就不详细解说了,这里简单概述下该插件是如何编写的事件
// 声明一个异步事件,这里因为返回参数一致,所以用了统一的声明方式,简单快速便捷
const asyncHooks = new HookMap(key => new AsyncParallelHook(['result', 'callback']))

IconfontDownloadCss 构造函数中:

// 判断是否存在 iconfontCssCreateEnd 的注册事件
const iconCssCreateEndHooks = asyncHooks.get('iconfontCssCreateEnd') if (iconCssCreateEndHooks) {
// 存在使用 iconCssCreateEndHooks.callAsync 执行注册事件
iconCssCreateEndHooks.callAsync(result, handledData => {
if (!handledData) return callback()
resultHandle(handledData)
})
} else {
// 不存在则执行默认方法
resultHandle(result)
} // 注册事件时使用如下方法,和官网是一致的:
WebpackQcIconfontPlugin.getHooks.for('iconfontCssCreateEnd').tapAsync(pluginName, (result, cb) => {
result += '1111111'
cb(result)
})

IconfontDownloadFontFile 构造函数中:

// 判断是否存在 iconfontFileDownloadEnd 的注册事件
const iconfontFileDownloadEndHooks = asyncHooks.get('iconfontFileDownloadEnd') if (iconfontFileDownloadEndHooks) {
存在使用 iconfontFileDownloadEndHooks.callAsync 执行注册事件
iconfontFileDownloadEndHooks.callAsync(fontFileList, handledData => {
if (!handledData) return callback()
resultHandle(handledData)
})
} else {
// 不存在则执行默认方法
resultHandle(fontFileList)
} // 注册事件时使用如下方法:
WebpackQcIconfontPlugin.getHooks.for('iconfontFileDownloadEnd').tapAsync(pluginName, (fontFileList, cb) => {
const testFile = '测试使用的文件而已'
fontFileList.push({
filename: 'test.text',
data: {
source: function () {
return testFile;
},
size: function () {
return testFile.length;
}
}
})
cb(fontFileList)
})

使用方法

npm install webpack-qc-iconfont-plugin

webpack.config.js 文件中进行调用:

// 引入插件
const WebpackQcIconfontPlugin = require('iconfont-webpack-plugin') module.exports = {
plugins: [
// 插件调用代码
new WebpackQcIconfontPlugin({
url: '//at.alicdn.com/t/font_xxxxxxx_xxxxxx.css',
isDev: true,
fontPath: './iconfont/iconfont',
iconPrefix: '.cu-icon-',
keepIconFontStyle: false,
fontExt: ['.eot', '.ttf', '.svg', '.woff', '.woff2'],
template: 'index.html'
}),
]
};

Options

  • url

    • 类型:String
    • 默认:无,该参数是必须(没有将会报错)
    • 描述:为阿里图标中 - 我的图标项目 - 中获取的css代码url
    • 基础用法:new WebpackQcIconfontPlugin({url: '//at.alicdn.com/t/font_xxxxxxx_xxxxxx.css' })
  • isDev

    • 类型:String,
    • 默认:true
    • 描述:当前是否为开发模式
  • fontPath

    • 类型:String
    • 默认:'./iconfont/iconfont'
    • 描述:下载的字体图标文件保存路径,只有在 isDev 为false,也就是生产环境才有效
  • iconPrefix

    • 类型:String
    • 默认:与源文件保持一致 .icon-
    • 描述:字体图标统一前缀,如设置为 { iconPrefix: '.cu-icon-' },则图标调用为:<i class="iconfont cu-icon-XXX"></i>
  • keepIconFontStyle

    • 类型:Boolean
    • 默认:undefined,即未开启,不保留
    • 描述:是否保留css源文件中的 .iconfont{/*...*/} 中的样式,该属性多用于与vant等类似已有自己字体图标相关初始设置的组件库配合使用,如您没有与类似组件使用,建议开启或自定义一个,否则您的图标将不会有初始样式
  • fontExt

    • 类型:Array
    • 默认:['.eot', '.ttf', '.svg', '.woff', '.woff2'] ,即全部下载
    • 描述:需要下载的字体图标格式扩展名,只有在 isDev 为false时有效
  • template

    • 类型:String
    • 默认:index.html
    • 描述:生成的图标css将自动注入模板文件,图标生成后会根据该配置自动注入到模板文件中,无需手动调用,如不需要自动注入,可以将该值设置为 null
    • 补充:开发模式下会css会以 <style> ... </style> 形式注入,生成模式下会以 <link rel="stylesheet" href="./iconfont.css"> 方式注入

总结

  • 详细的用法与源码在 我的github webpack-qc-iconfont-plugin 中有,有需要小伙伴可以去下载
  • 最后分析下插件的不足之处:
    • 没有实现自动根据webpack mode环境判断生产和开发环境,学艺不精,找了很多文档,仍不知道如何实现,若有大神愿意指点迷津将非常感谢
    • 插件模板文件那一块总感觉有点什么说不上来问题,本来想实现根据webpack的出口配置,自动实现,不需要配置的,但是发现好像理想丰满,现实骨感
  • 我学到的知识
    • 深入了解了 webpack 插件的实现原理,初步掌握了 compilercompilation
    • 半懵半懂的了解了 tapable
    • 看懂了未来前端的前景会越来越好,因为能实现的东西越来越多,查阅资料的同时也重新认识了一次前端,发现很多新东西和自己不曾了解的代码写法
    • 最后祝愿每一位努力奋斗的前端小伙伴们越来越好,在前端这条路上不断奋进和学习,不要轻易放弃哟!

注:此前该文发布与52pojie,由于很多小伙伴反应没有账号看不到文章,故转到我的博客再发一次,鉴于此以后我发文都尽量一式两份~~~~~哈哈哈

作者:leona

原文链接:https://www.cnblogs.com/leona-d/p/12697281.html

版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接

【Web】阿里icon图标webpack插件(webpack-qc-iconfont-plugin)详解的更多相关文章

  1. Spring Boot的Maven插件Spring Boot Maven plugin详解

    Spring Boot的Maven插件(Spring Boot Maven plugin)能够以Maven的方式为应用提供Spring Boot的支持,即为Spring Boot应用提供了执行Mave ...

  2. 前端html、CSS快速编写代码插件-Emmet使用方法技巧详解

    前端html.CSS快速编写代码插件-Emmet使用方法技巧详解   Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生.它使用仿CSS选择器的语法来 ...

  3. 提高Java代码质量的Eclipse插件之Checkstyle的使用详解

    提高Java代码质量的Eclipse插件之Checkstyle的使用详解 CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发人员遵守某些编码规范的工具.它能够自动化代 ...

  4. js插件---videojs中文文档详解

    js插件---videojs中文文档详解 一.总结 一句话总结: js插件网上都有很多参考资料,使用起来也非常简单 二.lavarel中使用实例 <video id="example_ ...

  5. web缓存服务器varnish-4.1.6的部署及配置详解

    web缓存服务器varnish-4.1.6的部署及配置详解 1.安装varnish4.1.6安装依赖 yum install -y autoconf automake jemalloc-devel l ...

  6. Elasticsearch之sense插件安装之后的浏览详解

    前提博客是 Elasticsearch之sense插件的安装(图文详解) 立马,可以看到 http://192.168.80.145:5601/app/sense 以后更新

  7. 使用Navicat或者其他数据库工具连接阿里云EDS(数据库服务器)实例过程详解

    使用Navicat或者其他数据库工具连接阿里云EDS(数据库服务器)实例过程详解 背景:这几天从阿里云上面购买了云服务器,最垃圾的那种,还送oss和EDS数据库服务器,只不过EDS数据库服务器只有一个 ...

  8. 【前端必会】不知道webpack插件? webpack插件源码分析BannerPlugin

    背景 不知道webpack插件是怎么回事,除了官方的文档外,还有一个很直观的方式,就是看源码. 看源码是一个挖宝的行动,也是一次冒险,我们可以找一些代码量不是很大的源码 比如webpack插件,我们就 ...

  9. webpack学习(五)配置详解

    配置详解 //使用插件html-webpack-plugin打包合并html //使用插件extract-text-webpack-plugin打包独立的css //使用UglifyJsPlugin压 ...

随机推荐

  1. BrowserSync(保存代码后,自动刷新浏览器)

    摘要 Browsersync能让浏览器实时.快速响应您的文件更改(html.js.css.sass.less等)并自动刷新页面.更重要的是 Browsersync可以同时在PC.平板.手机等设备下进项 ...

  2. Cinemachine中噪音的应用

    两种默认产生噪音的方式 Nosie阶段的Component   Component在流水线中主要通过MuteCameraState来处理对State的计算.   对于Noise类型的Component ...

  3. typescript package.json vscode 终端 运行任务 Ctrl shift B

    { "dependencies": { "typescript": "^3.6.4" } }

  4. 深入理解计算机系统 (CS:APP) - 高速缓存实验 Cache Lab 解析

    原文地址:https://billc.io/2019/05/csapp-cachelab/ 这个实验是这学期的第四个实验.作为缓存这一章的配套实验,设计得非常精妙.难度上来讲,相比之前的修改现成文件, ...

  5. Linux中更换为国内镜像源

    推荐使用清华镜像:https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/ 将下列文本添加到/etc/apt/sources.list文件里 # 默认注释了源 ...

  6. weblogic漏洞练习

    About WebLogic WebLogic是美商Oracle的主要产品之一,系购并得来.是商业市场上主要的Java(J2EE)应用服务器软件(application server)之一,是世界上第 ...

  7. JDBC(三)----JDBC控制事务

    ##  JDBC控制事务 1.事务:一个包含多个步骤的业务操作.如果这个业务员操作被事务管理,则这多个步骤要么同时成功,要么同时失败. 2.操作: 1.开启事务 2.提交事务 3.回滚事务 3.使用C ...

  8. Check If It Is a Straight Line

    2019-10-21 10:35:33 问题描述: 问题求解: public boolean checkStraightLine(int[][] coordinates) { int n = coor ...

  9. [bfs,深度记录] East Central North America Regional Contest 2016 (ECNA 2016) D Lost in Translation

    Problem D Lost in Translation The word is out that you’ve just finished writing a book entitled How ...

  10. Building Applications with Force.com and VisualForce(Dev401)(七):Designing Applications for Multiple users:Managing your users' experience I

    Dev 401-007 Designing Applications for Multiple users: Managing your users' experience part 1 Module ...