前端工程化之新晋打包工具

新晋打包工具

  • 新晋打包工具
    • 前端模块工具的发展历程
    • 分类
    • 初版构建工具
      • grunt
        • 使用场景
      • gulp
        • 采用管道机制
        • 任务化配置与api简洁
    • 现代打包构建工具基石--webpack
    • 基于webpack改进的构建工具
      • rollup 推荐
        • 举例说明
          • package.json
          • rollup.config.mjs
          • my-extract-css-rollup-plugin.mjs
          • src/index.js
        • src/utils.js
          • src/index.css
        • src/utils.css
        • build/cjs.js
        • build/esm.js
        • build/umd.js
        • build/index.css
        • build/666.css
        • 使用场景
        • 不适用场景
      • Parcel 不推荐
        • 举例说明
          • parcel/index.html
          • parcel/index.js
          • parcel/App.js
          • parcel/index.css
          • package.json
        • 使用场景
    • 突破JS语言特性的构建工具
      • SWC 推荐使用 √ - 平替babel
        • jsc-parser语法解析相关配置
        • jsc-target 输出代码的es版本
        • 典型配置案例
      • ESbuild - 作为工具去使用的
    • 基于ES Module的bundleless(no bundle)构建工具 => vite
      • 基于bundle的解决方案
      • vite - 重点掌握
        • vite原理
        • 为什么vite之前没有,到2021年后才有这样的开发链路呢?
        • vite插件
          • package.json
          • vite.config.js
          • 自定义插件 -plugins/myPlugin.js
        • vite插件的相关钩子
        • 通用钩子
      • rspack - 推荐尝试使用
        • 示例:通过 rsbuild 创建一个工程
      • turpopack 国外的

新晋打包工具

构建为了将工程化的思想和自动化的思想应用在前端的工程链路中

前端模块工具的发展历程

  • 09年,commonJS:指定浏览器外js的相关 api 规范, nodejs 就采用了这样的规范
  • 11年,requireJS:作为客户端模块加载器,提供了异步加载模块的能力,之后就变成了 AMD 的规范
  • 13年,grunt,gulp 诞生。
  • 14年,UMD,统一模块定义,跨平台的前后端兼容
  • 14年,6to5,ES6 语法 => ES5,经历了 词法分析,语法分析,AST => new AST => generator code。这也就是 babel 的能力
  • 14年,system is 简化模块加载工具
  • 14年,webpack,第一个稳定版本的
  • 15年,ES6 规范正式发布的
  • 15年,rollup 基于ES6模块化,并且提供 tree shaking相关能力
  • 17年,Parcel,零配置,内部集成配置,能力进行收口,parcel,index.html
    => 做平台,开发基础能力,具备插件化机制
  • 19年,构建工具深水区,不再使用js语言卷了,使用go,rust语言来卷。由于JS是高级语言,使用 babel 会经历各种AST转换
    snowpack,使用rust语言,天生支持多线程能力
  • 20年,浏览器对 ESM,http2 支持,使得 bundless 思路开始出现,esbuild 进入到大众视野中
  • 21年,vite诞生

分类

  • 初版构建工具
  • 现代打包构建工具基石 webpack
  • 突破JS语言特性的构建工具
  • esmodule 的 bundless 构建工具

初版构建工具

grunt

最早的构建工具,构建工具的鼻祖
基于 nodejs 来开发的,借助nodejs实现跨系统,跨平台的操作文件系统
自动化的配置工具集,像官方所说的是一种 Task Runner,是基于任务的,整体配置json,由JSON配置设置驱动的。
基于 grunt 可以进行JS语法监测,或者合并一些JS文件,合并后的文件压缩,以及将我们预处理的sass,less文件进行编译
配置驱动、插件化、任务链

