zoukankan      html  css  js  c++  java
  • webpack打包原理

    1.创建一个项目文件夹,包含lib,dist,src三个文件

    2.创建配置文件webpack.config.js,和bundle.js文件

    3.lib里创建webpack.js文件,用来封装webpack类

    4.src里创建一个index.js文件,作文打包的入口文件

    ##src--index.js
      import a from "./a.js"
      console.log("hello webpack");
    ##webpack的基础配置--webpack.config.js
    const path = require("path")
    
    module.exports = {
        mode:"development",
        entry:"./src/index.js",
        output:{
            path:path.resolve(__dirname,"./dist"),
            filename:"main.js"
        }
    }
    ##bundle.js
    //打包入口
    const options = require("./webpack.config.js")
    const webpack = require("./lib/webpack.js")
    new webpack(options).run()
    ##webpack.js
    这里面要做的事儿:
    1.首先接收一份配置(webpack.config.js)
    2.分析出入口模块的位置
      2.1读取入口模块的内容,分析哪些是依赖,哪些是源码
    2.2处理需要编译的代码,使其变为浏览器能执行的代码(es6,jsx等的编译等)
      2.3分析其他模块
    3.将第二部处理出的内容变为对象数据结构
      3.1包含模块路径
    3.2包含处理好的内容
    4.创建bundle.js:启动器函数,来补充代码里有可能出现的module exports require,让浏览器能够顺利的执行

    首先分析入口文件,这里我们要安装几个插件:

    安装babel/parser:npm i @babel/parser --save        分析文件中哪些是源码哪些是依赖

    安装babel/traverse:npm i @babel/traverse --save  提取抽象语法树里的节点信息

    安装babel/core babel/preset-env:npm i @babel/core @babel/preset-env --save   对源码js进行解析处理

      babel/core:核心库

      babel/preset-env:做代码转换,比如解析jsx,es6的Promise等等

    ##分析入口文件,解析依赖及源码,并进行编译
    const fs = require("fs");
    const path = require("path");
    const parser = require("@babel/parser");//引入parser
    const traverse = require("@babel/traverse").default;//引入traverse
    const {transformFromAst} = require("@babel/core")//处理ast抽象语法树
    
    module.exports = class webpack {
        constructor(options){
            let {entry,output} = options
            //入口信息
            this.entry = entry;
            //出口信息
            this.output = output;
         //缓存模块信息
        
    this.module = [];
        }
        run(){
            //开始分析入口模块内容
            this.parse(this.entry);
        }
        parse(entryFile){
            //拿到文件内容
            const content = fs.readFileSync(entryFile,"utf-8");
            //分析-哪些是源码-哪些是依赖 我们借助babel插件:安装:npm i @babel/parser --save
            //console.log(content)
            //使用parser分析内容,返回ast 抽象语法树
            const ast = parser.parse(content,{
                sourceType:"module"
            })
            //console.log(ast.program.body)通过上面的截图 我们可以看到打印的数据
            //提取依赖路径信息
            const dependencies = {}   //key为相对于相对入口路径,val为填充后的路径   
            traverse(ast,{
                ImportDeclaration({node}){
                    //node.source.value 是相对于入口的相对路径
                    //做路径填充 path.dirname(entryFile)
                    const newPathName =  "./" + path.join(
                        path.dirname(entryFile),
                        node.source.value
                    )
                    dependencies[node.source.value] = newPathName
                }
            })
            //代码编译
            const {code} = transformFromAst(ast,null,{ //此api处理ast抽象语法树
                presets:["@babel/preset-env"]
            });
            //console.log(code)  解析后的代码依然不能再浏览器中执行   “require is not defined”   所以接下来我门要告诉浏览器  require是干嘛的
            //信息汇总返回
            return {
                entryFile,
                dependencies,
                code
            }    
        }
    }

     将上面得到得数据做一个递归,即对所有依赖文件做了分析,并将拿到得数组转换为对象数据结构

    run(){
            //开始分析入口模块内容
            const info = this.parse(this.entry);
            console.log(info)
            //入口数据放入模块
            this.module.push(info);
            //做递归,拿到所有得地址依赖做parse()
            for (let i = 0; i < this.module.length; i++) {
                const item = this.module[i];
                const { dependencies } = item;
                if (dependencies) {
                  for (let j in dependencies) {
                    this.module.push(this.parse(dependencies[j]));
                  }
                }
                //console.log(this.module)
                
            }
            //数据结构转换-数组转换成对象
            const obj = {};
            this.module.forEach(item => {
                obj[item.entryFile] = {
                    dependencies:item.dependencies,
                    code:item.code 
                }
            })
        }

    生成bundle

    file(code) {
            //生成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(require,exports,code){ eval(code) })(localRequire,exports,graph[module].code) return exports } require('${this.entry}') })(${newCode})` //写文件操作 fs.writeFileSync(filePath,bundle,"utf-8") }

    然后执行  node bundle.js  转义后得代码 放到浏览器里可执行即正确

    ----------------

    完整代码

    const fs = require("fs");
    const path = require("path");
    const parser = require("@babel/parser");//引入parser
    const traverse = require("@babel/traverse").default;//引入traverse
    const {transformFromAst} = require("@babel/core")//处理ast抽象语法树
    
    module.exports = class webpack {
        constructor(options){
            let {entry,output} = options
            //入口信息
            this.entry = entry;
            //出口信息
            this.output = output;
            //缓存模块信息
            this.module = [];
        }
        run(){
            //开始分析入口模块内容
            const info = this.parse(this.entry);
            console.log(info)
            //入口数据放入模块
            this.module.push(info);
            //做递归,拿到所有得地址依赖做parse()
            for (let i = 0; i < this.module.length; i++) {
                const item = this.module[i];
                const { dependencies } = item;
                if (dependencies) {
                  for (let j in dependencies) {
                    this.module.push(this.parse(dependencies[j]));
                  }
                }
                //console.log(this.module)
                
            }
            //数据结构转换-数组转换成对象
            const obj = {};
            this.module.forEach(item => {
                obj[item.entryFile] = {
                    dependencies:item.dependencies,
                    code:item.code 
                }
            })
            //生成文件
            this.file(obj)
        }
        parse(entryFile){
            //拿到文件内容
            const content = fs.readFileSync(entryFile,"utf-8");
            //分析-哪些是源码-哪些是依赖 我们借助babel插件:安装:npm i @babel/parser --save
            //console.log(content)
            //使用parser分析内容,返回ast 抽象语法树
            const ast = parser.parse(content,{
                sourceType:"module"
            })
            //console.log(ast.program.body)
            //提取依赖路径信息
            const dependencies = {}   //key为相对于相对入口路径,val为填充后的路径   
            traverse(ast,{
                ImportDeclaration({node}){
                    //node.source.value 是相对于入口的相对路径
                    //做路径填充 path.dirname(entryFile)
                    const newPathName =  "./" + path.join(
                        path.dirname(entryFile),
                        node.source.value
                    )
                    dependencies[node.source.value] = newPathName
                }
            })
            //代码编译
            const {code} = transformFromAst(ast,null,{ //此api处理ast抽象语法树
                presets:["@babel/preset-env"]
            });
            //console.log(code)
            //信息汇总返回
            return {
                entryFile,
                dependencies,
                code
            }    
        }
        file(code) {
            //生成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(require,exports,code){
                        eval(code)
                    })(localRequire,exports,graph[module].code)
                    return exports
                }
                require('${this.entry}')
            })(${newCode})`
    
            //写文件操作
            fs.writeFileSync(filePath,bundle,"utf-8")
        }
    }
  • 相关阅读:
    sql中实现先排序后分组
    mysql中的锁机制之概念篇
    PHP对程序员的要求更高
    给初学PHP的学习线路和建议
    设计模式六大原则
    Mysql忘记密码怎么办
    数据库 sql
    精准优化 if…else ,干掉,过多,烂代码!
    JDK1.8 新特性(全)
    mysql 如何修改 删除 添加 表主键
  • 原文地址:https://www.cnblogs.com/znLam/p/13062966.html
Copyright © 2011-2022 走看看