单文件组件
引入:@1官方脚手架 @2挂载vm对象 @3组件引入并渲染到vm中
单文件组件是单独写在.vue文件中的组件,每个.vue文件就是一个独立完整的组件,可以供别的组件引入注册使用.webpack有专门的vue加载器,能够识别vue文件,将其打包到项目中。
引入文件的路径:一般使用相对路径,也可以使用"@"符号表示入口根文件目录,即src文件夹。
<div id="app">
<nav1 son-proprety="子组件使用时属性传进去的值"></nav1>
<content1 img2src="./img/2.png"></content1>
</div>
<div id="app2">
<nav1 son-proprety="子组件使用时属性传进去的值2"></nav1>
<content1 img2src="./img/2.png"></content1>
</div>
<script type="text/javascript">
//祖册全局组件
Vue.component("nav1",{
data(){return {sondata:"子组件的数据"}},
template:`<div><h1>{{sondata}}</h1><p>{{sonProprety}}</p></div>`,
props:["sonProprety"]
})
// let content1={
// data(){return {img1:"./img/1.png"}},
// template:`<div><img :src="img1"/><img :src="img2src"/></div>`,
// props:["img2src"]
// }
new Vue({
el:"#app",
data:{},
components:{
// content1
//注册局部组件
content1:{
data(){return {img1:"./img/1.png"}},
template:`<div><img :src="img1"/><img :src="img2src"/></div>`,
props:["img2src"]
}
}
})
new Vue({
el:"#app2",
data:{}
})
</script>
组件的属性:
组件的属性可以用来接收父组件往子组件传递数据。
在父组件中使用子组件时,在标签上绑定子组件的属性,指定传递的数据,在子组件中注册对应的属性,就能接收到数据,子组件可以直接在插值表达式中插入该属性来使用这些数据。
//父组件
<template>
<div id="app">
<Box :title1="msg"></Box>
<Box :title1="msg2"></Box>
<Box3 :title="b3" :title2="b4" title3="父组件传的值" :title4="n"></Box3>
<SinaBox :data="arr[0]"></SinaBox>
</div>
</template>
<script>
import Box from "./Box.vue"
import Box2 from "./Box2.vue"
import Box3 from "./Box3.vue"
export default {
data(){
return {
arr:[{text:"xxx",created:"123123"}],
msg:"父组件的数据",
msg2:"父组件的数据2",
b3:"100",//"hello",
b4:100,//"hello",
n:4//3//"hello66666"
}
},
components:{
Box,
Box2,
Box3
}
}
</script>
//子组件
<template>
<div>
<h1>{{title}}--{{title2}}--{{title3}}--{{title4}}</h1>
</div>
</template>
<script>
export default {
//属性的类型验证
props:{
title:String,
title2:[String,Number],
title3:{
type:String,
default:"默认属性的值"
},
title4:{
validator:(v)=>{
return isNaN(v)?false:v%2==0
}
}
}
}
</script>
组件的属性的两种写法
- 简单声明:props:[“prop1”,“prop2”]
- 对属性做详细的描述
props: {
propA: Number, // 基础的类型检查 (`null` 匹配任何类型)
propB: [String, Number], // 多个可能的类型
propC: { type: String,
required: true // 必填的字符串
},
propD: { type: Number,
default: 100 // 带有默认值的数字
},
propE: { type: Object, // 带有默认值的对象或者数组填Array
default: function () { // 不建议直接填对象(因为对象直接量会一直占用内存),一般使用工厂函数,调用时才创建对象节省资源(面试)
return { message: 'hello' }
}
},
propF: {
validator: function (value) {// 自定义验证函数返回为true就代表数据符合我们规定
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
插槽
v-slot 插槽,可以将指定的组件插入到另一组件的对应位置。
语法:v-slot:插槽名 语法糖:#插槽名
如果没有指定插槽名会插入到默认插槽,没有插入数据会使用组件的slot中的数据
插入的内容必须是template标签或者组件,不能是原生的元素。
//设计组件: .vue组件文件中
<template>
<div class="content1">
<slot></slot>
<slot name="slot1"></slot>
<h1>{{contentData.title}}</h1>
<h2>{{contentData.dt}}</h2>
<slot name="slot3">你不给我数据到3号插槽中 我就会默认显示出来</slot>
<p>{{contentData.text}}</p>
<slot name="slot2"></slot>
</div>
</template>
<script>
export default{
props:{
contentData:{
type:Object,
default:()=>{return {title:"0",dt:"0",text:"0"}}
}
}
}
</script>
//使用组件: .vue页面文件中
<template>
<div>
<content2 :contentData="arr[1]">
<template #slot1>
<img src="../assets/28.jpg">
</template>
<template #slot2>
<p>我在外部插入插槽的数据,不是子组件中的数据,也不是属性传进去的数据</p>
</template>
<template v-slot:slot3>
666
</template>
<p>我并没有指定插入到哪里</p>
</content2>
</div>
</template>
//
插槽按照对应的插槽名进行插入,最终的显示顺序,不是在父组件中使用插槽的顺序,而是子组件中定义插槽的顺序。
组件的嵌套
.vue 文件既可以是一个页面,也可以是一个组件。可以被别的.vue文件引入,作为子组件使用。
组件的自定义事件和原生事件:
//1.在原生组件(就是html标签)中 事件是由系统来设计触发条件的:
<div @click="fn">点我</div>
//2.在自定义组件中,事件是由自己来设计什么时候触发的:
//绑定事件:
<mydiv @myclick="fn">点我</mydiv>
//事件设计:
//在mydiv组件内部,你可以在你想要的条件下去触发事件
this.$emit("myclick","要给触发的事件的函数传入值")//这个代码放在你想触发自定义事件的地方
//3.如果希望组件绑定原生事件(事件的触发条件设计由系统设计)
//给事件绑定事件修饰符 .native
<mydiv @click.native="fn">点我</mydiv>//事件名必须是系统存在的事件
组件面试题
1.template:组件的模板中只能有一个根节点
#############
2. 组件的data为什么是个函数然后返回对象,以前使用vm时都是个对象?
组件和挂载到界面的vm对象的区别,vm挂载到页面上时,触发了钩子函数的,data生成了,页面上使用的数据就是data容器中渲染上去的,而且页面只有一个vm对象,所以的vm生成完毕(mouted)时data必须存在
组件是引入和注册以后不一定使用的,比如for循环0次就是组件对象生成了的,但是使用0次,所以组件对象并没有使用自己的data容器去渲染数据,造成资源浪费,解决方案就是懒加载:当使用data时去调用,才生成data对象
组件的data 设计成function的用义:组件可以多次使用,每使用一次,函数被调用一次则创建出不同的数据对象,实现同名组件的数据可以相互独立
################
3.组件的data数据和属性同名怎么办
data和props中成员都会成为this组件对象的成员
如果同名了那么属性就会覆盖data,因此为了保证减少不必要BUG,一般不要把data和属性同名
vue的eslint检测工具也会给我们检测出来这种写法上的错误
##################
4.vue文件中有三个标签 能不能不写
可以不写style标签 也可以不写script标签 甚至写了script不写导出语法,是因为vue的加载器用webpack打包.vue文件时,帮我们做了处理
template标签必须要写,因为组件至少要有一个关联模板或者render函数来生成虚拟节点
###################
5. 如果程序想在无法访问DOM的时候写访问DOM的代码怎么办?比如在beforeCreate函数中写网络请然后设置到data中
可以使用方法:this.$nextTick(fn) ,该方法会在mounted之后才会调用
###################
6.vue中怎么实现css的局部作用域?它的实现原理是什么
在.vue文件中的style标签添加scoped属性
原理是:vue加载器解析时会为当前组件每一个标签添加一个独一无二的属性(生成一个hash值属性)然后把css代码添加属性选择器,实现了css局部作用域
###################
7..box1{} .box2{} 这两个选择器选择到同一个元素 会按照哪一套的样式绘制
//都会遵循,只不过有相同的样式时 按照权重的高低进行绘制 如果权重一样按照样式表的加载顺序绘制
###################
8.render函数的理解
每一个组件的底层都会先调用一个函数render函数,按照模板,然后去生成虚拟节点
new Vue({render}) 配置对象中也有一个render 这个函数是用于把组件生成虚拟节点然后渲染到挂载或者关联的DOM节点中
###################
9.$mount和el选项的区别
new Vue({el:"#app"})
//运行的时候指定根组件的template模板
new Vue({
render:(h)=>h(App)
}).$mount("#app")
//加载器解析时 先看是否有关联项el 没有就直接往"#app"选择器对应的元素中挂载
###################
10.对DIFF算法的理解
data 中的数据改变了 页面就会刷新 ,为什么会刷新,其核心算法就是DIFF算法。
.vue文件会在JS环境中生成一个巨大的对象,将所有组件及其属性作为这个对象的属性挂载到对象上,DIFF算法会在数据更新后比较新旧两个对象。
DIff算法,我认为大致是这样的,数据更新以后, 虚拟节点创建完毕,然后跟旧虚拟节点树比较
//1.先比较同层级组件 判断是否需要更新
//2.在比较组件内部的子组件或者元素 数量,类型,其他属性数据,注释是否需要更新
//3.在比较元素内部的数据是否需要更新
|