Vue3+Vite+Pinia 项目的搭建以及常用方法介绍

一.项目基础构建

1.vite构建vue

npm init vite@latest

2.src下创建router文件夹,router文件夹创建index.js文件

import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";

const routes = [
    {
        path: "/",
        name: "Home",
        component: Home,
    },
    {
        path: "/about",
        name: "About",
        component: () =>
            import(/* webpackChunkName: "about" */ "../views/About.vue"),
    },
];

const router = createRouter({
    history: createWebHistory(),
    routes,
});

export default router;

2.src下创建views文件夹,views文件夹创建About.vue、Home.vue两个路由文件

3.安装插件AutoImport(自动导入 Vue 相关函数,如:ref, reactive, toRef 等),并设置可以让@来代替src使用。

npm i unplugin-auto-import -D

vite.config.js写法如下

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from "unplugin-auto-import/vite"; //自动导入 Vue 相关函数,如:ref, reactive, toRef 等
import { resolve } from 'path';

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      imports: ['vue', 'vue-router']
    })
  ],
  define: {
    'process.env': {},
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
    },
  },
});

二.常用方法介绍

ref:响应式数据,在使用时候需要:str.value

(可以使用volar插件自动添加.value)

let str = reactive('1');

 reactive:响应式数据,不需要.value,只能写对象或者数组

let str = reactive(['a','b','c']);

toRefs:结构响应式数据

let obj = reactive({
    name: '张三',
    age: 20
})

let { name, age } = toRefs(obj)

const btn = () => {
    name.value = '李四'
}

computed:计算属性

// 第1种写法
let changeStr = computed(() => {
    return str.value;
})

// 第2种写法
let changeStr2 = computed({
    get() {
        return str.value;
    },
    set(val) {
        str.value = val
    }
})

watch:监听事件

// (1)监听第一个
let str = ref('这是第一个数据');
watch(str, (newVal, oldVal) => {
    console.log(newVal, oldVal);
})

// (2)同时监听多个(用得不多)-------------------------
let str2 = ref('这是第二个数据');
watch([str, num], (newVal, oldVal) => {
    console.log(newVal, oldVal);
})

// (3)初始化监听-------------------------------------
let str3 = ref('这是第三个数据');
watch(num, (newVal, oldVal) => {
    console.log(newVal, oldVal);
}, {
    immediate: true
})

// (4)监听对象--------------------------------------
watch(obj, (newVal) => {
    console.log(newVal);
}, {
    immediate: true
})

// (5)监听具体的某一个key值,并且深度监听-----------
let obj = reactive({
    a: 1,
    b: 2,
    m: {
        c: 3
    }
})
const btn = () => {
    obj.a = 'xxx';
}
watch(() => obj.a, (newVal, oldVal) => {
    console.log(newVal, oldVal);
}, {
    immediate: true,
    deep: true
})

// (6)立即执行监听函数------------------------------
let str4 = ref('这是第四个数据');
watchEffect(() => {
    console.log(str4.value);
})

// (7)监听路由--------------------------------------
let router = userRouter();
watch(() => router.currentRoute.value, (newVal) => {
    console.log(newVal);
}, {
    immediate: true
})

生命周期钩子

<template>
    首页Home.vue
</template>

<script setup>
import axios from 'axios';
let str = ref("这是数据")

// 请求接口
onBeforeMount(() => {
    axios({
        url: "http://testapi.xuexiluxian.cn/api/slider/getSliders"
    }).then(res => {
        console.log(res);
    })
})

// 生命周期钩子 
// onBeforeMount 
// onMounted 
// onBeforeUpdate 
// onUpdated 
// onBeforeUnmount 
// onUnmounted 
// onErrorCaptured 
// onRenderTracked 
// onRenderTriggered 
// onActivated 
// onDeactivated 

 
// 获取dom
onMounted(() => {
    console.log("获取dom", str.value);
})

// 修改前
onBeforeUpdate(() => {
    console.log("修改前");
})

// 修改后
onUpdated(() => {
    console.log("修改后");
})

// 销毁前
onBeforeUnmount(() => {
    console.log("销毁前");
})

// 销毁后
onUnmounted(() => {
    console.log("销毁后");
})

</script>

路由

