预备知识
前端工程的痛点
模块化需求:业界模块标准非常多,一方面需要落实这些模块标准,保证模块正常加载;另一方面需要兼容不同的模块规范,以适应不同的执行环境
兼容浏览器,编译高级语法: 由于浏览器的实现规范所限,高级语法需要在浏览器中正常运行,必须转化为浏览器可以理解的形式
线上代码质量:考虑代码的安全性
、兼容性
、性能
问题
开发效率:项目的冷启动/二次启动时间、热更新时间都可能严重影响开发效率
前端构建工具如何解决痛点
模块化方面: 提供模块加载方案,并兼容不同的模块规范
语法转译方面: 配合Sass
、TSC
、Babel
等前端工具链,完成高级语法的转译功能,同时对于静态资源也能进行处理,使之能作为一个模块正常加载
产物质量方面: 在生产环节中,配合Terser
等压缩工具进行代码压缩和混淆,通过Tree Shaking
删除未使用的代码,提供对低版本浏览器的语法降级处理
开发效率方面: 构建工具本身通过各种方式来进行性能优化,包括使用原生语言Go/Rust
、no-bundle
等思路,提高系统的启动性能和热更新速度
Vite的优势
- 模块化方面,Vite基于浏览器原生
ESM
的支持实现no-bundle
服务模块加载,并且无论是开发环境
还是生产环境
,都可以将其他格式的产物(如commonjs)转换为ESM* 语法转译方面,Vite内置了对TypeScript、JSX、Sass等高级语法的支持,也能够加载各种各样的静态资源,如图片、Worker等* 产物质量方面,Vite基于成熟的打包工具Rollup
实现生产环境打包,同时可以配合Terser
、Babel
等工具链,可以极大程度保证构建产物的质量### ES6 Module
ESM是由ECMAScript官方提出的模块化规范,作为一个官方提出的规范,ESM得到了现代浏览器的内置支持,在现代浏览器中,如果在HTML中加入含有type='module'
属性的script标签,那么浏览器会按照ESM规范来进行依赖加载
和模块解析
,这也是Vite在开发阶段实现no-bundle
的原因,由于模块加载的任务交给了浏览器,即使不打包也可以顺利运行模块代码。
Webpack为什么不能实现nobundle
平时开发用的都是webpack-dev-server
,实际上是调用webpack core
来构建项目,而webpack core正是问题所在,它整体的设计都是基于bundle
的,包括所有的loader
和plugin
都是基于这个约定开发的,所以如果要做esm按需加载,需要重构webpack core
甚至是重构生态
模块化发展历程
1.在没有模块规范时,主要以文件划分
、命名空间
、立即执行函数
来解决,但最终没有解决模块的加载问题,以及模块之间的依赖关系需要手动维护2.有了模块规范时* CommonJS是最先出来的,同步加载,拖慢速度,主要用于服务器端* AMD后出,主要用于浏览器端,异步加载,但是没有得到原生支持,需要第三方库来实现,如RequireJS* CMD,阿里的,和AMD类似,需要第三方库SeaJS支持* UMD,不算规范,只是兼容AMD和CommonJS的一个模块化方案,可以同时运行在浏览器和服务端* ESM,官方标准规范,浏览器原生支持,在CommonJS中也支持,可以混搭,跨平台Vite初相识
项目初始化
pnpm create vite
vite究竟有啥魔力
在index.html
中我们看到,body
中除了root
根节点,还包含了一个声明了type='module'
的script
标签,同时src指向了/src/main.tsx
,此时相当于请求了http://localhost:3000/src/main.tsx
这个资源,Vite的Dev Server此时会接收到这个请求,然后读取相应的文件内容,进行一定的中间
处理,最后将处理结果返回给浏览器
<script type="module" src="/src/main.tsx"></script>
浏览器并不能识别TSX语法呀,也无法直接import css文件,又怎么执行代码呢,这就归功于Vite Dev Server
所做的中间处理
了,也就是说,在读取到main.tsx
文件的内容之后,Vite会对文件的内容进行编译成浏览器可以识别的代码,与此同时,一个import语句即代表一个HTTP请求,Vite Dev Server会读取本地文件,返回浏览器可以解析的代码,当浏览器解析到新的import语句,又回发出新的请求,以此类推,直到所有的资源都加载完成
no-bundle真正的含义
利用浏览器原生ES模块的支持,实现开发阶段的Dev Server,进行模块的按需加载,而不是先完整打包再进行加载
接入CSS方案
社区对CSS的解决方案
1.CSS预处理器
:主流的包括Sass/Scss
、Less
和Stylus
2.CSS Modules
:能将CSS类名处理成哈希值,这样就可以避免同名的情况下样式污染
的问题
3.CSS后处理器PostCSS
:用来解析和处理CSS代码,可以实现的功能例如px
转ren
,自动加属性前缀等
4.CSS in JS
,主流的有emotion
、styled-components
等,基本包含CSS 预处理器
和CSS Modules
的各项优点,非常灵活,解决了开发体验和全局样式污染的问题
5.CSS原子化框架,如Tailwind CSS
、Windi CSS
通过类名来指定样式,大大简化了样式写法,提高了样式开发的效率,主要解决了原生CSS开发体验
的问题
配置Sass
pnpm i sass -D
css文件名以scss
结尾
全局variable.scss
的自动引入方案,即不再需要在各个文件中引入variable.scss
// vite.config.ts
import {normalizePath} from 'vite'
// 如果类型报错,需要安装@types/node:pnpm i @types/node -D
import path from 'path'
// 全局scss文件的路径
// 用normalizePath解决window下的路径问题
const variablePath=normalizePath(path.resolve('./src/variable.scss'))
export default defineConfig({// css 相关的配置css:{preprocessOptions:{scss:{// additionalData的内容会在每个scss文件的开头自动引入additionalData:`@import "${variablePath}";`}}}
})
配置CSS Modules
直接将index.scss
更名为index.module.scss
,然后改动引入,标签的类名就会被处理成哈希值的形式
自定义哈希值格式:
// vite.config.ts
export default{css:{modules:{// 一般我们可以通过generateScopedName属性来对生成的类名进行自定义 // 其中,name表示当前文件名,local表示类名generateScopedName:"[name]__[local]__[hash:base64:5]"},}
}
配置PostCSS
pnpm i autoprefixer -D
自动加前缀的插件
// vite.config.ts 增加如下的配置
import autoprefixer from 'autoprefixer'
export default{css:{// 进行PostCSS配置postcss:{plugins:[autoprefixer({// 指定目标浏览器overrideBrowserslist: ['Chrome > 40', 'ff > 31', 'ie 11']})]}}
}