no-bundle 构建原理浅析

目录
  • 为什么需要构建工具?
  • 什么是无包构建
  • 基于浏览器的 JS 模块加载功能
    • HTML 中的 Script 引用注意点:
    • 模块内依赖的引用
  • 无包构建工具的介绍:
    • Vite
    • Vite对导入模块的解析
      • 对 HTML 文件的预处理
      • 对外部依赖包的解析
      • 对 CSS 文件的解析
    • Vite 中的其他辅助功能
    • Vite 的使用限制
  • Snowpack
    • 与 Vite 相同的功能点
    • 与 Vite 的差异点
  • 无包构建 VS 打包构建
    • 无包构建的优点
    • 无包构建的缺点

为什么需要构建工具?

  • 处理其他类型文件使其能被浏览器正常加载 —— 许多其他类型的文件需要编译处理为 ES6 模块才能被浏览器正常加载(JSX、Vue、TS、CSS、Image 等)。
  • 解决引用路径的问题 —— 许多第三方依赖包在通过第三方 URL 引用时,不仅过程烦琐,而且往往难以进行灵活的版本控制与更新,因此需要构建工具来解决这类问题。
  • 为开发提供辅助工具 —— 对于现实中的项目开发而言,一些便利的辅助开发技术,例如热更新、sourceMap 等还是需要由构建工具来提供。

什么是无包构建

它的构建方式是:

  • 在构建时只需处理模块的编译而无须打包,把模块间的相互依赖关系完全交给浏览器来处理。
  • 浏览器会加载入口模块,分析依赖后,再通过网络请求加载被依赖的模块。

这种通过浏览器原生的模块进行解析的方式又称为 Native-ESM(Native ES Module)。

//./src/index.html
...
<!-- 注意: type="module" -->
<script type="module" src="./modules/foo.js"></script>
...
//.src/modules/foo.js
import { bar } from './bar.js'
import { appendHTML } from './common.js'
...
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.15/slice.js').then((module) => {...})

基于浏览器的 JS 模块加载功能

HTML 中的 Script 引用注意点:

  • 入口模块文件在页面中引用时需要带上 type="module" 属性。
  • 带有 type="module" 属性的 script在浏览器中通过 defer 的方式异步执行(异步下载,不阻塞 HTML,顺次执行),即使是行内的 script 代码也遵循这一原则(而普通的行内 script 代码则忽略 defer 属性)。
  • 带有 type="module" 属性且带有 async 属性的 script,在浏览器中通过 async 的方式异步执行(异步下载,不阻塞 HTML,按该模块和所依赖的模块下载完成的先后顺序执行,无视 DOM 中的加载顺序),即使是行内的 script 代码,也遵循这一原则(而普通的行内 script 代码则忽略 async 属性)。
  • 即使多次加载相同模块,也只会执行一次。

模块内依赖的引用

  • 只能使用 import ... from '...' 的 ES6 风格的模块导入方式,或者使用 import(...).then(...) 的 ES6 动态导入方式,不支持其他模块化规范的引用方式(例如 require、define 等)。
  • 导入的模块只支持使用相对路径('/xxx', './xxx', '../xxx')和 URL 方式('https://xxx', 'http://xxx')进行引用,不支持直接使用包名开头的方式('xxxx', 'xxx/xxx')。
  • 只支持引用MIME Type为 text/javascript 方式的模块,不支持其他类型文件的加载(例如 CSS 等)。

无包构建工具的介绍:

Vite

Vite 是 Vue 框架的作者尤雨溪最新推出的基于 Native-ESM 的 Web 构建工具。

在开发环境下基于 Native-ESM 处理构建过程,只编译不打包,在生产环境下则基于 Rollup 打包。

Vite对导入模块的解析

对 HTML 文件的预处理

启动 Vite 时,会通过 serverPluginHtml.ts 注入 /vite/client 运行时的依赖模块,该模块用于处理热更新,以及提供更新 CSS 的方法 updateStyle。

对外部依赖包的解析

  • resolver.ts 负责找到对应在 node_modules 中的真实依赖包代码(Vite 会在启动服务时对项目 package.json 中的 dependencies 做预处理读取并存入缓存目录 node_modules/.vite_opt_cache 中)。
  • serverPluginModuleRewrite.ts 负责把源码中的 bare modules 加上 /@module/ 前缀。
  • serverPluginModuleResolve.ts 负责解析加上前缀后的模块。

