1.Vue.js介绍
- Vue.js是渐进式JavaScript框架(由浅入深)
Vue.js优点
- 体积小:压缩后33k;
- 拥有更高的运行效率:基于虚拟DOM,一种可以先通过JavaScript进行各种计算,把最终的DOM操作并优化的技术,由于这个DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM。
- 数据双向绑定:让开发者不再操作DOM对象,把更多的精力投入到业务上。
- 生态丰富,学习成本低。
2.Vue.js入门
Vue.js引用
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
Vue.js基本代码结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue基本代码结构</title>
<style>
/* v-cloak 让vue渲染完成前显示空白 */
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<!-- 容器 (必须项)-->
<div id="app" v-cloak>{{nickname}}</div>
<!-- 引入vue (必须项) -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.config.devtools = false//解决默认警告
Vue.config.productionTip = false
// 新建vue实例
let vm = new Vue({
el: "#app",//容器(必须项)
created() {},//data初始化完成,el没有,在此可以进行ajax请求或数据处理
mounted() {},//el挂载完成(data和el都初始化完成了),可在此进行DOM相关操作
watch: {},//数据监听器,可以监听到data中变量的变化,可以做一些处理。
computed: {},//计算属性。可以简化表达式,方便使用(通过已有变量,得到一个新的变量)
data() {//放置数据
return {}
},
methods: {}//放置方法
})
</body>
</html>
3.Vue.js生命周期

