Project Scaffolding and Configuration
Creating the Project
To initiate a new Vue 3 project with TypeScript support, use the Vue CLI:
vue create my-vue-app
During the setup, select the following options:
- Manually select features
- Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter
- Vue.js version: 3.x
- Use class-style component syntax? No
- Use Babel alongside TypeScript? Yes
- Use history mode for router? No
- Pick a CSS pre-processor: Sass/SCSS
- Pick a linter / formatter config: Prettier
- Lint on save
- Where to place config files: In dedicated config files
- Save as preset? No
- Package manager: NPM
Code Standards
.editorconfig
Define consistent coding styles across editors:
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
Prettier
Install Prettier for code formatting:
npm i -D prettier
Create a .prettierrc configuration file:
{
"useTabs": false,
"tabWidth": 2,
"printWidth": 80,
"singleQuote": true,
"trailingComma": "none",
"semi": false
}
Create a .prettierignore file to exclude certain files:
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
Add a script to your package.json:
{
"scripts": {
"prettier": "prettier --write ."
}
}
ESLint
Install ESLint and integrate it with Prettier:
npm i -D eslint-plugin-prettier eslint-config-prettier
Configure ESLint in .eslintrc.js:
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/typescript/recommended',
'@vue/prettier',
'@vue/prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
parserOptions: {
ecmaVersion: 2020
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off'
}
}
Git Hooks with Husky
Set up Husky to run linting before commits:
npx husky-init && npm install
Configure the pre-commit hook in .husky/pre-commit:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint
Commitizen
Standardize commit messages:
npm i -D commitizen
npx commitizen init cz-conventional-changelog --save-dev --save-exact
Add a script to your package.json:
{
"scripts": {
"commit": "cz"
}
}
Commitizen will prompt you for the type, scope, and description of your changes.
Commitlint
Validate commit messages:
npm i -D @commitlint/config-conventional @commitlint/cli
Create a commitlint.config.js file:
module.exports = {
extends: ['@commitlint/config-conventional']
}
Add a commit-msg hook:
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
Project Structure
vue.config.js
Configure Vue CLI options:
const path = require('path')
module.exports = {
outputDir: 'dist',
publicPath: '/',
configureWebpack: {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
components: '@/components'
}
}
}
}
Vue Router
Install Vue Router:
npm i -S vue-router@next
Configure routes in src/router/index.ts:
import { createRouter, createWebHashHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
const routes: Array<routerecordraw> = [
{
path: '/',
redirect: '/dashboard'
},
{
path: '/dashboard',
component: () => import('../views/Dashboard.vue')
},
{
path: '/profile',
component: () => import('../views/Profile.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router</routerecordraw>
Vuex
Install Vuex:
npm i -S vuex@next
Set up the store in src/store/index.ts:
import { createStore, useStore as useVuexStore } from 'vuex'
import user, { IUserState } from './modules/user'
export interface IRootState {
user: IUserState
}
export default createStore<irootstate>({
modules: {
user
}
})
export function useStore() {
return useVuexStore<irootstate>()
}</irootstate></irootstate>
Element Plus
Install Element Plus:
npm i -S element-plus
Import and use it in src/main.ts:
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
Axios
Install Axios:
npm i -S axios
Configure Axios in src/utils/request.ts:
import axios from 'axios'
import type { AxiosRequestConfig } from 'axios'
import Cookies from 'js-cookie'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
service.interceptors.request.use(
(config) => {
const token = Cookies.get('token')
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
service.interceptors.response.use(
(response) => {
switch (response.data.code) {
case 999:
console.log('Error occurred')
break
default:
}
return response
},
(error) => {
switch (error.response.status) {
case 401:
console.log('Unauthorized or token expired')
break
case 403:
console.log('Access denied')
break
default:
}
return Promise.reject(error)
}
)
interface IResponseBody<t any=""> {
code: number
data: T
message: string
}
export default function <t any="">(
config: AxiosRequestConfig
): Promise<iresponsebody>> {
return service(config).then((res) => {
return res.data
})
}</iresponsebody></t></t>
Environment Variables
Vue CLI supports different environment files:
.env.developmentfor dveelopment.env.productionfor production.env.stagingfor staging
Variables must start with VUE_APP_ to be injected into the code.
tsconfig.json
Configure TypeScript options:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
shims-vue.d.ts
Declare Vue component types:
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
Advanced Vue 3 Concepts
Component Instance Types
Access child component instance types:
<template>
<div>
<child-component ref="childRef"></child-component>
<el-button @click="decrementAge">Decrement</el-button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default defineComponent({
components: { ChildComponent },
setup() {
const childRef = ref()
type ChildComponentType = InstanceType<typeof ChildComponent>
const decrementAge = () => {
const childInstance: ChildComponentType = childRef.value
childInstance.person.age--
}
return {
childRef,
decrementAge
}
}
})
</script>
Props Typing
Define props with TypeScript:
<script lang="ts">
import { defineComponent, PropType } from 'vue'
interface IPerson {
username: string
age: number
}
export default defineComponent({
props: {
persons: {
type: Array as PropType<IPerson[]>,
required: true
}
},
setup(props) {
props.persons.forEach((item) => {
console.log(item.username)
})
}
})
</script>
Using Props in <script setup>
Define props with defaults in the setup syntax:
<template>
<div ref="chartRef" :style="{ width: width, height: height }"></div>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts'
import { EChartsOption } from 'echarts'
import { ref, onMounted, withDefaults, defineProps } from 'vue'
const props = withDefaults(
defineProps<{
options: EChartsOption
width?: string
height?: string
}>(),
{
width: '100%',
height: '100%'
}
)
const chartRef = ref<HTMLDivElement>()
onMounted(() => {
const chart = echarts.init(chartRef.value!, 'light', {
renderer: 'svg'
})
chart.setOption(props.options)
})
</script>
Browser Repaint and Reflow
Optimize for performance:
- Use
transforminstead ofposition - Avoid table layouts
- Use
visibility: hiddeninstead ofdisplay: none - Minimize DOM manipulation
ECharts Rendering
Choose between Canvas and SVG:
- Canvas: Suitable for large datasets and visual effects
- SVG (Recommended): Lower memory usage, scales with out blurring
Exposing Properties and Methods
Expose component properties and methods to parent components:
<script lang="ts" setup>
import { defineExpose, ref } from 'vue'
const userData = ref({ name: 'John Doe', age: 30 })
const greet = () => {
console.log('Hello!')
}
defineExpose({
userData,
greet
})
</script>
Project Packaging and Automated Deployment
Setting Up CentOS 7 with DNF
Install DNF package manager:
yum install epel-release -y
yum install dnf -y
Installing Java
Install Java 8:
dnf search java-1.8
dnf install java-1.8.0-openjdk.x86_64 -y
Installing Jenkins
Install Jenkins using RPM:
dnf install daemonize -y
rpm -ivh jenkins-2.332-1.1.noarch.rpm
Start and enible Jenkins:
systemctl start jenkins
systemctl enable jenkins
Installing Nginx
Install and start Nginx:
dnf install nginx -y
systemctl start nginx
systemctl enable nginx
Installing Git
Install Git:
dnf install git -y
Jenkins Configuration
Configure Jenkins for Node.js and deployment:
- Initialize Jenkins and install recommended plugins.
- Install the Node.js plugin and configure Node.js installations.
- Change the Jenkins user to
rootfor file access permissions. - Create a new freestyle project.
- Configure source code management (Git) and build triggers.
- Set up the build environment to provide Node.js and npm.
- Add a build step to execute a shell script.
Example shell script for deployment:
pwd
node -v
npm -v
npm install
npm run build
echo 'Build successful'
ls
rm -rf /usr/share/nginx/html/*
cp -rf ./dist/* /usr/share/nginx/html/
Example cron job for scheduled builds:
# Run every 30 minutes
H/30 * * * *
# Run every 2 hours
H H/2 * * *
# Run daily at 2 AM
H 2 * * *