// 1. tag属性去除了

<router-link to='/about' tag='div'>跳转到关于我们</router-link>


// 2. 写法问题

let router = useRouter(); ===> this.$router

let route = useRoute(); ===> this.$route


// 3. 导航守卫

全局路由守卫

beforeEach(to, from, next) 全局前置守卫,路由跳转前触发

beforeResolve(to, from, next) 全局解析守卫 在所有组件内守卫和异步路由组件被解析之后触发

afterEach(to, from) 全局后置守卫,路由跳转完成后触发

路由独享守卫

beforeEnter(to,from,next) 路由对象单个路由配置 ,单个路由进入前触发

组件路由守卫

beforeRouteEnter(to,from,next) 在组件生命周期beforeCreate阶段触发

beforeRouteUpdadte(to,from,next) 当前路由改变时触发

beforeRouteLeave(to,from,next) 导航离开该组件的对应路由时触发

组件间的传值

一、父传子

// 父组件
<template>
	<div>
		<List :msg='msg'></List>
	</div>
</template>

<script setup>
import List from '../components/List.vue'
let msg = ref('这是父传过去的数据');
</script>

//子组件:通过defineProps接收
<template>
	<div> 
		这是子组件 ==> {{ msg }}
	</div>
</template>

<script setup>
import {defineProps} from 'vue'
defineProps({
	msg:{
		type:String,
		default:'1111'
	}
})
</script>




// 二、子传父

// 父组件:
<template>
	<div>
		<List @fn='changeHome'></List>
	</div>
</template>

<script setup>
import List from '../components/List.vue'
const changeHome = (n)=>{
	console.log( n.value );
}
</script>

// 子组件:
<template>
	<div> 
		这是子组件 ==> {{ num }}
		<button @click='changeNum'>按钮</button>
	</div>
</template>

<script setup>
import { ref , defineEmits } from 'vue'
let num = ref(200);

const emit = defineEmits(['fn'])

const changeNum = ()=>{
	emit('fn',num)
}	
</script>


// 三、兄弟组件
// 步骤:
// 1》下载使用:mitt,类似于bus,npm install mitt -S
// 2》新建目录文件:plugins/Bus.js
import mitt from 'mitt';
const emitter = mitt()
export default emitter;


// A组件:
<template>
	<div>
		<h1>A组件</h1>
		<button @click='btn'>按钮</button>
	</div>
</template>

<script setup>
import emitter from '../plugins/Bus.js'
import { ref } from 'vue'
let str = ref('这是A组件的数据');

const btn = ()=>{
	emitter.emit('fn',str);
}
</script>

// B组件:
<template>
	<div>
		<h1>B组件</h1>
		{{ s }}
	</div>
</template>

<script setup>
import emitter from '../plugins/Bus.js'
import { ref , onBeforeMount } from 'vue'
let s = ref('');

onBeforeMount(()=>{
	emitter.on('fn',e=>{
		s.value = e.value;
	})

})
</script>

插槽

// 匿名插槽
        //父:
            <A>
                这是xxxxx数据
                这是yyyyy数据
            </A>
        //子
            <header>
                <div>头部</div>
                <slot></slot>
            </header>
            <footer>
                <div>底部</div>
                <slot></slot>
            </footer>
// 具名插槽

        //父:
            <A>
                <template v-slot:xxx>//***简写:<template #xxx>
                    这是xxxxx数据
                </template>
                <template v-slot:yyy>
                    这是yyyyy数据
                </template>
            </A>
        //子:
            <header>
                <div>头部</div>
                <slot name='xxx'></slot>
                <slot name='yyy'></slot>
            </header>
            <footer>
                <div>底部</div>
                <slot name='xxx'></slot>
            </footer>
// 作用域插槽
        //父:
            <template #header='{user}'>
               <div>{{ user.age }}</div>
            </template>
        //子:
            <div>
              <slot name="header" :user="user"></slot>
            </div>
              data(){
                return{
                  user:{
                    name:"张三",
                    age:18
                  }
                }
              }
         //父:
            <template v-slot='{data}'>//  简写:<template #default='{data}'>
                {{ data.name }} --> {{ data.age }}
            </template>
        //子:
            <div v-for='item in list' :key='item.id'>
                <slot :data='item'></slot>
            </div>