'use strict'
module.exports = function (grunt) {//构建的初始化配置grunt.initConfig({/*配置具体任务 */pkg: grunt.file.readIsON('package.json'),dirs: {src: 'path',dest: 'dest/<%= pkg.name >/<%= pkg.version 名>'},// clean任务(删除dest/test_grunt/0.0.1 目录下非min的文件)clean: {js: ['<%= dirs.dest &>/*.js', '!<%= dirs.dest %>/*.min.js'],css: ['<%= dirs,dest %>/*.css', '!<%= dirs.dest 名>/*.min.css'],},// copy任务(拷贝path目录下的文件到dest目录)copy: {main: {files: [// includes files within path{expand: true,src: ['path/*'],dest: '<%= dirs.dest %>/',filter: 'isFile',},],},},//concat任务(将dest目录下的a.js和b.js合并为built.js)concat: {options: {separator: '\n',},concatCss: {src: ['<%= dirs,dest &>/a.css', '<%= dirs.dest &>/path/b.css'],dest: '<%= dirs.dest %>/built.css',},concatJs: {src: ['<%= dirs,dest &>/a.js', '<%= dirs.dest &>/b.js'],dest: '<%= dirs.dest %>/built.is'}},// cssmin任务(压缩css)cssmin: {target: {files: [{expand: true,cwd: '<%= dirs.dest %>',src: ['*.css', '!*.min.css'],dest: '<%= dirs.dest %>',ext: '.min.css'}]},},// uglify任务(压缩js)uglify: {options: {mangle: {except: ['jQuery', 'Backbone'],},},my_target: {files: {'<%= dirs.dest %>/bulit.min.js': ['<%= dirs.dest %>/*.js']},},},})// 载入要使用的插件grunt.loadNpmTasks('grunt-contrib-clean')grunt.loadNpmTasks('grunt-contrib-copy')grunt.loadNpmTasks('grunt-contrib-concat')grunt.loadNpmTasks('grunt-contrib-cssmin')grunt.loadNpmTasks('grunt-contrib-uglify')//注册刚配置好的任务grunt.registerTask('cls', ['clean'])grunt.registerTask('cpy', ['copy'])grunt.registerTask('con', ['concat'])grunt.registerTask('cmpCSS', ['cssmin'])grunt.registerTask('cmpJS', ['uglify'])grunt.registerTask('default', ['copy', 'concat', 'cssmin', 'uglify', 'clean'])
}

缺点:
针对 文件处理模式

  • grunt 任务,基于磁盘文件操作,先读取 => 再处理 => 后写入

效率是非常低下的

grunt.initConfig({uglify: {files:{'dest/output.min.js': ['src/input1.js','src/input2.js']}}
})

读取 less => 编译 css => 写入磁盘 => 读取 css => 压缩处理 => 写入磁盘

使用场景
  • 传统项目维护 已经是使用grunt来处理
  • 简单任务自动化 使用grunt也足够了

gulp

基于 nodejs 的流式前端构建工具。特点:代码驱动任务,高效流处理,基于task驱动
完成 测试,检查,合并,压缩 能力

采用管道机制

采用管道pipe机制处理文件,所有操作在内存中处理,基于内存流的,避免频繁io操作
在管道 pipe 中 =>使用 less 插件=>转成 css =>使用 minicss 插件压缩css => 写入磁盘,由于是在内存中完成的,因此效率提升

任务化配置与api简洁
gulp.task('css',()=>gulp.src('./src/css/**').pipe(cssmin()).pipe(gulp.dest('./dist/css'))
)

插件生态庞大,包含文件压缩,语法编译等

基于流式的高效性和插件驱动的灵活性

var gulp = require('gulp')
var pug = require('gulp-pug')
var less = require('gulp-less')
var minifyCss = require('gulp-csso')gulp.task('html',function(){return gulp.src('client/templates/*.pug').pipe(pug()).pipe(gulp.dest('build/html'))
})
gulp.task('css',function(){return gulp.src('client/templates/*.less').pipe(less()).pipe(minifycss()).pipe(gulp.dest('build/css'))
})gulp.task('default', ['html''css'])

现代打包构建工具基石–webpack

上篇文章中已说到了,这里就不再赘述了。

特性:基于各种各样配置,包含loader对文件进行编译处理,webpack内容当中,所有内容皆为模块,需要转译成JS模块,需要使用不同的loader进行处理,另外,还有插件的能力,webpack基于事件流的,集成自 tapable 的,学会开发自定义插件,了解compiler,complation 各自的有哪些钩子,并且钩子能做哪些事情,落地一些插件才行

