Vue2基础语法
【一】插值语法
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>插值语法</title> |
| |
| <script src="./js/vue/vue.js"></script> |
| |
| |
| |
| </head> |
| <body> |
| <div id="app"> |
| <ul> |
| <li>字符串:{{name}}</li> |
| <li>数值:{{age}}</li> |
| <li>数组:{{list1}}</li> |
| <li>对象:{{obj1}}</li> |
| <li>html标签字符串:{{link1}} <small>依旧以字符串形式显示</small></li> |
| <li>运算:{{10 + 20 + 30 + 40}}</li> |
| <li>三目运算符:{{10 > 20 ? '是' : '否'}}</li> |
| </ul> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| name: 'User', |
| age: 18, |
| list1: [1,2,3,4], |
| obj1: {name: 'User', age: 18}, |
| link1: '<a href="https://www.baidu.com">百度一下 你就知道</a>' |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |
| </html> |

【补】响应式
-
深入响应式原理 — Vue.js (vuejs.org)
-
在 Vue 中,当你把一个普通的 JavaScript 对象传递给 Vue 实例作为数据对象时,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty
把这些属性全部转为 getter/setter
,这样 Vue 就能追踪数据的变化。
-
当数据发生变化时,所有依赖于该数据的地方都会自动更新,这包括 DOM 中绑定了该数据的元素。这种自动的响应式更新使得开发者无需手动操作 DOM,只需关注数据的变化即可。
-
值得注意的是,Vue 在初始化实例时会递归地将所有的属性转为 getter/setter,这使得 Vue 能够在数据被访问时添加依赖,从而在数据变化时通知更新。这也意味着,当你添加新属性时,它不会触发视图更新,因此需要提前声明所有的根级响应式属性。
简单来讲,当你修改Vue中的数据时,Dom上对应的元素将会改变

【二】指令
【1】 文本指令
指令 |
释义 |
v-html |
让HTML渲染成页面 |
v-text |
标签内容显示js变量对应的值 |
v-show |
放1个布尔值:为真 标签就显示;为假 标签就不显示 |
v-if |
放1个布尔值:为真 标签就显示;为假 标签就不显示 |
v-show
与 v-if
的区别:
- v-show:标签还在,只是不显示了(
display: none
)
- v-if:直接操作DOM,删除/插入 标签
| <body> |
| <div id="app"> |
| <h2>文本指令</h2> |
| <ul> |
| <li v-text="name"></li> |
| <li v-text="age"></li> |
| <li v-text="list1"></li> |
| <li v-text="obj1"></li> |
| <li v-html="link1"></li> |
| <li v-show="1">show显示标签</li> |
| <li v-show="0">show隐藏标签</li> |
| <li v-if="1">if显示标签</li> |
| <li v-if="0">show删除标签</li> |
| </ul> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| name: 'User', |
| age: 18, |
| list1: [1,2,3,4], |
| obj1: {name: 'User', age: 18}, |
| link1: '<a href="https://www.baidu.com">百度一下 你就知道</a>' |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |
| </html> |

通过修改li标签
属性展示v-show=false
的标签

