Vue3项目学习笔记

284 阅读9分钟

Vue3基础学习

main.js入口文件

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')

createApp创建Vue实例

createApp(App).use(store).use(router).mount('#app')其中.mount('#app')表示将这个app参数对象挂载到app节点上

即这个public/index.html上

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

组合式API

1. 常规方法引入子组件

<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>

<script>
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
export default {
  components: {
    HelloWorld,
  }
}
</script>

js写法引入一个helloWorld的子组件到父组件页面

2.setup 函数

用来设置组件的逻辑和响应式数据的地方。它是在组件内部调用的第一个函数,用于替代Vue2中的各种选项(如 data、methods、computed 等)

写法如下:

import { defineComponent } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src

export default defineComponent({
  components: {
    HelloWorld,
  },
  setup(){
    console.log("hello")
    const message='消息'
    const logMessage=()=>{
      console.log(message)

    }
    return{
      message,
      logMessage
    }
  },

})

defineComponent 函数是 Vue 3 提供的一个工具函数,用于定义组件

const message='消息'相当于将一个message对象转换为可以被组件调用的对象{}

const logMessage=()=>{
     console.log(message)
}

相当于将这个方法转为响应式的方法

还需要将这些响应式的元素进行return return{ message, logMessage }

这样就可以在template中直接调用

    <h1>{{ message }}</h1>
    <button @click="logMessage">点击</button>

特: <script setup>:这是一个开关语法糖,允许在script中书写组合式api

注意,只要使用了setup语法糖,调用子组件连export defalute都可以省去,直接defineComponent 创建一个名为 MyComponent 的组件

<script setup>
import { defineComponent } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
// 在这里定义组件的选项,不需要使用 export default
const MyComponent = defineComponent({
  components: {
    HelloWorld,
  }
});
const message = '消息'
const logMessage = () => {
  console.log(message)
}
</script>

vue3中方法的写法有三种

const logMessage = () => {
    console.log(message)
}
function logMessage() {
    console.log(message);
}
const logMessage = function() {
    console.log(message);
};

function()等同于()=》

个人觉得最舒服的写法为

const logMessage=function(){
//方法体
}

这样可以很清楚的知道这是方法

3.reactive和ref

reactive 则是用来创建一个包装了普通对象的响应式代理。它接收一个普通对象作为参数,并返回该对象的响应式代理。这意味着当对象的属性发生变化时,Vue 会在 UI 中进行相应的更新

ref 是用来创建一个包装了基本类型值(如数字、字符串、数组等)的响应式引用。它返回一个包含 .value 属性的对象,该属性包含了被包装的值。当对这个值进行更改时,Vue 将会在 UI 中进行相应的更新。

注意:当你在 setup 中直接声明一个对象时,这个对象的属性将不会被视为响应式的。换句话说,它们不会自动地在 UI 中进行更新。这是因为 Vue 3 不会自动对 setup 中直接声明的对象进行响应式处理。

let message = '消息'
function logMessage() {
  console.log(message);
  message=message+'变化'
}

这里只能用let,因为常量 为(const),意味着它的值是不可变的

let和var为变量,是可以改变的

message不为响应式,不能改变界面UI的真实值

响应式函数的用法:

let message = '消息'
let varMessage=ref('响应式消息')
let varJson=reactive({
  count :0,
  name:"戴立龙"

})
function logMessage() {
  varMessage.value+='变化'
  varJson.count+=1;
  varJson.name+="修改"
}

对于ref值的修改:对于响应式引用的值的更改应该通过 .value 属性来进行

4.computed函数

const list=ref([1,2,3,4,5,6])
const computeedState = computed(function() {
  return list.value.filter(item => 
    item > 2
  )
})
function logMessage() {
  list.value.push(7,8,1)
  console.log(computeedState.value)
}

vue3中computed函数的写法如下:

const computedState = computed(() => { ... })

5.watch函数

1712739684058.png

watch(varMessage,(newValue,oldValue) => {
  console.log(`新值为${newValue},老值为${oldValue}`)
})

注意

  • 这里的参数必须取名为newValue和oldValue,不能自定义(内置方法)
  • watch方法里面,ref不需要value属性

监听多个对象

// 监听 多个对象
 watch([varMessage,varJson], ([newMes,newJson]) => {
      console.log('New value:', [newMes,newJson.name]);
      
    });

watch函数的两个选项:

deep 选项: deep 选项用于深度监听数据对象内部值的变化。默认情况下,Vue 只会监听对象的引用是否发生变化,而不会深度递归地监听对象内部属性的变化。但是,如果你将 deep 选项设置为 true,Vue 将会递归监听对象内部的所有属性,当对象内部的任何一个属性发生变化时都会触发监听器。

watch(varMessage,
  (newValue) => {
    console.log('New value:', newValue);
  },
  { deep: true });

