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、并添加甚至删除stateactionsgetters

目前,只有Vite被官方支持,不过任何实现import.meta.hot规范的构建工具都应该能正常工作。你只需要在任何 store 声明旁边添加这段代码。比方说,你有三个 store:auth.jscart.jschat.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 类型声明,否则编辑器会报错,虽然不影响代码运行。

 安装 @types/node
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"],
  },
});

 

 

 

 

 

 

 

 

 

 

 

 

参考:http://lihuaxi.xjx100.cn/news/1247996.html?action=onClick

 

posted @ 2023-11-10 13:09  无心々菜  阅读(162)  评论(0编辑  收藏  举报