【2】事件指令
指令 |
释义 |
v-on |
触发事件 |
@ |
触发事件 |
@[event] |
触发event事件(可以是其他任意事件) |
例如:v-on:click
可以缩写成@click
【2.1】常见的事件指令
- 常见的事件类型有
click
、input
、change
、mouseover
、keydown
等
| <body> |
| <div id="app"> |
| <h2>事件指令</h2> |
| <p>{{eventS}}</p> |
| <ul> |
| <li> |
| <button v-on:click="handleClick">点击事件</button> |
| </li> |
| <li><input type="text" @input="handleInput">input事件:输入的内容【{{stringI}}】</li> |
| <li><input type="text" @change="handleChange">change事件:修改的内容【{{stringC}}】</li> |
| <li><span @mouseover="handleOver" @mouseout="handleOut">悬浮事件</span></li> |
| <li> |
| <button @keydown="handleKD" @keyup="handleKO">键盘事件</button> |
| 按下的键【{{keycode}}】 |
| </li> |
| </ul> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| eventS: '事件名称', |
| stringI: '', |
| stringC: '', |
| keycode:'' |
| }, |
| methods: { |
| handleClick(event) { |
| this.eventS = '点击事件'; |
| }, |
| handleInput(event) { |
| this.eventS = 'input事件'; |
| this.stringI = event.data; |
| console.log(event) |
| }, |
| handleChange(event) { |
| this.eventS = 'change事件' |
| this.stringC = event.target.value |
| console.log(event) |
| }, |
| handleOver(event) { |
| this.eventS = 'mouseover事件' |
| console.log(event) |
| }, |
| handleOut(event) { |
| this.eventS = 'mouseout事件' |
| console.log(event) |
| }, |
| handleKD(event) { |
| this.eventS = 'mousekeydown事件' |
| this.keycode=event.key |
| console.log(event) |
| }, |
| handleKO(event) { |
| this.eventS = 'mousekeyon事件' |
| console.log(event) |
| } |
| }, |
| computed: {} |
| }) |
| </script> |

| <body> |
| <div id="app"> |
| <h1>input事件</h1> |
| <input type="text" v-model="name1" @input="handleInput">-->{{name1}} |
| <h1>change事件</h1> |
| <input type="text" v-model="name2" @change="handleChange">-->{{name2}} |
| <h1>blur事件</h1> |
| <input type="text" v-model="name3" @blur="handleBlur">-->{{name3}} |
| <h1>focus事件</h1> |
| <input type="text" v-model="name4" @focus="handleFocus">-->{{name4}} |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| name1: '', |
| name2: '', |
| name3: '', |
| name4: '提示信息', |
| }, |
| methods: { |
| handleInput(event) { |
| console.log('输入的值:',event.data) |
| }, |
| handleChange() { |
| console.log('handleChange:只有离开输入框,且内容发生变化才会触发,如果内容不变也不触发') |
| }, |
| handleBlur() { |
| console.log('handleBlur:失去焦点触发') |
| }, |
| handleFocus() { |
| console.log('Focus:获得焦点触发,可以执行清空输入框') |
| this.name4 = '' |
| } |
| }, |
| computed: {} |
| }) |
| </script> |

【补】数据双向绑定v-model
| <body> |
| <div id="app"> |
| <h1>数据单向绑定</h1> |
| <input type="text" :value="name1">-----------> 通过插值语法查看值{{name1}} |
| <h1>数据双向绑定</h1> |
| <input type="text" v-model="name2">----------->{{name2}} |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| name1: '', |
| name2: '' |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |

【2.3】事件修饰符
- 在事件处理程序中调用
event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
- 为了解决这个问题,Vue.js 为
v-on
提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop
:阻止事件冒泡,即不再向上层元素派发事件。
.prevent
:阻止事件的默认行为,例如阻止表单提交或者超链接跳转。
.capture
:事件捕获模式,即事件将在捕获阶段触发,而不是在冒泡阶段触发。
.self
:只有当事件是由元素自身触发时才触发事件处理函数,而不是子元素触发的。
.once
:事件只会触发一次,即事件处理函数只会执行一次,之后便移除监听。
.passive
:告诉浏览器不要阻止事件的默认行为,可以用来提升移动端的滚动性能。
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。

事件冒泡:当一个事件发生在DOM元素上时,它会从最具体的元素(文档中嵌套层次最深的那个节点)开始向上逐级触发,直到触发到最顶层的文档对象(document对象)为止
【2.3.1】self和stop
| <style> |
| .div1 { |
| width: 300px; |
| height: 300px; |
| } |
| |
| .father { |
| background-color: #0d6efd; |
| } |
| |
| .son { |
| background-color: #20c997; |
| } |
| </style> |
| <body> |
| <div id="app"> |
| <div @click.self="handleFunc" class="div1 father"> |
| <h2>@click.self 阻止父标签的冒泡事件</h2> |
| <p>当为事件指定self修饰符时,该事件只有当前标签触发</p> |
| <div @click.stop="handleFunc2" class="div1 son"> |
| <h3>@click.stop 阻止子标签的冒泡事件</h3> |
| <p>当为事件指定stop修饰符时,该事件不会向上层元素派发事件,也就是不会触发click</p> |
| </div> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: {}, |
| methods: { |
| handleFunc() { |
| alert('父标签的点击事件') |
| }, |
| handleFunc2() { |
| alert('子标签的点击事件') |
| } |
| }, |
| computed: {} |
| }) |
| </script> |

