vue


1. 库和框架的区别

库: 本质上是一些函数的集合。每次调用函数,实现一个特定的功能,接着把控制权交给使用者(程序猿)

  • 代表:jQuery axios
  • jQuery这个库的核心:DOM操作+对ajax的封装,即:封装DOM操作,简化DOM操作
  • axios: 封装了对ajax的操作

框架: 是一套完整的解决方案,使用框架的时候,需要把你的代码放到框架合适的地方,框架会在合适的时机调用你的代码

  • 框架规定了自己的编程方式,是一套完整的解决方案
  • 使用框架的时候,由框架控制一切,我们只需要按照规则写代码

库和框架的区别

You call Library, Framework calls you

核心点:谁起到主导作用(控制反转)

  • 控制整个流程的是框架

  • 使用库,由开发人员决定如何调用库中提供的方法(辅助)

  • 框架的侵入性很高(从头到尾)


2. 目前主流的前端框架

  • angular(前两三年非常火)

  • react(目前特别火的框架)—>国外使用较多

  • Vue(中国人自己写的框架 尤雨溪) https://cn.vuejs.org/

Vue的版本:

2.x: 使用较多的版本

3.x: 还不太稳定的版本


3. MVVM的介绍

image-20211220192211484

4. MVC与MVVM

4.1 mvc

  • M: Model 数据模型(专门用来操作数据,数据的CRUD)
  • V:View 视图(对于前端来说,就是页面)
  • C:Controller 控制器(是视图和数据模型沟通的桥梁,用于处理业务逻辑)

4.2 mvvm

  • MVVM ===> M / V / VM
  • M:model数据模型
  • V:view视图
  • VM:ViewModel 视图模型

4.3 优势对比

MVC模式,将应用程序划分为三大部分,实现了职责分离

在前端中经常要通过 JS代码 来进行一些逻辑操作,最终还要把这些逻辑操作的结果现在页面中。也就是需要频繁的操作DOM

MVVM通过数据双向绑定

  • V(修改数据) —vm–> M
  • M(修改数据)—vm–> V
  • 数据是核心

Vue这种MVVM模式的框架,不推荐开发人员手动操作DOM


5 Vue中的MVVM

虽然没有完全遵循 MVVM 模型,Vue 的设计无疑受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的简称) 这个变量名表示 Vue 实例

学习Vue要转化思想

不要在想着怎么操作DOM,而是想着如何操作数据!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">{{ msg }}</div>

<!-- 引入 vue.js -->
<script src="vue.js"></script>

<!-- 使用 vue -->
<script>
var vm = new Vue({
// el:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标
el: '#app',
// Vue 实例的数据对象,用于给 View 提供数据
data: {
msg: 'Hello Vue'
}
})
</script>

6. Vue实例

  • 注意 1:先在data中声明数据,再使用数据
  • 注意 2:可以通过 vm.$data 访问到data中的所有属性,或者 vm.msg
1
2
3
4
5
6
7
8
9
<script>
var vm = new Vue({
data: {
msg: '大家好,...'
}
})

vm.$data.msg === vm.msg // true
</script>

7. 数据绑定

最常用的方式:Mustache(插值语法),也就是 {{}} 语法

​ 解释:{{}}从数据对象data中获取数据

​ 说明:数据对象的属性值发生了改变,插值处的内容都会更新

​ 说明:{{}}中只能出现JavaScript表达式(运算元+运算符) ,而不能解析js语句

​ 注意:Mustache 语法不能作用在 HTML 元素的属性上

1
2
3
4
5
6
7
8
<script>
<h1>Hello, {{ msg }}.</h1>
<p>{{ 1 + 2 }}</p>
<p>{{ isOk ? 'yes': 'no' }}</p>

<!-- !!!错误示范!!! -->
<h1 title="{{ err }}"></h1>
</script>

8. 双向数据绑定

  • 双向数据绑定:将DOM与Vue实例的data数据绑定到一起,彼此之间相互影响

    • 数据的改变会引起DOM的改变
    • DOM的改变也会引起数据的变化
  • 原理:Object.defineProperty``get``set

    • gettersetter:访问器
    • 作用:指定读取或设置对象属性值的时候,执行的操作
  • 参考地址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <script>
    /* defineProperty语法 介绍 */
    var obj = {}
    Object.defineProperty(obj, 'msg', {
    // 设置 obj.msg = "1" 时set方法会被系统调用 参数分别是设置后和设置前的值
    set: function (newVal, oldVal) { },
    // 读取 obj.msg 时get方法会被系统调用
    get: function ( newVal, oldVal ) {}
    })
    </script>

9. 自定义模拟数据绑定(数据代理)

1
2
3
4
5
6
7
8
9
10
11
12
const obj = {}
obj.name = "admin"
Object.defineProperty(obj, "age", {
set(newVal) {
console.log("age被改变了")
},
get() {
return 20
}
})

console.log(obj);

10. 动态添加数据的注意点

  • 注意:只有data中的数据才是响应式的,动态添加进来的数据默认为非响应式

    可以通过以下方式实现动态添加数据的响应式

    1
    2
    Vue.set(object, key, value) - 适用于添加单个属性
    Object.assign() - 适用于添加多个属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <script>
    var vm = new Vue({
    data: {
    stu: {
    name: 'jack',
    age: 19
    }
    }
    })

    /* Vue.set */
    Vue.set(vm.stu, 'gender', 'male')

    /* Object.assign 将参数中的所有对象属性和值 合并到第一个参数 并返回合并后的对象*/
    vm.stu = Object.assign({}, vm.stu, { gender: 'female', height: 180 })
    </script>

11. 异步DOM更新

  • 说明:Vue 异步执行 DOM 更新,监视所有数据改变,一次性更新DOM

  • 优势:可以去除重复数据,对于避免不必要的计算和 避免重复 DOM 操作上,非常重要

  • 如果需要拿到更新后dom中的数据 则需要通过 Vue.nextTick(callback)

    • 实例调用vm.$nextTick(function () {})
1
2
3
4
5
6
7
8
9
10
11
<script>
methods: {
fn() {
this.msg = 'change'
this.$nextTick(function () {
console.log('$nextTick中打印:', this.$el.children[0].innerText);
})
console.log('直接打印:', this.$el.children[0].innerText);
}
}
</script>

