Vue3.2+Pinia+VueRouter+Vite+TS环境搭建及知识点学习

一.环境搭建

1.搭建vite项目
npm init vite@latest my-project
cd my-project
npm install
npm run dev
2.安装vue-router
npm install vue-router@next --save

在src目录下创建router文件夹,在文件夹下创建index.ts

import { createRouter,Router, createWebHistory, RouteRecordRaw } from 'vue-router';
const history = createWebHistory()
const routes: Array = [
  {
    path: '/',
    name: '/index',
    component: () => import('../views/Index.vue'),
    meta:{
        title:'首页'
    }
  },
];
const router: Router = createRouter({
   history:createWebHistory('/'),
  routes,
})
export default router

在main.ts中引入router

import router from './router/index'
createApp(App).use(router).mount('#app')

将App.vue中内容替换

3.安装Sass
npm install --save-dev sass
就这么一句安装就可以使用了,用vuecli的时候,还要安装sass-loader、node-sass等,但是vite只需要安装sass就可以了
4.安装pinia
npm install pinia@next

数据持久化,可使用插件pinia-plugin-persist
npm install pinia-plugin-persist -S

import {createPinia} from "pinia"
//需要注意的是从pinia中结构出来的createPinia是一个函数,挂载需要先调用执行
import piniaPersist from 'pinia-plugin-persist'
const store =createPinia()
store.use(piniaPersist)

app.use(createPinia())
// index.js

import {defineStore} from "pinia"
export const useAppStore = defineStore("app",{
    state:()=>{
        return{
            count:1,
            number:1
        }
    },
    getters:{
        bdCount(){
            return  this.count*2
        }
    },
    actions:{
        increment(num){
            this.count=Math.round(100 * Math.random()*num)
        }
    },
    persist: {
        enabled: true,
        //通过修改配置项,改成localStorage
        strategies: [
            {
                storage: localStorage,
                //paths: ["count"], //可以通过paths字段指定需要缓存的数据,如不指定paths,则默认是缓存所有
            },
        ],
    }
})

pinia使用





5.安装axios
  1. 安装axios,然后main.ts文件挂载axios

npm install axios

import Axios from 'axios'
createApp( App ).config.globalProperties.Axios = Axios
  1. 创建utils目录并封装request.ts

import axios, {AxiosInstance, AxiosRequestConfig} from 'axios'
import { $serverUrl } from "../config"
class HttpRequest {
    private readonly baseUrl: string;
    constructor() {
        this.baseUrl = $serverUrl
    }
    getInsideConfig() {
        const config = {
            baseURL: this.baseUrl,
            headers: {
                //
            }
        }
        return config
    }

// 请求拦截
    interceptors(instance: AxiosInstance, url: string | number | undefined) {
        instance.interceptors.request.use(config => {
            // 添加全局的loading..
            // 请求头携带token
            return config
        }, (error: any) => {
            return Promise.reject(error)
        })
        //响应拦截
        instance.interceptors.response.use(res => {
            //返回数据
            const {data} = res
            return data
        }, (error: any) => {
            console.log('error==>',error)
            return Promise.reject(error)
        })
    }

    request(options: AxiosRequestConfig) {
        const instance = axios.create()
        options = Object.assign(this.getInsideConfig(), options)
        this.interceptors(instance, options.url)
        return instance(options)
    }
}

const http = new HttpRequest()
export default http
  1. 创建apis目录编写请求文件

import http from '../utils/request'

export function getFileList() {
  return http.request({
    url: '/lencon/pc/index/school_type',
    method: 'post'
  })
}
export function saveSysEventType (parameter:any) {
  return http.request({
    url: '/lencon/pc/index/titleImg',
    method: 'post',
    data: parameter,
    headers: {
      'Content-Type': 'application/json;charset=UTF-8'
    }
  })
} 
  1. 页面请求编写

6.如果 vue3 报错提示 找不到模块“./XXX.vue”或其相应的类型声明
在src根目录下创建一个后缀为env.d.ts的文件

declare module '*.vue' {
    import type { DefineComponent } from 'vue'
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
    const component: DefineComponent<{}, {}, any>
    export default component
}

declare module '@/store/index.js'