【2.3.2】once
| <body> |
| <div id="app"> |
| <div> |
| <p>{{count}}</p> |
| <p><button @click="handleCount">点击数字加1</button></p> |
| <p><button @click.once="handleCount">点击数字加1【仅触发一次】</button></p> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| count: 0 |
| }, |
| methods: { |
| handleCount() { |
| this.count += 1 |
| } |
| }, |
| computed: {} |
| }) |
| </script> |

【2.4】键盘事件的按键修饰符
| <body> |
| <div id="app"> |
| <h1>按键事件</h1> |
| <input type="text" v-model="keyname" @keyup="handleKeyUp">-->{{keyname}} |
| <hr> |
| <h2>按键修饰符</h2> |
| <input type="text" v-model="keyname2" @keyup.ctrl="handleKeyCtrl">-->{{keyname2}} |
| <br> |
| <h3>使用keycode对应码表</h3> |
| <input type="text" @keyup.65="handleKey13"> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| keyname: '', |
| keyname2: '', |
| }, |
| methods: { |
| handleKeyUp(event) { |
| console.log(event) |
| console.log('按下', event.key) |
| this.keyname = event.key |
| }, |
| handleKeyCtrl() { |
| alert('按下了【ctrl】') |
| }, |
| handleKey13() { |
| alert('按下了【A】') |
| } |
| }, |
| computed: {} |
| }) |
| </script> |

【2.4】案例:过滤筛选
| <body> |
| <div id="app"> |
| <h1>过滤案例</h1> |
| <input type="text" v-model="myText" @input="handleInput"> |
| <hr> |
| <ul> |
| <li v-for="item in newdataList">{{item}}</li> |
| </ul> |
| |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| myText: '', |
| dataList: ['a', 'at', 'atom', 'be', 'beyond', 'cs', 'csrf', 'atome', 'atomem'], |
| newdataList: ['a', 'at', 'atom', 'be', 'beyond', 'cs', 'csrf', 'atome', 'atomem'], |
| }, |
| methods: { |
| handleInput(event) { |
| console.log(event) |
| this.newdataList = this.dataList.filter((item) => item.indexOf(this.myText) >= 0) |
| |
| |
| }, |
| }, |
| computed: {} |
| }) |
| </script> |