11. vue指令

  • 解释:指令 (Directives) 是带有 v- 前缀的特殊属性

  • 作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

11.1 v-text

  • 解释:更新DOM对象的 textContent

    1
    <h1 v-text="msg"></h1>

11.2 v-html

  • 解释:更新DOM对象的 innerHTML
1
<h1 v-html="msg"></h1>

11.3 v-bind

  • 作用:当表达式的值改变时,将其产生的连带影响,作用于 DOM的属性
  • 语法:v-bind:title="msg"
  • 简写::title="msg"
1
2
3
4
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>

11.4 v-model

  • 作用:在表单元素上创建双向数据绑定
  • 说明:监听用户的输入事件以更新数据
1
2
3
4
<script>
<input type="text" v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
</script>

v-model修饰符

  • number: 把绑定的值转换为 数值类型;
  • trim: 把绑定的值去掉两边的空格;
  • lazy: 为了优化效率问题,可以实现 懒绑定,非实时的绑定(当焦点失去的时候,才把视图中的数据绑定在model上)

11.5 v-on

  • 作用:绑定事件

  • 语法:

    1
    2
    v-on:click="say"  #直接不传递参数的绑定,默认或传递一个event参数
    v-on:click="say('参数', $event)" #如果传递了参数,值不会自动传递event参数,如果需要则要手动传递 $event
  • 简写:@click="say"

  • 说明:绑定的事件定义在methods

    1
    2
    3
    4
    <!-- 完整语法 -->
    <a v-on:click="doSomething"></a>
    <!-- 缩写 -->
    <a @click="doSomething"></a>

事件修饰符

.stop 阻止冒泡,调用 event.stopPropagation()

.prevent 阻止默认行为,调用 event.preventDefault()

.capture 添加事件侦听器时使用事件捕获模式

.self 只当事件在该元素本身(比如不是子元素)触发时,才会触发事件

.once 事件只触发一次

11.6 v-for

  • 作用:基于源数据多次渲染元素或模板块
1
2
3
4
5
6
7
8
9
10
<!-- 1 基础用法 -->
<div v-for="item in items">
{{ item.text }}
</div>

<!-- item 为当前项,index 为索引 -->
<p v-for="(item, index) in list">{{item}} -- {{index}}</p>
<!-- item 为值,key 为键,index 为索引 -->
<p v-for="(item, key, index) in obj">{{item}} -- {{key}}</p>
<p v-for="item in 10">{{item}}</p>

key属性

  • 推荐:使用 v-for 的时候提供 key 属性,以获得性能提升。

  • 说明:使用 key,VUE会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

    1
    2
    3
    <div v-for="item in items" :key="item.id">
    <!-- 内容 -->
    </div>

11.7 v-if 和 v-show

  • 条件渲染
  • v-if:根据表达式的值的真假条件,销毁或重建元素
  • v-show:根据表达式的真假值,切换元素的 display CSS 属性
1
2
<p v-show="isShow">这个元素展示出来了吗???</p>
<p v-if="isShow">这个元素,在HTML结构中吗???</p>

11.8 v-pre

  • 说明:vue会跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
1
<span v-pre>{{ this will not be compiled }}</span>

11.9 v-once

  • 说明:vue只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
1
<span v-once>This will never change: {{msg}}</span>

12. 样式处理 class和style

12.1 style的处理

1
2
3
4
5
6
7
8
9
10
11
<div id="root">
<h1 style="color: red" :style="bgColor">java大数据开发</h1>
</div>
<script>
new Vue({
el: "#root",
data: {
bgColor: {backgroundColor: "green", fontSize:"40px"}
}
});
</script>

12.2 class的处理

普通的class绑定(推荐使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
.bgColor {
background-color: red;
}
</style>

<div id="app">
<h1 :class="bg">{{name}}</h1>
</div>

<script>
var vm = new Vue({
el: "#app",
data: {
name: "丽丽",
bg: 'bgColor'
},
});
</script>

绑定多个class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <style>
.redBorder {
border: 4px solid red;
}

.pinkBg {
background-color: pink;
}
</style>
</head>
<body>

<div id="root">
<h1 :class="['redBorder','pinkBg']">java大数据开发</h1>
</div>

通过判断来进行class的绑定(推荐使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<style>
.redBorder {
border: 4px solid red;
}

.pinkBg {
background-color: pink;
}
</style>
</head>
<body>

<div id="root">
<h1 :class="{'redBorder':isBd,'pinkBg':isBg}">java大数据开发</h1>
</div>

<script>
new Vue({
el: "#root",
data: {
isBg: true,
isBd: true
}
});
</script>

注意:无论是绑定style还是绑定class,都在原始的style或者class的基础上进行的样式叠加,并不是替换原始的样式;


13. 过滤器

  • 作用:文本数据格式化
  • 过滤器可以用在两个地方:{{}}和 v-bind 表达式
  • 两种过滤器:1 全局过滤器 2 局部过滤器

13.1 全局过滤器

  • 说明:通过全局方式创建的过滤器,在任何一个vue实例中都可以使用
  • 注意:使用全局过滤器的时候,需要先创建全局过滤器,再创建Vue实例
  • 显示的内容,是由过滤器的返回值决定
  • 使用语法: <h1>{{name | myfilter1}}</h1>
1
2
3
Vue.filter('filterName', function (value) {
// value 表示要过滤的内容
})

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<h1>{{name | myfilter1}}</h1>
</div>

<script>
Vue.filter("myfilter1", function (val) {
return val + "hello";
});
var vm = new Vue({
el: "#app",
data: {
name: "丽丽",
},
});
</script>

13.2 局部过滤器

  • 说明:局部过滤器是在某一个vue实例的内容创建的,只在当前实例中起作用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="app">
<h1>{{name | myfilter2}}</h1>
</div>

<script>
var vm = new Vue({
el: "#app",
data: {
name: "丽丽",
},
filters: {
myfilter2(val) {
return "miss" + val;
}
}
});
</script>

14. 按键值修饰符

  • 说明:在监听键盘事件时,Vue 允许为 v-on 在监听键盘事件时添加关键修饰符
  • 键盘事件 - 键值修饰符
  • 其他:修饰键(.ctrl等)、鼠标按键修饰符(.left等)
1
2
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

15. 监听器watch

  • 概述:watch是一个对象,键是需要观察的表达式,值是对应回调函数
  • 作用:当表达式的值发生变化后,会调用对应的回调函数完成响应的监视操作

监听普通属性的改变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
new Vue({
el: "#root",
data: {
firstName: "张",
secondName: "三",
fullName: ""
},
methods: {},
watch: {
//监听器的简写形式
firstName(newVal,oldVal) {
console.log(newVal)
this.fullName = newVal + this.secondName
},
secondName(newVal,oldVal) {
this.fullName = this.firstName + newVal
}
}
});

配置初始化监视:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
new Vue({
el: "#root",
data: {
firstName: "张",
secondName: "三",
fullName: ""
},
methods: {},
watch: {
//非简写形式
firstName: {
immediate: true,//立刻监视(初始化监视)
handler(newVal, oldVal) {
this.fullName = newVal + this.secondName
}
},
secondName: {
handler(newVal, oldVal) {
this.fullName = this.firstName + newVal
}
},

}
});

监视数组的改变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
ages: [10, 20, 30]
},
methods: {
update() {
// this.ages[1] = 800; 此种方式不能监听到数组元素的改变
this.ages.splice(1, 1, 800)
},
push() {
this.ages.push(200)
},
unshift() {
this.ages.unshift(100)
},
shift() {
this.ages.shift()
},
pop() {
this.ages.pop()
}
},
watch: {
ages: function (newVal) {
console.log("监听到数组的改变:", newVal)
}
}
});

