Vite+React前端实践

Posted 非著名coder

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vite+React前端实践相关的知识,希望对你有一定的参考价值。

Vite+React前端实践

2021年2月17日,Vite2.0发布了,在前端圈引起了 不小的轰动。至于引起轰动的原因恐怕要数官网上介绍的这几点原因:

  • 去掉打包步骤,快速的冷启动。
  • 及时的模块热更新,不会随着模块变多而使得热更新变慢。
  • 真正的按需编译。

Vite是基于浏览器native的ES module开发的,基于Bundleless思想。在了解Vite前,需要先了解Bundle和Bundleless。Bundle和Bundleless是两种开发方式,自 2015 年 ESM 标准发布后,这两种开发方式就逐渐明确。

在日常开发中,一般是使用Webpack 对代码进行编译,并打包生成Bundle文件。其原因如下:

  • 很多应用都是运行在HTTP/1.1上,并且各浏览器有连接限制。
  • 浏览器不支持的模块系统不能直接运行,如CommonJS。
  • 新的语法浏览器不识别。
  • 代码依赖关系与顺序管理。

但是,在项目达到一定的规模后,基于 Bundle 构建优化的收益就变得越来越有限,无法实现质的提升。Webpack 变慢的主要原因是,它将各个资源打包整合在一起形成 Bundle,项目规模越大,资源就越多。是否可以不用打包直接在浏览器中执行代码呢?当然可以,这就是Bundleless。

在 Bundleless 模式下,应用不再需要构建成一个完整的 Bundle,修改文件时也不需要重新生成Bundle文件,浏览器只需要重新加载单个文件即可。 也就是说,只需要刷新即可即时生效。如果有 HotModuleReplace等相关技术加持,则可以实现完美的开发体验。

实现 Bundleless 一个很重要的前提是模块的动态加载能力,实现这个功能主要的思路有两个:

  • 使用System.js 之类的 ES 模块加载器,优点是具有很好的模块兼容性。
  • • 直接利用标准的 ES Module。该module实现已经标准化,并且各个浏览器厂商也已纷纷支持(Edge79,Firefox 67,Chrome63, Safari11.1和Opera50, 这是几个浏览器支持ES module的最低版本 )。相信以后前端同时整体架构在各种标准化的基础上也会变得更加简单。

Bundle和Bundleless的对比如表2所示

BundleBundleless
启动时间时间较长短,只启动Server ,其他可按需加载
构建时间随项目体积线性增长构建时间复杂度O(1)
加载性能打包后加载对应的Bundle请求映射的本地文件
缓存能力缓存利用率一般,受split方式影响缓存利用率近乎完美
文件更新重新打包重新请求单个文件
调试体验需要SourceMap不强依赖SourceMap,可对单个文件进行调试
生态比较完善目前相对不成熟,但是方案已越来越多
表2

基于ES module的构建,其实Vite并不是首创,同样的实践在之前有类似的轮子,如esbuild、snowpack、es-dev-server等。下面通过实例讲解Vite是如何进行开发的。

与常见的开发工具一样,Vite 提供了用 npm 或者 yarn 一键生成项目结构的方式。这里使用 yarn生成一个React项目:

yarn create vite-app vite-project
cd vite-project
yarn install

目录结果如图3所示。

├── index.html
├── node_modules
├── package.json
├── src
|  ├── App.css
|  ├── App.jsx
|  ├── api
|  |  ├── request.js
|  |  ├── serviceApi.js
|  |  └── urlConfig.js
|  ├── constants
|  |  └── statusCode.js
|  ├── contanier
|  |  ├── home
|  |  └── main
|  ├── favicon.svg
|  ├── index.css
|  ├── logo.svg
|  ├── main.jsx
|  ├── routers
|  |  ├── history.js
|  |  └── index.js
|  └── utils
├── vite.config.js
└── yarn.lock
图3

index.html为页面入口,main.jsx为系统主入口,vite.config.js为配置文件,该文件可以类比vue项目的vue.config.js。

在项目开始之前,先引入几个项目核心库:核心库react-router-dom和history, UI库ant design,ajax库axios和css预处理器Less。

第1步,配置组件库,因为在后面的组件中会用到UI组件。注意,组件库可以在配置文件中引入,而不是在main.jsx中引入。如果在main.jsx中引入,则在创建项目时构建工具会把整个CSS文件全部引入,这是没有必要的,所以尝试按需加载。

Vite需要借助插件vite-plugin-imp 来按需加载:

yarn add vite-plugin-imp -D

在vite.config.js中配置插件:

import vitePluginImp from 'vite-plugin-imp'
 plugins: [
    vitePluginImp({
      libList: [
        {
          libName: "antd",
          style: (name) => `antd/lib/${name}/style/index.less`,
        },
      ],
    })
  ],
   css: {
    preprocessorOptions: {
      less: {
        // 支持内联 javascript
        javascriptEnabled: true,
      }
    }
  }

CSS预处理器提取公有css变量及css函数并放在一个文件中,所以确认增加以上配置。并且配置 javascriptEnabled为 true,支持 less 内联 JS。

另一个比较实用的功能是自动刷新,Vite也没有掉队,借助插件@vitejs/plugin-react-refresh即可实现:

import reactRefresh from '@vitejs/plugin-react-refresh'
plugins: [
   reactRefresh()
]

短路径配置:

resolve: {
    alias: {
      "@": path.resolve(__dirname, 'src') 
    }
 },

代理配置:

server : {
    proxy: {
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\\/api/, '')
      }
    }
  }

环境参数配置

在日常开发中,有些代码或者配置是要区分环境的。在Webpack 中可以在scripts中定义NODE_ENV或者在webpack .config.js中定义DefinePlugin区分环境。在Vite中,使用dotenv环境目录 中的下列文件加载额外的环境变量。

如建立开发环境配置配置.env.development,生产环境配置.env.production,需要注意的是:第一,需要安装dotenv依赖包;第二:只有以 VITE_ 为前缀的变量才会暴露到import.meta.env上。

VITE_ENV_FLAG=DEV

就可以使用 import.meta.env.VITE_ENV_FLAG来获取。

还可以在vite中可以通过在scripts中定义mode实现:

"dev": "vite --mode development",
//环境值
const env = process.argv[process.argv.length - 1]
console.log("当前环境:", env) // development

ESLint + Prettier

  1. .eslintignore:配置 esLint 忽略检查的文件

  2. .eslintrc:esLint 编码规则配置

    yarn add eslint eslint-plugin-react  -D
    

    增加如下配置:

module.exports = {
  root: true,
  settings: {
    react: {
      version: "detect",
    },
  },
  env: {
    browser: true,
    amd: true,
    es6: true,
    node: true,
  },
  extends: ["eslint:recommended", "plugin:react/recommended"],
  parserOptions: {
    ecmaVersion: 2020,
    sourceType: "module",
    ecmaFeatures: {
      jsx: true,
    },
  },
  rules: {
    "react/jsx-uses-react": 2,
  },
};
  1. 增加.prettierrc文件,用来做文件格式化
{
 "singleQuote": true,
 "tabWidth": 2,
 "bracketSpacing": true,
 "trailingComma": "none",
 "printWidth": 100,
 "semi": false,
 "overrides": [
   {
     "files": ".prettierrc",
     "options": { }
   }
 ]
}

页面实现:

先在container目录下新建两个组件: home和main。当path为“/”时是渲染home组件,当path为“/main”时是渲染main组件:

// container/home/home.jsx
import { Button } from 'antd'
function Home() {
  return (
    <div>
      <div>Home, from router</div>
      <Button type="primary">submit</Button>
    </div>
  );
}
//container/main/index.jsx
function Main() {
  return (
    <div>
      Main, from router
    </div>
  );
}

有了组件之后,下面开始配置router,在routers目录下建立index.js:

import Home  from "@/contanier/home"
import Main from "@/contanier/main"
export default [
  {
    path: "/",
    component: Home
  },
  {
    path: "/main",
    component: Main 
  }
]

定义配置后,需要在app.jsx中遍历这个数组,生成路由配置:

//app.jsx
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import routes from "./routers"
function App() {
  const [count, setCount] = useState(0)
  return (
    <Router>
      <div className="App">
        <header className="App-header">
          // 省略部分代码
          <Switch>
            {
              routes.map(route => <Route exact key={route.path} path={route.path}>
                <route.component />
              </Route>)
            }
          </Switch>
        </header>
      </div>
    </Router>    
  )
}

先启动项目,看看效果,如图4所示。

yarn run dev

vite v2.1.2 dev server running at:
  > Local:    http://localhost:3000/
  > Network:  http://192.168.1.6:3000/
  > Network:  http://192.168.192.196:3000/

图4

然后再输入http://localhost:3000/main,效果如图5所示。

图5

有了页面组件之后,就需要考虑AJAX请求的事儿了,否则页面是没有灵魂的。在api目录下新建request.js,先对axios做一层封装,配置request和response拦截器,这也是前端开发里面的通用做法。

import axios from "axios";
import StatusCode from "@/constants/statusCode";

const instance = axios.create({
  baseURL: "",
  timeout: 50000,
  xsrfCookieName: "xsrf-token",
});
//请求拦截器,如果需要在hearder中增加一些参数,则可以在这里统一处理
instance.interceptors.request.use(
  (config) => {
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
// 添加一个响应拦截器,对每次返回的数据进行拦截,并进行业务判断
instance.interceptors.response.use(
  (response) => {
    return Promise.reject(response.data);
  },
  (error) => {
    return Promise.reject(error);
  }
);

axios拦截器为我们的日常开发提供了很多便利,如果需要在每个请求中增加相同的参数,则可以在request拦截器中进行配置。如果是统一处理返回的数据,如无权限、404、没有登录等这种通用场景,则可以统一在response的拦截器中进行处理。

以上是Vite项目的基本配置,摘自新书《前端开发必知必会:从工程核心到前沿实战》, 这本书以工程化为切入点,详细介绍工程化涉及的多个环节:web开发的管家package.json, Babel常用配置和ES规范,单元测试,前端文档生成,构建工具等。解析常用的几种设计模式,模块化,宏任务和微任务,让开发者了解其中的所以然。提高前端开发核心价值。并且对微应用实现细节、Docker和前端融合,前端怎么介入Webassembly进行了详细的介绍。


京东入口
淘宝入口
当当入口

以上是关于Vite+React前端实践的主要内容,如果未能解决你的问题,请参考以下文章

Vite+React前端实践

Vite+React前端实践

Vite + React 组件开发实践

Vite + React 组件开发实践

使用Vite快速构建前端React项目

基于Vite+React构建在线Excel