immediate 选项: immediate 选项用于指定是否在组件创建时立即执行监听器的处理函数。默认情况下,immediate 选项为 false,即不会在组件创建时立即执行处理函数。但是,如果你将 immediate 选项设置为 true,Vue 将会在组件创建时立即执行处理函数,并且在监听的属性发生变化时也会执行处理函数

 watch(varMessage,
      (newValue) => {
      console.log('New value:', newValue);
      },
      {immediate: true});

6.生命周期

1713162524351.png

onmounted方法

onMounted(()=>{
  console.log('开始')
})

注意,vue3里面的生命周期函数可以被多次执行,执行顺序为同步依次执行

onmounted方法调用其他方法

const init = () => {
  console.log(varJson)
}
onMounted(()=>{
  console.log('开始')
  init()
})

7.父传子组件

父组件写法:

<template>
<ChildrenVue :varMessage="varMessage" parentMessage="这里是子组件静态值"/>
</template>

import ChildrenVue from '@/components/ChildrenVue.vue';

子组件写法:

<template>
    <div class="hello">
      <h1>{{ parentMessage }}</h1>
      <h1>{{ varMessage }}</h1>
    </div>
  </template>
  
  
import { defineProps ,onMounted} from 'vue';//必须要引入这个defineProps方法
const props=defineProps({
    parentMessage:String,
    varMessage:String
})
onMounted(()=>{
  console.log(props.parentMessage,props.varMessage)
})

这里要输出父组件的值,需要用到props.parentMessage来进行获取

"props" 是 "properties" 的简写,意为属性

进阶

父组件要传递多个值和使用动态绑定来动态传递数据的写法

<ChildrenVue :varMessage="varMessage"  parentMessage="这里是子组件静态值"/>
let varMessage=ref('响应式消息')

通过:varMessage="varMessage"指定这个varMessage绑定这个响应式数据

8.子传父组件

父组件写法:

父组件通过@标签绑定子组件的方法

<ChildrenVue :varMessage="varMessage"  parentMessage="这里是子组件静态值" @getMessage="getChildren"/> 
    
//这里@绑定一个getMessage方法,将其设置为触发该方法则调用父组件的getChildren方法
    
const getChildren=((parameter)=>{
  console.log('测试子传父',parameter)
})

注意:这里的const getChildren=((parameter)=>{ console.log('测试子传父',parameter) })中的parameter只是一个形参,目的是让子组件的值可以输出

子组件写法:

子组件使用emit方法触发事件

const emit=defineEmits(['getMessage'])
//生成emit方法
const sendMes=(()=>{
  emit("getMessage","这是子传递给父亲的值")
})
onMounted(()=>{
  console.log(props.parentMessage,props.varMessage)
  sendMes()
})

这里的emit("getMessage","这是子传递给父亲的值")是传递数据的核心

9.模版引用

通过ref标识拿到组件实例

重点是h1Ref=ref(null)

<h1 ref="h1Ref">dom标签</h1>
//ref标识进行绑定组件

h1Ref=ref(null)
//将这个标签绑定到ref的value上
const init = () => {
  console.log(varJson)
  console.log(h1ref.value)
}

pinia

pinaia是vue3的状态管理库,以代替vuex。

Pinia 可以帮助用户管理应用程序的全局状态,例如用户身份验证状态、主题设置、语言偏好等。通过定义全局 store,可以在整个应用程序中访问和修改这些状态。

pinia和直接定义全局变量有什么区别?

Pinia 提供了响应式的状态管理机制,可以轻松地在应用程序的不同组件之间共享和同步状态。这意味着当状态发生变化时,相关的组件会自动更新以反映最新的状态。

1. 安装配置pinia

npm install pinia

import { createPinia } from 'pinia'

const pinia = createPinia()

createApp(App).use(store).use(router).use(pinia).mount('#app')

2. pinia的基本用法

  • 定义Store
import { defineStore } from 'pinia'
import {ref} from 'vue'
export const useCounterStore = defineStore('counter', () => {
    const count = ref(0)
    //可以直接写方法,免去了vue2中Actions 相当于组件中的methods的步骤
    const increment=()=> {
      count.value++
    }
  //retrun之后可供其他组件使用
    return { count, increment }
  })

注意:这里export 的元素为 useCounterStore ,其属性和方法均为响应式数据

  • 组件使用Store
 <h1>{{ store.count }}</h1>
  <div style="margin: 2rem;">
      <button @click="store.increment">store测试</button>
    </div>
//响应式绑定方法

import { useCounterStore } from '@/store/counter'

const store = useCounterStore()
const init = () => {
  console.log(store.count)
}

3.Getter

Getter 完全等同于 Store 状态的 计算值,因此pinia可以直接用computed方法进行模拟,不需要写Getter方法

