IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 写一个简单的webpack打包工具 -> 正文阅读

[JavaScript知识库]写一个简单的webpack打包工具

??通过写一个简单的webpack工具,理解打包过程中的技术以及基础理论

1 AST概念?

?????????抽象语法树( Abstract Syntax Tree,AST ),或简称语法树( Syntax tree ),是源代码语法结构的一种抽象表示。
????????它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

2 AST应用

????????在前端,可以通过 Javascript 解析器将我们程序的源代码映射成为一棵语法树,而树的每个节点对应着代码里的一种结构;比如表达式,声明语句,赋值语句等都会被映射为语法树上的一个节点,进而我们就可以通过操作语法树上的节点来控制我们的源代码;

总结下来就是AST三板斧:

(1) 将源代码映射成AST

(2) 操作AST进行遍历更新

(3) 将更新后的AST再转换为源

????????说到以上三点,作为一个前端开发者,必然想到babel,下面就以babel来解释一下AST在编译中的应用

3 babel与AST的关系

????????Babel解析器是一个javascript解析器;最常见的功能就是将我们的高版本js语法解析为浏览器识别的es5语法;为了使用高版本js带来的便利,工作中会经常用es6或者es7来开发,但是某些浏览器并不能完全兼容该语法,所以需要babel将他们编译为浏览器可识别的es5的语法;其实基本过程就是上面提到的三板斧

3.1 @babel/parser

????????负责将源码解析为AST;该api负责将javascript源码根据表达式,函数声明,变量定义,导入声明等类型,解析为树节点,用树的方式将代码组织起来;方便后续遍历更新

3.2 @babel/traverse

?????????负责遍历操作AST节点;通过该api可以方便的获取所有节点,比如获取所有变量类型节点,或者获取所有的console.log()表达式,获取所有debugger等等;在打包时设置的去除所有打印语句或者debugger关键词等;就是在这一步实现,我们根据传入的规则,将一些不需要的节点遍历删除或者更改;

?3.3 ?@babel/core

? ? ? ? ?在遍历更新完ast后,将更新后的AST重新编译为浏览器可兼容的低版本源码;

4 代码演示

?4.1 新建一个目录,

?在根目录下新建src文件夹,然后在src文件夹新建如下三个测试文件

hello.js

export const hello="hello"

name.js

export const name="name"

message.js?

import {hello} from './hello.js'
import {name} from './name.js'
 
export default function message() {
  console.log(`${hello} ${name}!`)
}

4.2 新建入口文件

在src目录下新建entry.js

// 这是一个入口文件

// 导入
import message from './message.js'
import {name} from './name.js'
 
// 变量
const value="xiaobai";
// 函数
function getName(){
    return value
}
// 表达式 
message()
let res=value==="xiaobai"?true:false
console.log('----name-----: ', name)

到这里准备工作完成了;下面剧通过AST三板斧对入口文件关联的所有代码进行打包

4.3 安装babel包

在根目录下打开命令行,安装如下包,版本没要求,安装最新的即可

? "devDependencies": {
? ? "@babel/core": "^7.15.0",
? ? "@babel/parser": "^7.15.3",
? ? "@babel/preset-env": "^7.15.0",
? ? "@babel/traverse": "^7.15.0"
? }

4.4 在根目录下建一个配置文件minipack.config.js

执行脚本时,读取该配置文件;跟vue cli中vue.config.js是一个道理?

const path = require('path')
module.exports = {
    entry: 'src/entry.js',
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, './dist'),
    }
}

4.5 代码编译脚本文件(打包核心部分)

????????根目录下新建一个index.js;待会我们直接执行node index.js 即可打包我们的代码;跟vue工程中 npm run build是一个道理;

(1)第一步:获取所有源码内容

根据三板斧流程,获取更新后的源码

