vue——插槽v-slot、组件的自定义事件、网络请求、面试题相关(数据的劫持顺序、单向数据流、DIFF算法)

温故知新:

组件的认识:

  • 组件是可复用的
  • 把组件当做一种标签使用 使用一次 它内部的代码就会完整的执行一次
  • 类比函数 使用组件就相当于调用函数 实参就相当于组件的属性传值
  • 不用把数据全部放在数据源中,会变化更新或网络请求的数据才放在data中,固定的数据直接写在模板标签中

关于项目的资源打包问题:

  热更新服务:将打包后的静态项目在内存中托管起来

  项目中的本地资源打包时:

  • 引用文件的资源(import)、标签中引入的资源(img-src)会启用loder去加载资源  会被webpack打包
  • 放在data数据源中的网址对应的资源(如把图片等本地资源的路径放在data中)  webpack不会读取data 所以webpack不打包
  • data数据源中的图片应该放网络图片

编辑器写代码有波浪线

v-for:不写key会有警告

解决:绑定一个key 如果没有id就绑定for循环的下标 或者忽略不管没影响

标签的其他地方 命令窗口报错

解决:关闭webpack中配置的eslint的严格检测模式:lintOnSave:false

一、插槽v-slot

v-slot:插槽名    是具名插槽的用法  

       #s1            是插槽的语法糖
没有指定插槽名就是默认插入到插槽,不给插槽插入数据的话,就会使用组件的slot中的数据
插槽名不用使用引号引起来,直接写变量名
插入的内容必须是template标签或者组件 不能是原生的元素

eg:

设计组件里面:

  • 默认槽位:< slot> < /slot>
  • 具名槽位:< slot name="s1"> < /slot>

使用组件时:

< 组件名> 尖括号中的东西插入默认槽位 < /组件名>

< 组件名>

   < template v-slot:s1>插入内容必须放在这个标签中,老版本不用< template>

   < template #s1>插入内容必须放在这个标签中,老版本不用< template>

< /组件名>

(1)组件的属性传值:

   App.vue文件:

<template>
	<div class="appbox">
		<Box1 :msg="n1" :title="n2"></Box1>
		<Box1 msg="msg1" title="标题1"></Box1>	
	</div>
</template>

<script>
	import Box1 from "@/components/Box1.vue"
	export default {
		data() {
			return {
				n1: "app组件传给box组件的数据",
				n2: "app组件传给box的标题"
			}
		},
		components: {
			Box1,
		}
	}
</script>

<style scoped="scoped">
	.appbox {
		width: 500px;
		min-height: 300px;
		background-color: honeydew;
		padding: 0px;
		border: 1px honeydew solid;
	}
</style>

  Box1.vue文件:

<template>
	<div class="box">
		<h1>{{title}}</h1>
		<div>{{msg}}</div>		
	</div>
</template>
<script>
	export default {
		props:["msg","title"]
	}
</script>
<style scoped="scoped">
	.box {
		width:400px;
		min-height: 100px;
		background-color: darkgray;
		margin: 20px;
	}
</style>

 结果显示:

(2)插槽

父组件使用子组件时要在子组件中插入内容 在子组件中就要用插槽来装 否则不会显示出来

插槽的位置在哪 插入的内容就在哪

   App.vue文件:

<template>
	<div class="appbox">
		<Box2 msg="msg2" title="标题2">
			<b>app组件的插槽数据</b>
 <!-- b标签是App组件的而非Box2的 只是通过插槽的方式插入到了Box2的模板中去-->
		</Box2>

		<Box2 msg="msg2" title="标题2">
			<img src="./img/9.png">
		</Box2>

		<Box2 msg="msg2" title="标题2">
			6666
		</Box2>

		<!-- 这种写法 并非嵌套 也不是父子组件关系 -->
		<Box2 msg="msg2" title="标题2">
			<Box3></Box3>
		</Box2>
	</div>
</template>

