likes
comments
collection
share

Vue2 / 3

作者站长头像
站长
· 阅读数 2

2 3 3

vue2 vuerouter3 vuex

3 4 4

vue3 vuerouter4 vuex4

1. 思路

做任何需求的套路:

  1. 先铺设标签和样式
  2. 铺设数据(固定数据/js动态创建数据/后台返回数据)
  3. 写交互,多思考用户在页面上的动作,实现对应功能的代码
  4. 如果需要把用户输入的值/动作的结果,返回给后台,则调用后台接口即可

核心步骤

  1. 准备容器
  2. 引包
  3. 创建vue实例new Vue()
  4. 指定配置项——>渲染数据
    • el 指定挂载点(通过el冒号配置选择器,指定Vue管理的是哪个盒子)
    • data 提供数据(提供变量)

el后面跟上一个选择器

Vue2 / 3

vue.js网址(引包)

v2.cn.vuejs.org

cn.vuejs.org

可以下载👇 Vue2 / 3


也可以访问在线链接👇

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

一旦引入vuejs核心包,在全局环境中,就有了vue构造函数

👇

有构造函数就可以创建实例了呀

    <script>
        const app=new Vue()
    </script>

👇

指定配置项(el,data)

2. 插值表达式

3. Vue指令:v-

  • v-html="",用来设置innerhtml

Vue2 / 3

  • v-show="" 和 v-if=""
  • v-else 和 v-else-if=""
  • v-on:事件名="一段可执行的代码"(v-on:可简写为@)
  • v-on:事件名="methods中的函数名"(@)

this指向的也就是你当前的实例(data上的数据是挂在你const的实例上的)

  • el,data,methods是并列的 Vue2 / 3
  • v-bind:属性名="表达式" 动态设置html的标签属性(src,url,title)(可省略v-bind,直接冒号...)
    • 可操作:class="{}"/"[]"
        1. 传对象(导航栏点击高亮显示,适用于一个类名的来回切换)(键就是类名,值是布尔值,如果为true就说明有这个类,否则没有)
      • <li v-for="(item,index) in list" :key="item.id" @click="activeIndex=index"><a :class="{active:index===activeIndex}" href="#">{{item.name}}</a></li>
        1. 传数组
    • 可操作style :style="css属性名:属性值"

Vue2 / 3

  • 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是唯一标识,更加稳定)
  • Vue2 / 3
  • Vue2 / 3

😙小黑记事本需求

  1. 列表渲染 v-for
  2. 删除功能v-on(@click)
  3. 添加功能v-on(@click)
  4. 底部统计 和 清空(插值表达式,@click)

4. 指令的修饰符

Vue2 / 3

5. 计算属性,配置项computed

声明在computed中的,一个计算属性对应一个函数

Vue2 / 3

methods与computed的区别

  • computed计算属性是基于它们的依赖依赖属性进行缓存的,只有在它的相关依赖发生改变时才会重新求值

  • 而methods只要被触发就会重新渲染,调用执行 Vue2 / 3

😙成绩表单需求

  1. 渲染功能(v-for,:key)
  2. 删除功能
    • (@click.prevent="del(item.id)",prevent表示点击后不刷新页面,也就是阻止默认行为)
  3. 添加功能
  4. 统计总分,求平均分(计算属性,页面直接插值表达式调用就行)
    • v-model绑定数据,再用methods方法绑定点击事件
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"

Vue2 / 3

统计总数与总价

跟之前那个成绩表单不同

  • 成绩表单是只要加入进去的数据的成绩都是有用的,所以整体求和
  • 但是这个购物车的统计是有选择性的,只有你勾选上它才参与统计 Vue2 / 3

7. Vue生命周期和其生命周期的四个阶段和钩子函数

四个阶段:

  1. 创建
  2. 渲染(挂载)
  3. 更新
  4. 销毁

生命周期函数也就是钩子函数 Vue2 / 3

