JS框架之vue.js(深入三:组件1)
这个要单独写,原文是这么描述vue的组件的:组件(Component)是Vue.js最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js的编译器为它添加特殊功能。在有些情况下,组件也可以是原生HTML元素的形式,以is特性扩展。
这个特性我感觉比较难理解,一步步来,看看组件到底是个什么东西?
1.举个栗子
//model层: //通过extend方式定义一个Vue组件 varMyComponent=Vue.extend({ template:'<div>Acustomcomponent!</div>' }) //向Vue注册这个组件,名称定为my-component Vue.component('my-component',MyComponent) //创建根实例 newVue({ el:'#example' }) //Vue层: <divid="example"> <my-component></my-component> </div>
渲染为:
<divid="example"> <div>Acustomcomponent!</div> </div>
就是这个栗子,差点把我忽悠了,以为前面对extend的概念理解错了。还记得前面是这么描述
varMyComponent=Vue.extend()的,Vue相当于基类,MyComponent继承了Vue,拥有了Vue的属性和方法,但是继承的概念还有另一层,就是基类是木有子类自定义的属性和方法的。这里的子类MyComponent扩展了一个属性template,按照继承的说法,Vue基类是不能使用的,但是这个栗子看似违背了这个规则,最后创建的是Vue实例,同时让模板生效了。正常的写法不是应该这样:
//model层: //通过extend方式定义一个Vue组件 varMyComponent=Vue.extend({ template:'<div>Acustomcomponent!</div>' }) //不用注册 //Vue.component('my-component',MyComponent) //创建MyComponent实例 newMyComponent({ el:'#example' }) //Vue层: <divid="example"> //不用组件 //<my-component></my-component> </div>
经过试验,这种写法确实没错,也可以正常显示。问题来了,为什么第一种写法也是可以的,比较两处代码,发现第一种写法有一个注册过程,注册了一个my-component,最后使用的也是这个my-component,仔细想想,并不是说Vue实例可以使用template,而是向Vue注册了这个组件后,Vue实例就可以使用这个组件了,所以并不冲突。(吓死宝宝了--)
想清楚这个后,再来考虑另外一个问题,这两种写法的区别在于哪里?
有没有发现,第二种写法其实是很有限制的,他替换了整个div,不管div中有多少内容。比如:
<divid="example"> ssssdfsdaf <button>abc</button> </div>
最后统统不见,被替换成<div>Acustomcomponent!</div>。灵活度太低,如果我只想替换ssssdfsdaf怎么办?所以就要用第一种方式了,于是幡然醒悟,原来这就是组件,就像一个零件一样,想往哪塞就往哪塞:
<divid="example"> <my-template>ssssdfsdaf<my-template> <button>abc</button> </div>
另外,注册必须在新建实例前,反过来的话,新建的实例肯定不能使用组件的。
原文还说replace可以决定是否替换,这个不知道咋用,先留一坑在这,后面看看能否用上。//坑1
2.组件注册有两种方式:
一是前面看到的全局注册方式,Vue.component,这种全局可用。
二是局部注册方式
//局部注册也可以这么做 varParent=Vue.extend({ components:{ 'my-component':{ template:'<div>Acustomcomponent!</div>' } } })
这种写法最简,很明显Parent扩展了Vue,拥有了组件my-component。此时的组件只有Parent能用,Vue不能用。
3.is属性
组件在使用的过程中也是有限制的。原因在于:
Vue的模板是DOM模板,使用浏览器原生的解析器而不是自己实现一个。所以组件被替换后必须依照html的正常标准来,它必须是有效的HTML片段。一些HTML元素对什么元素可以放在它里面有限制。常见的限制:
a不能包含其它的交互元素(如按钮,链接)
ul和ol只能直接包含li
select只能包含option和optgroup
table只能直接包含thead,tbody,tfoot,tr,caption,col,colgroup
tr只能直接包含th和td
以table为例
<table> <my-component></my-component> <my-component></my-component> </table> //定义 varMyComponent=Vue.extend({ template:'<tr>Acustomcomponent!</tr>' })
这样的写法看似正常,因为<table><tr></tr></table>结构是正常的,但是实际上不能依赖自定义组件在浏览器验证之前的展开结果,所以这里不被认为是<tr>。为此,is属性便有作用了,将以上写法改写:
<table> <tris="my-component"></tr>//这里改成is属性 <tris="my-component"></tr> <tris="my-component"></tr> </table> //定义 varMyComponent=Vue.extend({ template:'<div>Acustomcomponent!</div>'//这里不能用tr })
修改后,相当于
<table> <tr><my-component></my-component></tr> <tr><my-component></my-component></tr> <tr><my-component></my-component></tr> </table>
保留了原来的tr,所以dom解析不会出错
4.Props:组件通讯的手段
4.1“prop”是组件数据的一个字段,期望从父组件传下来。子组件需要显式地用props选项声明props:
Vue.component('child',{ //声明props,这里驼峰式命名 props:['myMessage'], //模板中可以这样用 template:'<span>{{myMessage}}</span>' })
HTML特性不区分大小写。名字形式为camelCase驼峰式的prop用作特性时,需要转为kebab-case(短横线隔开),所以html中是这个样子的:
<!--kebab-caseinHTML--> <childmy-message="hello!"></child>
以上这种是props的静态用法,也可以用v-bind绑定动态Props到父组件的数据。每当父组件的数据变化时,也会传导给子组件:
<div> <inputv-model="parentMsg"> <br> <childv-bind:my-message="parentMsg"></child> </div>
这时候看到v-model有点懵逼,这货不是跟{{}}类似,引用data属性中的parentMsg吗?此时肯定是没有定义parentMsg的,所以v-bind:my-message=”parentMsg”绑定组件的同时,赋予了父组件parentMsg属性。
4.2prop的绑定类型:
prop默认是单向绑定:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。不过,也可以使用.sync或.once绑定修饰符显式地强制双向或单次绑定:
<!--默认为单向绑定--> <child:msg="parentMsg"></child> <!--双向绑定--> <child:msg.sync="parentMsg"></child> <!--单次绑定--> <child:msg.once="parentMsg"></child>
双向绑定会把子组件的msg属性同步回父组件的parentMsg属性。单次绑定在建立之后不会同步之后的变化。这里原文还特定强调了下,prop是一个对象或数组时,是按引用传递,修改内容会随时修改父组件内容,这个有语言基础的都知道。
4.3prop验证:
组件可以为props指定验证要求。当组件给其他人使用时这很有用,因为这些验证要求构成了组件的API,确保其他人正确地使用组件。此时props的值是一个对象({}而不是[]),包含验证要求:
Vue.component('example',{ props:{ //基础类型检测(`null`意思是任何类型都可以) propA:Number, //多种类型(1.0.21+) propM:[String,Number], //必需且是字符串 propB:{ type:String, required:true }, //数字,有默认值 propC:{ type:Number, default:100 }, //对象/数组的默认值应当由一个函数返回 propD:{ type:Object, default:function(){ return{msg:'hello'} } }, //指定这个prop为双向绑定 //如果绑定类型不对将抛出一条警告 propE:{ twoWay:true }, //自定义验证函数 propF:{ validator:function(value){ returnvalue>10 } }, //转换函数(1.0.12新增) //在设置值之前转换值 propG:{ coerce:function(val){ returnval+''//将值转换为字符串 } }, propH:{ coerce:function(val){ returnJSON.parse(val)//将JSON字符串转换为对象 } } } })
type可以是下面原生构造器:
String
Number
Boolean
Function
Object
Array
type也可以是一个自定义构造器,使用instanceof检测。
当prop验证失败了,Vue将拒绝在子组件上设置此值,如果使用的是开发版本会抛出一条警告。
这里也是看的我一脸懵逼,连个栗子都不给,拿刚才的例子改一下打个比方
Vue.component('child',{ //声明props,这里驼峰式命名 props:['myMessage'], //模板中可以这样用 template:'<span>{{myMessage+1}}</span>'//改成表达式 }) <!--kebab-caseinHTML--> <childmy-message="hello!"></child>//这里先不改
如果我们希望别人把child组件的myMessage当做Number类型来处理,而我们这里又没有做prop验证,结果就是{{myMessage+1}}会变成字符串拼接,当html传入的是hello!,渲染出来结果:hello!
所以说,告诉别人这里要传入Number类型是必要的,于是改为:
Vue.component('child',{ //声明props,这里驼峰式命名 props:{myMessage:Number}, //模板中可以这样用 template:'<span>{{myMessage+1}}</span>'//改成表达式 })
这时候如果传入hello!,此时渲染结果?没错,就是NaN。这样别人就知道要传入一个数字了。
如果这样传入
<childmy-message="123"></child>//改成123
这样总行了吧,运行,他喵的居然还不行,还是NaN。原文有这样的解释:
//#字面量语法vs.动态语法 //初学者常犯的一个错误是使用字面量语法传递数值: <!--传递了一个字符串"1"--> <compsome-prop="1"></comp> 因为它是一个字面prop,它的值以字符串"1"而不是以实际的数字传下去。如果想传递一个实际的JavaScript数字,需要使用动态语法,从而让它的值被当作JavaScript表达式计算: <!--传递实际的数字--> <comp:some-prop="1"></comp>
好吧,也就是说刚才传递的实际上是字符串”123”,结果必然是NaN,再改:
<child:my-message="123"></child>//改成123
此时{{myMessage+1}}会得到正确的结果:124
以上所述是小编给大家介绍的JS框架之vue.js(深入三:组件1),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!