Said in the previous

Today is the first day of the Chinese New Year. First of all, I wish you a happy New Year. 🎉🎉🎉 This article is a New Year gift for you all!

Testing different front-end build tools has always been a strange hobby for me because, to be honest, WebPack is really hard to use. It’s expensive to get started, a jumble of plugins, and, worst of all, a slow startup dev. Until Vite came along, I didn’t realize front-end development could be so silky.

ViteWhat is?

A new front-end build tool from Evan You, the granddaddy of the popular vue.js framework. It consists of two main parts:

  • A throughnative ES modulesA development server that provides source files, with rich built-in features and surprisingly fast hot module replacement (HMR).
  • The code andRollupBundled together, output highly optimized build packages for production.

I’ve seen Vite on various platforms recently (Twitter, GayHub, Nuggets, Zhihu, etc.), especially Evan You, and the speed of updates is amazing. He also promotes Vite on Twitter almost every day, and for those reasons, it’s hard not to try Vite.

purpose

As mentioned above, I am always looking for the best build tool to use, and my requirements are really simple:

  • It must be fast (not slow down as the project grows)
  • It must supportTypescript
  • It must support current mainstream front-end frameworks (includingvue,reactEtc.)
  • It must supportHMR(Hot module replacement)
  • It must supporttree shaking
  • It must support a variety ofCSStool
  • It supports importingSVG.PNG.JSONAnd whatever else we want to import

Be reasonable, in fact these requirements are not too much.

Armed with this list of requirements, let’s move on to see if Vite meets those requirements.

testVite

To test Vite, I created a simple Todo App. Its function is very simple, we can input our to-do items in the to-do page, click the to-do items, that is, we can mark it has been completed, at the same time we can view what we have completed the to-do items in the completed page.

As mentioned above, we wanted to know how vite supports various front-end frameworks, so I decided to implement our Todo App using Vue3, React, and Svelte respectively. It’s also worth noting that although I wrote this article specifically to test Vite, you can learn from start to finish how to use Vite with Vue3, React, and Svelte.

But you might be wondering, why am I also trying to combine Vite and Svelte here? Since Svelte, the new frontend framework, is the most popular frontend framework of 2020, I wanted to give it a try.

The advantages of svelte are not discussed here. As for why Svelte is so popular, check out this article: It’s almost 2020 and you haven’t heard of SvelteJS?

With that said, let’s get started!

Oh, and before we start, one more caveat. Since we tested vue3, React, and Svelte separately, let’s also compare them. I will compare it from the following two dimensions:

  • Development experience
  • Build package volume

The development experience includes support for typescript, state management, route management, and more.

For this purpose, we want to maintain some fairness, which means we implement functionality with as little leverage as possible outside of the framework itself. For example, when we implement state management, we try to use the functionality of the framework itself to achieve.

Ok, with these two purposes in mind, let’s work together!

Vue3 + Vite

In order to put all three Todo apps under one project, we used Lerna to manage our three Todo apps.

# install lerna and init project
$ npm install lerna -g
$ mkdir vite-vue3-react-svelte-todo && cd vite-vue3-react-svelte-todo
$ lerna init
Copy the code

Next we go into Packages and create our Vue3-Todo App. Since Vite itself was implemented by vue’s authors, there is no doubt that vite+vue3 has a template:

$ cd packages
$ yarn create @vitejs/app vue3-todo --template vue-ts
Copy the code

Then enter vuE3-todo and create router, Store, Views, and Components. These are so common that I won’t talk too much about them. Let’s first look at the project structure:

.├ ── index.html ├── Download.json ├─ public │ ├─ favicon. Ico │ ├─ SRC │ ├─ app.vue │ ├─ assets │ ├─ └ ─ ─ logo. PNG │ ├ ─ ─ components │ │ ├ ─ ─ FinishItem. Vue │ │ └ ─ ─ TodoItem. Vue │ ├ ─ ─ main. Ts │ ├ ─ ─ the router │ │ └ ─ ─ index. The ts │ ├ ─ ─ store │ │ ├ ─ ─ action. The ts │ │ ├ ─ ─ but ts │ │ └ ─ ─ state. The ts │ ├ ─ ─ views │ │ ├ ─ ─ Finish. The vue │ │ └ ─ ─ Todo. Vue │ └ ─ ─ ├─ └─ vue-shim. D. bass Exercises ─ viteCopy the code

