基础知识
模块化:增强代码可读性和维护性
传统的网页开发转变成 Web Apps 开发
代码复杂度在逐步增高
分离的 JS文件/模块,便于后续代码的维护性 部
署时希望把代码优化成几个 HTTP 请求
常见的几种模块化方式
1.ES module
import * as largeNumber from 'large-number';
// ...
largeNumber.add('999', '1');
2.CJS
const largeNumbers = require('large-number');
// ...
largeNumber.add('999', '1');
3.AMD
require(['large-number'], function (large-number) {
//...
largeNumber.add('999', '1');
});
AST 基础知识
抽象语法树(abstract syntax tree 或者缩写为 AST),或者语法树(syntax tree),是 源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都 表示源代码中的一种结构。
Webpack 模块机制
· 打包出来的是一个 IIFE (匿名闭包)
· modules 是一个数组,每一项是一个模块初始化函数
· __webpack_require 用来加载模块,返回 module.exports
· 通过 WEBPACK_REQUIRE_METHOD(0) 启动程序
简易webpack
1. 可以将 ES6 语法转换成 ES5 的语法
·通过 babylon 生成AST
·通过 babel-core 将AST重新生成源码
2. 可以分析模块之间的依赖关系
·通过 babel-traverse 的 ImportDeclaration 方法获取依赖属性
3. 生成的 JS 文件可以在浏览器中运行
目录结构
package.json
{
"name": "spack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@babel/preset-env": "^7.16.11",
"babel-core": "^6.26.3",
"babel-preset-env": "^1.7.0",
"babel-traverse": "^6.26.0",
"babylon": "^6.18.0"
}
}
src/index.js
import { greeting } from './greeting.js'
import {hh} from './hh.js'
const a = greeting('baishu')
const b = hh()
document.write(`${a} ${b}`)
src/greeting.js
export function greeting(name){
return 'hello ' + name
}
src/hh.js
export function hh () {
return 'hai webpack'
}
lib/index.js
const Compiler = require('./compiler.js')
const options = require('../simplepack.config')
new Compiler(options).run();
simplepack.config.js
'use strict';
const path = require('path')
module.exports = {
entry: path.join(__dirname,'./src/index.js'),
output:{
path: path.join(__dirname,'./dist'),
filename:'main.js'
}
}
.babelrc
{
"presets": [
"@babel/preset-env"
]
}
dist/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
lib/compiler.js
const fs = require('fs')
const { getAST, getDependencies, transform } = require('./parser')
const path = require('path')
module.exports = class Compiler {
constructor(options){
const { entry, output } = options
this.entry = entry
this.output = output
this.modules = []
}
run(){
const entryModule = this.buildModule(this.entry,true)
this.modules.push(entryModule)
this.modules.map((_module)=>{
_module.dependencies.map((dependency) => {
this.modules.push(this.buildModule(dependency))
})
})
this.emitFiles()
}
buildModule(filename, isEntry){
let ast
if(isEntry){
ast = getAST(filename)
}else{
let absolutePath = path.join(process.cwd(), './src', filename);
ast = getAST(absolutePath)
}
return {
filename,
dependencies: getDependencies(ast),
source:transform(ast)
}
}
// 输出内容输出到哪里
emitFiles(){
const outputPath = path.join(this.output.path, this.output.filename)
let modules = '';
this.modules.map((_module) => {
modules += `'${ _module.filename }': function (require, module, exports) { ${ _module.source } },`
});
const bundle = `
(function(modules) {
function require(fileName) {
const fn = modules[fileName];
const module = { exports : {} };
fn(require, module, module.exports);
return module.exports;
}
require('${this.entry}');
})({${modules}})
`;
// console.log(bundle,'bundle')
fs.writeFileSync(outputPath,bundle,'utf-8')
}
}
lib/parser.js
const fs = require('fs')
const babylon = require('babylon')
const traverse = require('babel-traverse').default
const { transformFromAst } = require('babel-core')
module.exports = {
getAST:(path) => {
const source = fs.readFileSync(path,'utf-8')
return babylon.parse(source,{
sourceType:'module'
})
},
getDependencies:(ast) => {
const dependencies = []
traverse(ast,{
ImportDeclaration: ({ node })=>{
dependencies.push(node.source.value)
}
})
return dependencies
},
transform:(ast) => {
const {code} = transformFromAst(ast,null,{
presets:['env']
})
return code
}
}
lib/test.js
const { getAST, getDependencies, transform } = require('./parser')
const path = require('path')
const ast = getAST(path.join(__dirname,'../src/index.js'))
const dependences = getDependencies(ast)
if(process.argv[2] === 'ast'){
console.log(ast)
return
}
if(process.argv[2] === 'source'){
const source = transform(ast)
console.log(source)
return
}
if(process.argv[2] === 'd'){
const dependences = getDependencies(ast)
console.log(dependences)
return
}
- 测试parser功能, node lib/test.js d
浏览器中运行
- 本文链接:http://example.com/2022/03/16/webpack/build/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。