【3】属性指令
指令 |
释义 |
v-bind |
直接写js的变量或语法 |
: |
直接写js的变量或语法 |
例如:v-bind:class='js变量'
可以缩写成::class='js变量'
【3.1】属性
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>属性指令</title> |
| <script src="../js/vue/vue.js"></script> |
| </head> |
| <body> |
| <div id="app"> |
| <div> |
| |
| <div v-bind:id="idDiv"></div> |
| <img :src="imgUrl" :alt=""> |
| <a :href="imgUrl">跳转</a> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| imgUrl: 'https://pic.netbian.com/uploads/allimg/240105/002731-17043856512195.jpg', |
| idDiv: '1' |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |
| </html> |
【3.2】style属性和class属性
- 都是可以使用三种形式来进行属性的指定
- 字符串
- 数组:对于class属性,数组是最适合的
- 对象:对于style属性,对象是最适合的
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>属性指令</title> |
| <script src="../js/vue/vue.js"></script> |
| </head> |
| <style> |
| .div1 { |
| height: 300px; |
| width: 300px; |
| background-color: #0a53be; |
| } |
| |
| .div2 { |
| height: 300px; |
| width: 300px; |
| background-color: #0a53be; |
| } |
| |
| .div3 { |
| height: 300px; |
| width: 300px; |
| background-color: #0a53be; |
| } |
| </style> |
| <body> |
| <div id="app"> |
| <h2>style属性三种方式</h2> |
| <div style="display: flex;justify-content: space-around"> |
| <div v-bind:style="styleStr"> |
| <p>style属性:str</p> |
| </div> |
| <div :style="styleList"> |
| <p>style属性:list</p> |
| </div> |
| <div :style="styleObj"> |
| <p>style属性:obj</p> |
| </div> |
| </div> |
| <hr> |
| <h2>class属性的三种方式</h2> |
| <div style="display: flex;justify-content: space-around"> |
| <div :class="classStr"> |
| <p>class属性:str</p> |
| </div> |
| <div :class="classList"> |
| <p>class属性:list</p> |
| </div> |
| <div :class="classObj"> |
| <p>class属性:obj</p> |
| </div> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| |
| styleStr: 'width: 300px;height: 300px;background-color: #c5e8ef', |
| |
| styleList: [{width: '300px'}, {height: '300px'}, {backgroundColor: '#c5e8ef'}], |
| |
| styleObj: {width: '300px', height: '300px', backgroundColor: '#c5e8ef'}, |
| |
| classStr: 'div1 div2 div3', |
| |
| classList: ['div1', 'div2', 'div3'], |
| |
| classObj: {div1: true, div2: true, div3: true}, |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |
| </html> |
【3.3】案例:使用点击事件动态修改样式
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>属性指令</title> |
| <script src="../js/vue/vue.js"></script> |
| </head> |
| <style> |
| .div1 { |
| height: 300px; |
| width: 300px; |
| background-color: #0a53be; |
| } |
| |
| .div2 { |
| height: 300px; |
| width: 300px; |
| background-color: #0a53be; |
| } |
| |
| .div3 { |
| height: 300px; |
| width: 300px; |
| background-color: #0a53be; |
| } |
| |
| .div_add { |
| font-size: 50px; |
| font-family: FangSong; |
| } |
| </style> |
| <body> |
| <div id="app"> |
| <h2>style属性三种方式</h2> |
| <div style="display: flex;justify-content: space-around"> |
| <div v-bind:style="styleStr"> |
| <p>style属性:str</p> |
| </div> |
| <div :style="styleList"> |
| <p>style属性:list</p> |
| </div> |
| <div :style="styleObj"> |
| <p>style属性:obj</p> |
| </div> |
| </div> |
| <hr> |
| <h2>class属性的三种方式</h2> |
| <div style="display: flex;justify-content: space-around"> |
| <div :class="classStr"> |
| <p>class属性:str</p> |
| </div> |
| <div :class="classList"> |
| <p>class属性:list</p> |
| </div> |
| <div :class="classObj"> |
| <p>class属性:obj</p> |
| </div> |
| </div> |
| <h2>使用点击事件动态修改样式</h2> |
| <div> |
| <p> |
| <button @click="handleStyle">添加样式</button> |
| </p> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| |
| styleStr: 'width: 300px;height: 300px;background-color: #c5e8ef', |
| |
| styleList: [{width: '300px'}, {height: '300px'}, {backgroundColor: '#c5e8ef'}], |
| |
| styleObj: {width: '300px', height: '300px', backgroundColor: '#c5e8ef'}, |
| |
| classStr: 'div1 div2 div3', |
| |
| classList: ['div1', 'div2', 'div3'], |
| |
| classObj: {div1: true, div2: true, div3: true}, |
| }, |
| methods: { |
| handleStyle() { |
| |
| |
| this.styleStr += ';border-radius: 50%;' |
| |
| this.classStr += ' div_add' |
| |
| this.styleList.push({borderRadius: '50%'}) |
| this.classList.push('div_add') |
| |
| this.styleObj['borderRadius'] = '50%' |
| this.classObj['div_add'] = true |
| } |
| }, |
| computed: {} |
| }) |
| </script> |
| </html> |

