本文将重点介绍如何使用Webpack捆绑程序组装BEM项目 。 我将展示一个配置示例,其中不会向阅读器加载不必要的实体。
该材料适用于刚开始接触BEM的人员。 首先,我们将探讨方法论的理论方面,在“实践”部分中,我将展示如何应用它们。
一点理论
如果这是您第一次了解BEM,并且想自己了解,请保存文档 。
BEM是一种用于组织任何规模项目的方法。 Yandex开发了它,起初仅在其服务工作中使用它,但后来在公共领域发布。
BEM代表“块,元素,修饰符”。
Block是具有可重用的自治体系结构的实体。 块可以包含其自己的元素。
元素是块的组成部分。 一个项目只能在父块内使用。
修饰符是更改块的显示,状态或行为的实体。
这些组成部分是方法论的基础。 它们提供美观且方便的代码分离。 有关其设备的更多详细信息,请参见文档 。
BEM文档编写广泛。 但是,有一个“但是”:进入材料的门槛很高。 如果可以通过阅读文档的一页来了解布局的基础知识,那么组装项目的问题就更加复杂了。
为什么要组装项目? 在进行大型项目时,每个人都面临组织代码的问题。 将一个大型项目的所有代码存储在一个文件中是不方便的。 将代码拆分为多个文件,然后手动编译也不是一个好方法。 为了解决此问题,使用了收集器或捆绑器,将项目源代码自动转换为准备发送到生产中的代码。
让我提醒您:进一步假设读者具有基本的Webpack技能。 如果您以前从未与他合作过,我建议您先熟悉此工具。
BEM文档为项目的组装提供了建议 。 仅提供两个选项作为示例:使用ENB和Gulp进行组装。
ENB是专门为构建BEM项目而设计的实用程序 。 她能够开箱即用地与BEM实体合作。 但是看看代码。 乍一看,他可以激励一个没有准备的开发人员:
make.jsconst techs = {
来自项目存根公共存储库的代码。
对于刚开始使用BEM的人来说,ENB配置代码显然会很复杂。
该文档包含收集器的现成设置 ,可以使用它们而无需深入研究组件的详细信息。 但是,如果您像我一样,想要对项目在构建过程中发生的事情有一个完整的了解,该怎么办?
BEM文档从理论上很好地解释了组装过程,但是,很少有实际示例,并且它们并不总是适合于清楚地了解该过程。 为了解决这个问题,我将尝试使用Webpack构建一个基本的BEM项目。
练习
在此之前,我提到过代码分离和汇编组织简化了该项目的工作。 在下面的示例中,我们将使用BEM提供代码分离,并使用Webpack进行汇编。
我们想要获得最简单的配置,组装逻辑应该是线性且直观的。 让我们将一个带有一个BEM块的页面放在一起,它将具有两种技术:CSS和JS。
您可以使用带有“ block”类的一个DIV编写HTML代码,并手动连接其所有技术。 使用BEM类命名和相应的文件结构 ,我们不会违反该方法的原理。
我得到以下项目树:
├── desktop
第一行是指“桌面”的替代级别。 在BEM术语中,重定义级别是包含其自己的块实现的目录。 组装项目时,所有重新定义级别中的实现都将按一定顺序放入最终捆绑包中。
例如,我们具有“桌面”的重新定义级别,其中存储了桌面设备的块实现。 如果我们需要用移动设备的布局来补充项目,那么对于我们来说,创建新级别的“移动”重新定义并用相同模块的新实现填充就足够了。 这种方法的方便之处在于,在新的重新定义级别上,我们将不需要复制“桌面”中已经存在的代码,因为它会自动连接。
这是Webpack配置:
在这里,我们将文件/pages/index.js
指定为入口点,添加CSS样式的加载程序,并将/pages/index.html
复制到/dist/index.html
。
index.html <html> <body> <div class="block">Hello, World!</div> <script src="index.js"></script> </body> </html>
block.css .block { color: red; font-size: 24px; text-align: center; }
block.js document.getElementsByClassName('block')[0].innerHTML += " [This text is added by block.js!]"
该示例使用一个覆盖级别和一个块。 任务是汇编页面,以便将我们块的技术(css,js)连接到该页面。
为了连接技术,我们使用require()
:
启动Webpack,看看会发生什么。 从./dist
文件夹中打开index.html
:
块样式已加载,javascript成功运行。 现在我们可以正确地在项目中添加珍贵的字母“ BEM”。
首先,创建BEM是为了处理大型项目。 假设我们的设计师尝试过,现在页面上的字符不是100个,而是100个。 按照前面的场景,我们将使用require()
手动连接每个块的技术。 也就是说,index.js中将至少出现一百行代码。
可以避免的多余代码行是不好的。 未使用的代码更糟。 如果在我们的页面上只有10个可用块,或者20个或53个块,该怎么办? 开发人员将进行其他工作:他将必须专注于页面上使用了哪些块,并手动连接和断开它们,以免最终捆绑包中不必要的代码。
幸运的是,这项工作可以委托给Webpack。
使该过程自动化的最佳动作算法:
- 从现有的HTML代码中选择与BEM命名相对应的类;
- 根据这些类,获取页面上使用的BEM实体列表;
- 检查在重新定义级别上是否有使用过的块,元素和修饰符的目录;
- 通过添加适当的
require()
表达式,将这些实体的技术连接到项目。
首先,我决定检查是否有用于此任务的现成的引导程序。 我没有找到一个可以在一瓶中提供所有必要功能的模块。 但是我遇到了bemdecl-to-fs-loader ,它将BEM声明转换为require()
表达式。 它基于项目文件结构中可用的重新定义级别和技术。
BEM声明 -页面上使用的BEM实体列表。 在文档中阅读有关它们的更多信息。
缺少一个链接-将HTML转换为一组BEM实体。 此任务由html2bemjson模块解决。
bemjson-反映未来页面结构的数据。 通常,bem-xjst模板引擎使用它们来形成页面。 bemjson的语法与声明的语法相似,但是声明仅包含所用实体的列表,而bemjson也反映了它们的顺序。
bemjson不是声明,因此我们首先将其转换为decl格式,以传输到bemdecl-to-fs-loader。 对于此任务,请使用SDK中的模块: bemjson-to-decl 。 由于这些是常规的NodeJS模块,而不是Webpack加载器,因此必须制作包装器加载器。 之后,我们可以使用它们将其转换为Webpack。
我们得到以下引导程序代码:
let html2bemjson = require("html2bemjson"); let bemjson2decl = require("bemjson-to-decl"); module.exports = function( content ){ if (content == null && content == "") callback("html2bemdecl requires a valid HTML."); let callback = this.async(); let bemjson = html2bemjson.convert( content ); let decl = bemjson2decl.convert( bemjson ); console.log(decl);
为了简化引导加载程序的安装并节省将来的时间,我在NPM上下载了该模块。
让我们在项目中安装bootloader并更改Webpack配置:
const webpack = require('webpack'); const path = require('path'); const opy = require('copy-webpack-plugin'); module.exports = { entry: path.resolve(__dirname, "pages", "index.js"), output: { filename: 'index.js', path: path.join(__dirname, 'dist') }, module: { rules: [ { test: /\.html$/, use: [ {
bemdecl-to-fs-loader
引导bemdecl-to-fs-loader
程序levels
参数指定要使用的替代级别以及使用的顺序。 extensions
提供了在我们的项目中使用的文件技术扩展名。
因此,我们只包括一个HTML文件,而不是手动连接技术。 所有必要的转换将自动执行。
让我们用以下行替换index.js的内容:
require('./index.html');
现在运行Webpack。 组装时,将显示以下行:
[ BemEntityName { block: 'block' } ]
这意味着声明的形成是成功的。 我们直接看一下Webpack的输出:
Entrypoint main = index.js [0] ./pages/index.js 24 bytes {0} [built] [1] ./pages/index.html 74 bytes {0} [built] [2] ./desktop/block/block.css 1.07 KiB {0} [built] [3] ./node_modules/css-loader/dist/cjs.js!./desktop/block/block.css 217 bytes {0} [built] [7] ./desktop/block/block.js 93 bytes {0} [built] + 3 hidden modules
我们得到的结果与上一个相同,不同之处在于所有块技术都是自动连接的。 到目前为止,我们已经足够将一个以BEM命名的类添加到HTML,将该HTML与require()
连接require()
并使用连接技术创建适当的目录。
因此,我们拥有一个与BEM方法相对应的文件结构,以及一种自动连接块技术的机制。
从方法的机制和实体中抽象出来,我们创建了一个非常简单但有效的Webpack配置。 我希望这个例子可以帮助每个刚接触BEM的人更好地理解构建BEM项目的基本原理。
有用的链接