0vue.js是一套构建用户界面的渐进式框架, 声明式渲染和组件系统是Vue的核心库所包含的内容。
- 构建用户界面 (通过数据渲染成页面模板,展示给前端用户)
- 渐进式 (循从Vue 模板-指令-组件-路由-vuex 等由简单到复杂的开发学习应用过程)
- 框架 (半成品的应用 , 不断在维护更新的开源框架 )
- 声明式渲染 (如同js基础一样, 要使用变量则必须先声明变量 , 这种称之为声明式)
- 组件化应用构建 (组件系统是Vue的另一个重要的概念 , 因为它是一种抽象的允许使用小型、独立和通常可复用的小积木构建大型应用 ,几乎任意类型的应用界面都可以抽象为一个组件树。)
一、 Vue的开发模式
M-V-VM
- M (model): 普通的JavaScript数据对象
- V (view) :前端展示页面(通俗讲就是html内容)
- VM(ViewModel): 用于双向绑定数据与页面
二 、 初识Vue
-
通过script标签引入Vue ,
<!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
-
new Vue() 创建Vue实例对象
- 第一个参数: el ( 指定Vue负责渲染的容器的选择器)
- 第二个参数: data { 定义数据 } (定义的是Vue实例需要的数据)
-
页面展示数据时使用插值表达式 , {{ 变量/表达式 }}
<body>
<!-- 1. 定义渲染的容器 -->
<div id="app">
<p>{{msg}}</p>
<div>
<!-- 2. 引入vue.js文件 ,-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
// 3. 产生vue实例(V是大写的),传递配置选项
new Vue({
// el => element,指定vue负责渲染的容器的选择器
el: "#app",
// data指定vue实例需要的数据(数据的初始化)
data: {
msg: "hello world",
},
});
</script>
</body>
三、Vue模板语法
1、插值表达式
是vue框架提供的一种在HTML模板中绑定数据的方式 , 使用 {{ 变量名 }} 方式进行绑定vue实例中data的数据变量, 会将绑定的数据实时的在视图中显示出来
在{{ }} 括起来的区域 , 就是一个js语法区域 , 在里面可以写部分的js语法 , 但是不能写 var a = 10 / 分支语句 / 循环语句
<div id="app">
<!-- 直接使用变量名 -->
<h1>{{name}}</h1>
<!-- 运算 -->
<h1>{{name + '好'}}</h1>
<h5>{{ 1 + 1 }}</h5>
<!-- 使用函数 -->
<h5>{{title.substr(0,6)}}</h5>
<!-- 三目运算 -->
<h5>{{ age > 18 ? '成年' : '未成年'}}</h5>
</div>
<script>
new Vue({
el: "#app",
data: {
title: "我是一个标题,你们看到没有",
name: "张三",
age: 20,
},
});
</script>
2、指令
指令以 v- 开头 , 在vue的html中 , 以v- 开头的自定义属性就是指令。
不常用的指令 (4种)
v-text (填充纯文本)
相当于之前原生的innerText , 相比插值表达式更加简洁 , 不存在插值表达式的闪动问题。
v-html (填充html片段 , 可以识别标签)
相当于原生的innerHTML , 它存在安全问题 , 容易被攻击。
v-cloak
解决浏览器在加载页面时因存在时间差而产生的闪动问题
v-once
只渲染元素或组件一次 , 之后元素或组件将失去响应式 (数据层面) 功能 .
<style>
[v-cloak] {
display: none;
}
</style>
<div id="app">
<p v-text="msg"></p> //hello world
<!-- v-text === innerText 纯文本添加 , 不解析, msg内容是什么 , p标签写入什么-->
<P v-html="msg1"></P> // 千峰 (斜体字的)
<!-- v-html === innerHTML 可以解析标签 结果是解析过的斜体字的千峰 黑客攻击,禁止使用 -->
<p v-cloak> {{msg}} </p>
<!-- v-cloak用于解决白屏问题 -->
<!-- v-once 渲染一次, 之后再发生改变不会重新渲染 -->
<p v-once> {{msg}} </p>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: 'hello world',
msg1: '<i>千峰</i>'
}
})
</script>
常用的指令
1, v-bind
它可以做到 在html内置的属性值中使用变量 ,
v-bind: 可以缩写为 :
<div id="app">
<!-- 动态属性 -->
<p v-bind:class="mycolor">千峰教育</p>
<!-- 简写动态属性 -->
<p :class="mycolor">千峰教育</p>
<!-- 动态属性 后面是对象的形式 对象值控制对象的属性 -->
<p :class="{'name': myname}">全等</p>
<!-- 动态属性 后面放数组 多个变量 -->
<p :class="[msg,mycolor]">全都健康</p>
<!-- style动态属性 -->
<p :style="myStyle">地方属性</p>
<p :style="{fontSize:'24px'}">ss</p>
</div>
<script>
new Vue({
el: '#app',
data: {
msg: 'hello world',
mycolor: 'my-color',
myname: false,
myStyle: {
fontSize: '50px'
}
}
})
</script>
2, v-on
绑定事件监听器 , 缩写形式 @
如果事件直接使用函数名且不写小括号 , 那么会默认将事件对象作为唯一参数进行传递 , 可以在定义函数的位置直接定义一个形参 , 并且在函数内可以使用该形参
如果使用常规的自定义函数调用 (写了小括号)) , 如果需要事件对象必须作为一个参数进行传递 , 事件对象的名称必须是 $event
v-on的事件修饰符
1, .once 事件只执行一次
2, .self 只有触发事件源本身时才触发 , 也可用于阻止事件冒泡行为
3, .stop 阻止事件冒泡
4, .prevent 阻止事件默认行为
v-on的按键修饰符
1, .enter 回车键的时候调用
2, .delete 删除键的时候调用
<input @keyup.enter="submit">
<button @click.once="clickfun()">点击</button>
//只执行一次clickfun函数
<a @click.prevent="clicka" href="http://baidu.com">百度</a>
//阻止点击之后默认跳转百度网页 , 执行 clicka 的内容
<div @click.self="clickparent()" style="background-color: aqua;width: 500px;height: 500px;"> //父元素添加 .self ,防止子元冒泡行为
<div @click.stop="clickchild()" style="background-color: rgb(65, 68, 68);width: 200px;height: 200px;margin: auto;">
//子元素添加 .stop 防止冒泡
</div>
</div>
<input type="text" @keyup.enter="enterFun">
//点击enter键 执行 enterFun
3, v-model
双向绑定数据 , 只适用于 表单
input , textarea , select 绑定的是value
checkbox , radio 绑定的是checked属性
v-model的修饰符:
lazy 把input事件变成 blur事件
number 因为v-model是自动转化为字符串的 , number可以将其转化为number类型
trim 去除前后的空白
<div id="app">
<p> {{msg}} </p>
<input type="text" v-model="msg">
<!-- v-model只适用于表单 -->
<p>{{text}}</p>
<textarea v-model="text"></textarea>
<p> {{checkvalue}} </p>
<label for="bj">北京</label>
<input type="checkbox" value="北京" v-model="checkvalue" id="bj">
<label for="tj">天津</label>
<input id="tj" type="checkbox" value="天津" v-model="checkvalue">
<label for="sh">上海</label>
<input type="checkbox" value="上海" v-model="checkvalue" id="sh">
<p> {{rediovalue}} </p>
<label for="bei">北京</label>
<input type="radio" v-model="rediovalue" id="bei" value="beijing">
<label for="tian">天津</label>
<input type="radio" v-model="rediovalue" id="tian" value="tianjin">
<label for="shang">上海</label>
<input type="radio" v-model="rediovalue" id="shang" value="shanghai">
<input type="text" v-model.lazy="msg">
<!-- lazy懒加载,从input事件 到blur事件-->
<p> {{msg1}} </p>
<input type="text" v-model.number="msg1">
<!-- number把当前变量转换成number类型, v-model绑定的变量是自动转换成字符串的 -->
<p> {{msg2}} </p>
<input type="text" v-model.trim="msg2">
</div>
<script>
new Vue({
el: '#app',
data: {
msg: 'hello',
msg1: 123,
msg2: 'htll'
text: '文本内容',
selectvalue: 'beijing',
checkvalue: [],
rediovalue: ''
}
})
</script>
4, v-for
循环指令 , 根据一组数组或者对象的选项列表进行渲染.
<div id="app">
<ul>
<li v-for="(item , index) in arr" :key="index">
下标:{{index}} 元素:{{item}}</li>
</ul>
<ul>
<li v-for="item in arr1" :key="item.id">
id:{{item.id}} , 姓名:{{item.name}}</li>
</ul>
<ul>
<li v-for="(val , key , index) in obj" :key="index">
下标: {{index}} 属性: {{key}} , 属性值: {{val}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#app',
data: {
arr: [1, 2, 3, 4, 5],
arr1: [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
}, {
id: 3,
name: '王五'
}
],
obj: {
name: '保定',
age: 22,
address: '保定'
}
}
})
</script>
5, v-if v-else-if v-else 或者 v-show
v-if v-else-if v-else 根据表达式的布尔值进行判断是否渲染该元素 ,
v-show 是已经全部渲染了 , 根据表达式的真假 , 切换display的属性值
<div id="app">
<p v-if="num === 0">零铃铃铃</p>
<p v-else-if="num === 1">一壹亿壹</p>
<p v-else>其他</p>
<p v-show="num1 > 1">大于1</p>
<p v-show="num1 <0">小于零</p>
<p v-show="num1>0 &&num1<1">大于零小于1</p>
</div>
<script>
new Vue({
el: '#app',
data: {
num: 1,
num1: 0.5
}
})
</script>
v-if 和 v-show的区别:
v-if 控制这个元素是不是要渲染 , true 是要渲染 , false 是不渲染
v-show是控制这个元素要不要展示 , 元素是肯定要渲染的 , false是通过行内样式 隐藏的 , 但是节点存在
v-if 和v-show 比较?
如果说不涉及到节点的删除和渲染的话就用 v-if
如果说切换频繁 , 就用 v-show
6, 自定义指令
除了核心功能默认内置的指令 , vue也允许开发者注册自定义指令 , 自定义指令分为 全局指令 和 局部指令.(全局指令适用于整个项目 , 局部指令适用于当前组件 )
自定义指令常用 钩子函数:
1, bind : 在指令第一次绑定到元素时调用
2, inserted : 被绑定元素插入父节点时调用 ,
3, update :数据更新时调用
4, componentUpdated : 指定元素及子节点更新完成后触发
5, unbind : 取消绑定后触发
注意: 不管是定义全局指令 还是 定义局部指令 , 所提及的指令名称均不带 v-前缀名称
<div id="app">
<div v-red>武汉上演</div>
<div v-color="'blue'">防御严寒</div>
<input type="text" v-model="mobile" v-mobile>
</div>
<script>
Vue.directive('red', {
inserted: function(el) {
el.style.color = 'red'
}
});
Vue.directive('color', {
inserted: function(el, val) {
el.style.color = val.value
}
})
new Vue({
el: '#app',
data: {
mobile: ''
},
directives: {
mobile: {
update: function(el) {
let mobile = el.value
if (/^1[3-9]\d{9}$/.test(mobile)) {
el.style.color = 'yellow'
} else {
el.style.color = 'pink'
}
}
}
}
})
</script>
四、vue常用属性
1, 计算属性
模板中放入太多的逻辑(方法 ) 会让模板过重难以维护 , 使用计算属性可以让模板变得简洁易于维护. 计算属性是基于它们的响应式依赖进行缓存的 , 计算属性比较适合对多个变量或对象进行处理后返回一个结果值 , 也就是数多个变量中的某一个值发生变化 , 我们监控的这个值也会发生变化
计算属性通过 computed属性对象中定义一个一个的函数 并返回一个值
注意:
计算属性必须要有 return
在某种特定的场景下 , 计算属性的效率要比methods效率高 , 计算属性支持数据的缓存操作 (在依赖数据不变的情况下)
<!--
执行结果:
computed本次只执行一次
methods执行
methods执行
-->
<div id="app">
<p> {{num}} </p>
<p> {{doublenum}} </p>
<p> {{doublenum}} </p>
<p> {{doublenum2()}} </p>
<p> {{doublenum2()}} </p>
<button @click="num++"> + </button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'hello',
num: 10 //数据改变当前页面会刷新
},
computed: {
doublenum() {
console.log('computed本次只执行一次')
return this.num * 2
}
},
methods: {
doublenum2() {
console.log('methods执行')
return this.num * 2
}
}
})
</script>
2, 监听器
使用watch来侦听data中的数据的变化 , watch中的属性一定是data中已经存在的数据 (特殊情况除外), 每个监听的方法可以接受两个参数 , 第一个参数是新的值 , 第二个参数是之前的值 ,
如果需要监听一个对象的改变时 , 普通的watch方法无法监听到对象内部属性的变化 , 此时需要deep属性对对象进行深度监听.
new Vue({
el: '#app',
data: {
msg: 'hello world',
obj: {
name: '大壮',
job: {
age: 22
}
}
},
watch: {
msg: {
'handler': 'changemsg',
'immediate': true,
},
obj: {
'handler': 'changeobj',
'immediate': true, //首次执行watch , 没有旧的值
'deep': true //是深度监听 , 默认是浅监听
}
},
methods: {
changemsg(newval, oldval) {
console.log('msg改变')
console.log(newval)
console.log(oldval);
},
changeobj(){
console.log('obj改变')
}
}
})
3, filter过滤器
用于格式化数据 , 比如字符串格式化为首字母大写 , 将日期格式化为指定的格式.
过滤器可以定义为全局过滤器 和 局部过滤器
全局过滤器
Vue.filter('过滤器名称' , (val) =>{
return val进行处理
})
局部过滤器
new Vue({
filters:{
过滤器名称: (val) =>{
return val进行处理
}
}
})
五、生命周期
生命周期: 从vue实例产生开始到vue实例被销毁这段时间所经历的过程.
vue更像工具人 , 在整个过程中只会按照作者预设的程序去做事 , 不能由开发者去控制 , 这样开发时的限制很多 , 因此开放了生命周期 , 允许我们定义vue在特定的时候执行钩子函数( 做自己想要的事情)
vue生命周期的主要阶段:
-
创建
beforeCreate 创建前 vue实例创建前阶段 , 咋此时不能获取data中的数据 created 创建后 vue实例已经创建完成 , 在此时可以获取data中的数据和methods中的方法 beforeMount 挂载前 还没有将渲染模板挂载在el元素上 , mounted 挂载后 页面加载完毕的时候 , 就是此时 , 默认情况下 , 在组件的生命周期中只会触发一次
-
更新 (元素或组件的变更操作)
beforeUpdate 更新前 updated 更新后 可以重复触发
-
销毁 (销毁相关属性)
beforeDestroy 销毁前 destroyed 销毁后 销毁( 手动 ) 使用 this.$destroy()
六、网络请求
1,fetch (不常用 , 了解)
优点:
1, 浏览器内置的, 相比于jQuery而言 , 省去了导入js包的麻烦
2, 可以看做xhr的升级版
3, 主持promise
4, 支持多种请求类型 ,但是默认为get请求类型
有几种语法形式:
语法1:
fetch(url)
.then( res => res.json())
.then( res => console.log(res))
语法2:
fetch(url , {
methods: 'post' , // 或者是get
body:JSON.stringify(data) , //发送的json数据
headers: new Headers({
'Content-Type' : 'application/json'
}),
}).then(res => res.json())
. then( res => connsole.log(res))
语法3:
fetch(url , {
methods: 'post' , // 或者是get
body:"username=zhangsan&age=11" , //发送的json数据
headers: new Headers({
'Content-Type' : 'application/json'
}),
}).then(res => res.json())
. then( res => connsole.log(res))
})
2, axios
axios是一个基于promise的HTTP库 , 可以用在浏览器和node.js 中 , axios是vue作者推荐使用的网络请求库 , axios具有以下特性:
-
1, 支持浏览器和node.js
-
2, 支持promise
-
3, 能够拦截请求和响应 (拦截器)
-
4, 自动转换json数据
在使用axios之前需要在对应的模板文件中引入axios的js库文件
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
有几种语法形式:
methods: {
getData0() {
axios.get('https://api.i-lynn.cn/college', {
params: {
id: 1,
name: '大壮'
}
})
.then(res => {
console.log(res)
this.data = res.data.data
})
}
,
getData1() {
axios.get('https://api.i-lynn.cn/college?name=dazhuang')
.then(res => {
console.log(res)
this.data = res.data.data;
})
},
getData2() {
axios.post('https://api.i-lynn.cn/college', 'name=dazhuang&age=22').then(
res => {
console.log(res)
this.data = res.data.data
}
)
},
getData3() {
axios({
method: 'post',
url: 'https://api.i-lynn.cn/college',
timeout: 1000, // 超过没响应 报超时错误
headers: {
//表单提交
// 'Content-Type': 'application/x-www-form-urlencoded'
//json提交 默认
// 'Content-Type': 'application/json'
},
data: {
username: 'zhangsan',
type: 2
}
}).then(res => {
console.log(res)
this.data = res.data.data
})
}
axios与fetch相比 , fetch最终的res就是我们的返回值 , 而axios这里的res不是我们的返回值 , 而是axios的请求响应的响应对象 , 我们的返回数据在res.data中.
七、脚手架
cnpm i -g @vue/cli 脚手架
vue --version 查看版本
vue create myapp myapp项目名称
八、组件
组件化把某个页面进行拆分, 最后再做合并 ,组件.vue文件结尾都是组件 , 组件有template script style 三部分组件, template模板( 不止可以写标签 , 还可写vue指令 ) ,
组件命名无论是 vue还是 react , 都需要使用大驼峰命名法
组件的使用:
1, 定义组件
2, 引入并注册组件
3, 使用组件
组件分全局组件和局部组件
在main.js中引入注册的组件是全局组件 ,
import Child2 from ' ./components/Child2 '
Vue.component(' Child2' , Child2 )
// 全局注册 , 第一参数是组件别名 , 第二参数是 组件
在某个组件内部注册的组件是局部组件
九、组件通信
父子通信:
父 – 子 传值 父组件通过动态属性绑定一个值 , 子组件通过props数组来接收参数变量。
子 – 父 传值 子组件调用事件,this.$emit触发父组件的自定义事件并传参
单项数据流: 父组件的数据向子组件流传 , 并且自动随之改变.
bus通信或事件总线通信
1, 创建一个全局的事件中心 一般建立一个js文件 ,
import Vue from 'vue'
export default new Vue()
2, 写自定义事件 , (A向B通信 , 要把自定义事件写在B上)
3, A组件触发B的自定义事件并且传参 (事件.$emit() )
1, 什么是vue生命周期?
vue生命周期分为创建 , 更新 , 销毁三个阶段
创建阶段有 beforeCreate created beforeMount mounted
更新阶段有 beforeUpdate updated
销毁阶段有 beforeDestroy destroyed
2, 发送ajax请求在mounted还是在created里面 , 为什么?
created代码会执行多次 , mounted只执行一次
3, vue的渲染过程?
vue只是一个框架 , 最终都要生成html页面 , 需要几步才能生成html页面
1, vue通过object.defineproperty把data数据变成set get 监听形式 (拿到data的数据 , 变成get set 监听形式) ,
2,vue通过render函数 , 传入数据 , 生输出vdom ,
3,vue通过patch函数 , 输入vdom , 直接渲染挂在dom上.
4, vue的更新过程?
1, vue触发set函数 , 触发更新流程 ,
2, vue通过调用render函数 , 传入数据 , 重新输出新的vdom
3, vue通过patch函数 输入的旧vdom 和新vdom 进行diff比较( 同行比较 , 少了创建 , 多了删除), 旧的vdom相对于新vdom中多的元素删除 , 少的元素创建 , 最后挂在渲染到真实的dom容器上
diff如何更新?
按vdom树的层级进行比较 , 标签不同的删除重建 , 标签相同的修改属性
5, 销毁自定义事件 , 计时器 , 自定义dom事件在哪个生命周期写?
beforeDestroy 这个生命周期写
6,组件框架中的data为什么是个函数?
为了组件被复用的时候都可以生成一个独立的数据拷贝 , 互相隔离不受影响.
7, v-for为什么要加key ,
为了提高性能 , 给标签一个主键.
在 删除其中某个元素时 , 其他元素可以直接复用. 大大提高性能
8, 为什么index不做key?
如果index 作为key的变量 , 数组下标永远从0开始 , 删除其中某个元素实际删除的是最后的元素 , 浪费性能
9, 单项数据流为啥是单项?
一个父组件对应多个子组件 , 父组件数据更新 , 多个子组件无条件更新 的就是 单向数据流
子组件更新 , 父组件更新不行吗?
子组件更新不能带动父组件更新 , 如果想修改父组件的数据 , 需要重新拷贝一份.
10, 父组件如何调用子组件中的函数?
ref通信 , (功能强大 , 不仅可以获取组件的实例 , 还可以获取标签节点 )
扩展:
-
v-if 控制节点创建和销毁 , v-show通过css方式控制元素显示或者隐藏 , 接口是渲染的
-
keep-alive 缓存组件 , 组件创建之后保存到内存中 , 不会销毁 , 是挺高性能的方法之一
- include=“ChildB” 是指将 ChildB 这个组件进行缓存
- exclude=“ChildB” 是排除ChildB这个组件, 剩下的组件都缓存
-
父子组件
- 子组件的创建阶段的渲染生命周期在父组件beforeMount之后 , mounted之前 ,
- 子组件的更新阶段在父组件beforeUpdate之后 , 在updated之前更新.
- 子组件的销毁阶段在父组件beforeDestroy之后 , 在destroy之前.
附加:
1、根目录下 npm init -y
2、全局安装 cnpm i -g browser-sync
3、package.json 添加自定义命令
“start”: “browser-sync start -s -f */ --directory --watch”
4、启动
http://localhost:3000/ 访问即可