完整vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//npm install @types/node --save-dev
import path from 'path' 
//vite打包性能优化之开启Gzip压缩
import viteCompression from "vite-plugin-compression"; //npm i vite-plugin-compression -D 
export default defineConfig({
  server:{
    host: '0.0.0.0',
    port: 3000,
    strictPort: false, // 若3000端口被占用,是否直接结束项目
    https: false, // 是否开启 https
    open: true, // 是否自动在浏览器打开
    proxy: {
      "/api": {
        target: "http://pro.dangjian.link",
        changeOrigin: true,
        secure: false, // 如果是https接口,需要配置这个参数
        // ws: true, // websocket是否支持
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
    }
  },
  build: {
    target: "es2020", //指定es版本,浏览器的兼容性
    outDir: "dist",
    assetsDir: "assets", // 指定静态资源存放路径
    // cssCodeSplit: true, // css代码拆分,禁用则所有样式保存在一个css里面
    sourcemap: false, // 是否构建source map 文件
    // manifest: true, // 是否在outDir中生成 manifest.json
    rollupOptions: {
      // input: '/path/to/main.ts' // 覆盖默认的 .html 入口
    },
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    }
  },
  resolve: { 
    // 配置项目路径别名,在开发时不需要写完整的路径名称,需要安装 @types/node,执行命令npm i -D @types/node --save-dev即可
    alias: {
      "@": path.resolve(__dirname, "src"),
      "@assets": path.resolve(__dirname, "src/assets"),
      "@components": path.resolve(__dirname, "src/components"),
    },
  },
  optimizeDeps: {
    include: [],
  },
  plugins: [
    vue(),
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: "gzip",
      ext: ".gz",
    })
  ]
})
7.使用Animate.css和hover.css
npm install animate.css --save
import 'animate.css';

Example
Animate.css官网 https://animate.style/
npm i hover.css --save 
import 'hover.css'

Example
hover.css官网 http://ianlunn.github.io/Hover/
8.封装全局方法
main.ts 

const checkImg = (value:string) => {
    if (!value) return ''
    if(value.substring(0,4)!='http'){
      return $serverUrl+value;
    }else{
      return value;
    }
}
app.config.globalProperties.$checkImg = checkImg

组件内使用
import { getCurrentInstance } from 'vue'
const { $checkImg } = currentInstance.appContext.config.globalProperties

9.1安装element-plus(全局引入)
npm install element-plus --save


html 引入






import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)

安装icon

npm install @element-plus/icons-vue

import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
9.2按需导入elementPlus
  1. 按需导入需要再继续安装两个插件

npm install -D unplugin-vue-components unplugin-auto-import
  1. 在项目的vite.config.ts中引入并配置plugins

// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default {
  plugins: [
    // ...
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}
9.3按需导入elementPlus使用icons。且按需引入icnon
  1. 正常使用icon

import * as Icons from "@element-plus/icons-vue";
Object.keys(Icons).forEach((key) => {app.component(key, Icons[key as keyof typeof Icons]);});
 
  1. 按需引入icon

安装插件

npm install -D unplugin-auto-import unplugin-icons

main可以去掉全局引入

// import * as Icons from "@element-plus/icons-vue";
 
const app = createApp(App)
// Object.keys(Icons).forEach((key) => {app.component(key, Icons[key as keyof typeof Icons]);});

vite.config.ts中修改

import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
 
plugins: [
    vue(),
    AutoImport({
      // Auto import functions from Vue, e.g. ref, reactive, toRef...
      // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
      imports: ['vue'],
      // Auto import functions from Element Plus, e.g. ElMessage, ElMessageBox... (with style)
      // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
      resolvers: [
        ElementPlusResolver(),
        // Auto import icon components
        // 自动导入图标组件
        IconsResolver({
          prefix: 'Icon',
        }),
      ],
      dts: path.resolve(path.resolve(__dirname,'src'), 'auto-imports.d.ts'),
    }),
    Components({
      resolvers: [
        // Auto register icon components
        // 自动注册图标组件
        IconsResolver({
          enabledCollections: ['ep'],
        }),
        // Auto register Element Plus components
        // 自动导入 Element Plus 组件
        ElementPlusResolver(),
      ],
 
      dts: path.resolve(path.resolve(__dirname,'src'), 'components.d.ts'),
    }),
    Icons({
      autoInstall: true,
    }),
    // AutoImport({
    //   resolvers: [ElementPlusResolver()],
    // }),
    // Components({
    //   resolvers: [ElementPlusResolver()],
    // }),
    vueSetupExtend (),
  ]