监听对象的改变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
new Vue({
el: "#root",
data: {
stu: {
name: "小明",
age: 20
}
},
methods: {
//直接改变对象的内存地址,可以监听到
change1() {
this.stu = {
name: "小花"
};
},

//改变对象的属性(要配置deep:true,才可以监听到)
change2() {
this.stu.name = "花花";
}
},
watch: {
stu: {
deep: true,//默认为false,要想实现深度监听则需要配置为true
handler(newVal) {
console.log("监听到对象的改变:", newVal)
}
}
},
});
# 16. 计算属性 - 说明:计算属性是基于它们的依赖进行`缓存`的,只有在它的依赖发生改变时才会重新求值 - 注意:Mustache语法({{}})中不要放入太多的逻辑,否则会让模板过重、难以理解和维护 - 注意:**computed中的属性不能与data中的属性同名,否则会报错** - 计算属性的值是通过其他属性计算出来的,不能直接赋值
1
2
3
4
5
6
7
8
9
10
11
12
var vm = new Vue({
el: '#app',
data: {
firstname: 'jack',
lastname: 'rose'
},
computed: {
fullname() {
return this.firstname + '.' + this.lastname
}
}
})

17. 实例(组件)生命周期

  • 所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象即可 (一些根实例特有的选项除外)。
  • 实例生命周期也叫做:组件生命周期

17.1 生命周期介绍

  • 一个组件从开始到最后消亡所经历的各种状态,就是一个组件的生命周期
  • 生命周期钩子函数的定义:从组件被创建,到组件挂载到页面上运行,再到页面关闭组件被卸载,这三个阶段总是伴随着组件各种各样的事件,这些事件,统称为组件的生命周期函数!
  • 注意:Vue在执行过程中会自动调用生命周期钩子函数,我们只需要提供这些钩子函数即可
  • 注意:钩子函数的名称都是Vue中规定好的

17.2 常用的钩子函数

17.2.1 钩子函数 - beforeCreate()

  • 说明:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
  • 注意:此时,无法获取 data中的数据、methods中的方法
1
2
3
4
5
6
7
8
9
10
11
var vue = new Vue({
el: "#app",
data: {
myname: "java",
user: {name: "admin", age: 20}
},
//生命周期函数
beforeCreate: function () {
console.log("实例创建之前调用...");
}
})

17.2.2 钩子函数 - created()

  • 注意:这是一个常用的生命周期,可以调用methods中的方法、改变data中的数据
  • 使用场景:发送请求获取数据

17.2.3 钩子函数 - beforeMount()

  • 说明:在挂载开始之前被调用

17.2.4 钩子函数 - mounted()

  • 说明:此时,vue实例已经挂载到页面中,可以获取到el中的DOM元素,进行DOM操作

17.2.5 钩子函数 - beforeUpdate()

  • 说明:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
  • 注意:此处获取的数据是更新后的数据,但是获取页面中的DOM元素是更新之前的

17.2.6 钩子函数 - updated()

  • 说明:组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。

17.2.7 钩子函数 - beforeDestroy()

  • 说明:实例销毁之前调用。在这一步,实例仍然完全可用。
  • 使用场景:实例销毁之前,执行清理任务,比如:清除定时器等

17.2.8 钩子函数 - destroyed()

  • 说明:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

18 js中的promise

18.1 promise是什么

  • 主要用于异步计算
  • 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
  • 可以在对象之间传递和操作promise,帮助我们处理队列

18.2 异步回调的问题

  • 之前处理异步是通过纯粹的回调函数的形式进行处理

  • 很容易进入到回调地狱中,剥夺了函数return的能力(异步调用如果return很有可能返回值为undefine)

  • 问题可以解决,但是难以读懂,维护困难

  • 稍有不慎就会踏入回调地狱 - 嵌套层次深,不好维护

  • 一般情况我们一次性调用API就可以完成请求。有些情况需要多次调用服务器API,就会形成一个链式调用,比如为了完成一个功能,我们需要调用API1、API2、API3,依次按照顺序进行调用,这个时候就会出现回调地狱的问题

18.3 promise详解

  • promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)

  • 并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据

  • 代码风格,容易理解,便于维护

  • 多个异步等待合并便于解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const a = 110;
new Promise((resolve, reject) => {
setTimeout(function () {
if (a == 10) {
resolve("通过")
} else {
reject("拒绝")
}
console.log("函数体执行了")
}, 3000);
}).then(resp => {
console.log(resp)
}).catch(resp => {
console.log(resp)
});

19. 网络请求axios

