摘要:構建工具是如何用操作文件的從本質上來說,源代碼文件都是文本文件,文本文件的內容都是字符串,對文本文件的操作其實就是對字符串的操作。在第二種方式中,一般也是將文本解析成一棵抽象語法樹,然后進行操作。
構建工具是如何用 node 操作 html/js/css/md 文件的
從本質上來說,html/js/css/md ... 源代碼文件都是文本文件,文本文件的內容都是字符串,對文本文件的操作其實就是對字符串的操作。
操作源代碼的方式又主要分成兩種:
當作字符串,進行增、刪、改等操作
按照某種語法、規則,把字符串讀取成一個對象,然后對這個對象進行操作,最后導出新的字符串
1. 操作 html 文件html 的語法比較簡單,并且一般操作 html 都是插入、替換、模板引擎渲染等在字符串上的操作,所以使用第一種方式的比較多。
比如:
html-loader
html-webpack-plugin
html-minifier
handlebars 模板引擎
pug 模板引擎
ejs 模板引擎
一般以第二種方式來操作 html 的都是將 html 文本解析成 dom 樹對象,然后進行 dom 操作,最后再導出成新的代碼文本。
比如:
cheerio
jsdom
parse5
以 cheerio 為例,操作 html 文本:cheerio 能夠加載一個 html 文本,實例化一個類 jQuery 對象,然后使用 jQuery 的 api 像操作 dom 一樣操作這段文本,最后導出新的 html 文本。
const cheerio = require("cheerio"); const $ = cheerio.load("以 jsdom 為例,操作 html 文本:Hello world
"); // 加載一個 html 文本 $("h2.title").text("Hello there!"); $("h2").addClass("welcome"); $.html(); // 導出新的 html 文本 //=>Hello there!
jsdom 是用 js 將一個 html 文本解析為一個 dom 對象,并實現了一系列 web 標準,特別是 WHATWG 組織制定的 DOM 和 HTML 標準。
const jsdom = require("jsdom"); const { JSDOM } = jsdom; const dom = new JSDOM(`2. 操作 js 文件Hello world
`); console.log(dom.window.document.querySelector("p").textContent); // "Hello world"
因為 js 語法比較復雜,僅僅是如字符串一樣進行增刪改,只能做一些小的操作,意義不大。所以,一般操作 js 文件都是采用的第二種方式。
在第二種方式中,一般是工具將 js 文本解析成抽象語法樹(AST,Abstract Syntax Tree,抽象語法樹),然后對這棵語法樹以面向對象的方式做增刪改等操作,最后再導出成新的代碼文本。
生成抽象語法樹的工具主要有:
Acorn: 比如 webpack、rollup、UglifyJS 等工具底層都是使用的 acorn 抽象語法樹解析器
babel-parser: babel 轉碼工具底層使用的抽象語法樹解析器
以 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-loader、autoprefixer、cssnano 等的底層都是使用的 postcss 來解析
rework、reworkcss: 抽象語法樹解析器
csstree: 比如 csso 的底層就是使用 csstree 來解析
以 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 文本的目的有兩個:
作為編輯器編輯 markdown 文本,或作為渲染器渲染 markdown 文本為 html 文本
從 markdown 文本中讀取信息、校驗嵌入的源代碼、優化格式等
所以,盡管 markdown 的語法也很簡單,但一般并不會直接去使用字符串的方式去操作 markdown 文本,一般都是使用的第二種方式。
比如:
markdown-it: 作為編輯器或渲染器的好手
remark: 構建抽象語法樹進行操作的好手
以 markdown-it 為例,操作 markdown 文本:const md = require("markdown-it")(); const result = md.render("# markdown-it rulezz!"); console.log(result);
輸出的文本:
以 remark 為例,操作 markdown 文本:markdown-it rulezz!
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
輸出的文本:
后續Hello world!
更多博客,查看 https://github.com/senntyou/blogs
作者:深予之 (@senntyou)
版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99010.html
摘要:構建工具是如何用操作文件的從本質上來說,源代碼文件都是文本文件,文本文件的內容都是字符串,對文本文件的操作其實就是對字符串的操作。在第二種方式中,一般也是將文本解析成一棵抽象語法樹,然后進行操作。 構建工具是如何用 node 操作 html/js/css/md 文件的 從本質上來說,html/js/css/md ... 源代碼文件都是文本文件,文本文件的內容都是字符串,對文本文件的操作...
閱讀 3267·2021-11-18 10:02
閱讀 3443·2021-10-11 10:58
閱讀 3376·2021-09-24 09:47
閱讀 1120·2021-09-22 15:21
閱讀 3915·2021-09-10 11:10
閱讀 3277·2021-09-03 10:28
閱讀 1748·2019-08-30 15:45
閱讀 2136·2019-08-30 14:22