页面使用

 
10.安装 Ant Design of Vue(https://www.antdv.com/docs/vue/getting-started-cn)
npm i --save ant-design-vue

import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';

const app = createApp(App);

app.use(Antd).mount('#app');

如果你使用的 Vite,你可以使用 unplugin-vue-components 来进行按需加载。但是此插件无法处理非组件模块,如 message,这种组件需要手动加载:

import { message } from 'ant-design-vue';
import 'ant-design-vue/es/message/style/css'; //vite只能用 ant-design-vue/es 而非 ant-design-vue/lib
10.1安装Swiper@6使用
npm install swiper@6 --save
import SwiperCore, {Autoplay,Pagination} from 'swiper';
import {Swiper,SwiperSlide} from 'swiper/vue';
import 'swiper/swiper.min.css';
import 'swiper/components/pagination/pagination.scss';
SwiperCore.use([Autoplay,Pagination]);

export default {
        components: {
            Vue3SeamlessScroll,Swiper, SwiperSlide
        },
        setup () {
            const store = useStore()
            let swiper_options = reactive({
                autoplay:{
                    delay:8000,
                    disableOnInteraction: true
                },
                loop:true,
                speed:1000,    
                pagination:{
                    clickable: true
                }
            })
            const data = reactive({
                tableData:[],
                page:1,
                totalPages:1,
                deptId: computed(() => {
                    return store.getters.deptId
                }),
                async getData () {
                    let { deptId } = data
                    let res = await  Axios.post("/lencon/bigData/learning/problem", { deptId,page:data.page,pageSize:5 })
                    if ( res.data.code == 1 ) {
                        data.tableData = res.data.res
                        data.totalPages= res.data.totalPages
                    }
                },
            })
            const onSwiper = (swiper:any) => {
                // if(data.page==data.totalPages){
                //     data.page=1
                // }else{
                //     data.page+=1
                // }
                // data.getData()
            };
            const onSlideChange = () => {
                if(data.page==data.totalPages){
                    data.page=1
                }else{
                    data.page+=1
                }
                data.getData()
            };
 
            watch(() => data.deptId, () => {
                data.getData()
            })
            // 初始化表格
            onMounted(() => {
                data.getData()
            })
     return {
       ...toRefs( data ),
        swiper_options,onSwiper,onSlideChange
   }
 }
10.2 Vue3SeamlessScroll无缝滚动
npm install vue3-seamless-scroll --save

import vue3SeamlessScroll from 'vue3-seamless-scroll';
const app = createApp(App);
app.use(vue3SeamlessScroll)

https://www.npmjs.com/package/vue3-seamless-scroll

11.集成echarts
npm i echarts --save

使用 可以采用这位大佬的笔记 懒得写了  哈哈
https://blog.csdn.net/sunddy_x/article/details/124432548

补充 vite 引入文件

{ iconClass: new URL('@/assets/images/index/index_right_2.png', import.meta.url).href}
12. vite-plugin-vue-setup-extend此插件解决了:使用setup语法糖的时候没办法直接为组件定义name(页面缓存需要name属性)
npm i vite-plugin-vue-setup-extend -D

在vite.config.ts配置

import vueSetupExtend from 'vite-plugin-vue-setup-extend'
plugins: [
    vueSetupExtend ()
    ]

完整的main.ts

import { createApp } from 'vue'

import App from './App.vue'
import router from './router/index'
import Login from '@/components/common/Login.vue'

import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import 'element-plus/dist/index.css'
import 'animate.css'
import 'hover.css'
import {createPinia} from "pinia"
import piniaPersist from 'pinia-plugin-persist'
const store =createPinia()
store.use(piniaPersist)

import Axios from 'axios'
import { $serverUrl } from "./config"
const checkImg = (value:string) => {
    if (!value) return ''
    if(value.substring(0,4)!='http'){
      return $serverUrl+value;
    }else{
      return value;
    }
}

const app = createApp(App)
app.config.globalProperties.$serverUrl = $serverUrl 
app.config.globalProperties.Axios = Axios
app.config.globalProperties.$checkImg = checkImg
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

app.component('Login',Login).use(ElementPlus).use(router).use(store).mount('#app')
13.引入插件时候报错:无法找到模块“pinia”的声明文件等。

可能是TS升级到5.x带来的规范性问题

把tsconfig.json配置项moduleResolution:"bundler"改为 moduleResolution:"node"就可以了

("moduleResolution": "node" 表示 模块化查找的时候按照nodejs方式进行查找。"moduleResolution": "bundler" 表示 打包工具的模块解析策略来查找。 TS升级到5.x,默认"moduleResolution": "bundler" )

————————————————

版权声明:本文为CSDN博主「shuijiao123456」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/shuijiao123456/article/details/130498486

二、组件通讯

  1. 父子向子组件传值

父组件

  
子组件
defineProps<{
   jsonData:any
}>();
  1. 子组件向父组件传值

子组件
const emit = defineEmits(['addclick','removeclick','editclick'])
const addFather = ()=>{
emit('addclick', {name: "干部培训666",type:22} )
}
const removeFather = (e:String,index:number)=>{
emit('removeclick', {id:e,type:index} )
}
const editFather = (e:object,index:number)=>{
emit('editclick', {...e,index:index} )
}

父组件
const addclick = (obj:object)=>{
  data.dirct.push(obj)
}
const removeclick = (e:any)=>{
  data.dirct.splice(e.type,1)
}
const editclick = (e:any)=>{
  data.dirct[e.index].name='干部培训a:'+e.type
}
  1. 父组件获取子组件内的值及修改子组件的值

父组件

我是子组件内的值:{{data.sondata}}
获取子组件值

let navbarheader = ref()
const getSonData= ()=>{
  data.sondata=navbarheader.value.sonName
}
const editSonData= ()=>{
  navbarheader.value.editSon(5)
}

子组件(切记一定要defineExpose出来)
const counts = ref(0)
const editSon = (e:number)=>{
  counts.value+=e
}
const sonName = ref("JackSon");
defineExpose({
sonName,
editSon
})

完整代码

父组件



子组件

  
  
  
  
  1. 补充(兄弟组件传值)

1.安装mitt
npm install --save mitt

2.创建一个js文件 比如 utils/bus.ts

import mitt from 'mitt'
const emiiter = mitt()
export default emiiter

3.组件中使用
组件A传值
import emitter from '../../utils/bus'
emitter.emit('handleEvent', ref({show:true}))

组件B收值
import { ref, reactive, onMounted, computed, watch, onBeforeMount, onUnmounted } from 'vue'  
import emitter from '../../utils/bus'

onBeforeMount(() => {
    // 开启监听,监听到handleEvent事件后,执行handleEvent函数
    emitter.on('handleEvent',handleEvent) 
})

const handleEvent = (Aname:any)=>{
   navData.NeedLogin = Aname.value.show
}

onUnmounted(() => {
    emitter.off('handleEvent',handleEvent)//  取消handleEvent事件的handelEventFn函数监听
    emitter.off('handleEvent')//  取消handleEvent事件的全部处理函数的监听
})
  1. 多层组件传值(Provide / Inject)

import { provide, ref } from'vue'

let flag = ref(1)
provide('flag', flag)

子组件接收
import { inject, Ref, ref } from 'vue'
const flag = inject>('flag', ref(1))

修改值
const change = () => {
    flag.value = 2
}

三、计算属性

购物车案例




四、监听属性watch和watchEffect

watch

watch(token,(newVal,oldVal)=>{
  console.log('新的值----', newVal);
  console.log('旧的值----', oldVal);
},{
  immediate:true,
  deep:true
})
watch([total,token],(newVal,oldVal)=>{
  console.log('新的值----', newVal);
  console.log('旧的值----', oldVal);
},{
  immediate:true
})

immediate的值为true时表示非惰性的立即执行的(默认情况下是false)
deep深层次触发(此处设置deep无意义)
  1. 监视所定义的响应式数据和多个响应式数据

  1. 监听reactive对象

1.若监视的是reactive定义的响应式数据,则无法正确获得oldValue
2.若监视的是reactive定义的响应式数据,则watch会强制开启深度监视
let person = reactive({
    name:'张三',
    age:'18',
    job:{
        j1:{
            salary:20
        }
    }
})
watch(()=>person.name,(newValue,oldValue)=>{
    console.log('person的job改变了',newValue,oldValue)
})
 watch(()=>person.age,(newValue,oldValue)=>{
    console.log('person的job改变了',newValue,oldValue)
})
watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job改变了',newValue,oldValue)
})
//从上边我们发现改变name,age都会触发监听,但是改变job不会
//这是因为name和age属性的值只是一个简单的基本类型数据,
//而job属性的值是一个对象,比较深,想要监视到,就要开启深度监视,程序如下:
watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job改变了',newValue,oldValue)
},{deep:true})//此时job改变,会被监视到,此处的deep配置生效
//需要和情况三进行区分,此处watch监视的是reactive所定义的对象中的某个属性,而情况三watch监视的是reactive所定义的对象

