- Language:
typescript
- Vue:
vue3
,vite
,vuex@next
,vue-router@next
- CSS Preprocessor:
sass
,scss
,less
- UI Library:
ant-design-vue@next
- Testing Library:
jest
,cypress
- WebSocket Client:
socket.io-client
- Vuex type intellisense
- .vue: π₯ Type intellisense also support on
.vue
. That's right! Try yourself to feel the difference. - Actions: The
action context
properties are all supporting type intellisense- including
commit
,dispatch
,state
,rootState
,getters
,rootGetters
- including
- Getters: The
getter context
parameters are all supporting type intellisense- including
state
,rootState
,getters
,rootGetters
- including
- .vue: π₯ Type intellisense also support on
- Antd
- Styles import on demand
- Theme customization
- WS x Vuex Plugin
- Receiving WS events will be triggered to dispatch the same name actions of a specified namespaced store.
- Code-split & Gzip
git clone https://github.com/ChaoLiou/vite-starter.git your-project-name
cd your-project-name
rmdir /s /q .git # Windows
rm -rf .git # MacOS or Linux
git init
git add .
git commit -m "project init"
code .
yarn install
yarn dev # wait on localhost:3000
git clone https://github.com/ChaoLiou/vite-starter.git your-project-name & cd your-project-name & rmdir /s /q .git & git init & git add . & git commit -m "project init" & code .
git clone https://github.com/ChaoLiou/vite-starter.git your-project-name & cd your-project-name & rm -rf .git & git init & git add . & git commit -m "project init" & code .
βββ build # all about vite/rollup build
| βββ generateModifyVars.ts # customize theme(antd)
β βββ styleImport.ts # [DNC] introduces component library(antd) styles on demand
βββ public
β βββ configs
| | βββ index.js # global variables: 'processEnv' for environment variables
β βββ fonts # Roboto fonts
β βββ *.svg # svg files
β βββ favicon.ico
βββ src
β βββ asseets
β βββ components
β β βββ dashboard # related to dashboard page
β β βββ debug # born for debugging
β β βββ icons # component wrapping with svg
β β βββ statusLabel # badge for vm/ltm status
| | βββ *.vue
β βββ composables # divide your component's logical concerns
| | βββ global # composables for global use
| | βββ tableControl # related to TableControl
| | βββ use*.ts
β βββ interfaces
| | βββ *Interface.ts # view model extended with data model
| | βββ UtilityInterface.ts # utility usage
β βββ miragejs
| | βββ generators # fake data generator function or pure json
| | βββ routes # miragejs server route configs
| | βββ directus.ts # directus cms api
| | βββ server.ts # miragejs server
| | βββ utils.ts # utils/helpers, e.g. randomPick, randomAmountOfResult
β βββ models
| | βββ *Model.ts # data model for api response
| | βββ UtilityModel.ts # utility usage
β βββ pageConfigs
| | βββ declarations.ts # interface extended with npm package's declarations
| | βββ *PageConfig.ts # specified page configs
| | βββ utilityPageConfig.ts # utility usage
β βββ plugins
| | βββ localStorage
| | | βββ index.ts # local storage utils/helpers
| | | βββ mock.ts # local storage mock
| | βββ ws
| | | βββ declarations.ts # [DNC] type/interface plugin options
| | | βββ socket-io.ts # socket-io client plugin
| | βββ formatter.ts # data format functions
β βββ router
| | βββ menu # menu items
| | βββ routes # route items
| | βββ index.ts
β βββ services
| | βββ *Service.ts # requests to api
β βββ store
| | βββ dashboard
| | βββ ltm
| | βββ profile
| | βββ server
| | βββ serverEnv
| | βββ wsqueue
| | βββ declaration.ts
| | βββ index.ts
β βββ styles
| | βββ modules # scss modules
| | | βββ _color.scss
| | | βββ _font.scss
| | βββ index.scss # global scss
β βββ utils
| | βββ http
| | βββ axios.ts # axios client
β βββ views
β β βββ Server
β | | βββ Detail.vue
β | | βββ List.vue
β | βββ Dashboard.vue
β | βββ LoadBalancing.vue
β βββ App.vue
β βββ env.d.ts # [DNC] provided from vite
β βββ main.ts
β βββ router.ts # declaractions for Vue Route Meta
βββ tests
βββ e2e
| βββ fixtures # [DNC]
| βββ integration # e2e tests
| | βββ basic.spec.ts
| βββ plugins # [DNC]
| βββ support # init cypress
| | βββ commands.ts # for extending {cy} methods
| | βββ index.ts # [DNC]
| βββ tsconfig.json # [DNC]
βββ unit
βββ components
| βββ *.spec.ts # unit tests for components
| βββ setupTests.ts # [DNC]
βββ composables
| βββ *.spec.ts # unit tests for composables
βββ *.spec.ts # unit tests
[DNC]: means 'DO NOT CHANGE'
* declarations.ts: contains related types and interfaces
// build/generateModifyVars.ts
...
export function generateModifyVars(dark = false) {
const modifyVars = getThemeVariables({ dark });
return {
...modifyVars,
// custom themes
};
}
...
You could go to default theme for available variables.
// src/main.ts
if (import.meta.env.DEV) {
import('ant-design-vue/dist/antd.less');
}
First of all, vite-starter
is using import styles on demand
feature for ant-design-vue
component styles.
// vite.config.ts
...
import { configStyleImportPlugin } from './build/styleImport';
...
export default defineConfig(({ command }: ConfigEnv): UserConfig => {
const isBuild = command === 'build';
return {
...
plugins: [..., configStyleImportPlugin(isBuild)],
...
};
});
But if we use import styles on demand
feature on development mode
, it might slow down the browser refresh speed. Therefore, we only enable in production mode
, and import whole theme on development mode
.
A model
is the definition of data from api service, we only define the fields we needs, and a interface
is rewrapping from a model, like a view model for component data scheme.
Example in vite-starter
A fake feature api response:
// public/data/features.json
[
{
"id": 1,
"title": "Language",
"tags": ["typescript"]
},
...
]
A model for feature api response:
// models/FeatureModel.ts
export interface FeatureModel {
title: string;
tags: string[];
}
A interface for FeatureList.vue:
// interfaces/FeatureInterface.ts
import { FeatureModel } from '@/models/FeatureModel';
export interface FeatureInterface extends FeatureModel {
highlightedTags: string[];
}
You can see that the highlightedTags
field in FeatureInterface
is just for component logic concerns, not exactly a data field, so we need a view model for division from 2 models.
// store/*/state.ts
...
export const state = {
...
newState: {} as NewStateType,
};
...
Add a new state and define a type for it.
// store/*/declarations.ts
...
export type DeclareGetters = {
...
newGetter(...context: GetterContext<State, Getters>): NewGetterType;
};
// store/*/getters.ts
...
import { DeclareGetters as Getters } from './declarations';
export const getters: GetterTree<State, RootState> & Getters = {
...
newGetter: (state, getters, rootState, rootGetters) => {
// return your `NewGetterType` value
},
};
Add a definition of new getter into Getters
type, including name(newGetter
) and return type(NewGetterType
), then add new getter into getters
. According to vuex
API documentation, each getter callback should at most have 4 parameters.
* GetterContext
is wrapping through our own State
, Getters
, RootState
and RootGetters
that let you use intellisense
feature.
// store/*/types.ts
export enum MutationTypes {
NEW_MUTATION_TYPE = 'NEW_MUTATION_TYPE',
}
First, add new type name of your new mutation.
// store/*/declarations.ts
export type Mutations<S = State> = {
...
[MutationTypes.NEW_MUTATION_TYPE](state: S, payload: NewMutationPayloadType): void;
};
// store/*/mutations.ts
export const mutations: MutationTree<State> & Mutations = {
...
[MutationTypes.NEW_MUTATION_TYPE](state, payload: NewMutationPayloadType) {
// mutate your state by payload
},
};
Add a definition of new mutation into Mutations
type, including name(import from MutationTypes
) and payload type(NewMutationPayloadType
), then add new mutation into mutations
.
// store/*/types.ts
export enum ActionTypes {
NEW_ACTION_TYPE = 'NEW_ACTION_TYPE',
}
First, add new type name of your new action.
// store/*/declarations.ts
export interface Actions {
...
[ActionTypes.NEW_ACTION_TYPE]({ commit, dispatch, state, rootState, getters, rootGetters }: ActionContext<State, Getters>, payload: NewActionPayloadType): void;
}
// store/*/actions.ts
export const actions: ActionTree<State, RootState> & Actions = {
...
[ActionTypes.NEW_ACTION_TYPE]({ commit, dispatch, state, rootState, getters, rootGetters }) {
// commit mutation, or dispatch actions from current store
},
};
Add a definition of new action into Actions
type, including name(import from ActionTypes
) and payload type(NewActionPayloadType
), then add new action into actions
. According to vuex
API documentation, each action callback should have a context parameter which contains 6 properties.
* ActionContext
is wrapping through our own commit
type, dispatch
type, State
, Getters
, RootState
and RootGetters
that let you use intellisense
feature.
- Clone
store/sample
folder including*.ts
inside, and rename folder with yourmodule name
- Here are what you should change in
*.ts
file and the main store file, such asstore/index.ts
store/declarations.ts
.
...
type ModuleName = 'myModule';
...
Change sample
to your module name.
...
import {
ModuleName as MyModuleModule,
Store as MyModuleStore,
State as MyModuletate,
NamespacedActions as MyModuleActions,
NamespacedMutations as MyModuleMutations,
NamespacedGetters as MyModuleGetters,
} from '@/store/myModule/declarations';
export type RootState = {
...
myModule: MyModuleState;
};
export type RootMutations = ... & MyModuleMutations;
export type RootActions = ... & MyModuleActions;
export type RootGetters = ... & MyModuleGetters;
export type Store = ... &
SampleStore<Pick<RootState, MyModuleModule>>;
...
...
import { store as myModule } from '@/store/myModule';
...
export const store = createStore({
plugins,
modules: {
...
myModule,
},
});
...
According to Vue3 Guide, the purpose of composition api
is to extract logical concerns into standalone composition functions, or you can think as what functionalities should be hooked
on this component? and then pick them under composables
folder.
According to Vue3 Guide, unit tests allow you to test individual units of code in isolation. The purpose of unit testing is to provide developers with confidence in their code. By writing thorough, meaningful tests, you achieve the confidence that as new features are built or your code is refactored your application will remain functional and stable.
Think about these questions:
- What's
unit
in our code? - What kind of code is that you'll
have no confidence
if you change it again? - What kind of test is
meaningful to you
?
According to Vue3 Guide, end-to-end (E2E) tests provide coverage on what is arguably the most important aspect of an application: what happens when users actually use your applications. In other words, E2E tests validate all of the layers in your application. This not only includes your frontend code, but all associated backend services and infrastructure that are more representative of the environment that your users will be in.
First time to use cypress
, you can type yarn cypress
and it'll open the dashboard window, then select base.spec.ts
to run test.