Promise based HTTP client for the browser and node.js

  • 以Promise为基础的HTTP客户端,适用于:浏览器和node.js

  • 封装ajax,用来发送请求,异步获取数据

  • 安装:npm i -S axios

  • cdn引入(如果项目简单就没必要使用脚手架)

    1
    2
    3
    4
    <!--axios vue中除了vue-resource官方的库的另外一个优秀的库-->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <!--qs(用来对象和请求字符串相互转换的库)-->
    <script src="https://cdn.bootcss.com/qs/6.5.1/qs.min.js"></script>
    1
    2
    3
    4
    5
    6
    7
    8
    //正在热映的电影
    axios.get("https://douban.uieee.com/v2/movie/in_theaters")
    .then((resp) => {
    console.log(resp.data);
    })
    .catch(err => {
    console.log(err);
    })

19.1 发送get请求

完整写法:

1
2
3
4
5
6
7
8
9
10
11
this.$myaxios({
url:"/stu/findById?id=1",
method:"GET",
headers:{
"MyToken":"admin"
}
}).then(resp => {
console.log(resp.data)
}).catch(error => {
console.log("出错了...." + error)
});

简写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// url中带有query参数
axios.get('/user?id=89')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

// url和参数分离,使用对象
axios.get('/user', {
params: {
id: 12345
}
})

19.2 发送post请求

完整写法:

1
2
3
4
5
6
7
8
this.$myaxios({
url: "/stu/findById",
method: "POST",
data: "id=1" //只能是query参数的格式(请求实体)
}
).then(resp => {
console.log(resp.data)
});

简写

  • 默认情况下,axios 会将JS对象序列化为JSON对象。为了使用 application/x-www-form-urlencoded 格式发送请求,我们可以这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
qs.stringify({ 'bar': 123 }) ===> "bar=123"
axios.post('/foo', qs.stringify({ 'bar': 123 }))

// 或者:
axios.post('/foo', 'bar=123&age=19')
const url = 'http://vue.studyit.io/api/postcomment/17'
axios.post(url, 'content=点个赞不过份')

axios.post('/user', qs.stringify({
firstName: 'Fred',
lastName: 'Flintstone'
}))
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

19.3. 创建自定义的axios实例

默认使用axios实例,是由axios库提供的,我们可以基于axios库提供的实例进行增强,然后再使用;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
new Vue({
el: "#root",
data: {},
methods: {
sendGet() {
this.$myaxios.get("/stu/findById", {
params: {
id: 1
}
}).then(resp => {
console.log(resp.data)
});
},
sendPost() {
this.$myaxios.post("/stu/findById", "id=1"
).then(resp => {
console.log(resp.data)
});
}
},
mounted() {
//创建一个自定义的axios实例,并且挂载到vm实例上
Vue.prototype.$myaxios = axios.create({
baseURL: "http://localhost:8080",
timeout: 3000
});
}
});

19.4 axios拦截器

  • 拦截器会拦截发送的每一个请求,请求发送之前执行request中的函数,请求发送完成之后执行response中的函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 请求拦截器
    axios.interceptors.request.use(function (config) {
    // 所有请求之前都要执行的操作

    return config;
    }, function (error) {
    // 错误处理
    return Promise.reject(error);
    });

    // 响应拦截器
    axios.interceptors.response.use(function (response) {
    // 所有请求完成后都要执行的操作

    return response;
    }, function (error) {
    // 错误处理
    return Promise.reject(error);
    });

20. Vue自定义指令

20.1 全局自定义指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 第一个参数:指令名称
// 第二个参数:配置对象,指定指令的钩子函数
Vue.directive('directiveName', {
// bind中只能对元素自身进行DOM操作,而无法对父级元素操作
// 只调用一次 指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
bind( el,binding, vnode ) {
// 参数详解
// el:指令所绑定的元素,可以用来直接操作 DOM 。
// binding:一个对象,包含以下属性:
// name:指令名,不包括 v- 前缀。
// value:指令的绑定值,等号后面的值 。
// oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
// expression:字符串形式的指令表达式 等号后面的字符串 形式
// arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
// modifiers:指令修饰符。例如:v-directive.foo.bar中,修饰符对象为 { foo: true, bar: true }。
// vnode:Vue 编译生成的虚拟节点。。
// oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
},
// inserted这个钩子函数调用的时候,当前元素已经插入页面中了,也就是说可以获取到父级节点了
inserted ( el,binding, vnode ) {},
// DOM重新渲染前
update(el,binding, vnode,oldVnode) {},
// DOM重新渲染后
componentUpdated ( el,binding, vnode,oldVnode ) {},
// 只调用一次,指令与元素解绑时调用
unbind ( el ) {
// 指令所在的元素在页面中消失,触发
}
})
// 简写 如果你想在 bind 和 update 时触发相同行为,而不关心其它的钩子:
Vue.directive('自定义指令名', function( el, binding ) {})
// 例:
Vue.directive('color', function(el, binding) {
el.style.color = binging.value
})
// 使用 注意直接些会被i成data中的数据“red” 需要字符串则嵌套引号"'red'"
<p v-color="'red'"></p>

20.2 局部自定义指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div id="app">
<button @click="downData" v-color="'red'">点我下载数据</button>
</div>

<script>
var vm = new Vue({
el: "#app",
methods: {
downData: function () {
//正在热映的电影
axios.get("https://douban.uieee.com/v2/movie/in_theaters", {params: {'id': 1}})
.then((resp) => {
console.log(resp.data);
})
.catch(err => {
console.log(err);
})
;
}
},
directives: {
color: function (el, binding) {
el.style.color = binding.value;
}
}
});

</script>

21. Vue组件

组件: 完成局部特定功能的代码和资源的组合;

创建组件的两种方式:1 全局组件 2 局部组件

  • 定义组件时,组件名称不能含有特殊字符,- 和 _也不能有
  • 如果定义组件时,组件名称复杂建议使用大驼峰命名法

定义组件时如果使用的是驼峰命名法,那么使用时就要使用 - 分隔


21.1 全局组件

  • 说明:全局组件在所有的vue实例中都可以使用(全局定义,全局有效)
  • 注意:组件先得注册之后才能使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<div id="root">
<!--使用组件 -->
<common1></common1>
<h2>你好</h2>
</div>


