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函数
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.生命周期
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'
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(代码臃肿)
- 每次发请求都需要填写完整的请求路径(不利于后期的维护)
- 新建一个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配置基本地址
- 在 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);
}
};
- 调用方法
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')
}
- 二级路由
- 在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 提示警告信息
首页搭建
一般项目会有一个模版让你模仿:
就近原则:在该Layout组件下面建立搭建该组件的字组件components库