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")
        }
    }
  • 相关阅读:
    【转】VS2010中 C++创建DLL图解
    [转]error: 'retainCount' is unavailable: not available in automatic reference counting mode
    [转]关于NSAutoreleasePool' is unavailable: not available in automatic reference counting mode的解决方法
    【转】 Tomcat v7.0 Server at localhost was unable to start within 45
    【转】Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If
    【转】SVN管理多个项目版本库
    【转】eclipse安装SVN插件的两种方法
    【转】MYSQL启用日志,和查看日志
    【转】Repository has not been enabled to accept revision propchanges
    【转】SVN库的迁移
  • 原文地址:https://www.cnblogs.com/znLam/p/13062966.html
Copyright © 2011-2022 走看看