Vue项目工程化进阶⏫:
前言:紧跟前文,目标学习Vue2.0——3.0:懂个锤子Vue、WebPack5.0、WebPack高级进阶 涉及的技术栈…
当然既然学习框架的了,HTML+CSS+JS三件套必须的就不说了:JavaScript 快速入门
学习前置链接:懂个锤子Vue 项目工程化
组件的样式冲突:
在 Vue 开发中,组件样式冲突是一个常见的问题:为什么会发生组件样式冲突⁉️
我们都知道,组件是由:模板Template\脚本Script\样式Style组成,每个组件都是有自己的三件套)
但,因为Vue在运行过程中,本质是将多个组件合并成一个:html文件
所以,多个组件样式,相互匹配就发生组件样式冲突:
解决样式冲突
Vue支持定义全局\局部样式:
默认情况:组件中的样式会,全局生效→ 很容易造成多个组件之间的样式冲突问题,
- 全局样式:
默认组件中的样式会作用到全局 - 局部样式:可以给组件加上 scoped 属性, 可以让样式只作用于当前组件
scoped原理
scoped可以让组件样式变成局部样式:组件都应该有独立的样式
scoped:给当前组件模板的所有元素,都会添加上一个自定义属性data-v-hash值data-v-hash值:用于区分开不通的组件,渲染样式时css选择器后面,被自动处理,添加上了属性选择器;
组件的 data函数:
在 Vue 组件中,data函数的主要作用是为每个组件实例提供独立的数据对象:
- 因为: 一个组件可能会使用多次,如果通过原始属性定义,多个组件会依赖一个数据源;
- 通过:
date(函数){ return {} }return返回对象,确保每一个组件都是独立的数据源;
访问组件实例:在data(){ //函数内部 }你可以访问组件实例的其他属性和方法;
响应式系统:Vue响应式系统,会将data 函数返回的对象包裹起来,
并以$data的形式存储在组件实例;数据发生变化,自动更新视图;
<!-- 模板结构 --><template><div><P>A组件: 声明成功!</P><button@click="count--">-</button><span>{{ count }}</span><button@click="count++">+</button></div></template><!-- 脚本结构 --><script>exportdefault{//原始定义:组件不支持\报错x)// data: {// //基本数据类型使用// count:1// },//组件函数式定义:data(){//因为: 一个组件可能会使用多次,如果通过原始属性,多个组件会依赖一个数据源;//通过: date(函数){ return {} } return返回对象,确保每一个组件都是独立的数据源;return{count:1}}}</script><!-- 样式结构 --><!-- style中的样式,默认是作用到全局的 scoped可以让样式变成局部样式,组件都应该有独立的样式,推荐scoped 原理) scoped给当前组件模板的所有元素,都会添加上一个自定义属性 ><stylescoped>div{border:3px solid red;margin:30px;}</style>组件的通信📶
Vue.js 是一个基于组件的框架,应用通常由多个组件组成:
组件通信是指在 Vue.js 应用中,不同组件之间传递数据和消息的过程:
组件之间的数据是独立的,无法直接访问其他组件的数据,组件通信的主要目的是:
- 数据共享:在父子组件之间或兄弟组件之间共享数据;
- 事件传递:在组件之间传递事件,以响应用户操作或其他事件;
组件之间有哪些关系?
父子关系:一个组件包含另一个组件
例如,
组件A包含组件B+组件C,那么 A 和 B、C 是父子关系;非父子关系:也有开发者称为
兄弟\隔代关系,同一个父组件下的多个子组件之间的关系;组件B和组件C都是组件A的子组件,那么 B 和 C 是兄弟关系;组件A包含组件B,组件B又包含组件D,那么 A 和 D 是隔代关系;
对应的组件通信方案有哪几类?这里简单介绍,常用的几种;
- Props 和 $emit:用于父子组件之间的通信;
- provide 和 inject:用于祖先组件和后代组件之间的通信;
- 事件总线(Event Bus):通过创建一个空的 Vue 实例作为中央事件总线,用它来触发和监听事件,实现任意组件间的通信;
- Vuex:用于全局状态管理,适用于大型应用,通过 Vuex 的 store 来管理全局状态,实现组件间的通信;
父子组件通信:
父—>子通信:
Props是父组件向子组件传递数据的机制,
父组件通过在子组件标签上绑定属性来传递数据,子组件通过声明props来接收这些数据;
- Prop 定义\使用:父组件在子组件上,注册的一些 自定义属性:
<组件名 :属性名='传递值' ></组件名> - 子组件通过:
props获取父组件的数据,props:['属性名'],props中的属性可以像data中数据一样使用;
props 详解\校验:
Props作用:向子组件传递数据,可以传递任意数量、可以传递任意类型
props校验是确保传递给组件的数据符合预期的一种机制:通过定义props的类型和验证规则,提高组件可靠性和可维护性;
'props':{'校验属性名1':'类型',// Number String Boolean ...'校验属性名2':{type:'类型',// Number String Boolean ...required:true,// 是否必填default:'默认值',// 默认值validator(value){// 自定义校验逻辑return是否通过校验;};}},data & props 区别:
在 Vue.js 中,props和data都是用于管理组件数据的重要机制,但它们有一些显著的区别:
data:用于组件内部管理数据,组件可以自由修改props:用于父组件向子组件传递数据,子组件只能读取,不能修改单向数据流:父级 props 的数据更新,会向下流动,影响子组件,这个数据流动是单向的
父组件更新
props,子组件会接收到最新的数据,但子组件不能直接修改props的值,子组件修改值报错⚡
检测到props中数据修改报错,Vue直接抛出异常!那么如何修改父组件的数据呢?
子—>父通信:
👆这就涉及到子——父通信了,那么如何:子—>父通信:
BaseCount.vue:子组件通过调用$emit方法触发一个@自定义事件,
并传递需要发送的数据:this.$emit('自定义事件名', 传递给父组件数据)
<!-- 模板结构 --><template><divclass="base-count"><button@click="handleSub">-</button><span>{{ count }}</span><button@click="handleAdd">+</button></div></template><!-- 脚本结构 --><script>exportdefault{//子组件data数据data(){return{dcount:0}},props:{//props获取父组件数据;count:{type:Number,},},methods:{handleSub(){//子组件通过调用 $emit 方法触发一个自定义事件,并传递需要发送的数据this.$emit('changeCount',this.count-1)},handleAdd(){this.$emit('changeCount',this.count+1)},},}</script><!-- 样式结构 --><style></style>App.vue:父组件通过在:子组件标签上自定义监听事件,并绑定一个函数来接收处理数据;
<子组件 :属性名='传递值' @自定义监听事件="处理函数" >,当子组件调用:this.$emit('自定义事件名', 传递父组件数据)
父组件监听事件触发,并执行对应处理函数,函数内部可以获取子组件传递的数据:处理函数(value){ //内部执行相应处理 }
<!-- 模板结构 --><template><divid="app"style="border:3px solid #000;margin:10px">App 父组件<!-- 加减组件 --><!-- 父组件通过在子组件标签上监听自定义事件,并绑定一个触发函数 --><BaseCount:count="count"@changeCount="handleChange"></BaseCount></div></template><!-- 脚本结构 --><script>importBaseCountfrom'./components/BaseCount.vue'exportdefault{name:'App',data(){return{count:1,}},components:{BaseCount},methods:{handleChange(newVal){//函数获取: 子组件传递的数据,给其赋值count;this.count=newVal;}}}</script><!-- 样式结构 --><style></style>- 用户点击:
+\-触发对应的子组件函数, - 子组件函数内部执行:
this.$emit('父组件自定义监听函数',传递值)给父组件传递修改后的值; - 父组件,事先定义子组件时:设置,自定义监听函数,并绑定对应处理函数;
- 父组件,监听
@自定义函数执行,并触发函数 - 获取子组件传递最新值,重新赋值:
count - 子组件页面同步数据更新;
非父子组件通信:
event bus 事件总线
在 Vue.js 中,事件总线(Event Bus)是一种用于实现非父子组件之间通信的机制
它允许我们在 Vue 应用中创建一个全局的事件中心,组件通过该事件中心来:发送\接收事件,实现数据的传递和共享
创建事件总线:
首先,我们需要创建一个事件总线:
- 通常是在主文件如
main.js,中创建一个新的 Vue 实例,并将其挂载到 Vue 的原型上,以便所有组件都可以访问; - 还有另一种方式是直接在组件中创建和使用事件总线,这种方法适用于局部通信需求,不需要全局事件总线;
//全局工具包//utils/EventBus.jsimportVuefrom'vue';constEventBus=newVue();exportdefaultEventBus;//对外暴漏公共实例;//main.js//const EventBus = new Vue();//Vue.prototype.$bus = EventBus;//将一个新的 Vue 实例即EventBus)挂载到Vue的原型上,并命名为 $bus//这样,所有的 Vue 组件都可以通过 this.$bus 访问这个事件总线;发送事件:
在需要发送事件的组件中,通过实例.$emit方法发送事件,并传递数据;
<template><divclass="base-b"><div>我是B组件(发布方)</div><button@click="sendMsgFn">发送消息1</button><button@click="sendMsgFn2">发送消息2</button></div></template><script>//引入局部事件总线importBusfrom'../utils/EventBus'exportdefault{methods:{//通过 `$bus.$emit` 方法发送事件,并传递数据;sendMsgFn(){Bus.$emit('sendMsg','引入局部事件总线')},sendMsgFn2(){this.$bus.$emit('sendMsg2','this.$Bus全局事件总线')},},}</script><stylescoped><!-- 省略Css样式 --></style>监听事件:
在需要接收事件的组件中,通过$bus.$on方法监听事件,并处理接收到的数据;
<template><divclass="base-a">我是A组件(接受方)<p>{{msg}}</p></div></template><script>importBusfrom'../utils/EventBus'exportdefault{data(){return{msg:'',}},created(){Bus.$on('sendMsg',(msg)=>{this.msg=msg});//接受局部事件总线消息this.$bus.$on('sendMsg2',(msg)=>{this.msg=msg});//接受全局事件总线消息},}</script><stylescoped><!-- 省略Css样式 --></style>注意事项
事件移除:在组件销毁前,应该移除事件监听,以避免内存泄漏和重复监听,
全局事件总线:虽然事件总线使用方便,但在大型应用中可能会导致难以维护的“灾难”,
因此需要谨慎使用,对于复杂的状态管理,推荐使用 Vuex:
provide & inject 跨层级共享数据
在 Vue.js 中,provide和inject是一种高级的数据传递机制:
主要用于解决组件树中深层组件间通信的问题,特别是当不直接存在父子关系时
它们允许父组件提供一个值,而任何后代组件都可以通过inject来接收这个值;如:上图D—A的隔代通信;
根组件发送消息:
父组件使用 provide 提供数据:父组件通过provide(){ return { 发送数据 } }选项将数据提供给子组件及其后代组件;
<template><divid="app"style="border:3px solid #000;margin:10px">App 父组件<Son3></Son3></div></template><script>importSon3from'./components/Son3.vue';exportdefault{name:'App',components:{Son3,},data(){return{color:'pink',userInfo:{name:'zs',age:18,},}},//父组件使用 provide 提供数据:provide(){return{// 简单类型是非响应式的color:this.color,// 复杂类型 是响应式的userInfo:this.userInfo,}},}</script>后代组件接受消息:
子组件使用 inject 接收数据:子组件通过inject: ['匹配数据名', '匹配数据名','...']选项注入这些数据;
<template><divclass="grandSon">我是GrandSon {{ color }} -{{ userInfo.name }} -{{ userInfo.age }}</div></template><script>exportdefault{//子组件使用 inject 接收数据:inject:['color','userInfo'],}</script>响应式数据、问题:
需要注意的是,provide提供的简单类型数据(如字符串、数字)是非响应式的,
而复杂类型数据(如对象、数组)是响应式的,这意味着子组件可以监听到复杂类型数据的变化;
版本管理:
本代码已经使用Git进行管理:公众号回复:Vue项目工程化