基于webpack改进的构建工具

rollup 推荐

vue2,vue3,react,babel等,源码层面上,都是使用 rollup 做构建工具的
专注于 js 模块打包的工具
特点:高效性,轻量性,一般都是在前端 Library 基础类库工具函数等打包,打包出来的效果要优于webpack的,体积也要优于webpack。
对于基础类库/工具函数库需要被其他函数库引用,像引入 vue2,vue3,react。针对他们的诉求肯定是越小越好,没有用到的相关特性就不要打包进来了,所以 tree shaking 能力是必备的,能够对当前代码进行静态分析,esModule的导入导出,没有用到的功能(deadcode )就会精准剔除

  • 高效 tree shaking 能力

  • 减小包体积,避免冗余依赖,适用于按需加载的场景

  • 支持输出 ESM commonjs AMD IIFE UMD模块格式,满足不同环境需求
    配置时候也比较简单,只需要在配置文件中进行如下操作:

    rollup index.js -f cjs -o bundle.cjs.js #输出 CommonJS格式
    
  • 轻量化代码输出
    几乎不添加额外代码
    打包仅包含一些必要的函数,辅助代码

  • 强大的插件生态,vite线上发布使用rollup进行打包的,vite扩展了rollup插件生态,包含代码转换,依赖解析,压缩等场景

  • @rollup/plugin-babel

  • @rollup/plugin-terser 压缩代码

  • @rollup/plugin-commonjs,将commonjs => ESM

很多相关的插件
针对 rollup 有插件,没有loader,但是也能对 非 js 文件进行处理,有扩展的能力

  • transform 对代码进行转换
    • 语法转换
    • 添加额外功能
    • 等等
      因此在开发插件的时候,需要重点关注 transform 方法
举例说明

pnpm init

在这里插入图片描述

package.json
  • “rollup-plugin-cleaner”:“^1.0.0”, —— 清除当前目录下的dist文件的
  • “rollup-plugin-cleanup”:“^3.2.1”, —— 清除代码注释,删除无效的console等等
  • “rollup-plugin-postcss”:“^4.0.2” —— 针对css文件的插件
{"name": "about-builder","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","build":"npx rollup -c rollup.config.mjs --watch"},"keywords": [],"author": "","license": "ISC","dependencies":{"parcel": "^2.13.0","react":"^18.3.1","react-dom":"^18.3.1","rollup":"^4.27.4","rollup-plugin-cleaner":"^1.0.0", "rollup-plugin-cleanup":"^3.2.1","rollup-plugin-postcss":"^4.0.2"}, "devDependencies":{"process":"^0.11.10"}
}
rollup.config.mjs

使用rollup的话,就需要提供这样的一个配置文件

