前言
node的版本如下:
- node:
v22.13.0- npm:
v10.9.2- pnpm:
10.5.0
最近在鼓捣一些新的项目,需要一个适合的项目模板,但是目前找到的模板功能都太全了反而不适合,于是打算自己从零搭建一个vue3项目,也刚好巩固一下知识。
创建项目
这边用vite创建vue3项目
执行下面命令,按照提示选择就会创建一个最基本的vue3项目
npm create vite
刚开始的项目结构如下
运行项目
因为是高版本的
node推荐用pnpm,后面的操作都用pnpm
安装相关的node包
pnpm install
下面这样依赖就安装成功了,warning里面的不用管
运行下面的命令
pnpm run dev
这样项目就成功运行起来了,第一步就完成了接下来安装常用的第三方库。
路由配置
安装依赖
pnpm add vue-router@4
在
src下面创建一个router文件夹专门用来放路由文件,并且新建index.ts文件
我个人比较习惯按照业务模块来拆分我的路由文件,所以我在router下面创建了一个modules文件夹用来管理不同的子模块路由
最终结构如下
index.ts的内容
import { createMemoryHistory, createRouter, type RouteRecordRaw } from 'vue-router'
import arcgisRoute from './modules/arcgis'
import cesiumRouter from './modules/cesium'
import openlayerRoute from './modules/openlayer'
const routes:RouteRecordRaw[] = [
{ path: '/', name:"home",redirect:'/arcgis',
children:[
arcgisRoute,
cesiumRouter,
openlayerRoute
]
},
]
const router = createRouter({
history: createMemoryHistory(),
routes,
})
export default router
modules模块里面的内容
// cesium.ts文件
import type { RouteRecordRaw } from "vue-router"
const arcgisRouter:RouteRecordRaw = { path: '/arcgis', component: ()=> import('@/pages/arcgis/index.vue') }
export default arcgisRouter
// openlayer.ts文件
import type { RouteRecordRaw } from "vue-router"
const openlayerRouter:RouteRecordRaw = { path: '/openlayer', component: ()=> import('@/pages/openlayer/index.vue') }
export default openlayerRouter
// arcgis.ts文件
import type { RouteRecordRaw } from "vue-router"
const arcgisRouter:RouteRecordRaw = { path: '/arcgis', component: ()=> import('@/pages/arcgis/index.vue') }
export default arcgisRouter
最后在main.ts文件里面注册我们的路由
import { createApp } from 'vue'
import './styles/style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
App.vue代码如下
<template>
<div class="container">
<router-view></router-view>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.container{
position: relative;
height: 100%;
background: #fff;
color: #000;
padding: 24px;
box-sizing: border-box;
}
</style>
arcgis.vue代码如下
<template>
<div>
arcgis
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
操作完成之后,最终你的页面上应该就可以正常显示对应路由的内容了
这里有点要注意,我们的路由用的是默认的createMemoryHistory模式,直接改url是不会触发页面变化的,如果你要触发页面变化,就用vue-router提供的api
如果想要修改url就改变页面,就把路由状态修改为别的模式
import { createWebHistory , createRouter, type RouteRecordRaw } from 'vue-router'
import arcgisRoute from './modules/arcgis'
import cesiumRouter from './modules/cesium'
import openlayerRoute from './modules/openlayer'
const routes:RouteRecordRaw[] = [
{ path: '/', name:"home",redirect:'/arcgis',
children:[
arcgisRoute,
cesiumRouter,
openlayerRoute
]
},
]
const router = createRouter({
// history: createMemoryHistory(),
history:createWebHistory(),
routes,
})
export default router
修改完成之后,路由就可以通过修改url进行修改
状态管理
安装pinia
pnpm add pinia
在main.ts文件中注册pinia
import { createApp } from 'vue'
import './styles/style.css'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).use(router).mount('#app')
在src下面创建store文件夹,这个文件夹以后用来管理各模块的状态
user.ts内容如下
import { defineStore } from 'pinia'
export const useUserStore = defineStore('userStore', {
state: () => {
return {
name: ''
}
},
getters: {
getStatus: (state) => state.name?'登录':'未登录'
},
actions: {
updatUserName(name:string = '') {
this.name = name
},
}
})
使用例子
<template>
<div class="demo">
<span> 用户名:{{ userStroe.name }}</span>
<span>状态:{{ userStroe.getStatus }}</span>
<button @click="setName">设置名字</button>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '../store/user'
const userStroe = useUserStore()
const setName = ()=>{
userStroe.updatUserName('测试名字')
}
</script>
<style scoped>
.demo{
display: flex;
gap: 24px;
flex-direction: column;
button{
width: 160px;
height: 38px;
line-height: 38px;
}
}
</style>
效果如下
路径别名
安装依赖
pnpm add install @types/node
因为需要用到node的一些模块所以需要安装对应的类型依赖
vite.config.ts配置如下
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
})
最后在tsconfig.app.json和tsconfig.node.json配置里面添加如下内容
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
}
这样配置完成之后就可以通过@/xxx的方式进行访问了
代码规范
安装依赖
pnpm add eslint prettier eslint-plugin-prettier globals @eslint/js eslint-plugin-vue typescript-eslint vue-eslint-parser
eslint
创建eslint.config.ts文件,直接复制下面的配置填入
import globals from 'globals'
import eslint from '@eslint/js'
import tseslint from 'typescript-eslint'
import eslintPluginVue from 'eslint-plugin-vue'
import vueParser from 'vue-eslint-parser'
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
export default [
{
ignores: [
'node_modules',
'dist',
'.gitignore',
'package.json',
'package-lock.json',
'dist-ssr',
'*.local',
'.npmrc',
'.DS_Store',
'dev-dist',
'dist_electron',
'*.d.ts',
'src/assets/**'
]
},
/** js推荐配置 */
eslint.configs.recommended,
/** vue推荐配置 */
...eslintPluginVue.configs['flat/recommended'],
/** prettier 配置 */
eslintPluginPrettierRecommended,
//javascript 规则
{
files: ['**/*.{js,mjs,cjs,vue,ts}'],
rules: {
// 对象结尾逗号
'comma-dangle': 'off',
// 关闭未定义变量
'no-undef': 'off',
// 确保 Prettier 的行为不会被 ESLint 覆盖
quotes: ['error', 'single', { allowTemplateLiterals: true }],
// 关闭对未定义变量的警告
'no-undefined': 'off',
//不使用的变量不报错
'no-unused-vars': 'off',
// 禁止使用不规范的空格
'no-irregular-whitespace': 'off',
// 函数括号前的空格
'space-before-function-paren': 0,
// 箭头函数的空格
'arrow-spacing': [
2,
{
before: true,
after: true
}
],
// 代码块的空格
'block-spacing': [2, 'always'],
// 大括号风格
'brace-style': [
2,
'1tbs',
{
allowSingleLine: true
}
],
// 对象属性换行
'object-property-newline': 'off',
// JSX 引号风格
'jsx-quotes': [2, 'prefer-single'],
// 对象键值对之间的空格
'key-spacing': [
2,
{
beforeColon: false,
afterColon: true
}
],
// 关键字之间的空格
'keyword-spacing': [
2,
{
before: true,
after: true
}
],
// 构造函数首字母大写
'new-cap': [
2,
{
newIsCap: true,
capIsNew: false
}
],
// new 操作符使用时需要括号
'new-parens': 2,
// 禁止使用 Array 构造函数
'no-array-constructor': 2,
// 禁止调用 caller 和 callee
'no-caller': 2,
// 禁止重新分配类名
'no-class-assign': 2,
// 禁止条件中的赋值操作
'no-cond-assign': 2,
// 禁止 const 重新分配
'no-const-assign': 2,
// 正则表达式中的控制字符
'no-control-regex': 0,
// 禁止删除变量
'no-delete-var': 2,
// 禁止在函数参数中使用重复名称
'no-dupe-args': 2,
// 禁止在类中使用重复名称的成员
'no-dupe-class-members': 2,
// 禁止在对象字面量中使用重复的键
'no-dupe-keys': 2,
// 禁止重复的 case 标签
'no-duplicate-case': 2,
// 禁止空的字符类
'no-empty-character-class': 2,
// 禁止空的解构模式
'no-empty-pattern': 2,
// 禁止使用 eval
'no-eval': 2,
// 不允许出现空的代码块
'no-empty': 2,
// 禁止不必要的布尔转换
'no-extra-boolean-cast': 2,
// 禁止不必要的括号
'no-extra-parens': [2, 'functions'],
// 禁止 case 语句落空
'no-fallthrough': 2,
// 禁止在数字后面添加小数点
'no-floating-decimal': 2,
// 禁止对函数声明重新赋值
'no-func-assign': 2,
// 禁止出现歧义多行表达式
'no-unexpected-multiline': 2,
// 禁止不需要的转义
'no-useless-escape': 0,
// 数组的括号前后的间距
'array-bracket-spacing': [2, 'never']
}
},
// vue 规则
{
files: ['**/*.vue'],
languageOptions: {
parser: vueParser,
globals: { ...globals.browser, ...globals.node },
parserOptions: {
/** typescript项目需要用到这个 */
parser: tseslint.parser,
ecmaVersion: 'latest',
/** 允许在.vue 文件中使用 JSX */
ecmaFeatures: {
jsx: true
}
}
},
rules: {
'vue/component-definition-name-casing': 'off',
'vue/singleline-html-element-content-newline': ['off'],
'vue/no-mutating-props': [
'error',
{
shallowOnly: true
}
],
// 要求组件名称始终为 “-” 链接的单词
'vue/multi-word-component-names': 'off',
// 关闭 index.html 文件报 clear 错误
'vue/comment-directive': 'off',
// 关闭对 defineProps 的有效性检查
'vue/valid-define-props': 'off',
// 允许在一个文件中定义多个组件
'vue/one-component-per-file': 'off',
// 关闭 Prop 类型要求的警告
'vue/require-prop-types': 'off',
// 关闭属性顺序要求
'vue/attributes-order': 'off',
// 关闭对默认 Prop 的要求
'vue/require-default-prop': 'off',
// 关闭连字符命名检验
'vue/attribute-hyphenation': 'off',
// 关闭自闭合标签的要求
'vue/html-self-closing': 'off',
// 禁止在关闭的括号前有换行
'vue/html-closing-bracket-newline': 'off',
// 允许使用 v-html 指令
'vue/no-v-html': 'off'
}
}
]
prettier
创建.prettierrc.cjs文件并填入下面内容
module.exports = {
printWidth: 120, // 一行的字符数换行
tabWidth: 2, // 一个tab代表几个空格数
useTabs: false, // 是否使用tab进行缩进
singleQuote: true, // 字符串是否使用单引号
semi: false, // 行尾是否使用分号,默认为true
trailingComma: 'none', // 是否使用尾逗号
arrowParens: 'avoid', // 箭头函数单变量省略括号
bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
endOfLine: 'auto', // 保留在 Windows 和 Unix 下的换行符
quoteProps: 'preserve' // 对象属性的引号使用
}
vscode这边记得设置一下配置文件路径,不然格式化的时候找不到配置文件
打开vscode的settting.json文件
写入配置
"prettier.configPath": ".prettierrc.cjs"
创建.prettierignore文件,用来忽略不想被格式化的文件
node_modules
dist
*.local
.npmrc
dist_electron
auto-imports.d.ts
最后package.json里面配置相关命令
{
"name": "vue-gis",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview",
"lint:fix": "eslint . --fix",
"lint": "eslint ."
},
"dependencies": {
"@eslint/js": "^9.21.0",
"@types/node": "^22.13.5",
"element-plus": "^2.9.5",
"eslint-plugin-prettier": "^5.2.3",
"globals": "^16.0.0",
"pinia": "^3.0.1",
"prettier": "^3.5.2",
"sass": "^1.85.1",
"vue": "^3.5.13",
"vue-eslint-parser": "^9.4.3",
"vue-router": "4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.21.0",
"eslint-plugin-vue": "^9.32.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.25.0",
"vite": "^6.1.0",
"vue-tsc": "^2.2.0"
},
"pnpm": {
"ignoredBuiltDependencies": [
"esbuild"
]
}
}
这样就可以通过命令修复全局文件了
pnpm run lint:fix
这样最基本的代码格式化就配置完成,如果需要stylelint和git hook就需要额外配置这边略过
UI库
vue比较常用的还是element系列,这边以element-plus为例子
安装依赖
pnpm add element-plus
main.ts中引入element-plus
import { createApp } from 'vue'
import './styles/style.css'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(ElementPlus).use(pinia).use(router).mount('#app')
使用
<template>
<div class="demo">
<span> 用户名:{{ userStroe.name }}</span>
<span>状态:{{ userStroe.getStatus }}</span>
<button @click="setName">设置名字</button>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '../store/user'
const userStroe = useUserStore()
const setName = ()=>{
userStroe.updatUserName('测试名字')
}
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
</script>
<style lang="scss" scoped>
$primary-color: #cb3837;
.demo{
display: flex;
gap: 24px;
flex-direction: column;
button{
width: 160px;
height: 38px;
line-height: 38px;
background:$primary-color;
}
}
</style>
现在就已经可以使用element-plus组件了
样式处理
日常开发用的比较多的就就是
sass,vue3的sass安装非常方便
安装依赖
pnpm add sass
安装完成之后就可以了,vue组件中就可以使用sass了
<template>
<div class="demo">
<span> 用户名:{{ userStroe.name }}</span>
<span>状态:{{ userStroe.getStatus }}</span>
<button @click="setName">设置名字</button>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '../store/user'
const userStroe = useUserStore()
const setName = ()=>{
userStroe.updatUserName('测试名字')
}
</script>
<style lang="scss" scoped>
$primary-color: #cb3837;
.demo{
display: flex;
gap: 24px;
flex-direction: column;
button{
width: 160px;
height: 38px;
line-height: 38px;
background:$primary-color;
}
}
</style>
最后
经过上面的各种操作,一个vue3项目就搭建起来了,如果没有从0开始搭建过项目,强烈建议动手一次,说不定会有额外的收获.
当然如果是正常开发,肯定是直接使用别人搭建好的,功能全的模板来进行开发.