//动态插槽:

//说了就是通过数据进行切换

        //父:
            <template #[xxx]>
                这是xxxxx数据
            </template>
            <script setup>
            let xxx = ref('xxx');
            </script>

Teleport:传送

// A组件

<template>
  <h1>子组件</h1>
  <teleport to=".main">
    <div>这是A.vue的传送</div>
  </teleport>
</template>

<script setup>

</script>

// 父组件
<template>
    首页
    <div class="main">
        传送到这里:
    </div>
    <A />
</template>

<script setup>
import A from '@/components/A.vue' 
</script>

动态组件 <component :is="动态去切换组件"></component>

<template>
  <div>
    <ul>
      <li v-for="(item, index) in tabList" :key="index" @click='change(index)'>
        {{ item.name }}
      </li>
    </ul>
    <component :is='currentComponent.com' />
  </div>
</template>
<script setup>
import A from '../components/A.vue'
import B from '../components/B.vue'
import C from '../components/C.vue'
let tabList = reactive([
  { name: 'A准备好的面试题', com: markRaw(A) },
  { name: 'B贮备好的简历', com: markRaw(B) },
  { name: 'C准备好的项目', com: markRaw(C) }
])
let currentComponent = reactive({
  com: tabList[0].com
})
const change = (idx) => {
  currentComponent.com = tabList[idx].com
}
</script>

异步组件    

 ***提升性能

npm i @vueuse/core

使用场景1:

组件按需引入当用户访问到了具体组件再去加载该组件

<template>
  <div ref='target'>
    <C v-if='targetIsVisible'></C>
  </div>
</template>
<script setup>
import { useIntersectionObserver } from '@vueuse/core'
const C = defineAsyncComponent(() =>
  import('../components/C.vue')
)
const target = ref(null);
const targetIsVisible = ref(false);
const { stop } = useIntersectionObserver(
  target,
  ([{ isIntersecting }]) => {
    if (isIntersecting) {
      targetIsVisible.value = isIntersecting
    }
  },
)
</script>

使用场景2 

A组件是异步的如axios或promise等等,需要用Suspense

<Suspense>
  <template #default>//加载出数据后
      <A></A>
  </template>
  <template #fallback>//没加载出数据时
      加载中...
  </template>
</Suspense>
<script setup>
const A = defineAsyncComponent(() =>
  import('../components/A.vue')
)
</script>
----------------------------------------------------------------------------------------------------------------------
<div ref='target'>
 <Suspense v-if='targetIsVisible'>
  <template #default>//加载出数据后
     <C></C>
  </template>
  <template #fallback>//没加载出数据时
      加载中...
  </template>
</Suspense>    
</div> 

打包分包处理(按需引入)

npm run build打包完成后,异步组件有单独的js文件,是从主体js分包出来的

Mixin : 混入

来分发 Vue 组件中的可复用功能

        mixin.js
            import { ref } from 'vue'
            export default function(){
                let num = ref(1);
                let fav = ref(false);
                let favBtn = ()=>{
                    num.value += 1;
                    fav.value = true;
                    setTimeout(()=>{
                        fav.value = false;
                    },2000)
                }
                return {
                    num,
                    fav,
                    favBtn
                }
            }


<template>
  <div>
    <h1>A组件</h1>
    {{ num }}
    <button @click='favBtn'>
      {{ fav ? '收藏中...' : '收藏' }}
    </button>
  </div>
</template>
<script setup>
import mixin from '../mixins/mixin.js'
let { num, fav, favBtn } = mixin();
</script>

Provide / Inject  ==> 依赖注入 传值

提供:

<script setup>
  provide('changeNum', num );
</script>

注入:

<template>
  <div>
    <h1>B组件</h1>
    {{ bNum }}
  </div>
</template>

<script setup>
const bNum = inject('changeNum');
</script>

Vuex

let store=useStore();

 state:

let num = computed( ()=> store.state.num );

getters:

let total = computed( ()=> store.getters.total );

mutations:

store.commit('xxx')

actions:

store.dispatch( 'xxx' )

modules: 和之前的版本使用一样 

 Vuex持久化存储【插件】