😙小黑记账清单需求

  1. 基本渲染
  2. 添加功能
  3. 删除功能
  4. 饼图渲染

发送请求修改的是后端数据,所以必须再重新渲染一遍,这样前端页面也就可以看到变化了 Vue2 / 3 饼图暂时没复习,等有需要了再来看.........


8. 工程化开发和脚手架vue cli

9. 父子关系

父 ——> 子

  1. 给父组件中的子组件添加自定义属性,并传值(子组件以添加属性的方式传值)
  2. 父组件通过props——将数据传递给组件

props是在子组件中的,子组件在props里去接收父组件的数据

  1. 子组件使用
  • 就是现在想把父组件‘学前端,来黑马!’这个title用在子组件中 Vue2 / 3

子 ——> 父

  1. 先在子组件里写好按钮,注册好事件,并写好方法
  2. 子组件在这个方法里利用$emit——通知组件去修改更新
  3. 父组件需要接收这个消息,所以在父组件中的子组件上绑上监听
  4. 父组件中提供对应处理的逻辑函数

Vue2 / 3

10. props校验

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
        }
      },
    },
  },
}

😙小黑记事本——组件通信

  1. 拆分组件(分成头,身,底)
  2. 渲染待办事件
  3. 添加任务
  4. 删除
  5. 合计和清空
  6. 持续化存储

但凡数据变化了,我们都需要往本地里去存储

  • 所以我们用watch去深度监听myList的变化
  • 监视后往本地里存
  • 进入页面优先读取本地
//父组件中(是与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(跨层级共享数据)

Vue2 / 3

13. v-model详解和简写(子父双向绑定)

  • 数据变,视图跟着变:value
  • 视图变,数据跟着变@input

相当于 Vue2 / 3

为什么要把v-model拆开用??

  • 假如你现在写一个下拉表单
    • 因为你的数据是写在父组件里的
    • 但是你的v-model是双向绑定,但是父传子又是单向数据流,所以不能用

14. .sync修饰符(子父双向绑定)

跟v-model的区别

Vue2 / 3

15. ref和$refs 获取dom和组件

Vue2 / 3

Vue2 / 3

16. Vue异步dom更新——解决方法$nextTick


为了提升性能,Vue是异步更新Dom的

$nextTick:等dom更新后,立即触发执行此方法里的函数体(虽然settimeout也可以实现同样的功能,但是等待的时间要自己写)

  • 语法:this.$nextTick(函数体)
