深入浅出Vite&&搭建vite项目&&css工程化&&代码规范&&静态资源优化(2)


项目环境搭建

  1. node -v
    推荐 12.0.0 及以上版本,如果低于这个版本,推荐使用 nvm 工具切换 Nodejs 版本。

当然,在现代的前端项目中,我非常不推荐使用 npm 作为项目的包管理器,甚至也不再推荐yarn(npm 的替代方案),因为两者都存在比较严重的性能和安全问题,而这些问题在 pnpm 中得到了很好的解决

  1. 因此,包管理器方面我推荐使用 pnpm,安装方式非常简单,输入如下命令即可:
1
npm i -g pnpm

由于默认的镜像源在国外,包下载速度和稳定性都不太好,因此我建议你换成国内的镜像源,这样pnpm install命令的体验会好很多,命令如下:

1
pnpm config set registry https://registry.npmmirror.com/

项目初始化

1.命令

1
pnpm create vite

官网

项目入口加载

言归正传,我们继续学习 Vite 初始化后的项目。项目的目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── index.html
├── package.json
├── pnpm-lock.yaml
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── favicon.svg
│ ├── index.css
│ ├── logo.svg
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
└── vite.config.ts

值得注意的是,在项目根目录中有一个index.html文件,这个文件十分关键,因为 Vite 默认会把项目根目录下的index.html作为入口文件。也就是说,当你访问http://localhost:3000的时候,Vite 的 Dev Server 会自动返回这个 HTML 文件的内容。
可以看到这个 HTML 文件的内容非常简洁,在 body 标签中除了 id 为 root 的根节点之外,还包含了一个声明了type=”module”的 script标签:

1
<script type="module" src="/src/main.tsx"></script>

由于现代浏览器原生支持了 ES 模块规范,因此原生的 ES 语法也可以直接放到浏览器中执行,只需要在 script 标签中声明 type=”module” 即可。比如上面的 script 标签就声明了 type=”module”,同时 src 指向了/src/main.tsx文件,此时相当于请求了http://localhost:3000/src/main.tsx这个资源,Vite> 的 Dev Server 此时会接受到这个请求,然后读取对应的文件内容,进行一定的中间处理,最后将处理的结果返回给浏览器。

vite
我们可以来看看 main.tsx 的内容:

1
2
3
4
5
6
7
8
9
10
11
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)

到这里可能你会诧异: 浏览器并不识别 tsx 语法,也无法直接 import css 文件,上面这段代码究竟是如何被浏览器正常执行的呢?

这就归功了 Vite Dev Server 所做的“中间处理”了,也就是说,在读取到 main.tsx文件的内容之后,Vite 会对文件的内容进行编译,大家可以从 Chrome 的网络调试面板看到编译后的结果:
调试

这里你只需要知道,Vite 会将项目的源代码编译成浏览器可以识别的代码,与此同时,一个 import 语句即代表了一个 HTTP 请求,如下面两个 import 语句:

1
2
import "/src/index.css";
import App from "/src/App.tsx";

需要注意的是,在 Vite 项目中,一个import 语句即代表一个 HTTP 请求。上述两个语句则分别代表了两个不同的请求,Vite Dev Server 会读取本地文件,返回浏览器可以解析的代码。当浏览器解析到新的 import 语句,又会发出新的请求,以此类推,直到所有的资源都加载完成。

现在,你应该知道了 Vite 所倡导的no-bundle理念的真正含义: 利用浏览器原生 ES 模块的支持,实现开发阶段的 Dev Server,进行模块的按需加载,而不是先整体打包再进行加载。相比 Webpack 这种必须打包再加载的传统构建模式,Vite 在开发阶段省略了繁琐且耗时的打包过程,这也是它为什么快的一个重要原因。

CSS工程化

那么,如果我们不用任何 CSS 工程方案,又会出现哪些问题呢?

  1. 开发体验欠佳。比如原生 CSS 不支持选择器的嵌套:
1
2
3
4
5
6
7
8
// 选择器只能平铺,不能嵌套
.container .header .nav .title .text {
color: blue;
}
.container .header .nav .box {
color: blue;
border: 1px solid grey;
}
  1. 样式污染问题。如果出现同样的类名,很容易造成不同的样式互相覆盖和污染。
1
2
3
4
5
6
7
8
9
// a.css
.container {
color: red;
}
// b.css
// 很有可能覆盖 a.css 的样式!
.container {
color: blue;
}
  1. 浏览器兼容问题。为了兼容不同的浏览器,我们需要对一些属性(如transition)加上不同的浏览器前缀,比如 **-webkit-、-moz-、-ms-、-o-**,意味着开发者要针对同一个样式属性写很多的冗余代码。
  2. 打包后的代码体积问题。如果不用任何的 CSS 工程化方案,所有的 CSS 代码都将打包到产物中,即使有部分样式并没有在代码中使用,导致产物体积过大。

针对如上原生 CSS 的痛点,社区中诞生了不少解决方案,常见的有 5 类

  1. CSS 预处理器:主流的包括Sass/Scss、Less和Stylus。这些方案各自定义了一套语法,让 CSS 也能使用嵌套规则,甚至能像编程语言一样定义变量、写条件判断和循环语句,大大增强了样式语言的灵活性,解决原生 CSS 的开发体验问题。
  2. CSS Modules:能将 CSS 类名处理成哈希值,这样就可以避免同名的情况下样式污染的问题。
  3. SS 后处理器PostCSS,用来解析和处理 CSS 代码,可以实现的功能非常丰富,比如将 px 转换为 rem、根据目标浏览器情况自动加上类似于–moz–、-o-的属性前缀等等。
  4. CSS in JS 方案,主流的包括emotion、styled-components等等,顾名思义,这类方案可以实现直接在 JS 中写样式代码,基本包含CSS 预处理器和 CSS Modules 的各项优点,非常灵活,解决了开发体验和全局样式污染的问题。
  5. CSS 原子化框架,如Tailwind CSS、Windi CSS,通过类名来指定样式,大大简化了样式写法,提高了样式开发的效率,主要解决了原生 CSS 开发体验的问题。

参考

代码规范:使用Link工具链保证代码风格和质量

介绍了 3 个方面的自动化代码规范工具:

  1. JavaScript/TypeScript 规范。主流的 Lint 工具包括 Eslint、Prettier;
  2. 样式开发规范。主流的 Lint 工具包括Stylelint、Prettier;
  3. Git 提交规范。主流的 Lint 工具包括Commitlint。

如何加载静态资源和如何在生产环境中对静态资源进行优化

首先是如何加载各种静态资源,如图片、svg(组件形式)、JSON、Web Worker 脚本、Web Asssembly 文件等等格式,并通过一些示例带大家进行实际的操作。

其次,我们会把关注点放到生产环境,对自定义部署域名、是否应该内联、图片压缩、svg 雪碧图等问题进行了详细的探讨和实践,对于如何解决这些问题,相信你也有了自己的答案。

当然,在编码实操的过程当中,我也给你穿插了一些 Vite 其他的知识点,比如如何定义环境变量文件、如何使用 Glob 导入的语法糖。相信在学习本节的过程中你能更加体会到 Vite 给项目开发带来的便利,同时也对 Vite 的掌握更深入了一步。

HTTP2 的多路复用设计可以解决大量 HTTP 的请求导致的网络加载性能问题,因此雪碧图技术在 HTTP2 并没有明显的优化效果,这个技术更适合在传统的 HTTP 1.1 场景下使用(比如本地的 Dev Server)。

参考

深入浅出 Vite