npm i vuex-persistedstate -S 
import { createStore } from 'vuex';
import persistedState from 'vuex-persistedstate'

export default createStore({
    state: {
        num: 10,
        sum: 10,
        str: '这是store数据'
    },
    getters: {
        total(state) {
            return state.num + state.sum;
        }
    },
    mutations: {
        changeNum(state, val) {
            state.num = val
        }
    },
    actions: {},
    modules: {
        user
    },
    plugins: [persistedState({
        key: 'keyName',      //浏览器中的名字
        paths: ['user']      //需要存储起来的参数模块
    })]
}) 

Pinia

 Vuex和Pinia的区别

大致总结:

支持选项式api和组合式api写法

1.pinia没有mutations,只有:state、getters、actions

2.pinia分模块不需要modules(之前vuex分模块需要modules)、TypeScript支持很好、自动化代码拆分

3.pinia体积更小(性能更好)        

pinia可以直接修改state数据

一、安装使用Pinia

1.1 安装下载

yarn add pinia
# or with npm
npm install pinia

1.2 main.js引入

import { createPinia } from 'pinia'

app.use(createPinia())

1.3 根目录新建store/index.js中写入

import { defineStore } from 'pinia'

export const useStore = defineStore('storeId', {
  state: () => {
    return {
      counter: 0,
    }
  },
  getters:{},
  actions:{}
})

1.4 组件使用

<script setup>
import { useStore } from '../store'
const store = useStore();
</script>

二、State

2.1 Pinia定义state数据

import { defineStore } from 'pinia'

export const useStore = defineStore('storeId', {
  state: () => {
    return {
      counter: 0,
      name: 'Eduardo',
      isAdmin: true,
    }
  },
  getters:{},
  actions:{}
})

2.2 组件使用pinia的state数据

<template>
	<div>
		<h1>A组件</h1>
		{{ name }}
	</div>
</template>

<script setup>
import { useStore } from '../store'
const store = useStore();
let { name } = store;
</script>

2.3 组件修改pinia的state数据

本身pinia可以直接修改state数据,无需像vuex一样通过mutations才可以修改,但是上面写的let { name } = store;这种解构是不可以的,所以要换解构的方式。

<template>
	<div>
		<h1>A组件</h1>
		{{ name }}
		<button @click='btn'>按钮</button>
	</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useStore } from '../store'
const store = useStore();
let { name }  = storeToRefs(store);
const btn = ()=>{
	name.value = '123';
}
</script>

如果state数据需要批量更新

store/index.js

import { defineStore } from 'pinia'

export const useStore = defineStore('storeId', {
  state: () => {
    return {
      counter: 0,
      name: 'Eduardo',
      arr:['a','b','c']
    }
  },
  getters:{},
  actions:{}
})

组件代码

<template>
	<div>
		<h1>A组件</h1>
		{{ name }}
		{{ counter }}
		{{ arr }}
		<button @click='btn'>按钮</button>
	</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useStore } from '../store'
const store = useStore();
let { name,counter,arr }  = storeToRefs(store);
const btn = ()=>{
	//批量更新
	store.$patch(state=>{
		state.counter++;
		state.arr.push(4);
		state.name = '456';
	})
}
</script>

***使用$patch进行批量更新

三、actions

actions就比较简单了,写入方法,比如我们可以让state中的某一个值+=,而且传入参数

import { defineStore } from 'pinia'

export const useStore = defineStore('storeId', {
  state: () => {
    return {
      counter: 0
    }
  },
  getters:{},
  actions:{
  	changeCounter( val ){
  		this.counter += val;
  	}
  }
})
<template>
	<div>
		<h1>A组件</h1>
		{{ counter }}
		<button @click='add'>加10</button>
	</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useStore } from '../store'
const store = useStore();
let { counter }  = storeToRefs(store);
const add = ()=>{
	store.changeCounter(10);
}
</script>

四、getters

getters和vuex的getters几乎类似,也是有缓存的机制

import { defineStore } from 'pinia'

export const useStore = defineStore('storeId', {
  state: () => {
    return {
      counter: 0,
    }
  },
  getters:{
  	counterPar(  ){
  		console.log(111);
  		return this.counter + 100;
  	}
  },
  actions:{}
})
<template>
	<div>
		{{ counterPar }}
		{{ counterPar }}
		{{ counterPar }}
		<h1>A组件</h1>
		{{ counter }}
	</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useStore } from '../store'