const fs = require('fs');
const path= require('path')
// 获取配置文件
const config = require('./minipack.config');
// 获取入口文件路径
const entry = config.entry;
// 获取入口文件内容
const mainAssert = createAsset(entry)
// 获取入口文件内容
function createAsset(){
    // 1 获取AST
    const content = fs.readFileSync(entry, 'utf-8');
    const babelParser = require('@babel/parser')
    const ast = babelParser.parse(content, {
      sourceType: "module"
    })
    const dependencies = []
    
    // 2 遍历AST;获取入口文件的所有依赖
    const traverse = require('@babel/traverse').default
    traverse(ast, {
      // 遍历所有的 import 模块,并将相对路径放入 dependencies
      ImportDeclaration: ({node}) => {
        dependencies.push(node.source.value)
      }
    })
    // 3 AST编译为源码
    const {transformFromAst} = require('@babel/core');
    const {code} = transformFromAst(ast, null, {
        presets: ['@babel/preset-env'],   // 代码解析规则
      })
      // 返回结果
      return {
        dependencies,
        code,
    }
}

我们打印ast变量,看一下ast树的组织节点长啥样:

?打印查看可能不太直观,我们借助一个网站查看:AST explorer

将entry.js文件内容复制到左侧,右侧自动展示出AST树,可以直观的看到源码是如何被以节点的方式组织;如下

7行源码,分别在body节点中被管理;主要有导入声明、变量声明、函数声明、表达式声明等类型;

打开最后一个输出语句表达式看一下;结构如下

?看到标志符的类型是console;其实我们再vue工程中打包的时候,会加如一些规则插件,比如去掉console,debugger等;在编译过程中,就是在利用Travser遍历AST后,然后根据节点名称去做删除操作的;将删除后的AST再转换成新的源码。

(2)递归解析所有依赖,形成依赖关系图

我们根据解析后的入口文件AST,将所有以依赖的文件用数组管理起来;代码如下

// entry: 入口文件绝对地址
const queue = {
  [entry]: mainAssert
}
// 递归解析所有的依赖项,生成一个依赖关系图
// 遍历 queue,获取每一个 asset 及其所以依赖模块并将其加入到队列中,直至所有依赖模块遍历完成
for (let filename in queue) {
  let assert = queue[filename]
  recursionDep(filename, assert)
  console.log("queue",queue);
}





/**
 * 递归遍历,获取所有的依赖
 * @param {*} assert 入口文件
*/
function recursionDep(filename, assert) {
  // 跟踪所有依赖文件(模块唯一标识符)
  assert.mapping = {}
  // 由于所有依赖模块的 import 路径为相对路径,所以获取当前绝对路径
  const dirname = path.dirname(filename)
  assert.dependencies.forEach(relativePath => {
    // 获取绝对路径,以便于 createAsset 读取文件
    const absolutePath = path.join(dirname, relativePath)
    // 与当前 assert 关联
    assert.mapping[relativePath] = absolutePath
    // 依赖文件没有加入到依赖图中,才让其加入,避免模块重复打包
    if (!queue[absolutePath]) {
      // 获取依赖模块内容
      const child = createAsset(absolutePath)
      // 将依赖放入 queue,以便于继续调用 recursionDep 解析依赖资源的依赖,
      // 直到所有依赖解析完成,这就构成了一个从入口文件开始的依赖图
      queue[absolutePath] = child
      if(child.dependencies.length > 0) {
        // 继续递归
        recursionDep(absolutePath, child)
      }
    }
  })
  }

(3) 根据依赖关系数组,返回浏览器可执行的js文件

这一步就是将入口文件所依赖的所有js文件代码,写入到匿名IIFE函数中;

// 使用依赖图,返回一个可以在浏览器运行的 JavaScript 文件
let modules = ''
for (let filename in queue) {
  let mod = queue[filename]
  modules += `'${filename}': [
    function(require, module, exports) {
      ${mod.code}
    },
    ${JSON.stringify(mod.mapping)},
  ],`
}



const result = `
  (function(modules) {
    function require(moduleId) {
      const [fn, mapping] = modules[moduleId]
      function localRequire(name) {
        return require(mapping[name])
      }
      const module = {exports: {}}
      fn(localRequire, module, module.exports)
      return module.exports
    }
    require('${entry}')
  })({${modules}})
`

(4)将可执行js代码,写入到文件

利用nodejs fs的writeFile将文件写入到js文件;

// 写入 ./dist/bundle.js
fs.writeFile(`./dist/bundle.js`, result, (err) => {
  if (err) throw err;
  console.log('文件已被保存');
})

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-24 10:44:41  更:2022-01-24 10:45:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:34:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码