//情况五:监视reactive所定义的响应式数据中的某些属性,写成数组的形式
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
    console.log('person的name或age改变了',newValue,oldValue)
})

watchEffect立即执行,没有惰性

watchEffect函数内部所指定的回调中用到的数据只要发生变化,就会重新执行回调

watchEffect(()=>{
  console.log('新的值----', token.value);
})

对比

是否有惰性

参数

获得值

watch

有惰性,数值再次改变后执行监听函数

可以侦听多个数据的变化

参数可以拿到当前值和原始值

watchEffect

立即执行没有惰性

不需要传递侦听内容,自动感知代码依赖

不需要传递到很多参数,不能获取原始值

五、Vue3生命周期

onBeforeMount()
在组件DOM实际渲染安装之前调用。在这一步中,根元素还不存在。
onMounted()
在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
onBeforeUpdate()
数据更新时调用,发生在虚拟 DOM 打补丁之前。
onUpdated()
DOM更新后,updated的方法即会调用。
onBeforeUnmount()
在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
onUnmounted()
卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。

六、全局组件,递归组件,动态组件

  1. 全局组件的使用

Vue3.2+Pinia+VueRouter+Vite+TS环境搭建及知识点学习_第1张图片
  1. 递归组件的使用

父组件



子组件



  1. 动态组件




