1.关于 render
render这个函数,个人感觉就是让js直接写html和css,可以做到完全分离
这里贴一下书上的代码示例,和我自己瞎搞的一个实例
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <style> 8 .btn { 9 outline: none; 10 border: none; 11 cursor: pointer; 12 padding: 5px 12px; 13 } 14 .btn-text { 15 color: #409eff; 16 background-color: transparent; 17 } 18 .btn-text:hover { 19 color: #66b1ff; 20 } 21 </style> 22 <div id="app"> 23 <!-- 将实例中 fields & goods 传入组件 --> 24 <fly-table :fields="fields" :goods="goods"> 25 <span slot="title">Fly Table Component</span> 26 </fly-table> 27 </div> 28 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> 29 <script type="text/javascript"> 30 Vue.component('fly-table', { 31 props: { // 组件接收外界传入的参数 32 fields: { 33 type: Array, 34 default () { 35 return [] 36 } 37 }, 38 goods: { 39 type: Array, 40 default () { 41 return [] 42 } 43 } 44 }, 45 methods: { 46 reverse () { // 定义数组倒序方法 47 this.goods.reverse() 48 } 49 }, 50 render (createElement) { // 使用render函数渲染DOM 51 /** 52 * createElement 可接收三个参数 53 * 1. HTML标签字符串(String)| 组件选项对象(Object)| 节点解析函数(Function) 54 * 2. 定义节点特性的对象(Object) 55 * 3. 子节点,createElement构建的VNode节点或字符串生成的无标签文本节点(Array|String) 56 */ 57 return createElement('div', { 58 // * 作为子组件时的插槽名称 59 slot: 'fly-table' 60 }, [ 61 createElement('h2' ,this.$slots.title), 62 createElement('button', { 63 // class 用于绑定类名,同v-bind:class的绑定方式 64 class: ['btn', 'btn-text'], 65 // attrs 用于绑定节点一般属性,如id、disabled、title等 66 attrs: { 67 disabled: false, 68 title: '点击使数组倒序' 69 }, 70 // domProps 用于绑定节点DOM属性,如innerHTML、innerText等 71 domProps: { 72 innerText: '倒序' 73 }, 74 on: { 75 // 绑定事件,使用箭头函数以免创建函数作用域 76 click: () => { 77 this.goods.reverse() 78 } 79 }, 80 // 自定义指令 81 directives: [], 82 // 其他属性 83 key: 'btnReverse', 84 ref: 'btnReverse' 85 }), 86 createElement('table', { 87 // style 用于绑定样式,同v-bind:style的绑定方式 88 style: { 89 '400px', 90 textAlign: 'left', 91 lineHeight: '42px', 92 border: '1px solid #eee', 93 userSelect: 'none' 94 } 95 }, [ 96 createElement('tr', [ 97 this.fields.map(field => createElement('th', field.prop)) 98 ]), 99 this.goods.map(item => createElement('tr', { 100 style: { 101 color: item.isMarked ? '#ea4335' : '' 102 } 103 }, this.fields.map(field => createElement('td', { 104 style: { 105 borderTop: '1px solid #eee' 106 } 107 }, [ 108 field.prop !== 'operate' // 如果不是操作列,显示文本 109 ? createElement('span', item[field.prop]) 110 : createElement('button', { // 否则显示按钮 111 class: ['btn', 'btn-text'], 112 domProps: { 113 innerHTML: '<span>切换标记</span>' 114 }, 115 on: { 116 click: () => { // 当按钮被点击时,切换该行文本标记状态(被标记时字体颜色为红色) 117 item.isMarked = !item.isMarked 118 } 119 } 120 }) 121 ])))) 122 ]) 123 ]) 124 } 125 }) 126 // 声明 Vue 实例 127 let vm = new Vue({ 128 el: '#app', 129 data () { 130 return { 131 fields: [ 132 { 133 label: '名称', 134 prop: 'name' 135 }, 136 { 137 label: '数量', 138 prop: 'quantity' 139 }, 140 { 141 label: '价格', 142 prop: 'price' 143 }, 144 { 145 label: '', 146 prop: 'operate' 147 } 148 ], 149 goods: [ 150 { 151 name: '苹果', 152 quantity: 200, 153 price: 6.8, 154 isMarked: false 155 }, 156 { 157 name: '西瓜', 158 quantity: 50, 159 price: 4.8, 160 isMarked: false 161 }, 162 { 163 name: '榴莲', 164 quantity: 0, 165 price: 22.8, 166 isMarked: false 167 } 168 ] 169 } 170 } 171 }) 172 </script> 173 </body> 174 </html>
1 <!DOCTYPE html> 2 <html lang="zh-CN"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>Document</title> 7 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 8 </head> 9 <style> 10 .test{ 11 color: coral; 12 } 13 #slot-title{ 14 color: pink; 15 } 16 </style> 17 <body> 18 <div id="app"> 19 <test :msg="msg"> 20 <span id="slot-title" slot="title">this is a ttile</span> 21 </test> 22 </div> 23 </body> 24 25 <script> 26 Vue.component('test', { 27 props: { 28 msg: { 29 type: String, 30 default() { 31 return [] 32 } 33 } 34 }, 35 render: function (createElement) { 36 return createElement('div', { 37 class: ["test"] 38 // domProps:{ 39 // innerText:'hello world' 40 // } 41 }, [ 42 createElement("h2",this.$slots.title), 43 // createElement("h2",{ 44 // slot:"", 45 // style:{ 46 // color: "" 47 // } 48 // }), // 这个好像没有用 49 createElement("div", { 50 domProps: { 51 innerText: this.msg 52 } 53 }) 54 ]) 55 } 56 }) 57 var vm = new Vue({ 58 el: '#app', 59 data() { 60 return { 61 msg: 'this is created by [render]' 62 } 63 }, 64 methods: {} 65 }); 66 </script> 67 68 </html>
简单来说,render 里面就是一个 不断套娃 的过程,正如html的标签一样,盒子套盒子,整吐了都
render ( createElement ) 中 createELement 的参数 ( 第一个 通常就是 html标签类型 | 第二个是一个对象,里面是下图的一些属性 | 第三个 通常是一个数组 装的是createElement方法 /* 套娃 */ )
如果看完了上面的代码,可能就会有一个简单的概念,然后我们深入一下
有没有想过 render 里面的 createElement 到底返回的是什么,其实可以这样理解
每有一个 createElement 就有一个 VNode
官网的解释
然后是 VNode的约束,即
书上有一句看似不起眼的话
“ 最后一点,在组件树中,VNode 必须保持其身份的唯一,以便 Vue 一一对应地对每一个真实的 DOM 节点进行追踪。 ”
其实 我认为 就是 重复的 VNode 就没办法好好更新 真实的 DOM
2.关于 filter
{{ msg | dealMsg_1 | dealMsg_2 }} 用管道符号搞的这种,就是filter 而且应该是
从左往右,依次递交
(
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <div id="app"> 8 <h1>{{ title }}</h1> 9 <h1>{{ title | supplyTitle1 }}</h1> 10 <!-- 存在多个filter时,将从左向右执行 --> 11 <h1>{{ title | supplyTitle1 | supplyTitle2 }}</h1> 12 </div> 13 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> 14 <script type="text/javascript"> 15 let vm = new Vue({ 16 el: '#app', 17 data () { 18 return { 19 title: 'Test#%for#%Filter.' 20 } 21 }, 22 filters: { 23 supplyTitle1 (value) { // 表达式的值将作为形参传入 24 console.log('Supply Title 1') 25 return value.replace(/#/g, ' ') 26 }, 27 supplyTitle2 (value) { 28 console.log('Supply Title 2') 29 return value.replace(/%/g, '') 30 } 31 } 32 }) 33 </script> 34 </script> 35 </body> 36 </html>
3.关于 el,template , render 的顺序
render > template > el
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <div id="app"> 8 <h1>el: {{ msg }}</h1> 9 </div> 10 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> 11 <script type="text/javascript"> 12 let vm = new Vue({ 13 el: '#app', 14 render (c) { 15 return c('h1', 'render: ' + this.msg) 16 }, 17 template: '<h1>template: {{ msg }}</h1> ', 18 data () { 19 return { 20 msg: 'I want you!' 21 } 22 } 23 }) 24 </script> 25 </body> 26 </html>
4.对于 mixin与实例的冲突与合并
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <style> 8 #app { 9 color: #2c3e50; 10 font-family: Roboto, sans-serif; 11 } 12 .label { 13 display: inline-block; 14 min-width: 160px; 15 } 16 </style> 17 <div id="app"> 18 <h1>{{ title }}</h1> 19 <p><strong class="label">Text:</strong>{{ text }}</p> 20 <p><strong class="label">Plus Text:</strong>{{ plusText }}</p> 21 <p><strong class="label">Upper Text:</strong>{{ text | supplyUpper }}</p> 22 <button @click="toggleText">切换文本</button> 23 </div> 24 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> 25 <script type="text/javascript"> 26 // 强耦合,需要被混入组件的data根节点中包含text属性 27 let mixin = { 28 data () { 29 return { 30 title: 'Test for mixin' 31 } 32 }, 33 mounted () { 34 console.log('mixin mounted') 35 }, 36 methods: { 37 toggleText () { 38 this.text = 'mixin text' 39 } 40 }, 41 computed: { 42 plusText () { // 此处需要创建函数作用域以使this指向Vue实例 43 return '+ ' + this.text + ' +' 44 } 45 }, 46 filters: { // 选项过滤器 47 supplyUpper: value => value.toUpperCase() 48 }, 49 watch: { // 监听器 50 text (value) { 51 console.log('mixin text: ' + value) 52 } 53 } 54 } 55 let vm = new Vue({ 56 el: '#app', 57 mixins: [ mixin ], 58 data () { 59 return { 60 title: 'A Title', 61 text: 'which one?' 62 } 63 }, 64 mounted () { 65 console.log('instance mounted') 66 }, 67 methods: { 68 toggleText () { 69 this.text = 'instance text' 70 } 71 }, 72 watch: { 73 text (value) { 74 console.log('instance text: ' + value) 75 } 76 } 77 }) 78 </script> 79 </body> 80 </html>
总结
今天学习的还好,就是对render 那里卡了一下午
有过一次做轮播图的经验,但是感觉这些东西 在实际项目的时候还是会用笨方法来操作
看后续实战项目的学习了