Vue项目工程化扩展📶:
前言:当然既然学习框架的了,HTML+CSS+JS三件套必须的就不说了: JavaScript 快速入门
紧跟前文,目标学习Vue2.0------3.0: 懂个锤子Vue、WebPack5.0、WebPack高级进阶 涉及的技术栈...
学习前置链接: 懂个锤子Vue 项目工程化 懂个锤子Vue 项目工程化进阶⏫
- 🆗,上述学习了Vue组件的很多种通信方式,而除此之外还有很多隐藏的组件通信方式:
- 本篇:扩展一些Vue的常见面试题、特殊操作词原理:
v-model 详解
v-model
是 Vue 框架中的一个内置指令:
用于在表单元素,如: input
、textarea
和 select
)上创建双向数据绑定;
双向绑定: 指在视图View
和数据模型Model
之间建立的一种同步机制,通过这种机制:
- 当视图中的数据发生变化时,数据模型会自动更新,同样,当数据模型发生变化时,视图也会自动更新
- 双向同步的特性使得数据和视图之间的交互变得更加简便和高效;
它的本质是一种语法糖,简化了数据绑定和事件监听的过程:其原理:
-
数据绑定 :
v-model
将表单控件的值value
,绑定到 Vue 实例的数据属性; -
事件监听 :
v-model
监听用户对表单控件的输入事件,如input
事件,并在用户输入时自动更新数据属性的值; -
视图更新 :当数据属性的值发生变化时,
v-model
自动更新表单控件的值,确保视图和数据的同步;
html
<template>
<div id="app" style="border: 3px solid #000; margin: 10px">
<p>V-model 详解</p>
<p>v-model: <input v-model="msg" type="text"><br/></p> <!-- 双向绑定; -->
<p>原始input <input :value="msg2" type="text"><br/></p> <!-- 并没有双向绑定; -->
<p>自定义v-model <input :value="msg3" @input="msg3 = $event.target.value" type="text"><br/></p>
<!-- 在input中绑定value:value="vue数据属性" -->
<!-- input本身有个事件叫input, 用于监听value的值, 在input事件中监听并给 msg3 重新赋最新的值; 完成自定义双向绑定 -->
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg:'',
msg2:'',
msg3:'',
}
},
}
</script>
-
实际情况: 根据不同的表达元素会有不同的监听事件,
输入表单: @input
、单选表单: @change=""
-
$event.target.value
$event: 这是一个特殊变量,代表当前事件对象,.target: 事件对象的一个属性,它指向的是触发事件的那个具体的DOM元素
.value : 当这个表达式用在表单元素上时,它返回的是该表单元素的当前值:
value
表单类组件封装
表单类型组件的封装是前端开发中提高代码复用性和可维护性的重要实践: but,随着UI框架丰富,实际开发接触不多
在Vue.js项目中,结合Element UI这样的UI库,封装可以更加高效,自定义特有的样式风格;
-
提高可维护性:组件化使得每个表单项独立,修改一处不影响其他部分
-
简化开发:减少重复代码,通过配置即可生成不同的表单元素
-
增强协作:团队成员可以独立开发各自的组件,减少合并冲突
-
统一风格:确保整个应用的表单样式一致,增强用户体验
封装表单下拉框组件:
Demo 封装一个自定义表单:表单下拉框组件:
-
自定义表单组件:
/components/menu/BaseSelect.vue
场景: 查询城市、区县下拉项目中很多地方都会使用,为了方便管理通常定义为一个组件,统一管理数据、样式;
-
父组件调用: 子组件,及传递数据,部分情况如:修改信息会传递修改前的信息进行展示,默认情况也不会传递;
-
子组件传递数据: 用户修改表单,表单监听输入,子传父------重新修改父组件值;
自定义组件: /components/menu/BaseSelect.vue
html
<template>
<div>
<!-- 绑定 :value 值
监听change事件: 获取值并传递给父组件 -->
<select :value="selectId" @change="selectCity">
<!-- 组件内加载的下拉数据: -->
<option value="101">北京</option>
<option value="102">上海</option>
<option value="103">武汉</option>
<option value="104">广州</option>
<option value="105">深圳</option>
</select>
</div>
</template>
<script>
export default {
//获取父组件传递的默认值;
props: { selectId: String, },
methods: {
//@change:用户修改触发事件
selectCity(e) {
this.$emit('changeCity', e.target.value); //向父组件传递 新数据;
},
},
}
</script>
<style>/* 设置自定义表单样式 */</style>
App.vue 主组件:
html
<template>
<div id="app">
<!-- 父组件传递数据:
:selectId 父组件在子组件上,注册的 自定义属性传值;
父组件通过在: 子组件标签上监听自定义事件,并绑定 获取值同步修改selectId;
-->
<BaseSelect :selectId="selectId" @changeCity="selectId = $event" ></BaseSelect>
</div>
</template>
<script>
import BaseSelect from './components/menu/BaseSelect.vue'
export default {
name: 'App',
components:{ BaseSelect },
data() { return { selectId: '102', } }, //默认选择102
}
</script>
v-model 表单下拉框组件:
父组件通过:v-model传递组件数据: 实现子组件和父组件数据双向绑定;
-
父组件中: 使用
v-model
给组件直接绑数据,因为v-model本质是::value + @input
所以:
v-model
相当于调用子组件配置:<BaseSelect :value="selectId" @input="selecteId = $event" />
-
所以:子组件中: 必须使用
props
通过value(必须)
接收,事件触发名input(必须)
<BaseSelect v-model="selectId" ></BaseSelect>
相当于:
<BaseSelect :value="selectId" @input="selectId = $event" ></BaseSelect>
**要注意:与,子组件配合使用哦!
.sync 修饰符
.sync
修饰符是Vue.js
中用于实现父子组件间数据:双向绑定的一种特殊语法糖🍬 主要在Vue 2.x版本中使用:
特别是在需要:子组件能够直接修改父组件状态的场景下:
-
简化双向数据流:.sync提供了一种更简洁的方式来实现子组件向父组件传递更新,避免了手动触发事件和监听的繁琐过程;
-
维护数据流向:虽然Vue推崇单向数据流,但在某些复杂场景下,需要子组件能够影响父组件的状态
.sync提供了一种控制这种需求的方法,同时保持代码的清晰;
.sync 修饰符使用:
.sync 和 v-model 使用类似: 但,相比v-model
更高级方便,支持自定义:父子组件传递属性值
- 假设我们有一个父组件和一个子组件: 父组件引入子组件,并设置传递属性|值:
<子组件 :属性名.sync="xxx" ></子组件>
- 子组件通过:
props:['属性名']
获取父组件传递值,如需传递|修改父组件数据: - 子组件通过:
this.$emit('update:属性名', "传递值");
更新修改父组件数据;
v-model: 实现组件双向绑定,固定了父子传递属性value
、监听事件名对应表单的修改事件名
;
.sync: 可以不局限表单组件,且支持自定义属性名,监听事件名@update:属性名
,相比之下更加,灵活方便;
封装弹框类的基础组件:
Demo场景: 封装弹框类的基础组件,使用visible
属性 true|false
显示|隐藏,组件;
.sync 与 v-model 区别:
.sync
修饰符和 v-model
都是Vue.js中用于实现组件间数据绑定的机制:
.sync修饰符:
.sync主要用于父子组件间的双向数据绑定, 特别是在Vue 2.x中;
它允许子组件修改父组件传递的属性值,通过触发一个特定的事件,通常是update:属性名
来实现;
可以用于: 多个属性,实现对多个数据项的双向绑定 ,不限于特定类型的元素或组件,适用于任何需要双向数据流的场景;
v-model
v-model
是Vue提供的一个指令,主要用于表单输入元素,实现数据的双向绑定;
对于原生表单元素: v-model通常等价于value属性绑定和对应的输入事件监听;
对于自定义组件: v-model内部转换为value的prop和input事件的监听;
在一个组件中只能有一个v-model,因为它代表单一的数据绑定点;
固定了父------子组件传递值:value
总结:
适用场景:v-model更适合简单的表单输入双向绑定,而.sync适用于需要子组件修改父组件数据的复杂场景
版本差异:在Vue 3中,.sync已被废弃,推荐使用Composition API中的方法来实现类似功能
而v-model依然存在,且支持更广泛的类型和自定义行为
灵活性:.sync提供了更灵活的双向绑定方式,尤其是在需要子组件影响父组件状态时
而v-model则更专注于简化用户体验设计中的数据绑定
ref 和 $refs:
在Vue框架中,ref
和$refs
是用于: 访问、操作组件内部、子组件的DOM元素及实例的关键特性:
ref是一个属性: 可以被添加到Vue模板中的元素、组件上:
-
元素上: 当应用在普通的HTML元素上时,通过
this.$refs
可以访问到该元素的DOM
节点; -
组件上: 当应用在子组件上时,
this.$refs
将指向:该子组件的实例,允许你调用其方法或访问其数据;超级厉害的一个功能: 但注意:可以获取子组件的实例、属性、函数....,但并不能修改!!
$refs是一个对象,它包含了所有通过ref定义的引用:
- 重要的是要注意:
$refs
中的引用在DOM渲染完成后才可用,因此通常在:mounted()
钩子中访问,确保元素\组件存在; - 当在v-for循环中使用ref时: 如果引用名相同,
this.$refs[refName]
会返回一个包含所有对应元素或组件的数组;
ref 和 $refs 获取DOM:
通常情况,我们想要获取一个DOM元素:使用JS的 document.querySelector('选择器..');
这就有一个问题,如果存在相同选择器,就会获取到多个元素,而无法准确的获取某个DOM,当然可以通过设置ID选择器
实际开发中,并不建议设置特别多的ID,且组件化开发: 最后会将组件,合并为一个html
页面,导致无法准确获取对应DOM;
ref
属性类似于ID,定义在元素属性上:<元素 ref="属性x" ></元素>
- JS中通过
this.$refs.属性x
可快速访问到该元素的DOM
节点,匹配最后一个匹配的属性;
html
<template>
<div>
<p class="w" ref="w" >组件中的p属性</p>
<p ref="w" >组件中的p2属性</p>
</div>
</template>
<script>
export default {
//通常在:`mounted()` 钩子中访问,确保元素\组件存在;
mounted() {
//子组件中的P标签 和 父组件中P标签 相同的属性;
//因为: 组件最终会合并为一个html页面所以,会发送元素冲突情况: 获取class="w" DOM设置样式;
var mydom = document.querySelector('.w');
mydom.style.fontSize = '30px'; // 设置字体大小
mydom.style.fontWeight = 'bold'; // 设置字体加粗
//使用 $refs 获取Dom
var mydom2 = this.$refs.w;
mydom2.style.fontSize = '30px'; // 设置字体大小
mydom2.style.fontWeight = 'bold'; // 设置字体加粗
}
}
</script>
<style>/* 设置自定义表单样式 */</style>
ref 和 $refs 获取组件:
ref 和 $refs最强大之处莫过于: 直接获取组件的实例、属性、函数;
- 父组件引用子组件: 并在子组件上定义
ref
值:<子组件 ref="属性x" ></子组件>
- 即可在父组件:mounted钩子函数中:
this.$refs.属性x;
直接获取子组件实例,并通过实例获取:实例对象;
主组件$refs
调用 子组件:
html
<template>
<div id="app">
<!-- 干扰组件,获取Dom -->
<p class="w" >干扰组件,获取Dom</p>
<BaseStr ref="ww" ></BaseStr><!-- 引入组件 -->
</div>
</template>
<script>
import BaseStr from './components/menu/BaseStr.vue'
export default {
name: 'App',
components:{ BaseStr },
mounted(){
//获取子组件实例:
var myzzj = this.$refs.ww;
console.log(myzzj.str);
myzzj.show();
}
}
</script>
Vue异步更新、$nextTick
Vue的异步更新机制和$nextTick方法是其核心特性之一:
用于优化:DOM更新的性能,管理数据变化与视图更新之间的关系;
异步更新机制
Vue采用异步更新策略来处理数据变化与DOM的同步:
-
当数据发生变化时,Vue并不会立即更新视图,而是将这些变更放入一个队列中;
-
这个队列会在当前
JavaScript
执行环境的事件循环结束之后,或在下一个宏任务如:setTimeout、setInterval、I/O完成等之前被处理:目的是合并多个数据变化,减少不必要的DOM操作,提高性能;
同时也导致一些问题: 由于数据变化和视图更新不是即时的,这可能导致调试时的逻辑断层
:
Demo需求: 点击页面编辑按钮,显示一个输入框,并立即获取编辑框的焦点
,因为异步更新机制: 立刻获取焦点失败!
$nextTick 解决逻辑断层:
$nextTick是一个方法,它允许开发者指定一个回调函数: 该函数将在Vue完成其当前的DOM更新周期后执行:
- 这意味着,当你在数据变化之后立即需要访问更新后的DOM时
- 可以使用
$nextTick
来确保你的代码在DOM
已经根据最新的数据渲染之后执行, - 可以在组件生命周期钩子中,如
mounted()
或updated()
,确保DOM已经更新后再执行某些操作;
编辑按钮\显示输入框\立即获取焦点
在更新数据的函数中,定义:$nextTick回调函数;
当函数内操作的数据修改后,等待DOM更新执行回调函数,完成对DOM的操作,解决了页面逻辑断层渲染问题;
html
<template>
<div class="app">
<!-- 默认不展示编辑框 -->
<div v-if="isShowEdit"> <input type="text" v-model="editValue" ref="inp" /> <button>确认</button> </div>
<div v-else><button @click="editFn">编辑</button></div>
</div>
</template>
<script>
export default {
data() {
return { isShowEdit: false, editValue: '', } //默认不展示编辑框
},
methods: {
editFn() {
// 1.显示文本框
this.isShowEdit = true //修改data数据 显示编辑框
// 2.让文本框聚焦\$nextTick等dom更新完之后 立马执行nextTick中的回调函数
// this.$refs.inp.focus() //使用: $refs 获取指定的DOM元素focus()设置焦点,因为异步刷新机制Dom并不会立刻更新;
this.$nextTick(() => {
console.log(this.$refs.inp)
this.$refs.inp.focus()
})
},
},
}
</script>
<style></style>
$nextTick还可以使用微任务,如:Promise、MutationObserver
或宏任务如:setTimeout,优先使用微任务来实现;
代码管理:
本代码已经使用Git进行管理: 公众号回复:Vue项目工程化