构建工具是如何用 node 操作 html/js/css/md 文件的

从本质上来说,html/js/css/md ... 源代码文件都是文本文件,文本文件的内容都是字符串,对文本文件的操作其实就是对字符串的操作。

操作源代码的方式又主要分成两种:

  1. 当作字符串,进行增、删、改等操作
  2. 按照某种语法、规则,把字符串读取成一个对象,然后对这个对象进行操作,最后导出新的字符串

1. 操作 html 文件

html 的语法比较简单,并且一般操作 html 都是插入、替换、模板引擎渲染等在字符串上的操作,所以使用第一种方式的比较多。

比如:

一般以第二种方式来操作 html 的都是将 html 文本解析成 dom 树对象,然后进行 dom 操作,最后再导出成新的代码文本。

比如:

cheerio 为例,操作 html 文本:

cheerio 能够加载一个 html 文本,实例化一个类 jQuery 对象,然后使用 jQueryapi 像操作 dom 一样操作这段文本,最后导出新的 html 文本。


const cheerio = require('cheerio');
const $ = cheerio.load('<h2 class="title">Hello world</h2>'); // 加载一个 html 文本 $('h2.title').text('Hello there!');
$('h2').addClass('welcome'); $.html(); // 导出新的 html 文本
//=> <h2 class="title welcome">Hello there!</h2>

jsdom 为例,操作 html 文本:

jsdom 是用 js 将一个 html 文本解析为一个 dom 对象,并实现了一系列 web 标准,特别是 WHATWG 组织制定的 DOMHTML 标准。


const jsdom = require("jsdom");
const { JSDOM } = jsdom; const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
console.log(dom.window.document.querySelector("p").textContent); // "Hello world"

2. 操作 js 文件

因为 js 语法比较复杂,仅仅是如字符串一样进行增删改,只能做一些小的操作,意义不大。所以,一般操作 js 文件都是采用的第二种方式。

在第二种方式中,一般是工具将 js 文本解析成抽象语法树(AST,Abstract Syntax Tree抽象语法树),然后对这棵语法树以面向对象的方式做增删改等操作,最后再导出成新的代码文本。

生成抽象语法树的工具主要有:

acorn 为例,将 1 + 1 片段进行解析:


const acorn = require('acorn'); const tree = acorn.parse('1 + 1');

// tree 的 json 化表示
{
type: 'Program',
start: 0,
end: 5,
body: [{
type: 'ExpressionStatement',
start: 0,
end: 5,
expression: {
type: 'BinaryExpression',
start: 0,
end: 5,
left: { type: 'Literal', start: 0, end: 1, value: 1, raw: '1' },
operator: '+',
right: { type: 'Literal', start: 4, end: 5, value: 1, raw: '1' }
}
}],
sourceType: 'script'
}

babel-parser 为例,将 1 + 1 片段进行解析:


const parser = require('@babel/parser'); const tree = parser.parse('1 + 1');

// tree 的 json 化表示
{
type: 'File',
start: 0,
end: 5,
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 5 }
},
program: {
type: 'Program',
start: 0,
end: 5,
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 5 }
},
sourceType: 'script',
interpreter: null,
body: [{
type: 'ExpressionStatement',
start: 0,
end: 5,
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 5 }
},
expression: {
type: 'BinaryExpression',
start: 0,
end: 5,
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 5 }
},
left: {
type: 'NumericLiteral',
start: 0,
end: 1,
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 5 }
},
extra: { rawValue: 1, raw: '1' },
value: 1
},
operator: '+',
right: {
type: 'NumericLiteral',
start: 4,
end: 5,
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 5 }
},
extra: { rawValue: 1, raw: '1' },
value: 1
}
}
}],
directives: []
},
comments: []
}

3. 操作 css 文件

css 的语法比 html 要复杂一些,一些简单的操作如插入、替换,可以用直接以字符串的方式操作,但如果是压缩、auto prefix、css-modules 等复杂的功能时,就需要用第二种方式操作 css 了。

在第二种方式中,一般也是将 css 文本解析成一棵抽象语法树,然后进行操作。

比如:

postcss 为例,操作 css 文本:


const autoprefixer = require('autoprefixer');
const postcss = require('postcss');
const precss = require('precss'); const css = `
.hello {
display: flex;
color: red;
backgroundColor: #ffffff;
}
`; postcss([precss, autoprefixer({browsers: ['last 2 versions', '> 5%']})])
.process(css)
.then(result => {
console.log(result.css);
});

输出的文本:


.hello {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
color: red;
backgroundColor: #ffffff;
}

rework 为例,操作 css 文本:


const css = require('css');
const ast = css.parse('body { font-size: 12px; }'); console.log(css.stringify(ast));

输出的文本:


body {
font-size: 12px;
}

4. 操作 markdown/md 文件

一般来说,操作 markdown 文本的目的有两个:

  1. 作为编辑器编辑 markdown 文本,或作为渲染器渲染 markdown 文本为 html 文本
  2. markdown 文本中读取信息、校验嵌入的源代码、优化格式等

所以,尽管 markdown 的语法也很简单,但一般并不会直接去使用字符串的方式去操作 markdown 文本,一般都是使用的第二种方式。

比如:

  • markdown-it: 作为编辑器或渲染器的好手
  • remark: 构建抽象语法树进行操作的好手

markdown-it 为例,操作 markdown 文本:


const md = require('markdown-it')();
const result = md.render('# markdown-it rulezz!'); console.log(result);