<script>
	import Box2 from "@/components/Box2.vue"
    import Box3 from "@/components/Box3.vue"
	export default {
		data() {
			return {
			}
		},
		components: {
			Box2,
            Box3
		}
	}
</script>

<style scoped="scoped">
	.appbox {
		width: 500px;
		min-height: 300px;
		background-color: honeydew;
		padding: 0px;
		border: 1px honeydew solid;
	}
</style>

    Box2.vue文件:

<template>
	<div class="box">
		<!-- 插槽的槽位 -->
		<div class="s">
			<slot></slot>
		</div>
		<h1>{{title}}</h1>
		<div>{{msg}}</div>
	</div>
</template>

<script>
	export default {
		props: ["msg", "title"]
	}
</script>

<style scoped="scoped">
	.box {
		width: 400px;
		min-height: 100px;
		background-color: goldenrod;
		margin: 20px;
	}
	.s {
		width: 60px;
		height: 60px;
		background-color: ghostwhite;
	}
	.s img {
		width: 60px;
		height: 60px;
	}
</style>

     Box3.vue文件:

<template>
	<i>box3</i>
</template>

 结果显示:

(3)插槽

   App.vue文件:

<template>
	<div class="appbox">
		<Box4 title="hello">
			<template v-slot:s1>
				<b>111111</b>
			</template>
			<template v-slot:s2>
				<b>2222</b>
			</template>
		</Box4>
		
		<Box4 title="helloBox4">
			<template v-slot:s2>
				<b>222222</b>
			</template>
			<template v-slot:s1>
				<b>111111</b>
			</template>		
		</Box4>
		
		
		<Box4 title="helloBox4">
			<template v-slot:s1>
				<b>222222</b>
			</template>
			<template v-slot:s2>
				<b>111111</b>
			</template>		
		</Box4>
		
		<Box4 title="helloBox4">
			<i>Box4</i>	
			<template #s1>
				<b>222222</b>
			</template>
			<template #s2>
				<b>111111</b>
			</template>			
		</Box4>
	</div>
</template>

<script>
	import Box4 from "@/components/Box4.vue"
	export default {
		data() {
			return {
			}
		},
		components: {
			Box4,
		}
	}
</script>

<style scoped="scoped">
	.appbox {
		width: 500px;
		min-height: 300px;
		background-color: honeydew;
		padding: 0px;
		border: 1px honeydew solid;
	}
</style>

   Box4.vue文件:

<template>
	<div>
		<slot name="s1"></slot>
		<h2>{{title}}</h2>
		<slot name="s2"></slot>
        <slot name="s2">不传数据到s2插槽中 就会默认显示这段文本出来</slot>
		<slot></slot>
	</div>
</template>

<script>
	export default {
		props:["title"]
	}
</script>

<style>
</style>

 结果显示:

二、组件的自定义事件

  • 事件的三要素: 事件源 事件类型 事件监听器

如:<box v-on:myevent="fn"></box>

事件源target:box组件

事件类型type:myevent

使用组件时 绑定监听器,内部触发事件时 监听器就会调用

1.在原生组件(就是html标签)中  事件是由系统来设计触发条件的:

 如:<div @click="fn">点我</div>

  App.vue文件:

<template>
	<div>
		<button @click="clicked">点击</button>
	</div>
</template>

<script>
	export default {
		methods:{
			clicked(){
				console.log("原生事件触发了")
			}
		}
	}
</script>

结果显示: 

2.在自定义组件中,事件是由自己来设计什么时候触发:

绑定事件:
<Box v-on:myevent="fn"></Box>

  • Box组件是事件源  
  • myevent是Box组件绑定的事件类型  
  • fn是Box组件上面绑定的监听器

事件设计:
在Box组件内部,可以在想要的条件下去触发事件

以下代码放在想触发自定义事件的地方 
this.$emit("myevent","要给触发的事件的函数传入的参数")

