如果不使用 Vite
官方提供的 template
选项,要如何从一个 Vite
依赖的情况下逐步配置启动一个 React
?如何实现 Webpack
的静态资源服务,如何实现基于 Webpack
的 loader
的 JSX
的自动转换?
下面是这个项目运行效果,以及完整的示例源代码
普通启动
如果你是刚开始接触 HTML/CSS/JavaScript
三件套开始接触的前端,那么你可能比较熟悉或者比较能接受的引入 React
的方式可能是使用 CDN
的方式,大概如下(下面这个是我要介绍的例子)
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const useState = React.useState;
const createElement = React.createElement;
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((count) => count + 1);
};
return createElement("div", {
children: [
"count:" + count,
createElement(
"button",
{
key: "2",
onClick: handleClick,
},
"click + 1"
),
],
});
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
createElement(React.StrictMode, {
children: [createElement(App, { key: "1" })],
})
);
</script>
但这种方式是基于 React.createElement
去编写 DOM
的代码的,React
中比较常用的是 JSX
语法,它们的文档认为 JSX
它有助于编写UI代码 - 无论是使用 React 还是其他库。
而 React
的文档推荐使用 JSX
的方式是使用 babel
转换,其实使用 babel
这已经涉及到前端工程化的初步阶段了,因为使用 babel
需要使用 node.js
,毕竟你知道了 node.js
后就会知道 npm
就会知道 webpack
就会知道 create-react-app
所以一般来说接触 React
的同学很快会过度到基于 Node.js
的现代前端工程化开发
关于什么是基于 Node.js
的现代前端工程化开发?也许就是下面的 Vite
Vite 启动
在进入 Vite
配置之前先做好基于 Node.js
环境的配置
"Node.js" 配置
先找个位置并在终端(或者命令行)初始化一个项目
npm init
初始化后会有一些选项,可以直接回车全部忽略,也可以根据自己意向填写
选择完成之后
这个时候系统会创建一个 package.json
文件
喜欢记录的同学可以添加 git
(记得添加 .gitignore
避免添加 node_modules/.vscode/.idea
进入版本依赖)
git init
然后将上面的 HTML
例子的 CDN
切换成 npm
包,比如在终端/命令行执行以下命令
npm install react react-dom
解构 "HTML"
下载完 React
系列套件的依赖后,就需要将上面的 html
例子进行解构,创建 ./src/index.js
、 ./src/App.js
和 ./index.html
./
指代当前工作目录(根目录)
将上面的 html
的例子转换成下面的结构
// App.js
import { useState, createElement } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((count) => count + 1);
};
return createElement("div", {
children: [
"count:" + count,
createElement(
"button",
{
key: "2",
onClick: handleClick,
},
"click + 1"
),
],
});
}
export default App;
// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
React.createElement(React.StrictMode, {
children: [React.createElement(App, { key: "1" })],
})
);
注意,在 .js
文件中都是使用的 ES Module
而不是 Commonjs
规范是因为此处编写的 .js
将直接在浏览器中运行并且不会配置类似 Webpack
提供的统一规范配置和转换
<!-- index.html -->
<body>
<div id="root"></div>
</body>
<script src="./src/index.js"></script>
此时的文件目录结构
├── node_modules
├── public
| └── index.html
├── src
| ├── App.js
| └── index.js
├── package-lock.json
├── package.json
配置 Vite
- 下载
Vite
依赖
npm install -D vite
- 在根目录创建
Vite
配置文件,vite.config.js
// vite.config.js
import { defineConfig } from "vite";
// https://vitejs.dev/config/
export default defineConfig({
// root 默认值是 process.cwd(), 即当前工作文件夹
// index.html 中引用的 .js 需要在同一个 root 目录下
root: process.cwd(),
server: {
port: 5173, // 默认端口是 5173
},
});
注意,Vite
的关于运行配置里只有 index.html
需要指定,并不像 webpack
那样需要指定一个 .js
的入口文件,这是因为 Wepback
需要打包模块至一个 .js
文件而 Vite
是采用逐个请求处理后的模块
- 配置
package.json
的脚本
{
// ..
"scripts": {
// 新增部分
"dev": "vite",
},
// ...
"dependencies": {
"vite": "^3.2.4",
// ...
}
// ...
}
- 运行
npm run start
,得到预期效果
配置 JSX
JSX
在 React
的地位等同于 Vue
的 SFC
,项目里面不配置一下完全无法展开呀!可以看到上面的 HTML
例子其实使用了 React.createElement
代替 JSX
,接下来就为了更好的开发体验配置一下 JSX
- 下载
@vitejs/plugin-react
依赖
npm install -D @vitejs/plugin-react
- 在
vite.config.js
新增以下内容
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
// ...
plugins: [react()],
});
@vitejs/plugin-react
的底层其实是 @babel/plugin-transform-react-jsx
,和 Webpack
的 JSX
配置中 @babel/preset-react
用的是同一个,@vitejs/plugin-react
同样也支持 .babelrc
和 babel.config.js
配置
- 修改项目中关于
React.createElement
至JSX
,并修改文件后缀为.jsx
// index.js -> index.jsx
// ...
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// App.js -> App.jsx
import { useState } from "react";
function App() {
// ...
return (
<div>
{"count:" + count}
<button onClick={handleClick}>click + 1</button>
</div>
);
}
export default App;
<!-- index.html -->
<body>
<div id="root"></div>
</body>
<script src="./src/index.jsx"></script>
- 运行
npm run start
,得到预期效果
留空问题
在阅读文章的时候可以去思考一些问题,比如
Webpack
在打包的过程中会有转换(transform
)的流程,这是loader
和plugin
干的事,比如.vue to .js
,又比如html-webpack-plugin
会对打包.js
对静态资源服务的index.html
的变量自动导入,那么Vite
是如何解决这些问题的呢?Vite
实际请求的module
是否有经过transform
?
附带 Vite
原理图