this.$nextTick(()=>{
...
}

17. 自定义指令v-...——全局和局部

自己定义的指令,可以封装一些dom的操作

18. v-loading指令封装

发送请求需要时间

所以请求的数据回来之前,页面处于空白未渲染的状态

所以:

  • loading——就是一个蒙层效果,盖在你的盒子上
  • 数据请求中——添加蒙层
  • 数据请求完毕——移除蒙层

19. 插槽——默认和具名

默认(一个定制位置)

  1. 首先你这个含有插槽的需要单独封装成一个组件

  2. 其次在父组件里按正常导入组件的方式导入这个含有插槽的组件(不过插槽里的文字要写在这个里面)

  3. 然后在子组件中需要插槽的地方写成<slot></slot>

// 父组件中
<MyDialog>你确定要退出吗?(正式内容)</MyDialog>

// 子组件中
<slot>我是备用(默认)内容</slot>
// 如果上面父组件里的子组件标签中没有内容,就会使用这个备用内容

区别

  • 默认插槽:只有一个定制位置
  • 具名插槽:有多个定制位置,并且只要插槽起了名字,就是具名的

具名(多个定制位置)

  1. 子组件中有多个插槽的定制位置<slot></slot>
    • 所以为了区分不同位置,给slot加name属性
    • 比如:<slot name="head"></slot>
  2. 在父组件中用template上配合v-slot的写法来分发插槽
    • <template v-slot="head">大标题</template>
    • 也可以简写成“井号+插槽名”的方式<template #head>大标题</template>

插槽的传值(以添加属性的方式)——作用域插槽

Vue2 / 3

😙商品列表知识点

  1. @dblclick="方法"——双击
  2. @blur="isEdit = false"——失焦时隐藏

20. 路由

为什么要用路由?

因为“单页面应用程序”,所有的功能都在一个页面上,特点是页面按需更新

什么是路由?

一种映射关系

路径和组件之间的

  • 就比如单页面中,有不同的组件页面,所对应的路径也就不同
  • 所以我们要确定的就是什么路径去匹配什么组件

VueRouter插件

  • 作用:修改地址栏路径时,切换显示匹配的组件
  • vue2所对应的vuerouter版本是3.6.5

VueRouter的使用(5+2)

  1. 固定的5个步骤在main.js里写
  2. 组件建在views目录下
    • (页面组件要放在views里,小的复用组件就可以放在components里)
  3. 把创建的views目录下的组件在main.js里去配路由规则
    • 路由规则——就是路径与组件之间的对应关系(path,component)
  4. 先配置导航,然后配置路由出口
    • 导航:也就是你你切换组件的导航,href="#/find"
    • 路由出口:也就是你这个路径所匹配的组件的显示位置,<router-view></router-view>

路由模块封装

路由配置全部写在main.js里不易维护

所以我们将有关路由的文件,统一放在router文件夹下的index.js中,并且在index.js里需要导出路由router

声明式导航——全局组件router-link(代替a标签实现高亮)

本质还是a标签,只不过多了一些属性

  • 自带类名,可以直接在css里写样式,让标签高亮
//herf
<a href="#/find">发现音乐</a>

//to
<router-link to="/find">发现音乐</router-link>
  • 类名分别是模糊匹配router-link-active精确匹配router-link-exact-active
  • 但是我们嫌上面这两个类名都太长了
  • 所以可以这样写,来自定义👇
const router = new VueRouter({
      routes:[...],
      linkActiveClass:"类名1",
      linkExactActiveClass:"类名2" 
      })

声明式导航——跳转传参

!!!!- $route.query.参数名key,这里接收参数用的是route

  • 分为 查询参数传参 和 动态路由传参

在跳转路由时,进行传值

Vue2 / 3

Vue路由重定向

直接在路由最前面里加一句就行了

const router = new VueRouter({
  routes:[
     {path:'/',redirect:'/home},
     {...}
  ],
})

404

直接在路由最后面里加一句就行了

还要创建NotFound组件

*表示任意路径

const router = new VueRouter({
  routes:[
     {...},
     {path:"*",component:NotFound(这可以写任意的路径模块)}
  ],
})

路由模式设置

  • hash路由(默认),带井号#
  • history路由(常用),不带,但是需要后台配置访问规则
const router = new VueRouter({
  mode:"history"
  routes:[...],
})

21. 路由——编程式导航

基本跳转

!!!!这里接收参数用的是router,this.$router.push('路由路径')

  1. path路径跳转
  2. name命名路由跳转

路由传参

  1. path路径传参

查询参数传参

this.$router.push('/路径?参数名1=参数值1 & 参数2=参数值2')

this.$router.push({
  path:'/路径'
  query:{
     参数名1'参数值1'
     参数名2'参数值2'
  }
})

接收参数跟原来一样$route.query.参数名

动态路由传参

this.$router.push('/路径/参数值')
this.$router.push({
   path:'/路径/值'
})

接收参数跟原来一样$route.params.参数名


  1. name路径传参

查询参数传参

//只有这一种写法
this.$router.push({
  name:'路由名字'
  query:{
     参数名1'参数值1'
     参数名2'参数值2'
  }
})

接收参数跟原来一样$route.query.参数名

动态路由传参

this.$router.push({
   name:'路由名字'
   params:{
      参数名:'参数值'
   }
})

接收参数跟原来一样$route.params.参数名

嵌套路由

22. vuex(状态管理工具/管理通用数据的工具)


state、mutations、actions、getters

  1. 搭建环境,创建项目
  2. 创建一个空仓库(在store文件夹里新建index.js)
  3. 把新建的仓库挂载到main.js上
  4. 完成后,所有组件就都可以去访问它了

  1. state——获取数据
    • 类似于vue组件中的data
    • 使用数据方式——按层次顺序打点(从store开始) / 辅助函数mapState (自动映射到计算属性中)
  2. mutations——修改(同步)
    • 不过vuex同样遵循单项数据流,组件中不能直接修改仓库的数据
    • mutation函数的第一个参数必须是state,最多两个参数(如果需要多个,需要包装成一个对象)
    • 使用——commit调用this.$store.commit('') / 辅助函数mapMutationns(自动映射到methods方法中)
    • 通过e形参,然后e.target.value可以拿到输入框的值,+e.target.value可以转数字
  3. actions——处理异步操作
    • 使用——dispatch调用(在绑定的事件的methods里写) / 辅助函数mapActions(映射到methods方法中)
    • context上下文,相当于store;所以有store(有context就有commit)
  4. getters——计算属性的类似
    • 第一个参数必须是state,必须有返回值(返回的就是getters的值)
    • 使用——通过打点访问 / 辅助函数mapGetters(映射到计算属性中)

vuex模块化

  1. 在store文件夹下新建modules文件夹,再写js文件
  2. 在modules文件夹里的子模块js,需要在store文件夹下的总仓库index.js中导入并注册

如何使用子模块中的state、mmutations、actions、getters

23. Vue3

Vue2 / 3

vue2与vue3区别

这种称为是Vue2的选项式API

export default {
     data(){
         return {
         
         };
      methods:{
         
         };
      computed:{
         
         };
       watch:{
         
         }
     }
}

当代码逐渐增多,也就是比如methods和computed里的方法太多,从视觉上的对应关系就会变弱,不易维护

而Vue3属于组合式API Vue2 / 3

  • 直接——声明数据,声明方法 Vue2 / 3
  • 组件导完就能用,不用注册
  • template不再要求只能有一个根元素了
  • 加上setup就允许在script中直接编写组合式API

组合式API

setup选项——组合式api的入口

  1. 这个比钩子beforeCreate还要早
  2. 也就是它在创建实例之前就执行了,所以setup函数中是获取不到this的,也就是undefined(vue3很少用this)
  3. setup需要写成一个函数,直接往配置项里面去写
  4. 将来里面可以编写组合式的API,可以往里面调各种函数

Vue2 / 3

5. 特点——要想使用,必须return——但是如果加了setup就不需要了

Vue2 / 3

reactive和ref函数(声明数据)

  1. reactive({})——接收一个对象类型的数据,返回一个响应式的对象

比如👇:

<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>
  1. ref()——可接收 简单 类型 或者 对象类型的数据,传入并返回一个响应式的对象
  • 跟reactive唯一不同的就是可以接收简单类型,其他都一样

  • 但是,脚本中访问数据,需要通过 .value(不点的话,返回的是对象),而template页面中不需要加

computed

<script setup>
// 导入
import { computed } from 'vue'

// 执行函数 变量接受 在回调参数中return计算值

//执行函数 在回调参数中return基于响应式数据做计算的值(计算逻辑) ,用变量接收
const computedState = computed(() => {
	return 基于响应式数据做计算之后的值
})
</script>

tip:👇

Vue2 / 3

watch

  1. 侦听 一个或多个数据 的变化,数据 变化时执行回调 函数
  2. 分为——侦听单个数据 和 侦听多个数据
  3. 两个额外参数——immediate (一进页面立刻执行一次)和 deep (深度侦听),
    • 默认watch进行的是 浅层监视
    • (也就是可以直接监测到简单类型数据变化;但是监测不到复杂类型内部数据的变化)
  4. 也可以精确侦听对象里的某一个属性

单个👇

<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>

多个👇

<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>

额外参数👇

<script>
const count = ref(0)
watch(count, () => {
	console.log('count发生了变化变化')
}, {
	immediate: true // 一进页面立刻执行一次
        deep:true
})
</script>

精确侦听某个属性👇

Vue2 / 3

父子通信

父传子

  • 相当于去掉了父组件的data传参
  • 由于写了setup,所以无法直接配置props选项,所以用到了编译器宏
  1. 传死数据 Vue2 / 3
  2. 传动态数据 Vue2 / 3

子传父

Vue2 / 3

模板引用

通过ref ——表示获取真实的dom对象 或者 组件实例对象

如何使用👇

Vue2 / 3 然后通过ref对象.value即可访问到绑定的元素

tip:👇

  • 在默认情况下<script setup>语法糖下 组件内部 的属性和方法是 不开放给父组件访问
  • 可以通过defineExpose()编译宏 指定哪些属性和方法允许访问,把他们暴露出去!

Vue2 / 3

provide和inject

顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信(比如像爷传孙)

  1. 顶层组件通过  provide 函数提供 数据
provide('key',顶层组件中的数据 / ref对象,也就是响应式数据 / 方法)
  1. 底层组件通过  inject 函数 提供数据
const message = inject('key')

tip:渲染时候注意,顶层组件包中间,中间组件包底层

  1. 如果想要修改某个数据时,就要遵循“谁的数据谁维护”的原则
  • 所以引出——跨层级传递函数=>给孙后代传递可以改数据的方法
// 在你想要改的数据的vue(也就是你最开始provide这个数据的地方)中提供provide修改的函数,共享方法

//跨层级传递函数=>给孙后代传递可以改数据的方法
// !!!相当于往子孙后代中共享了一个方法
provide('changeCount',(newCount) =>{
  count.value = newCount
})


//在子孙后代中去接收这个函数
const changeCount = inject('changeCount')
const clickFn = () => {
// 调用
  changeCount(1000)
}

24. Vue3.3 新特性

defineOptions宏

  • 为了解决这一问题,引入了defineProps 与 defineEmits这两个宏。但这只解决了propsemits这两个属性
  • 如果我们要定义组件的name或其他自定义的属性,还是得回到最原始的用法 – 再添加一个普通的<script>标签。
  • 视觉上不合理
  • 所以
  • 引入 defineOptions宏
  • .顾名思义,主要是用来定义 Option API 的选项. 可以用defineOptions 定义任意的选项(props,emits,expose,slots除外,它们有专属的宏)
<script setup>
defineOptions({
	name: 'Foo',//给组件命名
	inheritAttrs: false,
	// ...更多自定义属性
})
<script>

defineModel

在Vue3中,自定义组件上使用v-model,相当于传递一个modelValue属性, 同时触发 updata:modelValue 事件

所以就要求我们,先定义 props, 再定义 emits. 其中有许多重复的代码. 如果需要修改此值, 还需要手动调用 emit 函数

  • 太麻烦了,所以用defineModel来简化(只动了子组件里的代码)

Vue2 / 3

<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中加配置

plugins: [
    vue({
      script:{
        defineModel:true
      }
    }),
  ],

25.Pinia

Pinia 是 Vue 的最新状态管理工具, 是Vuex的 替代品(只有state,actions,getters)

  1. 提供了更加简单的API(去掉了mutation,也就是action既可以异步又可以直接修改数据了;;相当于mutations和actions合二为一)

  2. pinia如何实现getter?——computed计算属性函数

  3. 提供符合组合式风格的API(和Vue3新语法统一)

  4. 弃掉了modules的概念, 每一个store都是一个独立的模块

  5. 配合 TypeScript 更加友好,提供可靠的类型推断