参考资料:vue官方指南(https://cn.vuejs.org/v2/guide/components-registration.html)
一、组件注册
1.当直接在DOM中使用一个组件时(而不是在字符串模板或单文件组件中),组件名,强烈推荐遵循W3C规范,字母全小写且必须包含一个连字符,这会帮助你避免和当前以及未来的HTML元素相冲突。
2.定义组件时,组件名有两种写法:短横线分隔(kebab-case)和首字母大写(PascalCase)。当用首字母大写命名时,使用时
3.组件全局注册可以方便使用,但是即使不使用了,也会被包含在最终的构建结果中,会造成用户下载的JS的无谓增加,所以我们还需要局部注册使用频次不那么高的组件。
4.使用Babel和webpack模块系统的情况下,推荐创建一个components目录,并将每个组件放置在各自的文件中,然后需要在局部注册之前导入每个想使用的组件'import ComponentA from './ComponentA'',再'export default { components: ComponentA }'
5.基础组件的自动化全局注册,如果使用的webpack,就可以使用require.context在某个目录下获取所有基础组件,然后遍历全局注册。具体代码可查看官方文档。
6.全局注册的行为必须在根Vue实例(通过 new Vue)创建之前发生。???
二、Prop
1.大小写。当你使用DOM中的模板时,驼峰命名法的prop名需要使用等价的短横线分隔命名。如果你使用字符串模板,那这个限制就不存在了。
2.字符串形式列出的prop, props:['title','likes'] ;指定值的类型,props:{ title : String, likes:Number} 。
3.传入布尔值的prop时,如果没有值,就意味着true,
4.所有的prop使得父子prop直接形成了一个单向下行绑定,父级prop的更新会向下流动到子组件中,但是反过来不行。你不应该在子组件内部改变prop,vue会在浏览器的控制台中发出警告。
5.有两种试图改变prop的情形:(1) 用prop传递一个初始值,子组件希望将其作为一个本地的prop数据来使用。这种情况最好定义一个本地的data属性来用这个prop用作初始值。(2)prop以一种原始的值传入且需要进行转化,这种情况最好使用这个prop值来定义一个计算属性。
6.在JS中对象和数组是通过引用传入的,所以对于一个数组或对象类型的prop,在子组件中改变这个对象或数组本身将会影响父组件的状态,不会触发Vue的警告
7.对prop的验证:
propA:Number, // 基础的类型检查
propB:[String, Number],//多个可能的类型
propC: { type:String , require:true } , //必填检查
propD:{ type:Number, default:100}, // 简单类型的默认值
propE: { type: Object, default(){ return { msg: 'hi '} }} , //默认值是数组或对象
propF:{ validator(value){ return ['success','warning','error'].indexOf(value) !== -1}},// 自定义验证函数
8.prop会在一个组件实例创建以前进行验证,所以实例的属性(data、computed)在default或validator函数中是不可用的。
9.prop类型检查的可选项:String、Number、Boolean、Array、Object、Date、Function、Symbol、自定义的构造函数。自定义构造函数 function Person(name){ this.name = name},在props :{ author : Person}。
10.非prop的attribute,是指传入一个组件,但该组件并没相应定义prop的attribute。组件可以接受任意的attribute,这些attribute会被添加到这个组件的根元素上。对于绝大多数attribute,从外部提供给组件的值会替换组件内部设置好的值,比如type="text"会替换type="date",而有些会进行合并,比如 class 和 style。如果你不希望组件的根元素继承attribute,可以在组件的选项中设置 inheritAttrs:false,该属性设为false后,仍然可以继承style和class。
11.在子组件中可以通过$attrs获取到传入组件的非prop的attribute名和值。有了$attrs和inheritAttrs:false,可以手动决定哪些attribute会被赋予哪些元素,在写基础组件时会用到。
三、自定义事件
1.事件名需要完全匹配才能触发。而v-on监听器在DOM模板中会被自动转换为全小写,所以推荐使用小写短横线(kebab-case)的事件名。
2.一个组件上的v-model默认会利用名为value的prop和名为input的事件,但是像单选框、复选框等类型的输入控件可能会将value attribute用于不同的目的。这时,model选项可以用来避免这样的冲突,在组件的选项上,加入model选项,model : { prop:'checked' , event:'change'} , props: { checked : Boolean} , template : ` ` 。在组件上使用v-model,
相当于 将v-model 的功能 重新和checked属性、change事件对应,而不是原来的 value属性和input事件。注意,仍然需要再组件的props里声明属性 checked。
3.如果想要监听组件根元素上的原生事件,可以直接在使用组件时这样写,
在组件中,computed:{ inputListeners(){ return Object.assign({} , this.$listeners)}} , template: ``
4. .sync修饰符:有时候,我们会想对一个prop进行‘双向绑定’。但是真正的双向绑定会带来维护上的问题,父子组件都可以修改的值你会无法判断到底是谁改的。所以推荐以"update:myPropName"的模式触发事件,然后在父组件中监听并更新值。然后这种写法有一种简写,
四、插槽
1.如果在组件定义时没有插槽,那么在使用组件时起始标签和结束标签之间的内容都会被抛弃。
2.在2.6.0中,为具名插槽和作用域插槽引入了一个新的统一语法(v-slot),它取代了slot 和slot-scope这两个已经被废弃但未被移除的attribute。2.x版本仍会支持,但是Vue3不再支持。
3.在组件的插槽中想使用数据,可以使用父组件里所有可用实例属性data、methods之类的,但是访问不到组件的作用域(组件的attribute)。
4.父级模板里的所有内容都是在父级作用域中编译的,子模板里所有的内容都是在子作用域中编译的。 ???
5.可以在slot之间放入一个具体的内容作为默认的后备内容,在使用组件时如果没传插槽内容,就会渲染后备内容,如果传了插槽内容,就会替换掉后备内容。
6.在我们需要多个插槽时就需要使用具名插槽,在slot元素上加name attribute,不带name的slot隐含名字为default。在向具名插槽提供内容时,可以在template元素上使用v-slot指定,并以'v-slot:header'的形式提供名称,任何没有被包裹在带有v-slot的template中的内容都被会被视为默认插槽的内容。v-slot只能添加在template元素上。
7.如果想在外层访问到子组件中的数据,可以这样做,
8.当被提供的内容只有默认插槽时,组件的标签可以被当做插槽的模板用,可以把v-slot直接用在组件上,不用再多写一层template了。一旦有多个插槽,就必须使用基于template的语法了。
9.插槽prop可以直接被解构,还可以被重命名,还可以定义后备内容。
10.动态插槽名,。
11.具名插槽的缩写,'v-slot:'可以替换成'#'。'#'后必须跟名字,默认插槽必须这样写'#default'.
12.插槽prop允许我们将插槽转换为可复用的模板,这些模板可以基于输入的prop渲染出不同的内容。这在设计封装数据逻辑时同时允许父级元素自定义部分布局的可复用组件时是最有用的。样例写明了用法,但是感觉这个例子中没必要使用这种写法。
13.更多现实生活中的作用域插槽的用法,可以参考诸如 Vue Virtual Scroller、Vue Promised 和 Portal Vue 等库。
五、动态组件或异步组件
1.在动态组件外层使用keep-alive进行包裹,进行组件的切换时,会缓存失活的组件
2.在大型应用中,可能需要将应用分割成小一些的代码块,并且只在需要的时候才会从服务器加载一个模块。Vue允许你一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue只有在这个组件需要被渲染时才会触发该工厂函数,且会把结果缓存起来供以后使用。
六、处理边界情况
(1)访问元素及组件
1.访问根实例 $root:在每个new Vue实例的子组件中,可以通过$root访问到vue根实例的data、computed等属性。适合使用在demo和小型应用中,在其他情况还是推荐使用vuex。
2.访问父级组件实例 $parent: 可以通过这个方式更改父组件的数据,但会使数据变更难以追踪。另外使用了 $parent的组件又被嵌套了,你可能就会需要写一下 "var map = this.$parent.map || this.$parent.$parent.map" 这样的hack了。
3.访问子组件实例或子元素 ref 及 $refs:觉得主要用来调用子组件的方法,数据最好还是用prop。$refs只会在组件渲染完成之后生效,且不是响应式的,应该避免在模板或计算属性中访问$refs。
4.依赖注入 provide和inject:在祖先组件中通过provide属性将一些属性抛出,在后代属性中用inject属性来接收。可以把依赖注入看做一部分大范围有效的prop,使用的前提有两个,一是祖先组件不需要知道哪些后代组件使用了它提供的属性,二是后代组件不需要知道被注入的属性来自哪里。通过provide提供的属性是非响应式的,如果想要响应式的,可以试试放在对象里抛出来。
(2)程序化的事件侦听器 $emit、$on、$once 一次性侦听、$off 停止侦听。引入第三方组件库时,可以在引入代码之后监听beforeDestroy的钩子,然后在其中销毁引入的组件,这样可以很方便的程式化的清理所有我们建立的东西。"this.$once('beforeDestroy',function(){picker.destroy()})" 。即使如此,如果你发现自己不得不在单个组件里做很多建立和清理的工作,最好的方式还是创建更多模块化的组件,模块化的组件会自动销毁吗?
(3)循环引用
1.递归组件,通过name来调用自身,一定要保证递归调用是条件性的,最终会得到false的if,否则会'max stack size exceeded'。
2.组件之间的循环引用。如果这样的组件时全局注册的,那相互引用不会出现问题。但如果是用模块系统依赖导入组件,如webpack或Browserify,将会遇到错误。为了解决这个问题,我们需要给模块系统一个点,在那里“A反正是需要B的,但是我们不需要先解析B”。在A组件的定义中,这样注册B,“beforeCreate(){ this.$options.componets.B = require(./B.vue)}” 或者 “ components:{ B: () => import('./B.vue')}”
(4)模板定义的替代品
1.内联模板inline-template。当inline-template 这个特殊的attribute出现在一个组件上,这个组件将会使用其中间的内容作为模板,而不是作为被分发的内容。内联模板需要定义在Vue所属的DOM元素内。 不知有何意义这样写。
2.X-Template 。。x-template需要定义在Vue所属的DOM元素外。
(5)控制更新
1.强制更新$forceUpdate。如果你发现自己需要在Vue中做一次强制更新,99.9%的情况是你在某个地方做错了,要么没有留意到数组或对象的变更检测注意事项,要么你可能依赖了一个未被Vue的响应式系统追踪的状态。如果检查之后都没有问题,那你可以通过$forceUpdate来强制更新。
2.v-once。可以用v-once来包裹不需要更新的大量静态内容,来确保只计算一次然后缓存下来。但是最好在你觉得不加指令渲染就非常慢的情况下再使用。因为别人可能没有注意到这个指令,且在里面加了需要响应的属性,然后要花很多时间才能找出模板为什么没有正确更新。