Now vite2 doesn’t automatically support vue3 in order to accommodate more front-end frameworks, so we need to install @vitejs/plugin-vue as plugins for Vite:

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
	plugins: [vue()]
})

Copy the code

To mention, the route uses the latest official routing library: vue-Router 4.x. This directory can be seen at a glance. We put the state management of todo List into store to manage it. We want to focus on state management here. For the sake of fairness, we will not resort to Vuex here. Now that Vue3 is based on VUE-composition-API, we can use this feature to implement our state management. First we need to create a state:

// store/state.ts
import { reactive } from 'vue'

export interface TodoItemType {
	id: number
	done: boolean
	content: string
}

export type VuexState = {
	todoList: Array<TodoItemType>
}

const state: VuexState = {
	todoList: [{id: 0.done: false.content: 'your first todo'}}]export const createStore = () = > {
	return reactive(state)
}

Copy the code

Next, we need to define some actions to change state, including adding, deleting, changing, and checking to-do items:

// store/action.ts
import { VuexState, TodoItemType } from './state'

function addNewTodoItem(state: VuexState) {
	return (newItem: TodoItemType) = > {
		state.todoList = [...state.todoList, newItem]
	}
}

function delteTodoItem(state: VuexState) {
	return (item: TodoItemType) = > {
		state.todoList = state.todoList.filter((e) = >e.id ! == item.id) } }function changeTodoItemStatus(state: VuexState) {
	return (todoItem: TodoItemType) = > {
		let list = [...state.todoList]
		list.map((item) = > {
			if(item.id === todoItem.id) item.done = ! item.donereturn item
		})
		state.todoList = [...list]
	}
}

export function createAction(state: VuexState) {
	return {
		addNewTodoItem: addNewTodoItem(state),
		delteTodoItem: delteTodoItem(state),
		changeTodoItemStatus: changeTodoItemStatus(state)
	}
}
Copy the code

And then we expose it uniformly:

// store/index.ts
import { readonly } from 'vue'
import { createAction } from './action'
import { createStore } from './state'

const state = createStore()
const action = createAction(state)

export const useStore = () = > {
	return {
		state: readonly(state),
		action: readonly(action)
	}
}

Copy the code

This allows us to take advantage of vue3’s latest features for state management without the need for Vuex. Best of all, we also implement typescript support perfectly.

If you want to see more about managing your own state with VUe3, check out this article: Now that Vuex4 is in beta, can Vuex5 be far behind?

Now that the most important part is out of the way, let’s look at todo.vue:

<template>
	<div class="todo">
		<div class="card">
			<input
				class="input"
				type="text"
				placeholder="your new todo"
				v-model="newItemContent"
				@keyup.enter="addNewTodoItem"
			/>
			<div class="card-content">
				<TodoItem
					v-for="item in todoList"
					:key="item.id"
					:todoItem="item"
					@changeTodoItem="changeTodoItem"
					@delteTodoItem="delteTodoItem"
				/>
			</div>
		</div>
	</div>
</template>

<script lang="ts">
import { defineComponent, computed, ref } from 'vue'
import TodoItem from '.. /components/TodoItem.vue'
import { useStore } from '.. /store/index'
import { TodoItemType } from '.. /store/state'

export default defineComponent({
	name: 'Todo'.components: {
		TodoItem
	},
	setup() {
		let newItemContent = ref(' ')
		const store = useStore()
		const todoList = computed(() = > store.state.todoList)

		function addNewTodoItem() {
			store.action.addNewTodoItem({
				done: false.id: todoList.value.length,
				content: newItemContent.value
			})
			newItemContent.value = ' '
		}

		function changeTodoItem(todoItem: TodoItemType) {
			store.action.changeTodoItemStatus(todoItem)
		}

		function delteTodoItem(todoItem: TodoItemType) {
			store.action.delteTodoItem(todoItem)
		}

		return {
			todoList,
			newItemContent,
			addNewTodoItem,
			delteTodoItem,
			changeTodoItem
		}
	}
})
</script>.Copy the code

Easy, right. On this page, we take the Todo List in State and render each ToDO item. An Input field is also provided, using the V-Model to bind the value of the Input field. When we press enter, the addNewTodoItem method we provided is triggered. This method does two things: it takes the Input value and then adds a Todo item to our store via action Dispatch.

