Vue3入门
1 项目介绍
之前跟着实战项目做了Vue2的项目,由于对Vue学艺不精,以个人能力比较难跟着Vue2做Vue3项目,且Vue3可参考的博客对Vue基础比较高。故现在把Vue3实战关键知识点记录下来,仅作学习用途。
本项目是一个用Vue3实现的待做任务清单。点击网页的按钮跳转到清单页面(主要页面)。在清单页面,可以在上方输入框内输入待做任务:当input不在列表中时在列表中生成任务。点击任务前面的checkbox表示任务已完成。鼠标在当前行时改变背景颜色且显示删除链接,点击删除实现删除任务的功能。最底下是任务和已完成的数量统计,以及清除已完成按钮。
2 新建项目
2.1 新建
cmd进入新建项目的地址,输入
Vue3 create 项目名称
选择Manually select features,自定义Vue版本、用到的features(比如Vuex/Router/Babel等)、以及后续设置。
cd 项目名称
进入项目文件夹,运行
npm run serve
命令即可运行vue项目。
2.2 目录结构
整个文件目录:node_modules项目依赖;public中的.ico文件是浏览器标签页最左侧的图标,html文件是项目最终的呈现页面;babel.config.js管理babel(转义语法);package.json包管理;vue.config.js 设置。
src文件: assets静态文件,components组件,router 配置路由,store状态,views路由组件, App.vue是根组件,main.js是入口js文件。
由于是单页面网页,实际上把vue的主页放入publc/index.html的id="app"的div元素。
2.3 vue文件结构
vue文件分为三块:template 主要写HTML,script 主要写js,style 写样式。
其中template标签只能有一个子节点,若有多个标签应该写入一个div中。
3 组件
在components文件夹新建三个组件的文件夹,每个文件夹新建相应的Vue文件。
在Views文件夹下的HomeView.vue当成主页面,在script标签中输入import引入三个组件,@指代src文件夹,比如
import 组件 from ‘@/components/组件文件夹/组件’。
3.1 组件基本设置
import 组件名 from 组件路径
import {defineComponent,ref} from 'vue'
export default defineComponent {
name: 'Home',
props: { // 父组件的传值
},
components: {
},
setup(props, ctx) { //处理数据
let a = ref('sd')
let b = ref(3)
return {
a,
b
}
}
注意setup中的数据一定要写入return,此时这几个引入的组件就可以像html标签一样在template标签中使用了。比如{{a}}。类似之前vue2中写的: script标签中的export default中的data {}中的变量就能在html中使用了。
3.2 数据
3.2.1 ref
定义单个数据 ,主要用于setup函数中
import {ref} from 'vue' //首先引入ref
setup(props, ctx) {
let num = ref(4)
let arr = ref(['a','v','t']) //ref定义数组
let obj = ref({ //ref定义对象
age: 10,
name: 'aaa'
})
return { //要有返回,才能在html中使用
num, //实际上是键值对num: num,由于两个值相同因此可以简写
arr,
obj
}
}
注意:在方法中访问ref定义的数据的值要使用value属性,如a.value
3.2.2 reactive
实际上是一个对象,比ref更便捷, 使用数据需要加前缀,如下面要使用obj对象需要写成 data.obj。
import {reactive} from 'vue' //同样要先引入
setup(props, ctx) {
let data = reactive({
str: 'obj',
arr: ['q','t','9','t','y'],
obj:{
size: 10,
width: 20,
id: '124'
}
})
return {
data
}
}
3.2.3 toRefs
和reactive组合使用,可以简化前缀。如果使用的时候不想要前缀,可以使用toRefs。注意:里面的变量名不能和其他单独定义的变量重名。
import {toRefs} from 'vue' //引入
// 前面同上
return {
// data
...toRefs(data)
}
3.3 方法
箭头函数
let 方法名 = (参数) => {
}
4 Vuex状态管理
4.1 基础
4.1.4 state基础设置 createStore & useStore
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: { //定义状态
name: 'js'
},
// 同步修改状态
mutations: {
setName(state, payload) {
state.name = payload
}
},
// 异步提交mutations
actions: {
asyncSetName(store, params) {
setTimeout(() => {
// commit是调用mutations的方法
store.commit('setName', params)
}, 2000)
}
},
// 模块
modules: {
}
})
通过计算属性得到vuex中定义的数据,此时在该页面template标签可以使用list
// 主页面比如Home.vue
<script scoped lang="scss">
import { useStore } from 'vuex'
// 通过computed得到store/index.js中的state字段里的list值
setup() {
let state = useStore()
let list = computed(() => {
return store.state.list
})
return {
list
}
}
</script>
4.2.2 computed计算
必须有返回值,调用使用函数名即可。
<template>
<div>
<!-- 使用computed计算只需要调用函数名-->
{{a}}+{{b}} 两数之和为 {{add}}
</div>
</template>
<script>
import {defineComponent, computed, ref} from 'vuex'
export default defineComponent {
name: '',
props: {},
components: {},
setup() {
let a = ref(1)
let b = ref(5)
let add = computed(() => {
return a.value + b.value
})
return {
a,
b,
add
}
}
}
</script>
5 路由
5.1 router文件夹下的index.js
哈希模式createWebHashHistory比历史模式在url上多一个#
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 配置路由
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// 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/AboutView.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL), //历史模式
routes: routes //同名可以简写成routes
})
export default router
5.2 组件使用路由
在需要跳转的组件页面,使用router自带的函数,比如back, push, go。这里只显示和路由跳转有关的代码,实现了点击按钮就跳转到about页。
<template>
<div>
<button @click="goto">跳转到ABOUT</button>
</div>
</template>
<script>
import {useRouter} from 'vue-router'
export default defineComponent({
setup(props, ctx) {
let router = useRouter()
// 在按钮中定义的点击事件触发goto跳转函数
let goto = () => {
// 使用router自带的push函数,参数为router文件夹下js文件中定义的path
router.push('/about')
}
return {
goto
}
}
})
</script>
5.3 路由传参
使用router对象的push函数传递参数的两种办法。
5.3.1 query传参
会在地址栏显示参数值,刷新后值还在,参数传递过去的类型是string,有path/name其一即可
router.push({
// 这里选择path或name即可
path: '/home',
query: {
name: name.value,
num: num.value,
obj: JSON.stringify(obj.value)
}
})
5.3.2 params传参
地址栏不显示参数,刷新后值消失,要求有name。其中name属性是router中js文件的name属性值。
router.push({
// params传参必须是name
name: 'home',
params: {
name: name.value,
num: num.value,
obj: JSON.stringify(obj.value)
}
})
5.3.3 接收参数
在接收参数的组件中接收参数的相关代码,其中route是当前路由对象。
<script>
...
import {useRoute} from 'vue-router'
export default defineComponent({
...
setup() {
// route是当前路由对象。可以接收query传递的参数。
let route = useRoute()
// 这是query传参的接收
console.log(route.query.num)
// 这是params
console.log(route.params.num)
...
return {
}
}
})
</script>
6 生命周期
setup 组件创建的过程
onMounted 组件挂载的过程。数据、dom。作用:发请求; 初始化数据,接收路由传参。
onUnmounted 组件卸载的生命周期。作用:清除定时器、闭包函数…
7 父子组件传参
7.1 父组件传值
父组件把要传的参动态绑定,子组件通过props接收。如 :msg=‘msg’, 前者:msg是子组件接收参数存储的对象, 后者msg是父组件中要传参的名称。
在子组件中设置props,必须要设置数据类型校验。
然后在setup函数中设置参数props,此时直接用props.msg即为父组件的传参。子组件要显示该参数只需要写{{msg}}。
// 父组件的前端样式,动态绑定子组件,传递参数给子组件的msg对象
<child :msg='msg'></child>
// 子组件的前端 使用msg即可直接引用父组件传值
{{msg}}
// 子组件的脚本
import { defineComponent , ref , onMounted} from 'vue'
export default defineComponent({
name: 'child',
// props接收父组件的传值
props: {
msg: {
// 数据类型校验
type: String
// 参数是否必传,默认F
// required: True,
// default: '默认值'
}
},
setup(props, ctx) {
// 打印props的msg对象
console.log(props.msg)
let childMsg = ref('我是子组件的数据')
let childNum = ref(11)
let send = () => {
// 通过ctx.emit分发事件 第一个参数是事件名称,第二个是数据
ctx.emit('send', childMsg.value)
}
// 分发事件可以通过按钮点击事件触发,也可以在onMounted触发
onMounted (() => {
ctx.emit('send', childMsg.value)
// 传递多个参数 1.数组 2.对象
ctx.emit('sen', [childMsg.value, childNum.value])
ctx.emit('s', {
msg: childMsg.value,
num: childNum.value
})
})
return {
childMsg,
childNum,
send
}
},
})
7.2 子组件传值
子组件通过ctx的emit分发事件传值,第一个参数是事件名称,第二个是数据;分发事件可以通过按钮点击事件触发,也可以在onMounted触发。传递多个参数时, 1.数组 [obj1.value,obj2.value…] 2.对象 {name1: obj1.value,name2: obj2.value…}。父组件通过@事件设置回传函数获取参数,@事件名即emit设置的第一个参数。
// 父组件前端
<child @sen='send'></child>
// 父组件脚本
setup() {
...
// 父组件接收子组件传值 通过send函数获取参数
let send = (val) => {
console.log(val)
}
return {
...
send
}
},
// 子组件前端
<button @click="sen">传值给父组件</button>
// 子组件的脚本
import { defineComponent , ref , onMounted} from 'vue'
export default defineComponent({
...
setup(props, ctx) {
let childMsg = ref('我是子组件的数据')
let childNum = ref(11)
let sen = () => {
// 通过ctx.emit分发事件 第一个参数是事件名称,第二个是数据
ctx.emit('send', childMsg.value)
}
// 分发事件可以通过按钮点击事件触发,也可以在onMounted触发
onMounted (() => {
ctx.emit('send', childMsg.value)
// 传递多个参数 1.数组 2.对象
ctx.emit('sen', [childMsg.value, childNum.value])
ctx.emit('s', {
msg: childMsg.value,
num: childNum.value
})
})
return {
childMsg,
childNum,
send
}
},
})