MVVM 模式包含三个部分:
M 模型 model
V 视图 view
VM 视图模型 view-model
特点: 实现了数据双向绑定
数据由模型进入视图,通过数据绑定实现
数据由视图进入模型,通过数据监听实现
vue 就是基于 MVVM 模式实现的
早期js被设计的很简单,主要解决一些简单的交互问题。主要的问题就是浏览器兼容性,所以jQuery就出现了,解决了兼容性问题,简化了开发。
随着技术的发展,富客户端应用,单页面应用程序的出现,前端逻辑更加的复杂,因此如何维护好前端的代码,成了主要的问题。所以MVC一类的框架就出现了,让我们将逻辑分成模型,视图和控制器,变得更容易开发和维护。
但是在MVC模式中,开发项目非常的慢,因此MVVM模式就出现了,提供了数据双向绑定技术,极大的提高了开发效率。
Vue就是基于MVVM模式实现的
官网 https://cn.vuejs.org/
gitHub https://github.com/vuejs/vue
获取 vue
在 ES5 开发中,我们要获取 vue.js 文件,可以通过 bower 工具
bower install vue
我们可以通过 npm 来安装 bower 指令
npm install -g bower
在 ES6 开发中,我们要获取 vue 模块
npm install vue
vue 是基于 MVVM 模式实现的,因此也包含三个部分
模型: js 中的数据对象
视图:页面中的模板视图
视图模型:就是 vue 实例化对象
实例化 vue 类的时候,要传递参数对象
通过 el 属性与页面中的模板绑定上
通过 data 属性与模型数据绑定上
$el 代表视图中的容器元素
模型中的数据会添加给实例化对象自身,并设置了特性
并且在 _data 以及 $data 属性中,对模型数据做了备份
当模型中的数据改变,视图会同步更新,这个过程就是 vue 实例化对象实现的(数据的绑定技术)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 视图 -->
<div id="app">
<h1>{{msg}}</h1>
</div>
<script src="./vue.js"></script>
<script>
// vm 对象
let app = new Vue({
// 绑定视图
el:"#app",
// 绑定模型
data:{
msg:"大闸蟹"
}
})
</script>
</body>
</html>
vue 中,模型中的数据被设置了特性,因此ES5 中属性的特性是 vue 数据绑定技术实现的关键
vue 中数据的绑定是通过 ES5 中属性的特性实现的
因此不支持ES5 中特性技术的浏览器,是不能使用 vue 的
vue 是一个运行在高级浏览器下的框架
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 视图 -->
<div id="app">
<h1>{{title}}</h1>
</div>
<script src="./vue.js"></script>
<script>
// 模型
var data = {
// 备份数据
_data:{}
};
// 为数据定义特性
Reflect.defineProperty(data,'title',{
// 取值
get(){
return this._data.title;
},
// 赋值
set(val){
this._data.title = val;
// 更新视图
updateVuew(this._data);
}
})
// 获取模板
var tpl = document.getElementById('app').innerHTML;
// 更新视图
function updateVuew(data){
// 用数据格式化模板
var html = tpl.replace(/{{(\w+)}}/g,(match,$1)=>{
// 返回data 中,$1 对应的数据
return data[$1] || '';
})
// 更新视图
document.getElementById('app').innerHTML = html;
}
// 给 data 赋值 页面更新
data.title = 'hello';
console.log(data.title);
</script>
</body>
</html>
我们基于ES6语法开发,因此需要通过webpack将ES6编译成ES5或者ES3.1的版本。让更多的浏览器支持。
在nodejs中,我们使用commonjs规范
在ES6中,我们使用ES Module规范
ES6文件拓展名可以是.es,也可以是.js等。但是在工作中到底使用哪种方式,要看使用的编辑器。
resolve配置
我们在resolve配置中,通过alias配置模块入口文件
alias配置有两点作用:1 可以更改入口文件。 2 简化路径的书写
在resolve我们还可以通过extensions配置文件的默认拓展名
是一个数组,每一个成员代表一个默认拓展名
注意:这里有问题webpack 执行出来报错好像是版本的问题
问题已经解决 就是 sass-loader 和 node 的版本号冲突的问题
// 使用 commonjs 文件,定义配置
module.exports={
// 开发环境
mode:'development',
// 解决问题
resolve:{
// vue 入口文件
alias:{
// 以 vue 结尾
'vue$':'vue/dist/vue.js'
},
// 默认拓展名
// 工组中,样式文件一般不会设置默认拓展名,是为了区分脚本文件和样式文件
extensions:['.js','.css']
},
// 入口文件
entry:{
'01':'./modules/01.js',
},
// 发布
output:{
filename:'[name].js'
},
// 模块
module:{
// 加载机
rules:[
// css
{
test:/\.css$/,
use:['style-loader','css-loader']
},
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
{
test:/\.scss$/,
use:['style-loader','css-loader','sass-loader']
}
]
}
}
解决数据丢失的两种方式: 一种是用新的数组替换原来的数组,一种是使用 $set() 的方式解决
vue 实现了 MVVM 模式,因此实现了对数据的绑定(当我们修改模型中的数据的时候,视图会同步更新)
当我们修改数据的时候,如果视图没有更新,那么就会被称为数据丢失
注意:数据丢失不是什么好特性,是框架的 bug
数据绑定的实现原理:基于 ES5 中属性的特性实现的,因此设置了特性的数据是不会丢失的,所以没有设置特性的数据就丢失了,工作中常见的数据丢失有四类
$set
作者为了解决数据丢失的问题,提供了一个更加保险的方法: $set
第一个参数表示数据对象
可以是 vue 实例化对象,也可以是其他对象
第二个参数表示属性名称
第三个参数表示属性值
import Vue from 'vue';
// 实例化
let app = new Vue ({
el:"#app",
//数据
data:{
title:'hello',
// 数组
colors:['red','pink','blue',{msg:'hello'}],
// 对象
obj:{
width:20,
height:30
},
// 未初始化的,将其初始化
num:100
}
})
// 常情况下
app.title="msg"
// 1 修改数组中的值
// app.colors[0]= 'pink'
// 解决;新的数组替换原来的数组
// app.colors=['pink','green','blue']
// 第二种方式
app.$set(app.colors,'0',200)
// 引用类型没有问题
app.colors[3].msg = 'abc'
// 2 数组种的新成员
// app.colors[5] = "yellogreen"
// 解决 :新的数组替换原来的数组
// app.colors = ['pink','green','blue','red','balck']
// 3 对象种的新属性
// app.obj.size = 600;
// 解决:新的对象替换原来的对象
app.obj={
width:20,
height:30,
size:600
}
// 4 未初始化
// app.num=200;
我们定义在data中定义的模型数据是固定不变的,我们想在获取数据的时候,动态改变数据,可以使用计算属性数据技术。
静态的数据定义在data属性中:定义的是什么数据,获取的就是什么数据
计算属性数据定义在comptued属性中:定义的是方法,获取的时候,会将执行的结果返回(是计算的)
computed与data的用法是一样的,添加给vue实例化对象自身,并设置了特性,定义的时候都是一个对象
key表示数据名称
value是一个函数
参数和this都指向vue实例化对象,因此通过参数或者this可以获取vue中的其它数据.必须有返回值,就是获取的数据
注意:当多次使用计算属性数据的时候,该方法只会执行一次。只有当内部使用的数据发生改变的时候,计算数据数据的方法才会执行一次。
不论是data中定义的数据还是computed中定义的数据都会添加给vue实例化对象自身并设置特性。
01 computed.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1 :title="msg">{{msg}}</h1>
<h3 >计算属性数据 --- {{dealMsg}}</h3>
<h4 :title="msg.toUpperCase()">{{msg.toUpperCase()}}</h4>
<!-- 多行字符串 -->
<h1>{{`111 ${message} 222`}}</h1>
</div>
<script src="./dist/01.js"></script>
</body>
</html>
01 computed.js
import Vue from 'vue';
// 实例化
let app = new Vue ({
el:"#app",
// 定义数据
data:{
msg:"hello"
},
// 计算属性数据
computed:{
// 将 msg 数据大写
dealMsg(val,...args){
// console.log(this,args);
return val.msg.toUpperCase()
}
}
})
为了在模板中,使用模型中的数据,我们可以使用插值语法:{{}}
插值语法提供了一个真正的js环境,我们可以直接书写js表达式。
注意:插值语法中的js表达式无法复用,想复用可以放在计算属性数据中(多定义监听器,性能)。
如果逻辑复杂:建议计算属性数据
如果逻辑简单:建议js表达式。
我们不能在元素的属性中使用插值语法。想动态设置属性,要使用属性绑定的技术。
vue提供了v-bind指令,
指令:指令就是对DOM元素的拓展,使其具有一定的行为特征(功能)
指令的属性值都是js环境,我们可以直接使用js表达式
属性绑定的语法:v-bind:key=”value”
语法糖:语法糖就是对某个操作的简化,来提高我们的开发效率。
v-bind指令的语法糖是冒号语法糖
v-bind:key=”value” 可以写成 :key=”value”
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1 :title="msg">{{msg}}</h1>
<!-- <h3 >{{dealMsg}}</h3> -->
<h4 :title="msg.toUpperCase()">{{msg.toUpperCase()}}</h4>
<!-- 多行字符串 -->
<h1>{{`111 ${message} 222`}}</h1>
</div>
<script src="./dist/01.js"></script>
</body>
</html>
1 避免插值符号闪烁。
2 只能渲染元素的全部内容,我们还可以用字符串拼接形式来解决这个问题。
注:v-text指令与插值语法一样,都不能渲染标签。
1 避免插值符号闪烁。
2 只能渲染元素的全部内容,我们还可以用字符串拼接形式来解决这个问题。
3 可以渲染标签。
注:渲染的内容一定要确保是安全的。否则会导致xss注入的问题。
该指令会让其所在的元素及其所有子元素上的所有插值与指令只执行一次。
02 指令.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<h2 v-text="msg"></h2>
<p v-html="text"></p>
<p v-html="text + '!'"></p>
<hr>
<div v-once>
<h1>{{msg}}</h1>
<h2 v-text="msg"></h2>
<p v-html="text"></p>
<p v-html="text + '!'"></p>
</div>
</div>
<script src="./dist/02.js"></script>
</body>
</html>
02 zhiling.js
import Vue from 'vue';
let app = new Vue({
el:"#app",
data:{
msg:"hello",
text:'点击改变数据'
}
})
setTimeout(()=>{
app.text="大闸蟹"
app.msg="dazhaxie"
},2000)
我们想对插值语法中的数据进行修改,可以使用js表达式,但是如果逻辑很复杂,会导致模板很臃
肿。
为了解决臃肿的问题,vue提供了插值过滤器技术。
插值过滤器技术有三个优势:
1 避免模板臃肿的问题。
2 可以复用。
3 可以跨组件使用。
在2.0中,作者弱化了过滤器,移除了内置的过滤器。
注意:在2.0中,作者建议用插值表达式以及计算属性数据的形式代替插值过滤器。
使用过滤器
我们可以在插值语法以及指令中使用插值过滤器。
语法 {{data | 过滤器(参数1, 参数2) | 过滤器2}}
前一个过滤器的输出将作为后一个过滤器的输入。
自定义过滤器
在vue中自定义过滤器有两种方式
通过Vue.filter(name, fn)
在vue实例化对象(组件)中,通过filters属性定义。
filters: { name: fn }
name表示过滤器名称:建议驼峰式命名
fn(data, …args)表示过滤函数:
data表示处理的数据。
args表示使用过滤器时候传递的参数。
必须有返回值,就是处理的结果,
全局定义过滤器的时候,要注意:1 filter方法不能解构。2 要在vue实例化对象之前定义。
03 过滤器.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{msg}}</p>
<h1>{{ msg | tocamel(true,100,200) }}</h1>
<h1>{{ msg | tocamel(false,100,200) }}</h1>
<h1>{{ msg | uppercase }}</h1>
<h1>{{ msg | tocamel | uppercase }}</h1>
</div>
<script src="./dist/03.js"></script>
</body>
</html>
03 过滤器.js
import Vue from 'vue';
// 全局定义过滤器
Vue.filter('tocamel',(str,fristCharIsUpper, ...args)=>{
console.log(args);
// 强制转换成 字符串
str = String(str);
// 首字母大写
if(fristCharIsUpper){
str = str[0].toUpperCase() + str.slice(1);
}
return str.replace(/[-_]([a-z])?/g,(match,$1 = '')=>{
return $1.toUpperCase()
})
})
let app = new Vue({
el:"#app",
// 局部过滤器
filters:{
// 字母大写
uppercase(str){
return String(str).toUpperCase()
}
},
data:{
msg:'abc-efg-bng-Hu'
}
})
vue实现了数据双向绑定,vue为了简化双向绑定,提供了v-model指令
属性值就是绑定的数据
注意:
1 只能绑定数据,不能使用表达式
2 绑定的数据必须在模型中定义。如;data等
例如:表单元素可以与用户交互,因此可以使用v-model指令
数据双向绑定有两个方向:
数据由模型进入视图:通过数据绑定实现的。为value绑定模型数据
数据由视图进入模型:通过事件监听实现的。监听input事件,更新数据
v-model指令简化了这两个过程,因此也可以看成是语法糖。
用来解决插值符号闪烁的问题
只需要两步:
第一步 为容器元素设置 v-cloak 指令(属性)
第二步 在 style 标签内(style 标签要定义在容器元素的前面),通过 v-cloak 属性选择器,设置 display:none 样式,将元素隐藏
注意:
① style 标签要定义在容器元素的前面
② v-cloak 只能给容器元素内部的元素使用(包括容器元素)
04 v-vlovk.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 属性选择器 防止插值符号闪烁 */
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<input type="text" v-model="msg">
<p>{{msg}}</p>
<hr>
<div v-cloak>
<input type="text" v-model="tit">
<p>{{tit}}</p>
</div>
</div>
<script src="./dist/04.js"></script>
</body>
</html>
我们将 input 元素的 type 属性设置未 radio 就可以得到单选框,通过 checked 属性设置其选中状态对单选框实现数据双向绑定,也是用 v-model 指令
特点:
① 一组单选框绑定同一份数据
② 选框的值通过 value 属性定义
③ 此时 checked 属性失效了
④ 一组单选框的默认值就是绑定数据的值
我们将 input 元素的 type 属性设置为 checkbox 就可以得到多选框,通过 checked 属性设置其选中状态,对多选框实现数据双向绑定,也是用 v-model 指令
特点:
① 一组多选框绑定不同的数据,为了访问页面,我们将其放在同一个命名空间下
② 选框的默认是布尔值,通过 v-bind:true-value 以及 v-bind:false-value 可以自定义其值
:true-value:定义选中时候的值
:false-value :定义未选中时候的值
③ checked 属性失效了
④ 一组多选框的默认值就是绑定数据的值,如果自定义了 :true-value 以及 :false-value 属性,就是自定义数据值
我们通过 select 元素定义下拉框,通过 option 元素定义选项
选项的值默认是内容值,设置了 value 就是 value 值
我们通过为 select 元素添加 v-model 指令实现数据双向绑定
单选下拉框绑定的是字符串
多(复)选 下拉框绑定的是数组
我们 通过为 select 元素 设置 multiple 属性,实现单选下拉框于多选下拉框的切换
05 redio+ chekbox + select.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<h2>单选框</h2>
<p>请选择喜欢的运动</p>
<label>足球
<input type="radio" v-model="supper" value="footerball">
</label>
<label>篮球
<input type="radio" v-model="supper" value="basktball">
</label>
<label>乒乓
<input type="radio" v-model="supper" value="pingpang">
</label>
<p>喜欢的运动时{{supper}}</p>
</div>
<div>
<h2>复选框</h2>
<span>兴趣爱好</span>
<label>羽毛球
<input type="checkbox" v-model="interset.yumao" >
</label>
<label>台球
<input type="checkbox" v-model="interset.tai" :true-value="'选中台球'" :false-value="notTai">
</label>
<label>保龄球
<input type="checkbox" v-model="interset.baoling" >
</label>
<p>喜欢的运动有{{interset}}</p>
</div>
<div>
<h2>select 单选下拉框</h2>
<select v-model="optionSe" >
<option value="red">red</option>
<option value="pink">pink</option>
<option value="green">green</option>
<option value="yellow">yellow</option>
<option value="black">black</option>
</select>
<p>选中的颜色有{{optionSe}}</p>
</div>
<div>
<h2>select 多选下拉框</h2>
<select v-model="selecteds" multiple size="2">
<option value="isred">red</option>
<option value="ispink">pink</option>
<option value="isgreen">green</option>
<option value="isyellow">yellow</option>
<option value="isblack">black</option>
</select>
<p>选中的颜色有{{selecteds}}</p>
</div>
</div>
<script src="./dist/05.js"></script>
</body>
</html>
05 redio+ chekbox + select.js
import Vue from 'vue';
let app = new Vue({
el:"#app",
data:{
supper:"pingpang",
interset:{
tai:"选中台球"
},
notTai:"没有选中台球",
// 单选下拉框使用的是字符串
optionSe:'red',
// 多选下拉框使用的是数组
selecteds:['isred','ispink']
}
})
vue实现了数据双向绑定,当模型中的数据改变,会被视图监听到。进而同步到视图中。
如果想在js中,监听模型中数据的改变,可以使用监听器技术
我们在实例化对象中的watch属性中,定义监听器,是一个对象
key 被监听的数据名称
value 当数据改变的时候,执行的回调函数。
第一个参数表示新的值,
第二个参数表示旧的值。
this指向vue实例
注意:watch不仅仅可以监听模型中的数据,实例化对象中,设置了特性的数据都可以监听,例如:路由数据等。
状态监听
状态监听就是当数据改变的时候,我们平滑的将其过渡到某个值。
我们在监听器中,监听数据的变化。
我们再启动循环定时器,改变数据。
06 watch.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<h2>监听数据的改变</h2>
<p><input type="text" v-model="msg"></p>
<span>{{msg}}</span>
</div>
<div>
<h2>状态监听</h2>
<p>{{total}}</p>
</div>
</div>
<script src="./dist/06.js"></script>
</body>
</html>
06 watch.js
import Vue from 'vue';
// 定义计时器的初始值
let timebaer;
let app = new Vue({
el:"#app",
data:{
msg:100 ,
total:100
},
watch:{
msg(value,...args){
// console.log(args,this);
// 监听 msg 的改变 更新 total
// 静态改变
// this.total = value
// 定时器开始前,将其取消
clearInterval(timebaer);
//动态改变 设置定时器,更新 total
timebaer = setInterval(()=>{
console.log(this,"这里的 this 就是 监听的 this ");
this.total += this.total > this.msg ? -1 :1;
// 如果值相等,终止定时器
if(this.total == this.msg){
clearInterval(timebaer)
}
},20)
}
}
})
事件绑定:vue为了绑定DOM事件,提供了v-on指令
语法:v-on:click=”fn()”
v-on指令的语法糖是@ 也可以写成 @click=”fn()”
fn代表事件回调函数,定义在vue实例的methods属性中。
methods中定义的方法跟data中的定义的数据一样,添加实例化对象自身了。
methods中定义的方法,内部的this指向vue实例化对象。
methods中定义的方法不仅仅可以在事件中使用,所有可以获取实例化对象的地方,都可以通过实例化对象来使用该方法。
参数集合
()表示参数集合,可有可无。
如果没有定义参数集合,
默认有一个参数,就是事件对象
如果添加了参数集合,
默认没有参数,此时使用什么数据可以在参数集合中传递。
想使用事件对象,要传递 $event。
vue中事件绑定技术,没有使用事件委托技术,是直接绑定给元素的。
因此事件对象是源生的事件对象
07 v-on + $event.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<button @click="setTitle">改变 msg</button>
<button @click="setTow($event)">改变 msg</button>
</div>
<script src="./dist/10.js"></script>
</body>
</html>
07 v-on + $event.js
import Vue from 'vue';
let app = new Vue({
el:"#app",
data:{
msg:"标题"
},
methods:{
setTitle(...args){
this.msg="改变了"
console.log(args,this,"111");
},
setTow(...args){
console.log(args,this,"2222");
}
}
})
我们使用jQuery的时候,想判断事件绑定的元素与事件触发的元素是同一个元素的功能,要获取事件对象,再通过判断事件对象的e.target是否等于e.currentTarget,来确定结果,很麻烦。
vue简化了这些流程,让我们在绑定事件的时候就实现这些功能。通过事件修饰符实现。
语法: v-on:click.修饰符=”fn”
“.修饰符”就表示事件修饰符,我们可以通过多个“.”来同时应用多个修饰符。
stop:实现阻止冒泡的修饰符。
prevent:实现阻止默认行为的修饰符。
once:表示单次触发的修饰符。
self:表示绑定事件的元素与触发事件的元素是同一个元素。
这些修饰符还可以混合使用。
left:点击鼠标左键
right:点击鼠标右键
middle:点击鼠标中间件
ctrl:点击ctrl辅助键
shift:点击shift辅助键
alt:点击alt辅助键
meta:点击window|command辅助键
键盘事件:keydown,keyup,keypress(输入有效键)
当我们绑定键盘事件的时候,可以使用键盘事件修饰符。
有效修饰符:esc, tab, space, enter, delete(delete|backspace),up, down, left, right,以及所有字母键。
07 修饰符的实现.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.parent{
border: 1px solid #000;
}
</style>
</head>
<body>
<div id="app">
<p>{{msg}}</p>
<div class="parent" @click="parent">父元素
<!-- self 绑定事件的元素于出发时间的元素是同一元素 -->
<!-- <div class="parent" @click.self="parent">父元素 -->
<!-- .stop 阻止事件冒泡 -->
<button @click.stop="child">
子元素
</button>
</div>
<!-- 阻止默认行为 -->
<p><a href="">没有阻止默认行为的a 标签元素</a></p>
<p> <a href="" @click.prevent="stop">阻止默认行为的 a 标签元素</a></p>
<!-- once 绑定一次事件 -->
<p><button @click.once="stopOne">绑定一次事件</button></p>
<!-- 也可以通过打点的方式 阻止事件 -->
<!-- 鼠标的修饰符 -->
<div>
<button @click.left="fn1()">鼠标left</button>
<button @click.right="fn2()">鼠标left</button>
<button @click.middle="fn3()">鼠标left</button>
</div>
<!-- 辅助键触发 -->
<div>
<button @click.ctrl="ctrl()">ctrl</button>
<button @click.shift="shift()">shift</button>
<button @click.alt="alt()">alt</button>
<!-- meta 点击 window | command 都可以 -->
<button @click.meta="meta()">meta</button>
</div>
<!-- 键盘的事件 -->
<div>
<input type="text" @keydown="onkeydown">
<input type="text" @keyup="onkeyup">
<input type="text" @keypress="onkeypress">
<!-- 键盘修饰符有以及所有的字母 esc,tab,space,enter,delete(deleta | backspace), up, down,left,right -->
<p>修饰符事件
<br>
<!-- 注意 tab 事件 只能在当前页 点击tab 按钮执行到才能触发-->
<input type="text" @keyup.Tab.prevent="onshijian" placeholder="tab 键 不好触发">
<input type="text" @keyup.esc="onshijian" placeholder="esc 键">
<input type="text" @keyup.space="onshijian" placeholder="space 键">
<br>
<input type="text" @keyup.delete="onshijian" placeholder="delete 键">
<input type="text" @keyup.up="onshijian" placeholder="up 键">
<input type="text" @keyup.down="onshijian" placeholder="down 键">
<br>
<input type="text" @keyup.left="onshijian" placeholder="left 键">
<input type="text" @keyup.right="onshijian" placeholder="right 键">
</p>
</div>
</div>
<script src="./dist/11.js"></script>
</body>
</html>
07 修饰符实现.js
import Vue from 'vue';
let app = new Vue({
el:"#app",
data:{
msg:"标题"
},
methods:{
// 阻止默认事件
parent(){
console.log("父元素");
},
child(){
console.log("子元素");
},
stop(){
console.log("阻止默认行为 parent");
},
stopOne(){
console.log("执行一次");
},
// 鼠标事件
fn1(){
console.log("鼠标左键触发");
},
fn2(){
console.log("鼠标右键触发");
},
fn3(){
console.log("鼠标中间件触发");
},
// 辅助键
ctrl(){
console.log("ctrl 触发的事件");
},
shift(){
console.log("shift 触发的事件");
},
alt(){
console.log("alt 触发的事件");
},
meta(){
console.log("meta 触发的事件");
},
// 键盘事件
onkeydown(e){
console.log("键盘按下",e.key,e.keyCode);
},
onkeyup(e){
console.log("键盘松开",e.keyCode);
},
onkeypress(e){
console.log("keypress 事件",e.keyCode);
},
// 键盘的修饰符事件
onshijian(e){
console.log(e.keyCode,e.key,"修饰符");
}
}
})
在 vue 中,为元素绑定类有三种方式
①:class=”{}”
key 表示一组类的名称(可以包含空格,切割成多个类)
value 表示是否保留这组类
②:class=”[]”
每一个成员代表一组类(可以包含空格,切割成多个类)
③:class=”str”
类名之间用空格隔开。
08 clsss.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h5>{{title}}</h5>
<!-- 通过对象的方式 -->
<p :class="{
'red bg-blue' : true,
'fs-50':100,
'clicle':0
}">实现类 对象类</p>
<!-- 数组类 -->
<p :class="['clicle','green']">实现类 数组类</p>
<!-- 字符串类 注意字符串类拼接 需要加 空格 隔开 -->
<p :class="'bg-gold ' + cls">实现类 字符串类</p>
</div>
<script src="./dist/07.js"></script>
</body>
</html>
08 class.js
import Vue from 'vue';
import './demo.scss';
let app = new Vue({
el:"#app",
data:{
title:"实现类",
cls:"fs-50"
}
})
demo.scss
.red{
color: red;
}
.bg-blue{
background-color: blue;
}
.fs-50{
font-size: 50px;
}
.clicle{
width: 100px;
height: 100px;
border-radius: 50%;
border: 1px solid #000;
background-color: pink;
}
.green{
color: green;
}
.bg-gold{
background-color: gold;
}
效果图
与绑定类一样,绑定样式也有三种方式
①:style=”{}” 绑定样式对象
key 表示样式名称,建议使用驼峰式命名
value 表示样式属性值
②:style=”[]” 绑定多组样式对象
每一个成员是一个对象,代表一组样式
key 表示样式名称,建议使用驼峰式命名
value 表示样式属性值
③:style=”str” 做样式字符串拼接。
09 style.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 对象的方式渲染 -->
<div :style="{
color:'red',
'background-color':'pink',
fontSize:'30px'
}">{{title}}</div>
<!-- 数组的方式 -->
<div :style="[
{
color:'red'
},
{
'background-color':'green'
}
]"
>{{title}}</div>
<!-- 字符串的方式 -->
<div :style="'color:' + color"
>{{title}}</div>
</div>
<script src="./dist/13.js"></script>
</body>
</html>
09 style,js
import Vue from 'vue';
let app = new Vue({
el:"#app",
data:{
title:"大闸蟹",
color:'blue'
}
})
模板指令就是控制元素创建和删除的指令
条件模板指令*
vue 模拟了 js 中的 if 条件语句,提供了条件模板指令 v-if
if 条件语句: if () {} else if () else ()
所以vue也为v-if指令提供了组合指令:v-if,v-else-if, v-else。
v-if指令的属性值是布尔值
true 表示创建这个元素
false 表示删除这个元素
是真正的创建和删除,不是显示和隐藏。
用来显示或者隐藏元素的指令,属性值是布尔值
true 显示元素
false 隐藏元素
通过控制样式“display: none”实现的。
与v-if相比,有两点不同
1 原理不同
v-if是真正的创建和删除。 v-show是通过样式实现的显示和隐藏。
2 组合指令
v-if有组合指令(v-else-if,v-else)。v-show没有组合指令
由于v-show是通过修改样式实现的,因此性能更高。
10 v-if + v-show.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 条件模板指令 -->
<!-- <h1 v-if="isShow === true">大闸蟹</h1>
<h1 v-else-if="isShow === 100">丰富的蟹黄</h1>
<h1 v-else>hello</h1> -->
<hr>
<h1 v-show="isShow">大闸蟹</h1>
<!-- v-show不能与v-else-if,v-else指令一起使用 -->
<!-- <h1 v-else>丰富的蟹黄</h1> -->
</div>
<script src="dist/06.js"></script>
</body>
</html>
vue模拟js中的for in循环语句实现了v-for循环模板指令:
有两种用法:
第一种:v-for=”item in data”
第二种:v-for=”(item, index) in data”
v-for 和 in 都是关键字
data表示循环的数据,可以是数字,对象和数组
如果是数字:item 表示数值(从1计数)。 index 表示索引值(从0计数)
如果是对象:item 表示value(属性值)。 index 表示key(属性名称)
如果是数组:item 表示成员值。 index 表示索引值
使用v-for指令的时候,我们要设置key属性,要求属性值时候唯一的。
我们可以设置成员值item中的唯一属性,如id等
我们还可以设置索引值index。
建议用成员中的唯一属性。
注意:
1 在vue cli中,必须设置key属性
2 循环中的变量item和index只在循环中生效,循环结束后被销毁
当我们想渲染多个兄弟元素的时候,那么我们就要将指令放在父元素上,这样会导致引入其它的元素。为了避免引入其它的元素,vue建议我们使用模板元素
在html中定义模板元素有两种语法:
第一种:通过script模板标签定义
第二种:html5提供了tempalte模板元素。
vue建议我们使用template模板元素
模板元素跟普通的元素一样,可以包含子元素,可以添加属性,可以设置指令等等,但就是不会渲染到页面中,我们将循环指令添加给模板元素,就不会引入其它的元素了。
注意:使用模板元素template,不能设置key属性
11 template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 循环指令 -->
<ul>
<!-- 第一种用法 -->
<!-- <li v-for="item in num">{{item}}</li> -->
<!-- 第二种用法: 使用索引值 -->
<!-- 遍历数字,item从1计数,index从0计数 -->
<!-- <li v-for="(item, index) in num">item-{{item}} -|- index- {{index}}</li> -->
<!-- 遍历对象:item表示value, index表示key -->
<!-- <li v-for="(item, index) in obj">index-{{index}} -|- item- {{item}}</li> -->
<!-- 遍历数组: item表示成员值,index表示索引值 -->
<!-- <li v-for="(item, index) in news">index-{{index}} -|- item- {{item}}</li> -->
<!-- 我们要设置key -->
<!-- <li v-for="(item, index) in news" :key="item">index-{{index}} - |- item-{{item}}</li> -->
<!-- <li v-for="(item, index) in news" :key="index">index-{{index}} -|- item-{{item}}</li> -->
<!-- 斑马线 -->
<!-- <li v-for="(item, index) in news" :key="index" :style="{ backgroundColor: index % 2 ? 'skyblue' : 'pink' }">index-{{index}} -|- item-{{item}}</li> -->
<!-- 每一条新闻下面有一条线 -->
<!-- <div v-for="(item, index) in news" :key="index"> <li>{{index}} -|- {{item}}</li> <hr> </div> -->
<!-- 不引入div可以使用模板元素 -->
<!-- 注意:template模板元素不能设置key属性 -->
<template v-for="(item, index) in news">
<li>{{index}} -|- {{item}}</li>
<hr>
</template>
</ul>
<!-- 循环外部无法使用循环变量 -->
<!-- <h1>{{item}}--{{index}}</h1> -->
</div>
<script src="dist/07.js"></script>
</body>
</html>
① 微博导航.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 对象的方式渲染类 -->
<!-- <div :class="{
weibo:true,
show:isShow
}"
@mouseenter="show"
@mouseleave="hide"
> -->
<!-- 数组的方式渲染 -->
<!-- <div :class="['weibo',isShow]"
@mouseenter="show"
@mouseleave="hide"
> -->
<!-- 字符串的方式渲染 -->
<div :class="'weibo ' + isShow"
@mouseenter="show"
@mouseleave="hide"
>
<span>微博导航</span>
<ul>
<li>微博首页</li>
<li>微博详情</li>
</ul>
</div>
</div>
<script src="./dist/12.js"></script>
</body>
</html>
① 微博导航.js
import Vue from 'vue';
import './weibo.scss';
let app = new Vue({
el:"#app",
data:{
isShow:false
},
methods:{
show(){
// 对象的方式
// this.isShow = true
// 数组 + 字符串的方式渲染
this.isShow = 'show'
},
hide(){
// 对象的方式
// this.isShow = false
// 数组 + 字符串的方式渲染
this.isShow = ''
}
}
})
weibo.scss
*{
margin: 0;
padding: 0;
}
.weibo{
margin: 50px;
span{
display: inline-block;
padding: 5px 10px;
font-size: 14px;
background-color: #efefef;
border: 1px solid #000;
}
ul{
width: 100px;
display: none;
list-style: none;
background-color: #e3e3e3;
li{
line-height: 30px;
border: 1px solid #000;
}
}
&.show{
ul{
display: block;
}
}
}
②选择商品.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 鞋的颜色 鞋号 -->
<div class="xie">
<p>
<label>颜色</label>
<img :style="{ backgroundColor:img[0] }" @click="toggle(42)" src="./img/01.jpg" alt="">
<img :style="{ backgroundColor:img[1] }" @click="toggle(43)" src="./img/01.jpg" alt="">
<img :style="{ backgroundColor:img[2] }" @click="toggle(44)" src="./img/01.jpg" alt="">
</p>
<p>
<label>尺码</label>
<span :class="span[0]">42</span>
<span :class="span[1]">43</span>
<span :class="span[2]">44</span>
</p>
</div>
</div>
<script src="./dist/14.js"></script>
</body>
</html>
②选择商品.js
import Vue from 'vue';
import './xie.scss';
let app = new Vue({
el:"#app",
data:{
color:'red',
img:[],
span:[]
},
methods:{
toggle(e){
// console.log(e);
switch(e){
case 42:
// this.img[0]= 'red'
// $set 解决数据丢失
// this.$set(this.img,0,'red')
// 新数组解决数据丢失
this.img=['red']
this.span=['disabled','disabled']
break;
case 43:
this.img=['','red']
this.span=['disabled']
break;
case 44:
this.img=['','','red']
this.span=[]
break;
default:
break;
}
}
}
})
xie.scss
*{
margin: 0;
padding: 0;
}
.xie{
width: 500px;
margin: 50px;
text-align: center;
border: 1px solid pink;
label{
margin-right: 30px;
}
p{
margin-bottom: 20px;
}
img{
width: 50px;
height: 50px;
vertical-align: middle;
border: 1px solid #000;
cursor: pointer;
}
span{
display: inline-block;
width: 50px;
line-height: 50px;
vertical-align: middle;
border: 1px solid #000;
cursor: pointer;
&.disabled{
cursor: not-allowed;
border-style: dashed;
}
}
}
③ 支付Email.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div class="zhifubao">
<label for="username">邮箱:</label>
<input
type="text"
id="username"
autocomplete="off"
v-model="msg"
@keydown.up.prevent="checkUp"
@keyup.down="checkDown"
@keyup.enter="checkEnter"
>
<ul v-show="msg && dealEmail.length">
<!--注意 使用template渲染数据 可以不用写 key 属性 -->
<li
v-for="(item,index) in dealEmail"
:key="item"
@click="checkLi"
:class="cls[index]"
>{{dealMag}}@{{item}}<template v-if="item == '189'">.cn</template><template v-else>.com</template>
</li>
</ul>
</div>
</div>
<script src="./dist/15.js"></script>
</body>
</html>
③ 支付Email.js
import Vue from 'vue';
import './zhifubao.scss';
let app = new Vue({
el:"#app",
data:{
// 输入的内容
msg:"",
email:['qq','163','126','189','hotmail','gmail','sine','21cn'],
// 类
cls:['hover'],
// 当前选中的 li
num:-1,
// 是否已经点击过
// hasMove:false
},
// 使用计算属性进行数据的切割
computed:{
dealMag(){
let index = this.msg.indexOf("@");
// @ 符合存在 进行截取
if(index >= 0){
return this.msg.slice(0,index)
}
// if 中有 return ,else 可以省略
// 没有 @ 符号直接返回原始字符串
return this.msg;
},
// 过滤截取 @ 后面的数据
dealEmail(){
// 获取 @ 符合
let index = this.msg.indexOf("@");
if(index >= 0){
// 获取后面的字符
let str = this.msg.slice(index + 1);
// 对邮箱进行过滤
return this.email.filter(item=>{
// 添加拓展名
item += item == '189' ? '.c' : '.co'
// 以 str 开头
return item.indexOf(str) === 0
})
}
return this.email
}
},
// 设置事件
methods:{
// 点击上箭头
checkUp(){
// 是否选中过
if(this.hasMove){
// 更改 num
this.num--;
}
// 根据 li 的个数,以及 num 找到对应的 li 的索引
// 更改数据
this.changeStyle()
// 选中过
this.hasMove = true
},
// 点击下箭头
checkDown(){
// 更改 num
this.num++;
// 根据 li 的个数,以及 num 找到对应的 li 的索引
// 更改数据
this.changeStyle()
// 设置未选择过的
this.hasMove = true
},
// 点击回车
checkEnter(){
// 获取索引值
let index = this.getIndex();
// 获取内容
let msg = this.dealMag + '@' + this.dealEmail[index] + (this.dealEmail[index] == '189' ? '.cn' : '.com');
// 更新 msg
this.msg = msg
},
// 点击 li
checkLi(e){
// 获取点击的数据
let msg = e.target.innerHTML;
console.log(msg ,"霍去病的s");
// 返回到页面中去
this.msg = msg
},
// 更改数据
changeStyle(){
// 获取 li 的个数
// let li = this.dealEmail.len;
// 通过方法获取索引
let index = this.getIndex()
// console.log(index);
// 排它法
// 修改样式
this.cls = [];
// 特殊添加类
this.cls[index] = 'hover'
},
// 获取索引
getIndex(){
// 当前 num this.num
// 获取 li 的个数 this.dealEmail.len
// 总结
// 不论数多大,对 10 取余,结果是 (-10,10) 之间,我们希望获取一个[0,10) 之间的数
// 再加上 10 ,就是 (0,20)
// 继续对10 取余就是 [0,10)
// 取余
// let index= this.num % this.dealEmail.length;
// // 加长度
// index = index + this.dealEmail.length;
// // 取余
// index = index % this.dealEmail.length;
// // 返回结果
// return index;
// 将上面的数据步骤合并
return (this.num % this.dealEmail.length + this.dealEmail.length ) % this.dealEmail.length
},
}
})
zhifubao.scss
*{
margin: 0;
padding: 0;
}
.zhifubao{
width: 500px;
height: 600px;
margin: 50px auto;
border: 1px solid #000;
position: relative;
input{
width: 300px;
outline: none;
}
ul{
width: 200px;
background-color: #efefef;
list-style: none;
position: absolute;
left: 40px;
li{
font-size: 14px;
line-height: 26px;
&:hover,
&.hover{
background-color: aquamarine;
}
}
}
}