<script>
//定义组件
const common111 = Vue.extend({
template: `<h1>{{msg}}</h1>`,
data(){
return {
msg:"组件一"
}
}
})
//组件可以简写为
// const common111 = {
// template: `<h1>我是一个全局组件</h1>`
// }

//注册全局组件
Vue.component("common1", common111)
new Vue({
el: "#root",
data: {},
methods: {}
});
</script>

21.2 局部组件

  • 说明:局部组件,局部定义,局部有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<div id="root">
<!--使用组件 -->
<common1></common1>
<common2></common2>
<h2>你好</h2>
</div>

<script>

//定义组件
const common1 = Vue.extend({
template: `<h1>{{msg}}</h1>`,
data() {
return {
msg: "组件一"
}
}
})

const common2 = Vue.extend({
template: `<h1>{{msg}}</h1>`,
data() {
return {
msg: "组件二"
}
}
})

new Vue({
el: "#root",
data: {},
methods: {},
components: {
common1,
common2
}
});
</script>

22. vue-cli脚手架的使用

  • 安装nodejs
  • 安装npm
1
2
node -v
npm -v
  • 配置淘宝镜像
1
2
3
4
5
#查看当前镜像地址
npm config get registry #默认为: https://registry.npmjs.org/

#配置淘宝镜像服务器
npm config set registry https://registry.npm.taobao.org
  • 安装vue-cli脚手架
1
2
npm install @vue/cli -g
vue -V #查看脚手架的版本 4.5.15
  • 使用脚手架创建vue商业项目
1
2
3
4
#创建项目
vue create pro01
#在项目的根目录(package.json所在目录)运行项目
npm run serve

23. 关闭eslint的语法检查

  • 在项目的根目录创建vue项目的配置文件 vue-config.js

    1
    2
    3
    4
    module.exports = {
    //关闭vue-cli中eslint的语法检查
    lintOnSave: false
    }

24. vue-cli添加对scss的支持

  • 安装scss的loader加载器

    1
    2
    #查看npm远程仓库中的全部版本号
    npm show sass-loader versions
    1
    2
    npm install sass-loader@7.3.1 --save-dev | -D
    npm install node-sass@4.14.1 --save-dev | -D
  • 安装完成之后就可以使用scss的预编译器了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <style scoped lang="scss">
    #TodoHeader {
    border: 2px solid red;
    .box {
    h1{
    color: gray;
    }
    }
    }
    </style>

22. Vue的组件通信

22.1 父组件到子组件通信

  • 方式:通过子组件props属性来传递数据 props是一个数组

  • 注意:属性的值必须在组件中通过props属性显示指定,否则,不会生效

  • 说明:传递过来的props属性的用法与data属性的用法相同

  • 步骤:

    1. 父组件确定传字面量 my-sex="男" 还是父组件中的data属性:my-age="age"
    2. 在子组件中可以声明要接受的数据名称 props: ["mySex", "myAge"]
    3. 子组件的模板中即可使用父组件传递的数据了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template id="tp1">
<div>
<h2>my name is {{mySex}} {{myAge}}</h2>
</div>
</template>

<div id="app">
<!-- 如果传递的是字面量 那么直接写-->
<child-copmon1 my-sex="男"></child-copmon1>
<!-- 如果需要往子组件总传递父组件data中的数据 需要加v-bind="数据名称" -->
<child-copmon1 :my-age="age"></child-copmon1>
</div>

<script>
//vue实例其实也是一个组件,就相当于父组件
var vm = new Vue(
{
el: "#app",
data: {name: "admin", age: 20},
components: {
"childCopmon1": {
template: "#tp1",
data() {
return {name: "admin123"}
},
//一定要在子组件声明传递的值的名称
props: ["mySex", "myAge"]
}
}
}
);
</script>