Vue.js生命周期是什么?
Vue的生命周期通俗来讲就是我们用Vue写的网页在浏览器运行起来之后,我们写的代码要在内存里执行。例如我们都会的let vm = new Vue();就是new出来的一个Vue实例。这个实例从创建一直到我们关掉浏览器这个实例消亡,这段时间里,Vue这个框架干了啥,Vue的实例做了啥,先做啥,后做啥,这一系列的关系是怎样的,这就是Vue的生命周期。
Vue.js的生命周期分为三个阶段:创建阶段,运行阶段,销毁阶段
钩子函数
- beforeCreate: 创建实例前,在实例初始化之后,数据观测和事件、生命周期初始化配置之前被调用。
- created:(data已存在) 实例创建后,实例已经创建完成之后被调用。在这一步,实例已经完成以下配置:数据观测,属性和方法的运算,事件回调。然而挂载阶段还没开始,$el目前不可见。
- beforeMount: 实例挂载前,在挂载之前被调用:相关的render函数首次被调用,此时有了虚拟DOM。
- mounted:(el已存在) 实例挂载后,el被创建的vm.$el替换,并挂载到实例上去之后调用该钩子,渲染为真实的DOM.
- beforeUpDate: 在数据更新前调用,发生在虚拟DOM重新渲染和打补丁之前。在此进一步更改状态,不会重复渲染。
- upDate: 数据更新后,由于数据更改导致的虚拟DOM重新渲染和补丁,在这之后调用该钩子,应避免在此期间更改状态,因为可能会导致更新出现无限循环。
- beforeDestroy: 实例销毁前调用,此时实例仍然是可用的。
- destroyed: 实例销毁之后调用。调用后Vue实例指示的所有东西都会被解绑, 所有的事件监听会被卸载移除,所有的子实例也会被销毁。 注意:该钩子函数在服务端渲染期间不被调用。
4.Vue.js常用指令
v-model
用于数据双向绑定。v-model不仅可以给input赋值,还可以获取input中的数据,而且数据是实时的。(本质上是一个语法糖),目前只能给表单使用。 例如:
<input type="text" v-model="nickname">
可以在v-model后面添加修饰符:
<input type="text" v-model.trim="nickname"> //去空格
<input type="text" v-model.trim.number="num_a"> //去空格且为数字(Vue自动pasInter)
v-for
用于循环 例如:
<li v-for="(item,i) in list">{{item}}</li> //item为数组元素 i为下标 list为循环的数组
无需用到下标时
<li v-for="item in list">{{item}} </li>
v-for除了可以循环数组,也可以是数字
<li v-for="num in 10">{{num}}</li>
v-on
绑定事件,语法糖为@ 例如:
<button v-on:click="add"></button>
<button @click="add"></button>
v-cloak
Vue渲染完成之前有,渲染完成后消失。结合display:none,可以提高用户体验 例如:
<style>
/* v-cloak 让vue渲染完成前显示空白 */
[v-cloak] {
display: none;
}
</style>
<div id="app" v-cloak></div>
v-if
v-if是标签的删除和创建,标签的删除和创建需要耗内存,因此对于不需要频繁切换的场景,用v-if 比较好。
<div v-if="false"> 123 </div> //v-if为false时,此div不会被渲染
<div v-if="true"> 123 </div> //v-if为true时,此div会被渲染
可以给v-if绑定一个变量
<div v-if="isShow"> 123 </div> //当isShow为false时,不显示,为true时显示
v-show
v-show只是切换元素的display为none还是block,也就是切换它的显示还是隐藏,因此,对于需要频繁切换显示和隐藏时,用v-show合适,如:轮播图 v-show用法与v-if同理
小案例:点击按钮时,让isShow取反(在标签中,this可以省略)
<div v-if="isShow">
你好
</div>
<button @click="isShow=!isShow">切换</button>
<br>
<div v-show="isShow">
哈哈哈
</div>
<button @click="isShow=!isShow">切换</button>
<br>
v-bind
在标签内绑定属性,语法糖":"例如:
<a v-bind:href="'#detail?id='+item.id">跳转</a>
<a :href="'#detail?id='+item.id">跳转</a>
v-html
v-html可以渲染标签
<div>
<p v-html="str"></p> //可以把str中的字符串当作html标签渲染
</div>
data() {
return {
str: "<h1>你好</h1>"
}
5.Vue.js混入
混入(mixin)定义了一部分可复用的方法或者计算属性。混入对象可以包含任意组件选项。当组件使用汇入对象时,所有混入对象的选项将被混入该组件本身的选项。 mixin其实就相当于一个Vue的小型实例,里面也可以写data、methods等。下面我们结合mixin做一个axios的封装小案例。
- 第一步:新建文件request.js,代码如下
let req = axios.create({
baseURL: 'http://47.92.50.43:8888',
})
// 添加请求拦截器
req.interceptors.request.use(function (config) {
let loadingBar = document.getElementById('global-loading')
loadingBar.style.display = 'block'
let { method, url } = config
console.log(`${method}了${url}`)
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
req.interceptors.response.use(function (response) {
let loadingBar = document.getElementById('global-loading')
loadingBar.style.display = 'none'
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
- 第二步新建文件mixin.js,代码如下:
// 混入
Vue.mixin({
methods: {
$post(url, params) {
return req.post(url, params)
},
$get(url, params) {
return req.get(url, {
params
})
},
}
})
- 第三步使用封装好的axios请求数据(注意:使用时要先引入上面两个文件)
//调接口增删查数据
methods: {
//添加数据
async save() {
let { data } = await this.$post('/sys/savefl', {//get请求
name: this.name,
path: this.path
})
if (data.success) {
this.name = this.path = ''
// alert('添加成功')
this.getList()
}
},
//查询数据
async getList() {//get请求
let { data } = await this.$get('/sys/firendslink')
this.list = data
},
//删除数据
async del(i, id) {
if (confirm("确定删除?")) {//post请求
let { data } = await this.$post('/sys/removefl', { id })
if (data.success) {
// alert('删除成功')
this.getList()
}
}
//数组删除
// this.list = this.list.filter(r => r.id !== id)
//根据下标删除
//this.list.splice(i, 1)
}
}
6.Vue.js过滤器
- filter:将模板中的值处理完再返回
局部过滤器
// 写在vue实例内部
filters: {
fmtGender(val) {//可以过滤性别
return ['男', '女'][val]
}
全局过滤器
// 写在vue实例内部,也可以建一个vue_filter.js文件,把过滤器都写在里面,需要用到时引入即可
Vue.filter('fmtGender', function (val) {
return ['男', '女'][val]
}, )
7.Vue.js组件
局部组件
- 局部组件:写在vue实例内部,与el同级
components:{
star:{//star为自定义组件的名称,切记:自定义组件名称不可与html标签重复
template:`
//注意:组件一定要有根节点
<div>
<h1>我是局部组件</h1>
</div>
`
}
},
全局组件
- 全局组件:写在vue实例外部或放到外部文件中,需要用时引入即可
Vue.component('star', {//star为自定义组件的名称,切记:自定义组件名称不可与html标签重复
template: `
//注意:组件一定要有根节点
<div>
<h1>我是局部组件</h1>
</div>
`
})
父子组件传值
- 1.父组件向子组件传值props
- props:在组件中可以当data来用,vue组件通过props属性声明一个自己的属性,然后父组件就可以往子组件里面传递数据。
- 父组件传过来的值,子组件不可以修改(即props里的值不能修改),如果需要改props中的值,可以设置一个中转变量。
- 若绑定的属性是Number类型或变量,绑定时要在属性前加冒号。
<body>
<div id="app">
<zz></zz>
<!-- 传入的值为字符串时 属性名前不需要加冒号 -->
<zz nickname='Alice'></zz>
<!-- 把nickName绑定在v-model上,则在文本框输入文本时,对应的子组件中的值也会随之被改变 -->
<input type="text" v-model="nickName">
<!-- 传入的值为变量时 属性名前必须加冒号 -->
<zz :nickname='nickName'></zz>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
<script>
Vue.component('zz', {
// props可以为一个数组或者对象:
//若不需要限制属性类型,可以用数组 属性名要小写且不能为html标签
// props:['nickname'],
//若需要限制属性类型,则用对象
props: { //接收父组件传的值
nickname: {
type: String, //传入类型
default: "alice", //默认值
required: false //false表示此属性为非必须传入项 若为true 则为必须传入项
}
},
template: `
<div>{{nickname}}</div>
`
})
new Vue({
el: "#app",
data() {
return {
nickName: ""
}
}
})
</script>
</body>
- 2.子组件向父组件传值$emit
- 子组件需要响应一个$emit方法,在这里定义一个事件,然后在父组件里面进行监听这个事件,最后进行响应。
<body>
<div id="app">
<input type="text" v-model="nickName">
<!-- 外面的父组件会响应change事件 并将值赋给nickName -->
<zz :nickname='nickName' @change="nickName=$event"></zz>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
<script>
Vue.component('zz', {
props: ['nickname'],
data() {
return {
innerName: this.nickname //innerName中转变量
}
},
watch: {
nickname(val) { //一旦外面的值发生改变,就把外面的值传给中转变量
this.innerName = val
},
innerName(val) { //当里面的值发生改变时 会触发change事件 并把值传出去
this.$emit('change', val)
}
},
template: `
<div>{{innerName}}
<input type="text" v-model="innerName">
</div>
`
})
new Vue({
el: "#app",
data() {
return {
nickName: "alice"
}
}
})
</script>
</body>
组件中使用v-model
- 将value属性绑定到一个名叫value的props上,在其input事件被触发时,将新的值通过自定义的input事件抛出
- 示例:
<body>
<div id="app">
<input type="text" v-model="nickName">
<!-- v-bind:value="nickName" v-on:input="nickName=$event" =>语法糖 v-model="nickName" -->
<zz v-model="nickName"></zz>
<!-- <zz :value='nickName' @input="nickName=$event"></zz> -->
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
<script>
Vue.component('zz', {
props: ['value'],
data() {
return {
innerName: this.value //innerName中转变量
}
},
watch: {
value(val) { //一旦外面的值发生改变,就把外面的值传给中转变量
this.innerName = val
},
innerName(val) { //当里面的值发生改变时 会触发input事件 并把值传出去
this.$emit('input', val)
}
},
template: `
<div>{{innerName}}
<input type="text" v-model="innerName">
</div>
`
})
new Vue({
el: "#app",
data() {
return {
nickName: "alice"
}
}
})
</script>
</body>
修饰符sync
- v-model与sync:
- 如果外面需要拿到里面的值做后续操作,用v-model(一般用1个)
- 如果只是为了拿到值来显示、隐藏,则用sync就可以了(可以写多个)
- 一般v-model用的多一些,如果项目比较复杂,可以结合一起用
<body>
<div id="app">
<input type="text" v-model="nickName">
<!-- 属性名.sync是vue提供的固定写法 -->
<zz :nickname.sync="nickName"></zz>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
<script>
Vue.component('zz', {
props: ['nickname'],
data() {
return {
innerName: this.nickname //innerName中转变量
}
},
watch: {
nickname(val) { //一旦外面的值发生改变,就把外面的值传给中转变量
this.innerName = val
},
innerName(val) {
this.$emit('update:nickname', val) //'update:属性名是vue提供的固定写法'
}
},
template: `
<div>{{innerName}}
<input type="text" v-model="innerName">
</div>
`
})
new Vue({
el: "#app",
data() {
return {
nickName: "alice"
}
}
})
</script>
</body>
组件嵌套
- 父组件获取子组件中的值:this.$children(在mounted)
- 子组件获取父级值:this.$parent
- 注:普通标签不算子级、父级,若页面结构发生改动,用this.
parent就不太合适,因此用的比较少(除非是不需要改动的结构才用这种方法)
星星评分小案例
- 利用父子组件传值实现
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>星星评分</title>
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/4.2.0/css/font-awesome.css" rel="stylesheet">
<style>
.star {
color: gold;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<!-- $event:是vue内部的一个对象,表示组件里面自定义事件触发的那个值 -->
<star label="客服态度" :score="score" @change="score = $event"></star>
<!-- <star label="物流速度"></star>
<star label="商品质量" :count="count"></star> -->
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
<script>
Vue.component('star', {
props: { //接收父组件传的值
count: {
type: Number,
default: 5 //默认值
},
score: {
type: Number,
default: 0
},
label: {
type: String,
required: true //必须传
}
},
template: `
<div>
{{label}}--{{innerScore}}
<i v-for="order in count" @click="innerScore=order" @mouseleave="score1 = innerScore"
@mouseenter="score1=order"
:class="order<=score1?'fa-star':'fa-star-o'" class="fa star" aria-hidden="true">
</i>
</div>
`,
watch: {
innerScore(val) {
//手动触发一个叫change的事件,事件对象e值为val
this.$emit('change', val)
}
},
data() {
return {
innerScore: this.score, //中转变量
score1: this.score //临时变量
}
},
})
let app = new Vue({
el: "#app",
data() {
return {
score1: 2,
score: 2,
count: 10
}
}
})
</script>
</body>
8.Vue.js插槽
默认插槽
- 组件
Vue.component('cc', {
template:`
<div>
<slot></slot>
</div>
`
})
- 使用
<cc>
默认插槽
</cc>
具名插槽
- 组件
Vue.component('cc', {
template: `
<div>//给插槽起名字
<slot name="login"></slot>
</div>
`
})
- 使用:#与v-slot:作用相同
<cc #login>登录</cc>
<!-- <cc v-slot:login>登录</cc> -->
作用域插槽
- 作用域插槽:带有数据的插槽。
- 如果希望调用者自行控制组件里面的样式,并且拿到组件内部的数据,就可以使用作用域插槽的方式,先从组件内部把数据传出来,再在外面接收处理。
- 示例:
- html
<cc :list='list'>
<!-- 给插槽绑定一个变量,用<template v-slot="scope"></template>获取插槽内的数据,scope为自定义的名称,里面包含了绑定的所有内容。 -->
<template v-slot="scope">{{scope.item.name}}</template>
<!-- 解构方式 -->
<!-- <template v-slot="{item}">{{item.name}}</template> -->
</cc>
- js
Vue.component('cc', {
props:['list'],
//从组件内部把数据传出来 <slot :item="item"></slot>
template: `
<div>
<ul>
<li v-for="item in list">
<slot :item="item">{{item.name}}</slot>
</li>
</ul>
</div>
`
})
new Vue({
el: "#app",
data() {
return {
list:[
{ id:1, name:"zs"},
{ id:2, name:"ls"},
{ id:3, name:"ww"},
]
}
}
})
9.Vue.js自定义指令
除了默认设置的核心指令( v-model 和 v-show ), Vue 也允许注册自定义指令。
下面我们注册一个全局指令 v-focus, 该指令的功能是在页面加载时,元素获得焦点:
<div id="app">
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
</div>
<script>
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中。
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
// 创建根实例
new Vue({
el: '#app'
})
</script>
我们也可以在实例使用 directives 选项来注册局部指令,这样指令只能在这个实例中使用:
<div id="app">
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
</div>
<script>
// 创建根实例
new Vue({
el: '#app',
directives: {
// 注册一个局部的自定义指令 v-focus
focus: {
// 指令的定义
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
}
})
</script>
有时候我们不需要其他钩子函数,我们可以简写函数,如下格式:
Vue.directive('runoob', function (el, binding) {
// 设置指令的背景颜色
el.style.backgroundColor = binding.value.color
})
10.Vue.js自定义插件
Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或 property
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
10.Vue.js特殊 attribute key
key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
最常见的用例是结合 v-for:
<ul>
<!-- 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key attribute: -->
<li v-for="item in items" :key="item.id">...</li>
</ul>
它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有: 完整地触发组件的生命周期钩子 触发过渡 例如:
<transition>
<span :key="text">{{ text }}</span>
</transition>
当 text 发生改变时, 总是会被替换而不是被修改,因此会触发过渡。
10.Vue.js路由
Vue.js 路由允许我们通过不同的 URL 访问不同的内容。
通过 Vue.js 可以实现多视图的单页Web应用(single page web application,SPA)。
Vue.js 路由需要载入 vue-router 库
中文文档地址:vue-router文档。
安装
1.直接下载 / CDN
https://unpkg.com/vue-router/dist/vue-router.js
2.NPM 推荐使用淘宝镜像:
cnpm install vue-router
简单实例
Vue.js + vue-router 可以很简单的实现单页应用。
是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。 to 属性为目标地址, 即要显示的内容。
以下实例中我们将 vue-router 加进来,然后配置组件和路由映射,再告诉 vue-router 在哪里渲染它们。代码如下所示:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
tag
有时候想要 渲染成某种标签,例如
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>
路由配置
let router = new VueRouter({
routes: [{
path: "/",
component: Layout,
children:[
{
path: "",
meta: {
title: "首页"
},
component: Home
},
{
path: "foo/:id",
meta: {
title: "学生管理"
},
props: true,
component: Foo
},
{
path: "bar",
meta: {
title: "教师管理"
},
component: Bar
}
]
},
{
path: "/login",
meta: {
title: "登录"
},
component: Login
},
{
path: "*",
meta: {
title: "页面未找到"
},
component: Err404
},
]
})