参考视频:学 Vue.js 看这个就够了
官方文档:Vue.js
基本概念
框架与分层架构
- 前端框架
-
三大框架ARV:Angular、React、Vue。都比较适合做单页面。
-
提高开发效率的发展历程:原生JS - Jquery之类的类库 - 前端模板引擎 - Angular.js / Vue.js
- Vue.js
-
Vue.js主要作用:构建用户界面,只关注视图层(MVC)
-
Vue.js核心概念:不再操作DOM元素,更多关注业务逻辑
- 框架与库
-
框架:完整的解决方案,对项目入侵性较大。如果需要更换框架,则需要重新架构整个项目。
-
库(插件):提供某一个小功能,对项目入侵性较小,很容易切换其他库实现需求。
- MVC与MVVN
- MVC:Model模型层,View视图层,Controller控制层。后端分层概念。
- MVVM:Model-View-ViewModel,前端视图层概念。
引入Vue
- 官方文档
- 引入过程
<script src="js/vue.js"></script>
绑定数据(innerHTML)
new Vue({ })构造函数
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp', //element元素,实例控制页面上的哪个区域
data: { //data属性, el中要用到的属性
msg: 'HelloWorld'
},
methods: { //methods方法,el中要用到的方法
show: ()=>{alert(this.msg)} //this访问
}
});
</script>
- 注意符号
- 注意el、data、methods之间的
,
,和data、methods内部元素之间的,
。 - 注意构造函数内部的
{ }
。
- 注意事项
- el中元素的选择器同CSS。
- 不可以给body等标签加id。
{{ }}表达式:插值表达式
将data中定义的属性,以插值表达式形式嵌入HTML。
- 基本代码(在body标签内)
<div class="myapp">
<p>{{msg}}</p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld'
}
});
</script>
data中自定义的元素msg被写入了p标签。
- MVVM分析
- html中div代码:View视图层
- js代码中的vm对象:ViewModel层
- js代码中的vm对象中的data:Model层
v-cloak属性(布尔):解决插值表达式闪烁问题
若加载速度较慢,用户可能会先看到{{msg}}这个变量,再看到其对应值。
- 增加
v-cloak
属性并设置CSS样式
<style>
[v-cloak]{
display: none;
}
</style>
<div class="myapp">
<p v-cloak>{{msg}}</p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld'
}
});
</script>
结果:加载成功之前,调用CSS样式(不显示)
v-text属性:引入文本
- 渲染数据的两种方式(等效)
<div class="myapp">
<p v-cloak>{{msg}}</p>
<p v-text="msg"></p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld'
}
});
</script>
- 比较
区别 | 插值表达式{{ }} | v-text |
---|---|---|
作用 | 渲染文本数据 | 渲染文本数据 |
闪烁 | 有闪烁问题 | 无闪烁问题 |
文本 | 任意添加和使用文本 | 会覆盖innerHTML |
v-html属性:引入文本(含html元素)
<div class="myapp">
<p v-html="myhtml"></p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
myhtml: '<h1>标题</h1>'
}
});
</script>
绑定数据
v-bind绑定(缩写:)
将字符串解析为Vue内部数据。
<div class="myapp">
<!--渲染结果:msg-->
<input type="button" value="按钮" title="msg">
<!--渲染结果:HelloWorld--> <!--识别为变量-->
<input type="button" value="按钮" v-bind:title="msg">
<!--渲染结果:HelloWorld123--> <!--识别为变量:可以使用JS表达式-->
<input type="button" value="按钮" v-bind:title="msg + '123'">
<!--渲染结果:HelloWorld123--> <!--缩写:只保留冒号-->
<input type="button" value="按钮" :title="msg + '123'">
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld'
}
});
</script>
绑定CSS:普通样式
包括内部和外部引入。
<style>
.red{color: red} /*颜色*/
.thin{font-weight: 200} /*宽度*/
.italic{font-style: italic} /*斜体*/
.active{letter-spacing: 0.5em} /*字符间距*/
</style>
<div class="myapp">
<h1 class="red active">h1</h1> <!--不使用v-bind-->
<h1 :class="['red', 'active']">h1</h1> <!--字符串数组-->
<h1 :class="['red', flag?'active':'']">h1</h1> <!--字符串数组:变量+三元运算符-->
<h1 :class="['red', {active:flag}]">h1</h1> <!--字符串数组:变量+对象 (对象中可以去掉引号)-->
<h1 :class="[{red:true, active:flag}]">h1</h1> <!--直接使用对象-->
<h1 :class="classObj">h1</h1> <!--直接使用对象+变量-->
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data:{
flag: true,
classObj: {red:true, active:true}
}
});
</script>
绑定CSS:内联样式
<div class="myapp">
<h1 style="color: red">h1</h1> <!--不使用v-bind-->
<h1 :style="{color: 'red', 'font-weight': 20}">h1</h1> <!--使用对象(必须引号:取值为字符串 属性名带横线)-->
<h1 :style="styleObj">h1</h1> <!--使用对象:用变量定义-->
<h1 :style="[styleObj, styleObj2]">h1</h1> <!--使用对象[数组]:用变量定义-->
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data:{
flag: true,
classObj: {red:true, active:true}, //样式集合为类
styleObj: {color: 'red', 'font-weight': 20}, //普通样式
styleObj2: {'font-style': 'italic'} //普通样式
}
});
</script>
v-model属性:双向数据绑定
- 单向绑定:使用
v-bind
等
<div class="myapp">
<input type="text" :value="msg">
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld'
}
});
</script>
-
修改msg,页面中的msg会被修改
HTML修改前:
修改msg-使用Chrome的console输入:
HTML修改后:
-
修改页面中的msg(通过表单input等),msg属性不会被修改。
HTML修改前:
修改表单文本:
Vue属性修改后:不变
-
结论
v-bind
只能实现单向数据绑定(Model向View),但无法实现反向数据绑定。
- 双向绑定:使用
v-model
属性
<div class="myapp">
<input type="text" v-model="msg">
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld'
}
});
</script>
可以解决如上问题。
双向绑定: (Model向View),(View向Model)
注意:v-model
只能运用在表单元素中,基于value
属性
- 使用双向绑定自制简易计算器(略)
- 下拉选择框,
select
标签内有多个option
标签。select
标签内value
属性对应取值。 eval()
函数解析字符串并执行,返回执行结果。
绑定事件方法:v-on绑定(缩写@)
<div class="myapp">
<input type="button" value="按钮" v-on:click="show1">
<input type="button" value="按钮" @click="show1">
<input type="button" value="按钮" v-on:click="show2('world')">
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld'
},
methods: {
show1: ()=>{alert('hello')},
show2: str=>{alert(str)}
}
});
</script>
Vue对象的函数写法
- 普通定义函数
- JS
function fn1(val){ //普通写法
return val + 1;
}
var fn2 = function(val) { //使用变量承接
return val + 1;
}
const fn3 = function(val) { //使用ES6的const或let
return val + 1;
}
- JS对象/Vue对象
var vm = new Vue({
el:'.myapp',
methods: {
fn: function(val) { //普通函数写法
return val + 1;
}
}
});
- ES6箭头函数:this指向上下文相同
- JS
const fn = val => val + 1;
- JS对象/Vue对象
var vm = new Vue({
el:'.myapp',
methods: {
fn: val => val + 1 //箭头函数
}
});
- ES6名称定义函数
- JS
class中的get
和set
。 - JS对象/Vue对象
var vm = new Vue({
el:'.myapp',
methods: {
fn(val) {
return val + 1;
}
}
});
实例:跑马灯效果
- 原生JS实现
<div>
<button onclick="openAct()">开启</button>
<button onclick="closeAct()">关闭</button>
<p id="mytext">HelloWorld</p>
</div>
<script>
/*需改变元素的DOM*/
const mytextDOM = document.getElementById("mytext");
/*轮转字符串函数*/
const transStr = str => str[str.length - 1] + str.slice(0, str.length - 1);
/*开启与关闭*/
let t;
const openAct = () => {
t = setInterval( () => {
mytextDOM.innerHTML = transStr(mytextDOM.innerHTML);
}, 500);
};
const closeAct = () => {
clearInterval(t);
};
</script>
- 代码结果:点击开启时,字符串开始旋转
- 代码结果:点击关闭时,字符串停止旋转
- Vue.js实现
<div class="myapp">
<button @click="openAct">开启</button>
<button @click="closeAct">关闭</button>
<p v-text="msg"></p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data: {
msg: 'HelloWorld', //字符串
timer: null //用于接收定时器的变量
},
methods: {
transStr:
str => str[str.length - 1] + str.slice(0, str.length - 1), //字符串单次轮转函数
openAct(){
if(this.timer == null){ //debug:多次点击开启会一直分配新的内存空间
this.timer = setInterval(()=>{ //设置定时器
this.msg = this.transStr(this.msg); //将字符串轮转单次
}, 500) //定时器this指向window,应使用箭头函数同质化this或bind()方法绑回this
}
},
closeAct(){
clearInterval(this.timer); //清除定时器
this.timer = null; //debug:关闭后重新开启
}
}
});
</script>
- 使用方法
使用了v-on
绑定点击事件,使用Vue对象中的数据和方法。 - 自动监听
Vue对象会自动监听,data中的值会自动同步到页面中。 - 优点
不必操作DOM,不必关心页面渲染,把核心工作放到数据处理。
事件修饰符
- 总结
事件修饰符 | 作用 | 场景举例 |
---|---|---|
.stop | 阻止冒泡 | 大标签内小标签,点击时触发两个事件 |
.prevent | 阻止默认行为 | a标签的默认跳转 |
.capture | 添加事件监听器时使用捕获模式 | 大标签内小标签,点击时触发两个事件 |
.self | 只当事件在该元素(比如不是子元素)本身触发时回调 | 大标签内小标签,点击时触发两个事件 |
.once | 事件只触发一次 | 任何事件 |
.stop
:阻止冒泡
- 普通状态:冒泡
<div class="myapp" @click="fn1">
<button @click="fn2">按钮</button>
</div>
- 使用
.stop
:阻止冒泡
<div class="myapp" @click="fn1">
<button @click.stop="fn2">按钮</button>
</div>
建议在子级元素定义。
.prevent
:阻止默认行为/事件
- 普通状态:默认跳转
<div class="myapp">
<a href="http://www.baidu.com">链接</a>
</div>
- 使用
.prevent
:阻止默认跳转
<div class="myapp">
<a href="http://www.baidu.com" @click.prevent="fn">链接</a>
</div>
.capture
:添加事件监听器时使用捕获模式
冒泡:从内往外
捕获:从外往内
- 普通状态:冒泡从内往外
<div class="myapp" @click="fn1">
<button @click="fn2">按钮</button>
</div>
- 使用
.capture
:捕获从外往内
<div class="myapp" @click.capture="fn1">
<button @click="fn2">按钮</button>
</div>
建议在父级元素定义。
.self
:只当事件在该元素(比如不是子元素)本身触发时回调:添加事件监听器时使用捕获模式
- 普通状态:点击内部时,外部会冒泡
<div class="myapp" @click="fn1">
<button @click="fn2">按钮</button>
</div>
- 使用
.self
:点击内部触发内部,点击外部触发外部
<div class="myapp" @click.self="fn1">
<button @click="fn2">按钮</button>
</div>
建议在父级元素定义。
.self
和.stop
的区别:.self
使自身不会被其它元素向外冒泡,.stop
是自身不会触发向外冒泡。
.once
:事件只触发一次
- 普通状态:触发事件
<div class="myapp">
<a @click="fn">链接</a>
</div>
- 使用
.once
:多次点击只触发一次事件
<div class="myapp">
<a @click.once="fn">链接</a>
</div>
- 可串联特性:可以串联使用事件修饰符
<div class="myapp">
<a href="http://www.baidu.com" @click.prevent.once="fn">链接</a>
</div>
第一次点击,不跳转(阻止了默认行为),触发事件。
第二次点击,跳转(阻止默认行为只触发一次)。
两者顺序交换,效果等价。
按键修饰符
<div class="myapp">
<!--键盘事件-->
<input type="text" onkeyup="alert('键盘事件')"> <!--松开时-->
<input type="text" onkeydown="alert('键盘事件')"> <!--按下时-->
<input type="text" onkeypress="alert('键盘事件')"> <!--按下并松开时-->
<!--使用v-on-->
<input type="text" @keyup="show">
<!--按键修饰符-->
<input type="text" @keyup.enter="show"> <!--按下enter时才触发-->
<input type="text" @keyup.13="show"> <!--enter的键盘码为13-->
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
methods:{
show(){
alert("键盘事件")
}
}
});
</script>
- 按键码别名
- 按键码
参考文档:js 里面的键盘事件对应的键码
自定义按键修饰符:Vue.config.keyCodes.名称
- 根据按键码自定义按键修饰符
Vue.config.keyCodes.myf = 113;
113
为F2。
如此定义后,可以使用自定义修饰符.myf
代表F2。
逻辑渲染
v-for属性:迭代渲染
- 目标可以为:数组、对象、数字
<div class="myapp">
<p>普通数组</p>
<p v-text="mylist"></p>
<p>迭代数组:参数(值, 索引)</p>
<p v-for="item in mylist">{{item}}</p>
<p v-for="(myval, myindex) in mylist">The {{myindex}} is {{myval}}</p>
<p>迭代对象数组</p>
<p v-for="item in myobjlist">The id {{item.id}} is {{item.name}}</p>
<p>迭代对象:参数(值, 键值, 索引)</p>
<p v-for="(myval, mykey, myindex) in myobj">The {{myindex}}: {{mykey}} is {{myval}}</p>
<p>迭代数字:从1到n,而非0到n-1</p>
<p v-for="i in 5">{{i}}</p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data:{
mylist: ['a', 'b'], //数组
myobjlist: [{id:1, name:"s1"}, {id:2, name:"s2"}], //数组,每个元素是对象
myobj: {id:3, name:"s3"} //对象
}
});
</script>
- 代码结果(检查):
- 注意事项
2.2.0版本后,v-for
遍历对象必须有key(HTML)属性
- key属性不接受对象,只接收string或number。
- key属性可以用
v-bind
指定其取值,可以使用v-for
中用于遍历的变量(此处为item)中的字符串或数字。 - 如果有复选框等,指定key属性可以防止已选定按钮,在(从上往下)添加后会错位的问题。
- 结论:key属性将遍历后的结果一一标记。
<div class="myapp">
<p v-for="item in myobjlist" :key="item.id" style="color:#ff0000">
<input type="checkbox">
{{item.id}}: {{item.name}}
</p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data:{
myobjlist: [{id:1, name:"s1"}, {id:2, name:"s2"}] //数组,每个元素是对象
}
});
</script>
v-if/v-show属性:选择渲染
<div class="myapp">
<button @click="trans">切换方式1</button>
<button @click="flag=!flag">切换方式2</button>
<p v-if="flag">内容</p>
<p v-show="flag">内容</p>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data:{
flag: true
},
methods:{
trans () { //使用箭头函数this会指向window
this.flag = !this.flag;
}
}
});
</script>
- 代码结果:取值为
true
时渲染 - 比较
条件渲染属性 | true | false | 性能消耗较高的场景 |
---|---|---|---|
v-if | 渲染 | 删除 | 渲染后频繁切换布尔值 |
v-show | 渲染 | style="display: none;" | 初始渲染,尤其是一开始为false 且不会改变布尔值 |
Vue实例的生命周期
生命周期图示
- 官方文档
构造函数对象的生命周期函数 & 构造函数对象总结
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
/*基础-Vue实例(元素、数据与方法)*/
el:'.myapp',
data:{msg:'ok'},
methods:{},
/*可复用性&组合-过滤器、自定义指令*/
filters:{},
directives: {},
/*基础-Vue实例-生命周期钩子函数:组件创建*/
beforeCreate(){},//执行:实例创建之前 //不可用data、methods
created(){},//执行:实例创建之后 //可用data、methods
beforeMount(){},//执行:HTML模板已编译,挂载渲染到页面之前 //不可用{{插值表达式}}、DOM
mounted(){},//执行:HTML模板已编译,挂载渲染到页面之后 //可用{{插值表达式}}、DOM
/*基础-Vue实例-生命周期钩子函数:组件运行、销毁*/
beforeUpdate(){},//执行:data数据改变后,HTML界面更新之前
updated(){},//执行:data数据改变后,HTML界面更新之后
beforeDestroy(){},//执行:关闭页面,销毁之前 //可用所有Vue内容
destroyed(){}//执行:关闭页面,销毁之后 //不可用所有Vue内容
});
</script>
调试工具vue-devtools
安装过程
- 获取工具(Github、翻墙、百度等)
- 打开Chrome浏览器,最右侧扩展栏点击->更多工具->扩展程序->开发者模式->加载已解压的扩展程序
- 将先前获取的工具文件夹添加进去
- 在已出现的vuedevtools下,设置详细信息
调试
- 双向绑定:即时更新
vue-resource
Vue 要实现异步加载需要使用到 vue-resource 库。
Vue.js 2.0 版本推荐使用 axios 来完成 ajax 请求。
引入
<script src="js/vue.js"></script>
<script src="js/vue-resource.js"></script>
vue-resource.js依赖vue.js,要在其之后引入。
就和bootstrap.js依赖jQuery库一样。
引入后可以使用this.$http
。
跨域问题
参考文档:Chrome - 开发时暂时关闭跨域限制
发起请求
- Github:示例
- 测试
<div class="myapp">
<button @click="getInfo">按钮</button>
</div>
<script src="js/vue.js"></script>
<script src="js/vue-resource.js"></script>
<script>
var vm = new Vue({
el:'.myapp',
data:{
url: 'https://www.baidu.com',
mydata: {foo: 'bar'}
},
methods:{
getInfo(){
this.$http.get(this.url).then(
res => { //successCallback
console.log(res)
}, response => { //errorCallback(alternative)
});
},
postInfo(){
this.$http.get(this.url, this.data).then(
res => { //successCallback
console.log(res)
}, response => { //errorCallback(alternative)
});
}
}
});
</script>
- 代码结果