Vue
Vue是一个渐进式的框架。Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
Vue有很多特点和Web开发常见的高级功能
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
一. 安装Vue.js
- cdn引用方式:
<!--开发环境版本,包含有帮助的命令行警告-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--生产版本,优化尺寸和速度-->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
- 下载和引入
开发环境 https://vue.js.org/js/vue.js
开发环境 https://vue.js.org/js/vue.min.js
- npm安装
二. Vue中的MVVM
MVVM是什么:
MVVM分为三个部分:分别是M(Model,模型层 ),V(View,视图层),VM(ViewModel,V与M连接的桥梁,也可以看作为控制器)
1、 M:模型层,主要负责业务数据相关;
2、 V:视图层,顾名思义,负责视图相关,细分下来就是html+css层;
3、 VM:V与M沟通的桥梁,负责监听M或者V的修改,是实现MVVM双向绑定的要点;
MVVM支持双向绑定,意思就是当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改,反之修改V层则会通知M层数据进行修改,以此也实现了视图与模型层的相互解耦;
三. 创建Vue实例传入的options
const app = new Vue({
el:'#app',
data:{
conter:1112
},
methods:{
add: function (){
},
sub: function (){}
}
})
//el:
//类型:string 、HTMLElement
//作用:决定Vue实例会管理哪一个DOM
//data:
//类型:Object 、Function(组件当中data必须是一个函数)
//作用:Vue实例对象对应的数据对象
//methods:
//作用:定义属于Vue的方法,可以在其他的地方调用,也可以在指令中使用
四.Vue的生命周期
vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期
1. beforeCreated
在实例创建之前,数据还没有初始化,dom结构还没有挂载
2. created
实例已经创建完成之后被调用,完成了属性和方法的运算,初始化完毕,dom结构依然没有挂载
在这个钩子里面可以执行一些函数自调用和一些数据的初始化
3. beforeMount
在挂载开始之前被调用,初始化完毕,dom结构依然没有挂载
4. mounted
在挂载完成之后被调用,初始化完毕,dom结构已经挂载
在这个钩子里面可以进行一些数据的交互,dom结构的操作
5. beforeUpdate,updated
更新时调用 以及 更新完成时调用 这个放在一起来看,当然这里面我们添加一个事件,用来更改dom结构中的文字
但是在log中我们看到更新前和更新后的dom中的msg显示的都是更新后的数据,这里我不知道为什么,可能是更新之后beforeUpdate的数据又是更新前的数据了
6. beforeDestroy、destroyed
还没被销毁之前会调用beforeDestroy,已经被销毁后会调用destroyed这个函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dk8xpjCo-1605609638029)(G:\桌面\vue生命周期函数.png)]
五. Vue的基本操作
5.1插值操作 Mustache语法
Mustache语法(也就是双大括号),可以跟一些简单的表达式
<div id="app">
<h1>{{message}}</h1>//hello
<h1>{{message+''+lastName}}</h1><!--hello Vue-->
<h1>{{age * 2}}</h1>//40
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello',
lastName: 'Vue',
age: 20
}
})
</script>
5.1.1 简单指令操作
- v-once
加上了v-once就只会显示第一次的那个值,如果后面改变了值他也不会被改变
<h1 v-once>{{message}}</h1>
- v-html
可以解析html标签,一个字符串里面含有标签就会被解析
<h1 v-html = "url"></h1><!--url是一个带标签的字符串-->
-
v-text
将数据展示到页面,注意会覆盖h1原有的数据
<h1 v-text = "message"></h1>
-
v-pre
原样输出,会把数据原封不到的展示出来
<h1 v-pre>{{message}}</h1>
-
v-cloak
可以解决浏览器闪出{{message}}在显示数据的问题
<style> [v-cloak] { display:none; } </style> <h1 v-cloak>{{message}}</h1>
-
v-show
可以时元素显示或者隐藏
<div> <h2 v-show="true"> dsaaa </h2> </div>
5.2 v-bind基本使用
作用:动态绑定属性
<img v-bind:src="imgURL"><!--这样就可以把imgURL作为变量来识别,从而实现动态绑定-->
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello',
lastName: 'Vue',
age: 20,
imgURL:'https://www.baidu.com'
}
})
</script>
第二种
在类名可以写对象形式,里面是键值对,如果值是true就会显示red这个类,
<div v-bind:class="{red: isActive, green: istine}"></div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello',
lastName: 'Vue',
age: 20,
imgURL:'https://www.baidu.com',
isActive:true,
istine:false
}
})
</script>
第三种,可以写数组
<div>
<h1 :class = "['aa','bb',cc]"></h1><!--有引号的就是字符串,没有引号的就是变量是动态的-->,
</div>
5.2.1 v-bind语法糖
就是简写把v-bind省略也可以
<img :src="imgURL"><!--这样就可以把imgURL作为变量来识别-->
5.2.2 v-bind动态绑定样式
- 第一种是对象
<h1 v-bind:style="{key(css属性名):value(css属性值)}"></h1>
<h1 v-bind:style="{color:'red'}"></h1><!--属性值要加单引号,不加就会当成变量-->
- 第二种是数组
<h1 v-bind:style="[baseColor]"></h1><!--直接在数组里面放变量,然后再js里面把一个对象
baseColor: {color: 'green'}放在变量里面-->
5.3 computed计算属性
methods和computed的区别
计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。
每一个计算属性都包含一个getter和一个setter
计算属性的完整写法,
<div id="app">
<h1>{{fillName}}</h1> <!--计算属性使用的时候不用加小阔号了-->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName:'kobi',
lastName:'james'
},
computed:{
fillName:{
set:function(newValue){
}
get: function (){
return this.firstName +''+ this.lastName
}
}
}
})
</script>
<div id="app">
<h1>{{fullName}}</h1> <!--计算属性使用的时候不用加小阔号了-->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName:'kobi',
lastName:'james'
},
computed:{
fillName:function (){
return this.firstName +''+ this.lastName
}
}
})
</script>
5.4v-on
监听事件事件
在定义事件时,写方法时省略了小括号,但是本身需要传递参数,这个时候,Vue会默认将浏览器生产event事件对象传入到方法中。
在调用方法时如何手动获取浏览器参数的event对象:在参数直接写$event
<h1 v-on:click="调用的函数"></h1>
//简写是语法糖就是省略v-on改成@
<h1 @click="调用的函数"></h1>
<h1 @click="fn()"></h1>
5.4 .1 v-on的修饰符使用
-
.stop
阻止事件冒泡
<button @click.stop="fn()"></button>
-
.prevent
阻止浏览器的默认事件
<button @click.prevet="fn()"></button>
- .enter
监听键盘点击了哪一个按键,只有特定键触发时才会触发回调函数
<input type="text" @keyup.enter="fn()">
- .native
监听组件根元素的原生事件,就算可以监听组件的点击事件,不这个修饰符就不能监听组件的点击事件
<button @click.native="fn()"></button>
-
.once
触发一次回调函数
<button @click.once="fn()"></button>
5.5 判断条件
通过条件判断是否执行对应的代码
<div id="app">
<h2 v-if="score > 90">优秀</h2>
<h2 v-else-if="score > 60">及格</h2>
<h2 v-else>不及格</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
score: 91
}
})
</script>
v-show如果是true就会显示h2标签的内容,如果不是就会隐藏
<div id="app">
<h2 v-show="isShow">优秀</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isShow:true
}
})
</script>
v-show和v-if的区别:一个是显示和隐藏,一个分支执行者句代码还是不执行。
5.6 v-for的使用
5.6.1 遍历数组
可以遍历数组再页面中显示出来,item是数组里面的元素,index是数组对应的下标,names是要遍历的数组,这样就能再页面动态的创建li输出这几个名字,绑定一个key属性如果再数组中间插入元素可以提高性能,所以key的作用主要是为了高效的更新虚拟DOM
<div id="app">
<ul>
<li v-for="(item,index) in names":key="item">{{index}}{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
names: ['why', 'kobe', 'james', 'curry']
}
})
</script>
5.6.1.1 数组中常用的方法响应式的
响应式:响应式就是更改了数组再页面动态展示的数据也会发生相应的改变,通过索引号的方法来修改或者删除数组的元素就不会有响应式
-
push() 再数组的后面追加元素
names.push('zhangsan')
-
pop() 删除数组的最后一个元素
name.pop()
-
shift() 删除数组中的第一个元素
name.shift()
-
inshift() 再数组中的前面添加元素
name.shift('lisi')
-
splice() 删除数组中的元素/替换数组中的元素/插入数组中的元素
//从第二个元素开删除一个再添加一个数字3 name.splice(1,1,'3') //从第二个元素开始删除0个,添加a,b,c name.splice(1,0,'a','b','c')
-
reverse() 翻转数组
name.reverse()
-
Vue.set() 也可以替换数组当中的元素
Vue.set(要修改的对象,索引值,修改后的值) //修改了name这个数组的第一个值为wmz Vue.set(name,0,'wmz')
-
Vue.delete()删除对象当中的属性,可以响应式
Vue.set(要修改的对象,索引值)
//删除了object这个对象里面的name属性
Vue.set(object,object.name)
5.6.2 遍历对象
遍历对象的过程中如果只是获取一个值,那么获取到的是value,第二个值是属性名称,第三个是下标
<div id="app">
<ul>
<li v-for="(value,key,index) in info">{{value}}-{{key}}-{{index}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
info: {
name: 'curry',
age: 18,
height: 1.88
}
}
})
</script>
5.7 过滤器
<div id="app">
//使用过滤器
<h1>{{price | showPrice}}</h1>//hello
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
price:20
},
filters:{//过滤器
showPrice(price){
return '$'+price.tosFixed(2)//保留两位小数
}
}
})
</script>
5.8 表单绑定v-model
Vue中使用v-model指令来实现表单和数据的双向绑定
双向绑定:双向绑定就是如果更改变message会改变input的value值,如果再显示页面更改input的值message也会跟着被更改
<div id="app">
<input type="text" v-model="message">
<!--等同于
<input type="text" :value="message",@input="message = $event.target.value">
-->
</div>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊 Vue',
}
})
</script>
5.8.1 v-model结合radio
<div id="app">
<label for="man">男</label>
<input type="radio" id="man" v-model="sex" value="男">
<label for="wom">女</label>
<input type="radio" id="wom" v-model="sex" value="女">
<h2>{{sex}}</h2><!--观察sex的变化-->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
sex: '男'//再这里可以实现默认选择的是男还是女
}
})
</script>
5.8.2 v-model结合checkbox
<div id="app">
<input type="checkbox" v-model="hobbies" value="篮球">篮球
<input type="checkbox" v-model="hobbies" value="足球">足球
<input type="checkbox" v-model="hobbies" value="排球">排球
<input type="checkbox" v-model="hobbies" value="棒球">棒球
<div>爱好:{{hobbies}}</div>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
hobbies: []//由于v-model是双向绑定,所以当用户点击按钮就会把爱好传到数组里面
}
})
</script>
5.8.3 v-model结合select
<div id="app">
<select name="" v-model="fruits" multiple><!--加上multiple可以进行多选-->
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="榴莲">榴莲</option>
<option value="橘子">橘子</option>
</select>
<h2>你选择的是:{{fruits}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
fruit: '苹果',
fruits: []
}
})
</script>
5.8.4 值绑定
这样做的目的是为了动态生成input当中的值
<div id="app">
<label :for="item" v-for="item in fruits">
<input type="checkbox" :value="item" :id="item">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
fruits: ['苹果', '鸭梨', '西瓜', '香蕉']
}
})
</script>
5.8.5 修饰符
-
lazy
可以让数据失去焦点或者回车时数据才会更新
<input type="text" v-model.lazy="message">
-
number
可以让表单的值是number类型,默认是String类型
<input type="text" v-model.number="age">
-
trim
可以去除表单内容的两边的空格
<input type="text" v-model.trim="name">
watch的基本使用
watch:watch可以动态的监听组件里面的某一个变量的值的改变。
<div id="app">
<input type="text" v-model="name">
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: '张三'
},
watch: {
name(newValue,oldValue) {//如果name的值改变了就会触发他
//他有两个参数,一个是修改前的值,一个是修改后的值
console.log(oldValue);//这个是name改变之前的值
console.log(newValue);//这个是改变之后的值
}
}
})
</script>
6. 组件化
6.1 什么是组件化
- 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
- 但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了
- 组件是一个单独的功能模块的封装
- 这个模块也有自己的HTML模板,也有属于自己的数据data
- 组件不能直接访问Vue实例的数据
- 组件有自己单独的数据data
6.2 创建组件
-
原始方法
创建组件的三大步:
- 创建组件构造器
- 注册组件
- 使用组件
- 调用Vue.extend()创建一个组件构造器
- 通常再创建组件构造器时,传入template代表外面自定义组件的模板
- 该模板就是再使用组件的地方,要显示的HTML代码
- 注册组件全局组件的时候要写在在那个Vue实例下使用的上面,否则会保错
<div id="app">
<!-- 使用组件 -->
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
//创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>啊哈哈哈</h2>
<p>呵呵呵呵呵呵</p>
<h2>啧啧啧啧啧</h2>
</div>
`
});
//注册组件
Vue.component('my-cpn', cpnC) //第一个参数是自定义标签,第二个参数组件构造对象,这样创建的是全局组件,全局组件可以在多个Vue的实例下面使用
const app = new Vue({
el: '#app',
components:{//在这里面注册的是局部组件
cpn:cpnC //cpn是标签名,cpnC是组件构造器对象
}
})
</script>
6.3 父组件和子组件
子组件的注册是在他的父级组件里面注册的,子组件的使用只能在父组件里面。
<div id="app">
<cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
const cpnC2 = Vue.extend({ //子组件
template: `<div>
<h2>子组件</h2>
</div>
`
})
const cpnC1 = Vue.extend({ //父组件
template: `<div>
<h2>父组件</h2>
<cpn2></cpn2> //使用子组件
</div>
`,
components: { //注册件(也就是他的子组件)
cpn2: cpnC2
}
})
const app = new Vue({ //根组件
el: '#app',
data: {
},
components: {
cpn1: cpnC1
}
})
</script>
6.4 组件的语法糖写法
- Vue为了简化注册组件的方式,提供了注册的语法糖
- 主要就是省去了调用Vue.extend()的步骤,而是直接使用对象来代替
- 在Vue内部还是有调用Vue.extend()
<div id="app">
<cpn1></cpn1>
<cpn2>
</cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1', {
template: `
<h2>我是全局注册的组件</h2>
`
})
const app = new Vue({
el: '#app',
components: {
cpn2: {
template: `
<h2>我是局部注册的组件</h2>
`
}
}
})
</script>
6.5 组件模板抽离写法
-
第一种
把模板写在script标签给script标签指定text/x-template,在加一个id,
<div id="app"> <cpn2></cpn2> </div> <script src="../js/vue.js"></script> <script type="text/x-template" id="cpn2"> <div> <h2>我是局部组件</h2> </div> </script> <script> // Vue.component('cpn2', { //这是注册全局主件的 // template: '#cpn2' // }) const app = new Vue({ el: '#app', components: { cpn2: { template: '#cpn2'// 这里直接写模板的id名 } } }) </script>
-
第二种
直接用一个template标签包裹,在添加一个id名字,使用的方法和第一种差不多
<div id="app">
<cpn2></cpn2>
</div>
<template id="cpn2">
<div>
<h1>我是组件</h1>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// Vue.component('cpn2', { //这是注册全局主件的
// template: '#cpn2'
// })
const app = new Vue({
el: '#app',
components: {
cpn2: {
template: '#cpn2'
}
}
})
</script>
6.6 组件数据的存放
- 组件对象之间也有一个
data
属性(也可以有methods等属性) - data属性必须是一个函数
- 函数返回一个对象,对象里面保存着数据
<div id="app">
<cpn1></cpn1>
</div>
<template id="cpn1">
<div><h2>{{title}}</h2></div>
</template>
<script src="../js/vue.js"></script>
<script>
//注册全局组件
Vue.component('cpn1', {
template: '#cpn1',
data() {
return {
title: '我是标题'
}
}
})
const app = new Vue({
el: '#app'
})
</script>
6.6.1 为什么组件的data必须是函数
组件是可复用的vue
实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data
数据都应该是相互隔离,互不影响的,基于这一理念,组件每复用一次,data
数据就应该被复制一次,之后,当某一处复用的地方组件内data
数据被改变时,其他复用地方组件的data
数据不受影响。
6.7 父子组件的通信
6.7.1 为什么要使用父子组件的通信
- 在一个页面中,我们从服务器请求到了很多的数据
- 其中一部分数据,并非是在整个页面的打组件来展示,而是需要下面的子组件来进行展示
- 整个时候,不会在让子组件再次发送一个网络请求,而是直接让父组件将数据传递给小组件
6.7.2 如何进行父子组件通信
-
通过
props
向子组件传递数据- 第一种写法
<div id="app"> <cpn :cmessage="message" :cage="age"></cpn><!--要使用v-bind绑定,这样就能使用父组件的message和age--> </div> <template id="cpn"> <div> <!--如果有个标签就要给他一个根标签,一般外面给他包一个div--> <h1>显示</h1> <h2>{{cmessage}}</h2><!--调用的时候使用在子组件自己取的名字--> <h1>{{cage}}</h1> </div> </template> <script src="../js/vue.js"></script> <script> //子组件 const cpn = { template: '#cpn', data() { return {} }, props: ['cmessage', 'cage'] //这里自己取一个名字, } const app = new Vue({ el: '#app', data: { message: 'hello props', age: 18 }, components: { //注册组件 cpn //这里是使用了增强版的写法,cpn=cpn的简写 } }) </script>
- 第二种写法
就是把props修改一下,这样可以给他加一下限制
props: {
cmessage: {//如果这里使用的驼峰标识 cMessage再上面添加属性就要写成:c-message=“”的形式
type: String, //接收的数据类型
default: '默认显示', //如果接收不到就先这个数字
required: false //是不是必传
}
}
-
通过自定义事件向父组件发送消息
首先给子组件绑定事件,然后使用
$emit
发送自定义事件,然后再监听自定义事件,对他进行一些简单的处理
<div id="app">
<!-- 监听itemclick事件是由子组件发送的, -->
<cpn @itemclick="cpnClick"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in list" @click="btnClick(item,item.name)">{{item.name}}</li></ul>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data() {
return {
list: [{
id: 'aaa',
name: '列表一'
}, {
id: 'aaa',
name: '列表二'
}, {
id: 'aaa',
name: '列表三'
}, {
id: 'aaa',
name: '列表四'
}]
}
},
methods: {
btnClick(item, name) {
//$emit是自定义事件,后面的item和name都是要传递的参数多个参数用逗号隔开。这个自定义事件不要写驼峰命名
this.$emit('itemclick', item, name) //向父组件发送事件
}
}
}
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn
},
methods: {
cpnClick(item, name) {
console.log('cpnClick', item, name);
}
}
})
</script>
6.7.3 父子组件通信的双向绑定
想要修改父组件传给子组件的变量,在页面中进行双向绑定。
思路:
在子组件首先不能通过v-model对props里面的变量进行双向绑定,所以第一步要把props的变量转换到data里面,在data里面声明一个变量,然后把props里面的变量赋值给他,在data变量的值给input的value,然后再监听input事件,这个事件可以监听输入的东西,再把里面的值赋值给data里面的变量从而实现了他们之间的双向绑定,再这个过程中在自定义事件,把变量发送给父组件,父组件在通过监听自定义事件,然后接收到的值对父组件的data里面的数据进行修改。
<div id="app">
<!-- 动态绑定num1和num2把数据传给子组件,监听子组件传给父组件的自定义事件 -->
<cpn :cnum1="num1" :cnum2="num2" @num1change="num1change" @num2change="num2change"></cpn>
</div>
<template id="cpn">
<div>
<!-- 动态的绑定value还有监听input事件,这个事件默认传递了一个event这个对象 -->
<input type="text" :value="dcnum1" @input="num1Input">
<h1>data:{{dcnum1}}</h1>
<h1>props:{{cnum1}}</h1>
<input type="text" :value="dcnum2" @input="num2Input">
<h1>data:{{dcnum2}}</h1>
<h1>props:{{cnum2}}</h1>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
props: {
cnum1: { //接收父组件传来的数据,类型是数字类型
type: Number
},
cnum2: { //接收父组件传来的数据,类型是数字类型
type: Number
}
},
data() {
return { //把数据进行一次处理再进行使用,父组件传来的数据是只读的数据,
//只能用于显示可以,但是不能更改,利用新的变量来接收,
//就可以对他的值进行修改不会影响父组件的传来的数据
dcnum1: this.cnum1,
dcnum2: this.cnum2
}
},
methods: {
num1Input(event) { //接收浏览器默认传递的一个对象
this.dcnum1 = event.target.value;
//这个对象的属性里面存放有input的值把他赋值给dcnum1
//这样就可以实现修改文本框里面的值并且改变dcnum1的值(双向绑定)
//向父组件发送一个自定义事件,和dcnum1的值
this.$emit('num1change', this.dcnum1);
},
num2Input(event) {
this.dcnum2 = event.target.value;
this.$emit('num2change', this.dcnum2);
}
}
}
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 2
},
components: {
cpn
},
methods: {
num1change(value) {
//得到子组件发送过来的dcnum1的值,默认是字符串类型,将他转换为数字类型
//再赋值给num1,这样就实现子组件和父组件的双向绑定
this.num1 = parseFloat(value)
},
num2change(value) {
this.num2 = parseFloat(value)
}
}
})
6.7.4 父子组件的访问方式
6.7.4.1 父访问子$children $refs
使用:$children
和 $refs
(reference的缩写)
-
$children
拿到的是一个数组
<div id="app"> <cpn></cpn> <button @click="btnClick">点击</button> </div> <template id="cpn"> <div> <h2>{{age}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', methods: { btnClick() { //拿到的是一个数组,数组里面包含多个子组件, //通过索引号确定是那一个子组件点上变量名就可以得到子组件的变量 console.log(this.$children[0].age); } }, components: { cpn: { template: '#cpn', data() { return { age: 18 } } } } }) </script>
-
$refs
拿到的是一个对象,默认是一个空对象,要给子组件加上属性
ref
就能在对象里面看见子组件,是以键值对形式存在的ref绑定在组件里面,通过
this.$refs.组件的ref值
拿到的是组件对象ref绑定在标签里面,通过
this$refs.标签里面的ref的值
拿到的是元素对象<div id="app"> <cpn ref="first"></cpn><!--给子组件添加属性--> <button @click="btnClick">点我</button> </div> <template id="cpn"> <div> <h2>{{name}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', components: { cpn: { template: '#cpn', data() { return { name: '张三' } } } }, methods: { btnClick() { //通过属性名得到对应的子组件,然后再打点得到子组件的name变量 console.log(this.$refs.first.name); } } }) </script>
6.7.4.2 子组件访问父组件 p a r e n t 和 parent和 parent和root
$parent
:这个可以访问父组件
$root
:这个是访问根组件的
下面的代码是Vue实例下面创建了一个组件,再这个组件下面有创建了一个组件,然后用最顶层的那个组件来分别访问他的父组件还有他的根组件
<div id="app">
<h2>{{name}}</h2>
<cpn> </cpn>
</div>
<template id="cpn">
<div>
<h2>{{age}}</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2></h2>
<button @click="btnClick">点击</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: '张三'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
age: 18
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
btnClick() {
//这样拿到的是他的父组件里面的age变量
console.log(this.$parent.age);
//这样那到的是根组件的name变量
console.log(this.$root.name);
}
}
}
}
}
}
})
</script>
6.8.组件里的插槽
6.8.1 什么是插槽
- 插槽(Slot)是Vue提出来的一个概念,正如名字一样,插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。
- 插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制
6.8.2 slot-插槽的基本使用
- 插槽再子组件里面
<slot></slot>
- 插槽的可以有默认值直接再插槽里面写上就行
<slot>哈哈</slot>
- 如果有多个值,同时放入组件进行替换,就会一起作为替换元素
<div id="app">
<cpn>
<div>slot</div><!--插入新的东西-->
</cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<slot>哈哈哈</slot><!--声明插槽的位置,里面的元素是插槽的默认值,可以有可以没有-->
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
6.8.3 具名插槽
- 给插槽都添加一个name属性
- 指定使用的时候给标签加一个slot属性,来对应name属性一样的插槽做出修改
- 其他的插槽还是保持默认的,不会被改变
<div id="app">
<cpn>
<!--给标签加上一个属性slot就可以指定对应的插槽被修改,而不是用默认值 -->
<h2 slot="center">哈哈</h2>
</cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<!-- 给插槽都添加一个name属性,再使用的时候可以根据name名来确定是使用那个插槽 -->
<slot name="left">我是左边</slot>
<slot name="center">我是中间</slot>
<slot name="right">我是右边</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
6.8.4 编译作用域
编译作用域:父级组件模板所有的东西都会在父级的作用域编译,子组件模板的所有东西都会在子级作用域内编译
<div id="app">
<!-- 这个子组件显示了,证明是用的Vue实例的isShow -->
<cpn v-show="isShow">
<!-- 这个会显示,他也是再Vue实例里面的 -->
<h1 v-show="isShow">我是插槽</h1>
</cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<!-- 这个h3不会显示,因为他用的是自己的isShow -->
<h3 v-show="isShow">hhhh1</h3>
<slot></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: true
},
components: {
cpn: {
template: '#cpn',
data() {
return {
isShow: false
}
}
}
}
})
</script>
6.8.5 作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供
<div id="app">
<cpn>
<!-- 在2.5版本以前必须使用template标签接收,新版本可以用是什么标签都可以 -->
<!-- 给标签添加一个slot-scope属性,属性值是自定义的,这样就可以得到插槽 -->
<template slot-scope="slot">
<ul>
<!-- 通过循环把数组遍历循环打印到页面 -->
<!-- 通过打点上自定义的属性名,就可以获得该属性对应的变量 -->
<li v-for="item in slot.data">{{item}}</li>
</ul>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<!-- 首先在这自定义一个属性,然后属性值是要传的变量 -->
<slot :data="planguager"></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components: {
cpn: {
template: '#cpn',
data() {
return {
planguager: ['java', 'php', 'python', 'c++', 'c#']
}
}
}
}
})
</script>
7. 模块化
7.1 Es5解决导出变量和函数的方法
- 首先使用一个自调函数,包所有的代码都放在里面
- 在里面定义一个空对象
- 把里面想要导出的变量或者方法都放在这个对象里面
- 使用return将这个对象返回
- 在其他的页面使用script标签引入
- 用对象的名字打点就可以调用另一个js文件里面的方法和变量
常见的模块化的规范
- CommonJS
- AMD
- CMD
- ES6的Modules
7.2 ES模块化导入和导出
首先要在HTML里面引入俩个js文件,并且类型需要设置为module
-
导出
export
let name = 'zs'; let age = 18; //这样就导出了name,age这两个变量,也可以导出函数 export {name,age} //第二种在声明变量的时候导出 export let sex = 'man'; //导出函数 export function(a+b){ return a+b } //导出类 export class Person{ run(){ console.log('奔跑') } }
-
导入
import {} from '路径'
//这样就导入name和age import {name,age,Person} from './aa.js' const p = new Person();
//这样就能使用类里面的函数run
p.run()
//全部导入,适用于要导入的东西比较多,相当于是把这些东西全部导入在info(自定义的)这个对象里面
//import * as info from ‘路径’
import * as info from ‘./aa.js’
//使用导入的变量的时候 info. 变量名
3. 导出`export default`
同一个模块只能使用一次
```js
const address = 'guizhou';
export default address
//导入export default的方法
//import 名字(由自己自定义)路径
import add './aa.js'
六. webpack
webpack可以做的事情:
- 代码转换
- 文件优化
- 代码分割
- 模块合并
- 自动刷新
- 代码校验
- 自动发布
6.1 webpack安装
-
首先webpack依赖node环境
-
所以要安装node
-
在安装npm用来管理node的包
全局安装webpack,后面的是安装的版本,-g是全局安装的意思
npm install webpack@3.6.0 -g
6.2 webpack 的基本使用
-
创建一个根文件夹,里面在创建一个src文件和dist文件
-
src文件夹是用来放写代码的文件夹,dist是存放打包好的文件
-
在创建一个html文件
-
在src下面创建一个入口文件,一般名字为main.js,在创建一个a.js
-
在a.js里面导出要导出的变量(用什么语法都可以)
-
直接在main.js文件里面写要导入的变量,(用什么语法都可以)
-
然后再打开终端切换到根文件夹
-
使用命令
webpack 要打包的文件路径和文件名 打包好存放的路径和文件名
webpack .\src\main.js .\dist\bundle.js
6.3 webpack.config.js配置和package.json配置
6.3.1 webpack.config.js
-
首先在文件里面创健
webpack.config.js
文件//引入node系统模块path const path = require('path'); module.exports = { //要打包的文件 entry: './src/main.js', output: { path:path.resolve(__dirname,'dist'),//打包到哪里,用绝对路径 filename:'bundle.js'//打包后的文件 } } //这样打包的时候直接执行webpack就可以打包,只要是终端下执行的都是全局的webpack
6.3.2 package.json
-
把执行
webpack
的命令和npm run build
对应起来-
在终端使用
npm init
命令就会创建package.json文件 -
打开package.json文件
{ "name": "meetwebpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", //再里面自定义一个build然后值对应的就是webpack "build": "webpack" }, "author": "", "license": "ISC" }
- 使用打包的时候就可以直接使用
npm run build
命令 - 这样写的好处是他优先使用本地的webpack
- package.json中的script脚本在执行时,会按照一定的顺序寻找命令
- 首先会寻找本地的node_modules/bin路径中对应的命令
- 如果没有找到,就会去全局寻找
-
6.3.3 本地安装webpack
使用命令npm install webpack@3.6.0 --save-dev
下载
--save-dev
:是开发时依赖。项目打包后不需要继续使用的
6.4 css文件处理
-
使用命令下载
npm install --save-dev css-loader
- 这个loader是用来加载css文件的
-
使用命令下载
npm install style-loader --save-dev
下载- 这个loader是用来负责将样式添加到DOM里面
-
去官网查看 https://www.webpackjs.com/loaders/ 使用的步骤
-
在webpack.config.js文件里面添加下面代码(官网也有)
module: {
rules: [{
test: /\.css$/,
//使用多个loader的时候是从右向左,先加载css文件,在添加到DOM里面顺序不能乱
use: ['style-loader', 'css-loader']
}]
}
}
6.5 less文件的处理
- 首先使用命令下载,
npm install --save-dev less-loader less
less
是解析less文件的less-loader
是加载less文件的- 在webpack.config.js文件里面添加下面代码(官网也有)
- 添加到 rules是一个数组里面
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}
6.6 图片文件处理
-
首先使用命令下载,
npm install --save-dev url-loader
-
在webpack.config.js文件里面添加到 rules是一个数组里面(官网也有)
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'url-loader',
options: {
//当加载的图片大小大于limit时,需要使用file-loader模块进行加载
//当加载的图片大小小于limit时,会将图片编译为base64字符串形式
limit: 8192,
//将打包好的图片文件放在img文件夹里面名字为自己原理的名字加上8位的哈希值
name: 'img/[name].[hash:8].[ext]'
}
}]
}
- 下载file-loader
npm install --save-dev file-loader
- 要在webpack.config.js文件里面添加下面代码
publicPath
让index.html找到打包好的js
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: './dist/' //这就是添加的位置
},
6. 7 ES6转ES5 babel
-
使用命令下载
npm install --save-dev babel-loader babel-core babel-preset-es2015
-
要在webpack.config.js文件里面 把代码添加到rules是一个数组里面
{ test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } }
6.8 Vue的配置
-
使用
npm install vue --save
进行下载(因为后续的项目也会使用Vue,所以不是开发依赖) -
Vue有两类版本
- 第一种是runtime-only ,代码当中不可以有任何的template
- 第二种是runtime-compiler,代码中可以有template,因为有compiler可以用于编译template
-
由于下载的Vue默认的是指向的第一种,所以要通过配置来修改
- 在webpack文件里面添加以下代码
//它和之前配置的entry、output、module是并列的,添加在下面就行
resolve: {
alias: {
//在下载的vue文件夹里面有一个vue.esm.js的文件,指定一下要使用的vue是那类版本
'vue$': 'vue/dist/vue.esm.js'
}
}
- 在要写vue代码的文件里面引用
import Vue from 'vue'
- 这样导入没有写路径,就是去node_modules文件夹下面找
6.9 template和el的关系
-
可以省略不用声明变量去接收Vue实例
-
el和template同时存在的话会,template会把el绑定的div给替换掉
new Vue({
template: `
<div>
<h2>{{message}}</h2>
</div>
`,
el: '#app',
data: {
message: 'hello webpack'
}
})
6.10 处理Vue文件的配置
- 下载loader
npm install --save-deve vue-loader vue-template-compiler
- 打开webpack文件在module里面添加以下代码
{
test: /\.vue$/,
use: ['vue-loader']
}
-
这样就能打包vue文件
-
把vue实例的数据抽离出来,新建一个文件后缀为.vue的
<template> <div> <h2 class="title">{{message}}</h2> </div> </template> <script> // 导出这个对象 export default { data() { return { message: 'hello webpack 哈哈哈哈哈1' } } } </script> <style> .title{ color:yellow; } </style>
-
在入口文件导入
//使用这个把他导入
import App from './vue/App.vue'
new Vue({
//这里会把HTML里面对应的div替换
template: ` <App></App> `,
el: '#app',
components: {//创建子组件
App
}
})
6.11 webpack的Plugin的使用
6.11.1 plugin横幅的使用
- loader:主要用于转换某些类型的模型。他是一个转换器
- plugin:翻译过来叫插件,他是对webpack本身的扩展,是一个扩展器
在webpack.config.js文件里面添加下面代码
//首先引入webpack模块
const webpack = require('webpack')
//把这个代码添加到module.exports里面,
//和entry、output、module、resolve同一级别的,写在他们下面就行
plugins: [
new webpack.BannerPlugin('最终版权归loujunjun所有')
]
在打包好后的js文件的第一行就会多出这行版权信息/*! 最终版权归loujunjun所有 */
6.11.2 打包HTML的html-webpack-pugin的使用
由于发布程序的时候是发布dist文件夹,所以要在dist目录下自动生成html文件
-
安装:
npm install html-webpack-plugin --save-dev
-
修改webpack.config.js文件
//引入下载好的插件 const HtmlWebpackPlugin = require('html-webpack-plugin'); //在plugin里面添加这句代码 new HtmlWebpackPlugin({ template:'index.html'//让他根据当前目录里面面的index.html在dist里面生成html })
-
在把之前在webpack.config.js的module.exports的output里面加的这配置
// publicPath: './dist/'
注释掉因为,index.html和bundle.js在同一个文件夹下面。
6.11.3 压缩js文件Uglifyjs-webpack-plugin的使用
-
下载
npm install uglifyjs-webpack-plugin --save-dev
-
修改webpack.config.js文件
//引入下载好的插件 const UglifyjsWebpackPlugin = require('uglify-webpack-plugin'); //在plugin里面添加这句代码 new UglifyjsWebpackPlugin();
-
他会把版权的那个横幅给去掉,因为会去掉所有的注释
6.12 webpack-dev-server搭建本地服务器
作用:服务当前的文件夹,实时监听我们写的代码进行编译,他会先放在在内存里面,等真正的发布的时候在使用npm run build
进行打包。
-
下载
npm install --save-dev webpack-dev-server
-
修改webpack.config.js文件,和entry、output、module、resolve同一级别的,写在他们下面就行
devServer: { contentBase: './dist',//为哪一个文件夹提供本地服务,默认是根文件夹, inline: true,//页面实时刷新 port:3000, //使用那个端口号,默认是8080 }
-
在package.json文件下面添加一个脚本
//在script里面添加 "dev": "webpack-dev-server --open"//--open在终端输入后会自动打开对应的html
-
使用的时候直接在终端输入
npm run dev
就能直接在
6.13 webpack配置文件分离
-
建立一个build的文件夹,用来放配置相关的东西
-
建立一个用于存放公共配置的文件
base.config.js
-
建立一个用于存放开发时依赖的文件
dev.config.js
-
建立一个用于存放运行时依赖的文件
prod.config.js
-
下载用于合并的东西
npm install --save-dev webpack-merge
这是生产时的
//这是引用压缩代码的 const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); //这是引用合并东西的 const webpackMerge = require('webpack-merge'); //引入公共配置 const baseConfig = require('./base.config'); //使用合并东西的那个把运行的配置文件和公共配置文件合并 module.exports = webpackMerge(baseConfig, {//这个里面写运行时依赖的配置 plugins: [ new UglifyjsWebpackPlugin() ] })
这是开发时的
//这是引用合并东西的 const webpackMerge = require('webpack-merge'); //使用合并东西的那个把开发时的配置文件和公共配置文件合并 const baseConfig = require('./base.config'); module.exports = webpackMerge(baseConfig, { devServer: { contentBase: './dist', inline: true } })
-
这时候就要重新修改package.json的配置了,json文件里面是不能写注释的,
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config ./build/prod.config.js",//给他添加路径运行时的 "dev": "webpack-dev-server --open --config ./build/dev.config.js"//给他添加路径开发时的 },
-
还要修改打包的路径,在公共配置里面修改
output: { path: path.resolve(__dirname, '../dist'),//打包路径 filename: 'bundle.js', },
七、Vue CLI
-
如果只是简单写几个Vue的Demo程序,就不需要用Vue CLI
-
在开发大型项目,就需要使用Vue CLI
- 使用Vue.js开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情
- 如果每一个项目都要手动完成这些工作,那么效率就比较低,所以通常使用一些脚手架工具来完成这些事情
-
CLI是什么
- CLI是Command-Line Interface,翻译为命令行界面,但是俗称脚手架
- Vue CLI是一个官方发布Vue.js项目脚手架
7.1 安装Vue CLI
-
安装
npm install -g @vue/cli@3.2.1
我安装的是脚手架3的版本 -
为了能使用脚手架2,所以在安装
npm install -g @vue/cli-init@3.2.0
7.2 使用 vueCli2创建初始化项目的过程
- 使用命令
vue init webpack 项目文件夹的名字
- 会出现
project name
,是让我们设置项目文件的名字,直接敲回车,会使用默认第一步的项目文件的名字 - 会出现
Project description
是让我们设置项目信息的描述, - 会出现
Author
,是作者信息,默认会读取git的信息 - 会出现
Runtime + Compiler: recommended for most users Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere
暂时先选第一个。
-
会出现
Install vue-router? (Y/n)
问我们是否要安装一个路由 -
会出现
Use ESLint to lint your code? (Y/n)
,就是问我们要对代码的一些限制,一旦代码写的不规范就会编译不通过 -
如果选择yes了就会出现
Standard (https://github.com/standard/standard) Airbnb (https://github.com/airbnb/javascript) none (configure it yourself)
让我们选择一种规范。第一个是标准的规范,第二个是爱彼迎的规范,最后一个是配置一个自己的。 -
会出现
Set up unit tests (Y/n)
写单元测试的 -
下一步会出现
Setup e2e tests with Nightwatch? (Y/n)
端到端的测试 -
下一步会出现
Yes, use NPM Yes, use Yarn No, I will handle that myself
,准备用什么来管理项目
7.3 runtime-compiler和runtime-olny的区别
7.3.1 runtime-compiler
首先这个版本有template,compiler(解析template),他的执行过程
template
——parse(解析)——ast
(完整写法是abstract syntax tree,抽象语法树)——compile(编译)——》render
(函数)——翻译成——》virtual dom
(虚拟dom)——显示——UI
(真实的dom,就是显示在页面上的东西)
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
7.3.2 runtime-only
首先这个版本没有template,这个要比上面那个轻6kb,直接就是render,他的性能更高,他的执行过程
render
(函数)——翻译成——》virtual dom
(虚拟dom)——显示——UI
(真实的dom,就是显示在页面上的东西)
new Vue({
el: '#app',
render: h => h(App)
})
7.3.3 render函数
- 第一种用法
new Vue({
el: '#app',
render: function(createElement) {
//第一个参数是要创建的元素,第二个是元素给元素添加的类名,第三个是元素内部的内容是一个数组
//他会替换掉页面当中id为app的那个元素
return createElement('h1',
{ class: 'box' },
//元素里面还可以在一次调用这个函数来创建新的元素
['hello render',createElement('h3', '我是h3')])
}
})
- 第二种用法,传入一个组件
//创建cpn组件
const cpn = {
template: `<div>{{name}}</div>`,
data() {
return {
name: 'zs'
}
}
}
new Vue({
el: '#app',
render: function(createElement) {
return createElement(cpn)//直接传入组件,这个createElement可以简写成h,或者其他名字也行
}
})
在由于在开发依赖里面已经有了一个叫vue-template-compiler
了,他是用来解析template
的,所以使用 runtime-only
这个版本比runtime-compiler
版本的性能高
7.4 使用vueCli3创建项目
7.4.1 vueCli3和vueCli2的区别
- vue-cli3是基于webpack4打造的,vue-cli2还是webpack3
- vue-cli3的设计原则是0配置,移除配置文件根目录下的bulid和config等目录
- vue-cli3提供了vue ui 命令,提供可视化配置
- 移除了static文件夹,新增publick文件夹,并且index.html移动到public中
7.4.2 创建vueCli3项目
-
vue create 项目的名称
-
Please pick a preset: (Use arrow keys)
default (babel, eslint) Manually select features
选择配置,第一个是默认配置,第二个是手动配置
3.Check the features needed for your project: (*) Babel ( ) TypeScript ( ) Progressive Web App (PWA) Support ( ) Router ( ) Vuex ( ) CSS Pre-processors ( ) Linter / Formatter ( ) Unit Testing ( ) E2E Testing
要用什么就使用键盘上下键切换,点击空格就是可以
-
Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)
In dedicated config files In package.json
一些配置是要放在一个单独的配置文件还是放在packeg.json文件的后面,第一个是选择单独的文件
-
Save this as a preset for future projects? (y/N)
是不是要保存这些配置,如果是的话在下一次创建项目的时候第一个选项就会多出这个配置 -
如果第五步选择了yes就会来到
Save preset as:
保存配置的名称用于下次创建项目的时候在第一个选项中可以出现这个配置
启动npm run serve
7.4.3 vue-cli3的配置
- 通过在终端使用:
vue ui
可以显示配置的可视化页面 - 在
node_modules library root
的@vue
的cli-service
里面可以看到详细配置 - 在项目的根目录下创建一个
vue.config.js
的文件
八、Vue-Router
8.1 url的hash和html5的history
改变地址栏的地址但是页面不会刷新
-
第一种hash
location.hash = 'addv'
,这样会把浏览器的url改了但是不会去请求服务器 -
第二种history
history.pushState({},'','home')
这样改页面也不会刷新,使用多次的话可以返回上一个页面,保留了很多的历史纪录,有点像栈结构的方式hitory.back()
返回上一个页面history.go(-1)
也是可以返回上一个页面,括里面填写正数和负数都可以,根据括号里面的数字返回到对应的页面history.replaceState({},'','list')
这个如果使用多次他是通过替换的方法式,不能返回,前一次的页面
8.2 vue-router 介绍
- vue-router是vue.js官方的路由插件,他和vue.js是深度集成的,适用于构建单页面应用
- 官网(https://router.vuejs.org/zh/)
- 在路由用于设定访问路径,将路径和组件映射起来
- 在vue-router的单页面应用中,页面的路径的改变就是切换组件
8.3 下载和使用vue-router
- 下载
npm install vue-router --save
使用
-
导入路由对象,并且调用
Vue.use(VueRouter)
安装 -
创建路由实例,并且传入路由映射配置
-
在vue实例中挂载创建的路由实例
-
创建一个文件router在里面在创建一个index.js的文件
//导入vue-router import VueRouter from 'vue-router' //导入vue import Vue from 'vue' //通过vue.use安装插件 Vue.use(VueRouter); //创建VueRouter对象 const routes = [ ] //创建路由实例 const router = new VueRouter({ //设置路由和组件之间的应用关系 routes }) // 导出router export default router
-
在入口的main.js里面引用
import Vue from 'vue' import App from './App' //引入路由的文件 import router from './router/index.js' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', //在vue实例当中挂载 router, render: h => h(App) })
-
-
8.3.1 路由映射配置和呈现出来
-
创建两个vue文件一个home和一个about
//这是about文件的内容 <template> <div> <h2>我是about,呵呵呵</h2> </div> </template> <script> export default { } </script> <style> </style> //这是home文件的内容 <template> <div> <h2>我是home,哈哈哈哈</h2> </div> </template> <script> export default { } </script> <style> </style>
- 载App.vue里面添加
<template>
<div>
<!-- 载页面会显示这两个链接,点击就会切换到对应的页面在下面显示
是vue内部注册的两个组件,最终会渲染成a标签,
to属性如果点击这个a标签就会把网页改成to里面的路径-->
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<!-- 这是可以用来呈现内容的 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
- 在路由的文件里面添加路由
import Vue from 'vue'
import VueRouter from 'vue-router'
//引用那俩个vue文件
import Home from '../components/Home'
import About from '../components/About'
Vue.use(VueRouter)
//一个路由就是一个对象,有俩个文件所以在数组里面是有两个对象
//对象有俩个属性,第一个是path路径,第二个是写引入的vue文件
const routes = [
{
path: '/',
redirect: '/home'//重定向到home,就是默认是显示home界面
},
{ path: '/home', component: Home },
{ path: '/about', component: About }]
const router = new VueRouter({
routes,
//默认是使用hash来修改路径的,把改成用history来修改
mode: 'history',
//这里可以统一修改当前活跃项添加的类名,默认是router-link-active
linkActiveClass:'active'
})
export default router
8.3.2 router-link标签的详细补充
<router-link to="home" tag="button" replace active-class="active">首页</router-link>
to属性:当点击router-link按钮的时候会把网页的路径修改成to的属性值,
tag属性:默认router-link在页面渲染的时候是a标签,可以通过这个属性把他修改成其他的标签
replace:低层默认使用的是hiestory模式,这样可以把他改成replace模式,就是点击切换的时候不能返回上一个页面
active-class:点击当前项的时候添加的类名改成自己想要的,默认是router-link-active
8.3.3 通过代码也能实现跳转路由
<template>
<div>
<!--绑定点击事件-->
<button @click="homeClick">首页</button>
<button @click="aboutClick">关于</button>
<!-- 这是可以用来呈现内容的 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
methods:{
homeClick(){
//只要下载了router这个路由,每一个组件都会有$router这个对象,使用这个对象下面的方法就能改变页面路径
this.$router.push('/home')
},
aboutClick(){
this.$router.push('/about')
}
}
}
</script>
<style>
</style>
8.4 动态路由
$router
和$route
,第一个拿到的是整个创建出来的router对象,第二个是拿到当前处于活跃状态的就是拿到的是谁
index.js:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../components/Home'
import About from '../components/About'
import User from '../components/User'
Vue.use(VueRouter)
const routes = [{
path: '/',
redirect: '/home'
},
{ path: '/home', component: Home },
{ path: '/about', component: About },
{
path: '/user/:nameId',//nameId是参数
component: User
}
]
const router = new VueRouter({
routes,
mode: 'history'
})
export default router
App.vue
<template>
<div>
<!-- 载页面会显示这两个链接,点击就会切换到对应的页面在下面显示 -->
<router-link to="/home" tag="button" replace active-class="active">首页</router-link>
<router-link to="/about" replace>关于</router-link>
<!--拼接要显示的参数 -->
<router-link v-bind:to="'/user/'+userId">用户</router-link>
<!-- 这是可以用来呈现内容的 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return{
userId:'lisi+zs'
}
}
}
</script>
<style>
</style>
User.vue
<template>
<div>
<h2>我是用户</h2>
<!-- 让她显示在页面里面 -->
<h2>{{name}}</h2>
</div>
</template>
<script>
export default {
computed:{
name(){
//获取到当前活跃项的参数叫nameId,然后就能得到App.vue里面声明的参数值
return this.$route.params.nameId
}
}
}
</script>
<style>
</style>
8.5 vue-router 路由懒加载
当打包构建应用的时候,JavaScript包会变的非常大,影响页面加载,所以我们要把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样比较高效,一个懒加载会对应好一个js文件
//使用懒加载获取路径的时候这样写就行,在打包好的文件里面会有对应的js文件是对应这个的
const Home = () =>//这里不要用大括号包起来,括起来要加return
import ('../components/Home');
const routes = [
{
path: '/home',
component: Home,}】
8.6 vue-router路由的嵌套使用
const routes = [{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home,
//在要嵌套的路由里面加一个childeren属性,然后把要嵌套进去的路由添加在里面
children: [{
path: '',
component: HomeNews
}, {
//注意这里不用加/
path: 'news',
component: HomeNews
},
{
path: 'messages',
component: HomeMeages
}
]
}]
在Home里面显示路由对应的内容
<template>
<div>
<h2>我是home,哈哈哈哈</h2>
<!--这里要把路径写全-->
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/messages">消息</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
8.6 vue-router参数传递
- params的类型
- 配置路由格式:
/router/:id
- 传递方式:
在path(路径)后面跟上对应的值
- 传递后形成的路径:
/router/123
- 配置路由格式:
<router-link v-bind:to="'/user/'+userId">用户</router-link>
//通过代码跳转,监听一个点击事件,然后再里面写上这个,括号里面写跳转的路径,拼接上要传递的参数
this.$router.push('/profile'+this.name)
- query的类型
- 配置路由的格式:/router
- 传递的方式:对象中使用query的key作为传递的方式
- 传递后的路径:
/router?id=123
<router-link :to="{path:'/profile',query:{name:'ljj',age:18}}">档案</router-link>
//通过代码的方式跳转页面和传递参数
this.$router.push("{path:'/profile',query:{name:'ljj',age:18}}")
8.7 导航守卫
全局导航守卫:监听全局的路由跳转
在跳转到对应的页面让他的title显示对应的内容,
第一步在路由里面添加一个属性
const routes=[
{
path: '/about',//这是路径
component: About,//对应的组件
meta: {
title: '关于'//添加一个title
}
}]
在路由的那个文件里面调用router.beforEach这个函数,把这个函数写在路由里面就是路由独享守卫,可以参考官网,还有组件守卫
//前置守卫(guard)
router.beforeEach((to, from, next) => {
document.title = to.matched[0].meta.title//这里可以获取当前获得页面的title的内容
next();//这个必须的写
})
//to:即将要进入的路由对象
//from:当前导航即将要离开的对象
//next:调用这个方法后才能进入下一个钩子
//后置钩子(hook)
router.afterEach((to, from) => {
console.log(1);
})
组件守卫
- beforeRouteEnter():进入路由前
- beforeRouteUpdate():路由复用同一个组件时
- beforeRouteLeave():离开当前路由时
export default {
// 组件内守卫
// 因为这个钩子调用的时候,组件实例还没有被创建出来,因此获取不到this
beforeRouteEnter (to, from, next) {
console.log(to.name);
// 如果想获取到实例的话
// next(vm=>{
// // 这里的vm是组件的实例(this)
// });
next();
},
// 路由即将要离开的时候调用此方法
// 比如说,用户编辑了一个东西,但是还么有保存,这时候他要离开这个页面,就要提醒他一下,还没保存,是否要离开
beforeRouteLeave (to, from, next) {
const leave = confirm("确定要离开吗?");
if(leave) next() // 离开
else next(false) // 不离开
},
}
处于活跃和不活跃会调用的函数
activated(){
console.log('我是活跃就执行');
},
deactivated(){
console.log('我是不活跃就执行');
},
8.8 keep-alive
- keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染
- router-view也是一个组件如果之间被包在keep-live里面所有路径匹配到的视图组件都会被缓存
- 他里面的属性include:值正则表达式会在字符串,只有匹配的组件才会被缓存
- exclude:值正则表达式会在字符串,任何匹配的组件都不会被缓存
- 字符串的话就写每一个组件的name值就行了多个用逗号隔开
<keep-alive exclude="">
<router-view></router-view>
</keep-alive>
8.9 文件路径起一个别名
- 找到build文件,找到
webpack.base.conf.js
这个配置文件打开
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {//在这可以起别名
'@': resolve('src'),
'assets': resolve('src/assets')
}
},
在html标签里面使用的使用的时候必须在别名前面加一个~
8.10 Promise的基本使用
把异步操作的东西放在promise里面
//promise接收的参数是一个函数,函数里面的两个参数一个是resolve和reject也是俩个函数,
new Promise((resolve,reject)=>{
resolve();//成功调用这个函数
reject();//失败调用这个函数
}).then(data=>{
//如果是成功的话就会执行这里面
}).catch(err=>{
//失败就执行这里面
})
异步操作后的三种状态
pending
:等待状态,比如在进行网络请求或者定时器没有到时间fulfill
:满足状态,当我们主动调了resolve就是处于满足状态,并且会调then()reject
:拒绝状态,当我们主动回调了reject时,就处于该状态,并且回调catch()
8.10.1 Promise的链式调用
// 可以有简写的方法,可以自己去试试
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('aaa')
},1000)
}).then((data)=>{
console.log('我是第一层的aaa')
return new Promise((resolve,reject)=>{
resolve(data+'加上第二层')
})
}).then((data)=>{
console.log(data)
})
8.10.2 Promise的all方法
如果要发送两次网络请求要俩个同时返回结果才能对请求进行操作,就可以使用这个方法,下面是用定时器模拟的
Promise.all([//接收一个数组作为参数
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('aaa')
})
}),
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('bbb')
})
})
]).then(results=>{//返回的是一个数组
console.log(results)
})
九、Vuex
9.1 Vuex是什么
Vuex是一个专门为vue.js应用程序的状态管理模式
- 他采用集中存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
- Vuex也是集成到Vue的官方调试工具devtools extension ,提供零配置的time-travel调试,状态快照导入导出等高级调试功能
简单的说就是一个组件里面的内容可以给所有的Vue创建处理的组件使用,而且是响应式的
例如用户登入状态、用户名称、头像,在多个页面都要共享的问题
9.2 Vuex的基本使用
- 使用npm下载
npm install --save vuex
- 浏览器里面也要下载一个插件叫
vue.js devtools
的插件 - 在项目的src文件夹里面创建一个store的文件夹,再里面创建一个index.js的文件
//这是最基本的格式
import Vue from 'vue';
import Vuex from 'vuex';
//安装插件
Vue.use(Vuex);
//创建对象
const store = new Vuex.Store({
state:{
//这个变量可以在所有的组件里面都能使用
//使用的时候:$store.state.counrer,这样就能再其他的组件里面使用这个变量
counter:1
},
mutations:{},
actions:{},
getters:{},
modules:{}
})
// 导出
export default store
-
再main.js(入口文件)文件里面引用他,和router一样的使用步骤
-
对共享的变量实行双向绑定,再mutations里面添加俩个方法,
9.3 Vuex的核心概念
9.3.1 state
保存共享状态的地方,单一状态树能够让我们最直接的方式找到某个状态的片段,在维护和调试的过程中,也非常的方便的管理和维护,类似于组件里的data
单一状态树:英文(Single Source of Truth)翻译成单一数据源,
9.3.2 getters
类似于计算属性一样,把需要做一些处理的数据进行返回
getters:{
more20stu(state){
return state.stu.filter(s=>s.age>20)
},
more20stuLength(state,getters){//他默认会传俩个参数,getters就是当前的getters
return getters.more20stu.length
},
moreAgeStu(state){//如果想通过动态的来传递参数到里面,可以让他返回一个函数,就可以传递参数了
return age=>state.stu.filter(s =>s.age>age).length
}
},
//在别的组件使用他里面的东西,例如
$store.getters.more20stu
9.3.3 mutations
通常情况下Vuex要求我们mutations中的方法必须是同步的方法,
- 主要的原因是我们使用devtools时,可以帮助我们捕捉mutation的快照,如果是异步操作,就不能很好的跟踪这个操作什么时候被完成
Vuex的store的状态更新的唯一方式是通过mutation提交的,
mutations:{
increment(state,count){//他会默认传一个参数,再其他组件调用这个方法,count可以接收传递过来的参数,多个参数可以传递一个对象。
state.counter++
},
decrement(state)
state.counter--
}
},
调用的两种方法方法
//第一种
addition(count){
this.$store.commit('increment',count)//第一个参数填,调用的方法名称,后面可以跟传递参数
}
//第二种
addition(count){//mutations里面接收的时候这个传递过去的是一个对象
this.$store.commit({
type:'increment',
count
})
}
mutation的类型常量
因为在调用的时候第一个是参数是一个字符串,容易写错
-
创建一个文件,里面声明一些变量来存储字符串
//在mutation里面声明方法的时候就可以用里面的变量 //引用 import {INCREMENT} from './mutation-types.js' mutations:{ //使用 [INCREMENT](state){ state.counter++ }, }
//在别的组件调用的时候也可以使用变量来作为第一个参数
//引入
import {INCREMENT} from '@/store/mutation-types.js'
//使用
this.$store.commit(INCREMNT)
9.3.4 actions
如果有异步的方法就写在这里面,比如说延时改变state里面的变量,把异步的代码写在action里面,然后再action里面调用mutations里面的对应的方法就行
actions:{
aupdataInfo(context,data){//默认传递一个参数context(上下文),这个参数相当于store,data是定义来接收传递过来的形参,
setTimeout(()=>{
context.commit('updataInfo')//在这里调用mutations里面的updataInfo方法来修改state里面的值,
console.log(data)
},1000)
}
}
//使用promise的把这个异步的操作包裹起来,这样再调用这个函数的可以再后面使用them方法,
actions:{
aupdataInfo(context,data){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('updataInfo')
resolve(123)
},1000)
})
}
}
9.3.5 module
Vue使用单一状态树,store对象可能变得比较复杂,为了解决这个问题,Vuex允许我们将store分割成模块,每一个模块都拥有自己的state、mutations、action、getters
//再外面定义,这样结构看着不会太乱
const moduleA = {
state:{
text:'我是模块里面的内容'
},
mutations:{
},
actions:{},
getters:{}
}
const store = new Vuex.Store({
state:{
counter:1,
stu:[{name:'kobe',age:40},{name:'why',age:18},{name:'zs',age:24},{name:'ls',age:12}],
info:{name:'kobe',age:18}
},
modules:{
a:moduleAstate
}
})
//想要拿到modules里面的state的东西,虽然是写在modules里面的,其实内部会把他全部放在state里面,所以调用里面的变量就要这样使用
$store.state.a.text1
//模块里面想要拿到根对象里面state的变量,模块里面的第三个参数rootState
rootState.根对象里面的state的变量
//模块里面的modules,执行的方法和根的一样,不要和根里面的方法名字重复,他执行的机制是,先在根里面的modules里面查找,如果有就执行,然后再模块里面再查找执行,如果名字重复会执行俩次,并以模块的modules里面的内容为最终结果
$store.commit('方法名',传递的参数)
//模块里面的getters,调用的方法也是和根对象里面的getters一样的
$store.getters.方法
//模块里面的actions,也是把异步的东西放在这里,他里面的context参数是不再是store对象了,而是他所在的模块再调用的时候也是和根对象里面的一样的
$store.dispatch('方法名')
9.4 Vuex-store的文件结构目录
- 首先再src文件夹下面创建一个store文件夹,里面再创建一个index.js的文件
- 把store对象里面的mutation、getters、actions,放在单独文件里面,
- 把modules抽出来单独放一个文件夹,再把里面的模块再文件夹里分别创建一个文件保存
vue 项目的主要目录结构
src里面的文件夹目录
- src
- assets(存放静态资源)
- img(图片类)
- css(css样式)
- normalize.css(初始化标签的一些样式,可以去网上下载)
- base.css(自己定义的初始化标签的样式)
- common(存放公共的一些js文件,比如说在js里面抽出来的常量)
- components(存放组件)
- common(存放一些公共的组件,除了在这个项目可以用还可以在别的项目使用的)
- content(存放本项目的一些公共的组件)
- network(存放网络相关的文件,比如说封装的axios)
- router(存放路由相关的文件)
- store(存放公共的数据,就是vuex相关的)
- views(存放大一点的视图组件,比如说项目的首页等等)
- App.vue(最大的组件)
- main.js(入口文件)
- assets(存放静态资源)
,count)//第一个参数填,调用的方法名称,后面可以跟传递参数
}
//第二种
addition(count){//mutations里面接收的时候这个传递过去的是一个对象
this.$store.commit({
type:‘increment’,
count
})
}
mutation的类型常量
因为在调用的时候第一个是参数是一个字符串,容易写错
1. 创建一个文件,里面声明一些变量来存储字符串
```js
//在mutation里面声明方法的时候就可以用里面的变量
//引用
import {INCREMENT} from './mutation-types.js'
mutations:{
//使用
[INCREMENT](state){
state.counter++
},
}
//在别的组件调用的时候也可以使用变量来作为第一个参数
//引入
import {INCREMENT} from '@/store/mutation-types.js'
//使用
this.$store.commit(INCREMNT)
9.3.4 actions
如果有异步的方法就写在这里面,比如说延时改变state里面的变量,把异步的代码写在action里面,然后再action里面调用mutations里面的对应的方法就行
actions:{
aupdataInfo(context,data){//默认传递一个参数context(上下文),这个参数相当于store,data是定义来接收传递过来的形参,
setTimeout(()=>{
context.commit('updataInfo')//在这里调用mutations里面的updataInfo方法来修改state里面的值,
console.log(data)
},1000)
}
}
//使用promise的把这个异步的操作包裹起来,这样再调用这个函数的可以再后面使用them方法,
actions:{
aupdataInfo(context,data){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('updataInfo')
resolve(123)
},1000)
})
}
}
9.3.5 module
Vue使用单一状态树,store对象可能变得比较复杂,为了解决这个问题,Vuex允许我们将store分割成模块,每一个模块都拥有自己的state、mutations、action、getters
//再外面定义,这样结构看着不会太乱
const moduleA = {
state:{
text:'我是模块里面的内容'
},
mutations:{
},
actions:{},
getters:{}
}
const store = new Vuex.Store({
state:{
counter:1,
stu:[{name:'kobe',age:40},{name:'why',age:18},{name:'zs',age:24},{name:'ls',age:12}],
info:{name:'kobe',age:18}
},
modules:{
a:moduleAstate
}
})
//想要拿到modules里面的state的东西,虽然是写在modules里面的,其实内部会把他全部放在state里面,所以调用里面的变量就要这样使用
$store.state.a.text1
//模块里面想要拿到根对象里面state的变量,模块里面的第三个参数rootState
rootState.根对象里面的state的变量
//模块里面的modules,执行的方法和根的一样,不要和根里面的方法名字重复,他执行的机制是,先在根里面的modules里面查找,如果有就执行,然后再模块里面再查找执行,如果名字重复会执行俩次,并以模块的modules里面的内容为最终结果
$store.commit('方法名',传递的参数)
//模块里面的getters,调用的方法也是和根对象里面的getters一样的
$store.getters.方法
//模块里面的actions,也是把异步的东西放在这里,他里面的context参数是不再是store对象了,而是他所在的模块再调用的时候也是和根对象里面的一样的
$store.dispatch('方法名')
9.4 Vuex-store的文件结构目录
- 首先再src文件夹下面创建一个store文件夹,里面再创建一个index.js的文件
- 把store对象里面的mutation、getters、actions,放在单独文件里面,
- 把modules抽出来单独放一个文件夹,再把里面的模块再文件夹里分别创建一个文件保存
vue 项目的主要目录结构
src里面的文件夹目录
- src
- assets(存放静态资源)
- img(图片类)
- css(css样式)
- normalize.css(初始化标签的一些样式,可以去网上下载)
- base.css(自己定义的初始化标签的样式)
- common(存放公共的一些js文件,比如说在js里面抽出来的常量)
- components(存放组件)
- common(存放一些公共的组件,除了在这个项目可以用还可以在别的项目使用的)
- content(存放本项目的一些公共的组件)
- network(存放网络相关的文件,比如说封装的axios)
- router(存放路由相关的文件)
- store(存放公共的数据,就是vuex相关的)
- views(存放大一点的视图组件,比如说项目的首页等等)
- App.vue(最大的组件)
- main.js(入口文件)
- assets(存放静态资源)