• webpack构建原理和实现简单webpack


    webpack打包原理分析

    • 基础配置,webpack会读取配置 (找到入口模块)

      • 如:读取webpack.config.js配置文件:
        const path = require("path")
        module.exports = {
            entry:"./src/index.js"
            mode:"development"
            output:{
              path:path.resolve(__dirname,"./dist"),
              filename:"main.js"
         }
        }//读取这里面的入口文件,和导出文件夹等
        
        
    • 入口分析

      • 分析依赖模块(分析这个入口文件依赖了哪些模块,并且拿到模块的路径)
      • 分析内容(并对内容进行处理)
      • 编译内容
    • 依赖模块(怎么分析了入口模块以同样的方式分析依赖模块:递归)

      • 分析依赖模块是否有其他模块
      • 分析内容(并对内容处理)
      • 编译内容
    • 生成bundle.js (这个js可以直接在浏览器中执行)

      • 基础结构为一个自执行函数  
        (function(){
            
         })({})
         并且传入了一个对象属性,
         对象属性的键为入口出口文件和依赖文件的路径
         对象属性的值为webpack实现他具体功能的函数
         
        

    实现

    index.js入口文件   webpack.config.js配置文件  webpack文件  bundle.js 中间执行文件
    index.js文件的依赖文件expo.js
    
    webpack.js文件:
    const fs = require("fs")
    const parser = require("bable/parser")
    const traverse = require('@babel/traverse').default;
    const path = require("path");
    const {tarnsformFromAst} = require('@babel/core');
    //因为webpack是基于node的,而node是遵循common.js规范的,所以不能用import,export导入导出
    module.exports = class Webpack{
        constructor(options){ //接收传入参数
            console.log(options)  //读取作为参数传入的webpack配置文件
            //将参数保存下来
            const {entry ,output} = options;
            this.entry = entry;
            this.output = output;
            
            //我们已经编写好了解析入口模块的方法,只需要用这个方法,去处理依赖模块,依赖中的依赖模块即可		
            this.moudles = [] ;
        }
        run(){
            console.log('hello webpack')
            this.parse(this.entry)
            //处理其他依赖模块,做一个信息汇总
            this.modules.push(info);
            for(let i=0 ; i<this.modules.length ;i++){
                const item = this.modules[i];
                const { dependencies} = item;
                if(dependencies){
                    for(let j in dependencies ){
                        //递归,处理所有模块的信息
                        this.modules.push(this.parse(dependencies[j])) 
                    }
                }
            }
            //将这个数组结构转换成对象
            console.log(this.moudles)
            const obj = {}
            this.modules.forEach((item)=>{
                obj[item.entryFile] = {
                    dependencies:item.dependencies
                    code:item.code
                } 
            })
        }
    	//解析模块函数    
        parse(entryFile){ 
            //解析入口函数
            //运用nodejs的文件模块,读取模块内容
            const content = fs.readFileSync(entryFile,"utf-8")  //他会将入口模块的内容给返回
            
            //分析出哪些是依赖?以及依赖的路径
            //推荐使用@bable/parser,这是bable7的工具,来帮助我们分析内部的语法,包括es6,返回一个ast抽象语法树,便于分析提取
            //安装插件 npm install @bable/parser --save
            const ast = parser.parse(content,{
                sourceType:"module"
            });
            
            const dependencies = {}  //存储import模块
            //此时,ast中就有所有的节点信息,并且分类为import为import模块名,即依赖的文件名模块和表达式节点
            //可以用bable下的traverse模块   npm installl @bable/traverse --save
             traverse(ast, {
               //根据ast中type类型作为函数名的函数来提取  
                 ImportDeclaration({ node }) {
                 const dirname = path.dirname(filename);
                 //node.source.value为import模块名,即依赖的文件名 
                 const newFile = './' + path.join(dirname, node.source.value);
                  //依赖的文件文件名为key,原始路径为值保存下来
                 dependencies[node.source.value] = newFile;
                 }
               });
            //然后再利用@bable/core和@bable/preset-env,把ast语法树转换成合适的代码
            //处理内容
            const {code} = tarnsformFromAst(ast,null,{
                presets:["@bable/preset-env"]
            });
            //处理完毕
            return {
                entryFile,//分析的哪个模块
                dependencies,//依赖是什么
                code  //代码是什么
            };
        }
        //生成最后的合成文件函数
        file(code){
           //根据之前解析出来的参数,生成一个自执行函数,然后内部完成对require以及import的处理
            //生成bundle.js  =>./dist/main.js (路径为配置文件当中的路径+文件名)
            const filePath = path.join(this.output.path,this.output.filename)
            const newcode = JSON.stringify(code)
            const bundle = `(function(graph){
                function require(module){
                    function localRequire(relativePath){
    				return require(graph[module].dependencies[relativePath]) 
                    }
    			   var exports = {};
                    (function(exports,code){
    				eval(code)
                    })(localRequire,graph[module].code)
                }
    		   require(exports,'$this.entry')
    		})(${newcode})`;
            fs.writeFileSync(filePath,bundle,"utf-8")
        }
    }
    
    
    bundle.js文件:
    //拿到webpack配置文件 (配置文件本身就是导出一个对象,将配置导出)
    const options = require("./webpack.config.js");
    //创建一个webpack实例,接收配置参数,然后根据参数,完成构建
    const Webpack = require("./lib/webpack.js");
    new Webpack(options).run();  //打印 hello webpack
    
    index.js文件:
    imports {add,minus} from "./expo,js"
    add(1,2);
    
    expo.js文件:
    export const add =function(a,b){
       return a+b;
    }
    export const minus = function(a,b){
       return a-b;
    }
    
    

    总结

    • 理解webpack打包流程
    • AST基础知识
    • 分析模块之间的依赖图谱(借助bable的几个模块,和递归,解析所有的依赖文件)
    • 动手实现一个简易webpack
  • 相关阅读:
    poj 1655 Balancing Act 树的重心
    poj 1985 Cow Marathon 树的直径
    hdu 4607 Park Visit 求树的直径
    hdu 1548 A strange lift 宽搜bfs+优先队列
    poj 2711 Leapin' Lizards && BZOJ 1066: [SCOI2007]蜥蜴 最大流
    poj 2449 Remmarguts' Date K短路+A*
    hdu 1285 确定比赛名次 拓扑排序
    hdu 3061 Battle 最大权闭合图
    hdu 3879 Base Station 最大权闭合图
    poj 2987 Firing 最大权闭合图
  • 原文地址:https://www.cnblogs.com/JCDXH/p/12073532.html
Copyright © 2020-2023  润新知