对 Vue文件的解析

对 Vue 文件的解析是通过 serverPluginVue.ts 处理的,分离出 Vue 代码中的 script/template/style 代码片段,并分别转换为 JS 模块,然后将 template/style 模块的 import写到script 模块代码的头部。

对 CSS 文件的解析

对 CSS 文件的解析是通过 serverPluginCSS.ts 处理的,解析过程主要是将 CSS 文件的内容转换为下面的 JS 代码模块,其中的 updateStyle 由注入 HTML 中的 /vite/client 模块提供

import { updateStyle } from "/vite/client"
const css = "..."
updateStyle(""..."", css) // id, cssContent
export default css

Vite 中的其他辅助功能

  • 多框架:除了在默认的 Vue 中使用外,还支持在 React 和 Preact 项目中使用。
  • 热更新(HMR) :默认提供的 3 种框架的脚手架模板中都内置了 HMR 功能,同时也提供了 HMR 的 API 供第三方插件或项目代码使用。
  • 自定义配置文件:支持使用自定义配置文件来细化构建配置,配置项功能参考 config.ts。
  • HTTPS 与 HTTP/2:支持使用 --https 启动参数来开启使用 HTTPS 和 HTTP/2 协议的开发服务器。
  • 服务代理:在自定义配置中支持配置代理,将部分请求代理到第三方服务。
  • 模式与环境变量:支持通过 mode 来指定构建模式为 development 或 production。相应模式下自动读取 dotenv 类型的环境变量配置文件(例如 .env.production.local)。
  • 生产环境打包:生产环境使用 Rollup 进行打包,支持传入自定义配置,配置项功能参考 build/index.ts。

Vite 的使用限制

  • 面向支持 ES6 的现代浏览器,在生产环境下,编译目标参数 esBuildTarget 的默认值为 es2019,最低支持版本为 es2015(因为内部会使用 esbuild 处理编译压缩,用来获得最快的构建速度)。
  • 对 Vue 框架的支持目前仅限于最新的 Vue 3 版本,不兼容更低版本。

Snowpack

  • 从整体功能来说和上述 Vite工具提供的功能大致相同。
  • Snowpack 在生产环境下默认使用无包构建而非打包模式。

与 Vite 相同的功能点

两者都支持各种代码转换加载器、热更新、环境变量(需要安装 dotenv 插件)、服务代理、HTTPS 与 HTTP/2 等。

与 Vite 的差异点

  • 相同的功能,实现细节不同: 例如对 Bare Module 的处理,除了转换后前缀名称不同外(Vite 使用 /@module/ 前缀,而 Snowpack 使用 /web_modules/ 前缀)
  • 工具稳定性
  • 插件体系: 除了版本差异外,Snowpack 提供了较完善的插件体系,支持用户和社区发布自定义插件。
  • 打包工具:在生产环境下,Vite 使用 Rollup 作为打包工具,而 Snowpack 则需要引入插件来实现打包功能,官方支持的打包插件有 @snowpack/plugin-webpack 和 @snowpack/plugin-parcel,暂未提供 Rollup 对应的插件。
  • 特殊优化:Vite 中内置了对 Vue 的大量构建优化,因此对 Vue 项目而言,选择 Vite 通常可以获得更好的开发体验。

无包构建 VS 打包构建

无包构建的优点

  • 初次构建启动快: 无包构建流程中,模块依赖分析与编译都是在浏览器渲染页面时异步处理的
  • 按需编译:在浏览器渲染时,根据入口模块分析加载所需模块,编译过程按需处理,因此相比之下处理内容更少,速度也会更快。
  • 增量构建速度快:rebuild 过程中,只需处理编译单个模块。

无包构建的缺点

  • 浏览器网络请求数量剧增: 无包构建最主要面对的问题是,它的运行模式决定了在一般项目里,渲染页面所需发起的请求数远比打包构建要多得多,使得打开页面会产生瀑布式的大量网络请求,将对页面的渲染造成延迟。这也是 Vite 在开发环境下才使用无包构建,在生产环境下则仍旧使用打包构建的原因吧。
  • 浏览器的兼容性:

以上就是no-bundle 构建原理浅析的详细内容,更多关于no-bundle 构建原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • Javarscript中模块(module)、加载(load)与捆绑(bundle)详解

    JS模块简介 js模块化,简单说就是将系统或者功能分隔成单独的.互不影响的代码片段,经过严格定义接口,使各模块间互不影响,且可以为其他所用. 常见的模块化有,C中的include (.h)文件.java中的import等. 为什么JS需要模块 很显然,没有模块我们也可以实现同样的功能,为什么我们还要使用模块来写js代码呢?下面几点是模块化给我们带来的一些变化: 抽象代码:我们在使用模块来调用一个api时,可以不用知道内部是如何实现的,避免去理解其中复杂的代码: 封装代码:在不需要再次修改代码的前

  • 浅析webpack-bundle-analyzer在vue-cli3中的使用

    正常的使用方法 安装 npm install webpack-bundle-analyzer -D webpack.config.js: vue-cli3的配置方法 根目录的vue.config.js(没有则自己创建) module.exports = { chainWebpack: config => { config .plugin('webpack-bundle-analyzer') .use(require('webpack-bundle-analyzer').BundleAnalyze

  • 浅谈Vue SSR中的Bundle的具有使用

    前言 写过Vue SSR的都知道,Vue通过提供server和client的webpack插件生成bundle josn,从而实现类似服务端的热更以及客户端资源的优化注入.那么这两个个bundle到底有什么神奇的呢?OK,话不多说,进入正题 客户端 vue-ssr-client-manifest.json 首先看看客户端的json,明显看到,里面借助webpack插件,把spa用到的文件进行了分类, publicPath 是公共路径,all 是所有的文件, initial 是入口文件依赖的js和

  • 浅析vue-cli3配置webpack-bundle-analyzer插件【推荐】

    为优化vue项目性能,需要使用webpack-bundle-analyzer分析报文件,找出最占用空间的插件有哪些,对应做出优化 网上看了一些网站,有的写的太麻烦了,现将最简单的一种写出来供大家参考 安装: npm install webpack-bundle-analyzer --save-dev vue.config.js配置 module.exports = { chainWebpack: config => { config .plugin('webpack-bundle-analyze

  • 浅谈webpack打包生成的bundle.js文件过大的问题

    问题 使用webpack进行打包时,发现bundle.js竟然有2M多. 解决办法 网上有去除插件.提取第三方库.压缩代码等方法. 还有一个比较容易忽略的原因就是开了sourcemap 在生产环境中,应使用devtool: false 关闭sourcemap后bundle.js的大小从2.46M降到302k 以上这篇浅谈webpack打包生成的bundle.js文件过大的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们. 您可能感兴趣的文章: 彻底解决 webpa

  • no-bundle 构建原理浅析

    目录 为什么需要构建工具? 什么是无包构建 基于浏览器的 JS 模块加载功能 HTML 中的 Script 引用注意点: 模块内依赖的引用 无包构建工具的介绍: Vite Vite对导入模块的解析 对 HTML 文件的预处理 对外部依赖包的解析 对 CSS 文件的解析 Vite 中的其他辅助功能 Vite 的使用限制 Snowpack 与 Vite 相同的功能点 与 Vite 的差异点 无包构建 VS 打包构建 无包构建的优点 无包构建的缺点 为什么需要构建工具? 处理其他类型文件使其能被浏览器

  • Android Flutter实现原理浅析

    目录 前言 一.安卓原生界面绘制的流程 原生绘制流程 SurfaceView绘制流程 二.Flutter上界面绘制的流程 FlutterActivity中的流程 FlutterView中的实现 native流程 三.总结 Flutter的简单实现原理 Flutter的几个高频问题 前言 flutter可以说是当下最流行的跨平台技术了,其最突出的 网上可以搜到的文章,大多数都是flutter的用法,即使介绍其实现原理的,也直接深入源码直接解读,造成只有一定功能的读者才能理解. 本文希望以最通俗易解

  • Javascript自执行匿名函数(function() { })()的原理浅析

    函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数指没有指定函数名或指针的函数,自执行匿名函数只是其中一种,下文中称这种函数为:自执行函数 下面是一个最常见的自执行函数: // 传统匿名函数 (function() { alert('hello'); })(); 这段代码的执行效果就是在页面再载入时弹出:"hello" 是什么促使它自动执行的?,来看下面的代码 // 在传统写法上去掉小括号,并在前面加上运算符 ~,!,+,- ~function(){

  • 深入理解jQuery()方法的构建原理

    前言 虽然JQuery相对简单,但要全面掌握,且快速灵活的使用它也并不那么容易,它提供了很多方法,包含了网页开发的各个知识面,所以要全面掌握这些知识点,个人认为还是需要对jquery有深入的理解,对这些知识点做分类整理记忆,这样你才能面对一些JQuery代码的时候不会感到迷惑,才会知道采用何种方式实现某个特效是最佳实践,才能快速的采用JQuery来进行项目开发. jQuery中最常用方法的就是jQuery( ) ,也即$( ) . jQuery( )是一个函数调用,调用的结果是返回了一个jQue

  • Android微信抢红包功能的实现原理浅析

    快到过农历年了,微信红包也越来越多了,出现了好多红包外挂程序,就很好奇如何实现的,于是自己研究了一番,亲自写了个微信抢红包的APP.现在就一步一步来实现它. 实现思路 微信抢红包程序开启时候,他就可以随时识别.捕获红包,服务可以实现正在功能,当我们开启服务的时候,服务就不停的在后台运行,不停地轮询着微信里面的消息,当发现红包时候就立即打开微信红包所在的界面.但是他怎识别红包呢?需要找到微信抢红包里面节点的view,当找到对应的view,在获取view的关键字或者id,根据关键字或者id,自动的模

  • Java 读写锁实现原理浅析

    最近做的一个小项目中有这样的需求:整个项目有一份config.json保存着项目的一些配置,是存储在本地文件的一个资源,并且应用中存在读写(读>>写)更新问题.既然读写并发操作,那么就涉及到操作互斥,这里自然想到了读写锁,本文对读写锁方面的知识做个梳理. 为什么需要读写锁? 与传统锁不同的是读写锁的规则是可以共享读,但只能一个写,总结起来为:读读不互斥,读写互斥,写写互斥,而一般的独占锁是:读读互斥,读写互斥,写写互斥,而场景中往往读远远大于写,读写锁就是为了这种优化而创建出来的一种机制. 注

  • java DelayQueue的原理浅析

    在对DelayQueue延迟功能的使用上,很多人不能后完全理解延迟的一些功能使用,这里我们深入来挖掘一下DelayQueue的原理. 下面将从构造方法.接口.继承体系三个方面进行分析,需要注意的是,相较于其它的阻塞队列,DelayQueue因为延迟的功能多了接口的使用,一起来看具体内容. 1.构造方法 public DelayQueue() {} public DelayQueue(Collection<? extends E> c) { this.addAll(c); } 构造方法比较简单,

  • Docker镜像构建原理解析(不装docker也能构建镜像)

    在devops流程里面 构建镜像是一个非常重要的过程,一般构建镜像是写dockerfile文件然后通过docker client来构建的image. docker client 会先检查本地有没有image,如果没有帮你 从镜像仓库 pull 下来 然后解析你写的dockerfile构建新的image. 本文带你了解 pull 命令 背后是怎么做的? build 命令 背后是怎么做的? 下篇文章带你解析: 如果我不用docker 我如何构建一个镜像? 我们以微软的aspnet2.2为基础构建一个

  • mysql事务和隔离级别底层原理浅析

    目录 前言 一.事务底层原理浅析 原子性: 持久性 隔离性: 一致性: 二.隔离级别底层原理浅析 三.总结 前言 首先回顾一下什么是事务,事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作:这些操作作为一个整体一起向系统提交,要么都执行.要么都不执行:事务是一组不可再分割的操作集合(工作逻辑单元). 事务的特性: 原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚.一致性(Consistency):事务执行的结果必须是使数据库从一个一致性

  • Golang map实现原理浅析

    目录 map的声明 map声明 map使用的方式 map遍历 map切片 map 排序 map使用细节 map的练习题 map的声明 基本语法 var map变量名 map[keytype]valuetype key可以是什么类型 golang中的map,的key可以是很多中类型,比如bool,数字,string,指针,channel,还可以是包含前面几个类型的 接口,结构体,数组 通常为int,string valuetype 可以是什么类型 valuetype的类型和key基本一样,这里我就

随机推荐