【3.4】注意:修改样式未生效的情况
深入响应式原理 — Vue.js (vuejs.org)
- Vue 的响应式系统无法自动检测到对数组元素或对象属性的直接赋值(例如
arr[0] = newValue
或 obj.key = newValue
)。
- Vue 的响应式系统是基于 JavaScript 属性的 getter 和 setter 实现的,而直接的索引赋值或添加新属性不会触发 setter。
- Vue 文档中有提到一些方法,如
Vue.set
或使用 Array.prototype.splice
,来处理这些限制,确保响应式系统能够检测到所有的更新。
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>vue set 修改样式</title> |
| <script src="../js/vue/vue.js"></script> |
| </head> |
| <style> |
| .div1 { |
| height: 300px; |
| width: 300px; |
| background-color: #0a53be; |
| } |
| |
| .div2 { |
| height: 300px; |
| width: 300px; |
| background-color: chartreuse; |
| } |
| </style> |
| <body> |
| <div id="app"> |
| <div style="display: flex;justify-content: space-around"> |
| <div> |
| <h1>样式不生效的情况</h1> |
| <div :class="classList"></div> |
| <p> |
| <button @click="handleClass">修改样式</button> |
| </p> |
| </div> |
| <div> |
| <h1>使用Vue.set使样式生效</h1> |
| <h2>共有很多种方法可以使虚拟dom触发更新</h2> |
| <p>【1】数组中:Vue.set(数组,索引,修改的值)</p> |
| <p>【1.1】对象中:Vue.set(对象,属性名,修改的值)</p> |
| <p>【2】vm.$set:与Vue.set用法一致</p> |
| <p>【3】调用变更方法,会触发更新</p> |
| <div :class="classList2"></div> |
| <p> |
| <button @click="handleClass2">使用【Vue.set】修改样式</button> |
| </p> |
| </div> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| classList: ['div1'], |
| classList2: ['div1'] |
| }, |
| methods: { |
| handleClass() { |
| |
| this.classList[0] = 'div2' |
| |
| console.log('classList',this.classList) |
| }, |
| handleClass2() { |
| |
| |
| Vue.set(this.classList2, 0, 'div2') |
| |
| vm.$set(this.classList2, 0, 'div1') |
| |
| this.classList2.splice(0, 1, 'div2') |
| console.log('classList2', this.classList2) |
| } |
| }, |
| computed: {} |
| }) |
| </script> |
| </html> |

- 需要注意,当我们点击前面一个样式不生效的情况时,在dom上,它的值已经被改变了,只是没能更新
当我们触发了vue的响应式更新时,将会查看页面上修改了的dom属性,并更新

【三】流程控制
【1】条件渲染v-if
v-else-if
v-else
指令 |
释义 |
v-if |
相当于: if |
v-else |
相当于:else |
v-else-if |
相当于:else if |
【1.2】案例:根据分数判断评价
- 将
v-if
v-else-if
v-else
包裹的标签视作一个标签即可,因为只有一个条件会符合条件
- 【注】单独的
v-if
是允许出现的,因为就是判断布尔值决定标签是否渲染
v-else-if
v-else
是不允许单独出现的
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>流程控制</title> |
| <script src="../js/vue/vue.js"></script> |
| </head> |
| <body> |
| <div id="app"> |
| <p>姓名:{{name}}</p> |
| <p>分数:{{score}}</p> |
| <p>评价:【 |
| <span v-if="score === 100">满分</span> |
| <span v-else-if="score > 90">优秀</span> |
| <span v-else-if="score > 80">良好</span> |
| <span v-else-if="score > 60">合格</span> |
| <span v-else>不合格</span> |
| 】</p> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| name: 'user', |
| |
| score: Math.floor(Math.random() * 100), |
| }, |
| methods: { |
| |
| handleScore(){ |
| this.score = Math.floor(Math.random() * 100) |
| } |
| }, |
| computed: {} |
| }) |
| </script> |
| </html> |
【2】遍历渲染v-for
【2.1】遍历数组 遍历对象 遍历数字
| <body> |
| <div id="app"> |
| <h1>v-for:数组</h1> |
| |
| |
| |
| <ul v-for="(item,index) in dataList"> |
| <li>item:{{item}}|index:{{index}}</li> |
| </ul> |
| <h1>v-for:对象</h1> |
| |
| <ul v-for="(value,key,index) in dataObj"> |
| <li>对象的值:{{value}} |对象的键:{{key}} | 对象的索引:{{index}}</li> |
| </ul> |
| <h2>v-for:对象,单个值【遍历出每一个item的值】</h2> |
| <ul v-for="value in dataObj"> |
| <li>对象的值:{{value}}</li> |
| </ul> |
| <h1>v-for:数字</h1> |
| |
| <ul v-for="item in num"> |
| <li>{{item}}</li> |
| </ul> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| |
| dataList: [1, 2, 3], |
| |
| dataObj: {num1: 6, num2: 4, num3: 13}, |
| |
| num: 5 |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |

【4】案例
【4.1】v-if
+v-for
+v-else
控制购物车商品的显示
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>v-if + v-for + v-else控制购物车商品的显示</title> |
| <script src="../js/vue/vue.js"></script> |
| <link rel="stylesheet" href="../css/bootstrap/bootstrap.css"> |
| <script src="../js/bootstrap/bootstrap.js"></script> |
| </head> |
| <body> |
| <div id="box"> |
| <h2 class="text-center">我的购物车</h2> |
| |
| <button @click="show">展示购物车</button> |
| <br> |
| |
| <table v-if="!shopping_car.length==0" class="table table-hover"> |
| <tr> |
| <td>商品名称</td> |
| <td>价格</td> |
| </tr> |
| <tr v-for="item in shopping_car"> |
| <td>{{item.name}}</td> |
| <td>{{item.price}}</td> |
| </tr> |
| </table> |
| |
| <table v-else class="table table-hover"> |
| <tr> |
| <td>商品名称</td> |
| <td>价格</td> |
| </tr> |
| <tr> |
| <td>暂无信息</td> |
| <td>暂无信息</td> |
| </tr> |
| </table> |
| </div> |
| </body> |
| <script> |
| let vm = new Vue({ |
| el: '#box', |
| data: { |
| isActive: false, |
| shopping_car: [] |
| }, |
| methods: { |
| show() { |
| if (this.shopping_car.length) { |
| this.shopping_car = [] |
| } else { |
| this.shopping_car = [ |
| {name: '华为-遥遥领先', price: '2999元'}, |
| {name: '苹果', price: '9元'}, |
| {name: '小米', price: '999元'}, |
| ] |
| } |
| } |
| } |
| }) |
| </script> |
| </html> |

