Vue - 创建 Vue3 项目
Vue - 创建 Vue3 项目
vite 官网:https://micro-zoe.github.io/micro-app/docs.html#/zh-cn/framework/vite
需搭建项目 Vue3 + ts + sass
node.js 版本使用 v21.1.0
Vue2.X和Vue3.X对比
beforeCreate --------> setup(()=>{})
created --------> setup(()=>{})
beforeMount --------> onBeforeMount(()=>{})
mounted --------> onMounted(()=>{})
beforeUpdate --------> onBeforeUpdate(()=>{})
updated --------> onUpdated(()=>{})
beforeDestroy --------> onBeforeUnmount(()=>{})
destroyed --------> onUnmounted(()=>{})
activated --------> onActivated(()=>{})
deactivated --------> onDeactivated(()=>{})
errorCaptured --------> onErrorCaptured(()=>{})
1. 创建项目
npm create vite@latest
安装依赖 并启动项目
tyarn
yarn dev
2. 处理其他配置问题
1) 打开HelloWorld.vue页面,发现一些报红,报错
解决:找到 tsconfig.json 文件, 将 moduleResolution设置成 node,报红就没了
{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, /* Bundler mode */ "moduleResolution": "node", // "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve", /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "references": [{ "path": "./tsconfig.node.json" }] }
2)在main.ts中可能也会报如下错
解决:找到 vite-env.d.ts 文件,在里面追加如下代码,保存即可:
declare module "*.vue" { import type { DefineComponent } from "vue"; const vueComponent: DefineComponent<{}, {}, any>; export default vueComponent; }
3)在写ts语法的时候,一些变量名未使用也会报错
解决:在 tsconfig.json 中将noUnusedLocals改为false,不去校验未使用的变量。
{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, /* Bundler mode */ "moduleResolution": "node", // "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve", /* Linting */ "strict": true, "noUnusedLocals": false, "noUnusedParameters": false, "noFallthroughCasesInSwitch": true }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "references": [{ "path": "./tsconfig.node.json" }] }
安装其他插件
3. 配置 sass
yarn add sass -D
创建一个common.scss文件,然后在main.ts中引入
import { createApp } from "vue"; import "./style.css"; import "./common.scss"; import App from "./App.vue"; import router from "./router"; const app = createApp(App); app.use(router); app.mount("#app");
4. 配置路由 vue-router
yarn add vue-router
1) 创建页面 src / pages / login.vue
<template> <div @click="tologin">登录</div> </template> <script lang="ts" setup> import { useRoute, useRouter } from "vue-router"; const router = useRouter(); function tologin() { router.push({ name: "index", }); } </script>
2)创建路由配置 src / router / index.ts
import { createRouter, createWebHistory, } from 'vue-router' export const routes = [ { path: '/', redirect: '/login', }, { name: 'login', path: '/login', component: () => import("../pages/login.vue") }, { name: 'index', path: '/index', component: () => import("../pages/index.vue") } ] const router = createRouter({ scrollBehavior: () => ({ left: 0, top: 0 }), history: createWebHistory(), routes, }) router.beforeEach((to, from, next) => { next() }) export default router
3) 在main.ts中引入路由
import { createApp } from "vue"; import "./style.css"; import App from "./App.vue"; import router from "./router"; const app = createApp(App); app.use(router); app.mount("#app");
4) 在 app.vue 设置 routerview
<script setup lang="ts"> </script> <template> <router-view></router-view> </template> <style scoped> </style>
5. 安装 pinia
vue3 中抛弃vuex,使用pinia
yarn add pinia
创建配置文件 src / store / index.ts
import { defineStore, acceptHMRUpdate } from "pinia"; export const useStore = defineStore({ id: "index", state: () => ({ name: "old name", }), getters: { myName: (state) => { return `getters ${state.name}`; }, }, actions: { changeName(name: string) { this.name = name; }, }, }); if (import.meta.hot) { import.meta.hot.accept(acceptHMRUpdate(useStore, import.meta.hot)); }
注意:这里有个import.meta.hot
判断作用如下:
Pinia 是 vuex 新替代方案。Pinia 中热更新实现,借助 import.meta
热更新:(Hot Module Replacement,简写 HMR)代码会自动更新页面。当修改代码时,HMR 能够在不刷新页面的情况下,把页面中发生变化的模块,替换成新的模块,同时不影响其他模块的正常运作。
Pinia 支持热更新,所以你可以编辑你的 store,并直接在你的应用中与它们互动,而不需要重新加载页面,允许你保持当前的state
、并添加甚至删除state
、actions
和getters
。
import.meta.hot
规范的构建工具都应该能正常工作。你只需要在任何 store 声明旁边添加这段代码。比方说,你有三个 store:auth.js、cart.js和chat.js,你必须在每个 store 声明后都添加(和调整)这段代码即可。即:if (import.meta.hot) { import.meta.hot.accept(acceptHMRUpdate(useStore, import.meta.hot)) }
在main.ts中引入
import { createApp } from "vue"; import "./style.css"; import "./common.scss"; import App from "./App.vue"; import router from "./router"; import { createPinia } from "pinia"; const app = createApp(App); app.use(router); app.use(createPinia()); app.mount("#app");
在页面中使用
<template> <div @click="tologin">登录</div> <h2>{{ store.name }}</h2> <h1 @click="changeName">修改名称</h1> </template> <script lang="ts" setup> import { useRoute, useRouter } from "vue-router"; import { useStore } from "../store/index"; const router = useRouter(); const store = useStore(); function tologin() { router.push({ name: "index", }); } function changeName() { store.name = "索索"; } </script>
6. 安装 Element-plus
yarn add element-plus
在main.ts中引入
import { createApp } from "vue"; import "./style.css"; import "./common.scss"; import App from "./App.vue"; import router from "./router"; import { createPinia } from "pinia"; import ElementPlus from 'element-plus';
import "element-plus/dist/index.css";
const app = createApp(App); app.use(router); app.use(createPinia()); app.use(ElementPlus) app.mount("#app");
在页面中使用
<template> <div @click="tologin">登录</div> <h2>{{ store.name }}</h2> <h1 @click="changeName">修改名称</h1> <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" /> </template> <script lang="ts" setup> import { useRoute, useRouter } from "vue-router"; import { useStore } from "../store/index"; const router = useRouter(); const store = useStore(); function tologin() { router.push({ name: "index", }); } function changeName(): void { store.name = "索索"; } interface Tree { label: string; children?: Tree[]; } const handleNodeClick = (data: Tree) => { console.log(data); }; const data: Tree[] = [ { label: "Level one 1", children: [ { label: "Level two 1-1", children: [ { label: "Level three 1-1-1", }, ], }, ], }, { label: "Level one 2", children: [ { label: "Level two 2-1", children: [ { label: "Level three 2-1-1", }, ], }, { label: "Level two 2-2", children: [ { label: "Level three 2-2-1", }, ], }, ], }, ]; const defaultProps = { children: "children", label: "label", }; </script>
7. 安装 axios
yarn add axios --save
封装 axios 请求文件:src / utils / request.ts
/**axios封装 * 请求拦截、相应拦截、错误统一处理 */ import axios from 'axios'; import router from '../router/index' // let protocol = window.location.protocol; //协议 // let host = window.location.host; //主机 // axios.defaults.baseURL = protocol + "//" + host; axios.defaults.baseURL = '/api' axios.interceptors.request.use( //响应拦截 async config => { // 每次发送请求之前判断vuex中是否存在token // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况 // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 config.headers.token = sessionStorage.getItem('token') return config; }, error => { return Promise.reject(error); }) // 响应拦截器 axios.interceptors.response.use( response => { if (response.status === 200) { return Promise.resolve(response); //进行中 } else { return Promise.reject(response); //失败 } }, // 服务器状态码不是200的情况 error => { if (error.response.status) { switch (error.response.status) { // 401: 未登录 // 未登录则跳转登录页面,并携带当前页面的路径 // 在登录成功后返回当前页面,这一步需要在登录页操作。 case 401: // 自定义过期之后的操作 break // 403 token过期 // 登录过期对用户进行提示 // 清除本地token和清空vuex中token对象 // 跳转登录页面 case 403: sessionStorage.clear() break // 404请求不存在 case 404: break; // 其他错误,直接抛出错误提示 default: } return Promise.reject(error.response); } } ); /** * get方法,对应get请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ const $get = (url: string, params: object) => { return new Promise((resolve, reject) => { axios.get(url, { params: params, }) .then(res => { resolve(res.data); }) .catch(err => { reject(err.data) }) }); } /** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ const $post = (url: string, params: object) => { return new Promise((resolve, reject) => { axios.post(url, params) //是将对象 序列化成URL的形式,以&进行拼接 .then(res => { resolve(res.data); }) .catch(err => { reject(err.data) }) }); } // 下面是将get和post方法挂载到vue原型上供全局使用、 // vue2.x中是通 Vue.prototype 来绑定的,像这样Vue.prototype.$toast = Toast。在vue3中取消了Vue.prototype,推荐使用globalProperties来绑定, export default { install: (app: any) => { app.config.globalProperties['$get'] = $get; app.config.globalProperties['$post'] = $post; app.config.globalProperties['$axios'] = axios; } }
在main.ts中引入
import http from './utils/request.js'; app.use(http)
在页面中使用请求,调登录接口
import { getCurrentInstance } from 'vue' const { proxy } = getCurrentInstance() as any; function login(): void { let data = { roleId: "A", username: "dpc", password: "dpc12345", sysType: "zhfw", } proxy.$post("/index/login", data).then((response: any) => { console.log(response) router.push({ name: 'index' }) }) } function tologin() { login() }
8. 设置路径别名,将相对路径改为绝对路径
@types/node 模块 在我们使用 node 方法(比如 path.resolve)时提供 ts 类型声明,否则编辑器会报错,虽然不影响代码运行。
yarn add @types/node -D
在 tsconfig.json 中设置 path
"paths": { "@/*": ["./src/*"] }
{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, /* Bundler mode */ "moduleResolution": "node", // "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve", /* Linting */ "strict": true, "noUnusedLocals": false, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "paths": { "@/*": ["./src/*"] } }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "references": [{ "path": "./tsconfig.node.json" }] }
在vite.config.ts中添加如下配置
import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import { resolve } from "path"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { "@": resolve(__dirname, "./src"), }, //extensions: [".ts", ".js", ".vue", ".json", ".mjs"], extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"], }, });
其他他用 方式参考:https://www.cnblogs.com/1285026182YUAN/p/18408078
参考:http://lihuaxi.xjx100.cn/news/1247996.html?action=onClick