(1)自定义事件

   App.vue文件:

<template>
	<div>
		<Box v-on:myevent="fn"></Box>
	</div>
</template>

<script>
	import Box from "./Box.vue"
	export default {
		methods:{
			fn(){
			//fn在myevent事件触发后就会运行 
			//myevent由Box组件内部自己设计规定什么时候触发
				console.log("自定义事件触发,执行了fn函数")
			},
		},
		components:{
			Box,
		}
	}
</script>

  Box.vue文件:

<template>
	<div>
		<button @click="add">增加</button>
		<p>{{count}}</p>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				count: 0
			}
		},
		methods:{
			add(){
				this.count++
			}
		},
       //侦听器:侦听count的值的变化  当count的值为5时 触发组件内的事件
		watch:{
			count(v){
				if(v==5){
					//触发组件内的事件
					this.$emit("myevent")
				}
			}
		},
       //挂载完毕后就触发组件内的事件
		mounted(){
			this.$emit("myevent")
		}
	}
</script>

  结果显示: 

(2)组件中写的只能是自定义事件 就算与系统原生事件一模一样的写法也不能执行

   App.vue文件:

<template>
	<div>
		<Box2 v-on:click="fn2"></Box2>
	</div>
</template>

<script>
	import Box2 from "./Box2.vue"
	export default {
		methods:{
			fn2(){
				console.log("Box2的自定义事件")
			},
		},
		components:{
			Box2,
		}
	}
</script>

  Box2.vue文件:

<template>
	<div>
		<button @click="x">box2</button>
	</div>
</template>

<script>
	export default {
		methods:{
			x(){
				this.$emit("click")
			}
		}
	}
</script>

  结果显示: 

3.希望组件绑定原生事件(事件的触发条件设计由系统设计) 

给事件绑定事件修饰符   .native
<mydiv @click.native="fn">点我</mydiv>//事件名必须是系统存在的事件

   App.vue文件:

<template>
	<div>
		<Box3 v-on:click.native="fn3"></Box3>
	</div>
</template>

<script>
	import Box3 from "./Box3.vue"
	export default {
		methods:{
			fn3(){
				console.log("自定义事件写法的原生事件")
			},
		},
		components:{
			Box3,
		}
	}
</script>

   Box3.vue文件:

<template>
	<div>
		<p>box3</p>
		<button>box3</button>
	</div>
</template>

  结果显示:

 三、组件中的网络请求

  配置代理(解决跨域问题):vue的8080服务器请求egg的7001服务器

  vue.config.js中:

devServer: {
    //代理配置,这里只是配置,不用写代理服务器的代码(配置好了它帮我们实现)
    // proxy: {
    // 	'/xxxx': 'http://localhost:7001',
    // },
    proxy: {
      '/xxxx': {
        target: 'http://ip:7001',
        secure: true, //如果代理的target是https接口,需要配置它 
        pathRewrite: {
          '^/xxxx': '/'
        }, //请求时重写pathname
        //如果项目中实际请求的是:http://ip:8080/xxxx/ajax1
        //8080服务器就会帮我们代理 请求:http://ip:7001/ajax1
      },
    },
  }

(1)在组件中引入axios模块 

   App.vue文件:

<template>
	<div>
		<div>
			<h2>{{obj.title}}</h2>
			<p>{{obj.info}}</p>
		</div>
	</div>
</template>

<script>
	import axios from "axios"//去node_modules中找到的axios文件夹中
	export default {
		data() {
			return {
				obj:{}
			}
		},
		components:{},
		async mounted() {
			// let res=await axios("http://ip/ajax1")
			let res=await axios("http://ip:8080/xxxx/ajax1")
			// let res=await axios("/xxxx/ajax1")
			//==>实际请求的是:http://http://ip:8080/xxxx/ajax1
			//==>希望8080服务器帮我们代理 请求:http://http://ip:7001/ajax1
			console.log(res)
			this.obj=res.data
		}
	}