22.2 子组件到父组件通信

  • 父组件给子组件传递一个函数,由子组件调用这个函数

  • 步骤:

    1. 在父组件中定义方法 parentFn

    2. 在子组件 组件引入标签 中绑定自定义事件 v-on:自定义事件名=”父组件中的方法” ==> @pfn=”parentFn”

    3. 子组件中通过$emit()触发自定义事件事件 this.$emit(pfn,参数列表。。。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<template id="tp1">
<div>
<button @click="doClick">点我给父组件传值</button>
</div>
</template>

<div id="app">
<!-- 2. 在子组件的标签上绑定自定义事件-->
<compon1 @pfn="parentFun"></compon1>
</div>

<script>
var vm = new Vue({
el: "#app",
methods: {
//1.在父组件声明方法(函数),指定形参接收数据
parentFun: function (data) {
console.log("从子组件传递过来的值:" + data);
}
},
components: {
compon1: {
template: "#tp1",
methods: {
doClick() {
//3. 在子组件中触发事件,传递数据
this.$emit("pfn", "hello it")
}
}
}
}
});

</script>

22.3 任意组件间的通信

  • 全局事件总线
  • 发布订阅(不常用)
  • Vuex(状态管理)

23. Vue中的插槽slot

插槽的作用: 让父组件可以向子组件的指定位置插入html结构,其实也是变相的实现了组件通信 父-->子

插槽的分类: 默认插槽 具名插槽 作用域插槽

给插槽传递的html结构尽量使用 <template> 包含在里面

23.1 默认插槽(匿名插槽)

1
2
3
4
5
6
7
8
<template>
<div>

<!--子组件中定义插槽 -->
<slot>我是插槽中没有东西时显示的内容</slot>

</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<div id="app">
<MovieList >
<template>
<!-- 父组件使用插槽-->
<ul>
<li v-for="(movie,index) in movieList" :key="index">{{ movie }}</li>
</ul>
</template>
</MovieList>
</div>
</template>


<script>
import MovieList from "@/components/MovieList";

export default {
name: "App",
components: {MovieList},
data() {
return {
movieList: ["西游记", "封神榜", "三国演义", "水浒传"]
}
},
methods: {},
mounted() {
}
}

</script>

23.2 具名插槽

1
2
3
4
5
6
7
<template>
<div>
<!--子组件中定义两个有 名称 的插槽 -->
<slot name="movieSlot1">我是插槽中没有东西时显示的内容</slot>
<slot name="movieSlot2">我是插槽中没有东西时显示的内容</slot>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<div id="app">
<MovieList>
<!-- 2.6.2之前的写法: slot="movieSlot1" 2.6.2之后 v-slot:movieSlot1或者#movieSlot1-->
<template v-slot:movieSlot1>
<!-- 父组件使用插槽-->
<ul>
<li v-for="(movie,index) in movieList" :key="index">{{ movie }}</li>
</ul>
</template>


<template v-slot:movieSlot2>
<!-- 父组件使用插槽-->
<ul>
<li v-for="(movie,index) in movieList" :key="index">{{ movie }}</li>
</ul>
</template>
</MovieList>
</div>
</template>


<script>
import MovieList from "@/components/MovieList";

export default {
name: "App",
components: {MovieList},
data() {
return {
movieList: ["西游记", "封神榜", "三国演义", "水浒传"]
}
},
methods: {},
mounted() {
}
}

</script>

23.3 作用域插槽

作用域插槽是: 插槽的使用者传递结构,而插槽中的数据却在slot的定义的组件中;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<!--子组件中定义插槽,并且把数据传递给父组件 ,传递给父组件的是一个对象 {movieList1:["西游记", "封神榜", "三国演义", "水浒传"]} -->
<slot :movieList1="movieList">我是插槽中没有东西时显示的内容</slot>
</div>
</template>

<script>
export default {
name: "MovieList",
data() {
return {
movieList: ["西游记", "封神榜", "三国演义", "水浒传"]
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <!--父组件中使用作用域插槽 -->
<template>
<div id="app">
<MovieList>
<!-- 2.6.2之前的写法: scope="{movieList1}"或者slot-scope="{movieList1}" 2.6.2之后 v-slot="{movieList1}"-->
<!--对象解构(接收到一个对象,需要结构其属性)-->
<template v-slot="{movieList1}">
<ul>
<li v-for="(movie,index) in movieList1" :key="index">{{ movie }}</li>
</ul>
</template>

</MovieList>
</div>
</template>

24. Vue中的路由

24.1 路由的基本使用

Spa页面应用理解:

  1. 单页web应用(single page web application,spa)
  2. 整个应用只有一个完整的页面 index.html
  3. 点击页面中的导航链接,不会刷新页面,只会做页面的局部更新
  4. 数据需要通过ajax获取

前端路由的理解:

  1. 一个路由就是一组映射关系(key—value)
  2. key为路径,value是component 用于展示页面内容
  3. 当浏览器路径改变时,对应的组件就会显示

安装vue-router插件:

npm install vue-router --save | npm install vue-router

src目录中创建一个目录 router,在router中新建一个文件 index.js

路由组件在使用的时候不需要注册,只需要在路由器中配置即可 src/router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Home from "@/pages/Home"
import About from "@/pages/About";
//导入vue-router插件
import VueRouter from "vue-router"
import Vue from "vue";
//使用vue-router插件
Vue.use(VueRouter);
export default new VueRouter({
mode: "history",
routes: [
{
path: "/home",
component: Home
},
{
path: "/about",
component: About
}
]
});

在main.js中引入编写的路由器:

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import App from './App.vue'
import router from "@/router/index.js"

Vue.config.productionTip = false

new Vue({
render: h => h(App),
beforeCreate() {
},
//注册自己编写的路由器
router
}).$mount('#root')

在App组件中编写路由链接及其路由容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div id="app">
<!-- 路由链接 -->
<router-link class="nav" to="/about" active-class="active">关于我们</router-link>
<router-link class="nav" to="/home" active-class="active">首页Home</router-link>
<hr>
<!--展示路由的容器 -->
<router-view class="rv"></router-view>
</div>
</template>

<script>
export default {
name: "App",
components: {},
data() {
return {}
},
methods: {},
mounted() {

}
}
</script>

24.2 多级(嵌套)路由

注册路由 src/router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import Home from "@/pages/Home"
import About from "@/pages/About";
//导入vue-router插件
import VueRouter from "vue-router"
import Vue from "vue";
import About1 from "@/pages/About1";
import About2 from "@/pages/About2";
//使用vue-router插件
Vue.use(VueRouter);
export default new VueRouter({
mode: "hash",
routes: [
{
path: "/home",
component: Home,
},
{
path: "/about",
component: About,
//注册子路由,在父组件中一定要有<router-view></router-view>
children: [{
path: "about1",//嵌套的子路由,路径前面绝对不能有 /
component: About1
},
{
path: "about2",//嵌套的子路由,路径前面绝对不能有 /
component: About2
}
]
}
]
});

编写About.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
关于我们

<router-link class="nav" to="/about/about1" active-class="active">关于我们1</router-link>
<router-link class="nav" to="/about/about2" active-class="active">关于我们2</router-link>
<router-view></router-view>
</div>
</template>

<script>
export default {
name: "About",
}
</script>
<style scoped>
</style>

24.3 路由的重定向

1
2
3
4
5
6
{
path: "/home",
component: Home,
//路由重定向
redirect:"/about/about1",
}

24.4 路由的query参数获取

在About组件中编写路由链接: About.vue

1
<router-link class="nav" to="/about/about1?id=10&name=admin" active-class="active">关于我们1</router-link>

在About1中获取参数 About1.vue

1
2
3
4
5
6
7
8
9
<script>
export default {
name: "",
mounted() {
console.log(this.$route.query.id)
console.log(this.$route.query.name)
}
}
</script>

24.5 命名路由

1
2
3
4
5
6
7
8
9
10
export default new VueRouter({
mode: "hash",
routes: [
{
name:"home", /*给路由起一个名字*/
path: "/home",
component: Home,
},
]
})

24.6 路由的params参数(RustFul风格)

params参数其实就是RestFul风格的一种路由;

注册路由 @/router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
path: "/about",
component: About,
children: [
{
path: "about1/:id/:name", //注册RestFul风格的路由
component: About1
},
{
path: "about2",
component: About2
}
]
}

在About组件中进行跳转

1
<router-link class="nav" to="/about/about1/10/admin123" active-class="active">关于我们1</router-link>

在About1组件中进行接收

1
2
3
4
5
6
7
8
9
<script>
export default {
name: "About1",
mounted() {
console.log(this.$route.params.id)
console.log(this.$route.params.name)
}
}
</script>

24.7 路由的跳转模式

默认router-link路由的跳转的方式为push,会不断的追加到浏览器的历史记录中;

我们可以使用replace方式直接进行替换,而不是push追加;

配置路由的跳转方式为replace

1
<router-link replace class="nav" to="/home" active-class="active">首页Home</router-link>

24.8 编程式路由导航

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<div id="app">
<button @click="pushTo">点我进行Detail跳转push跳转</button>
<button @click="replaceTo">点我进行Detail跳转replace跳转</button>
<hr>
<!--展示路由的容器 -->
<router-view class="rv"></router-view>
</div>
</template>

<script>
export default {
name: "App",
components: {},
data() {
return {}
},
methods: {
pushTo() {
//push的方式进行路由跳转
this.$router.push("/detail?id=10")
},
replaceTo() {
this.$router.replace("/detail?id=20")
}
},
mounted() {
console.log(this)
}
}
</script>

24.9 路由的两种工作模式(Hash|History)

Hash模式: #及其#之后的内容并不会发送给服务器

History模式:mode:"history"


24.10 全局路由守卫

全局路由守卫分为 全局前置路由守卫全局后置路由首位

着重了解 全局前置路由守卫

1
2
3
4
5
6
7
8
9
10
11
12
//全局前置路由守卫
router.beforeEach((to, from, next) => {
console.log("......前置路由首位......", to)
if (to.path == "/error") {
next();//放行
}
if (to.params.id > 15) {
next();//放行
} else {
next("/error");//导航到某个路由地址
}
})

24.11 动态添加路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
router.addRoute(
"home",//父路由名称
{
name: "home1",
path: "/home/home1", //路由的路径,从根路由开始写
component: () => import("../views/Home1"), //()=>import("")是导入组件的另一种写法
children: [
{
name: "home1a",
path: "home1a",
component: () => import("@/views/Home1a")
},
{
name: "home1b",
path: "home1b",
component: () => import("@/views/Home1b")
}
]
}
)

注意: 无论是静态路由还是动态添加路由,children中的path配置项都不能加 /;


25. 前端的模块化

25.1 为什么需要模块化?

  • 最开始的js就是为了实现客户端验证以及一些简单的效果
  • 后来,js得到重视,应用越来越广泛,前端开发的复杂度越来越高
  • 旧版本的js中没有提供与模块(module)相关的内容

25.2 模块的概念

  • 在js中,一个模块就是实现特定功能的文件(js文件)
  • 遵循模块的机制,想要什么功能就加载什么模块
  • 模块化开发需要遵循规范

25.3 模块化解决的问题

  • 命名冲突
  • 文件依赖(加载文件)
  • 模块的复用
  • 统一规范和开发方式

25.4 模块化的历史

  • 在Es6之前,javascript没有模块系统,它无法将一个大程序拆分成若干个互相依赖的小文件,然后在用简单的方法拼装起来.为了做到模块化,在Es6之前,引入了AMD(Asynchronous module definition)与CMD(common module definition)

  • 前者典型代表是requireJS(外国人搞出来的),后者是seajs(国内阿里大神)

  • 共同点:都是对模块定义的不同规范,都是异步加载模块,并且解决文件之间的依赖重命名冲突等问题。

  • 不同点:模块定义的方式和模块加载机制是不同的,前者AMD(requirejs)是将所有文件同时加载,一次性引入,推崇依赖前置,也就是在定义模块时要先声明其依赖的模块,加载完模块后会立马执行该模块(运行时加载)

  • CMD(seajs)强调的是一个文件一个模块,可按需引入,推崇依赖就近,加载完某个模块后不会立即执行,而是等遇到了require语句的时候在执行

  • 两者的使用加载机制不同,也就导致了AMD(requirejs)模块会提前执行,用户体验好,而CMD(seajs)性能好,因为只有在需要时候才执行,在服务器端,nodejs使用的就是cmd规范,也就是需要什么包,就引入什么包,按需加入(编译时加载)

  • 而在Es6的语言规格中引入了模块化功能,也就很好的取代了之前的commonjs和AMD规范了,成为了浏览器和服务器的通用的模块解决方案,在现今(vuejs,ReactJS)等框架大行其道中,都引入了Es6中的模块化(Module)机制,一些自动化打包工具webpack或者微信小游戏中也同样如此

26 Webpack

26.1 webpack概述

  • webpack解决了现存模块打包器的两个痛点

    • Code Spliting - 代码分离 按需加载
    • 静态资源的模块化处理方案
  • webpack 是一个现代 JavaScript 应用程序的模块打包器(特点 module、 bundler)

  • webpack 是一个模块化方案(预编译)

  • webpack获取具有依赖关系的模块,并生成表示这些模块的静态资源

  • 四个核心概念:入口(entry)输出(output)加载器loader插件(plugins)

  • webpack 和 requirejs对比

    1
    2
    webpack 预编译 (在开发阶段通过webpack进行模块化处理, 最终项目上线, 就不在依赖于 webpack)
    requirejs 线上的编译( 代码运行是需要依赖与 requirejs 的 )

26.2 webpack与模块

  • 在webpack看来:所有的静态资源都是模块
  • webpack 模块能够识别以下等形式的模块之间的依赖

26.3 webpack打包

vue-cli集成了webpack,我们可以不用去了解webpack复杂的打包过程

1
vue init webpack pro01  //使用vue-cli脚手架+webpack打包工具构建项目

27. Es6中的export

  • export default 向外暴露的成员,可以使用任意的变量来接收

  • 在一个模块中,export default只允许向外暴露一次

  • 在一个模块中,可以同时使用export default 和 export向外暴露成员

1
2
3
4
5
6
//test.vue
export default {
address:'深圳'
}
export var title = '星星'
export var zzz = 'hhh'
1
2
3
4
5
//mian.vue
import hahahah , {title as title123 , zzz} from './test.vue'

console.log(hahahah)
console.log(title123 + "6666" + zzz)
1
2
3
打印结果:
address:‘深圳’
星星6666hhh
  • 使用export向外暴露成员,只能用{}的形式来接收,这种形式,叫做【按需导出】
  • export可以向外暴露多个成员,同时,如果某些成员,我们在import的时候不需要,则可以不在{}中定义
  • 使用export导出的成员必须严格按照导出时候的名称,来使用{}按需接收;
  • 如果使用export按需导出的成员想换名称,可以用as进行替换

28. Vue中的全局事件总线

​ 在Vue开发中会遇到大量的组件之间共享数据的情形,针对不同的情形,Vue有相对应的解决方案。比如,父组件向子组件传值可以使用props,复杂项目中不同模块之间传值可以使用Vuex

​ 但是,对于一些简单的项目里的非父子组件来说,它们一方面不适用props,另一方面又没有必要使用Vuex,针对这种情形可以使用中央事件总线(Event Bus)来解决问题。

1
2
3
4
5
6
const vm = new Vue({
render: h => h(App),
//注册全局事件总线的时候,一定在beforeCreate中
beforeCreate() {
Vue.prototype.$bus = this;//安装全局事件总线
});

在组件TodoItem中发送数据(触发事件)

1
2
3
4
5
6
7
8
9
10
export default {
name: "TodoItem",
props: ["toDoObj"],
methods: {
delTodo(todoId) {
// this.$emit("delTodo", todoId)
this.$bus.$emit("delTodo",todoId)
}
}
}

然后就可以在组件App中接收了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
methods: {
// 自定义事件的回调函数
addTodo(todoObj) {
this.todoArray.unshift(todoObj)
},
delTodo(toDoId) {
this.todoArray = this.todoArray.filter(todo => todo.id != toDoId)
}
},

//注册事件一定要在组件挂载之后才能注册
mounted() {
//给全局事件总线绑定事件
this.$bus.$on("delTodo", this.delTodo)
}

29. 浏览器中的数据存储技术

  • cookie

    浏览器端的数据存储技术,以文本的形式存放,可以设置有效时间

    1
    document.cookie = "myname=admin"
  • sessionStorage

    只在本次会话中有效,浏览器关闭(新开标签页)就会失效;

    1
    2
    3
    4
    5
    6
    if (sessionStorage.getItem("myname")) {
    console.log("获取到sessionStorage中的数据:" + sessionStorage.getItem("myname"))
    } else {
    console.log("开始给sessionStorage添加数据")
    sessionStorage.setItem("myname", "admin")
    }
  • localStorage

    只要不是手动清除,永久有效

    1
    2
    3
    4
    5
    6
    if (localStorage.getItem("myname")) {
    console.log("获取到localStorage中的数据:" + localStorage.getItem("myname"))
    } else {
    console.log("开始给localStorage添加数据")
    localStorage.setItem("myname", "admin")
    }

30. Vuex状态管理

30.1 什么是Vuex?

专门在Vue中实现集中式状态(数据)管理的插件,对Vue中多个组件共享状态(数据)进行集中式管理(读/写),也是一种组件的通信方式,且适用于任何组件之间进行通信;


30.2 什么时候使用Vuex?

  • 多个组件依赖同一个状态
  • 来自不同组件的行为需要变更同一状态

30.3 Vuex的工作流程

VueComponent—->(dispatch)—->Actions—->(commit)—>Mutations–(mutate)–>State–(render)–>VueComponent

  1. 组件Vue Component通过dispatch来调用actions提供的方法
  2. 而actions除了可以和api打交道外,还可以通过commit来调mutations提供的方法
  3. 最后mutaions将数据保存到state中
  4. 当然,Vue Components还以通过getters提供的方法获取state中的数据
  • state (类似存储全局变量的数据)
  • getters (提供用来获取state数据的方法)
  • actions (提供跟后台接口打交道的方法,并调用mutations提供的方法)
  • mutations (提供存储设置state数据的方法)

30.4 Vuex的初级使用

1
npm install vuex --save  //安装vuex

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Vuex from "vuex";
Vue.use(Vuex) //引用Vuex插件,并把store注册给vue实例,则vue实例和所有的组件中都可以访问到$store

const store = new Vuex.Store({
state: {
a: 10
}
});

const vm = new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this;
},
mounted() {
console.log(this)
},
router,
store//注册vuex
}).$mount('#root')

组件中访问共享状态:

1
2
$store.state.a; //访问共享数据(不建议这种方式)
$store.state.a=20;//改变共享状态(不建议这种方式)

30.5 Vuex的商业使用规范

src目录中创建store目录,在store目录中再创建index.js

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import Vue from "vue";
import Vuex from "vuex";
//使用插件
Vue.use(Vuex)

//共享的状态数据
const state = {
a: 10
}

//向外提供的action操作
const actions = {
//context就是一个miniStore(迷你版的store)
change(context, value) {
context.commit("CHANGE", value)
}
}

//操作state的接口
const mutations = {
CHANGE(state, value) {
state.a = value
}
}

//创建store
const store = new Vuex.Store({
state,
actions,
mutations
}
)


export default store

组件中就可以使用一下方式改变store中存储的共享状态(数据)了:

1
2
3
4
//通过调用action动作修改
this.$store.dispatch("change", 200)
//或者通过调用mutation修改(不建议)
//this.$store.commit("CHANGE",300)

30.6 getters配置项的使用

getters配置项就是给state中的数据进行加工用的:

1
2
3
getters: {
bigA: state => state.a + "bbbb"
}

在组件中进行使用:

1
{{ $store.getters.bigA }}

31. devDependencies与dependencies

  • devDependencies用于本地环境开发时候。 npm install xxx --save-dev
  • dependencies用户发布环境(生产环境) npm install xxx --save
  • devDependencies是只会在开发环境下依赖的模块,生产环境不会被打入包内。
  • 而dependencies依赖的包不仅开发环境能使用,生产环境也能使用。其实这句话是重点,按照这个观念很容易决定安装模块时是使用--save还是--save-dev
  • ~表示末尾最大,^表示中间最大,*****表示第一个最大

32. Vue中的UI组件库

32.1 移动端UI组件库

  • Vant

  • Cube UI

  • Mint UI


32.2 PC端UI组件库

  • Element-ui
  • IView-ui

文章作者: Yang Shiyu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Yang Shiyu !
  目录