输出的文本:


<h1>markdown-it rulezz!</h1>

remark 为例,操作 markdown 文本:


const remark = require('remark')
const recommended = require('remark-preset-lint-recommended')
const html = require('remark-html')
const report = require('vfile-reporter') remark()
.use(recommended)
.use(html)
.process('## Hello world!', function(err, file) {
console.error(report(err || file))
console.log(String(file))
})

校验错误提示:


1:1 warning Missing newline character at end of file final-newline remark-lint ⚠ 1 warning

输出的文本:


<h2>Hello world!</h2>

后续

更多博客,查看 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证

构建工具是如何用 node 操作 html/js/css/md 文件的的更多相关文章

  1. Java构建工具:如何用Maven,Gradle和Ant+Ivy进行依赖管理

    原文来自:https://zeroturnaround.com/rebellabs/java-build-tools-how-dependency-management-works-with-mave ...

  2. grunt配置太复杂?发布一个前端构建工具,简单高效,自动跳过未更新的文件

    做前端项目,如果没有一个自动化构建工具,手动处理那简直就是坑爹O(∩_∩)O.于是上网了解了下,grunt用的人不少,功能也挺强大.看了一下grunt的配置(包括gulp),感觉稍显复杂.当时项目结构 ...

  3. JavaScript 项目构建工具 Grunt 实践:安装和创建项目框架

     Grunt 是一个基于任务的 JavaScript 项目命令行构建工具,运行于 Node.js 平台.Grunt 能够从模板快速创建项目,合并.压缩和校验 CSS & JS 文件,运行单元测 ...

  4. 前端构建工具之争——Webpack vs Gulp 谁会被拍死在沙滩上

    .table tr>td:nth-child(1){width: 2em !important;padding-left: .6rem !important;padding-right: .6r ...

  5. Java项目工程化之项目构建工具Maven

    欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系.本系列教程希望您能站在上帝 ...

  6. 前端开发构建工具gulp的安装使用

    曾几何时还在使用grunt作为前端的构建工具,直到有一天同事向我推荐了gulp,在这里博主将不讨论gulp与grunt各自优势的比较,只为大家介绍gulp如何安装和使用. Gulp 是用 nodejs ...

  7. vue开发工具node.js及构建工具webpack

    1.概念 node.js:可以运行JavaScript的服务平台,可以把它当做一个后端程序,只是它的开发语言是JavaScript (通常情况下,JavaScript的运行环境都是浏览器,因此Java ...

  8. 如何用node开发自己的cli工具

    如何用node开发自己的cli工具 灵感 写这个工具的灵感以及场景源于youtube的一次闲聊 github 地址 blog首发 使用场景 原本我们写博客展示shell,例如:安装运转docker,一 ...

  9. 关于node.js和npm,cnpm的安装记录以及gulp自动构建工具的使用

    关于node.js和npm,cnpm的安装记录以及gulp自动构建工具的使用   工作环境:window下 在一切的最开始,安装node.js (中文站,更新比较慢http://nodejs.cn/) ...

随机推荐

  1. 简单便捷的纯PHP网盘程序 Veno File Manager 2.6.3(VFM2)

    体验过很多国外网盘程序,例如:Owncloud.Bedrive.YetiShare.XFilesharing.uCloud.Cloudshare 等等,诸如此类,VFM2与这些臃肿的商用或非商用来的程 ...

  2. django orm高级查询 F表达式和Q表达式以及分组annotate

    1.关联关系映射及查询1.1django默认开启延迟加载所有多对1和1对1如果不使用select_related(),需要会延迟加载获取到相关对象,因为延迟可能会造成n+1次查询的问题,所以便有了se ...

  3. CentOS7.1 KVM虚拟化之经常使用管理虚拟机命令(3)

    一.查看虚拟机列表及状态 [root@kvm01 ~]# virsh list --all Id Name State ---------------------------------------- ...

  4. spring中的事件 applicationevent 讲的确实不错

    event,listener是observer模式一种体现,在spring 3.0.5中,已经可以使用annotation实现event和eventListner里. 我们以spring-webflo ...

  5. 关于C++项目指针对象未被初始化的问题(0xcdcdcd)

    http://blog.csdn.net/devfun/article/details/6900086 昨天我试图将一个封装好的模块加入到正在开发的项目中,这个模块不是单独的类,而且对应的声明和实例. ...

  6. Nginx负载均衡简易配置

    多台Web服务器水平扩展,进行负载均衡对外服务,是一种很常见的方案. 常用方法用DNS轮询,LVS. DNS轮询虽然有配置简单的有点,但无法实现健康检查,DNS修改需要较长时间失效,对于无域名的内部服 ...

  7. scikit-learn:4. 数据集预处理(clean数据、reduce降维、expand增维、generate特征提取)

    本文參考:http://scikit-learn.org/stable/data_transforms.html 本篇主要讲数据预处理,包含四部分: 数据清洗.数据降维(PCA类).数据增维(Kern ...

  8. C++复习:位运算

    与          a&b :    1010&1100=1000  或          a|b  :  1010|1100=1110 异或       a^b :     101 ...

  9. windows 2008配置运行PHP5.5.X

    1.安装web5.0平台安装程序.web5.0平台安装程序:http://www.iis.net/downloads (实际上更方便的是用WebPlalform安装PHP:http://www.mic ...

  10. 短信计时器Utils

    package com.lvshandian.partylive.utils; import android.content.Context;import android.os.CountDownTi ...