likes
comments
collection
share

Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

作者站长头像
站长
· 阅读数 19

前言

作为前端开发者,或多或少都接触过打包工具,如 webpackRollupParcel等打包工具,它们极大地改善了前端开发者的开发体验。

然而,当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。我们开始遇到性能瓶颈 —— 使用 JavaScript 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

Vite 的出现就是想要解决上述的问题!

本文将分文两个大的部分:

  • Vite 概念和设计思想
  • Vite vs CRA
  • 使用 Vite + React 开发 ToDoList

正文

Vite 概念和设计思想

带着问题去思考:

  1. 什么是打包呢?

使用工具抓取、处理并将我们的源码模块串联成可以在浏览器中运行的文件。

  1. 那我们熟悉的常用的前端构建打包工具有哪些呢?:

webpack、rollup、pracel、gulp

  1. 这些传统的打包工具存在什么问题呢?
  • 缓慢的服务启动:当冷启动开发服务器时,基于打包器的方式启动必须优先抓取并构建你的整个应用,然后才能提供服务。

Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

  • 缓慢的更新:基于打包器启动时,重建整个包的效率很低。原因显而易见:因为这样更新速度会随着应用体积增长而直线下降
  1. Vite 是怎么解决上述的问题?
  • Vite 通过在一开始将应用中的模块区分为 依赖 和 源码 两类,改进了开发服务器启动时间。
    • 依赖:Vite 将会使用 esbuild 预构建依赖。Esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。
    • 源码:通常为JSX、CSS 或者 Vue SFC等,时常会被编辑,需要转换,基于路由拆分。
  • Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。

Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

  • Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界之间的链失活大多数时候只是模块本身),使得无论应用大小如何,HMR 始终能保持快速更新。
  • Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。

最后再简单介绍一下, Vite 由两部分组成:

  • 一个开发服务,服务于开发环境,使用 ESM+HMR
  • 一套构建指令,服务于生产环境,使用 Rollup 打包

Vite vs CRA

你说好就好了?来点数据证明一下? 接下来使用 Vitecreate-react-app来构建以及打包,并记录时间来对比一下:

首先使用create-react-app创建一个空的React工程:

开始计时

npx create-react-app my-app
cd my-app
npm start

执行以上命令,打开网页为止,停止计时,记录构建时间 Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

使用 Vite创建一个空的React工程(Vite 虽然是尤大大写的但是对 React 的支持甚至比 Vue 更好):

使用 NPM:

$ npm init vite@latest

使用 Yarn:

$ yarn create vite

使用 PNPM:

$ pnpm create vite

开始计时

Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

*vanilla 是相当于原生 JS 不使用框架,其他几个应该都是框架,如图选择 react,然后输入以下命令

  cd vite-react
  yarn
  yarn dev

执行以上命令,打开网页为止,停止计时 Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

然后分别执行打包,进入package.json,查看打包命令,并执行:

yarn build

分别记录时间,并查看打包产物的大小:Vite生成的工程,打包产物在dist 目录,CRA的在 build

表格记录对比:

ViteCRA
初始化时间48s1min 17s
打包时间3s11s
产物大小156kb565kb

结论:

以上只是在无业务代码的工程中测试,随着业务代码量级的提升,时间对比会更加的明显。

好了数据说话的过程也结束了,这下能放心了。

不过一学就会,一敲就废的情况我们还是要通过一些实战代码来避免一下。

使用 Vite + React 开发 ToDoList

复习一下刚才使用 Vite 来创建一个 React 工程:

个人的习惯,作者使用 Yarn:

$ yarn create vite

framework 选择 reactvariant 根据个人是否启用 TS

推荐的一种目录,这些也因人而异,因项目大小而异:

Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

接下来就是一些常规的 React 开发;

main.tsx:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import 'antd/dist/antd.css';
import './index.css'

ReactDOM.render(<App />, document.getElementById("root"));

App.tsx:

import { useState } from "react";

import ToDoItem from "../components/ToDoItem";
import ToDoContainer from '../components/ToDoContainer'
import { TodoItemType } from "../constans";

