2019-08-08
Webpack
0

目录

loader
plugin
bundle

loader

loader本质上就是一个函数

使用自己本地的loader

js
use:[ {loader:path.resolve(__dirname,'./loader/a')}, {loader:path.resolve(__dirname,'./loader/b'),} ]

使用resolveLoader参数(webpack配置项):

js
resolveLoader: { modules: ['node_modules','./loader'] } use:[ {loader:'a'}, {loader:'b'} ]
js
const loaderUtils=require('loader-utils') module.exports=function (source) { // option参数获取 const options=loaderUtils.getOptions(this) } module.exports=function (source) { // callback,不需要手动return const result=source.replace(/1/g,'2').replace(/console.log\(.*\)/g,'') this.callback(null,result) } module.exports=function (source) { // 提前执行async,告知是异步返回 const result=source.replace(/1/g,'2').replace(/console.log\(.*\)/g,'') const callback=this.async() setTimeout(()=>{ callback(null,result) },3000) }

plugin

plugin本质上就是一个类

webpack plugin会自动使用apply函数,参数compiler下的hooks是plugin的各种生命周期。 tapAsync时,需要手动调用下callback,tap时不需要。constructor可以获取参数。

js
class A { constructor(options){ this.name=options.name } apply(compiler){ compiler.hooks.compile.tap('A',()=>{ console.log('每次编译都会被执行') }) compiler.hooks.emit.tapAsync('A',(compilation,cb)=>{ compilation.assets[`${this.name}.txt`]={ source:function(){ return '4' }, size(){ return 6 } } cb() }) } } module.exports=A

bundle

readFileSync

fs.readFileSync 会以你当前npm的执行路径为当前路径,而不是文件路径 readFile必须填写第二个参数(callback),不填写则报错

@babel/parser

content为文本,可以将文本输出为AST语法解析树

js
const parser=require('@babel/parser') const AST=parser.parse(content,{ sourceType:'module' })

@babel/traverse

根据你提供的语法解析树,筛选出你需要的树

js
const traverse=require('@babel/traverse').default traverse(AST,{ ImportDeclaration({node}){ console.log(node) } })

@babel/core

将语法树转换为es5代码

js
const babel=require('@babel/core') const {code}=babel.transformFromAst(AST,null,{ presets:["@babel/preset-env"] })

webpack bundle实现

  1. 获取入口文件js的文本内容
  2. 将入口文件的文本内容转换为AST抽象语法树
  3. 筛选出入口文件内的import语句
  4. 将入口文件的import整合成一个对象(为什么不整合成数组:可以以文件的相对路径来命名)
  5. 通过babel模块的core和preset将代码转换为浏览器能识别的代码
  6. 每个文件都输出filename, dependencies, code
  7. 至此我们获取到了入口文件解析后的代码以及入口文件的依赖关系
  8. 通过递归,解析入口文件的依赖关系,以及解析依赖文件的依赖关系,直到所有依赖文件被解析
  9. 至此我们获取到了所有文件的依赖关系
  10. 将所有文件的依赖图谱转换为对象,filename:{ dependencies,code }
  11. 将图谱转换为可以运行的代码
  12. 通过fs模块将文件输出到本地,进行使用
js
const fs = require('fs') const path = require('path') const parser = require('@babel/parser') const traverse = require('@babel/traverse').default const core = require('@babel/core') const moduleSync = function (entry) { //给定入口文件,输出文件解析后的代码以及相关依赖 const content = fs.readFileSync(entry, 'utf-8') //读取文件的JS内容 const AST = parser.parse(content, { //将JS内容导出为AST抽象语法树 sourceType: 'unambiguous' }) const dependencies = {} traverse(AST, { //从AST抽象语法树中筛选出import相关内容 ImportDeclaration({node}) { const dirname = path.dirname(entry) //获取当前JS的所在文件夹(相当于当前node执行路径) const file = path.join(dirname, node.source.value) //关联资源的路径(相当于当前node执行路径) dependencies[node.source.value] = file } }) const {code} = core.transformFromAst(AST, null, { presets: ["@babel/preset-env"] }) return { filename: entry, dependencies, code } } const transformGraph = function (moduleArr) { //将数组格式转换为对象格式,后续方便会用 let graph = {} moduleArr.forEach(item => { const key = item.filename graph[key] = { dependencies: item.dependencies, code: item.code } }) return graph } const makeDependGraph = function (entry) { //生成入口文件和其所有的依赖项 let entryModule = moduleSync(entry) let moduleArr = [entryModule] for (let i = 0; i < moduleArr.length; i++) { //for循环每次增加后,都可以在进行一次,而forEach不行 const dependencies = moduleArr[i].dependencies for (let j in dependencies) { //对依赖项进行遍历,如果存在,则再次对其进行统计其依赖项 moduleArr.push(moduleSync(dependencies[j])) } } return transformGraph(moduleArr) } const generateCode=function(entry){ //将对象转换为code代码 let graph=JSON.stringify(makeDependGraph(entry)) return `(function(graph){ function require(entry){ function localRequire(relativePath){ return require(graph[entry].dependencies[relativePath]) } var exports={}; (function(require,exports,code){ eval(code) })(localRequire,exports,graph[entry].code) } require('${entry}') })(${graph})` } let result = generateCode('src/index.js') fs.writeFileSync('./index.js',result)

本文作者:BARM

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!