const store = useStore();
let { counter, counterPar }  = storeToRefs(store);
</script>

设置代理和axios二次封装,api解耦

一、设置代理

文件:vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from "unplugin-auto-import/vite"; //自动导入 Vue 相关函数,如:ref, reactive, toRef 等
import { resolve } from 'path';

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      imports: ['vue', 'vue-router']
    })
  ],
  server: {
    proxy: {
      '/api': 'http://testapi.xuexiluxian.cn'
    }
  },
  define: {
    'process.env': {},
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
    },
  },
});

二、axios二次封装

 文件:新建utils/request.js

import axios from 'axios';

//1. 创建axios对象
const service = axios.create();

//2. 请求拦截器
service.interceptors.request.use(config => {
  return config;
}, error => {
  Promise.reject(error);
});

//3. 响应拦截器
service.interceptors.response.use(response => {
  //判断code码
  return response.data;
},error => {
  return Promise.reject(error);
});

export default service;

 文件:新建api/https.js

import request from '../utils/request'

export function mostNew(data) {
    return request({
        url: '/api/course/mostNew',
        method: "post",
        data
    })
}

页面调用

<template>
  <div>
    A组件
  </div>
</template>

<script setup> 
import { getSliders } from '../api/http'
onBeforeMount(() => {
  getSliders().then(res => {
    console.log(666, res.data.list);
  })
})
</script>

<style lang="scss" scoped></style>

使用element-plus

vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
import AutoImport from "unplugin-auto-import/vite";
// @代替src
import { resolve } from 'path';
// 按需引入element-plus
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      imports: ['vue', 'vue-router'],
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
  server: {
    proxy: {
      '/api': 'http://testapi.xuexiluxian.cn'
    }
  },
  define: {
    'process.env': {},
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
    },
  },
});

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你好!对于使用 Vue 3、TypeScript 和 Vite搭建项目,并结合 Pinia 进行状态管理,你可以按照以下步骤进行操作: 1. 首先,确保你已经安装了 Node.js 和 npm(或者使用 yarn)。 2. 创建一个新的项目文件夹,并进入该文件夹。 3. 在终端中运行以下命令来初始化一个新的 Vue 3 项目: ``` npm init vite@latest ``` 在初始化过程中,选择使用 Vue 3、TypeScript 和默认配置。 4. 进入项目文件夹并安装依赖: ``` cd <project-folder> npm install ``` 5. 接下来,安装 Pinia: ``` npm install pinia ``` 6. 在 `src` 目录下创建一个 `store` 文件夹,并在其中创建名为 `index.ts` 的文件。 7. 在 `index.ts` 中编写你的 Pinia store。例如,你可以创建一个名为 `counter` 的 store,并且在其中定义一个状态和一些操作: ```typescript import { defineStore } from 'pinia'; export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), actions: { increment() { this.count++; }, decrement() { this.count--; }, }, }); ``` 8. 在应用的入口文件 `main.ts` 中导入并使用 Pinia: ```typescript import { createApp } from 'vue'; import { createPinia } from 'pinia'; import App from './App.vue'; const app = createApp(App); const pinia = createPinia(); app.use(pinia); app.mount('#app'); ``` 9. 在组件中使用 Pinia store。在你的 Vue 组件中,可以使用 `useStore` 函数来访问 Pinia store: ```typescript import { useCounterStore } from '../store'; export default { setup() { const counterStore = useCounterStore(); return { counterStore, }; }, }; ``` 10. 最后,你可以在组件中使用 `counterStore` 来访问状态和操作: ```vue <template> <div> <p>{{ counterStore.count }}</p> <button @click="counterStore.increment()">Increment</button> <button @click="counterStore.decrement()">Decrement</button> </div> </template> <script> import { useCounterStore } from '../store'; export default { setup() { const counterStore = useCounterStore(); return { counterStore, }; }, }; </script> ``` 这样,你就可以使用 Vue 3、TypeScript、VitePinia 搭建一个基本的项目并进行状态管理了。希望对你有帮助!如果还有其他问题,请随时问我。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

web网页精选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值