Vue.js
vue.js前端主流框架,一套用于构建用户界面的渐进式框架,研发者尤雨溪
vue.js的引入
构建用户界面:ui html css 静态页面
渐进式框架:延伸出来有五部分
1.模板引擎 类似于pug nunjucks
2.组件(核心功能之一),增加页面的复用性
3.路由
4.状态管理器(统筹管理属性-可伸缩性)
5.自动化构建
#模板引擎
三个核心:
1.el 挂载点
2.template 模板(最终形成用户界面的原始结构)
3.data 数据
vue将data和template相结合,最后添加到挂载点上
new Vue({
el: '#app',
template:`
<h1>{{title}}</h1>
`,
data:{
title:'未来可期'
}
})
1.在模板生成后,会替换掉挂载点指定的元素
new Vue({
el:'#app',
template:`
<div id="app">
<h1>未来可期</h1>
</div>
`,
})
2.每个独立的模板有且只能有一个顶级的根节点
new Vue({
el:'#app',
template:`
<div id="app">
<h1>未来可期</h1>
</div>
<h1>{{name}}</h1>
`,
})
3.如果有指定的el而没有template,那么el中的outerHTML将作为template
如果有template,则会优先选择template中的内容
new Vue({
el:'#app',
data:{
name:'dsadd'
}
})
vue渲染
template→加载到VDOM(虚拟dom,与dom规则一致)→将结果返还给html页面
vue的render函数
渲染的方式有两种,template和render
render是字符串模板的代替方案,允许我们发挥JavaScript最大的编程能力,该写法相对复杂
该渲染函数接收一个 createElement方法作为第一个参数用来创建Vnode
这个createElement名字可自定义,它就是上面所说的虚拟DOM
createElement()有三个参数
参数1:元素或标签名
参数2:属性
参数3:内容 可以是一个数组也可以是一个字符串
new Vue({
el:'#app',
render(i){
// return i('h1','1906');
return i('div',{attrs:{id:'app'}},[
I('h1',{attrs:{id:'h'}},'1906')
]);
}
});
render依然会覆盖掉挂载点el,可通过重新创建一个一样的元素来解决这个问题
延迟挂载
实例化Vue时没有设置el挂载点的挂载方式
指定挂载点
1.el 挂载点不能是body和html
2.当实例被挂载以后,实例对象上就会有一个 e l 的 属 性 , 这 个 属 性 中 存 的 内 容 就 是 挂 载 的 元 素 3. v u e 实 例 上 的 内 置 属 性 都 是 以 el的属性,这个属性中存的内容就是挂载的元素 3.vue实例上的内置属性都是以 el的属性,这个属性中存的内容就是挂载的元素3.vue实例上的内置属性都是以或者_开头的,加上会被认为是内置属性
let app= new Vue({
template:`
<div id="app">
<h1>未来可期</h1>
</div>
`,
})
//指定挂载点
// 1.el 挂载带点 不是能body 和 html
// 2.当实例化被以后,实例化对象上就会有一个$el 的属性,这个属性中存的内容,就是挂载的元素
// 3.vue 实例上的内置属性都是以$或——开头的
app.$mount('#app');
data
1.在当前模板中可以直接使用(不需要去使用this一类的关键字)
2.data中的数据命名不要使用$或_开头
因为Vue解析data以后,会把当前data中的数据加载到 实例对象中
new Vue({
el:'#app',
template:`
<div id="app">
<h1>未来可期</h1>
</div>
`,
})
这里是引用视图更新
响应数据的变化(原理是数据驱动视图)
数据的变化会自动更新视图 根据数据劫持
let app = new Vue({
el: '#app',
data:{
title:'未来可期'
}
})
数据劫持
Object.defineProperty() 该方法只能监听某个对象中的某个属性
参数1:被监听的对象
参数2:被监听对象的属性
参数3:被监听的属性发生改变时调用的方法,以对象的形式传入set(){}和get(){}
每当数据发生改变时将新的值赋值给被监听的属性,但因为方法问题,会陷入死循环
所以需要一个数据,来代替被监听对象
流程:当被监听的属性发生改变时,会赋值给替代数据,然后渲染视图,渲染视图时会去找被监听的属
性,运行get()事件
let $data = {x:1}
//数据劫持一变相数据监听
Object.defineProperty(obj,'x',{
set(i){
$data.x = i;
render();
},
get(){
return $data.x ;
}
});
无法对新增的属性进行监听
只有更新已有属性时,才能更新未被监听的属性
let app = document.querySelector('#app');
function render(){
console.log('渲染开始-');
app.innerHTML = obj.x + "/" + obj.age;
}
let obj ={
x :1
}
//问题 无法对新增的属性进行监听(之能监听一个)
Object.defineProperty(obj,'age',{
set(i){
$data.age = i;
render();
},
get(){
return $data.age ;
}
});
在模板中,如果我们使用了一个不存在的数据,需要一个方法来添加这个属性
Vue.set()或实例.$set()方法可以添加属性并监听该属性
<body>
<div id="app">
<h1>x: {{obj.x}}</h1>
<h1>y: {{obj.y}}</h1>
</div>
<script>
let app =new Vue({
el:"#app",
data:{
obj:{
x:1
},
}
})
app.$set(app.obj,'y','2');
</script>
</body>
以下的操作中并不会触发监听拦截
属性新增属性
数组方法:push、pop、shift、unshift、splice、sort、reverse
数组新增值:[ ]
数组 length 属性
vue 对数组中的 push、pop 等方法进行重新包装,所以在 vue 中调用这些方法,可以对数组的修改进行监听拦截
<body>
<div id="app">
<h1>x: {{obj.x}}</h1>
<h1>y: {{obj.y}}</h1>
<p>{{arr}}</p>
</div>
<script>
let app =new Vue({
el:"#app",
data:{
obj:{
x:1
},
arr:[1,2,3]
}
})
app.$set(app.obj,'y','2');
</script>
</body>
|
表达式的值除了可以出现内容中,也可以使用在其它位置,比如:属性。但是不能使用 {{}} 语法,而是需要 指令
在 vue 中,指令是一个带有 v- 前缀的属性,与普通属性不一样的地方在于,指令的值是引号括起来的 表达式,不同的指令有不同的作用,vue 内置了一些常用的指令,后期我们还可以自定义属于自己的指令
常用指令:内容输出,循环,逻辑,属性绑定,事件,其它
指令格式: v-指令名称 = 表达式(而不是普通的字符串)
内容输出
1.v-text
这个方法有一个弊端,他会替换掉所有的内容,相当于innerHTML
<div id="app" v-text="x">
<div>{{}}</div>
</div>
<script>
new Vue({
el:'#app',
data:{
x:123
}
})
</script>
2.v-html 它会将内容当作一个html标签来解析
为了防止 xss 攻击,默认情况下输出是不会作为 html 解析的,通过 v-html 可以让内容作为 html 进行解析
<div id="app"">
<div>{title}}</div>
<h1 v-text = 'title'></h1>
<p v-html='title'></p>
</div>
<script>
new Vue({
el:'#app',
data:{
x:123,
title:'<div>是我</div>',
}
})
</script>
3.v-cloak 不需要表达式
这个指令保持在元素上直到关联实例结束编译
编译结束时效果失效
4.v-once 不需要表达式
只渲染元素和组件一次,后期的更新不再渲染,会被视为静态内容并跳过
用于优化更新性能
<div id="app"">
<div>{x}}</div>
<div v-once>{{x}}</div>
</div>
<script>
new Vue({
el:'#app',
data:{
x:123,
}
})
</script>
5.v-pre 不需要表达式
忽略这个元素和它子元素内容的编译,显示原始的标签
跳过大量没有指令的节点会加快编译
<div id="app">
<div>猜猜我是谁{x}}</div>
<div v-pre>2222222{{x}}</div>
</div>
<script>
new Vue({
el:'#app',
data:{
x:"冲",
}
})
</script>
逻辑处理
v-show
根据表达式的值(布尔值),切换元素的显示与隐藏(display 属性)
适用于状态切换比较频繁的情况
<div id="app">
<div v-show='islogin'>隐藏数据</div>
</div>
<script>
let app = new Vue({
le:'#app',
data:{
islogin:false
}
})
</script>
v-if
根据表达式的值(布尔值),创建或销毁元素
v-else / v-else-if
与 v-else
配合
<div id="app">
<!-- v-if与v-else以及v-else-if之间必须是链接的,中间不能出现其他元素 -->
<!-- vif改变的是元素的结构 (渲染或者删除)适用于状态切换不频繁的场景 --->
<div v-if='islogin'>数据暴漏</div>
<div v-else>please login</div>
</div>
循环与列表
v-for
根据数据循环渲染 v-for
指令所在的元素及其子元素
可以循环的数据:Array | Object | number | string | Iterable (2.6 新增)
<!-- 指令中的循环 -->
<div id="app">
<ul>
<!-- 也可以使用in作为连接词,两者在vue中无区别 -->
<li v-for = 'user of users'>
<input type="checkbox">
{{user.id}} - {{user.name}}
</li>
</ul>
</div>
<script>
let app = new Vue({
el:'#app',
data:{
users:[
{id:1,name:'犯罪@嫌疑人——张三'},
{id:2,name:'卑微@受害者——张四'},
{id:3,name:'隔壁@单身汉——王五'},
{id:4,name:'邻居@帅帅帅——八块'},
],
}
})
</script>
:key
默认情况下,在渲染 DOM
过程中使用 原地复用 ,这样一般情况下会比较高效,但是对于循环列表,特别是依赖某种状态的列表,会有一些问题,我们可以通过 :key
属性,来给每个循环节点添加一个标识
<!-- 指令中的循环 -->
<div id="app">
<ul>
<li v-for = 'user of users' :key='user.id'>
<input type="checkbox">
{{user.id}} - {{user.name}}
</li>
</ul>
<div v-for='(val,name,index) in object'>
<span>索引值:{{index}}</span>
<span>属性名:{{name}},值:{{val}} </span>>
</div>
</div>
let app = new Vue({
el:'#app',
data:{
users:[在这里插入代码片
{id:1,name:'犯罪@嫌疑人——张三'},
{id:2,name:'卑微@受害者——张四'},
{id:3,name:'隔壁@单身汉——王五'},
{id:4,name:'邻居@帅帅帅——八块'},
],
object:{
x:'a',
y:'b',
z:'c'
}
}
})
属性绑定
v-bind
绑定数据(表达式)到指定的属性上,<div v-bind:参数="值/表达式"></div>
,这里的参数就是指定的属性名称
<div id="app">
<div v-bind:class="myclass"></div>
<div :id="Id"></div>
</div>
<script>
v-指令参数: 参数= '指令的值'
简写:
:=> v-bind
let app =new Vue({
el:"#app",
data:{
myclass:"box",
Id:'haha'
}
});
</script>
属性绑定 v-bind
绑定数据(表达式)到指定的属性上,<div v-bind:参数="值/表达式"></div>
,这里的参数就是指定的属性名称
样式 tyle
针对样式属性,v-bind
值有一些特殊的写法
原生普通写法
<div id="app">
<!-- 原生写法 -->
<!-- <div style = "width: 100px ;height: 100px;"></div> -->
<!-- v-bind -->
<div :style='style1'></div>
<!-- 数组写法
:style 获取的是具体的样式
:class 获取的是类名 -->
<div :class="box"></div>
<div :style='[style1,style2]'></div>
<!-- 对象写法 -->
<div :class="{'box1' : new_box,'box2':two_box}"></div>
</div>
<script>
let app = new Vue({
el:"#app",
data:{
style1:{
width:'100px',
height:'100px',
background:'red'
},
style2:{
// width:'100px',
// height:'100px',
border: "1px solid black"
},
box:"dahua",
new_box:false,
nwo_box:false
}
})
</script>
单向数据流
通过上面的知识点和案例,我们可以看到,当数据更新的时候,页面视图就会更新,但是页面视图中绑定的元素更新的时候,对应的数据是不会更新的
单项数据流
v-bind 单向数据绑定 数据到视图
v-model: 双向数据绑定: 数据=> 视图 , 视图=>数据
不上=是所有的标签(组件)都支持 v-model
--默认 交互元素 (input textarea,)
不是所有的属性都支持 v-model
-- v-model 只能绑定一个指定好的属性
input :value
textarea ;value
select :selected
非交互 ; div p img 等静态类标签(不需要统计信息的)
<div id="app">
<!-- 双向绑定 -->
<input type="text" v-model="msg">
<div>{{msg}}</div>
<br>
<br>
<!-- 单项绑定 -->
<input type="text" v-model="msg2">
<div>{{msg2}}</div>
</div>
<script>
let app = new Vue({
el:"#app",
data:{
msg:"123",
msg2:"abc"
}
});
</script>
v-on
用法:
绑定事件监听器 事件类型由参数指定
表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略
修饰符:
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
指令修饰符
格式:指令名称:参数.修饰符=值
v-model的修饰符:
.lazy 取代 input 监听 change 事件
.number 将用户输入的值转换为number类型
.trim 输入首尾空格过滤
不同的指令有不同的参数,也有不同的修饰符
注意事项:1.不同的指令下,有不同的修饰符,需要使用时,看api中对应的指令
自定义指令
一、为什么允许注册自定义指令
vue2.0中,代码复用和抽象的主要形式是组件,但是有的情况下,仍然需要对普通dom元素进行底层操作,这是就会用到自定义指令
二、自定义指令分为全局指令和局部指令
1.注册一个全局指令需要使用directive()方法
//参数1: 指令名称
//参数2: 配置对象(方法名不是自定义)
Vue.directive("focus",{
//当被绑定的元素插入到DOM 中时触发
inserted: function (el) {
// console.log("我被触发了");
el.focus();
}
});
2.注册一个局部指令
let app = new Vue({
el:"#app",
directives:{
foncus:{
inserted:function(el){
el.focus()
})
}
})
三、钩子函数
存在的意义:可以让开发者灵活控制指令功能执行的时间
一个指令定义对象可以提供如下几个钩子函数 (均为可选)
1.bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
2.inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
3.update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
4.componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
5.unbind:只调用一次,指令与元素解绑时调用。
2.binding 一个对象,包含以下属性
name:指令名,不包括 v- 前缀。
value:指令的绑定值,
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression:字符串形式的指令表达式。
arg:传给指令的参数,可选。
modifiers:一个包含修饰符的对象。
.oldVnode 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
自定义修饰符
自定义修饰符用于节省自定义指令的数量,以及提高适用性
两个功能相类似,但是存在部分差异时,使用自定义修饰符
事件
通过指令v-on(简写@)来实现
v-on:事件名称.修饰符=“执行的函数或表达式”
注意点:
1.事件执行的函数,需要在methods中定义
2.事件绑定时的执行函数,有无小括号都是调用
3.this指向:methods对象中的函数,this指向永远是当前的Vue实例
4.事件对象:event 储存着事件所有信息
事件绑定函数中的第一个参数默认为 event 对象
当执行函数带小括号以及有其它参数时,event需要手动传入,名称为$event
模板中的 $event是规定死的
methods
在组件选项中,提供了一个 methods 选项,用来存放组件中使用的函数方法,且存放在 methods 中的函数方法可以通过组件实例(this)进行访问
过滤器
对内容或数据进行过滤或二次处理的一种方式
过滤器的使用:类似于管道流
管道流:将第一个的结果,以参数(第一位)的形式传递给下一个,以此类推
| : 管道符,表示数据从左至右通过管道符进行传递
过滤器可以有多个,执行顺序从左至右,过滤器函数第一个参数的值就是其管道符前一个的结果。
<div id="app">
<ul>
<li v-for='item of items'>
<span>名称:{{item.name}}</span>
<span>-</span>
<span>{{rmb(item.price,"$")}}</span>
</li>
</ul>
</div>
let app = new Vue({
el:'#app',
data:{
items:[
{id:1,name:'iphone',price:'88888'},
{id:1,name:'ipad',price:'777777'},
{id:1,name:'imac',price:'666666'}
]
},
这里是引用局部过滤器
Vue.component(‘组件’, {
…,
filters: {
‘过滤器名称’: 过滤器函数
}
})
new Vue({
el: '#app',
data: {
time: Date.now()
},
filters: { //过滤器的配置项
'timeFilter': function ( val,type ){
var date = new Date ( val )
return date.getFullYear() + type + ( date.getMonth() + 1 ) + type + date.getDate()
}
}
})
computed
在实际的应用中,我们会有一些原始数据,同时在应用中又会有一些数据是根据某些原始数据派生出来的,针对这样的一种情况,vue 定义了一个专门用来处理这种派生数据的选项:computed
computed
1.储存被修改过或逻辑处理过的数据
2.computed是一个对象,类似于data而不是methods,素以称为计算属性(如果数据需要被二次或多次处理,就属于计算属性)
3.computed中的属性可以像data中存放的数据一样去使用
4.它的结构类似(defineProperty 数据劫持),getter,setter
5.计算属性的值同时还会根据它所依赖的数据的变化而自动变化
<div id="app">
<div>
<button :class="{active: gender ===''}" @click = "gender =''">全部</button>
<button :class="{active: gender ==='男'}" @click = "gender ='男'">男</button>
<button :class="{active: gender ==='女'}" @click = "gender ='女'">女</button>
</div>
let users =[
{id:1,username:'iphone',gender:'男'},
{id:2,username:'ipad',gender:'女'},
{id:3,username:'imac',gender:'男'},
{id:4,username:'ipad7',gender:'男'},
{id:5,username:'imac8',gender:'女'},
{id:6,username:'ipad9',gender:'女'},
];
let app = new Vue({
el:"#app",
data:{
users,
// showUsers:users
gender:""
},
computed:{
showUsers:{
//当showUsers 这个数据被获取时,触发
get(){
if(this.gender === ""){
return this.users
}else{
return this.users.filter(user => user.gender === gender);
}
}
}
}
});
6.计算属性的值,在依赖的属性未发生改变时/或没有依赖数据
会自动缓存到应用中,下次获取时,不重新计算,直接获取上一次计算的值(Vue提升性能方法之一)
<div id="app">
<p v-if='showDate'>{{getNow()}}</p>
<p v-if="showDate">{{now}}</p>
<button @click="showDate=!showDate">check here</button>
</div>
</body>
<script>
let app = new Vue({
el:"#app",
data:{
showDate:true
},
methods:{
getNow(){
return Date.now();
}
},
computed:{
return Date.now();
}
}
})
7.如果某个计算属性只需要处理get方法,那么可以简写
computed:{
// now:{
// get(){
// return Date.now();
// }
// },
now(){
return Date.now();
}
}