2 3 3
vue2 vuerouter3 vuex
3 4 4
vue3 vuerouter4 vuex4
1. 思路
做任何需求的套路:
- 先铺设标签和样式
- 铺设数据(固定数据/js动态创建数据/后台返回数据)
- 写交互,多思考用户在页面上的动作,实现对应功能的代码
- 如果需要把用户输入的值/动作的结果,返回给后台,则调用后台接口即可
核心步骤
- 准备容器
- 引包
- 创建vue实例new Vue()
- 指定配置项------>渲染数据
- el 指定挂载点(通过el冒号配置选择器,指定Vue管理的是哪个盒子)
- data 提供数据(提供变量)
el后面跟上一个选择器
vue.js网址(引包)
可以下载👇
也可以访问在线链接👇
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
一旦引入vuejs核心包,在全局环境中,就有了vue构造函数
👇
有构造函数就可以创建实例了呀
js
<script>
const app=new Vue()
</script>
👇
指定配置项(el,data)
2. 插值表达式
3. Vue指令:v-
- v-html="",用来设置innerhtml
- v-show="" 和 v-if=""
- v-else 和 v-else-if=""
- v-on:事件名="一段可执行的代码"(v-on:可简写为@)
- v-on:事件名="methods中的函数名"(@)
this指向的也就是你当前的实例(data上的数据是挂在你const的实例上的)
- el,data,methods是并列的
- v-bind:属性名="表达式" 动态设置html的标签属性(src,url,title)(可省略v-bind,直接冒号...)
- 可操作
:class="{}"/"[]"
*- 传对象(导航栏点击高亮显示,适用于一个类名的来回切换)(键就是类名,值是布尔值,如果为true就说明有这个类,否则没有)
<li v-for="(item,index) in list" :key="item.id" @click="activeIndex=index"><a :class="{active:index===activeIndex}" href="#">{{item.name}}</a></li>
-
- 传数组
- 可操作style
:style="css属性名:属性值"
- 可操作
- v-for="(item,index) in 数组"(这个数组就是你data里的一组数据)
- 需要有key,:key="item.id"
- v-model='data里的变量',双向数据绑定(获取和设置)
- 应用于一些表单元素:
- 输入框input
- 文本域textarea
- 复选框 input:checkbox
- 单选框 input:radio
- 下拉菜单 select
删除列表对应项的思路(小黑书架)
不用改变flag布尔值的方式的原因是:你的数据是通过v-for渲染的,所以一删都删
思路
- 根据index下标删
- 根据id删(一般就用这个,id是唯一标识,更加稳定)
😙小黑记事本需求
- 列表渲染 v-for
- 删除功能v-on(@click)
- 添加功能v-on(@click)
- 底部统计 和 清空(插值表达式,@click)
4. 指令的修饰符
5. 计算属性,配置项computed
声明在computed中的,一个计算属性对应一个函数
methods与computed的区别
-
computed计算属性是基于它们的依赖依赖属性进行缓存的,只有在它的相关依赖发生改变时才会重新求值
-
而methods只要被触发就会重新渲染,调用执行
😙成绩表单需求
- 渲染功能(v-for,:key)
- 删除功能
- (@click.prevent="del(item.id)",prevent表示点击后不刷新页面,也就是阻止默认行为)
- 添加功能
- 统计总分,求平均分(计算属性,页面直接插值表达式调用就行)
- v-model绑定数据,再用methods方法绑定点击事件
js
this.list.unshift({
id:+new Date(),
subject:this.subject,
score:this.score
})
不过一般这种添加功能 的时候,方法里一般会有三部分
- 判断输入是否为正确的格式
- 添加
- 最后清除输入框内文字
6. watch监听器(与data等并列)
vue第二天
😙Vue水果购物车
全选反选
说明是双向的
- 当全部一个个勾选上时------要自动勾选全选
- 必须小选框都选中,全选按钮才选中,用
every
- 当勾全选时------上面也要全部反选上
- 全选框用
v-model="isAll"
- 因为你也需要在点击全选框修改值,所以这个计算方法要用完整写法(需要修改值,就必须用完整写法;因为默认简写的形式是不能修改值的)
- 要让所有小选框同步状态,所以用
for each
- 小选框------v-model="item.isChecked"
- 全选框------v-model="isAll"
统计总数与总价
跟之前那个成绩表单不同
- 成绩表单是只要加入进去的数据的成绩都是有用的,所以整体求和
- 但是这个购物车的统计是有选择性的,只有你勾选上它才参与统计
7. Vue生命周期和其生命周期的四个阶段和钩子函数
四个阶段:
- 创建
- 渲染(挂载)
- 更新
- 销毁
生命周期函数也就是钩子函数
😙小黑记账清单需求
- 基本渲染
- 添加功能
- 删除功能
- 饼图渲染
发送请求修改的是后端数据,所以必须再重新渲染一遍,这样前端页面也就可以看到变化了 饼图暂时没复习,等有需要了再来看.........
8. 工程化开发和脚手架vue cli
9. 父子关系
父 ------> 子
- 给父组件中的子组件添加自定义属性,并传值(子组件以添加属性的方式传值)
- 父组件 通过
props
------将数据传递给子组件
props是在子组件中的,子组件在props里去接收父组件的数据
- 子组件使用
- 就是现在想把父组件
'学前端,来黑马!'这个title
用在子组件中
子 ------> 父
- 先在子组件里写好按钮,注册好事件,并写好方法
- 子组件 在这个方法里利用
$emit
------通知父组件去修改更新 - 父组件需要接收这个消息,所以在父组件中的子组件上绑上监听
- 父组件中提供对应处理的逻辑函数
10. props校验
js
props: {
w: {
type: Number,
required: true,
default: 0,
validator(val) {
// console.log(val)
if (val >= 100 || val <= 0) {
console.error('传入的范围必须是0-100之间')
return false
} else {
return true
}
},
},
},
}
😙小黑记事本------组件通信
- 拆分组件(分成头,身,底)
- 渲染待办事件
- 添加任务
- 删除
- 合计和清空
- 持续化存储
但凡数据变化了,我们都需要往本地里去存储
- 所以我们用watch去深度监听myList的变化
- 监视后往本地里存
- 进入页面优先读取本地
js
//父组件中(是与methods并列的)
//尝试从 `localStorage` 中获取名为 `'myList'` 的项的值,并将其解析为一个JavaScript对象或数组,然后将这个值赋给 `myList` 属性。
//localStorage.getItem('list')得到的是一个json字符串
//我们还需要把它转成对象 JSON.parse
myList: JSON.parse(localStorage.getItem('list')) ||[
...
]
watch: {
myList: {
deep: true,//深度监视
//处理函数
handler(newVal) {
// 监视完------往本地里存
localStorage.setItem('list', JSON.stringify(newVal))
},
},
},
11. 非父子通信------eventbus事件总线(更复杂的话就用vuex,不复杂就可以用这个)
12. 非父子通信------provide和inject(跨层级共享数据)
13. v-model详解和简写(子父双向绑定)
- 数据变,视图跟着变
:value
- 视图变,数据跟着变
@input
相当于
为什么要把v-model拆开用??
- 假如你现在写一个下拉表单
- 因为你的数据是写在父组件里的
- 但是你的v-model是双向绑定,但是父传子又是单向数据流,所以不能用
14. .sync修饰符(子父双向绑定)
跟v-model的区别
15. ref和$refs 获取dom和组件
16. Vue异步dom更新------解决方法$nextTick
为了提升性能,Vue是异步更新Dom的
$nextTick
:等dom更新后,立即触发执行此方法里的函数体(虽然settimeout也可以实现同样的功能,但是等待的时间要自己写)
- 语法:
this.$nextTick(函数体)
js
this.$nextTick(()=>{
...
}
)
17. 自定义指令v-...------全局和局部
自己定义的指令,可以封装一些dom的操作
18. v-loading指令封装
发送请求需要时间
所以请求的数据回来之前,页面处于空白未渲染的状态
所以:
- loading------就是一个蒙层效果,盖在你的盒子上
- 数据请求中------添加蒙层
- 数据请求完毕------移除蒙层
19. 插槽------默认和具名
默认(一个定制位置)
-
首先你这个含有插槽的需要单独封装成一个组件
-
其次在父组件里按正常导入组件的方式导入这个含有插槽的组件(不过插槽里的文字要写在这个里面)
-
然后在子组件中需要插槽的地方写成
<slot></slot>
js
// 父组件中
<MyDialog>你确定要退出吗?(正式内容)</MyDialog>
// 子组件中
<slot>我是备用(默认)内容</slot>
// 如果上面父组件里的子组件标签中没有内容,就会使用这个备用内容
区别
- 默认插槽:只有一个定制位置
- 具名插槽:有多个定制位置,并且只要插槽起了名字,就是具名的
具名(多个定制位置)
- 子组件中有多个插槽的定制位置
<slot></slot>
- 所以为了区分不同位置,给slot加name属性
- 比如:
<slot name="head"></slot>
- 在父组件中用template上配合v-slot的写法来分发插槽
<template v-slot="head">大标题</template>
- 也可以简写成"井号+插槽名"的方式
<template #head>大标题</template>
插槽的传值(以添加属性的方式)------作用域插槽
😙商品列表知识点
Vue组件封装案例------商品列表 - 掘金 (juejin.cn)
@dblclick="方法"
------双击@blur="isEdit = false"
------失焦时隐藏
20. 路由
为什么要用路由?
因为"单页面应用程序",所有的功能都在一个页面上,特点是页面按需更新
什么是路由?
一种映射关系
路径和组件之间的
- 就比如单页面中,有不同的组件页面,所对应的路径也就不同
- 所以我们要确定的就是什么路径去匹配什么组件
VueRouter插件
- 作用:修改地址栏路径时,切换显示匹配的组件
- vue2所对应的vuerouter版本是3.6.5
VueRouter的使用(5+2)
- 固定的5个步骤在main.js里写
- 组件建在views目录下
- (页面组件要放在views里,小的复用组件就可以放在components里)
- 把创建的views目录下的组件在main.js里去配路由规则
- 路由规则------就是路径与组件之间的对应关系(path,component)
- 先配置导航,然后配置路由出口
- 导航:也就是你你切换组件的导航,
href="#/find"
- 路由出口:也就是你这个路径所匹配的组件的显示位置,
<router-view></router-view>
- 导航:也就是你你切换组件的导航,
路由模块封装
路由配置全部写在main.js里不易维护
所以我们将有关路由的文件,统一放在router文件夹下的index.js中
,并且在index.js里需要导出路由router
声明式导航------全局组件router-link(代替a标签实现高亮)
本质还是a标签,只不过多了一些属性
- 自带类名,可以直接在css里写样式,让标签高亮
js
//herf
<a href="#/find">发现音乐</a>
//to
<router-link to="/find">发现音乐</router-link>
- 类名分别是
模糊匹配router-link-active
和精确匹配router-link-exact-active
- 但是我们嫌上面这两个类名都太长了
- 所以可以这样写,来自定义👇
js
const router = new VueRouter({
routes:[...],
linkActiveClass:"类名1",
linkExactActiveClass:"类名2"
})
声明式导航------跳转传参
!!!!-
$route.query.参数名key
,这里接收参数用的是route
- 分为 查询参数传参 和 动态路由传参
在跳转路由时,进行传值
Vue路由重定向
直接在路由最前面
里加一句就行了
js
const router = new VueRouter({
routes:[
{path:'/',redirect:'/home},
{...}
],
})
404
直接在路由最后面
里加一句就行了
还要创建NotFound组件
*
表示任意路径
js
const router = new VueRouter({
routes:[
{...},
{path:"*",component:NotFound(这可以写任意的路径模块)}
],
})
路由模式设置
- hash路由(默认),带井号#
- history路由(常用),不带,但是需要后台配置访问规则
js
const router = new VueRouter({
mode:"history"
routes:[...],
})
21. 路由------编程式导航
基本跳转
!!!!这里接收参数用的是router,
this.$router.push('路由路径')
- path路径跳转
- name命名路由跳转
路由传参
- path路径传参
查询参数传参
js
this.$router.push('/路径?参数名1=参数值1 & 参数2=参数值2')
this.$router.push({
path:'/路径',
query:{
参数名1:'参数值1',
参数名2:'参数值2'
}
})
接收参数跟原来一样$route.query.参数名
动态路由传参
js
this.$router.push('/路径/参数值')
this.$router.push({
path:'/路径/值',
})
接收参数跟原来一样$route.params.参数名
- name路径传参
查询参数传参
js
//只有这一种写法
this.$router.push({
name:'路由名字',
query:{
参数名1:'参数值1',
参数名2:'参数值2'
}
})
接收参数跟原来一样$route.query.参数名
动态路由传参
js
this.$router.push({
name:'路由名字',
params:{
参数名:'参数值'
}
})
接收参数跟原来一样$route.params.参数名
嵌套路由
22. vuex(状态管理工具/管理通用数据的工具)
vuex 多组件共享数据 Vue学习第七天---Vuex - 掘金 (juejin.cn)
state、mutations、actions、getters
- 搭建环境,创建项目
- 创建一个空仓库(在store文件夹里新建index.js)
- 把新建的仓库挂载到main.js上
- 完成后,所有组件就都可以去访问它了
- state------获取数据
- 类似于vue组件中的data
- 使用数据方式------
按层次顺序打点
(从store开始) /辅助函数mapState
(自动映射到计算属性中)
- mutations------修改(同步)
- 不过vuex同样遵循单项数据流,组件中不能直接修改仓库的数据
- mutation函数的第一个参数必须是state,最多两个参数(如果需要多个,需要包装成一个对象)
- 使用------commit调用
this.$store.commit('')
/辅助函数mapMutationns
(自动映射到methods方法中) - 通过e形参,然后e.target.value可以拿到输入框的值,+e.target.value可以转数字
- actions------处理异步操作
- 使用------
dispatch调用
(在绑定的事件的methods里写) /辅助函数mapActions
(映射到methods方法中) - context上下文,相当于store;所以有store(有context就有commit)
- 使用------
- getters------计算属性的类似
- 第一个参数必须是state,必须有返回值(返回的就是getters的值)
- 使用------通过
打点访问
/辅助函数mapGetters
(映射到计算属性中)
vuex模块化
- 在store文件夹下新建modules文件夹,再写js文件
- 在modules文件夹里的子模块js,需要在store文件夹下的总仓库index.js中导入并注册
如何使用子模块中的state、mmutations、actions、getters
23. Vue3
vue2与vue3区别
这种称为是Vue2的选项式API
js
export default {
data(){
return {
};
methods:{
};
computed:{
};
watch:{
}
}
}
当代码逐渐增多,也就是比如methods和computed里的方法太多,从视觉上的对应关系就会变弱,不易维护
而Vue3属于组合式API
- 直接------声明数据,声明方法
- 组件导完就能用,不用注册
- template不再要求只能有一个根元素了
- 加上setup就允许在script中直接编写组合式API
组合式API
setup选项------组合式api的入口
- 这个比钩子beforeCreate还要早
- 也就是它在创建实例之前就执行了,所以setup函数中是获取不到this的,也就是undefined(vue3很少用this)
- setup需要写成一个函数,直接往配置项里面去写
- 将来里面可以编写组合式的API,可以往里面调各种函数
5. 特点------要想使用,必须return------但是如果加了setup就不需要了
reactive和ref函数(声明数据)
reactive({})
------接收一个对象类型的数据,返回一个响应式的对象
比如👇:
js
<script setup>
// reactive:接收一个对象类型的数据,返回一个响应式的对象
// 导包
import { reactive } from 'vue'
const state = reactive({
count: 100
})
const setCount = () => {
state.count++
}
</script>
<template>
<div>
<div>{{ state.count }}</div>
<button @click="setCount">+1</button>
</div>
</template>
ref()
------可接收 简单 类型 或者 对象类型的数据,传入并返回一个响应式的对象
-
跟reactive唯一不同的就是可以接收简单类型,其他都一样
-
但是,脚本中访问数据,需要通过 .value(不点的话,返回的是对象),而template页面中不需要加
computed
js
<script setup>
// 导入
import { computed } from 'vue'
// 执行函数 变量接受 在回调参数中return计算值
//执行函数 在回调参数中return基于响应式数据做计算的值(计算逻辑) ,用变量接收
const computedState = computed(() => {
return 基于响应式数据做计算之后的值
})
</script>
tip:👇
watch
- 侦听 一个或多个数据 的变化,数据 变化时执行回调 函数
- 分为------侦听单个数据 和 侦听多个数据
- 两个额外参数------immediate (一进页面立刻执行一次)和 deep (深度侦听),
- 默认watch进行的是 浅层监视
- (也就是可以直接监测到简单类型数据变化;但是监测不到复杂类型内部数据的变化)
- 也可以精确侦听对象里的某一个属性
单个👇
js
<script setup>
//1. 导入watch
import { ref, watch } from 'vue'
const count = ref(0)
// 2. 调用watch 侦听变化
// watch后面跟上一个ref对象(count),后面再去写一个回调
// 这里面写count而不是count.value(写.value相当于监视了0,0有什么好监视的)
watch(count, (newValue, oldValue) => {
console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`)
})
// 上面代码的意思是:一旦当前这个这个count变化了,就会执行后面的回调,后面的回调当中可以拿到新值和老值(即变化前和变化后的)
</script>
多个👇
js
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const name = ref('cp')
//侦听多个数据,写成数组形式在里面
// 里面任何一个值变化,都会触发回调
watch(
[count, name],
([newCount, newName], [oldCount, oldName]) => {
console.log('count或者name变化了',[newCount,newName],[oldCount,oldName])
}
)
</script>
额外参数👇
js
<script>
const count = ref(0)
watch(count, () => {
console.log('count发生了变化变化')
}, {
immediate: true // 一进页面立刻执行一次
deep:true
})
</script>
精确侦听某个属性👇
父子通信
父传子
- 相当于去掉了父组件的data传参
- 由于写了setup,所以无法直接配置props选项,所以用到了编译器宏
- 传死数据
- 传动态数据
子传父
模板引用
通过ref ------表示获取真实的dom对象 或者 组件实例对象
如何使用👇
然后通过ref对象.value即可访问到绑定的元素
tip:👇
- 在默认情况下
<script setup>
语法糖下 组件内部 的属性和方法是 不开放给父组件访问 的 - 可以通过
defineExpose()
编译宏 指定哪些属性和方法允许访问,把他们暴露出去!
provide和inject
顶层 组件向任意的 底层组件传递数据和方法 ,实现跨层组件通信(比如像爷传孙)
- 顶层组件通过 provide 函数提供 数据
js
provide('key',顶层组件中的数据 / ref对象,也就是响应式数据 / 方法)
- 底层组件通过 inject 函数 提供数据
js
const message = inject('key')
tip:渲染时候注意,顶层组件包中间,中间组件包底层
- 如果想要修改某个数据时,就要遵循"谁的数据谁维护"的原则
- 所以引出------跨层级传递函数=>给孙后代传递可以改数据的方法
js
// 在你想要改的数据的vue(也就是你最开始provide这个数据的地方)中提供provide修改的函数,共享方法
//跨层级传递函数=>给孙后代传递可以改数据的方法
// !!!相当于往子孙后代中共享了一个方法
provide('changeCount',(newCount) =>{
count.value = newCount
})
//在子孙后代中去接收这个函数
const changeCount = inject('changeCount')
const clickFn = () => {
// 调用
changeCount(1000)
}
24. Vue3.3 新特性
defineOptions宏
- 为了解决这一问题,引入了
defineProps
与defineEmits
这两个宏。但这只解决了props
与emits
这两个属性 - 如果我们要定义组件的
name
或其他自定义的属性,还是得回到最原始的用法 -- 再添加一个普通的<script>
标签。 - 视觉上不合理
- 所以
- 引入 defineOptions宏
- .顾名思义,主要是用来定义
Option API
的选项. 可以用defineOptions
定义任意的选项(props,emits,expose,slots除外,它们有专属的宏)
js
<script setup>
defineOptions({
name: 'Foo',//给组件命名
inheritAttrs: false,
// ...更多自定义属性
})
<script>
defineModel
在Vue3中,自定义组件 上使用v-model
,相当于传递一个modelValue属性
, 同时触发 updata:modelValue
事件
所以就要求我们,先定义 props, 再定义 emits. 其中有许多重复的代码. 如果需要修改此值, 还需要手动调用 emit 函数
- 太麻烦了,所以用defineModel来简化(只动了子组件里的代码)
js
<script setup>
import MyInput from '@/components/my-input.vue'
import { ref } from 'vue'
const txt = ref('123456')
</script>
<template>
<div>
<MyInput v-model="txt"></MyInput>
{{ txt }}
</div>
</template>
//------------------------------------------------------------------------------------------------------------------
//<script setup>
//defineProps({
// modelValue: String
//})
//const emit = //defineEmits(['updata:modelValue'])
//</script>
<script setup>
// 1. 不再需要props来接收了,而是直接model接收
//2.你需要修改这个数据也不再需要emit!!!,而是直接修改
import { defineModel } from 'vue'
const modelValue = defineModel()
</script>
<template>
<div>
<input
type="text"
:value="modelValue"
@input="e => modelValue = e.target.value"
>
//@input="e =>emit('update:modelValue,e.target.value')"
</div>
</template>
用defineModel的前提 是需要在vite.config.js中加配置
js
plugins: [
vue({
script:{
defineModel:true
}
}),
],
25.Pinia
Pinia 是 Vue 的最新状态管理工具, 是Vuex的 替代品(只有state,actions,getters)
-
提供了更加简单的API(去掉了mutation,也就是action既可以异步又可以直接修改数据了;;相当于mutations和actions合二为一)
-
pinia如何实现getter?------computed计算属性函数
-
提供符合组合式风格的API(和Vue3新语法统一)
-
弃掉了modules的概念, 每一个store都是一个独立的模块
-
配合 TypeScript 更加友好,提供可靠的类型推断
安装,基本使用,storeToRefs方法,持久化👇