function App() {

  const [todos,setTodos] = useState<TodoItemType[]|[]>([])

  const handleSubmit = (todoItem: any)=>{
    console.log(todoItem)
    setTodos([
      ...todos,
      todoItem
    ])
  }

  const handleOpreta =(todoItem:TodoItemType)=>{
    const newTodos = todos.filter(todo =>todoItem.id !== todo.id)
    newTodos.push(todoItem)
    console.log(newTodos)
    setTodos(newTodos)
  }

  return (
    <div className="todo-app">
      <h2 className="todo-title">代办清单</h2>
      <ToDoItem onSubmit={handleSubmit} />
      <ToDoContainer onOpreta={handleOpreta} todos={todos}/>
    </div>
  );
}

export default App;

/components/ToDoContainer/index.tsx:

import { List, Radio } from "antd";
import { STATUS, TodoItemType } from "../../constans";
import { CheckOutlined, CloseCircleOutlined } from "@ant-design/icons";
import { useState } from "react";

const ToDoContainer = (props: {
  todos?: TodoItemType[] | undefined;
  onOpreta: (arg: TodoItemType) => void;
}) => {
  const { todos = [], onOpreta } = props;
  const [optionsValue, setOptionsValue] = useState(STATUS.IS_CREATE);

  const options = [
    { label: "全部", value: '' },
    { label: "代办", value: STATUS.IS_CREATE },
    { label: "已办", value: STATUS.IS_DONE },
    { label: "已删除", value: STATUS.IS_DELETE },
  ];

  const handleChangeRadio = (e: any) => {
    console.log(e.target.value);
    setOptionsValue(e.target.value);
  };

  const handleOpreta = (opretaType: string, todo: TodoItemType) => {
    switch (opretaType) {
      case "is-delete":
        onOpreta &&
          onOpreta({
            ...todo,
            status: STATUS.IS_DELETE,
          });
        break;
      case "is-done":
        onOpreta &&
          onOpreta({
            ...todo,
            status: STATUS.IS_DONE,
          });
        break;
      default:
        break;
    }
  };

  return (
    <div>
      <Radio.Group
        options={options}
        onChange={handleChangeRadio}
        value={optionsValue}
        optionType="button"
        buttonStyle="solid"
      />
      <List
        className="todo"
        dataSource={todos}
        renderItem={(todo) =>
          optionsValue === todo.status && (
            <List.Item className="todo-list-item-container">
              {todo.content}
              <div>
                <CloseCircleOutlined
                  onClick={() => handleOpreta("is-delete", todo)}
                />
                <CheckOutlined onClick={() => handleOpreta("is-done", todo)} />
              </div>
            </List.Item>
          )
        }
      />
    </div>
  );
};

export default ToDoContainer;

/components/ToDoItem/index.tsx:

import { Button, Input } from "antd";
import { useState } from "react";
import { STATUS } from "../../constans";

const ToDoItem = (props: { onSubmit: (todoItem: any) => void }) => {
  const { onSubmit } = props;
  const [todoItem, setToDoItem] = useState({
    id: Math.random(),
    content: "",
    status: STATUS.IS_CREATE,
  });

  const handleSubmit = (e: any) => {
    onSubmit && onSubmit({ ...todoItem, id: Math.random() });
  };

  const handleInpurChange = (e: any) => {
    setToDoItem({
      ...todoItem,
      content: e.target.value,
    });
  };

  return (
    <div className="todo-item-input">
      <Input
        value={todoItem.content}
        onPressEnter={handleSubmit}
        onChange={handleInpurChange}
      />
      <Button size="large" type="primary" onClick={handleSubmit}>
        提交
      </Button>
    </div>
  );
};

export default ToDoItem;

/constans/index.ts:

export interface TodoItemType{
    id:number,
    content:string,
    status:number
}

export const STATUS = {
    /**
     * 全部
     */
    
    /**
     * 代办
     */
    IS_CREATE:0,
    /**
     * 已解决
     */
    IS_DONE:1,
    /**
     * 假删除
     */
    IS_DELETE:2
}

代码也没有完善,大家可以在我基础上进行完善。 ··· 开发完了,当然还有部署,推荐一个免费在线的部署网站:app.netlify.com/ ,使用github就能够直接登录

执行打包命令:

yarn build

Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

域名会出现这这个位置:

Vite 零基础极速入门前言 作为前端开发者,或多或少都接触过打包工具,如 webpack、Rollup、Parcel等

结语

以上就是关于Vite快速入门的全部内容,欢迎大佬们指正。

天气转凉了,大家多喝热水,多注意保暖休息。

转载自:https://juejin.cn/post/7029204845802307598
评论
请登录