Vue笔记
Vue2
使用Vue
引用Vue.js
1 放到body后,DOM加载完后引用
2 先引用vue.js 再引用自己的js
1 | </body> |
代码示例
1 | <div id="vue_test"> |
1.Vue构造器中参数el与DOM中元素的id保持一致
2.data用于定义属性,存放数据和变量
3.methods用于定义函数
Vue指令
v-html和v-text
v-html和v-text用来辅助渲染DOM元素的文本内容。
v-text(类似innerText)v-html(类似 innerHTML)
使用该标签属性后,会覆盖标签内部内容
1 | <body> |
v-for
v-for 可以绑定数据到数组来渲染一个列表:
1 | <label for="province" >省份:</label> |
样例中provinces是Vue实例中的数据 province是其元素迭代别名
v-for中的**:key后面跟唯一标识,确保列表项正确排序复用**
为什么加key:Vue 的默认行为会尝试原地修改元素(就地复用)
v-if
v-if用来显示或者隐藏元素,并控制元素的创建和移除:
1 | <div class="subject-row" id="type-0" v-if="type===0"> |
v-if后面跟着JS语句 其值决定是否隐藏该元素
还有v-else-if v-else 需要跟在v-if或者v-else-if后面
1 | <body> |
v-show和v-if的区别?
v-if用来显示或者隐藏元素,并控制元素的创建和移除
v-show切换css的display:none来控制显示隐藏
v-show适合频繁显示隐藏的,v-if则反之
v-on:click 和 v-on:change
- <button v-on:事件名=”内联语句”>按钮
- <button v-on:事件名=”处理函数”>按钮
- <button v-on:事件名=”处理函数(实参)”>按钮
v-on:
简写为 @
1 | <button type="button" @click="message">一键智能查询</button> |
缩写是 @click和@change
v-bind 和 v-model
v-bind 绑定Vue数据到html元素
作用:动态设置html的标签属性 比如:src、url、title
1 | <select id="province" name="province" onChange="switchSubjects()" > |
这里的:value 实际是 v-bind:value的简写
v-model实现数据和html元素的双向绑定
作用: 给表单元素(input、radio、select)使用,双向绑定数据,可以快速 获取 或 设置 表单元素内容
1 | <label for="score">分数:</label> |
改一个 变两个
指令修饰符
所谓指令修饰符就是通过.
指明一些指令后缀
不同后缀封装不同的处理操作 用于简化代码
按键修饰符
例如
@keyup.enter —>当松开enter键的时候才触发
v-model修饰符
- v-model.trim —>去除首位空格
- v-model.number —>转数字
事件修饰符
- @事件名.stop —> 阻止冒泡(例如点击子元素,阻止父元素也被点击)
- @事件名.prevent —>阻止默认行为
- @事件名.stop.prevent —>可以连用 即阻止事件冒泡也阻止默认行为
v-bind对样式的控制
v-bind控制class
语法
1 | <div> :class = "对象/数组">这是一个div</div> |
对象语法
当class动态绑定的是对象时,键就是类名,值就是布尔值,如果值是true,就有这个类,否则没有这个类
1 | <div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div> |
适用场景:一个类名,来回切换
数组语法
当class动态绑定的是数组时 → 数组中所有的类,都会添加到盒子上,本质就是一个 class 列表
1 | <div class="box" :class="[ 类名1, 类名2, 类名3 ]"></div> |
使用场景:需要对类批量添加或者删除时
v-bind控制style
语法
1 | <div class="box" :style="{ CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div> |
注意 属性键名不能用横杠 需使用引号或者驼峰
v-model应用表单元素
1 | 输入框 input:text ——> value |
计算属性computed
介绍
概念
基于现有的数据,计算出来的新属性。 依赖的数据变化,自动重新计算。
语法
- 声明在 computed 配置项中,一个计算属性对应一个函数
- 使用起来和普通属性一样使用
注意
- computed配置项和data配置项是同级的
- computed中的计算属性虽然是函数的写法,但他依然是个属性
- computed中的计算属性不能和data中的属性同名
- 使用computed中的计算属性和使用data中的属性是一样的用法
- computed中计算属性内部的this依然指向的是Vue实例
computed的简易使用
1 | const app = new Vue({ |
1 | <p>总数:{{totalCount}} 个</p> |
计算属性完整写法
1 | /* |
即 获取+设置
一个计算属性被获取时被调用,一个计算属性被设置时被调用
computed的优势-缓存特性
计算属性会对计算出来的结果,之后被调用会直接读取缓存
依赖项变化后,会重新计算再缓存
多次调用只计算一次
watch监视器
监视数据变化,执行一些业务逻辑
简单写法
1 | data:{ |
完整写法(对多个数据监视或者有其他要求时)
- deep:true 对复杂类型进行深度监听 (对象中的属性,只要一个变了就被监听到)
- immediate:true 初始化 立刻执行一次(一进页面,立刻执行一次)
1 | data: { |
防抖 延迟执行,一定时间没有执行后再请求
1 | watch: { |
Vue生命周期
生命周期:一个Vue实例从创建到销毁的整个过程
四个阶段:创建,挂载,更新,销毁
1.创建阶段:创建响应式数据
2.挂载阶段:渲染模板
3.更新阶段:修改数据,更新视图
4.销毁阶段:销毁Vue实例
初始化渲染请求的时间 创建阶段结束后
操作dom的时间 挂载阶段结束后
Vue生命周期函数(钩子函数)
Vue生命周期过程中,会自动运行一些函数,被称为【生命周期钩子】→ 让开发者可以在【特定阶段】运行自己的代码
created 发送初始化渲染请求
mounted 操作dom
beforeUpdate和updated只有 修改数据才会被调用
beforeUpdate里 数据已经修改 但视图还没更新
updated里 数据已经修改 视图也已经更新
beforedestroy 释放Vue实例以外 的资源
Vue工程化开发版本对应
Vue2—VueRouter3.x—Vuex3.x
Vue3—VueRouter4.x—Vuex4.x
Vue工程化开发和脚手架
开发Vue的两种方式
核心包传统开发模式:基于html / css / js 文件,直接引入核心包,开发 Vue。
工程化开发模式:基于构建工具(例如:webpack)的环境中开发Vue。
脚手架Vue CLI
基本概念
Vue CLI 是Vue官方提供的一个全局命令工具
可以帮助我们快速创建一个开发Vue项目的标准化基础架子。【集成了webpack配置】
好处:
- 零配置
- 内置babel等工具
- 标准化的webpack配置
使用步骤:
- 全局安装(只需安装一次即可) yarn global add @vue/cli 或者 npm i @vue/cli -g
- 查看vue/cli版本: vue –version
- 创建项目架子:vue create project-name(项目名不能使用中文)
- 启动项目:yarn serve 或者 npm run serve(命令不固定,找package.json)
VueCLi自定义创建项目
1.安装脚手架 (已安装)
1 | npm i @vue/cli -g |
2.创建项目
1 | vue create hm-exp-mobile |
- 选项
1 | Vue CLI v5.0.8 |
- 选择Vue版本
- 是否使用history模式 (项目目录不带#)
- 选择css预处理
- 选择eslint风格(代码规范校验工具)
- 选择校验的时机
- 配置文件的生成方式 独立的还是统一在package.json里
- 是否保存预设
Vue项目目录介绍和运行流程
项目目录介绍
重点
- main.js 入口文件
- App.vue App根组件 (vue实例都在这写)
- index.html 模板文件
运行流程
main.js代码示例
1 | //导入Vue |
组件化开发
组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。
好处:便于维护,利于复用 → 提升开发效率。
组件分类:普通组件、根组件。
比如:下面这个页面,可以把所有的代码都写在一个页面中,但是这样显得代码比较混乱,难易维护。可以按模块进行组件划分
根组件 App.vue
根组件包裹所有组件
三部分构成
- template:结构 (有且只能一个根元素)
- script: js逻辑
- style: 样式 (可支持less,需要装包)
让组件支持less
(1) style标签,lang=”less” 开启less功能
(2) 装包: yarn add less less-loader -D 或者npm i less less-loader -D
示例代码
1 | <!-- 结构 vue2只支持一个根节点--> |
普通组件的注册使用-局部注册
局部注册(只能在注册的组件内使用)
创建.vue文件
在使用的组件.vue内先导入再注册,最后使用
1
2
3
4
5
6
7
8
9
10// 导入
...
<script>
import 组件对象 from '.vue文件路径'
export default {
components:{
组件名: 组件对象,
}
}
</script>使用方式 当成html标签即可 <组件名></组件名>
组件名规范 —> 大驼峰命名法 如WinterHeader
普通组件的注册使用-全局注册
全局注册(在项目的任何组件中都能使用)
创建vue组件
main.js中进行全局注册
1
2import 组件对象 from '.vue文件路径'
vue.component('组件名', 组件对象)使用方式 当成html标签即可 <组件名></组件名>
vscode vue不高亮 有的文件夹没有图标
微软账号改 发送邮箱
scoped解决样式冲突
scoped 让组件的样式是独立的,只作用于当前组件
scoped使用
给style添加scoped属性
1 | <template> ... </template> |
scoped原理
当前组件内标签都被添加data-v-hash值 的属性
组件的数据 data必须写成函数
写成函数的目的是 让每一个组件实例的数据是独立的
示例代码
1 | <template> ... </template> |
组件通信
组件通信:组件通信,就是指组件与组件之间的数据传递
组件关系: 父子关系,非父子关系
不同关系的组件通信
父子通信流程与示例
流程
- 父组件 在标签里
:数据名="数据"
传递子组件 - 子组件
props['数据名']
接收数据,并通过$emit('方法', '数据新值')
通知父组件修改 - 父组件
@方法="父组件方法"
示例
1 | // 父组件 |
1 | //子组件 |
非父子组件通信-EventBus 事件总线
步骤
创建一个所有组件都能访问的事件总线 (例如放在utils里 EventBus.js)
1
2
3import Vue from 'vue'
const Bus = new Vue()
export default BusB组件 (发送方),触发bus的¥emit事件
1
2
3
4
5
6<script>
import Bus from '../utils/EventBus'
export default {
Bus.$emit('sendMsg','消息')
}
</script>A组件 (接收方),监听Bus的 ¥on事件
1
2
3
4
5
6
7
8<script>
import Bus from '../utils/EventBus'
export default {
Bus.$on('sendMsg', (msg) => {
this.msg = msg
})
}
</script>
跨层级的非父子组件通信-provide & inject
特点:跨层级共享数据
简单类型 (非响应式)
复杂类型 (响应式)
步骤
父组件提供数据
1
2
3
4
5
6
7
8
9
10
11
12<script>
export default {
provide () {
return {
// 普通类型【非响应式】
color: this.color,
// 复杂类型【响应式】
userInfo: {id: '1', name: 'winter'},
}
}
}
</script>子/孙组件 inject获取数据
1
2
3
4
5<script>
export default {
inject:['color', 'userInfo']
}
</script>
props校验
props概念:组件上注册的自定义属性,如<son :data1="" :data2=""><son>
使用方式
props类型校验
- 类型校验
- 非空校验
- 默认值
- 自定义校验
类型校验示例
1 | //写在子组件script里 |
props完整校验写法
语法
1 | <script> |
props & data、单向数据流
props和data的区别
- data 的数据是子组件自己的 → 随便改
- prop 的数据是父组件外部的 → 不能直接改,要遵循 单向数据流
单向数据流
父级的数据更新,会向下流动到子级的props里,影响这个子组件。这个数据流动是单向的
v-model详解
v-model原理
v-model本质是语法糖。例如输入框中,就是value属性和input属性的合写
不同表单元素v-model的语法糖不一样
1 | <template> |
v-model实现数据双向绑定,简化代码
前提 props通过value
接收,事件触发input
示例
子组件
1 | <template> |
父组件
1 | <BaseSelect v-model="selectId"></BaseSelect> |
Vue3 v-model原理
vue3中的v-model语法糖统一成了
:modelValue
和@update:modelValue
示例
子组件
1 | <script setup> |
父组件
1 | <channel-select v-model="params.cate_id"></channel-select> |
.sync修饰符
和v-model相比也是语法糖,也用于父子组件数据的双向绑定,但是可以自定义props属性名
使用场景:封装表单元素用v-model,其他元素可以用.sync修饰符
本质:.sync修饰符 就是 :属性名 和 @update:属性名 合写
示例:
1 | //父组件 |
子组件:
1 | <script> |
ref和$refs
作用:
- 专门获取组件内部的dom元素
- 获得组件内部的方法
获取dom示例:
1 | //某组件 |
父组件获得子组件内部方法示例
1 | //父组件 |
获得组件内部方法示例
1 | //父组件 |
Vue异步dom更新、$nextTick
Vue更新dom是异步的,所以用$nextTick
$nextTick
等DOM更新后,才会执行方法里的函数体
示例
1 | this.$nextTick(() => { |
注意:$nextTick 内的函数体 一定是箭头函数,这样才能让函数内部的this指向Vue实例
自定义指令
概念:自己定义的指令,可以封装一些DOM操作,扩展额外的功能
注册
全局注册 在main.js里
1
2
3
4
5Vue.directive('指令名', {
"inserted" (el) {
el.focus()
}
})局部注册 当前组件
1
2
3
4
5
6
7
8
9
10
11<script>
export default {
directives: {
"指令名": {
inserted(el) {
el.focus()
}
}
}
}
</script>
inserted:被绑定元素插入父节点时调用的钩子函数
el:使用指令的那个DOM元素
使用
标签里添加属性v-指令名
自指令的值
1.在绑定指令时,可以通过v-指令名=值
的形式为指令 绑定 具体的参数值
1 | <div v-color="color">我是内容</div> |
2.通过 binding.value 可以拿到指令值,指令值修改会 触发 update 函数
1 | <script> |
插槽
让我们在App.vue里使用组件内部结构支持自定义
默认插槽用法
组件内部需要定制的部分,使用
<slot></slot>
占位使用组件时,
<组件名></组件名>
标签内部传入的结构会替换slot
示例代码
1 | // 组件内部 |
1 | // App.vue |
插槽后备内容(默认值)
封装组件时,可以为预留的 <slot>
插槽提供后备内容(默认内容)
当外部使用组件时,不传内容,则会显示slot槽内的后备内容
具名插槽用法
适用情形:当一个组件内有多处结构,需要外部传入标签进行定制时
用name属性区分名字
1
2
3
4
5
6
7
8<template>
<div>
<slot name="name1"></slot>
</div>
<div>
<slot name="name2"></slot>
</div>
</template>template配合
v-slot:名字
或#名字
分发标签1
2
3
4
5
6
7
8<MyDialog>
<template v-shot:name1>
内容1
</template>
<template v-shot:name2>
内容2
</template>
</MyDialog>
作用域插槽(给插槽绑定数据)
使用步骤
给slot标签以添加属性的方式传值
1
<slot :id="item.id" msg="测试文本"></slot>
所有添加的属性都会被收集到一个对象里
1
{ id: 3, msg: '测试文本' }
在template中,通过
#插槽名=obj
接收,默认插槽的话用#default
1
2
3
4
5<MyTable :list="list">
<template #default="obj">
<button @click="del(obj.id)">删除</button>
</template>
</MyTable>
路由入门
单页应用程序 SPA:是指所有的功能都在一个html页面上实现
vue经常用来开发单页应用
Vue路由:访问路径和 组件的对应关系
路由使用步骤
下载VueRouter模块到当前工程,vue2使用3.x版本 vue3使用4.x版本
1
npm i vue-router@3.6.5
main.js中引入VueRouter
1
import VueRouter from 'vue-router'
main.js安装注册路由
1
Vue.use(VueRouter)
main.js创建路由对象
1
const router = new VueRouter
将路由对象注入到Vue实例中
1
2
3
4new Vue({
render: h => h(App),
router:router
}).$mount('#app')main.js创建需要的组件(views目录下),在步骤4里的路由对象配置路由规则
1
2
3
4
5
6
7
8
9
10
11
12import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'
//path 组件对应的路径 component 用到的组件
const router = new VueRouter({
routes: [
{ path: '/find', component: Find},
{ path: '/my', component: My},
{ path: '/friend', component: Friend}
]
})在App.vue里配置导航与路由出口(组件显示的位置)
1
2
3
4
5
6
7
8<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a>
</div>
<div class="top">
<router-view></router-view>
</div>
路由进阶
封装路由
步骤
- 将原先main.js里 导入VueRouter 安装注册路由 创建路由对象 这几步放到单独的js文件里
- 再将单独的js文件导出到 main.js
路径简写:vue可以直接用@指代src目录
不需要webpack中设置解析路径别名alias (创建vue项目时已自动设置)
声明式导航 router-link组件 导航链接
替代a标签 设置to属性 无需# to="/路径值"
router-link组件也是对a标签进行封装,样式设置在a标签上就可以了
1 | <router-link to='page1'>导航</router-link> |
默认提供高亮类名
声明式导航 高亮类名
router-link-active
模糊匹配(用的比较多) 可以匹配到以路径值开头的
router-link-exact-active
精准匹配 只能匹配到路径值
声明式导航 自定义类名
在创建路由对象时 添加属性 linkActiveClass
,linkExactActiveClass
1 | const router = new VueRouter({ |
声明式导航 跳转传参
查询参数传参
配置
router-link
的属性to="/路径名?参数名=值"
使用
$route.query.参数名
获得参数
动态路由传参
创建路由对象时,配置路由的path为
/path/:参数
跳转时 to属性设置为
/path/参数名
接收时
$route.params.参数名
获得参数
可选符 在创建路由对象配置path时 可以设置/path/参数?
表明该参数可传可不传
多个参数用查询参数传参,单个参数用动态路由传参
vue路由 重定向
示例
1 | const router = new VueRouter({ |
vue路由 404
创建路由对象时 配置在路由规则的最后面
1 | import NotFound from '@/views/NotFound' |
vue路由 模式设置
路由的路径看起来不自然, 有#,更改路径形式的方法
- hash路由(默认) 例如: http://localhost:8080/#/home
- history路由(常用) 例如: http://localhost:8080/home (以后上线需要服务器端支持,开发环境webpack给规避掉了history模式的问题)
创建路由对象时
1 | const router = new VueRouter({ |
vue路由 导航跳转
path路径跳转
push语法 跳转保留原先的地址 可以返回
replace 跳转不保留原先的地址 不可返回
1 | //简单写法 |
path路径传参 query传参
1 | this.$router.push('/路径?参数名1=参数值1&参数2=参数值2') |
path路径传参 动态路由params传参
1 | this.$router.push('/路径/参数值') |
name命名跳转
特点:适合 path 路径长的场景
语法:
路由规则,必须配置name配置项
1
{ name: '路由名', path: '/path/xxx', component: XXX },
通过name来进行跳转
1
2
3this.$router.push({
name: '路由名'
})
name命名传参 query传参
1 | this.$router.push({ |
name命名传参 动态路由params传参
1 | this.$router.push({ |
跳转到上一页
$router.back()
vue嵌套路由
在一级路由下,配置children属性即可
1 | const router = new Vue({ |
路由拦截-全局前置守卫
路由导航守卫 - 全局前置守卫
1.所有的路由一旦被匹配到,都会先经过全局前置守卫
2.只有全局前置守卫放行,才会真正解析渲染组件,才能看到页面内容
用途:一些页面不登陆不让看,点击那些页面时自动跳转到登录页
1 | // vuerouter3.x的写法 |
路由懒加载
打包时,js包会很大。
路由懒加载 & 异步组件, 不会一上来就将所有的组件都加载,而是访问到对应的路由了,才加载解析这个路由对应的所有组件
使用方式
1 | //路由文件 |
VueRouter4 路由代码解析
1 | import { createRouter, createWebHistory } from 'vue-router' |
Vue3 组合式API中获取路由
1 | <script setup> |
组件缓存 keep-alive
keep-alive的三个属性
- include 组件名数组, 只有匹配的组件会被缓存
- exclude 组件名数组, 匹配的组件不会被缓存
- max 最多可以缓存多少组件实例
示例:
App.vue
1 | <template> |
Layout.vue
1 | <script> |
额外的两个生命周期函数
keep-alive的使用会触发两个生命周期函数
activated 当组件被激活(使用)的时候触发 → 进入这个页面的时候触发
deactivated 当组件不被使用的时候触发 → 离开这个页面的时候触发
组件缓存后就不会执行组件的created, mounted, destroyed 等钩子了
所以其提供了actived 和deactived钩子,帮我们实现业务需求。
ESlint代码规范
JavaScript Standard Style 规范说明
多种eslint规范里的一种
https://standardjs.com/rules-zhcn.html
- 字符串使用单引号 – 需要转义的地方除外
- 无分号 – 这没什么不好。不骗你!
- 关键字后加空格
if (condition) { ... }
- 函数名后加空格
function name (arg) { ... }
- 坚持使用全等
===
摒弃==
一但在需要检查null || undefined
时可以使用obj == null
自动修正 ESlint插件
vscode里安装ESlint插件
在vscode里的setting.json里添加配置
1
2
3
4
5
6// 当保存的时候,eslint自动帮我们修复错误
"editor.codeActionsOnSave": {
"source.fixAll": true
},
// 保存代码,不自动格式化
"editor.formatOnSave": false
prettier
格式化代码插件 可以不使用vscode插件 创建vue项目时选择即可
配置文件 .eslintrc.cjs
添加如下内容
1 | rules: { |
prettier 风格配置 https://prettier.io
单引号
不使用分号
每行宽度至多80字符
不加对象|数组最后逗号
换行符号不限制(win mac 不一致)
vue组件名称多单词组成(忽略index.vue)
props解构(关闭)
忽略报错
例如用了element-plus组件库的自动导入后,用到一些组件会报未定义的错误
配置文件 .eslintrc.cjs
添加
1 | globals: { |
Vuex
Vuex概述
概念:Vuex是一个Vue的状态管理工具,状态就是数据
使用场景:多个组件都要用同一份数据
优势:
- 数据集中化管理,
- 响应式变化
- 操作简洁
vuex的使用
步骤
安装Vuex
新建vuex模块文件
新建store文件夹,新建index.js文件存放vuex
创建仓库
1
2
3
4
5
6
7
8
9
10
11
12// 导入 vue
import Vue from 'vue'
// 导入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 进行插件的安装初始化
Vue.use(Vuex)
// 创建仓库 store
const store = new Vuex.Store()
// 导出仓库
export default store导入到main.js中并挂载
1
2
3
4
5
6
7
8
9
10import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
核心概念-state状态
State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。
打开项目中的store.js文件,在state对象中可以添加我们要共享的数据。
1 | // 创建仓库 store |
获取公共数据state的语法
1 | 获取 store: |
辅助函数mapState
概念:
mapState能够把store中的数据映射到组件的计算属性computed里
使用步骤 (在需要使用state的组件里)
从vuex中导入mapstate函数
1
import { mapState } from 'vuex'
采用数组形式引入state属性
1 | mapState(['count']) |
利用展开运算符将导出的状态映射给计算属性
1
2
3computed: {
...mapState(['count'])
}
接下来要使用state中的count,就和使用state里的count一样
核心概念-mutations
在开发时,vuex应当遵循单向数据流,组件中不应该能直接修改仓库的数据
但默认可以修改,可以在开发时在创建store对象时,添加属性strict: true
上线时需 关闭严格模式
所以需要mutations来修改数据
步骤
store对象中定义mutations对象,对象中存档修改state的方法
1
2
3
4
5
6
7
8
9
10
11
12
13//mutations对象的方法里,第一个参数永远都是state
const store = new Vuex.Store({
strict: true,
state: {
title: 'Winter',
count: 100
},
mutations: {
addCount (state) {
state.count++
}
}
})组件中提交调用mutations里的方法
1
this.$store.commit('addCount')
mutations传参语法
mutations里定义方法 方法名 (state, 参数)
组件传参this.$store.commit('方法名', 参数)
注意:提交的参数只能有一个 所以如果有多个参数可以提交一个对象
辅助函数-mapMutations
mapState将state 映射到组件计算属性computed里
mapMutations将mutations里的方法 映射到组件方法method里
核心概念-actions
state用于存放数据,mutations用于同步更新数据 而action负责异步操作
步骤
定义actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14//此处未分模块,context可以当成store仓库
mutations: {
changeCount (state, newCount) {
state.count = newCount
}
}
actions: {
setAsyncCount (context, num) {
// 一秒后, 给一个数, 去修改 num
setTimeout(() => {
context.commit('changeCount', num)
}, 1000)
}
},组件中通过dispatch调用
this.$store.dispatch('setAsyncCount', 参数)
辅助函数 - mapActions
mapState将state 映射到组件计算属性computed里
mapMutations将mutations里的同步方法 映射到组件方法method里
mapActions将actions里的异步方法 映射到组件方法methods里
使用
1 | import { mapActions } from 'vuex' |
核心概念-getters
getters用于从state中筛选出符合条件的一些数据
步骤
在store对象中定义getters
1
2
3
4
5getters: {
// getters函数的第一个参数是 state
// 必须要有返回值
filterList: state => state.list.filter(item => item > 5)
}使用getters
原生方式 $store
this.$store.getters.filterList
mapGetters 映射到计算属性
1
2
3
4import { mapgetters } from 'vuex'
computed: {
...mapgetters(['filterList'])
}
核心概念-module
将vuex模块化 便于管理数据和维护
步骤
store目录下 新建modules文件夹,将模块放在这里(例如@/store/modules/user.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const state = {
userInfo: {
name: 'zs',
age: 18
}
}
const mutations = {}
const actions = {}
const getters = {}
export default {
state,
mutations,
actions,
getters
}在index.js里导入
1
2
3
4
5
6
7import user from './modules/user'
const store = new Vuex.Store({
modules:{
user
}
})
使用模块的数据state
- 通过模块名直接访问
$store.state.模块名.数据
- 通过mapstate映射
- 把整个模块映射
mapState['模块名']
- 映射某个数据
mapstate('模块名', ['数据'])
需要在对应模块导出时开启命名空间namespaced: true
- 把整个模块映射
使用模块筛选过的数据 getters
- 通过模块名访问
$store.getters('[模块名/数据]')
- 子模块的映射
mapGetters('模块名', ['数据'])
也需要开启命名空间
使用模块中的同步方法 mutations
- 直接通过 store 调用
$store.commit('模块名/xxx ', 额外参数)
- 通过 mapMutations 映射
- 默认根级别的映射
mapMutations([ 'xxx' ])
- 子模块的映射
mapMutations('模块名', ['xxx'])
- 需要开启命名空间
- 默认根级别的映射
使用模块中的异步方法 actions
- 直接通过 store 调用
$store.dispatch('模块名/xxx ', 额外参数)
- 通过 mapActions 映射
- 默认根级别的映射
mapActions([ 'xxx' ])
- 子模块的映射
mapActions('模块名', ['xxx'])
- 需要开启命名空间
- 默认根级别的映射
storage存储模块-Vuex持久化处理
一些登录凭证token和用户的信息userId 做一个持久化处理 封装到模块
新建
utils/storage.js
封装方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const INFO_KEY = 'hm_shopping_info'
// 获取个人信息
export const getInfo = () => {
const result = localStorage.getItem(INFO_KEY)
return result ? JSON.parse(result) : {
token: '',
userId: ''
}
}
// 设置个人信息
export const setInfo = (info) => {
localStorage.setItem(INFO_KEY, JSON.stringify(info))
}
// 移除个人信息
export const removeInfo = () => {
localStorage.removeItem(INFO_KEY)
}vuex user模块调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import { getInfo, setInfo } from '@/utils/storage'
export default {
namespaced: true,
state () {
return {
userInfo: getInfo()
}
},
mutations: {
setUserInfo (state, obj) {
state.userInfo = obj
setInfo(obj)
}
},
actions: {}
}
第三方组件库 vant-ui
地址:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/
常用的组件库还有以下几种:
pc: element-ui element-plus iview ant-design
移动:vant-ui Mint UI (饿了么) Cube UI (滴滴)
安装
vue2项目 npm i vant@latest-v2 -S
组件全部导入(不推荐)
在main.js注册
1 | import Vant from 'vant' |
按需导入(推荐)
安装插件
npm i babel-plugin-import -D
在
babel.config.js
中配置1
2
3
4
5
6
7
8
9
10
11
12module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}可将引入组件的步骤抽离到单独的js文件中 如
utils/vant-ui.js
1
2
3
4
5import Vue from 'vue'
import { Button, Icon } from 'vant'
Vue.use(Button)
Vue.use(Icon)在main.js中导入
1 | // 导入按需导入的配置文件 |
按需导入中的全局使用
在main.js中导入并注册需要的组件
1
2import { Toast } from 'vant'
Vue.use(Toast)之后在其他组件里直接使用 无需导入
1
2
3
4
5
6
7<script>
export default {
created () {
this.$Toast('登录')
}
}
</script>
移动端适配: vw适配
官方说明:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/advanced-usage
安装 npm i postcss-px-to-viewport@1.1.1 -D
为请求、响应拦截器添加loading
作用:用户提示,防止用户多次点击节流
- 请求时,打开 loading
1 | // 添加请求拦截器 |
- 响应时,关闭 loading
1 | // 添加响应拦截器 |
第三方组件库 element-plus
官方文档: https://element-plus.org/zh-CN/
- 安装
1 | $ pnpm add element-plus |
自动按需:
- 安装插件
1 | pnpm add -D unplugin-vue-components unplugin-auto-import |
- 然后把下列代码插入到你的
Vite
或Webpack
的配置文件中
1 | ... |
- 直接使用
1 | <template> |
表单校验规则
表单校验需要进行的配置
el-form
组件上绑定form数据对象1
2
3
4
5
6
7const formModel = ref({
username: '',
password: '',
repassword: ''
})
<el-form :model="formModel" >el-form
上绑定rules规则对象1
2
3
4
5<el-form :rules="rules" >
const rules = {
...
}表单元素上v-model绑定form数据对象的子属性
1
2
3
4
5<el-input
v-model="formModel.username"
:prefix-icon="User"
placeholder="请输入用户名"
></el-input>prop绑定校验规则到元素
el-form-item
1
2
3
4
5
6
7<el-form-item prop="username">
<el-input
v-model="formModel.username"
:prefix-icon="User"
placeholder="请输入用户名"
></el-input>
</el-form-item>
校验规则
1 | const rules = { |
表单预校验
validate是表单暴露的函数,可以表单所有规则的检测,要使用它只需
1 | // 定义响应式变量 |
表格结构与渲染数据
label用来显示到用户
data绑定数据 数组对象
prop用来接收对象中的属性
1 | <el-table :data="articleList" style="width: 100%"> |
表格自定义插槽
1 | <el-table-column prop="title" label="文章标题" width="width"> |
table的每个column都可以自定义内容 这里的row就是表单中data里的数组对象的每个item
默认显示中文
App.vue 中直接导入设置成中文即可
1 | <script setup> |
Vue3
Vue3优势
vue3更容易维护
组合式API 更好的typesript支持
更快的速度
重写difff算法 模板编译优化 组件初始化更高效
更小的体积
良好的treeshaking 按需引入
更好的数据响应式
proxy
create-vue搭建vue3项目
vue-cli用来创建vue2 底层为webpack
create-vue用来创建vue3 底层为vite
npm init vue@latest
执行命令 安装并执行create-vue
组合式API setup选项
1. 执行时机
在beforeCreate之前自动执行 这也导致setup函数无法获取到this
2. 写法
组合式api放在setup选项里
数据和函数需要在setup最后return 才能在模板中使用
3. <script setup>
语法糖
不必return 也不用加导出语句export default
组合式API reactive和ref函数
1. reactive
接受对象类型数据的参数传入并返回一个响应式的对象
1 | <script setup> |
2. ref
接收简单类型或者对象类型的数据传入并返回一个响应式的对象
1 | <script setup> |
3. reactive 对比 ref
- 都是用来生成响应式数据
- 不同点
- reactive不能处理简单类型的数据
- ref参数类型支持更好,但是js中必须通过.value做访问修改,模板中使用不需要加value
- ref函数内部的实现依赖于reactive函数,本质还是将简单类型数据变成了对象
- 在实际工作中的推荐
- 推荐使用ref函数,减少记忆负担
组合式API computed属性
思想与vue2一致 只是写法不同
1 | <script setup> |
组合式API watch属性
侦听一个或者多个数据的变化,数据变化时执行回调函数,俩个额外参数 immediate控制立刻执行,deep开启深度侦听
1. 侦听单个数据
1 | <script setup> |
2. 侦听多个数据
侦听多个数据,第一个参数可以改写成数组的写法
1 | <script setup> |
3. immediate
在侦听器创建时立即出发回调,响应式数据变化之后继续执行回调
1 | <script setup> |
4. deep
通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep
1 | <script setup> |
5. 精确侦听对象的某个属性
1 | const userInfo = { |
组合式API 生命周期函数
beforeCreated
和created
的相关代码 一律放在setup
里
选项式API的钩子在组合式API里也能用
vue3 同一个生命周期函数可以调用多次
Vue3的销毁不再是destroyed
组合式API 父子通信
1. 父传子
步骤
父组件绑定属性传值
子组件通过defineProps方法接收
示例
父组件
<SonCom name="winter" :age="age"></SonCom>
子组件
1
2
3
4
5
6
7
8
9
10
11
12<script setup>
const props = defineProps({
name: String,
age: Number
})
console.log(props.name);
</script>
<template lang="">
<div>
我是子组件 {{name}} {{age}}
</div>
</template>
2. 子传父
步骤
- 父组件在子组件标签中通过@绑定事件
- 子组件内部通过defineEmits生成emit方法 并触发事件传递参数
示例
父组件
<SonCom :age="age" @addWinterAge="addAge"></SonCom>
子组件
1 | <script setup> |
组合式API - 模板引用
1. 基本使用
概念:通过 ref标识 获取真实的 dom对象或者组件实例对象
步骤
- 调用ref函数生成ref对象
- 通过ref标识绑定ref对象到标签
示例
1 | <script> |
2. defineExpose
默认情况下在 <script setup>
语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法容许访问
示例
1 | 子组件 |
1 | 父组件 |
组合式API provide和inject
作用场景:顶层组件向底层组件传递数据和方法,实现跨层级通信
步骤
- 顶层组件通过
provide
函数提供数据和修改数据的方法 - 底层组件通过
inject
函数提供数据
示例代码
父组件
1 | <script setup> |
子组件
1 | <script setup> |
defineOptions
defineOptions 宏。顾名思义,主要是用来定义 Options API 的选项。可以用 defineOptions 定义任意的选项, props, emits, expose, slots 除外(因为这些可以使用 defineXXX 来做到)
1 | <script setup> |
defineModel
优化自定义组件的v-model用法
1 | import { fileURLToPath, URL } from 'node:url' |
1 | <script setup> |
Pinia
概念:Pinia是Vue最新状态管理库,Pinia去掉了mutation和modules,配合typescript更友好
Pinia使用步骤
如果使用vite创建vue项目时可以直接勾选pinia,也可以手动设置
手动设置 npm i pinia
Vue项目入口文件main.js中设置Pinia
1 | import { createApp } from 'vue' |
创建store
1 | import { defineStore } from 'pinia' |
pinia支持组合式api用法,因此可以用ref来指明state,用computed来指明getters,直接定义函数来代替actions
storeToRefs
从store中不能直接解构数据 解构需要用到storeToRefs
1 | // 数据不会是响应式 |
从store中可以直接解构方法
1 | const {addCount, SubCount} = couterStore |
Pinia持久化插件
官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
- 安装插件 pinia-plugin-persistedstate
1 | npm i pinia-plugin-persistedstate |
- 使用 main.js
1 | import persist from 'pinia-plugin-persistedstate' |
- 配置 store/counter.js
1 | import { defineStore } from 'pinia' |
- 其他配置,看官网文档即可
Pinia - 配置仓库统一管理
pinia 独立维护
- 现在:初始化代码在 main.js 中,仓库代码在 stores 中,代码分散职能不单一
- 优化:由 stores 统一维护,在 stores/index.js 中完成 pinia 初始化,交付 main.js 使用
实现
src/stores/index.js
1 | import { createPinia } from 'pinia' |
main.js
1 | import { createApp } from 'vue' |
仓库 统一导出
- 现在:使用一个仓库 import { useUserStore } from ./stores/user.js
不同仓库路径不一致
- 优化:由 stores/index.js 统一导出,导入路径统一 ./stores
,而且仓库维护在 stores/modules 中
实现
src/stores/index.js
1 | import { createPinia } from 'pinia' |