前端开发相关文档链接
Axios文档: https://www.axios-http.cn/docs/interceptors
VueRouter文档:https://v3.router.vuejs.org/zh/guide/
Vue API文档:https://cn.vuejs.org/api/
可视化图表库:https://echarts.apache.org/zh/index.html
Element Plus组件库:https://element-plus.org/zh-CN/
一、Vue简介
Vue 是一个用于构建用户界面的渐进式 JavaScript 框架,是前端三大框架之一(Angular、React、Vue)。它设计灵活,可以逐步集成到项目中,既适用于小型交互功能,也能支撑复杂的单页应用(SPA)。Vue 的核心库专注于视图层,同时提供配套工具和生态系统支持高级功能开发。
文档链接:https://cn.vuejs.org
注:vue2时,router为3,vuex为3;vue3时,router为4,vuex为4
1.1 快速上手
下面是vue的一个简单应用的示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 调用data中的数据 -->
{{ msg }}
</div>
<!-- 如果不下载,直接导入官方包 导入后有 Vue 构造函数 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app", // 指定挂载点
data: { // 提供数据
msg: "hello world"
}
})
</script>
</body>
</html>
插值表达式
上面的代码用到了插值表达式
{{ }},将data中的 msg 数据会展示在界面中,插值表达式中还可以以运算、函数等方式写入,插值表达式用于展示文本,而不适用于标签属性
<div id="app">
<p>{{ usename }}</p>
<p>{{ age + 1 }}</p>
<p>{{ age > 10 ? "大于" : "小于" }}</p>
<p>{{ func() }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app", // 指定挂载点
data: { // 提供数据
usename: "Tony",
age: 18
}
})
</script>
1.2 构造函数的选项对象
| 选项参数 | 说明 |
|---|---|
| el | 提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标 |
| data | 实例的数据对象。将会递归将data的属性转换为getter/setter,从而让data的属性能够响应数据变化 |
| methods | Vue实例的方法集合,可以在Vue直接调用或将方法绑定到DOM元素的事件上 |
| computed | Vue实例的计算属性集合 |
| watch | 观察Vue实例变化的一个表达式或计算属性函数 |
| components | 包含Vue实例可用组件的哈希表 |
| filters | 包含Vue实例可用过滤器的哈希表 |
| template | 定义字符串模板作为Vue实例的标识使用 |
computed
作用:封装了一段对于数据的处理,求得一个结果。依赖的数据变化,自动重新计算。
拥有缓存特性,即:多次调用时只会计算一次
声明在computed配置项中,一个计算属性对应一个函数,使用起来和普通属性一样使用{{ 计算属性名 }}
<div id="app">
<!-- total后面不能加括号 -->
<p>{{ total }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
computed: {
total () {
return 10
}
}
})
</script>
如果要进行修改操作,需要写计算属性的完整写法
<div id="app">
<!-- total后面不能加括号 -->
<p>{{ total }}</p>
<button @click="func">按钮</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
usename: "Tony"
},
methods: {
func () {
this.total = "Jack"
}
},
computed: {
total: {
get () {
return this.usename
},
set (value) {
this.usename = value
}
}
}
})
</script>
watch侦听器(监视器)
作用:执行一些业务逻辑或异步操作监视数据变化
简单写法:简单类型数据,直接监视
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
usename: "Tony"
},
watch: {
// 监听 usename 变化 变化时调用
usename (newValue, oldValue) {
console.log(newValue, oldValue)
}
// 子属性用法如下
// 'xx.usename' (newValue, oldValue) {}
}
})
</script>
完整写法:添加额外配置项
deep:true对复杂类型深度监视
immediate:true初始化立刻执行一次handler方法
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
obj: {
use: "Tony",
age: 10
}
},
watch: {
// 对属性中所以子属性监视
obj: {
deep: true,
handler (newValue, oldValue) {
console.log(newValue, oldValue)
}
}
}
})
</script>
1.3 生命周期(钩子函数)
Vue 组件的生命周期指的是从创建、挂载、更新到销毁的完整过程。每个阶段会触发对应的生命周期钩子函数,开发时可以通过这些钩子执行特定逻辑
生命周期函数可以分为如下四类(钩子函数)
初始化阶段(响应数据):beforeCreate → created
挂载阶段(渲染模板): beforeMount → mounted
更新阶段(数据修改):beforeUpdate → updated
销毁阶段(销毁实例):beforeDestroy → destroyed
| 函数 | 含义 |
|---|---|
| beforeCreate | 实例初始化后,数据观测(data observer)和事件配置之前调用。此时无法访问 data、methods 等属性 |
| created | 实例创建完成,数据观测和事件配置已完成。可访问 data 和 methods,但尚未挂载 DOM,$el 不可用 |
| beforeMount | 模板编译完成,但尚未将虚拟 DOM 渲染为真实 DOM |
| mounted | 实例挂载到 DOM 后调用,$el 可用。常用于操作 DOM 或发起异步请求。 |
| beforeUpdate | 数据更新时触发,发生在虚拟 DOM 重新渲染和打补丁之前。 |
| updated | 数据变更导致虚拟 DOM 重新渲染后调用。避免在此钩子中修改数据,可能导致无限循环。 |
| beforeDestroy | 实例销毁前调用,此时实例仍完全可用。适合清理定时器、解绑事件等操作。 |
| destroyed | 实例销毁后调用,所有指令和事件监听器被移除,子实例也被销毁 |
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
obj: "hello world"
},
beforeCreate () {
console.log("beforeCreate")
},
beforeMount () {
console.log("beforeMount")
}
})
</script>
1.4 Vue常用指令介绍(v-html、v-for等)
| 指令 | 含义 |
|---|---|
| v-html | 设置元素的innerHTML |
| v-show | 控制元素显示与隐藏 (有元素标签,通过 display 控制显示隐藏,适合频繁切换) |
| v-if v-else v-else-if | 控制元素显示与隐藏 (根据条件来控制 元素标签 的创建和移除 ) |
| v-for | 循环 |
| v-on | 事件绑定 ( v-on:click='xxx'简化为@click='xxx') |
| v-bind | 动态的设置html的标签属性 ( v-bind:xxx='xxx'简化为:xxx='xxx') |
| v-model | 双向数据绑定 (常用于表单输入框等) |
| v-slot | 占位符 |
v-html
作用:设置元素的innerHTML
语法:v-html="表达式"
<div id="app">
<div v-html="msg"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 以 innerHTML形式解析到界面 会解析标签
msg: `<h1>标题</h1>
<p>内容</p>`
}
})
</script>
v-on
作用:事件绑定,比如按钮点击事件绑定
语法:v-on:事件名="内联语句或函数",或简写为@事件名="内联语句或函数"
<body>
<div id="app">
<p> {{ age }}</p>
<button v-on:click="age--">点击-1</button>
<!-- v-on: 简化为 @ 传参 @click="func(参数)"-->
<button @click="func">点击+1</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app", // 指定挂载点
data: { // 提供数据
age: 18
},
methods: {
func () { // 接收参数 func (参数) {}
this.age++
}
}
})
</script>
</body>
除了click还有如下事件
| 事件 | 含义 |
|---|---|
| click | 点击一次 |
| dblclick | 双击 |
| focus | 获取焦点 |
| blur | 失去焦点 |
| mouseover | 鼠标移入 |
| mouserout | 鼠标移出 |
| keyup | 键盘按下 |
| @keyup.enter | 键盘回车监听 |
| @click.stop | 阻止冒泡 |
| @click.prevent | 阻止默认行为 |
<div id="app">
<!-- @keyup.enter 绑定 func 函数,输入后点 enter 键 等于触发func 函数 -->
<input @keyup.enter="func" type="text" v-model.trim="username"></input>
<button @click="func">按钮</button>
</div>
v-bind
作用:动态的设置html的标签属性(src、url、title等)
语法:v-bind:属性名="表达式",或简写为:属性名="表达式"
<body>
<div id="app">
<a v-bind:href="url">baidu</a>
<!-- v-bind: 简化为 : -->
<a :href="url">baidu</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
url: 'https://www.baidu.com',
}
})
</script>
</body>
v-bind 可以操作 class 属性
语法::class="对象/数组"
对象:键就是类名,值是布尔值。如果值为true,有这个类,否则没有这个类
数组:数组中所有的类,都会添加到盒子上,本质就是一个class列表
<div :class="{ 类名1: 布尔值, 类名2: 布尔值}"></div>
<div :class="[类名1, 类名2]"></div>
v-bind 可以操作 style 属性
语法::style="样式对象"
<!-- backbround-color 要写成 'background-color' 或 backgroundColor -->
<div :style="{ css属性名1: CSS属性值, css属性名2: CSS属性值}"></div>
<div :class="[类名1, 类名2]"></div>
v-for
作用:基于数据循环,多次渲染整个元素
语法:v-for="(item, index) in 数组",或者不带索引v-for="item in 数组"
<body>
<div id="app">
<ul>
<!-- :key 添加唯一标识 -->
<li v-for="(item,index) in items">{{ index+1 }} {{ item }}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
items: ["Tony", "Jack"]
}
})
</script>
</body>
也可以字典格式
:key作用:给元素添加的唯一标识,便于Vue进行列表项的正确排序复用(必须唯一性,推荐使用id,不推荐使用index)
<ul>
<li v-for="(item, key) in items" :key="key">{{ key }} : {{ item }}</li>
</ul>
items: {
use:"Tony",
age: 18
}
也可以是复杂的数组
<ul>
<li v-for="(item, key) in items" :key="item.id">{{ item.use }} : {{ item.age }}</li>
</ul>
items: [
{
use: "Tony",
age: 18,
id: "001"
},
{
use: "Jack",
age: 10,
id: "002"
}
]
v-model
作用:给表单元素使用,双向数据绑定(输入框内容变化,值也会变化) ,可以快速获取或设置表单元素内容
语法:v-model='变量',去除收尾空格:v-model.trim='变量',转数字:v-model.number ='变量'
<body>
<div id="app">
账号:<input type="text" v-model="username"></input>
密码:<input type="password" v-model="password"></input>
<button @click="func">按钮</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
username: "Tony",
password: "123456"
},
methods: {
func() {
console.log(this.username)
console.log(this.password)
}
}
})
</script>
</body>
常见的表单元素都可以用v-model绑定关联,快速获取或设置表单元素的值,它会根据控件类型自动选取正确的方法来更新元素
比如:输入框(input:text)、文本域(textarea)、复选框(input:checkbox)、单选框(input:radio)、下拉菜单(select)
<body>
<div id="app">
<input type="text" v-model="username"> <!-- 输入框 -->
<br>
<input type="checkbox" v-model="isFlag"> <!-- 复选框 -->
<br>
<!-- 单选 --> <!-- name属性相同会分到一组 -->
<input v-model="gender" type="radio" name="gender" value="1">男
<input v-model="gender" type="radio" name="gender" value="2">女
<br>
<select v-model="city">
<option value="101">北京</option>
<option value="102">上海</option>
</select>
<textarea v-model="desc"></textarea>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
username: "Tony",
isFlag: true,
gender: "1",
city: "102",
desc: ""
}
})
</script>
</body>
1.5 自定义指令(v-自定义)
Vue 允许注册自定义指令,用于对 DOM 元素进行底层操作。自定义指令适合封装需要直接操作 DOM 的逻辑,例如自动聚焦、拖拽、权限控制等
自定义指令提供以下钩子函数,用于在不同阶段执行逻辑
| 钩子函数 | 作用 |
|---|---|
| bind | 指令第一次绑定到元素时调用(仅一次)。 |
| inserted | 被绑定元素插入父节点时调用。 |
| update | 所在组件更新时调用(可能发生在子组件更新前)。 |
| componentUpdated | 所在组件及其子组件全部更新后调用。 |
| unbind | 指令与元素解绑时调用。 |
全局注册
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
Vue.directive("指令名", {
"inserted" (el) {
// 可以对 el 标签,扩展额外功能
el.focus()
}
}
new Vue({
render: h => h(App),
}).$mount('#app')
局部注册
export default {
directives: {
"指令名": {
inserted (el) {
// 可以对 el 标签,扩展额外功能
el.focus()
}
}
}
}
自定义指令的使用
<input v-指令名 type="text">
举例:实现一个color指令-传入不同的颜色变量,当变量变化时,文字颜色同步变化
<!-- color为颜色值,当发生变化时,颜色也发生变化 -->
<div v-color="color">颜色</div>
通过
binding.value可以拿到变量color的值,inserted提供的是元素被添加到页面中时的逻辑,指令值修改会触发update函数
export default {
directives: {
"color": {
inserted (el, binding) {
el.style.color = binding.value
},
update (el, binding) {
el.style.color = binding.value
},
}
}
}
1.6 Vue异步更新、$nextTick
当进入页面立马获取输入框的焦点时,可能获取不到,原因是Vue异步更新,界面可能还没完全渲染,此时需要用
$nextTick,等DOM更新后,才会触发执行此方法里的函数体
<template>
<div>
<input ref="inp" type="text">
</div>
</template>
<script>
export default {
methods: {
handleClick () {
// 获取焦点
// this.$refs.inp.focus() // 直接使用可能获取不到,可能还没渲染出来
this.$nextTick(() => { // 渲染完成之后,再获取
this.$refs.inp.focus()
})
}
}
}
</script>
<style scoped></style>
二、工程化搭建VUE项目(工程化开发与脚手架)
安装脚手架
全局安装(以管理员身份运行):
yarn global add @vue/cli(下载后如果无法识别vue,按这个指令下载npm i @vue/cli -g)
查看 Vue 版本:vue --version
2.1 项目创建与组件化开发
创建启动项目
创建项目架子(管理员省份运行):
vue create project-name(项目名-不能用中文)
启动项目:yarn serve或npm run serve(package.json中的script中配置了serve项)
创建项目会出现如下目录结构
PROJECT_DEMO
| - node_modules # 第三方文件夹
| - public # 存放 HTML 文件的地方
| | - favicon.ico # 网站图标
| | - index.html # index.html 模板文件
| - src # 项目代码开发位置
| | - assets # 静态文件目录 (图片、字体)
| | - components # 组件目录 (通用组件)
| | - App.vue # App根组件
| | - main.js # 入口文件 (打包或运行入口文件)
| - .gitignore # git忽视文件
| - babel.config.js # babel配置文件
| - jsconfig.js # js配置文件
| - onpackage.json # 项目配置文件>包含项目名、版本号、scripts、依赖包等
| - README.md # 项目说明文档
| - vue.config.js # vue-cli配置文件
| - yarn.lock # yarn锁文件,由yarn自动生成的,锁定安装版本
组件化开发
组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。
好处:便于维护,利于复用提升开发效率。
组件分类:普通组件、根组件。
根组件:整个应用最上层的组件,包裹所有普通小组件。
组件的三大组成部分:结构(
<template>)、逻辑(<script>)、样式(<style>)
<!-- 结构 -->
<template>
<div class="hello">
<h1>Hello</h1>
</div>
</template>
<!-- 逻辑 -->
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- 样式 -->
<style scoped>
.hello {
margin: 40px 0 0;
}
</style>
样式要支持less语法,需要在style上加上lang=“less”,即
<style lang="less">
还需要安装less、less-loader包,yarn add less less-loader -D
样式冲突(<style>)
全局样式(默认):默认组件中的样式会作用到全局
<style>
/* 默认全局样式 会影响其他组件样式 */
</style>
局部样式:可以给组件加上
scoped属性,可以让样式只作用于当前组件
<style scoped>
/* 局部样式 只在当前组件中有效 (加上 scoped ) */
</style>
2.2 普通组件的注册使用(局部与全局)
局部注册(只能在注册的组件内使用)
- 在components中创建 .vue 文件(三个组成部分)
<template>
<div>header部分</div>
</template>
<script></script>
<style scoped></style>
- 在使用的组件内导入注册并使用(如下面的MyHeader)
<template>
<div id="app">
<!-- 组件使用 -->
<MyHeader></MyHeader>
</div>
</template>
<script>
// 导入需要注册的组件
import MyHeader from './components/MyHeader.vue'
export default {
components: {
MyHeader: MyHeader // 局部注册
}
}
</script>
<style scoped></style>
全局注册(所有组件内都能使用)
- 在components中创建 .vue 文件(三个组成部分)
<template>
<button>按钮</button>
</template>
<script></script>
<style scoped></style>
- main.js中进行全局注册
import Vue from 'vue'
import App from './App.vue'
// 导入全局组件
import MyButton from './components/MyButton.vue'
Vue.config.productionTip = false
// 进行全局注册
Vue.component("MyButton", MyButton)
new Vue({
render: h => h(App),
}).$mount('#app')
- 全局使用
<template>
<div id="app">
<!-- 组件使用 -->
<MyButton></MyButton>
</div>
</template>
<script></script>
<style scoped></style>
2.3 组件间插槽使用(slot)
作用:让组件内部的一些结构支持自定义
需求:要在页面中显示一个对话框,封装成一个组件
问题:组件的内容部分,不希望写死,希望能使用的时候自定义。
插槽使用方法
- 定义子组件,子组件中使用
<slot></slot>占位(封装的代码如下 MyDialog ),如果有默认内容,没传入时使用默认内容,可使用<slot>默认内容</slot>占位
<template>
<div class="dialog-content">
<!-- 占位符 调用中的内容插入此处 -->
<slot></slot>
</div>
</template>
- 使用组件时,
<MyDialog></MyDialog>标签内部,传入结构替换<slot></slot>所在位置
<MyDialog>
要插入的内容
</MyDialog>
插槽 - 具名插槽
上面自定义的组件中只是传入一个值,当一个组件有多个地方需要传入值,需要使用具名插槽
- 多个slot使用name属性区分名字
<template>
<div>
<!-- 占位符 调用中的内容插入此处 -->
<slot name="head"></slot>
</div>
<div>
<!-- 占位符 调用中的内容插入此处 -->
<slot name="content"></slot>
</div>
</template>
- template配合v-slot:名字来分发对应标签 v-slot: 可简写为#
<MyDialog>
<template v-slot:head>标题</template>
<!-- v-slot: 可简写为 #head -->
<template #content>内容</template>
</MyDialog>
插槽 - 作用域插槽
作用域插槽:定义slot插槽的同时也可以传值的。给插槽上可以绑定数据,将来使用组件时可以用
使用步骤如下:
- 子组件中(如 MyTable),给 slot 标签占位符,以添加属性的方式传值
<slot :id="item.id" msg="test"></slot> <!-- 此处传入父组件的button 传参id、msg到父组件 -->
- 所有添加的属性,都会被收集到一个对象中
{id: 3, msg: "test"}
- 在父组件的 template 中,通过
#插槽名="obj”接收,默认插槽名为default
<MyTable :list="list">
<template #default="obj"> <!-- obj接收子组件传参的id、msg -->
<button @click="del(obj.id)">删除</button>
</template>
</MyTable>
三、组件通信
组件通信:组件与组件之间的数据传递。组件的数据是独立的,无法直接访问其他组件的数据。想用其他组件的数据,可以通过组件通信方式
3.1 父子通信
父传子(props):将值传给子组件,父组件中的
:MyTitle="MyTitle"是将值传给子组件,子组件中,通过props接收(:MyTitle名字要和props中的一致)
子传父(emit):从子组件将数据传给父组件,子组件通过emit传给父组件,父组件通过@returnData="handleReturn"接收处理(@returnData要和emit中的保持一致)
父组件代码
<template>
<div id="app">
<MyHeader :MyTitle="MyTitle" @returnData="handleReturn"></MyHeader>
</div>
</template>
<script>
import MyHeader from './components/MyHeader.vue'
export default {
components: {
MyHeader: MyHeader
},
data () {
return {
MyTitle: "标题"
}
},
methods: {
// 处理子元素返回来的值
handleReturn (message) {
console.log(message)
}
}
}
</script>
<style scoped></style>
子组件代码
<template>
<button @click="handleClick"> {{ MyTitle }}</button>
</template>
<script>
export default {
// 接收父组件传来的值
props: ['MyTitle'], // MyTitle 与父组件名字 一致
methods: {
handleClick () {
// returnData 与父组件名字 一致 hello 为传出的数据
this.$emit('returnData', 'hello')
}
}
}
</script>
<style></style>
传入校验
父传子可以在子组件中对其进行校验,比如类型、是否必传、默认值、自定义校验等
<script>
export default {
// 接收父组件传来的值
props: {
// usename 值
usename: String, // 声明传入 字符串类型
// phoneNum 值
phoneNum: { // 更多校验功能
type: Number, // 数字类型
required: true, // 是否必传
default: "", // 默认值
validator (value) { // 自定义校验逻辑
return true
}
}
},
methods: {
handleClick () {
// returnData 与父组件名字 一致
// hello 为传出的数据
this.$emit('returnData', 'hello')
}
}
}
</script>
非父子通信 - event bus 事件总线
非父子组件之间,进行简易消息传递。(复杂场景用 Vuex)
- 在utils/EventBus.js 创建一个都能访问到的事件总线(空Vue实例)
import Vue from'vue'
const Bus = new Vue()
export default Bus
- A组件(接收方),监听Bus实例的事件
created () {
Bus.$on('sendMsg', (msg) => {
this.msg = msg
)}
}
- B组件(发送方),触发Bus实例的事件(一对多,添加了监听的都能收到)
Bus.$emit('sendMsg','这是一个消息')
非父子通信 - provide & inject
provide & inject作用:跨层级共享数据(爷孙层级)
- 父组件 provide提供数据
<script>
export default {
provide () {
return {
// 普通类型【非响应式】
color: this.color,
//复杂类型【响应式】(字典、列表) 推荐
userInfo: this.userInfo,
}
},
}
</script>
- 子/孙组件inject取值使用
<script>
export default {
inject: ['color', 'userInfo']
}
</script>
3.2 单向数据流(prop&data)
共同点:都可以给组件提供数据
区别:data的数据是自己的,可随便改;prop数据是外部的,不能直接改,要遵循 单向数据流
<script>
export default {
data () {
return {
MyTitle: "标题" // 自己数据,可随便改
}
},
props: {
usename: String // 外部数据,不能直接改
}
}
</script>
3.3 v-model sync 简化组件代码
原理:V-model本质上是一个语法糖。例如应用在输入框上,就是
value属性和input事件的合写。
作用:提供数据的双向绑定(数据变,视图跟着变:value,视图变,数据跟着变@input)
注:$event 用于在模板中,获取事件的形参
<template>
<input :value="msg" @input="msg = $event.target.value" type="text">
<!-- v-model 等于 :value 和 @input -->
<input v-model="msg" type="text">
</template>
表单类组件封装&v-model简化代码
表单类组件封装
父传子:数据应该是父组件props传递过来的,V-model拆解绑定数据
子传父:监听输入,子传父传值给父组件修改
<template>
<div id="app">
<MyHeader :cityId="selectId" @handleChange="selectId = $event"></MyHeader>
</div>
</template>
<script>
import MyHeader from './components/MyHeader.vue'
export default {
components: { MyHeader: MyHeader },
data () {
return {
selectId: "101"
}
}
}
</script>
<style></style>
<template>
<div>
<select :value="cityId" @input="handleChange">
<option value="101">北京</option>
<option value="102">上海</option>
</select>
</div>
</template>
<script>
export default {
// 接收父组件传来的值
props: {
cityId: String
},
methods: {
handleChange (e) {
this.$emit('handleChange', e.target.value)
}
}
}
</script>
<style></style>
v-model简化代码
父组件v-model简化上面代码,实现子组件和父组件数据双向绑定
- 子组件中:props通过value接收(一定为value),事件触发input(一定为input)
- 父组件中:v-model给组件直接绑数据
<template>
<div id="app">
<MyHeader v-model="selectId"></MyHeader>
</div>
</template>
<script>
// 导入需要注册的组件
import MyHeader from './components/MyHeader.vue'
export default {
components: {
MyHeader: MyHeader // 局部注册
},
data () {
return {
selectId: "101"
}
}
}
</script>
<style></style>
<template>
<div>
<select :value="value" @input="handleChange">
<option value="101">北京</option>
<option value="102">上海</option>
</select>
</div>
</template>
<script>
export default {
// 接收父组件传来的值
props: {
value: String // 一定为value
},
methods: {
handleChange (e) {
this.$emit('input', e.target.value) // 一定为input
}
}
}
</script>
<style></style>
.sync 修饰符
作用:可以实现子组件与父组件数据的双向绑定,简化代码
特点:prop属性名 可以自定义,非固定为value
<template>
<div id="app">
<MyHeader :visible="isShow" @update:visibl="isShow = $event"></MyHeader>
<!-- 简化为如下代码 -->
<MyHeader :visible.sync="isShow"></MyHeader>
</div>
</template>
<script>
export default {
props: {
visible: Boolean
},
methods: {
handleChange (e) {
this.$emit('update:visible', false)
}
}
}
</script>
3.4 ref与refs(获取dom元素)
作用:利用
ref和$refs可以用于获取dom元素,或组件实例
特点:查找范围 当前组件内(更精确稳定)
获取DOM:目标标签添加
ref属性
<template>
<div ref="myChart"></div>
</template>
<script>
export default {
mounted() {
const my_chart = echarts.init(this.$refs.myChart)
// 图表配置项 略
const option = {}
my_chart.setoption(option)
}
}
</script>
<style></style>
可以通过ref在父组件中调用子组件中的方法(组件实例)
<template>
<div id="app">
<MyHeader ref="baseForm"></MyHeader>
<button @click="handleClick">获取数据</button>
</div>
</template>
<script>
import MyHeader from './components/MyHeader.vue'
export default {
components: {
MyHeader: MyHeader
},
methods: {
handleClick () {
console.log(this.$refs.baseForm.getValues())
}
}
}
</script>
<style></style>
<template>
<input v-model="username" type="text">
</template>
<script>
export default {
data () {
return {
username: ''
}
},
methods: {
getValues () {
return this.username
}
}
}
</script>
<style></style>
四、路由(VueRouter)
Vue中路由:路径和组件的映射关系
VueRouter介绍
作用:修改地址栏路径时,切换显示匹配的组件
说明:Vue官方的一个路由插件,是一个第三方包
官网:https://v3.router.vuejs.org/zh/
4.1 路由基本配置与使用
VueRouter使用基本步骤:
- 下载VueRouter模块到当前工程(vue2 3.x,vue3 4.x):
yarn add vue-router@3.6.5- 引入:
import VueRouter from 'vue-router'- 安装注册:
Vue.use(VueRouter)- 创建路由对象:
const router = new VueRouter()- 注入、将路由对象注入到newVue实例中,建立关联
new Vue({ render:h => h(App), router: router }).$mount("@app")
实例:在代码的main.js中添加如下内容
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// vue2的router3配置方式
const router = new VueRouter({
mode: 'history',
routes: []
})
// vue3的router4配置方式
const router = createRouter({
// history模式使用createWebHistory hash模式使用createWebHashHistory
history: createWebHistory('/'),
routes: []
})
new Vue({
render: h => h(App),
router: router
}).$mount('#app')
使用的两个核心步骤
- 创建需要的组件(views目录),配置路由规则(下面代码为main.js中)
import A from "./views/A.vue"
import B from "./views/B.vue"
import C from "./views/C.vue"
const router = new VueRouter({
routes: [
{path: '/a', component: A},
{path: '/b', component: B},
{path: '/c', component: C}
]
})
- 配置导航,配置路由出口(路径匹配的组件显示的位置)
<a href="#/a">链接</a>
<div class="footer_wrap>
<a href="#/a">a</a>
<a href="#/b">b</a>
<a href="#/c">c</a>
</div>
<div class="top">
<!-- 路由出口 > 匹配的组件所展示的位置 上面点击,下面切换 -->
<router-view></router-view>
</div>
router-link自带类名
a标签可以使用router-link取代,router-link默认会提供高亮类名,不需要像 a 标签一样自己配置点击项的类名增删操作,只需统一配好对于类即可
<div class="footer_wrap>
<a href="#/a">a</a>
<a href="#/b">b</a>
<a href="#/c">c</a>
</div>
<!-- 效果同上 -->
<div class="footer_wrap>
<router-link to="/a">a</router-link>
<router-link to="/b">b</router-link>
<router-link to="/c">c</router-link>
</div>
a.router-link-active {
xxx
}
类名:
router-link-exact-active,router-link-active
touter-link-active 模糊匹配,to="/my"可匹配/my/a(常用)
router-link-exact-active 精确匹配
router-link定制类名
const router = newVueRouter({
routes: [...],
linkActiveclass:"类名1",
linkExactActiveClass: "类名2"
)}
<div class="footer_wrap>
<router-link to="/a" class="类名2 类名1">a</router-link>
<router-link to="/b">b</router-link>
<router-link to="/c">c</router-link>
</div>
4.2 路由的封装抽离
上面的代码中,路由配置都是写在main.js中,现在将其单独写到一个文件 (src/router/index.js),然后在main.js中只需导入使用即可
src/router/index.js
// @ 表示src目录
import A from "@/views/A.vue"
import B from "@/views/B.vue"
import C from "@/views/C.vue"
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{path: '/a', component: A},
{path: '/b', component: B},
{path: '/c', component: C}
]
})
export default router
main.js
import router from './router/index.js'
new Vue({
render: h => h(App),
router: router
}).$mount('#app')
4.3 路由传参与重定向
声明式导航 - 跳转传参
在跳转路由时,进行传值,有两种方式传参:
查询参数传参、动态路由传参
查询参数传参(适合多个参数)
- 跳转:
to="/path?参数名1=值1&参数名2=值2"- 对应页面组件接收传递过来的值:
$route.query.参数名
<div class="footer_wrap>
<router-link to="/a?key=hello" class="类名2 类名1">a</router-link>
</div>
<!-- 组件代码 -->
<div>
{{ $route.query.key }}
</div>
<sctipt>
export default {
name: '',
created() {
connsole.log(this.query.key)
}
}
</script>
动态路由传参(适合单个参数)
- 配置动态路由
const router = newVueRouter({
routes: [
...,
{
path: "/search/:参数名",
component: Search
}
]
)}
- 配置导航链接:
to="/path/参数值"- 对应页面组件接收传递过来的值:
$route.params.参数名
动态路由参数可选符
问题:配了路由path:"/search/:words”为什么按下面步骤操作,会未匹配到组件,显示空白
原因:/search/:Words表示必须要传参数。如果不传参数,也希望匹配,可以加个"?"表示可选参数("/search/:参数名?"即可不传)
Vue路由 - 重定向
问题:url默认是/路径,未匹配到组件时,会出现空白网页打开
说明:重定向匹配path后,强制跳转path路径
语法:{path:匹配路径,redirect:重定向到的路径},
const router = newVueRouter({
routes: [
{ path: "/", redirect: "/home"},
{ path: "/home", component: home }
]
)}
Vue路由 - 404
问题:当路径找不到匹配时,给个提示页面
说明:配在路由最后
语法:path:"*"(任意路径)-前面不匹配就命中最后这个
const router = newVueRouter({
routes: [
{ path: "/", redirect: "/home"},
{ path: "/home", component: home }
{ path: "*", component: NotFind }
]
)}
Vue路由 - 模式设置
问题:路由的路径看起来不自然,有#,能否切成真正路径形式?
hash路由(默认) 例:http://localhost:8080/#/home
history路由(常用) 例如:http://localhost:8080/home
const router = newVueRouter({
routes,
mode: "history" // hash
)}
编程式导航 - 基本跳转
问题:点击按钮跳转如何实现?
编程式导航:用S代码来进行跳转
两种语法:
- path路径跳转(简易方便)
// 方式一
// 传参方式1:'路由路径?参数1=值1&参数2=值2'
// 传参方式2:'路由路径/参数值'
this.$router.push('路由路径') //
// 方式二
// 传参方式1:{ path:'路由路径', query: {参数1: 值1, 参数2: 值2 }}
// 传参方式2:{ path:'路由路径/参数值' }
this.$router.push({ path:'路由路径' }) //
- name命名路由跳转(适合path路径长的场景)
const router = newVueRouter({
routes: [
{ name: "路由名", path: "/path", component: path}
]
)}
// 传参方式:{ path:'路由路径', query: {参数1: 值1, 参数2: 值2 }}
// 传参方式:{ path:'路由路径', params: {参数1: 值1 }}
this.$router.push({ name: '路由名' path: '路由名' })
嵌套路由配置
const router = newVueRouter({
routes: [
{
path: "/",
component: index,
// 通过 children配置项配规则,可以配置嵌套子路由
children: { path: "/childindex", component: childindex }
},
{ path: "/home", component: home }
{ path: "*", component: NotFind }
]
)}
<div>
<!-- 准备二级路由出口 -->
<router-view></router-view>
</div>
<div class="footer_wrap>
<router-link to="/a">a</router-link>
<router-link to="/b">b</router-link>
<router-link to="/c">c</router-link>
</div>
路由返回上一层
<span @click="$router.back()">
</span>
组件缓存 keep-alive
keep-alive:keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。keep-alive是一个抽象组件,它自身不会渲染成一个DOM元素,也不会出现在父组件链中。
keep-alive的优点:在组件切换过程中把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。
<template>
<div class="h5-wrapper">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
keep-alive的三个属性
include:组件名数组,只有匹配的组件会被缓存
exclude:组件名数组,任何匹配的组件都不会被缓存
max:最多可以缓存多少组件实例.
<template>
<div class="h5-wrapper" :include="['xxxPage']">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
组件缓存 keep-alive 提供了两个声明周期函数 actived(进入时) 和 deactived(退出时)
自定义创建项目
基于VueCli自定义创建项目架子,使用该方式就会自动配置好路由等配置
安装脚手架 -> 创建项目 -> 自定义(Babel/Router/CSS/Linter)
创建项目:vue create ProjectName,创建时选择Manually select features,选择Babel/Router/CSS/Linter,css选择器选择Less,规范选择ESLint + Standard config,Lint on save,Indedicated config files
五、vuex(多组件共享一组数据)
概念:Vuex是一个vue的状态管理工具,状态就是数据。简单讲vuex是一个插件,可以帮我们管理vue通用的数据(多组件共享的数据)
应用场景:某个状态在很多个组件来使用(个人信息);多个组件共同维护一份数据(购物车)
优势:共同维护一份数据,数据集中化管理;响应式变化;操作简洁(Vuex提供了一些辅助函数)
构建vuex多组件数据共享环境
基于脚手架创建项目,构建vuex多组件数据共享环境
效果是多个组件,共享一份数据:任意一个组件都可以修改数据;多个组件的数据是同步的
- 装包:
yarn add vuex@3- 新建
store/index.js专门存放 vuex- 创建仓库
new Vuex.Store()
// 存在vuex相关核心代码
import Vue from 'vue'
import Vuex from 'vuex'
// 插件安装
Vue.use(Vuex)
// 创建仓库(空仓库)
const store = new Vuex.Store()
// 导出给 main.js 使用
export default store
- 在 main.js 中导入挂载到Vues实例上
import Vue from 'vue'
import App from './App.vue'
import store from '@/store/index'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
组件中访问
<script>
export default {
name: 'app',
created () {
console.log(this.$store)
}
}
</script>
核心概念 - state 状态(数据)
目标:明确如何给仓库提供数据,如何使用仓库的数据
- 提供数据:State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。在state对象中可以添加我们要共享的数据。(store/index.js)
// 创建仓库(空仓库)
const store = new Vuex.Store({
// state 类似 vue 的 data
// data 是自己的数据,state 是所有组件共享的数据
state: {
count: 1
}
})
- 使用数据,通过store直接访问或通过辅助函数
<!-- 直接访问 -->
<div>{{ $store.state.count}}</div>
<script>
export default {
name: 'app',
created () {
// 组件中
console.log(this.$store.state.count)
// js中
console.log(store.state.count)
}
}
</script>
<!-- 通过辅助函数访问 -->
<div>{{ count }}</div>
<script>
import { mapState } from 'vuex'
console.log(mapState(['count', 'title']))
export default {
name: 'app',
computed () {
...mapState(['count', 'title'])
}
}
</script>
核心概念- mutations
目标:明确vuex同样遵循单向数据流,组件中不能直接修改仓库的数据,可以通过 strict:true 可以开启严格模式
- 定义mutations对象,对象中存放修改state的方法
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount (state) { // 带参 (state, n)
state.count += 1
}
}
})
- 组件中提交调用 mutations:
this.$store.commit('addCount')(带参:this.$store.commit('addCount', 10))
辅助函数:mapMutations
目标:掌握辅助函数mapMutations,映射方法
mapMutations和mapState很像,它是把位于mutations中的方法提取了出来,映射到组件methods中
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount (state) { // 带参 (state, n)
state.count += 1
}
}
})
<div>{{ count }}</div>
<script>
import { mapMutations } from 'vuex'
export default {
name: 'app',
computed () {
...mapMutations(['addCount'])
}
}
</script>
调用:
this.addCount()
核心概念actions
目标:明确actions的基本语法,处理异步操作。
需求:一秒钟之后,修改state的count成666。
说明:mutations必须是同步的(便于监测数据变化,记录调试)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount (state, n) { // 带参 (state, n)
state.count += n
}
},
actions: {
setAsyncCount (context, num) {
setTimeout(() => {
context.commit('addCount', num)
}, 1000)
}
}
})
调用:
this.$store.dispatch('setAsycnCount', 200)
辅助函数- mapActions
目标:掌握辅助函数mapActions,映射方法
mapActions是把位于actions中的方法提取了出来,映射到组件methods中
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount (state, n) { // 带参 (state, n)
state.count += n
}
},
actions: {
setAsyncCount (context, num) {
setTimeout(() => {
context.commit('addCount', num)
}, 1000)
}
}
})
<div>{{ count }}</div>
<script>
import { mapActions } from 'vuex'
export default {
name: 'app',
computed () {
...mapActions(['setAsyncCount'])
}
}
</script>
调用:
this.setAsyncCount()
核心概念-getters
目标:掌握核心概念getters的基本语法(类似于计算属性)
说明:除了state之外,有时我们还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters
例如:state中定义了list,为1-10的数组,组件中,需要显示所有大于5的数据
const store = new Vuex.Store({
state: {
list: [3, 4 ,5 ,6 , 7]
},
getters: {
filterList (state) {
return state.list.filter(item => item >5)
}
}
})
调用:
this.$store.getters.filterList
通过mapGetters函数映射
核心概念-模块module(进阶语法)
目标:掌握核心概念module模块的创建
由于vuex使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。(当项目变得越来越大的时候,Vuex会变得越来越难以维护)
模块拆分
在store下创建模块文件,如:store/modules/user.js
const state = {
uesrInfo: {
name: 'Tony',
age: 10
}
}
const mutations = {}
const actions = {}
const getters = {}
export default {
mutations,
actions,
getters
}
主模块导入
import user from './modules/user'
// 创建仓库(空仓库)
const store = new Vuex.Store({
modules:{
user
}
})
目标:掌握模块中state的访问语法
尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的state中,属性名就是模块名
使用模块中的数据:
- 直接通过模块名访问
$store.state.模块名.xxx- 通过 mapState 映射:默认根级别的映射
mapState(['xxx']),子模块的映射mapState('模块名',['xxx'])- 需要开启命名空间(namespaced: true)
export default {
namespaced: true // 开启命名空间
mutations,
actions,
getters
}
目标:掌握模块中getters的访问语法使用模块中 getters中的数据:
- 直接通过模块名访问
$store.getters['模块名/xxx']- 通过 mapGetters 映射:默认根级别的映射
mapGetters(['xxx']),子模块的映射mapGetters('模块名',['xxx'])-需要开启命名空间
目标:掌握模块中mutation的调用语法
注意:默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块。
调用子模块中 mutation:
- 直接通过store调用
$store.commit('模块名/xxx',额外参数)- 通过 mapMutations 映射:默认根级别的映射
mapMutations(['xxx'])子模块的映射mapMutations('模块名',['xxx])-需要开启命名空间
目标:掌握模块中action的调用语法(同理- 直接类比mutation即可)
注意:默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块。调用子模块中action:
- 直接通过 store调用
$store.dispatch('模块名/xxx',额外参数)- 通过mapActions 映射
默认根级别的映射mapActions(['xxx'])子模块的映射mapActions('模块名',['xxx'])-需要开启命名空间
六、vue3
create-vue是Vue官方新的脚手架工具,底层切换到了vite(下一代构建工具),为开发提供极速响应
6.1 Vue3的基本使用
使用create-vue创建项目
条件:已安装16.0或更高版本的Node.js
创建一个Vue应用:npm init vue@latest(这一指令将会安装并执行create-vue)
项目目录和关键文件
vite.config.js- 项目的配置文件基于vite的配置- package.json - 项目包文件核心依赖项变成了Vue3.x和vite
- main.js - 入口文件createApp函数创建应用实例
- app.vue - 根组件 SFC单文件组件 script - template - style
变化一:脚本script和模板template顺序调整
变化二:模板template不再要求唯一根元素
变化三:脚本script添加setup标识支持组合式API- index.html - 单页入口提供id为app的挂载点
<template>
<div>{{ message }}</div>
</template>
<!-- 加上setup允许在script中直接编写组合式API -->
<script setup></script>
<style scoped></style>
组合式API - setup选项
原始写法
<template>
<div>{{ message }}</div>
</template>
<script setup>
export default {
// setup 执行的比 beforeCreate 早
// 数据 和 函数,需要在setup最后 return 才能模板中应用
setup () {
const message = 'hello'
const logMessage = () => {
console.log(message)
}
return {
message,
logMessage
}
},
beforeCreate () {
}
}
</script>
语法糖写法
<template>
<div>{{ message }}</div>
</template>
<script setup>
const message = 'hello'
const logMessage = () => {
console.log(message)
}
</script>
组合式API - computed
计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法
使用步骤:导入computed函数,执行函数 在回调参数中return基于响应式数据做计算的值,用变量接收
<template>
<div class="dialog">
{{ state }}
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const list = ref([3, 4, 5, 6, 7])
list.value.push(8)
// 过滤出大于5的值
const computedState = computed(() => {
return list.value.filter(item => item > 5)
})
console.log(computedState)
</script>
<style scoped></style>
组合式API - watch
作用:侦听一个或者多个数据的变化,数据变化时执行回调函数
俩个额外参数:1.immediate(立即执行)2.deep(深度侦听)
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const name = ref('hello')
// 监听单个数据
watch(count, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 监听多个数据
watch(
[count, name],
([newCount, newName], [oldCount, oldName]) => {
console.log("数据发生变化")
})
</script>
immediate:在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调
deep:默认watch 进行的是 浅层监视深度监视,ref(简单类型) 可以直接监视,ref(复杂类型)监视不到复杂类型内部数据的变化,需要deep
// 监听单个数据
watch(count, (newValue, oldValue) => {
console.log(newValue, oldValue)
}, {
immediate: true,
deep: true
})
组合式API-生命周期函数
| 选项式API | 组合式API |
|---|---|
| beforeCreate/created | setup |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
6.2 组合式API - reactive和ref函数
默认的数据不是响应式的,这两种数据是响应式的
reactive()
接受对象类型数据的参数传入并返回一个响应式的对象
<template>
<div class="dialog">
{{ state.count }}
</div>
</template>
<script setup>
import { reactive } from 'vue'
// reactive:接收一个对象类型的数据,返回一个响应式的对象
const state = reactive({ count: 1 })
console.log(state.count)
</script>
<style scoped></style>
ref()
接收简单类型或者对象类型的数据传入并返回一个响应式的对象
<template>
<div class="dialog">
{{ state }}
</div>
</template>
<script setup>
import { ref } from 'vue'
// reactive:接收一个对象类型的数据,返回一个响应式的对象
const state = ref(0)
// 调用需要使用value
console.log(state.value)
</script>
<style scoped></style>
6.3 组合式API-父子通信
父传子基本思想
- 父组件中给子组件绑定属性
<xxx :message='xxx' />- 子组件内部通过props选项接收
<xxx :message='xxx' />
<template>
<div class="dialog">
{{ message }}
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
// 接收父组件传入的值
const props = defineProps({
message: String
})
</script>
<style scoped></style>
子传父基本思想
- 父组件中给子组件标签通过@绑定事件
<xxx @message='xxxfunc' />- 子组件内部通过emit方法触发事件
<template>
<div class="dialog">
{{ message }}
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const emit = defineEmits(['message'])
const sendMsg = () => {
emit('message', 'data')
}
</script>
<style scoped></style>
组合式API-模版引用
通过ref标识获取真实的dom对象或者组件实例对象
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象到标签
defineExpose()
默认情况下在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法允许访问
<template>
<xxx ref='testRef' />
</template>
<script setup>
import { ref, onMounted } from 'vue'
const testRef = ref(null)
onMounted(() => {
// 调用某子函数 (下面子组件的sendMsg函数)
inpref.value.sendMsg()
})
</script>
<style scoped></style>
<script setup>
import { ref, onMounted } from 'vue'
const message = ref('')
consr sendMsg = () => {
console.log('hello')
}
defineExpose({
message,
sendMsg
})
</script>
组合式API -provide和inject
顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信
跨层传递普通数据(可以传递数据、函数等,在子层级中调用父层级的函数)
- 顶层组件通过provide函数提供数据
provide('key',顶层组件中的数据)- 底层组件通过inject函数获取数据
const message = inject('key')
<script setup>
import { provide } from 'vue'
provide('message','hello')
</script>
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
6.4 Vue3.3 新特性
Vue3.3新特性-defineOptions
背景说明:
有<script setup>之前,如果要定义props,emits可以轻而易举地添加一个与setup平级的属性。
但是用了<script setup>后,就没法这么干了setup属性已经没有了,自然无法添加与其平级的属性。
了解决这一问题,引入了defineProps 与 defineEmits 这两个宏,但这只解决了props与emits这两个属性。
如果我们要定义组件的name或其他自定义的属性,还是得回到最原始的用法,再添加一个普通的<Script>标签。
这样就会存在两个<script>标签。让人无法接受。
所以在Vue 3.3中新引入了 defineOptions宏。顾名思义,主要是用来定义Options API的选项。可以用
defineOptions 定义任意的选项,props,emits,expose,slots 除外(因为这些可以使用definexXX来做到)
<script setup>
defineOptions({
name: 'xxx',
inheritAttrs: false,
// ...
})
</script>
Vue3.3新特性-defineModel
在Vue3中,自定义组件上使用y-model,相当于传递一个modelValue属性,同时触发 update:modelValue 事件
<Child v-model="isVisible">
//相当于
<Child :modelValue="isVisible" @update:modelValue="isVisible=$event">
我们需要先定义props,再定义emits。其中有许多重复的代码。如果需要修改此值,还需要手动调用emit函数
<script setup>
import { defineModel } from 'vue'
const modelValue = defineModel()
modelValue.value++
</script>
还需要在vite.config.js中配置
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true
}
}),
],
xxx
})
七、项目发布
打包指令:yarn build
在项目根目录自动创建一个文件夹 dist ,里面的为打包后的文件,需要放到服务器中
默认情况,需要放到服务器根目录打开,如果希望双击运行,需要配置 vue.config.js 相对路径publicPath: './'
八、相关组件库
| 组件库 | 介绍 |
|---|---|
| json-server | 模拟后端的数据库(文档链接) |
| Pinia | vuex的替代品(文档链接) |

1986

被折叠的 条评论
为什么被折叠?