export const useCounterStore = defineStore('counter', () => {
    const count = ref(0)
    const increment=()=> {
      count.value++
    }
    //getter方法
    const computedCount = computed(()=> {
        return count.value*2
      })
  
    return { count, increment,computedCount }
  })

4.action

和组件中定义一个异步方法的方式一致

const action=async()=>{
    const res=await axios.get('/api/geoserver/catalog/getAll')
    console.log(res.data.result)
    }
return { count, increment,computedCount,action }

调用使用 store.action()

完整版(贴合项目)写法

const action=async()=>{
        const res=await axios.get('/api/geoserver/catalog/getAll')
        console.log(res.data.result)
        list.value=res.data.result
        list.value.splice(10)
    }

注意:

  • slice() 方法来截取一个数组,这个方法会返回一个新的数组,包含原数组中从开始到指定索引(不包括该索引)的所有元素

var firstTenItems = arr.slice(0, 10);

  • 在原数组上进行截取而不是创建一个新的数组,可以使用 splice() 方法,但需要注意 splice() 方法会修改原数组

arr.splice(10);

项目起步

别名路径联想设置

在编写代码过程中使用@ 即可查找src目录下的所有文件结构 import { useCounterStore } from '@/store/counter'

image.png

elementplus

导入

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

注意:这里会报错类型“{ version: string; install: (app: App, options?: any) => void; }”的参数不能赋给类型“Plugin”的参数。

解决方法:在 tsconfig.json 中通过 compilerOptions.type 指定全局组件类型

{
  "compilerOptions": {
    // ...
    "types": ["element-plus/global"]
  }
}

axios基本配置

axios在vue中使用,如果不封装的请求写法是如下这样:

//先导入axios
import axios from 'axios';

//然后再使用
onMounted(async () => {
  axios.get('http://localhost:3000/banner?type=2').then((res) => {
    console.log(res);
    state.images = res.data.banners;
    console.log(state.images);
  });
});

但是在实际项目开发中,几乎每个组件都会用到axios发起数据请求,此时会遇到如下两个问题:

  • 每个组件中都需要导入axios(代码臃肿)
  • 每次发请求都需要填写完整的请求路径(不利于后期的维护)

1713341356874.png

  1. 新建一个utils/http.js的axios配置文件
import axios from 'axios';
let service = axios.create({
  baseURL: 'http://192.168.10.12:8180/',
  timeout: 3000,
});
export default service;

axios.create配置基本地址

  1. 在 api.js添加需要用到axios请求的接口函数:
import service from '@/utils/http';
export function getAll() {
  return service({
    method: 'GEt',
    url: '/geoserver/catalog/getAll',
  });
}
export const getByID=(id)=>{
    return service({
        method: 'GEt',
        url: '/geoserver/catalog/getById',
        params: { id } 
    })
}
//另一种写法
export const getByID = async (id) => {
    try {
      const response = await service.get('/geoserver/catalog/getById', {
        params: {id},
      });
      console.log(response);
    } catch (error) {
      console.error(error);
    }
  };
  1. 调用方法
import { getAll,getByID } from '@/api/api';
const { BigInt } = window;
  let resAll = await getAll();
  let resByID=await getByID(BigInt(1775420333340360700))

路由设计

  • 一级路由
  {
    path: '/layout',
    name: 'layout',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '@/views/Layout/IndexView.vue')
  },
  {
    path: '/login',
    name: 'login',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '@/views/Login/IndexView.vue')
  }
  • 二级路由
  1. 在router里面使用children来将二级路由进行表示

其中默认二级路由的设置是 path:''

import HomeView from '../views/HomeView.vue'
import CategoryView from '@/views/Category/IndexView.vue'   
    
  {
    path: '/layout',
    name: 'layout',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '@/views/Layout/IndexView.vue'),
    children:[
      {
        path:'',
        component:HomeView
      },
      {
        path:'category',
        component:CategoryView
      }
    ]
  },

2.在Layout/IndexView.vue里面写一个路由的组件访问接口 <router-view/>

<template>
    <div >首页</div>
    <router-view/>
<div><el-button @click="routeAlter">更改二级路由</el-button></div>
</template>
<script setup>
import {useRouter} from 'vue-router';
const router=useRouter();
const routeAlter = () => {
    router.push({
        path:'/layout/category'
    })

}
</script>

注意: 这里使用import {useRouter} from 'vue-router';来实现vue3.0中路由跳转,核心代码 router.push({ path:'/layout/category' })

其他杂项

1.引入自定义样式

//引入自定义的样式文件
import '@/style/common.scss'

2.error lens 提示警告信息

首页搭建

一般项目会有一个模版让你模仿:

image.png

就近原则:在该Layout组件下面建立搭建该组件的字组件components库 1713513556253.png