At the same time, we also provide methods to update and delete items. When we check the check box before the item, it indicates that we have completed the to-do item. In todoitem. vue, when we click on the check box of an item, we emit the change to the parent component todo. vue via emit. However, in vue3 we have to change it a little bit.

// components/TodoItem.vue.setup(props, ctx) {
		const { todoItem } = props

		function statusChage() {
			ctx.emit('changeTodoItem', todoItem)
		}

		function deleteTodoItem() {
			ctx.emit('delteTodoItem', todoItem)
		}

		return {
			todoItem,
			statusChage,
			deleteTodoItem
		}
    }
...
Copy the code

To prove that our state did change after we checked our to-do list, we also prepared a finish. vue page that simply checks our completed to-do list:

<template>
	<div class="finish">
		<div class="card">
			<div class="card-content">
				<div class="card-content">
					<FinishItem v-for="item in finishList" :key="item.id" :finishItem="item" />
				</div>
			</div>
		</div>
	</div>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue'
import FinishItem from '.. /components/FinishItem.vue'
import { useStore } from '.. /store/index'

export default defineComponent({
	name: 'Finish'.components: {
		FinishItem
	},
	setup() {
		const store = useStore()
		const finishList = computed(() = > store.state.todoList).value.filter((item) = > item.done)

		return {
			finishList
		}
	}
})
</script>.Copy the code

This way, when we click on a to-do item on the Todo page, we can view the completed to-do item on the Finish page.

So far, we’ve managed state without using a third-party state management library, and we have excellent typescript support. Our Vue3 version of Todo App is done.

Let’s build the vue3 Todo App by running the vite build command:

Well, 285K, not too big, but if you want to see what it looks like online, just click vue3-todo.

Vite + React react

React + Vite

Here, as with Vue3, Vite provides us with a React template, which we can execute with a script command:

$ cd packages
$ yarn create @vitejs/app react-todo --template react-ts
Copy the code

As usual, look at the table of contents first:

.
|-- index.html
|-- package-lock.json
|-- package.json
|-- src
|   |-- App.scss
|   |-- App.tsx
|   |-- components
|   |   |-- FinishItem
|   |   |   |-- index.tsx
|   |   |   |-- styles.scss
|   |   |-- TodoItem
|   |       |-- index.tsx
|   |       |-- styles.scss
|   |-- index.css
|   |-- logo.svg
|   |-- main.tsx
|   |-- pages
|   |   |-- Finish
|   |   |   |-- index.tsx
|   |   |   |-- styles.scss
|   |   |-- Todo
|   |       |-- index.tsx
|   |       |-- styles.scss
|   |-- router
|   |   |-- index.tsx
|   |   |-- styles.scss
|   |-- store
|       |-- index.tsx
|       |-- reducer.ts
|       |-- state.ts
|-- tsconfig.json
`-- vite.config.ts


Copy the code

We set the directory this way to be consistent with vue3’s directory structure. As with 👆vue3, we need to install @vitejs/plugin-react-refresh as plugins for vite:

// vite.config.ts
import reactRefresh from '@vitejs/plugin-react-refresh'
import { defineConfig } from 'vite'

// https://vitejs.dev/config/
export default defineConfig({
	plugins: [reactRefresh()]
})
Copy the code

In addition, because the functions are the same, so we only introduce the different places. The first is routing. Here we use the react-router-dom route, which is the official route for React.

The second is state management, where we use context and useReducer.

First, we still need to create a state:

// store/state.ts
export interface TodoItemType {
	id: number
	done: boolean
	content: string
}

export type StateType = {
	todoList: Array<TodoItemType>
}

const state: StateType = {
	todoList: [{id: 0.done: false.content: 'your first todo'}}]export const createStore = () = > {
	return state
}

Copy the code

Next we need some reducer that can change the state:

// store/reducer.ts
import { StateType, TodoItemType } from './state'

export type ActionType =
	| { type: 'NEW_TODO_ITEM'; todoItem: TodoItemType }
	| { type: 'DELETE_TODO_ITEM'; todoItem: TodoItemType }
	| { type: 'UPDATE_TODO_ITEM'; todoItem: TodoItemType }

export const reducer = (state: StateType, action: ActionType) = > {
	switch (action.type) {
		case 'NEW_TODO_ITEM':
			return {
				...state,
				todoList: [...state.todoList, action.todoItem]
			}
		case 'DELETE_TODO_ITEM':
			return {
				...state,
				todoList: state.todoList.filter((e) = >e.id ! == action.todoItem.id) }case 'UPDATE_TODO_ITEM':
			let list = [...state.todoList]
			list = list.map((item) = > {
				if(item.id === action.todoItem.id) { item.done = ! item.done }return item
			})
			return {
				...state,
				todoList: list
			}
	}
}
Copy the code

We then combine state and Reducer and expose them through useReducer and Contenxt:

// store/index.tsx
import { createStore, StateType } from './state'
import { ActionType, reducer } from './reducer'
import React, { useReducer, createContext } from 'react'

const store = createStore()

export type TodoContextType = {
	state: StateType
	dispatch: React.Dispatch<ActionType>
}

export const TodoContext = createContext<any> ({})const TodoProvider: React.FC = (props) = > {
	const [state, dispatch] = useReducer(reducer, store)
	const contextValue = { state, dispatch }

	return <TodoContext.Provider value={contextValue}>{props.children}</TodoContext.Provider>
}

export default TodoProvider

Copy the code

We wrap the value exposed by the useReducer with the Provider and provide it to all the child components. Then wrap the Router component around app.tsx.

In Todo/index.tsx, we can use useContext to get the value provided by useReducer:

// pages/Todo/index.tsx.const { state, dispatch } = useContext<TodoContextType>(TodoContext)
...
Copy the code

So we can get state and dispatch. By using the context and the useReducer, we’ve replaced Redux perfectly. As with Vue3, we don’t use third-party state management. As for typescript support, I don’t need to tell you that React support for typescript is great.

Vite builds the React Todo App by running the vite build command:

363K, boy, that’s a bit big. If you want to see what’s going on online, just hit React-Todo.

Let’s take a look at vite + svelte.

Svelte + Vite

For Svelte, Vite didn’t provide an official template, so we had to do it ourselves. There is no official template, but we can follow suit. First we create a new directory under the Packages directory: svelte-todo. Then we create the public and SRC directories, index.html, tsconfig.json, and vite.config.ts files. Then we create a new directory and file under the SRC directory. The file directory looks like this:

.├ ── index.html ├── Download.json ├─ public │ ├─ favicon.ico ├─ SRC │ ├─ app.css │ ├─ app.svelte │ ├ ─ ─ assets │ │ └ ─ ─ logo. The SVG │ ├ ─ ─ components │ │ ├ ─ ─ FinishItem. Svelte │ │ └ ─ ─ TodoItme. Svelte │ ├ ─ ─ main. Ts │ ├ ─ ─ Pages │ │ ├ ─ ─ Finish. Svelte │ │ └ ─ ─ Todo, svelte │ ├ ─ ─ the router │ │ └ ─ ─ but svelte │ ├ ─ ─ store │ │ ├ ─ ─ action. The ts │ │ ├ ─ ─ │ ├─ ├─ ├─ ├.txt TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXTCopy the code

To use vite + svelte, install vite and svelte:

"devDependencies": {
    "@tsconfig/svelte": "^ 1.0.10"."svelte-preprocess": "^ 4.6.3." "."typescript": "^ 4.1.3." "."vite": 50 "" ^ 2.0.0 - beta.."vite-plugin-svelte": "https://github.com/benmccann/vite-plugin-svelte"
  },
  "dependencies": {
    "svelte": "^ 3.32.0"."svelte-routing": "^ 1.5.0." "
  }
Copy the code

Here, the same as the above two frameworks, we use the corresponding official route for routing, and svelte-routing is used for svelte. To get typescript support, install @tsconfig/svelte and svelte-preprocess, and create svelte.config.js in the root directory:

const preprocess = require('svelte-preprocess')

module.exports = { preprocess: preprocess() }

Copy the code

In addition, if we need HMR functionality, we also need to install a plugin,vite-plugin-svelte:

// vite.config.ts
import { defineConfig } from 'vite'
import svelte from 'vite-plugin-svelte'
import sveltePreprocess from 'svelte-preprocess'

// https://vitejs.dev/config/
export default defineConfig({
	plugins: [
		svelte({
			preprocess: sveltePreprocess(),
			compilerOptions: {
				dev: true
			},
			hot: true.emitCss: false})]})Copy the code

At this point, we have a perfect combination of Svelte and Vite.

Next, let’s take a look at svelte state management. Create a state under the store directory:

// store/state.ts
import { writable } from 'svelte/store'
export interface TodoItemType {
	id: number
	done: boolean
	content: string
}

export type StateType = {
	todoList: Array<TodoItemType>
}

const state: StateType = {
	todoList: [{id: 0.done: false.content: 'your first todo'}}]export const createStore = () = > {
	return writable(state)
}
Copy the code

Svelte provides us with writable, which wraps our state around it so that it is responsive.

Let’s create some actions that change state:

// store/action.ts
import type { Writable } from 'svelte/store'
import type { StateType, TodoItemType } from './state'

function addNewTodoItem(state: Writable<StateType>) {
	return (newItem: TodoItemType) = > {
		state.update((state) = > {
			return {
				...state,
				todoList: [...state.todoList, newItem]
			}
		})
	}
}

function delteTodoItem(state: Writable<StateType>) {
	return (item: TodoItemType) = > {
		state.update((state) = > {
			return {
				...state,
				todoList: state.todoList.filter((e) = >e.id ! == item.id) } }) } }// svelte do not change state by action ,beacase all of them is reactivity,it's amazing!

// function changeTodoItemStatus(state: Writable<StateType>) {
// return (todoItem: TodoItemType) => {
// state.update((state) => {
// let list = [...state.todoList]
// // list.map((item) => {
// // if (item.id === todoItem.id) item.done = ! item.done
// // return item
/ / / /})
// return {
/ /... state,
// todoList: [...list]
/ /}
/ /})
/ /}
// }

export function createAction(state: Writable<StateType>) {
	return {
		addNewTodoItem: addNewTodoItem(state),
		delteTodoItem: delteTodoItem(state)
		// changeTodoItemStatus: changeTodoItemStatus(state)}}Copy the code

When you’re todo item, you don’t have to go through an action because the value wrapped in writable is responsive, which is great!

Then we combine state with action:

// store/index.ts
import { createAction } from './action'
import { createStore } from './state'

const state = createStore()
const action = createAction(state)

export const useStore = () = > {
	return {
		state,
		action
	}
}
Copy the code

Now let’s look at how to get state and action in the svelte component:

// pages/Todo.svelte.const store = useStore()
	const { state, action } = store
	let newItemContent = ' '
	let todoList: Array<TodoItemType> = []

	state.subscribe((state) = > {
		todoList = state.todoList
	})
...
Copy the code

So, we get state and action perfectly. There is another point worth mentioning. How do we notify the parent todo.svelte from todoitem.svelte when we change todo Item?

Svelte provides us with createEventDispatcher:

// components/TodoItem.svelte
import { createEventDispatcher } from 'svelte'

const dispatch = createEventDispatcher()

Copy the code

With this dispatch, we can dispatch an action to the parent component:

function deleteTodoItem() {
	dispatch('delteTodoItem', todoItem)
}
Copy the code

In the parent component, with the action of the same name, we can get arguments carried from the child component:

// pages/Todo.svelte.function delteTodoItem(e: CustomEvent) {
	action.delteTodoItem(e.detail)
}
...
<div class="card-content">
	{#each todoList as item}
		<TodoItem todoItem={item} on:delteTodoItem={delteTodoItem} />
	{/each}
</div>
...
Copy the code

This is the same as vue emit.

Now let’s build svelte’s Todo App by running the vite build command:

Well, 262K is the smallest of the three.

conclusion

Let’s start by reviewing the requirements we just made:

  • It must be fast (not slow down as the project grows)
  • It must supportTypescript
  • It must support current mainstream front-end frameworks (includingvue,reactEtc.)
  • It must supportHMR(Hot module replacement)
  • It must supporttree shaking
  • It must support a variety ofCSStool
  • It supports importingSVG.PNG.JSONAnd whatever else we want to import

We now know that Vite meets all of these requirements. In fact, Vite brings us more, it also supports SSR and other features.

At this moment, is the last moment of the New Year, I hope friends happy New Year!

Let’s compare vue3, React, and Svelte.

In terms of building volume, Svelete is superior to VUe3 and react. In terms of state management, Svelte is superior to VUe3 and react. In terms of route management, svelte equals vue3 equals React. React is better than Vue3 than Svelete in terms of typescript support.

So back to the headline question, “Will Vite be the most popular front-end tool in 2021?” I believe you have the answer in mind.