https://cn.vuejs.org/
vue.js:是一个 js 框架,使开发无需关心dom,只关心数据
vue 源码和 jQuery 一样,也是一个自执行函数,结构如下
(function(){
}())
vue中, 只要数据有更改, 就会对容器重新解析渲染元素
Vue.js 作者参考 MVVM 模型,设计了 Vue.js 中所特有的模型
此处并不对 MVVM 详细讲解,主要针对 Vue.js 中根据 MVVM 设计出来的特有模型进行讲解
MVVM模型
代码对应:
观察发现,详见Vue.js中的this
data中设置的所有属性,都会出现在 Vue.js 实例上(数据代理),
Vue.js 实例上所有属性 及 Vue.js原型上所有属性,在 视图View(模板)中都可以直接使用
需求:有一个input输入框,在里面输入什么,页面的p标签就显示什么
原生js实现
<body>
<input type="text" id="inp">
<p id="show">我是文本p>
<script>
document.getElementById('inp').oninput = function(){
document.getElementById('show').innerText = this.value
}
script>
body>
vue实现
<body>
<div id="app">
<input type="text" v-model='msg'><br>
<p>{{msg}}p>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: '#app',
data: {
msg: '这是一个p标签'
}
})
script>
body>
解决打印提示:
谷歌应用商店下载:https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=zh-CN
<script>
new Vue.js({
// el:selement的缩写,相当于是元素选择器 -> querySelector
// 只会选中一个容器元素,布局都放在这个容器中
el: '#app',
data: {
msg: '这是一个p标签'
}
})
script>
<body>
<div class="app">
<p v-text="msg">我是一个p标签 <span>哈哈span> p>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el:'.app',
data: {
msg:'阿斯蒂芬'
}
})
script>
body>
类似于原生js中的 innerHtml,和 v-text 相似,但是可以解析成html
指令值会替换标签中所有的值,并以html的形式展示出来
常用语富文本(带标签的字符串)
v-html指令值(单行表达式):
表单控件或者组件上
使用 双向数据绑定。
Demo - 01:单选框-双向绑定
<body>
<div id="app">
<label>
<input type="radio" name="gender" value="male" v-model="gender">男
label>
<label>
<input type="radio" name="gender" value="female" v-model="gender">女
label>
<p>你选择的性别是:{{ gender }}p>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: '#app',
data: {
gender:'female'
}
})
script>
body>
Demo - 02:多选框-双向绑定
<body>
<div id="app">
<input type="checkbox" name="likeFood" v-model="likeFood" value="西瓜">西瓜
<input type="checkbox" name="likeFood" v-model="likeFood" value="可乐">可乐
<input type="checkbox" name="likeFood" v-model="likeFood" value="鸡腿">鸡腿
<input type="checkbox" name="likeFood" v-model="likeFood" value="鸡排">鸡排
<p>你喜欢的食物是:{{ likeFood }}p>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: '#app',
data: {
likeFood:['西瓜', '鸡腿']
}
})
script>
body>
Demo - 03:下拉菜单-双向绑定
<body>
<div id="app">
<select name="" id="" v-model="city">
<option value="九江市">九江市option>
<option value="赣州市">赣州市option>
<option value="抚州市">抚州市option>
<option value="吉安市">吉安市option>
select>
<p>你选择的城市是: {{ city }}p>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: '#app',
data: {
city: '吉安市'
}
})
script>
body>
以下三个修饰符只能给 表单控件或者组件上
使用
限制:
components
.trim: 去除输入内容的两端空格
.lazy: 失去焦点的瞬间收集表单项的数据( 在
“change”
时而非“input”
时更新 ), .lazy 只在按下回车键和失去焦点的时候才触发, 收集用户录入内容
.number: 输入字符串转为有效的数字(用户在页面输入的所有内容都是字符串, 该修饰符可以转为数字)
from中的属性action属性
1.用于指定表单提交的地址
2.但是目前的前后端交互已经不用这个属性了
3.现在主要是使用from标签作为表单元素的呈现、结构
Demo:
关于案例的扩展部分:
@submit.prevent
是为了禁用表单默认的submit事件.ckick
控制只能点击一次<body>
<div id="root">
<form @submit.prevent>
账号: <input type="text" v-model.trim="userInfo.account"> <br><br>
密码: <input type="password" v-model.trim="userInfo.password"> <br><br>
年龄: <input type="number" v-model.number="userInfo.age"> <br><br>
性别:
<label>
<input type="radio" name="gender" value="male" v-model="userInfo.gender">男
label>
<label>
<input type="radio" name="gender" value="female" v-model="userInfo.gender">女
label> <br><br>
爱好:
抽烟 <input type="checkbox" name="hobby" value="smoking" v-model="userInfo.hobby">
喝酒 <input type="checkbox" name="hobby" value="drink" v-model="userInfo.hobby">
烫头 <input type="checkbox" name="hobby" value="perm" v-model="userInfo.hobby">
<br><br>
籍贯:
<select v-model="userInfo.province">
<option value="">--请选择--option>
<option value="江西">江西option>
<option value="广东">广东option>
<option value="湖南">湖南option>
<option value="湖北">湖北option>
<option value="安徽">安徽option>
select>
<br><br>
座右铭:
<textarea v-model.lazy="userInfo.motto">textarea>
<br><br>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="#">《用户协议》a>
<br><br>
<button @click.once='demo'>提交button>
form>
div>
<script src="../js/vue.js">script>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
userInfo: {
account: "打发士大夫撒",
password: '',
age: '',
gender: 'female',
hobby: [],
province: '',
motto: '',
agree: '',
}
},
methods: {
demo() {
console.log(JSON.stringify(this.userInfo));
console.log((this.userInfo));
}
},
})
script>
body>
用于给DOM元素绑定事件
语法:v-on:事件名="单行表达式 或者是一个已经写好的函数"
<a v-on:click="doSomething">...a>
<a @click="doSomething">...a>
<a @[event]="doSomething"> ... a>
Demo - 01:语法练习
<body>
<div id="app">
<button v-on:click="clickNum++">左键单击+1button><br>
<button v-on:dblclick="dblclickNum++">左键双击+1button><br>
<button v-on:mouseenter="clickEnter++">鼠标移入+1button>
<p>你左键单击了 {{clickNum}} 次p>
<p>你左键双击了 {{dblclickNum}} 次p>
<p>你鼠标移入了 {{clickEnter}} 次p>
<br><br><hr>
<button @click='sayHi'>点我调用sayHi方法-1button><br>
<button v-on:click='sayHello("卡卡西")'>点我调用sayHello方法-2button>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: "#app",
data: {
clickNum: 0,
dblclickNum: 0,
clickEnter:0
},
methods: {
sayHi() {
alert("hi, v-on!")
},
sayHello(name) {
alert(`Hello, ${name}!`)
}
}
})
script>
body>
Demo - 02:模拟购物车“+”、“-”按钮增减
<body>
<div id="app">
<button @click='reduce'>-button>
<span>{{goodsNum}}span>
<button @click='add'>+button>
<br>
<p>你选择了{{goodsNum}}件商品p>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: '#app',
data: {
goodsNum : 0
},
methods: {
add(){
this.goodsNum++
},
reduce(){
if(this.goodsNum > 0)
this.goodsNum--
}
}
})
script>
body>
https://cn.vuejs.org/v2/api/#v-bind
但是官网描述过于生硬复杂
—> 适用于单属性值 ; 样式的类名不确定, 需要动态指定
用法:
v-bind:属性名=“属性值(单行表达式)”
1. 变量 2.基本运算 3.三元运算符
简写形式 -> 用
:
代替v-bind
<body>
<div id="app">
<button @click='btnClick'>点我随机切换图片button>
<br>
<img :src="imgSrc" :alt="imgDesc">
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: '#app',
data: {
imgSrc: 'https://img0.baidu.com/it/u=3322511293,1731852465&fm=26&fmt=auto&gp=0.jpg',
imgDesc: '默认图片'
},
methods: {
btnClick() {
let imgArr =
['https://img0.baidu.com/it/u=3528109070,1097836127&fm=26&fmt=auto&gp=0.jpg',
'https://img1.baidu.com/it/u=2729040690,3640786697&fm=26&fmt=auto&gp=0.jpg',
'https://img0.baidu.com/it/u=3597130045,896951826&fm=26&fmt=auto&gp=0.jpg',
'https://img1.baidu.com/it/u=596060129,2415043124&fm=26&fmt=auto&gp=0.jpg',
'https://img2.baidu.com/it/u=3536018770,3299932163&fm=26&fmt=auto&gp=0.jpg']
let desc = ['图片1','图片2','图片3','图片4','图片5',]
let index = Math.floor(Math.random() * 5)
this.imgSrc = imgArr[index]
this.imgDesc = desc[index]
}
}
})
script>
body>
<div class="box" :class="classArr">
{{ msg }}
</div>
`在data中,定义一个数组classArr,也就是样式名,其中数组的值会被其他形式改变`
—> 适用于多属性值
<head>
...
<style>
.box {
width: 100px;
height: 100px;
background-color: skyblue;
}
.active {
width: 150px;
height: 150px;
background-color: tomato;
}
.fontSize {
font-size: 20px;
}
style>
head>
<body>
<div id="app">
<button @click='bolValue = !bolValue'>点我改变盒子样式button>
<div class="box" :class="{active:bolValue, fontSize:!bolValue}">
{{ msg }}
div>
div>
<script src="./vue.js">script>
<script>
new Vue.js({
el: '#app',
data: {
msg:'我是div大盒子',
bolValue: true
}
})
script>
body>
用于对数组、对象、字符串进行遍历,得到每一项的值、索引,从而对数据列表进行渲染处理
开发中,应用于数组最为常见
用法:
数组:v-for="(item, index) in/of array"
// ietm:数组中的每一个元素,index:索引(可省略)
// 注意item 和 index的顺序
对象:v-for="(key, value, index) in/of object"
// key:对象的属性名,value:对象的属性值,index:在对象中序号(索引)
Demo要求:
<head>
...
<style>
.active {
background-color: tomato;
}
li {
cursor: pointer;
}
style>
head>
<body>
<div id="app">
<ul>
<li :class='{active: clickEventLi == index}'
@click='clickLi(item,index)'
v-for='(item,index) in/of arr'>{{item}} --- {{index}} li>
ul>
<ul>
<li v-for='(key,value,index) in/of obj'>{{key}} --- {{value}} --- {{index}} li>
ul>
div>
<script src="./vue.js">script>
<script>
const vm = new Vue.js({
data: {
// 记录当前点击的li标签索引
clickEventLi : -1,
arr: ['漩涡鸣人','春也樱', '宇智波佐助','旗木卡卡西'],
obj: {name:"张三",sco:"100",age:18}
},
methods: {
clickLi(item,index){
this.clickEventLi = index
alert(`点击的是第${index+1}个li标签,人物是${item}`)
}
},
})
vm.$mount("#app")
script>
body>
- 适用于:切换频率较低的场景。
- 特点:不展示的DOM元素直接被移除。
- 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
<body>
<div id="app">
<p v-if='age < 18'>未成年p>
<p v-else-if="age > 18 && age < 35">青年p>
<p v-else-if="age > 35 && age < 60">中年p>
<p v-else>老年p>
div>
<script src="./vue.js">script>
<script>
Vue.js.config.productionTip = false
const vm = new Vue.js({
data: {
age: 32
}
})
vm.$mount("#app")
script>
body>
在 vu 中, v-if 只能配合 templete标签 使用
1. 当定义的变量n等于1时,下列三个h2标签才同时显示, 2. 当满足条件,三个h2标签渲染到页面上的时候,template标签会消失, 3. 不影响页面结构
<template v-if='n === 1'>
<h2>Vue.jsh2>
<h2>Reacth2>
<h2>Angularh2>
template>
语法: v-show = “单行表达式”
v-if 条件渲染(动态控制节点是否存在)。
true:渲染该标签
false:不会渲染该标签
v-show 只是控制标签的显示与隐藏(display:none)。
- true:就显示该标签
- false:就隐藏该标签实际就是控制display:none
应用场景:
控制 vue 实例化完成前的 dom 样式
- 本质是一个特殊属性, Vue实例创建完毕接管容器后, 会删出 v-cloak 属性;
- 使用 css 配合 v-cloak 可以解决网速慢时, 页面显示出“{{ xxx }}”的问题;
- 解决原理: 先隐藏, 替换好值之后再显示最终的值.
问题引入:
<body>
<div id="app">
<div> {{ msg }} div>
div>
<script src="./vue.js">script>
<script>
// 这里用定时器是为了模拟发送请求的数据延时
setTimeout(() => {
const vm = new Vue.js({
data: {
msg: "星星之火可以燎原"
}
})
vm.$mount("#app")
}, 1500)
script>
body>
**问题:**上述代码片段为现象模拟,当发送请求时,有时候网络延时,在页面中会出现这样一个现象
解决:
在标签上添加“v-cloak”属性
通过观察可以看到:
根据观察的特点,可以利用当数据没来,标签拥有 v-cloak 属性这个特点,对其设置隐藏
<head>
...
<style>
/* 属性选择器 */
/* 当数据还没来,隐藏拥有 v-cloak 属性的div */
div[v-cloak] {
display: none;
}
style>
head>
<body>
<div id="app">
<div v-cloak> {{ msg }} div>
div>
<script src="./vue.js">script>
<script>
// 这里用定时器是为了模拟发送请求的数据延时
setTimeout(() => {
const vm = new Vue.js({
data: {
msg: "星星之火可以燎原"
}
})
vm.$mount("#app")
}, 1500)
script>
body>
- 让元素不会被vue渲染
- 让拥有该指令的标签内容,原样输出(跳过其所在节点的编译过程)
- 可以利用它跳过: 没有使用指令语法、插值语法的节点, 加快编译
- 使用场景: IT知识官网(vue官网)、技术博客网站,需要让代码原样输出,就可以用 v-pre
类比 pre 标签,原样输出:
<body>
<div>
北国风光,
千里冰封,
万里雪飘。
div>
<pre>
北国风光,
千里冰封,
万里雪飘。
pre>
注意-注意pre标签在vue中的使用
- pre标签 仅仅是不会解析空格和回车, 会把它们都渲染到页面上
- vue的插值语法和指令等语法, 同样可以在 pre标签中使用
- 页面效果
示例:
<body>
<div id="app">
<div v-pre>{{msg}}div>
div>
<script src="./vue.js">script>
<script>
const vm = new Vue.js({
data: {
msg:'星星之火可以燎原'
}
})
vm.$mount("#app")
script>
body>
页面效果:
打印 vue 的实例对象,在它所属的原型对象上,可以看到红色部分,都加了 $ 前缀,这一类方法都是给开发者的使用的,不加 $ 前缀的方法是 Vue.js 自己用的。
el: 'css选择器'
$mount('css选择器)'
<body>
<div id="app">
<h1>你好,{{name}}h1>
div>
<script src="./vue.js">script>
<script>
// 阻止 vue 在启动时打印生产提示
Vue.js.config.productionTip = false
const vue = new Vue.js({
// el: '#app',
data: {
name: '可爱的人'
}
})
// 和 el: '#app' 具有同样的作用
// vue.$mount('#app')
// vue.$mount()的方式更为灵活,例如:设置2秒后挂载在指定容器上
setTimeout(()=>{
vue.$mount('#app')
}, 2000)
console.log(vue);
script>
body>
<body>
<div id="app">
<h1>你好,{{name}}h1>
div>
<script src="./vue.js">script>
<script>
// 阻止 vue 在启动时打印生产提示
Vue.js.config.productionTip = false
new Vue.js({
el: '#app',
// 写法一:对象式
// data: {
// name: '可爱的人'
// }
// 写法二:函数式,注意data绝对不能写成箭头函数(this问题)
data(){
return {
name: '可爱的人'
}
}
})
script>
作用: 解决模板渲染冗余问题
开发中, 很多时候渲染一个数据需要经过一些处理(如反转字符串、求和), 就会增加模板语法的复杂福, 不便于维护且造成代码冗余, 可以通过计算属性解决
注意:
computed对象中
, 在函数体中一定要 把计算的结果return
, 不然该计算属性没有值;原理
第一次
使用计算属性时, vue会通过 setter方法 计算属性函数, 然后把返回值缓存起来
;第二次
使用计算属性时, vue会直接从缓存中读取
, 不执行计算属性的setter函数数据发生变化时
, 计算属性的 setter方法 才会重新执行一次
.关于计算属性的修改
在计算属性中
自定义一个 set方法
// newVlue是要修改的值, oldValue是修改之前的值
set(newVlue, oldValue){
// dosomething
}
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了objcet.defineproperty方法提供的getter和setter。
- get函数什么时候执行?
- 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
- 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生
示例Demo:
<body>
<div id="app">
<button @click='updateNum'>点我更改num1和num2的值button>
<pre>
num1 --> {{num1}}
num2 --> {{num2}}
pre>
<div>computed属性计算后的值:{{add}}div>
div>
<script src="../js/vue.min.js">script>
<script>
new Vue.js({
data: {
num1: 10,
num2: 20,
},
methods: {
updateNum(){
this.num1 *= 10,
this.num2 *= 10
}
},
computed: {
add(){
console.log(this);
return this.num1 + this.num2
}}
}).$mount("#app")
script>
body>
Demo: 购物车
<body>
<div id="app">
<div v-for="(item, index) in list" :key="index">
<span>{{item.goods}}span>
<button @click='sub(index)'>-button>
<input type="text" v-model="item.number">
<button @click='add(index)'>+button>
div>
<br>
<span>总价是:{{priceTitle}}span>
div>
<script src="../js/vue.min.js">script>
<script>
const vm = new Vue.js({
data: {
list: [
{ goods: '可乐', price: '4', number: 0 },
{ goods: '荔枝', price: '10', number: 0 },
{ goods: '山竹', price: '30', number: 0 },
]
},
methods: {
sub(index) {
if(this.list[index].number > 0)
this.list[index].number--
},
add(index) {
this.list[index].number++
}
},
computed: {
priceTitle() {
let allPrice = 0
this.list.forEach(item => allPrice += item.price * item.number)
return allPrice
}
}
})
vm.$mount("#app")
script>
body>
`写法一: 通过 vm.$watch 监视(和methods同级)`
watch: {
// 需要监视的属性
isHot: {
// 初始化时让handler函数调用一下,默认false
immediate: true,
// 当isHot发生改变时,调用handler函数
// handler函数接收2个参数,第一个是变化后的值,第二个是变化前的值
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
}
}
},
`写法二: new Vue.js 时传入 watch 配置`
// 参数1:监视的对象 参数2:配置项,和写法一中的一样
vm.$watch('isHot', {
immediate: true,
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
}
})
完整Demo:
<body>
<div id="app">
<h2>今天天气很{{info}}h2>
<button @click='changeWeather'>点我改变天气button>
div>
<script src="../js/vue.js">script>
<script>
const vm = new Vue.js({
el: '#app',
data: {
isHot: true
},
/* 写法一:
// 监视属性
watch: {
// 需要监视的属性
isHot: {
// 初始化时让handler函数调用一下,默认false
immediate: true,
// 当isHot发生改变时,调用handler函数
// handler函数接收2个参数,第一个是变化后的值,第二个是变化前的值
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
}
}
}, */
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
})
// 写法二:
vm.$watch('isHot', {
immediate: true,
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
}
})
script>
body>
deep属性
深度监视:
- Vue.js中的watch默认不监测对象内部值的改变(一层)。
- 配置 deep:true 可以监测对象内部值改变(多层)。
备注:
- Vue.js自身可以监测对象内部值的改变,但 Vue.js 提供的 watch 默认不可以 (为了效率) !
- 使用 watch 时根据数据的具体结构,决定是否采用深度监视。
<body>
<div id="app">
<h2>a的值是{{number.a}}h2>
<button @click='number.a++'>点我让a+1button>
<h2>a的值是{{number.b}}h2>
<button @click='number.b++'>点我让b+1button>
div>
<script src="../js/vue.min.js">script>
<script>
new Vue.js({
el: '#app',
data: {
number: {
a: 1,
b: 2
}
},
watch: {
// 监视多级结构中所有属性的变化
number: {
// 开启监测对象内部层的改变
deep: true,
handler() {
console.log('number改变了');
}
}
}
})
script>
body>
<body>
<div id="app">
<h2>今天天气很{{info}}h2>
<button @click='changeWeather'>点我改变天气button>
div>
<script src="../js/vue.min.js">script>
<script>
new Vue.js({
el: '#app',
data: {
isHot: true
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
watch: {
/* 如果需要监视的目标对象除了handler函数,还要写其他配置项,不能采用简写!
// 正常写法
number: {
immediate:true,
deep: true,
handler() {
console.log('number改变了');
}
}*/
// 简写,(只有handler函数一个配置项时才能简写)
isHot(newValue, oldValue) {
console.log(newValue, oldValue)
}
}
})
script>
body>
或者:
// 注意不能写成箭头函数
vm.$watch('isHot', function(newValue, oldValue){
console.log(newValue, oldValue)
})
Vue.js监视数据的原理:
vue会监视data中所有层次的数据。
如何监测对象中的数据?
通过setter实现监视,且要在new Vue.js时就传入要监测的数据。
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
1. 调用原生对应的方法对数组进行更新。
2. 重新解析模板,进而更新页面。
在Vue.js修改数组中的某个元素一定要用如下方法:
特别注意:Vue.js.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!
Demo:
<html>
...
<style>
button{
margin-top: 10px;
}
style>
<script type="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
<h1>学生信息h1>
<button @click="student.age++">年龄+1岁button> <br/>
<button @click="addSex">添加性别属性,默认值:男button> <br/>
<button @click="student.sex = '未知' ">修改性别button> <br/>
<button @click="addFriend">在列表首位添加一个朋友button> <br/>
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三button> <br/>
<button @click="addHobby">添加一个爱好button> <br/>
<button @click="updateHobby">修改第一个爱好为:开车button> <br/>
<button @click="removeSmoke">过滤掉爱好中的抽烟button> <br/>
<h3>姓名:{{student.name}}h3>
<h3>年龄:{{student.age}}h3>
<h3 v-if="student.sex">性别:{{student.sex}}h3>
<h3>爱好:h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
li>
ul>
<h3>朋友们:h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
li>
ul>
div>
<script type="text/javascript">
Vue.js.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue.js({
el:'#root',
data:{
student:{
name:'tom',
age:18,
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addSex(){
// Vue.js.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
addFriend(){
this.student.friends.unshift({name:'jack',age:70})
},
updateFirstFriendName(){
this.student.friends[0].name = '张三'
},
addHobby(){
this.student.hobby.push('学习')
},
updateHobby(){
// this表示当前Vue.js实例
// this.student.hobby.splice(0,1,'开车')
// Vue.js.set(this.student.hobby,0,'开车')
this.$set(this.student.hobby,0,'开车')
},
removeSmoke(){
this.student.hobby = this.student.hobby.filter((h)=>{
return h !== '抽烟'
})
}
}
})
script>
body>
html>
computed 和 watch 之间的区别:
computed 能完成的功能,watch都可以完成。
watch能完成的功能,computed 不一定能完成,例如:watch 可以进行异步(定时器)操作。
两个重要的小原则:
<body>
<div id="app">
<h2>人员列表h2>
<input type="text" name="" id="" placeholder="请输入名字" v-model="keyword">
<ol>
<li v-for="(item, index) in filPersons" :key="item.id + index">
{{item.name}} - {{item.age}} - {{item.sex}}
li>
ol>
div>
<script src="../js/vue.min.js">script>
<script>
new Vue.js({
el: "#app",
data: {
keyword: '',
// filPersons: [],
persons: [
{ id: 001, name: '周冬雨', age: 18, sex: '女' },
{ id: 002, name: '马冬梅', age: 20, sex: '女' },
{ id: 003, name: '周杰伦', age: 16, sex: '男' },
{ id: 004, name: '温兆伦', age: 22, sex: '男' },
]
},
/* // 方式一:watch实现
// 1.每次输入都是过滤原始数据,且不会影响原始数据
// 2.让代码初始化的时候调动,此时"字符串.includes('')"返回true,加载所有人
watch: {
keyword: {
// 让keyword初始化的时候调用
immediate: true,
handler(newValue) {
this.filPersons = this.persons.filter(
p => p.name.includes(newValue)
)
}
}
}
*/
// 方式二:computed实现
// 注意: 此时data中不能写filPersons,因为此时filPersons是计算属性
computed: {
filPersons() {
return this.persons.filter(
p => p.name.includes(this.keyword)
)
}
}
})
script>
body>
<body>
<div id="app">
<h2>人员列表h2>
<input type="text" name="" id="" placeholder="请输入名字" v-model="keyword">
<button @click='sortType = 1'>按年龄升序button>
<button @click='sortType = 2'>按年龄降序button>
<button @click='sortType = 0'>原来顺序显示button>
<ol>
<li v-for="(item) in filPersons" :key="item.id + index">
{{item.name}} - {{item.age}} - {{item.sex}}
li>
ol>
div>
<script src="../js/vue.min.js">script>
<script>
new Vue.js({
el: "#app",
data: {
keyword: '',
sortType: 0, // 0原来顺序 1升序 2降序
// filPersons: [],
persons: [
{ id: 001, name: '周冬雨', age: 18, sex: '女' },
{ id: 002, name: '马冬梅', age: 20, sex: '女' },
{ id: 003, name: '周杰伦', age: 12, sex: '男' },
{ id: 004, name: '温兆伦', age: 22, sex: '男' },
{ id: 005, name: '炎亚纶', age: 24, sex: '男' },
{ id: 005, name: '周冬伦', age: 16, sex: '女' },
]
},
computed: {
filPersons() {
const arr = this.persons.filter(
p => p.name.includes(this.keyword)
)
if(this.sortType){
arr.sort((p1, p2) =>
// 如果是1,就按升序; 如果是2,就按降序
this.sortType === 1 ? p1.age - p2.age : p2.age - p1.age
)
}
return arr
}
}
})
script>
body>
ref 是 reference 的缩写
reference
n. 参考,参照;涉及,提及;参考书目;介绍信;证明书; vi. 引用; vt. 引用
获取vue中的 dom 元素的方式之一: ref
用法:
<body>
<div id="app">
<ul ref='box' style="width: 200px; height: 100px; border: 1px red solid; overflow: auto;">
<li>我是第1个li标签li>
<li>我是第2个li标签li>
<li>我是第3个li标签li>
<li>我是第4个li标签li>
<li>我是第5个li标签li>
<li>我是第6个li标签li>
<li>我是第7个li标签li>
<li>我是第8个li标签li>
<li>我是第9个li标签li>
<li>我是第10个li标签li>
ul>
<button @click='backTop'>点我回到顶部button>
div>
<script src="../js/vue.min.js">script>
<script>
const vm = new Vue.js({
methods: {
backTop(){
this.$refs.box.scrollTop = 0
}
},
})
vm.$mount("#app")
script>
body>
**数据代理:**通过一个对象代理,对另一个对象中的属性的操作(读 / 写 );
vue 数据代理: 通过vm对象来代理data中所有属性的操作,更方便操作data中的数据
要了解 vue 的数据代理,必须先了解原生 js 对象中的defineProperty()
/* * obj 要定义属性或修改的属性 * prop 属性名称 * descriptior 属性描述符 */ Object.defineProperty(obj, prop, descriptor) 第三个参数,属性描述符中的选项 value 属性的值,默认undefined configurable 能否删除,默认false enumerable 能否枚举,默认false; 枚举方式:(for...in 或 Object.keys 方法) writable 能否修改,默认false get 当访问该属性时,该方法会被执行,默认undefined set 当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认undefined `注意:(value或writable) 和 (get或set)不能同时存在`
<body>
<script>
let boy = {
username: '宇智波带土',
age: 18
}
let person = {
username: '旗木卡卡西',
sex: '男'
}
Object.defineProperty(person, 'age', {
/* 属性的设置方式一 */
// value: 18, // 属性的值
// enumerable: true, // 能否枚举,默认false;Object.key(对象),返回所有可枚举的属性名
// writable: true, // 能否修改,默认false
// configurable: true, // 能否删除,默认false;delete person.age测试
/* 属性的设置方式二 */
// 读取person的age属性时,get函数(getter)会被调用,且返回值就是age的值
// eg: console.log(person.age)
get(){
console.log('age属性的值正在被读取...')
return boy.age
},
// 修改person的age属性时,set函数(setter)会被调用,且会收到需要修改的值
// eg: person.age = 55
set(value){
console.log(`age属性的值正在被修改成${value}...`)
boy.age = value
}
})
// console.log(person.age);
console.log(person);
script>
body>
关于getter获取的属性显示:(和 vue 中打印 data 的属性值一样)
Demo: 简单模拟 vue 数据双向绑定
<body>
输入姓名,反转显示:<input type="text" id="inp">
<br>
<br>
显示反转姓名:<input type="text" id="show">
<script>
let prop = {
name: '宇智波斑'
}
let obj = {
age: '22',
sex: '男'
}
Object.defineProperty(obj, 'userName', {
get() {
console.log('userName的值正在被读取...')
return prop.name
},
set(value) {
console.log(`userName的值正在被修改成${value}...`)
prop.name = value.split('').reverse().join('')
show.value = obj.userName
}
})
inp.addEventListener('change', ()=>{
obj.userName = inp.value
})
script>
body>
Vue.js中的数据代理:
- 通 vm 对象来代理 data对象 中属性的操作(读/写)
Vue.js中数据代理的好处:
- 更加方便的操作 data 中的数据
基本原理:
- 通过 object.defineProperty() ,把data对象中所有属性添加到vm上。
- 为每一个添加到vm上的属性,都指定一个 getter/setter。
- 在getter/setter内部去操作(读/写)data 中对应的属性。
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
vue 提供的一种处理数据的方式
非必须使用项, 通过 计算属性
和 定义函数
同样可以完成,
比较适合完成简单的逻辑
语法:
借助管道符:“ | ”
注册过滤器:
`全局过滤器,必须定义在所有需要使用的组件的前面`
Vue.filter( name,callback )
`局部过滤器`
new Vue{ filters:{} }
使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
使用:
filters: {
xxx(){
}
}
只能在 “插值表达式” 和 “v-bind(用的少)” 中使用;
过滤器方法中不能使用this, 因为过滤器相当于一个工具函数, data中任何数据都可以使用这个过滤器
过滤器不能写成箭头函数
可以不写(), 默认把左侧变量的值, 当成参数, 传递到右边的过滤器
如果过滤器有传递参数, 那么定义的过滤器接收的第一个实参依然是左侧的值
备注:
不传参
// aaa:需要过滤的数据, bbb:过滤器
{{ aaa | bbb }}
//以上形式调用bbb过滤器没有加括号传参, 但是会默认把aaa的值当做bbb过滤器的实参
new Vue({
...
filters:{
bbb(param){
// param为变量aaa的值
}
}
...
})
传参
// aaa:需要过滤的数据, bbb:过滤器, ccc:变量数据
{{ aaa | bbb(ccc) }}
//以上形式调用bbb过滤器传递了实参ccc, 但是aaa的值依然会作为bbb过滤器的第一个实参
new Vue({
...
filters:{
bbb(param1, param2){
// param1为变量aaa的值
// param2为变量ccc的值
}
}
...
})
Demo:
<body>
<div id="root">
<h2>显示格式化后的时间h2>
<h3>现在是:{{fmtTime}}h3>
<h3>现在是:{{getFmtTime()}}h3>
<h3>现在是:{{time | timeFormater}}h3>
<h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}h3>
<h3 :x="msg | mySlice">尚硅谷h3>
div>
<div id="root2">
<h2>{{msg | mySlice}}h2>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
new Vue({
el:'#root',
data:{
time:1621561377603, //时间戳
msg:'你好,尚硅谷'
},
computed: {
fmtTime(){
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
methods: {
getFmtTime(){
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
//局部过滤器
filters:{
timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
// console.log('@',value)
return dayjs(value).format(str)
}
}
})
new Vue({
el:'#root2',
data:{
msg:'hello,atguigu!'
}
})
script>
事件的基本使用:
注意:
- 如果事件处理函数不传参数,在dom元素的事件属性值中,方法调用的
( )
可以省略- 如果传递参数,在dom元素的事件属性值中,用
$event
这个关键字,表示事件对象的形参(位置无所谓,但是要对应实参的顺序)
https://cn.vuejs.org/v2/guide/events.html
.
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。用法(在dom元素中):
- @事件名.stop =‘事件处理函数’ 阻止冒泡(常用)
- @事件名.prevent =‘事件处理函数’ 阻止默认行为(常用)
- @keyup.enter(@keyup.13) =‘事件处理函数’ 回车键触发(常用)
- @事件名.once = ‘事件处理函数’ 事件只触发一次(常用)
- @事件名.capture = ‘事件处理函数’ 使用事件的捕获阶段
- @事件名.self = ‘事件处理函数’ 只有event.target是当前操作的元素才触发
- @事件名.passive = ‘事件处理函数’ 事件的默认行为立即执行,无需等待事件回调函数执行完毕
功能: .stop(阻止冒泡) .prevent(阻止默认行为) .enter(.13)(相当于按键盘回车才触发)
键盘事件-只按回车键触发
keyup.13
表示键码,回车键的键码是13.65
表示按 a 键才触发事件,只不过回车键/.13最为常用另: v-model的三个修饰符
ref="属性名"
this.$refs.属性名
- vue中key值作用
- 让vue准确的识别DOM元素 (类似于给元素添加一个身份证)
- 解决一些数据渲染的bug (vue渲染数据偶尔会出现一些bug)
- 应用场景:
- 使用v-if 切换输入框 (可能会出现数据渲染错误的bug)
- 使用v-for 渲染列表 (列表动态变化时, 可能会出现数据渲染的bug)
以下案例中,两个 input 标签十分相似,切换时 vue 无法识别,切换有问题
<body>
<div id="app">
<button @click="bolValue = !bolValue">点击切换登录方式button>
<div v-if="bolValue">
用户名:<input type="text" placeholder="请输入用户名" name="" id="">
div>
<div v-else>
手机号:<input type="text" placeholder="请输入用户名" name="" id="">
div>
div>
<script src="./vue.js">script>
<script>
const vm = new Vue.js({
data: {
bolValue: true
}
})
vm.$mount("#app")
script>
body>
解决:
原理(尚硅谷):
面试题:react、vue中的key有什么作用?(key的内部原理)
虚拟 DOM 中 key 的作用:
对比规则:
旧虚拟DOM中找到了与新虚拟DOM相同的key:
旧虚拟DOM中未找到与新虚拟DOM相同的key
用 index 作为key可能会引发的问题:
若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 —> 界面效果没问题,但效率低。
如果结构中还包含输入类的DOM:
会产生错误DOM更新会产生没有必要的真实DOM更新 —> 界面有问题。
开发中如何选择key?:
重要的钩子:
injections
和 reactivity
(数据监测 和 数据代理)当调用 vm.$destroy() 函数的时进入销毁流程;
官方并不推荐在开发中, 调用 vm.$destroy() , 而是建议使用 v-if
和 v-for
指令, 以数据驱动的方式控制子组件的生命周期
关于销毁 Vue 实例
- 销毁后, 自定义事件会被销毁, 但是原生的 DOM 事件依然生效;
- 一般不会再beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
<head>
...
<style>
#wrapper {
height: 200px;
width: 400px;
margin: 0 auto;
border: 1px red solid;
/* 隐藏默认的滚动条 */
overflow: hidden;
/* 让父盒子设置一个相对定位,
因为插件给ul加了绝对定位,
不然滚动条不在指定位置 */
position: relative;
}
ul {
/* ul标签有默认的margin-top值 */
margin-top: 0;
}
style>
head>
<body>
<div id="wrapper">
<ul id="ulDtata">
<li>我是第1个li标签li>
<li>我是第2个li标签li>
<li>我是第3个li标签li>
<li>我是第4个li标签li>
<li>我是第5个li标签li>
<li>我是第6个li标签li>
<li>我是第7个li标签li>
<li>我是第8个li标签li>
<li>我是第9个li标签li>
<li>我是第10个li标签li>
<li>我是第11个li标签li>
<li>我是第12个li标签li>
<li>我是第13个li标签li>
<li>我是第14个li标签li>
<li>我是第15个li标签li>
<li>我是第16个li标签li>
<li>我是第17个li标签li>
<li>我是第18个li标签li>
<li>我是第19个li标签li>
<li>我是第20个li标签li>
ul>
div>
<button id="btn">点我往上面添加数据button>
<script src="./iscroll.js">script>
<script>
var myScroll = new IScroll(document.getElementById('wrapper'), {
mouseWheel: true,
scrollbars: true
})
// 添加数据
btn.onclick = function () {
for (let i = 0; i < 10; i++) {
let lii = document.createElement('li')
lii.innerText = `我是新添加的第${i}个标签`
ulDtata.appendChild(lii)
}
// 刷新需要滚动的内容,只有这段代码会刷新滚动内容的高度
setTimeout(function () {
myScroll.refresh();
}, 0);
}
script>
body>
<head>
...
<style>
#wrapper {
height: 120px;
width: 400px;
margin: 0 auto;
overflow: hidden;
border: 1px red solid;
position: relative;
}
ul {
/* ul标签有默认的margin-top值 */
margin-top: 0;
}
style>
head>
<body>
<div id="app">
<div id="wrapper" ref="wrapper">
<ul id="ulBox">
<li v-for="(item, index) in list" :key="index">{{item}} - {{index+1}}li>
ul>
div><br>
<button @click="btnClick">点我添加数据button>
div>
<script src="../js/vue.min.js">script>
<script src="../js/iscroll.js">script>
<script>
Vue.js.config.productionTip = false
new Vue.js({
data: {
myIScroll: '',
list: ['我是li标签', '我是li标签', '我是li标签', '我是li标签', '我是li标签',
'我是li标签', '我是li标签', '我是li标签', '我是li标签', '我是li标签']
},
methods: {
btnClick() {
this.list.push("我是新增加的内容")
// 对iScroll刷新,方式一:
// setTimeout(function () {
// this.myScroll.refresh();
// }, 0);
// 对iScroll刷新,方式二:
// vue中的setTimeout
this.$nextTick(() => {
this.myIScroll.refresh()
})
}
},
mounted() {
this.myIScroll = new IScroll(this.$refs.wrapper, {
mouseWheel: true,
scrollbars: true
})
}
}).$mount('#app')
script>
body>
组件定义:
实现应用中局部功能, 代码 和 资源的集合
+ 代码: html、js、css… + 资源: mp3、mp4、ttf、zip…
组件理解:
几个注意点:
关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
备注:
关于组件标签:
第一种写法:
第二种写法:
备注:不用使用脚手架时,
打印组件示例, 可以看到组件示例是一个函数:
源码部分:
关于VueComponent:
组件本质是一个名为Vuecomponent的构造函数,由Vue.extend() 生成的。
我们只需要写
或 (组件的name属性值),Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。 特别注意:每次调用Vue.extend() ,返回的都是一个全新的VueComponent !
关于this指向:
- 组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是【Vuecomponent实例对象】。- new Vue( options ) 配置中:
data函数、methods中的函数、watch中的函数、computed中的函数, 它们的 this 均是【vue实例对象, 即vm】。
<template>
<!-- 组件模板 -->
<div id="#app">
<local></local>
<local2></local2>
</div>
</template>
<script>
/*学习目标:局部组件使用流程
1.导入局部组件 : 在scrip标签中导入
语法: import 组件名 from '组件路径'
* 注意点: 组件名 默认要与 文件名一致,否则会报错
2.挂载组件 : 在export default里面写一个属性components
语法: components:{ 组件名 }
*/
//1.导入组件
import local from './local.vue'
import local2 from './local2.vue'
/* 组件js */
export default {
name:'app',
//2.挂载组件
/* 这里使用了对象简写形式,相当于 local:local
如果写成 local1 : local , 那么在模板中 标签就表示组件
这里不建议大家修改,避免取的别名和其他组件名冲突
*/
components:{
local,
local2
}
};
</script>
<style>
</style>
注意: 注册全局组件必须在vue的脚手架中操作, 否则报错
在main.js文件中
//导入vue框架
import Vue from 'vue'
//导入App.vue根组件
import App from './App.vue'
//控制台打印信息
Vue.config.productionTip = false
/* 注册全局组件 : 在main.js文件中
1.导入组件
import global from './components/global.js'
2.注册全局组件
Vue.component('test', global)
一旦注册全局组件之后,可以在任何组件直接使用,并且不需要导入和挂载
*/
//1.导入组件
import global from './components/global.vue'
//2.注册全局组件
//参数1 : 组件的id (id叫什么,自定义标签名就叫什么)
//参数2 : 组件
Vue.component('test', global)
//创建vue实例,并且将根组件挂载到index.html的#app上
new Vue({
render: h => h(App)
}).$mount('#app');
<style scoped>
/* css */
.box{
width: 100px;
height: 100px;
background-color: red;
}
</style>
scoped作用是什么?
scoped原理是什么?
属性选择器
: 本质是给子组件添加一个唯一的
行内自定义属性,然后通过[data-v-xxx]
属性选择器避免样式被父组件覆盖插槽作用:
父
组件 传递 结构
给 子
组件
插槽使用2个步骤
父组件
传递给子组件
的数据是只读的
;父组件传递过来的数据
,不会通知父组件。就会造成数据不统一
问题