v-bind
v-bind动态属性数据绑定
src属性中的数据它是通过data数据源中得到的
标准语法:v-bind:属性="动态数据"
简写语法::属性="动态数据"
<img v-bind:src="src" alt=""> <img :src="src" alt="">
v-for
循环 vue对于v-for进行了增强,它可以用for/of 都可以,而且两者都可以进行对象的迭代
语法:语法:vue2中小括号可以写,也可以不写,在vue3中一定要写小括号
注:vue2中如果一个标签中同时有v-if和v-for,则v-for的优先级高于v-if,所以在vue2中不推荐两者在一个标签中
vue3中v-if优先级高于v-for
v-for="(元素,索引) in/of 对象"
v-for="(元素,键名,索引)" in/of 对象
注:v-for建议给循环项每个添加一个key来做标识,用于提升性能,key值,一定是唯一不重复的,不建议使用循环的索引当作key值,一般在和后台请求数据时,要求后台提供一个唯一的id给我们
<li v-for="(item,index) in user">{{index}} -- {{item}}</li> <li v-for="item,index in user">{{index}} -- {{item}}</li> <li v-for="item,index in user" :key="item.id">{{item.name}}</li> <script> const vm = new Vue({ el: '#app', data: { // user: ['张三', '李四', '王五'] // user: [{ id: 2, name: '李四' }, { id: 1, name: '张三' }] user: { id: 1, name: '张三' } } }) </script>
循环对象
<div v-for="item,key,index in user" :key="key">{{index}} -- {{key}} -- {{item}}</div>
v-on事件
v-on:事件名="实现的方法[此方法定义在vue配置中的methods中]"
v-on使用很频繁 @事件名="方法"
绑定的方法,它可以写小括号,也可以不写小括号
<button @click="clickFn">点击事件</button>
注:methods中定义方法不用使用箭头函数,但是在方法体中建议使用箭头函数,用来保持this指向
v-on事件对象
如果你绑定方法时,没有写小括号,则vue在解析时,会自动给你映射一个event给绑定方法
<button @click="clickFn">点击事件</button>
如果你绑定方法时,有写小括号,则需要手动把event对象传过去$event,$event可以传多个,但是建议只传一个,一般写在第1位或最后1位
<button uname="李四" data-uname="张三" @click="evt=>clickFn($event)">点击事件</button>
<script> const vm = new Vue({ el: '#app', data: { num: 100 }, // 注:methods中定义方法不用使用箭头函数,但是在方法体中建议使用箭头函数,用来保持this指向 methods: { // event可以用它来完成dom数据的获取 clickFn(evt) { this.num++ console.log(evt.target.getAttribute('uname')); console.log(evt.target.dataset.uname) }, onEnter(evt) { if (evt.keyCode === 13) { console.log(evt.target.value); } } } }) </script>
todolist案例
<!-- 第1步: 引入vue库文件 --> <script src="./js/vue.js"></script> </head> <body> <!-- 第2步:挂载点 --> <div id="app"> <input placeholder="请输入内容" type="text" @keyup="onEnter"> <hr> <ul> <!-- 条件渲染 --> <li v-if="todos.length===0">无任务</li> <li v-else v-for="item,index in todos" :key="item.id"> <span>{{item.title}}</span> <!-- <span @click="del(item.id)">删除</span> --> <span @click="del(index)">删除</span> </li> </ul> </div> <!-- 第3步:实例化vue --> <script> const vm = new Vue({ el: '#app', data: { todos: [] }, methods: { onEnter(evt) { if (evt.keyCode === 13) { this.todos.push({ id: Date.now(), title: evt.target.value.trim() }) evt.target.value = '' } }, // del(id) { // // 删除 // this.todos = this.todos.filter(item => item.id != id) // } del(index) { // 删除 它可以使用 vue中提供的变异方法splice来完成,用此方法它会触发视图更新 this.todos.splice(index, 1) } } }) </script>
事件修饰符
多个修饰符,通过点来连接操作
动态样式
class样式的动态添加,对象和数组方式
对象: {key它就是样式名称:布尔值【true生效,false不生效】} -- 一般用于开关显示
<div :class="titleClass">我是一个标题</div>
数组:[元素样式名称] 一般对于追加新样式,使用数组
addStyle() { // 给数组添加元素,元素就是样式名称,这样它会就追加样式 // push unshift shift pop splice sort reverse 调用时都会让视图更新 this.titleStyle.push('active') this.titleStyle.push('font30') }
//地址变了 所以会显示 this.titleClass = { ...this.titleClass, font30: true }
let titleClass=JSON.parse(JSON.stringify(this.title.titleClass)); titleClass.font30=ture; this.titleClass=titleClass;
this.titleClass = Object.assign({}, this.titleClass, { font30: true })
动态给对象添加成员属性
this.$set(this.titleClass, 'font30', true) let obj = Object.assign(参数1地址和返回值地址是同一个地址)
动态样式-style
//style样式的动态添加,对象和数组方式 //对象 <div :style="{color:'blue',fontSize:'30px'}">我是一个标题</div> //数组 <div :style="[{color:'blue'},{fontSize:'30px'}]">我是一个标题</div>
v-model
v-model它是一个语法糖,value和事件的综合体
用户登录案例
<script src="./js/vue.js"></script> </head> <body> <!-- 第2步:挂载点 --> <div id="app"> <div> <label> 账号: <input type="text" v-model="username"> </label> </div> <div> <label> 密码: <input type="text" v-model="password"> </label> </div> <div> <textarea v-model="intro"></textarea> </div> <div> <button @click="dologin">登录系统</button> </div> </div> <!-- 第3步:实例化vue --> <script> const vm = new Vue({ el: '#app', data: { username: '', password: '', intro: 'afewlkfjewl' }, methods: { dologin() { console.log(this.username, this.password, this.intro); } } }) </script>
单个复选框
单个复选框,定义的数据类型为布尔类型 true选中,false未选中
click事件可以用,但它是的状态有太过提前,用onchange事件,改变后来获取
<input type="checkbox" @change="clickFn"> <script> const vm = new Vue({ el: '#app', data: { // 单个复选框一定要用布尔类型 checked: false }, methods: { clickFn(evt) { console.log(evt.target.checked); } } }) </script>
多个复选框
多个复选框,定义的数据类型为数组
指定它的value值
<ul> <li> <input type="checkbox" value="html" v-model="lessons">html </li> <li> <input type="checkbox" value="css" v-model="lessons">css </li> <li> <input type="checkbox" value="js" v-model="lessons">js </li> </ul> <hr> <div>{{lessons}}</div> </div> </div> <!-- 第3步:实例化vue --> <script> const vm = new Vue({ el: '#app', data: { lessons: ["js",'css'] }, methods: { } }) </script>
全选
<div id="app"> <div> <input type="checkbox" v-model="checked" @change="onSelected"> <li> <input type="checkbox" value="html" @change="selectlesson" v-model="lessons">html </li> <li> <input type="checkbox" value="css" @change="selectlesson" v-model="lessons">css </li> <li> <input type="checkbox" value="js" @change="selectlesson" v-model="lessons">js </li> </ul> <hr> <div>{{lessons}}</div> </div> </div> <script> const vm = new Vue({ el: '#app', data: { lessons: [], checked: false }, methods: { onSelected(evt) { // 选中了 if (evt.target.checked) { this.lessons = ["js", 'html', 'css'] } else { this.lessons = [] } }, selectlesson() { // 只要来操作数据源就可以改变视图 //==判断 当this.lessons.length == 3时为true,赋值给checked this.checked = this.lessons.length == 3 } } }) </script>
单选和下拉
<div id="app"> <div> <h3>{{sex}} -- {{city}}</h3> <!-- 定义的数据类型为字符串 --> <label> <input type="radio" value="先生" v-model="sex">建行 </label> <label> <input type="radio" value="女神" v-model="sex">招行 </label> </div> <hr> <div> <select v-model="city"> <option value="0">==选择==</option> <option value="wh">芜湖</option> <option value="bj">北京</option> </select> </div> </div> <!-- 第3步:实例化vue --> <script> const vm = new Vue({ el: '#app', data: { sex: '先生', city: '0' } }) </script>
v-model
<!-- v-model修饰符 --> <!-- 延时更新数据源中的数据 --> <!-- <input v-model.lazy="title"> --> <!-- 去空格 trim --> <!-- <input v-model.trim="title"> --> //表单提交的数据都变成了字符串 <!-- 转为数值 number --> <input type="number" v-model.number="n1"> + <input type="number" v-model.number="n2"> = {{n1+n2}}
购物车
<div id="app"> <table border="1" width="600"> <tr> <th>序号</th> <th>名称</th> <th>单价</th> <th>数量</th> <th>操作</th> </tr> <tr v-for="item,index in carts"> <td>{{index+1}}</td> <td>{{item.name}}</td> <td>{{item.price}}</td> <td> <!-- <button @click="setNum(1,item)">+++</button> --> <button @click="setNum(1,index)">+++</button> <input type="number" v-model="item.num"> <!-- <button @click="setNum(-1,item)">---</button> --> <button @click="setNum(-1,index)">---</button> </td> <td> <button @click="del(index)">删除</button> </td> </tr> </table> <hr> <h3> 合计: {{totalPrice()}} </h3> </div> <!-- 第3步:实例化vue --> <script> const vm = new Vue({ el: '#app', data: { carts: [ { id: 1, name: '小米12pro', price: 1, num: 1 }, { id: 2, name: '华为手机', price: 2, num: 1 }, { id: 3, name: '水果手机', price: 3, num: 1 }, ] }, methods: { /* setNum(n, item) { item.num += n }, */ setNum(n, index) { this.carts[index].num += n // if (this.carts[index].num <= 1) this.carts[index].num = 1 // if (this.carts[index].num >= 3) this.carts[index].num = 3 // 用最大值和最小值来限制它的范围,可以了解一下 this.carts[index].num = Math.min(3, Math.max(1, this.carts[index].num)) }, totalPrice() { // 聚合运算 return this.carts.reduce((prev, { price, num }) => { prev += price * num return prev }, 0) }, del(index) { confirm('确定删除') && this.carts.splice(index, 1) } } }) </script>
持久化
function getCarts() { return !window.localStorage.getItem('carts') ? [{ id: 1, name: '小米12pro', price: 1, num: 1 }, { id: 2, name: '华为手机', price: 2, num: 1 }, { id: 3, name: '水果手机', price: 3, num: 1 }] : JSON.parse(window.localStorage.getItem('carts')) } function setCarts(carts) { window.localStorage.setItem('carts', JSON.stringify(carts)) }
自定义指令
自定义指令常用钩子函数
bind 第一次绑定到元素时调用
inserted 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
update 数据更新时调用
componentUpdated 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind 只调用一次,指令与元素解绑时调用。
// 定义自定义指令 // 全局 和 局部 // 全局定义,所有的组件或vue的实例都会生效,可以使用 // 对象写法,它提供5个钩子函数 // Vue.directive('指令名称,不需要写v-开头',对象或函数) /* Vue.directive('red', { // bind 第一次绑定到元素时调用 bind(el, bindings) { console.log('bind'); // 通过dom操作来完成对于当前绑定的标签添加css样式 // el.style.color = 'red' // el.style.fontSize = '30px' // cssText方案,现在推荐 // el.style.cssText = 'font-size: 30px;\ // color: blue;' el.style.cssText = `color:red;font-size:30px` }, // inserted inserted(el, bindings) { console.log('inserted'); }, // update update(el, bindings) { console.log('update'); }, // componentUpdate componentUpdated(el, bindings) { console.log('componentUpdated'); }, // unbind unbind(el, bindings) { console.log('unbind'); }, }) */ const vm1 = new Vue({ el: '#app1', data: { isShow: true, title: '我是一个标题111' }, // 定义局部指令,只有当前的实例能用 directives: { red: { // bind它还没有绑定到父元素中,初始化 bind(el) { el.style.cssText = `color:red;font-size:30px` const divDom = document.createElement('div') divDom.innerHTML = '我是它的兄弟' el.appendChild(divDom) }, // inserted(el) { // console.log(el.parentNode); // } }, auth: { inserted(el) { // query/search 字符串 ==> 对象 // if (location.search != '?username=admin') { // el.remove() // } // URLSearchParams它是html5提供的新的Api方法,用于获取url地址中的search转为对象 let urlSearch = new URLSearchParams(location.search) // console.log(urlSearch.get('username')); if (urlSearch.get('username') != 'admin') { // 以前兼容性更好的写法,但是现在可以不管 // el.parentNode.removeChild(el) el.remove() } } } } }) const vm2 = new Vue({ el: '#app2', data: { }, directives: { red: { bind(el) { el.style.cssText = `color:blue;font-size:30px` } } } }) </script>
模块化
const validateMethod = { phone(el) { // 手机号码 let reg = /^1[3-9]\d{9}$/ if (!reg.test(el.value)) { // 不合法 el.style.color = 'red' if (!el.nextSibling) { const spanDom = document.createElement('span') spanDom.innerHTML = '不合法,修改一下' // el.parentNode?.appendChild(spanDom) el.parentNode && el.parentNode.appendChild(spanDom) } } else { el.style.color = 'black' el.nextSibling && el.nextSibling.remove() } console.log('phone'); }, email(el, value) { console.log('email') }, str(el, value) { if (value) { if (el.value.length > value.len) { if (!el.nextSibling) { const spanDom = document.createElement('span') spanDom.innerHTML = value.msg el.parentNode?.appendChild(spanDom) } } else { el.nextSibling?.remove() } } } } Vue.directive('validate', (el, { value, modifiers }) => { Object.keys(modifiers).forEach(name => { validateMethod[name](el, value) }) }) const vm1 = new Vue({ el: '#app', data: { phone: '13525125121', email: 'aa@aa.com', str: 'aaa' }, methods: { } })