文章目录
一、vue 指令
- vue 指令就是
封装的 DOM 操作
,均以 v- 开头
(一)vue 指令简介
vue 指令 | 语法 | 简写 | 作用 |
---|---|---|---|
v-bind | v-bind:k=‘变量名’ | : | 动态设置 html 标签属性 |
v-on | v-on:event=‘少量代码’ v-on:event=‘函数名’ v-on:event=‘函数名(参数1,参数2…)’ |
@ | 注册事件 |
v-show | v-show=‘boolean’ | 无 | 控制css样式'display:none' ,操作频繁 |
v-if | v-if=‘boolean’ v-if=‘条件’ |
无 | 动态创建或删除元素节点 ,隐藏时需要删除节点 |
v-else-if | v-else-if=‘boolean’ v-else-if=‘条件’ |
无 | 与v-if并列 |
v-else | v-else=‘boolean’ v-else=‘条件’ |
无 | 与v-if并列 |
v-mode:value | v-model=‘变量值’ | v-model | 面向表单元素,实现双向数据绑定,快速获取或设置表单 |
v-text | v-text=‘值’ | 无 | innerText 不解析标签,不常用,一般用插值表达式 |
v-html | v-html=‘值’ | 无 | innerHTML 解析标签,禁止将用户输入直接作为值,易造成XSS攻击 |
v-for | v-for=‘item in arr’ v-for=‘(item, index) in arr’ |
无 | 遍历数组,用于渲染结构(重要) |
v-for | v-for=‘(val, k) in obj’ | 无 | 遍历对象,用于渲染结构(了解) |
v-for | v-for=‘item in 数字’ | 无 | 遍历数字,用于渲染结构(了解) |
(二)vue 指令补充
1、v-on
- 获取事件对象
- 无传参:形参接收 e
- 有传参:$event 指代 e
- 事件修饰符
- 阻止默认行为:@event.prevent(e.preventDefault() )
- 阻止冒泡:@event.stop(e.stopPropagation() )
- 按键修饰:@event.key(@keyup.enter)
- 原生事件:@event.native
2、v-model
(1)v-model
- 本质:
v-model:value
的缩写,通常用于表单上的双向数据绑定
,也可以实现子组件到父组件的双向数据动态绑定
- 修饰符
- .number:以 parseFloat 转成数字类型
- .trim:去除首位空白字符
- .lazy:在 change (回车或失去焦点)时触发,非 input 触发
// 1、父组件
// 底层
<demo :value='money' @input=money=$event'></demo>
// 简写
<demo v-model='money'></demo>
// 2、子组件
export default{
props:{
value: Number // 必须叫 value
},
methods:{
add(){
this.$emit('input', this.value+1) //必须叫 input,this.value+1 是参数
}
}
}
(2):model
- 本质:
v-bind:model
的缩写,父组件的值传递给子组件
。但是子组件改变引用类型
数据,父组件的数据也会随之改变
(三)自定义指令
1、局部注册
// 组件内设置
<input type='text' v-focus>
export default{
directives:{
focus:{
// 定义指令名时无需加 v-
inserted(el){
// 当指令所在的元素节点被插入到页面触发,DOM 解析完成
el.focus() // element,指令所在的元素节点
}
}
}
}
2、全局注册
- Vue.directive(指令名, 指令配置对象(元素, 指令值))
// main.js 内设置
<input type='text' v-focus v-color='color'>
Vue.directive('color', {
inserted(el, binding){
// 当指令所在的元素节点被插入到页面触发,DOM 解析完成
el.style.color = binding.value // 指令的值
}
update(el, binding){
// 当指令绑定的值修改时触发
el.style.color = binding.value // 指令的值
}
})
二、虚拟 DOM 和 Diff 算法
(一)就地复用
- 定义:vue 尽可能就地(同层级、同位置)对比虚拟 dom,复用旧 dom 结构,进行差异化更新
- 优点:复用旧 dom 结构,高效更新
(二)虚拟 DOM
- 本质:保存节点信息,
描述真实 DOM 的 JS 对象
- 作用:提升对比性能
(三)Diff 算法
- 同级根元素比较
- 根元素变化:不考虑复用,整个DOM 树删除重建
- 根元素不变:对比属性变化,向下递归复用
- 同级兄弟元素比较
- 不设 key:默认
按照下标
进行比较 - 设置 key:优先相同 key 的元素对比复用
- key 特性:必须为
字符串
或数字
,且保证唯一性
(一般为 id) - key 作用:提高虚拟 DOM 的对比复用性
- key 特性:必须为
- 不设 key:默认
三、计算属性
(一)认识
- 定义:一个特殊
属性
,值依赖于一些数据动态计算 - 注意
- 必须定义在 computed 节点中
- 必须是 function,必有返回值
- 不能作为方法调用,作为属性使用
- 特点
- 计算后立即缓存,下次直接读取
- 依赖项改变,函数重新执行并重新缓存
- 多次使用计算属性,性能极高
(二)完整写法
- 计算属性默认情况下,只能获取不能修改
- 给计算属性赋值需要完整写法
computed:{
full:{
get(){
},
set(val){
}
}
}
四、监视属性
watch: {
// 1、监视简单数据类型
'money' (oldVal, newVal) {
console.log(`银行卡余额为${
newVal},原${
oldVal}`)
},
'obj.age' (oldVal, newVal) {
// 监视复杂数据的属性必须用括号包裹
console.log(`长大了,今年${
newVal}岁,之前${
oldVal}岁`)
},
// 2、监视复杂数据类型
person: {
immediate: true, // 立即执行,一进页面立刻执行
deep: true, // 复杂数据深度监视
handler (newVal) {
// handler 执行函数,新旧值一致
console.log(newVal)
}
}
}
五、组件
(一)Vue 组件
- 组件:可复用的 Vue 实例,封装
标签
、样式
和JS 代码
- 目的:复用、拆解
- 优点:各自独立,便于复用,可维护性高
- 组件化开发:封装思想,把页面上重复部分封装为组件,便于项目开发和维护
1、局部注册
- 创建:组件中创建 .vue(标签/样式/JS)
- 引入:组件中引入
- 注册:组件中注册
- 标签:组件中使用
2、全局注册
- 创建:组件中创建 .vue(标签/样式/JS)
- 引入:main.js 中引入
- 注册:main.js 中注册
- 标签:组件中使用
(二)组件通信
- 各组件数据独立,组件间数据无法互相直接访问,组件通信实现跨组件访问数据
1、父传子
- 父组件给子组件添加
标签属性
传值 - 子组件内部通过
props
接收只读
数据
// 父组件
<MyHome>
v-for='item in list'
:key='item.id'
:title='item.proname'
:price='item.proprice'
:info='item.desc'
:pid='item.id'
</MyHome>
// 子组件
props:['title','price','info','pid']
- props 校验:为了提高子组件被使用时的稳定性,
验证传递数据
是否符合要求
props: {
// 1、基础类型检查 Number String Boolean Object Array Function...
data:Array,
money:Number,
// 2、多个可能类型 [String, Number,...]
params:[String, Number,Boolean],
// 3、必填项(要求用户必须填写,否则报错)
age:{
type:Number,
required:true
},
// 4、默认值(用户选填,不填则使用默认值)
title:{
type:String,
default:'大标题'
}
}
- 单向数据流:从父到子的单向数据流动,通过子传父发起申请解决
2、子传父
- 子组件通过
this.$emit('自定义event',参数,this.id)
触发事件的同时传参 - 父组件给子组件注册对应的自定义事件
<Son @自定义event='fn(参数,id)'></Son>
六、ref 属性和 $nextTick
(一)ref 和 $refs
- 作用:获取 dom 元素或组件实例
- 区别
- document.querySelector:查找整个 document,不区分组件,作用范围广
- ref 和 $refs:查找
当前整个组件
,有查找范围
// 1、目标元素添加 ref 属性
<h1 ref='myH1'>ref获取原生DOM</h1>
<MySon ref='son'>ref获取组件实例</MySon>
// 2、通过 this.$refs.xxx 获取元素/组件
console.log(this.$refs.myH1)
console.log(this.$refs.son.属性/方法)
(二)$nextTick
- 背景:Vue 异步更新 DOM,即当前主线程代码执行完毕后一起更新,不能在修改数据之后立即更新 DOM
- 解决原理:宏任务执行完成后优先查看执行微任务,
浏览器完成更新渲染
,然后执行下一个宏任务
1、setTimeout()
setTimeout(() => {
// setTimeout()为宏任务,执行此代码前浏览器完成更新渲染
}, 0)
2、$nextTick()
this.$nextTick(() => {
// DOM 更新完毕后立即执行此代码
})
七、动态组件
- 定义:可以改变的组件,解决
多组件同一位置切换显示
的需求 - 语法
component组件(显示位置)
+is属性(具体组件)
- 修改 is属性绑定值,进行组件切换
<component :is='要显示的组件'/>
- 步骤
- 准备被切换的组件并引入注册
- 准备变量 comName 来承载要显示的组件名
- 设置挂载点
<component>
,js 属性设置要显示的组件(component + is) - 点击事件-修改 comName 变量里的组件名(修改 is 的值)
八、插槽
- props:用于
传值
,解决简单场景组件定制 - 插槽:用于
传结构
,解决自定义场景组件定制 - 基本语法
- 组件内占位:
<slot></slot>
- 使用组件:写入实际代码结构
- 后备内容:slot 标签内放置内容,外部未传结构时默认显示
- 组件内占位:
(一)具名插槽
- 默认插槽:未定义名字,默认名字为 default
- 具名插槽:通过 name 自定义名字,实现定向分发
// 1、组件内多处 slot 需要外部传入标签,通过 name 属性进行定制
<slot name='first'></slot>
<slot name='second'></slot>
// 2、template 配合 v-slot:名字/#名字 分发标签
<template v-slot:first>
<p>一个slot结构</p>
</template>
<template #second> // v-slot: 简写为 #
<p>另一个slot结构</p>
</template>
(二)作用域插槽
- 插槽通过
添加属性
的方式传值携带参数 - 将添加的所有属性收集到一个对象中
- 组件使用者通过
=
接收,仅在当前 template 范围内使用
<slot name='first' title='我是标题' desc='我是描述'></slot>
<slot name='second' :change='change' :edit='edit' money='100'></slot>
<template #first='obj'>
<p>{
{
obj.title}}</p>
<p>{
{
obj.desc}}</p>
</template>
<template #second='{change, edit, money}'> // 结构数据
<button>{
{
change}}</button>
<button>{
{
edit}}</button>
<p>{
{
money}}</p>
</template>
九、生命周期
- 生命周期函数:Vue 的内置函数,伴随组件生命周期
自动按序执行
- 初始化阶段:结合数据,结合模板,渲染视图
- beforeCreate():data 数据未完成初始化,vue 实例上还未绑定数据
created()
:data 数据完成初始化
,vue 实例上成功绑定数据
- 数据更新阶段:数据变化,视图更新
- beforeMount():页面结构未完成解析渲染,无法获取 DOM
mounted()
:页面结构完成解析渲染
,可以获取 DOM- beforeUpdate():数据变化后由于 vue 异步更新 DOM,DOM 还未更新
- updated():数据变化后 DOM 完成更新
- 组件销毁阶段
- beforeDestory():实例销毁、资源释放前(定时器、延时器、服务器资源)
destoryed()
:实例销毁、资源释放后(定时器、延时器、服务器资源)
十、路由
- 定义:路径和组件的映射关系
(一)vue-router
- 定义:vue 官方路由插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8WjLauG-1669287358087)(./vue-router.png)]
1、基本使用
- 下包:下载 vue-router 模块到当前工程,vue2 使用 @3版本
- 引入:main.js 中引入 VueRouter
- 注册:添加到 Vue.use(),插件内部初始化,注册全局组件、指令
- 实例:创建路由组件
- 挂载:将路由对象注入到 new Vue 实例
规则
:路由实例中配置路径和组件的对应出口
:App.vue 中 router-view 渲染位置
2、操作技巧
- @ 指代 src 目录,可直接从 src 出发找文件,提高可维护性
- 封装抽离:将路由模块抽离到 @/router/index.js,使得模块拆分,利于维护
(二)配置页面
1、路由重定向
- 定义:匹配 path 后,
redirect
强制跳转 path 路径
const routes = [
{
path:'/',
redirect:'/home'
}
]
2、配置页面404
- 定义:找不到路径匹配时,
component
提供页面错误提示
const routes = [
...,
{
// 放在路由最后
path:'*', // 匹配任意路径,前面不匹配命中最后规则
component:NotFound
}
]
3、路由模式
- hash 路由
const router = new VueRouter({
mode:'hash', // 默认
routes
})
- history 路由(上线需服务器端支持)
const router = new VueRouter({
mode:'history',
routes
})
(三)跳转方式
1、声明式导航
(1)导航链接
- 组件
router-link
代替 a 标签,自带激活类名,可以做高亮- 模糊匹配:router-link-active
- 精准匹配:router-link-exact-active
<router-link to='/home'>首页</router-link>
<router-link to='/my'>我的</router-link>
<router-link to='/part'>朋友</router-link>
(2)跳转传参
- 目标:跳转路由时给对应组件传值
- 查询参数
- 传参:router-link 的 to=‘/path?k=val&k=val’
- 接收:
$route
.query.k
- 动态路由传参
- 路由对象配置 path:‘/path/:k/:k’
- 传参:router-link 的 to=‘/path/val&val’
- 接收:
$route
.params.k
(3)跳转缓存
- 背景:切换路由时默认销毁/重建页面,用户切换页面时体检不佳
- 方案:使用
<keep-alive>
包裹 router-link 进行缓存 - 影响:生成两个钩子
- activated:缓存组件被激活
- deactivated:缓存组件被隐藏
2、编程式导航
(1)基本跳转
- JS 代码
path 跳转
this.$router.push({
path:'路由路径', // router/index.js 内定义
'路由路径', // 简写形式
})
- JS 代码
name 跳转
this.$router.push({
name:'路由名', // router/index.js 内定义
})
- JS 代码 go 跳转
this.$router.go(n) // n 正负数代表前进或后退步数
- 注意
- router:整个大的路由实例,可用于路由跳转,等同 this.$router
- route:路由规则相关,等同 this.$route
(2)跳转传参
path 仅支持 query 传参
,忽略 params
this.$router.push({
path:'路由名', // path 带参数
query:{
'k':val,
'k':val
}
})
this.$router.push({
'路由名?k=val&k=val'}) // path 带参数简写
this.$router.push({
path:'路由名', // path 带参数
query:{
'k':val
},
params:{
// params 即使存在也被忽略
'k':val
}
})
- name 支持 query 和 params 传参,但 params 传参刷新会消失
this.$router.push({
path:'路由名', // path 带参数
query:{
// 拼接在地址栏,刷新不会丢失
'k':val,
'k':val
}
})
this.$router.push({
name:'路由名', // path 带参数
params:{
// 地址栏不显示,基于内存刷新会消失,需要配合本地存储进行持久化
'k':val,
'k':val,
}
})
(四)页面路由传参
1、动态路由传参(优雅)
- 配置路由:
路径携带参数
{
path:'/user/:k', component:xxx}
- 跳转:仅路径(含参数)
<router-link to='/user/val'></router-link> // 标签方式跳转
this.$router.push('/user/val') // JS 方式跳转,简洁版
this.$router.push({
// JS 方式跳转,复杂版
path:'/user/val'
})
- 获取参数
this.$route.params.k
2、query 传参(多参数)
- 路由:无需特殊配置
{
path:'/user', component:xxx} // 忽略
- 跳转:
携带查询参数
<router-link to='/user/?k=val'></router-link> // 标签方式跳转
this.$router.push('/user/?k=val') // JS 方式跳转,简洁版
this.$router.push({
// JS 方式跳转,复杂版
path:'/user',
query:{
k:val},
params:{
k:val} // params 被忽略
})
- 参数获取
this.$route.query.id
3、params 传参(了解)
- 路由:额外
配置 name
{
path:'/user', name:'user', component:xxx} // 配置 name
- 跳转:通过 name 跳转
this.$router.push({
name:'user',
query:{
k:val},
params:{
k:val}
})
- 参数获取:基于内存传参,刷新丢失
this.$route.params.k // 刷新会丢失,不显示在地址栏,基于内存
十一、Vuex
(一)概述
- vuex:vue 的状态(数据)管理工具,集中管理 vue 中
通用
的数据 - 作用:解决
多组件状态共享
问题 - 优点
- 响应式变化
- 操作更简洁
(二)基本使用
1、创建仓库
- 安装
yarn add vuex@3.x.x
- 新建 store/index.js 存放 vuex
- 创建仓库 store/index.js
// 导入 vue
import Vue from 'vue'
// 导入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 进行插件的安装初始化
Vue.use(Vuex)
// 创建仓库 store,基于Vuex里的Store构造函数创建仓库实例
const store = new Vuex.Store()
// 导出仓库
export default store
- 在 main.js 中导入挂载到 Vue 实例上
import 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')
此刻起, 就成功创建了一个 空仓库!!
2、state 状态
(1)基本使用
- 定义:存放数据的仓库
- 特点:State 提供唯一
公共数据源
,所有共享数据都统一放到 Store 中的 State 存储- data:组件内数据
- state:vue 项目公共数据
- 提供数据
// 创建仓库 store
const store = new Vuex.Store({
state: {
count: 101
}
})
(2)原生语法- 插值表达式
- 获取数据:
this.$store.state.属性
<h1>state 的数据 - {
{
$store.state.count }}</h1> // 原始复杂写法
- 将 state 属性定义在计算属性中,简化插值语法
<h1>state 的数据 - {
{
count }}</h1> // 简化后的插值语法
computed: {
count () {
// 可以使用 mapState 辅助函数简化
return this.$store.state.count
}
}
(3)辅助函数 - mapState
- 获取数据:mapState 辅助函数将 store 中的数据映射到组件的计算属性
- 导入 mapState
import {
mapState } from 'vuex'
<h1>state 的数据 - {
{
count }}</h1> // 简化后的插值语法
computed: {
...mapState(['count']) // 利用展开运算符将导出的状态映射给计算属性,
// 防止 mapState 辅助函数独占计算属性
}
3、mutations
(1)基本使用
- 定义:存放操作数据的方法
- 背景:vuex 遵循单向数据流,state 数据修改只能通过 mutations,且 mutations 必须是同步的
- 提供方法
// 提供 mutations 函数
const store = new Vuex.Store({
state: {
count: 0
},
// 定义mutations
mutations: {
// 第一个参数是当前store的state属性(固定)
addOne (state {
state.count ++ // 通过形参 state 拿到仓库 store 数据
}
},
})
// 调用方法
this.$store.commit('addOne')
(2)携带参数
- 提供 mutations 方法
const store = new Vuex.Store({
state: {
count: 0
},
// 定义mutations
mutations: {
// 第一个参数是当前store的state属性(固定)
addOne (state {
state.count ++ // 通过形参 state 拿到仓库 store 数据
}
// 第二个参数payload额外参数,调用mutaiions的时候传参(一次提交仅限携带一个参数,但参数类型不限,支持数组、对象等)
addNth (state, payload) {
state.count += payload
}
},
})
- 注册事件
<input type="text" :value="count" @input="fn">
- 调用方法
fn(e){
this.$store.commit('addNth', +e.target.value) // 请求参数
}
(3)辅助函数 - mapMutations
- 原代码
methods: {
subOne () {
this.$store.commit('subOne')
},
subNth (n) {
this.$store.commit('subOne', n) // commit(方法名, 载荷参数)
}
}
- 导入 mapMutations
import {
mapMutations } from 'vuex'
methods: {
...mapMutations(['subOne', 'subNth'])
}
- 调用
<button @click="subOne">值+1</button> // 无参数
<button @click="subNth(n)">值+1</button> // 携带参数
4、actions
(1)基本使用
- 定义:存放异步操作的方法,不能直接修改数据
actions: {
setAsyncCount (context, num) {
// 一秒后, 给一个数, 去修改 num
setTimeout(() => {
context.commit('inputCount', num)
}, 1000)
}
},
(2)原始调用
- $store (支持传参)
setAsyncCount () {
this.$store.dispatch('setAsyncCount', 200)
}
(3)辅助函数 - mapActions
import {
mapActions } from 'vuex'
<button @click="setAsyncCount(200)">+异步</button>
methods: {
...mapActions(['setAsyncCount'])
}
5、getters
(1)基本使用
- 定义:存放基于 state 的一些计算属性
state: {
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
},
getters: {
// getters函数的第一个参数是 state
// 必须要有返回值
filterList: state => state.list.filter(item => item > 5)
}
(2)原始方式 - $store
<div>{
{ $store.getters.filterList }}</div>
(3)辅助函数 - mapGetters
<div>{
{
filterList }}</div>
computed: {
...mapGetters(['filterList'])
}
6、模块 modules(重要)
- 定义:为解决 store 对象臃肿问题,建立 @/store/module/xx.js 进行 Vuex 的模块化
(1)模块定义
- 准备 state,定义两个模块 user 和 setting
- user 中管理用户的信息状态 userInfo
modules/user.js
const state = {
userInfo: {
name: 'zs',
age: 18
}
}
const mutations = {
}
const actions = {
}
const getters = {
}
export default {
state,
mutations,
actions,
getters
}
- setting 中管理项目应用的名称 title, desc
modules/setting.js
const state = {
title: '这是大标题',
desc: '描述真呀真不错'
}
const mutations = {
}
const actions = {
}
const getters = {
}
export default {
state,
mutations,
actions,
getters
}
- 使用模块中的数据
- 可以直接通过模块名访问
$store.state.模块名.xxx
=>$store.state.setting.title
- 可以通过 mapState 映射
- 可以直接通过模块名访问
(2)命名空间 namespaced
- 默认情况下,模块内部的 action、mutation 和 getter 是注册在
全局命名空间
,配置namespaced
保证内部模块的高封闭性
const state = {
userInfo: {
name: 'zs',
age: 18
},
myMsg: '我的数据'
}
const mutations = {
updateMsg (state, msg) {
state.myMsg = msg
}
}
const actions = {
}
const getters = {
}
export default {
namespaced: true, // 配置命名空间
state,
mutations,
actions,
getters
}
- 提交模块中的 mutation
全局: this.$store.commit('mutation函数名', 参数)
模块: this.$store.commit('模块名/mutation函数名', 参数)
- namespaced: true 后, 可以加上模块名添加映射, 找对应模块的 state/mutations/actions/getters
computed: {
// 全局的
...mapState(['count']),
// 模块中的
...mapState('user', ['myMsg']),
},
methods: {
// 全局的
...mapMutations(['addCount'])
// 模块中的
...mapMutations('user', ['updateMsg'])
}