import postcss from "rollup-plugin-postcss"
import cleanup from "rollup-plugin-cleanup"
import cleaner from "rollup-plugin-cleaner"
import myExtractCssRollupPlugin from "./my-extract-css-rollup-plugin.mjs"/** @type {import("rollup").RollupOptions} */
export default {input: 'src/index.js',output: [{file: 'build/esm.js',format: 'esm'},{file: 'build/cjs.js',format: 'cjs' //指定当前模块规范},{file: 'build/umd.js',name: 'Echo',format: 'umd'}],plugins: [cleaner({targets: ['dist',"build"], //需清理的目录silent: false, //显示操作日志watch: true, //监听模式exclude: ['README.md'], //保留特定文件}),// 代码清理cleanup({comments: false,sourcemap: false,targets: ['build/*']}),// 处理css,将css内容从js文件中提取出来postcss({extract: true,extract: 'index.css'}),// 自定义插件myExtractCssRollupPlugin({filename: '666.css'})]
}
my-extract-css-rollup-plugin.mjs
/** 为什么 rollup 没有 loader 呢?* 因为 rollup 的 plugin 有 transform 方法,也就相当于 loader 的功能了。* Rollup 打包过程中对模块的代码进行转换操作
*/const extractArr=[]export default function myExtractCssRollupPlugin(opts) {return {name: 'my-extract-css-rollup-plugin',transform(code, id) {//在这里对代码进行转换操作if (!id.endsWith('.css')) {return null}// 将后缀为css的文件内容收集起来extractArr.push(code)return {// 转换后的代码code: '',// 可选的源映射信息,如果需要生成源映射的话map: { mappings: '' }}},//此方法在Rollup生成最终的输出文件之前被调用generateBundle(options, bundle) {this.emitFile({fileName: opts.filename,type:"asset",source:extractArr.join('/* #echo# */\n')})}}
}
src/index.js
import { add } from './utils.js'
// rollup 默认开启 tree shaking
import './index.css'function main() {console.log(add(1, 3))
}export default main
src/utils.js
import './utils.css'function add(a, b) {return a + b;
}export { add };
src/index.css
body{background: skyblue;
}
src/utils.css
.bbb{background: red;
}

执行

pnpm run build

得到:
在这里插入图片描述

build/cjs.js
'use strict';function add(a, b) {return a + b;
}function main() {console.log(add(1, 3));
}module.exports = main;
build/esm.js
function add(a, b) {return a + b;
}function main() {console.log(add(1, 3));
}export { main as default };
build/umd.js
(function (global, factory) {typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :typeof define === 'function' && define.amd ? define(factory) :(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Echo = factory());
})(this, (function () { 'use strict';function add(a, b) {return a + b;}function main() {console.log(add(1, 3));}return main;}));
build/index.css
.bbb{background: red;
}
body{background: skyblue;
}
build/666.css
export default undefined;/* #echo# */
export default undefined;
使用场景
  • 开发 js 库,工具函数
  • 需要 tree shaking 优化的项目
  • 生成环境打包 vite
不适用场景
  • 依赖非 js 资源 非常多

Parcel 不推荐

  • 完全零配置
  • 构建速度快

parcel 官网

举例说明

还是在上面的 about-builder 包下,使用 React 框架来写案例
在这里插入图片描述

parcel/index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="index.css">
</head>
<body><div id="app"></div><script type="module" src="./index.js"></script>
</body>
</html>
parcel/index.js
import { createRoot } from 'react-dom/client'
import App from './App.js'const container = document.getElementById('app')
const root = createRoot(container)
root.render(<App />)
parcel/App.js
export function App() {return <h1>Hello World!</h1>
}
parcel/index.css
body{background-color: skyblue;
}
package.json

去掉 main 那一行,也就是:"main": "index.js"这个内容

执行:

npx parcel parcel/index.html

在这里插入图片描述

文件夹多了一个dist和.parcel-cache
在这里插入图片描述

页面:
在这里插入图片描述

热更新也是比较友好的

使用场景

适用小型项目

突破JS语言特性的构建工具

非 JS 语言相关的构建工具

SWC 推荐使用 √ - 平替babel

  • speedy web Compiler 快速web编译器
    => Compiler + bundler (编译+构建 所组成的)
    => bundler 有一定的缺陷,推荐使用 Compiler 编译 能力
    => 强调 快速 的能力,使用 rust 语言实现的,使用多线程
  1. 简历中做一些优化,针对 webpack 做一些常规的优化,像进行分包,还有像通过引入cache提升构建速度,像leo-plugins等方式,只是针对webpack本身所作的优化,但是现在
    webpack+babel 已经具备了性能瓶颈 => 优化措施:webpack+swc
    babel 对标 => swc
    babel-loader => swc-loader

  2. 文件比较多,使用 babel-loader 的话,需要经历 翻译、ast 是比较耗时的
    使用swc的话,性能会得到质的飞跃

  3. swc官方网站

  4. 性能表现原因:

    • rust 语言编写,编译时确定运行的行为,不像js是解释执行,解释成机器语言再执行机器语言。rust 是多线程的这样的一个能力
  5. 功能覆盖
    SWC 主要对 js 代码快速转换,核心将 es6+代码转换成 es5或者其他代码,在这过程中会进行代码压缩优化等相关的一些操作,比如swc能很好的处理箭头函数模板字符串解构赋值等es6+特性的转换,还有针对ts语言tsx语言等语言的处理,成熟度也是可以的

  6. 使用:简单转换代码
    @swc/core @swc/cli

    npx swc source.js -o dist.js

    const start = () => {console.log('app started')
    }
    // 转为
    var start = function (){console.log('app started')
    }
    
jsc-parser语法解析相关配置

使用 swc-loader 时候,需要着重注意 JSC 相关配置

swc-loader

  • JSC (javascript Compiler)
    配置项:
options:{//jsc相关能力配置"jsc":{//当前需要转义哪些语言"parser":{//指定当前语言类型"syntax": "typescript", //ecmascript"tsx": true, //是否编译tsx"dynamicImport": true //是否支持动态导入}}
}
jsc-target 输出代码的es版本

配置对应的target
接着上面写:

options:{//jsc相关能力配置"jsc":{//当前需要转义哪些语言"parser":{//指定当前语言类型"syntax": "typescript", //ecmascript"tsx": true, //是否编译tsx"dynamicImport": true //是否支持动态导入}//配置对应target"target": "es2015" //输出代码的es版本"transform":{     //代码转换"react":{"runtime":"automatic"},//启动代码优化"optimizer":{"simplify": true //简化}}}
}
典型配置案例

.swcrc 配置文件

{"jsc": {"parser": {"syntax": "typescript","tsx": true,"decorators": true,},"transform":{     //代码转换"react":{"runtime":"automatic"}},"target": "es2018",//是否需要辅助函数"externalHelpers": true,"baseUrl": ".","paths": {"@/*": ["src/*"]}},"minify": true //进行代码压缩
}

也可以自己写一些插件

import{ readFilesync } from 'fs'
import { transform } from '@swc/core'const run = async () => {const code = readFileSync('./source.js','utf-8')const result= await transform(code,{filename:'source.js',})//·输出编译后代码console.log(result.code)
}
run()

ESbuild - 作为工具去使用的

vite 在开发环境下,使用 esbuild 预构建依赖
由于并发处理包的构建是非常快的,因此才会使用,而JS本质是解释型语言,执行代码的时候需要一边将源码翻译成机器语言,一边调度执行。

  1. go编写程序,是编译型语言,少了动态解释过程

  2. 多线程
    go语言具备多线程能力,将所有的包都进行深度开发,因为JS是单线程,虽然也引入了webworker 做一些多线程的事情,但是还是有一些限制,比如说,go的多个线程之间是可以共享当前进程的内存空间,但是JS的webworker是不能共享进程内存空间的,如果想要数据共享的话,需要通过 postmessage 进行通信,但是这样的话,效率也比较低下的。因此,这也是JS的限制
    => 更高效的利用内存使用率 => 达到了更高的运行性能

  3. 全量定制
    比如,webpack中会用到babel实现ES5的版本转义,使用ESlint代码检查,使用tsc完成typescript代码转义,检查,使用less,scss等,这些使用插件去实现的。
    但是,esbuild中完全去重写,整套流程,工具都是重写的,意味着对这些文件的资源 tsx,jsx,js,ts等加载解析代码的生成逻辑,内部都会进行定制化开发,相对来说,成本也是非常高的,实现出来后,对编译的各个阶段都达到了非常好的性能。如果不去继续兼容webpack的loader,依然可能会达到不好的效果。
    webpack尤其针对 babel 的代码编译,会频繁的经历 string => AST => AST => string =>AST => string 这样的阶段,因此,esbuild重写了大多数转译工具,能够尽量共用相似的AST转换,减少AST结构的转换,进而提升内存利用率

  4. ESbuild 特性
    (1)极快的速度,无需缓存
    (2)支持 ES6 commonjs 模块
    (3)ES6 tree shaking
    (4)API 可以同时用于 js 和 go
    (5)兼容 ts,jsx语法
    (6)支持plugins
    这也是为什么 vite 使用 esbuild 作为包的转换

ESbuild官网
同时拷贝10个 three.js 库的扩展

在这里插入图片描述

基于ES Module的bundleless(no bundle)构建工具 => vite

http2 支持 多路复用 并发限制很大 10 50 100
浏览器 esm

基于bundle的解决方案

bundle based => entry 入口进行分析,分析当前的依赖内容,调用了哪些模块,对应的loader对当前进行处理 => modules,递归的完成这些模块的依赖分析 =>最终形成bundle => 启动 devServer 给到浏览器,然后浏览器去进行渲染

请添加图片描述

vite - 重点掌握

vite原理

而nobundle的思想:
本地启动一个服务,执行vite相关内容,会创建一个服务,启动devServer(本地请求资源服务),还有 websocket 两个服务(主要用于hm热更新)
no-bundle核心的两个特性:预构建、按需加载
请添加图片描述

使用按需加载的简单的vue3项目:

  1. 加载html,html中引入了main.js
    在这里插入图片描述
    还会引入 @vite/client,实现热更新
    在这里插入图片描述
  2. 加载client资源(热更新)
    监听消息
    在这里插入图片描述
    handleMessage方法:
    在这里插入图片描述
    在websocket中能看到payload.type,connect是建联,update是更新操作,等等。

先是建联:
在这里插入图片描述
更改 页面文字:
在这里插入图片描述
websocket会有update更新
在这里插入图片描述

类型是 js-update的话,会调用队列:
在这里插入图片描述
最终会发起 App.vue请求
App.vue请求会带着时间戳,不会复用之前的,避免了缓存的影响,就会拿到更改之后的数据替换之前的内容
在这里插入图片描述

  1. 加载main.js,引入了vue.js,style.css,等
    在这里插入图片描述

  2. 加载vue.js,style.css等,比如,style.css使用css插件做处理,创建style标签用在header当中
    在这里插入图片描述
    在这里插入图片描述

为什么vite之前没有,到2021年后才有这样的开发链路呢?
  1. http2.x 支持,多路复用
    之前webpack不拆包,将所有的都打包到一个bundle当中,热更新重新走整个链路的流程,最终形成bundle,然后再更新这个bundle,会受体积影响
    现在都是使用websocket,支持单文件的热更新
    多路复用
    http1.0 会对单个域名有tcp请求的限制,限制 6-8 tcp请求链接的数量,因此,将多个文件合并到一个文件当中进行处理,避免限制对有些请求发送不出去
    http2.x 有多路复用,同一个域名下对请求并发限制很大,10个,50个,100个同时请求服务器下的多个资源
  2. 浏览器支持 esm
    webpack时候还不支持 esm 这样的一个特性,需要经历编译这一层
    现在可以在浏览器中通过"import xxx"去加载到对应的资源内容
vite插件

使用 vite 创建 vue3 项目:

pnpm create vite my-vue3-app

在这里插入图片描述

使用vite构造的vue3项目:
在这里插入图片描述

package.json

这三个快捷指令
在这里插入图片描述

vite.config.js

内部集成了常见模块的插件,针对css等不需要单独额外处理
都是基于rollup插件去扩展的

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 自定义插件
import myVitePlugin from './plugins/myPlugin'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(), myVitePlugin()],test: {environment: 'jsdom',coverage: {reporter: ['text', 'json', 'html'],// 设置覆盖文件夹reportsDirectory: './coverage',// 检查每个文件的阈值perFile: true,// 设置代码覆盖率阈值lines: 75,functions: 75,branches: 75,statements: 75}}
})
自定义插件 -plugins/myPlugin.js

在工程当中,打印当前工程版本号

import path from 'path'
import fs from 'fs'//控制台打印当前工程版本号
export default function myVitePlugin() {let version, configreturn {name: 'my-vite-plugin',configResolved(resolvedConfig) {config = resolvedConfigconst pkgPath = path.resolve(config.root, 'package.json')const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))version = pkg.version},buildStart() {console.log('当前工程版本号:1.0.0')},transform(code, id) {if (id.endsWith('main.js')) {const info = `console.log('当前工程版本号:${version}')`return `${code}\n${info}\n`}}}
}

在这里插入图片描述
在这里插入图片描述

vite插件的相关钩子
  • config 解析vite相关配置时候
  • configResolved 解析配置之后的钩子
  • configuerserver 配置开发服务器
  • handlehotupdate 执行热更新时候的钩子
通用钩子
  • options
  • buildstart 开始创建
  • transform 每个模块传入请求时调用
  • buildend 构建结束

rspack - 推荐尝试使用

基于 rust 语言,实现的高性能前端构建工具
特性:兼容webpack生态
完全从webpack配置快速迁移到 rust 的技术体系当中,在构建速度上得到了显著的提升

rspack 官网

在这里插入图片描述

示例:通过 rsbuild 创建一个工程

pnpm create rsbuild@latest

在这里插入图片描述
在这里插入图片描述

类似 vite:
在这里插入图片描述
在这里插入图片描述
rsbuild 与 webpack区别:

  1. 语言优势,rust 语言编译时会转为机器码,少了解释执行的过程
  2. 多线程
    rsbuild 与 vite 的区别:
  3. vite 在生产环境依赖 rollup,在开发环境使用 esbuild+热更新,no-bundle按需下载的思想

turpopack 国外的

相对来说使用的比较少

基于 rust
turpopack 官网

由 Vercel 赞助的
vercel
可以一键去部署自己的项目,无需写git-action的配置,已经内置了这样的能力,做了CI/CD

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/78760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ai软件UI自动化

在AI与UI自动化结合的场景中,通常涉及计算机视觉(CV)、自然语言处理(NLP)和机器学习(ML)等技术。以下是实现AI驱动UI自动化的关键方向、工具和步骤: ‌一、核心应用场景‌ ‌元素定位增强‌ ‌问题‌:传统工具依赖XPath/CSS选择器,易因UI变化失效。‌AI方案‌:CV识别…

关于 C++ 中 cin 对象和 EOF 的详细解释

【DeepSeek提问】 给解释一下下面这段话&#xff08;C编程&#xff09; cin是 iostream 类的一个对象实例&#xff0c;如果输入正常&#xff0c; cin 将返回本身。 举个例子&#xff1a;cin>x>>y, 如果 cin>>x 读入正常&#xff0c;那么将返回cin, 相当于后面继…

Vue 3 和 Vue 2 的区别及优点

Vue.js 是一个流行的 JavaScript 框架&#xff0c;广泛用于构建用户界面和单页应用。自 Vue 3 发布以来&#xff0c;很多开发者开始探索 Vue 3 相较于 Vue 2 的新特性和优势。Vue 3 引入了许多改进&#xff0c;优化了性能、增强了功能、提升了开发体验。本文将详细介绍 Vue 2 和…

【特权FPGA】之UART串口

0.简介 通用异步收发器(Universal Asynchronous Receiver&#xff0f;Transmitter&#xff0c;UART)可以和各种标准串行接口&#xff0c;如RS 232和RS 485等进行全双工异步通信&#xff0c;具有传输距离远、成本低、可靠性高等优点。一般UART由专用芯片如8250&#xff0c;1645…

Vue3中watch监视reactive对象方法详解

在Vue3中&#xff0c;使用watch监视reactive对象时&#xff0c;需根据监视的目标选择合适的方法。以下是详细的步骤和说明&#xff1a; 1. 监视整个reactive对象 自动深度监视&#xff1a;直接监视reactive对象时&#xff0c;Vue3会默认启用深度监视&#xff0c;无需设置deep:…

如何制定性能调优策略

目录 性能测试攻略 微基准性能测试 宏基准性能测试 热身问题 多 JVM 情况下的影响 合理分析结果&#xff0c;制定调优策略 推荐阅读 性能测试攻略 性能测试是提前发现性能瓶颈&#xff0c;保障系统性能稳定的必要措施。下面我先给你介绍两种常用 的测试方法&#xff0c;帮…

HarmonyOS-ArkUI V2装饰器@Local装饰器:组件内部状态

@Local装饰器的作用 @Local装饰器是用来装饰组件内的状态的。而且它修饰的变量可以成为数据源。Local装饰器,作用跟名字差不多,重点突出了“本地”的特性,也就是使用的范围仅仅限制在组件内部。且它在初始化的时候必须是在本地进行初始化的,不能在外部组件,同时也禁止了外…

Linux线程属性与多线程开发:API详解与实战代码解析

Linux 线程的属性 线程池 多线程的创建 线程的属性 引入 我们设想一个场景&#xff0c;使用pthread_detach时&#xff0c;发现线程早就已经结束了&#xff0c;这时候pthread_detach还能正常发挥清理线程的 独有空间 的作用吗&#xff1f; 答案是可以的&#xff0c;但是这难…

测试第二课-------测试分类

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

MySQL安装实战分享

一、在 Windows 上安装 MySQL 1. 下载 MySQL 安装包 访问 MySQL 官方下载页面。选择适合你操作系统的版本。一般推荐下载 MySQL Installer。 2. 运行安装程序 双击下载的安装文件&#xff08;例如 mysql-installer-community-<version>.msi&#xff09;。如果出现安全…

数据库预热

介绍 Database Warm-up &#x1f9e0; 一句话理解 数据库是在应用启动阶段&#xff0c;提前建立数据库连接 或 执行轻量 SQL 操作&#xff0c;从而 加快首个请求的响应速度 的一种优化手段 &#x1f3af; 为什么需要数据库预热&#xff1f; 当 FastAPI 或其他 Web 服务刚启…

SearXNG

SearXNG 什么是 SearXNG &#xff1f;说白了&#xff0c;其实就是一个免费开源的搜索引擎。那为什么要本地安装它呢&#xff1f; 看它官网的解释(翻译)&#xff0c;当然&#xff0c;其中官方也有一篇文档解释了为什么需要部署使用私有示例&#xff1a;为什么使用私有实例&…

js 颜色转换分析

一、十六进制转RGB function hexToRgba(hex) {// 移除 # 字符hex hex.replace(#, );// 处理简写形式如 #fffif (hex.length 3) {hex hex[0] hex[0] hex[1] hex[1] hex[2] hex[2];}// 转换为十进制const r parseInt(hex.substring(0, 2), 16); // 截图前两位&#xff0…

智能资源管理机制-重传机制

一、发送端资源管理的核心机制 1. 滑动窗口&#xff08;Sliding Window&#xff09; 这是TCP协议的核心优化设计&#xff1a; 窗口动态滑动&#xff1a;发送端不需要保留所有已发送的分组&#xff0c;只需维护一个"发送窗口"窗口大小&#xff1a;由接收方通告的接…

基于SSM+Layui毕业设计选题系统源码

项目介绍 基于SSM+Layui毕业设计选题系统源码,可以作为课程设计项目参考,该系统分为三个角色: 管理员:用户管理(对学生和老师的信息进行维护),统计分析(对老师课题情况以及学生选题情况信息进行维护),修改密码 老师:个人信息维护,毕业设计题目管理,报名学生管理…

通过uri获取文件路径手机适配

青铜版本 return contentResolver.query(this, arrayOf(MediaStore.MediaColumns.DATA), null, null).let {if (it?.moveToFirst() true) {val columnIndex it.getColumnIndex(MediaStore.MediaColumns.DATA)val path it.getString(columnIndex)it.close()return path}&quo…

vue模拟扑克效果

vue模拟扑克效果 效果图&#xff1a; step1:C:\Users\wangrusheng\PycharmProjects\untitled18\src\views\Home.vue <template><div class"poker-container"><!-- 使用复合数据对象实现双行显示 --><divv-for"(card, index) in POKER_…

基础数学:图论与信息论

微积分与概率论由此进&#xff1a;基础数学&#xff1a;微积分和概率与统计-CSDN博客 线代与优化理论由此进&#xff1a;基础数学&#xff1a;线性代数与优化理论-CSDN博客 数值分析与离散数学由此进&#xff1a;基础数学&#xff1a;数值分析与离散数学-CSDN博客 四、图论与…

构建智能期货交易策略分析应用:MCP与AI的无缝集成

引言 随着金融科技的快速发展&#xff0c;数据驱动的交易决策已成为期货交易领域的重要趋势。本文将深入探讨一个结合了Model Content Protocol (MCP)和AI技术的期货交易策略分析应用——Futures MCP。该应用不仅提供了丰富的技术分析工具&#xff0c;还通过MCP协议与大型语言…

0x02.Redis 集群的实现原理是什么?

回答重点 Redis 集群&#xff08;Redis cluster&#xff09;是通过多个 Redis 实例组成的&#xff0c;每个主节点实例负责存储部分的数据&#xff0c;并且可以有一个或多个从节点作为备份。 具体是采用哈希槽&#xff08;Hash Slot&#xff09;机制来分配数据&#xff0c;将整…