【5】其他知识(了解)
【5.1】更新策略
维护状态 — Vue.js (vuejs.org)
- Vue.js 在使用
v-for
渲染元素列表时的更新策略。
- 在默认情况下,Vue 使用 "就地更新" 策略。这意味着当数据项的顺序改变时,Vue 不会移动 DOM 元素以匹配数据项的新顺序,而是会就地更新每个元素,确保它们在 DOM 中的位置与数据项的索引位置相对应。换句话说,Vue 会尽可能地复用已经存在的 DOM 元素,而不是重新创建或移动它们。
- 这种更新策略的好处是,在处理大型列表时能够更高效地进行 DOM 操作,因为不会频繁地创建、销毁或移动元素,而只是更新已存在的元素。这也可以提高性能并减少浏览器重新渲染的工作量,从而提升用户体验。
- 类似于 Vue 1.x 中的
track-by="$index"
,Vue.js 在更新列表时会跟踪每个元素的索引位置,以便正确地应用 "就地更新" 策略。这意味着你不需要手动指定索引,Vue 会自动处理好这些细节。
【5.2】优化数据效率参数key
API — Vue.js (vuejs.org)
【四】数组更新
【1】变更方法
- 变更方法,顾名思义,会变更调用了这些方法的原始数组
- Vue.js 在响应式地处理数组时,对数组的一些常用变更方法进行了包装,以便能够自动检测到数组的变化,并触发相应的视图更新。这些被包裹过的方法包括:
- push(): 将一个或多个元素添加到数组的末尾,并返回新的长度。
- pop(): 移除数组的最后一个元素,并返回该元素的值。
- shift(): 移除数组的第一个元素,并返回该元素的值,同时将数组长度减一。
- unshift(): 在数组的开头添加一个或多个元素,并返回新的长度。
- splice(): 在指定位置插入或删除元素,并返回被删除的元素组成的数组。
- sort(): 对数组元素进行排序,如果没有传递比较函数,则默认按照 Unicode 码点进行排序。
- reverse(): 将数组中的元素顺序颠倒,第一个元素变为最后一个,最后一个元素变为第一个。
- 这些方法被 Vue.js 包装后,当你使用它们修改数组时,Vue.js 将能够侦测到数组的变化,并在必要时更新视图,以确保视图与数据的同步。这是 Vue.js 实现响应式的重要特性之一,使得开发者可以更轻松地处理数据的变化并实时更新用户界面。
【2】非变更方法
- 非变更方法,它们不会变更原始数组,而总是返回一个新数组。
- 当使用非变更方法时,可以用新数组替换旧数组
Vue 使用一些智能的启发式方法(如虚拟 DOM 的 diff 算法)来尽可能重用现有的 DOM 元素,从而提高性能和效率。这意味着,即使整个数组被替换,如果新数组中包含与旧数组相同的元素,Vue 将尽量保持这些元素的 DOM 表示不变。
- filter():
- 功能:创建一个新数组,其中包含通过某个函数测试的所有元素。
- concat():
- 功能:用于合并两个或多个数组。不会改变现有数组,而是返回一个新数组。
- slice():
- 功能:从现有数组中返回选定的元素。不会改变现有数组。
【3】变更方法与非变更方法实例
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>数组更新</title> |
| <script src="../js/vue/vue.js"></script> |
| </head> |
| <body> |
| <div id="app"> |
| <h1>数组更新</h1> |
| <ul v-for="item in dataList"> |
| <li>{{item}}</li> |
| </ul> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| dataList: [2, 1, 9], |
| }, |
| methods: { |
| |
| handlePush() { |
| this.dataList.push(22) |
| }, |
| handlePop() { |
| this.dataList.pop() |
| }, |
| handleSort() { |
| this.dataList.sort() |
| }, |
| |
| handleFilter() { |
| |
| |
| return this.dataList.filter( |
| item => item < 5 |
| ) |
| } |
| }, |
| computed: {} |
| }) |
| </script> |
| </html> |

【五】表单控制
【1】checkbox和radio
- 当数据绑定的值为字符串时,接收一个值
- 当数据绑定的值为数组时,接收多个值
| <body> |
| <div id="app"> |
| <form> |
| <p> 用户名: <input type="text" v-model="username"></p> |
| <p> |
| 爱好:【checkbox:数组】 |
| <input type="checkbox" v-model="hobby" value="0">爱好1 |
| <input type="checkbox" v-model="hobby" value="1">爱好2 |
| <input type="checkbox" v-model="hobby" value="2">爱好3 |
| </p> |
| <p> |
| 性别: 【radio:字符串】 |
| <input type="radio" v-model="gender" value="0">女 |
| <input type="radio" v-model="gender" value="1">男 |
| </p> |
| <p> |
| 是否记住:【checkbox:字符串/布尔值】 |
| <input type="checkbox" v-model="is_remember"> |
| </p> |
| </form> |
| <hr> |
| <div> |
| <p> |
| 信息如下: |
| username : {{username}} <br> |
| hobby : {{hobby}} <br> |
| gender : {{gender}} <br> |
| is_remember : {{is_remember}} <br> |
| </p> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| username: '', |
| hobby: [], |
| gender: '', |
| is_remember: true |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |

【2】购物车案例
【2.1】前端
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Title</title> |
| <script src="./js/vue/vue.js"></script> |
| <script src="./js/bootstrap/bootstrap.js"></script> |
| <link rel="stylesheet" href="./css/bootstrap/bootstrap.css"> |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"> |
| </head> |
| <body> |
| <div id="app"> |
| <div class="container text-center"> |
| <h1>{{username}}购物车</h1> |
| <div class="row justify-content-md-center"> |
| <div class="col-6"> |
| <table class="table table-hover"> |
| <thead> |
| <tr> |
| <th scope="col">商品id</th> |
| <th scope="col">商品名称</th> |
| <th scope="col">商品价格</th> |
| <th scope="col">商品数量</th> |
| <th scope="col">全选/全不选 |
| <span><input type="checkbox" @change="checkAll" v-model="isAll"></span> |
| </th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr v-for="item in goodsList" v-if="item.count !== 0"> |
| <td>{{item.id}}</td> |
| <td>{{item.name}}</td> |
| <td>{{item.price}}</td> |
| <td> |
| |
| <button class="btn" @click="item.count>1?item.count--:item.count=1">-</button> |
| {{item.count}} |
| <button class="btn" @click="item.count++">+</button> |
| </td> |
| <td> |
| <input type="checkbox" @click="checkOne" v-model="goodsCheck" |
| :value="item" @change="checkOne"> |
| </td> |
| </tr> |
| </tbody> |
| </table> |
| <div> |
| <p>gross:{{goodTotal}}</p> |
| <button class="btn btn-success" @click="goodsSubmit">提交</button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| username:'', |
| isAll: false, |
| goodsList:[], |
| goodsCheck: [] |
| }, |
| methods: { |
| |
| goodsSubmit() { |
| console.log(this.goodsCheck) |
| alert('提交了数据') |
| }, |
| |
| checkAll() { |
| if (this.isAll) { |
| this.goodsCheck = this.goodsList |
| } else { |
| this.goodsCheck = [] |
| } |
| }, |
| |
| checkOne() { |
| |
| this.isAll = this.goodsCheck.length === this.goodsList.length; |
| }, |
| |
| dwindleCount(item) { |
| if (item > 1) { |
| item.count-- |
| } else { |
| item.count = 1; |
| alert('最低数量为1') |
| } |
| } |
| }, |
| computed: { |
| |
| goodTotal() { |
| let total = 0 |
| for (let item of this.goodsCheck) { |
| total += item.price * item.count |
| } |
| return total |
| } |
| }, |
| created() { |
| fetch('http://127.0.0.1:5000/cart/1/').then(response => response.json()).then( |
| res => { |
| console.log(res) |
| this.username=res.username |
| this.goodsList = res.result |
| } |
| ) |
| } |
| }) |
| </script> |
| </html> |
【2.2】后端(flask)
| import json |
| |
| from flask import Flask, jsonify |
| |
| app = Flask(__name__) |
| |
| |
| @app.route('/') |
| def index(): |
| res = jsonify({ |
| 'code': 100, |
| 'msg': 'success', |
| 'result': { |
| 'name': 'lea4ning', |
| 'age': 20 |
| } |
| }) |
| res.headers['Access-Control-Allow-Origin'] = '*' |
| return res |
| |
| |
| @app.route('/cart/1/') |
| def get_cart(): |
| with open('../goods.json', 'r', encoding='utf-8') as f: |
| data = json.load(f) |
| res = jsonify({ |
| 'code': 100, |
| 'msg': 'success', |
| 'username': 'lea4ning', |
| 'result': data |
| }) |
| res.headers['Access-Control-Allow-Origin'] = '*' |
| return res |
| |
| |
| if __name__ == '__main__': |
| app.run() |
| [ |
| { |
| "id": 101, |
| "name": "HuaWei", |
| "price": 7999, |
| "count": 3 |
| }, |
| { |
| "id": 102, |
| "name": "apple", |
| "price": 7998, |
| "count": 1 |
| }, |
| { |
| "id": 103, |
| "name": "Xiaomi", |
| "price": 3999, |
| "count": 3 |
| }, |
| { |
| "id": 104, |
| "name": "Oppo", |
| "price": 1230, |
| "count": 6 |
| } |
| ] |
【2.3】演示效果

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了