知识点补充 markRaw 和 toRaw

Vue3.2+Pinia+VueRouter+Vite+TS环境搭建及知识点学习_第2张图片

七、插槽

  1. 可以使用v-slot: 或者简写为# 进行插槽



 
 
  1. 作用域插槽



 
 
  1. 动态插槽



 
 

八、路由跳转及路由传参

  1. 路由跳转

router.ts

{
    path: '/newsDetail/:id',
    name: '/newsDetail',
    component: () => import('@/view/NewsDetail.vue'),
},

router.push({
    path: `/newsDetail/${item.news_id}`,
    query: {
        cateName: props.cateName
    }
})
  1. 路由传参

import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()

console.log(route.query.cateName)
console.log(route.params.id)

九、keep-alive

开启keep-alive 生命周期的变化

初次进入时: onMounted> onActivated

退出后触发 deactivated

再次进入:

只会触发 onActivated

事件挂载的方法等,只执行一次的放在 onMounted中;组件每次进去执行的方法放在 onActivated中

十、Vue3.x中的自定义hook函数是什么?

  • 使用Vue3的组合API封装的可复用的功能函数

  • 自定义hook的作用类似于vue2中的mixin技术

  • 自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂.

mixins的缺点:

1.变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护。
2.多个mixins的生命周期会融合到一起运行,但是同名属性、同名方法无法融合,可能会导致冲突。
3.mixins和组件可能出现多对多的关系,复杂度较高(即一个组件可以引用多个mixins,一个mixins也可以被多个组件引用)。

补充知识点

declare 是声明的关键字,通常用在编写声明文件,你可以通过 declare 去声明一个变量、声明一个函数等等,一旦声明了,TypeScript 编译器就知道它的存在,你就可以在任何地方使用了。

declare type AsideState = {
    menuList: RouteRecordRaw[];
    clientWidth: number;
};

// columnsAside
declare type ColumnsAsideState = {
    columnsAsideList: T[];
    liIndex: number;
    liOldIndex: null | number;
    liHoverIndex: null | number;
    liOldPath: null | string;
    difference: number;
    routeSplit: string[];
};

const state = reactive({
    menuList: [],
    clientWidth: 0,
});

十一、Teleport将其插槽内容渲染到 DOM 中的另一个位置


  
  

十二、vue3集成使用echarts

1.安装echarts

npm install echarts --save

2.在页面引入

import * as echarts from "echarts";

3.初始化方法

(类型“HTMLElement | null”的参数不能赋给类型“HTMLElement”的参数。 不能将类)

var myChart = echarts.init(document.getElementById('allmetting') as HTMLElement);

另外:vite.config.ts的打包优化请移步:Vite项目打包优化

你可能感兴趣的:(vue,vue.js,前端,vue3,vite,typescript)