I feel like everywhere I look I’m seeing the super fancy Vite logo. I thought it would be fun to take a look at Vite for my first time today and share my experience with it. Vite is a front-end build tool that comes out-of-the-box with a dev server and hot module reloading. I don’t know a lot beyond it yet, other than the claims of an “optimized build” and “rich features” from their GitHub.
Honestly, my hope going into this is that it can be like Webpack, but with a lot less headache. I primarily work with React and Elm for the front-end, so I would like to get both of those working with hot module replacement. With that in mind, let’s take a look at that gorgeous logo and get started!
๐ฉโ๐ฌ Development
The Getting Started page on their website tells us to run the following command:
npm create vite@latest
Upon running this command, we are prompted with a choice to pick our framework. I’m going to pick React to get things going quickly. After this I choose to use React without TypeScript, because I’m feeling a little wild today ๐ .
And… done? I’ll run a cd first-class-vite
and then run npm i
. After packages are installed, we see that Vite came with three scripts in package.json
: dev, build, preview. Assuming dev
is the one we’ll use to dev, let’s run that. At this point we’re shown a message saying the Vite app is running at localhost:3000
. Go there and take a look!
With a few less steps than create-react-app
, we have a React project up and running. This one even comes with Hot-Module Replacement. Sweet! At this point, I’m going to change out the default app for my own. Wordle is super popular right now, which reminds me of the game Mastermind that I played as a kid. I wrote up a similar game I’ll be calling Mistermind. I did everything using local react state with useState
hooks. I’ll push this up to this GitHub repo so you can check it out!
Elm
I adore Elm. I needed to get that off of my chest. It’s great and I need to be able to use it with Vite, if I’m going to use it! Luckily, this repo, hmsk/vite-plugin-elm
promises to provide Elm support. I’ll start by running the install command.
npm i -D vite-plugin-elm
After running this, I’ll update vite.config.js
to include the new elm plugin. This leaves the vite config looking as follows:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import elm from 'vite-plugin-elm';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), elm()],
});
At this point, I would like to use an Elm to hold some of the app state for the app. This is a bit contrived, since I’m only going to be moving past guesses/turns in there. If you want to learn more about using React & Elm in tandem, I just wrote a post about it! In our main.jsx
, I’ll import the Elm component I created and pass the ports to the React app below.
const appState = Elm.AppState.init({
node: document.getElementById('elm-root'),
});
ReactDOM.render(
<App ports={appState.ports} />,
document.getElementById('root')
);
Then back in the React component, we’ll hook it up and save. Now for the moment of truth. We have to stop and run npm run dev
again to pick up the plugin changes. After that we have a clean console and can go back to localhost:3000
.
Sweet! Without any more work than installing and turning on the plugins, we have React and Elm working in tandem. And with that, let’s take a turn towards deployment.
๐ Build & Deployment
First, let’s just build the static site using the npm run build
command included with Vite. After running this, a dist
folder is created containing the static assets for our Vite app. Nice. ๐
npm run build
The Vite website has a guide on deploying to Github pages, which is what I’m shooting to do. However, rather than run a deploy script, I’m just going to run the build and set GitHub pages to point to the /docs
folder in settings. You can see this up and running here! Although this worked pretty much right out-of-the-box, I did hit one snag with the base URL. Even though they mention it, I forgot to set the base
option in my Vite config.
I’m hosting the app at abshierjoel.github.io/mistermind
. Knowing that, I only need to set the base
option to that directory to get that to work. To do that, open up vite.config.js
one more time and update the defineConfig
call to look like this:
export default defineConfig({
base: '/mistermind/',
plugins: [react(), elm()],
});
๐ Conclusion
In conclusion, Vite is sick. Obviously this has been a fairly simple example of a static site, but I find myself creating a lot of those. But after this, I’ll definitely be spending more time with Vite to see if it can fully replace Webpack for me and save time configuring. I do hope my first swing at this has been helpful to anyone else wanting to take a look at what Vite has to offer. I’m excited to learn more, so if you’re a little further along on your Vite journey than I am, I’d love to hear from you down below.
Thanks for reading,
– Joel Abshier
This week I had received a code review from a teammate to add a @spec
to a start_link
function I wrote for a GenServer
. My start_link
function will take a keyword list, try to get the values :first_name
and :last_name
with defaults, and then call GenServer.start_link/2
with __MODULE__
as the module and those values as the initial state. This GenServer looks like this:
defmodule JoelServer do
use GenServer
def start_link(opts \\ []) do
first_name = Keyword.get(opts, :first_name, "Joel")
last_name = Keyword.get(opts, :last_name, "Abshier")
initial_state = %{
first_name: first_name,
last_name: last_name
}
GenServer.start_link(__MODULE__, initial_state, opts)
end
def init(state) do
{:ok, state}
end
end
What I would like to do is add a @spec
to describe how to use this function. Luckily, in Elixir we have Dialyzer to make this easy. Dialyzer is a static analysis tool within Erlang/Elixir. One feature of Dialyzer is to suggest @spec
annotations for a function. Let’s see what it thinks for this:
@spec start_link(keyword) :: :ignore | {:error, any} | {:ok, pid}
def start_link(opts \\ []) do
...
end
While that’s correct, a more specific @spec
is valuable to us. Let’s look at how we can do that.
๐จ Arguments / Input Type
Let’s take a look at the keyword list we’re passing in. It technically doesn’t matter to us which options are passed in, in addition to :first_name
and :last_name
, however if the spec were to define those specific keywords, we could catch any issues and/or typos while writing the code. Let’s define a private type that lists our keywords. First, add the typep
definition and then update the @spec
accordingly.
@typep start_opt :: {:first_name, String.t()} | {:last_name, String.t()}
@spec start_link(opts :: [start_opt]) :: :ignore | {:error, any} | {:ok, pid}
def start_link(opts \\ []) do
...
end
Now if you try to call JoelServer.start_link(title: "Mr.")
you’ll get a Dialyxir warning saying something like this:
([{title, <<77,114,46,32>>}]) breaks the contract (options::[start_opt()])
That’s pretty cool! Now the next time someone on my team has to use this GenServer, they’ll be told exactly how to use it while calling it. But what about the return type?
๐ฌ Output / Return Type
Now for the reason you’re here. A co-worker pointed me toward a type in the GenServer
docs on_start/0
. We can use this to replace the list of return types at :ignore | {:error, any} | {:ok, pid}
. This lets us define our start_link
with the return type as Elixir’s GenServer.start_link.
@spec start_link(opts :: [start_opt]) :: GenServer.on_start()
I love this, rather than managing our own list of types for each GenServer I define. If you’re working with a Supervisor, we are also given Supervisor
.on_start/0. If you want to allow any options allowed by a GenServer or Supervisor, you’ll also find options/0
, option/0
, init_option/0
, and so on.
๐ฐ That’s all the GenServer we have today, folks
Anyhow, this might be pretty basic, if you’re familiar with Elixir. But I thought it was exciting to be told about these and wanted to share it. A lot of Elixir packages, especially the core library, have excellent hexdocs, so I’m going to make a habit of paying closer attention to the @spec
s on them for when I can take advantage of types like this.
I’ll probably be writing some more shorter posts like this. It’s a bit basic, but I’ll just make a Today I Learned category here on the blog, so you can check those out.
If you have any short tips like this I’d love to hear about them. I’m new to the language, so I’d love to learn! Thanks for reading! ๐งช
– Joel Abshier