</script>

 结果显示:

(2)配置公共url:

  main.js中:

  在组件的原型链上配置axios工具,并配置公共网址:

import axios from "axios"
Vue.prototype.$axios=axios
axios.defaults.baseURL="http://ip:8001/xxxx"
//配置公共url 如果这个axios去请求 ajax1 实际网址是:http://ip:8001/xxxx/ajax1
//做网络请求都是基于baseURL
//代理去请求:http://ip:7001/ajax1

    App.vue文件:

<template>
	<div>
		<div>
			<h2>{{obj.title}}</h2>
			<p>{{obj.info}}</p>
		</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				obj:{}
			}
		},
		components:{},
		async mounted() {
		// let res=await this.$axios("http://ip:8080/xxxx/ajax1")
           let res=await this.$axios("ajax1")
           //==>基于baseurl:"http://ip:8080/xxxx/ajax1"
			console.log(res)
			this.obj=res.data
		}
	}
</script>

 显示结果同上

四、面试题相关

1、组件中的data设计为函数的原因:

1.组件被复用时,data数据源才不会被共用 设计成对象就会被共用 调函数调一次生成一次

2.函数的设计就像懒加载一样 当组件使用时,数据源的对象才会创建 这样设计性能更好

 2、数据劫持的顺序:

this组件对象有很多属性和方法 都是劫持“别人”的:如 data methods props

给this设置成员(初始化)的顺序:属性props>方法methods>数据源data>计算属性computed>对属性监听watch

3、单向数据流:

数据由 根组件-->父组件-->子组件  ,单向修改,不能反向修改

父组件通过属性传值给子组件,子组件内部修改数据,父组件的数据不改变;父组件修改数据,会重新传值给子组件,子组件的数据更新改变

即:

  • 在父传子的前提下,父组件的数据发生会通知子组件自动更新
  • 子组件内部,不能直接修改父组件传递过来的props => props是只读的

 4、vue加载流程

每一个组件在加载时都会调用vue内部的render函数把这个组件的template选项的模板解析为一个js对象,这个对象和DOM节点对象“长得一模一样”,就是为了后面的渲染。调render生成虚拟模板VNode.

然后是数据劫持代理监听等等:

底层设计:发布着/订阅者设计 其实就是写了一个watcher函数去订阅(监听)数据的改变(底层js语法就是Obj.defineproperty,vue3是proxy)

当数据变化以后:

当数据变更的时候,重新构造一颗新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树的差异。当第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图更新。(DIFF算法)

比较的过程是 一层一层的比较 父组件和父组件比较 并不会把父组件和子组件比较 同层级时 从两边到中间

比较的过程中 发现差异(组件/标签类型,文本,属性值。注释等) 就会异步地给DOM打补丁(操作页面)

5、DIFF算法

什么是DIFF?

用JavaScript对象结构表示DOM树的结构,然后用这个树构建一个真正的DOM树,插到文档中

当状态变更的时候,重新构建一颗新的对象树。然后用新的树和就的树进行比较(diff),记录两棵树的差异

当第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图更新。

DIFF算法的过程
当数据发生改变时,订阅者watcher就会调用patch给真实的DOM打补丁
通过sameVnode进行判断,相同则调用patchVnode方法
patchVnode做了以下操作:
找到对应的真实dom,称为el
如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点
如果oldVnode有子节点而VNode没有,则删除el子节点
如果oldVnode没有子节点而VNode有,则将VNode的子节点真实化后添加到el
如果两者都有子节点,则执行updateChildren函数比较子节点
updateChildren主要做了以下操作:
设置新旧VNode的头尾指针
新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点,从哈希表寻找 key一致的VNode 节点再分情况操作

 6、在.vue文件中写Sass/SCSS、Less

1.引入外部写的Sass编译后的css文件

2.配置sass的加载器

<style lang="scss">

     在里面写scss代码

</style>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈哈ha~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值