vue从入门到精通学习笔记

了解Vue.js

认识Vue.js

  • Vue是一个渐进式框架,什么渐进式?
    • 渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验
  • 响应式
    • vue的响应式
      • 数据发生改变的时候,页面也跟着改变
      • 可以直接在浏览器中的console下通过对象.属性 = 值(app.message = ‘hello’)来改变数据,体验vue的响应式
  • Vue的特点
    • 解耦视图和数据
    • 可复用的组件
    • 前端路由技术
    • 状态管理
    • 虚拟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://vuejs.org/js/vue.js
    生产环境	https://vuejs.org/js/vue.min.js
    
  • npm安装

    npm install vue
    

hello vue.js

  • 数据展示

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    	<script src="./node_modules/vue/dist/vue.js"></script>
    </head>
    
    <body>
    	<div id="app">{{ message }}</div>
    	<script>
    		const app = new Vue({
    			el: '#app',
    			data() {
    				return {
    					message: 'hello vue.js'
    				}
    			}
    		})
    	</script>
    </body>
    
    </html>
    

    {{}}语法:叫Mustache(玛斯塔)语法,可以进行数据计算,字符串的拼接。

    优点:可以完全的达到页面与数据的分离

  • 列表展示(for循环,item是每项的值,index是每个项的索引)

    <div id="app">
    	<ul v-for = "item in message">
    		<li>{{ item }}</li>
    	</ul>
    </div>
    <script>
    	const app = new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: ['少年的你','最好的我们','寻梦环游记','匆匆那年']
    			}
    		}
    	})
    </script>
    
  • vue案例—计数器

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    </head>
    
    <body>
    	<div id="app">
    		<!-- 方法1 -->
    		<!-- <button v-on:click="counter++">+</button>
    		<span>{{ counter }}</span>
    		<button v-on:click="counter--">-</button> -->
    		<!-- 方法2 -->
    		<input type="button" v-on:click="add" value="+" />
    		<span>{{ counter }}</span>
    		<input type="button" v-on:click="sub" value="-">
    	</div>
    	<script src="./node_modules/vue/dist/vue.js"></script>
    	<script>
    		new Vue({
    			el: '#app',
    			data() {
    				return {
    					counter: 0
    				}
    			},
    			methods: {
    				add:function() {
    					console.log('add');
    					this.counter++
    				},
    				sub:function() {
    					console.log('sub');
    					this.counter--
    				}
    			}
    		})
    	</script>
    </body>
    
    </html>
    
  • MVVM

    • Model View Model View

    • 搜索网站

      维基百科:https://www.wikipedia.org/

    • 绑定view和model实现数据的双向绑定,你变我也变

    • 解释

      • View层
        • 视图层
        • 在前端开发过程中,通常就是DOM
        • 主要作用是给用户展示各种信息
      • Model层
        • 数据层
        • 数据层是我们固定的四数据,更多的来自服务器,从网络上请求下来的数据
        • 在我们计算过程中,就是从后面抽取过来的obj
      • ViewModel层
        • 视图模型层
        • 视图模型层是视图层(View层)和模型层(Model层)沟通的桥梁
        • 一方面实现了数据的绑定,将Model层的实时改变渲染到视图层上
        • 另一方面实现了DOM监听,当DOM发生事件时,可以监听到,并在对应情况下改变对应的数据

创建vue实例传递的opactions

  1. el
    • 类型:string | HTMLElement
    • 作用:决定之后Vue实例会管理那个DOM
  2. data
    • 类型:Object | Function 在组件当中data必须是一个函数
    • 作用:Vue实例对应的数据对象
  3. methods
    • 类型:{[key:string]:Function} 函数名:函数体(function(){})
    • 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中调用

vue的生命周期

生命周期:事务从诞生到消亡的过程。

vue生命周期:创建前/后,载入前/后,更新前/后,销毁前/后。【重:面试会考】

生命周期函数,是vue自定义的公共函数,不需要创建,直接使用就🆗。

生命周期图

在这里插入图片描述

生命钩子函数

什么是生命周期函数?

比如:

beforeMount: function() {
}

// 或者
beforeMount() {
}
  • Vue的所有生命周期函数都是自动绑定到this的上下文上。所以,使用箭头函数的话,this指向的父级作用域,就会报错。

错误的形式:

mounted:() => {
}

beforeCreate

img

在实例初始化之后,数据观测和暴露了一些有用的实例属性与方法。

实例初始化——new Vue()

数据观测——在vue的响应式系统中加入data对象中所有数据,这边涉及到vue的双向绑定,可以看官方文档上的这篇深度响应式原理 深度响应式原理

暴露属性和方法——就是vue实例自带的一些属性和方法,我们可以看一个官网的例子,例子中带$的属性和方法就是vue实例自带的,可以和用户定义的区分开来

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})
复制代码

created

  • el属性对生命周期的影响

img

// 有el属性的情况下
new Vue({
el: '#app',
beforeCreate: function() {
  console.log('调用了beforeCreate')
},
created: function() {
  console.log('调用了created')
},
beforeMount: function() {
  console.log('调用了beforeMount')
},
mounted: function() {
  console.log('调用了mounted')
}
})

// 输出结果
// 调用了beforeCreate
// 调用了created
// 调用了beforeMount
// 调用了mounted
复制代码
// 在没有el属性的情况下,没有vm.$mount

new Vue({
beforeCreate: function() {
  console.log('调用了beforeCreate')
},
created: function() {
  console.log('调用了created')
},
beforeMount: function() {
  console.log('调用了beforeMount')
},
mounted: function() {
  console.log('调用了mounted')
}
})

// 输出结果
// 调用了beforeCreate
// 调用了created
复制代码
// 在没有el属性的情况下,但是有vm.$mount方法

var vm = new Vue({
beforeCreate: function() {
  console.log('调用了beforeCreate')
},
created: function() {
  console.log('调用了created')
},
beforeMount: function() {
  console.log('调用了beforeMount')
},
mounted: function() {
  console.log('调用了mounted')
}
})

vm.$mount('#app')

// 输出结果
// 调用了beforeCreate
// 调用了created
// 调用了beforeMount
// 调用了mounted
  • template属性对生命周期的影响

img

主要分三种情况:

  1. 在实例内部有template属性的时候,直接用内部的,然后调用render函数去渲染。
  2. 在实例内部没有找到template,就调用外部的html。实例内部的template属性比外部的优先级高。
  3. 要是前两者都不满足,那么就抛出错误。

我们来看以下几个例子:

new Vue({
  el: '#app',
  template: '<div id="app">hello world</div>'
})

//页面上渲染出了hello world
复制代码
<div id="app">hello world</div>

new Vue({
  el: '#app'
})

// 页面上渲染出了hello world
复制代码
//两者都存在的时候

<div id="app">hello world2</div>

new Vue({
  el: '#app',
  template: '<div id="app">hello world1</div>'
})
// 页面上渲染出了hello world1

从上述的例子可以看出内部的优先外部的。

  • 关于这个生命周期中的一些问题:

1、为什么el属性的判断在template之前? 因为el是一个选择器,比如上述例子中我们用到的最多的是id选择器app,vue实例需要用这个el去template中寻找对应的。

2、实际上,vue实例中还有一种render选项,我们可以从文档上看一下他的用法:

new Vue({
  el: '#app',
  render() {
    return (...)
  }
})

3、上述三者的渲染优先级:render函数 > template属性 > 外部html

4、vue编译过程——把tempalte编译成render函数的过程。

beforeMount和mounted

life-mounted.png

<div id="app">
  <p>{{message}}</p>
</div>

new Vue({
  el: '#app',
  data: {
    message: 1
  },
  beforeMount: function() {
    console.log('调用了beforeMount');
    console.log(this.message)
    console.log(this.$el)
  },
  mounted: function() {
    console.log('调用了mounted');
    console.log(this.message)
    console.log(this.$el)
  }
})

// 输出的结果:
// 调用了beforeMount
// 1
// <div>
// </div>

// 调用了mounted
// 1
// <div id="app">
//  <p>1</p>
// </div>

创建vue实例的$el,然后用它替代el属性。

beforeUpdate和updated

img

这个过程中,我们会发现,当一个数据发生改变时,你的视图也将随之改变,整个更新的过程是:数据改变——导致虚拟DOM的改变——调用这两个生命钩子去改变视图

  • 重点:这个数据只有和模版中的数据绑定了才会发生更新。
// 没绑定的情况

var vm = new Vue({
  el: '#app',
  template: '<div id="app"></div>',
  beforeUpdate: function() {
    console.log('调用了beforeUpdate')
  },
  updated: function() {
    console.log('调用了uodated')
  },
  data: {
    a: 1
  }
})

vm.a = 2
//这种情况在控制台中是什么都不会输出的。
复制代码
var vm = new Vue({
  el: '#app',
  template: '<div id="app">{{a}}</div>',
  beforeUpdate: function() {
    console.log('调用了beforeUpdate')
  },
  updated: function() {
    console.log('调用了uodated')
  },
  data: {
    a: 1
  }
})

vm.a = 2

// 输出结果:
// 调用了beforeUpdate
// 调用了uodated
复制代码

beforeDestory和destoryed

img

在beferoDestory生命钩子调用之前,所有实例都可以用,但是当调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

不常用的生命钩子函数

  • activated:当组件激活的时候调用
  • deactivated:当组件停用的时候调用
  • errorCaptured:这个生命钩子可以看官网,2.5.0之后才有的。当捕获一个来自子组件的错误时被调用。

let vm = new Vue({
  el: '#app',
  data: {
    message: 1
  },
  template: '<div id="app"><p>{{message}}</p></div>',
  beforeCreate() {
    console.log('调用了beforeCreate')
    console.log(this.message)
    console.log(this.$el)
  },
  created() {
    console.log('调用了created')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeMount() {
    console.log('调用了beforeMount')
    console.log(this.message)
    console.log(this.$el)
  },
  mounted() {
    console.log('调用了mounted')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeUpdate() {
    console.log('调用了beforeUpdate')
    console.log(this.message)
    console.log(this.$el)
  },
  updated() {
    console.log('调用了updated')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeDestory() {
    console.log('调用了beforeDestory')
    console.log(this.message)
    console.log(this.$el)
  },
  destoryed() {
    console.log('调用了Destoryed')
    console.log(this.message)
    console.log(this.$el)
  }
})

vm.message = 2
复制代码
  • 输出的结果:
// 调用了beforeCreate
// undefined
// undefined
// 调用了created
// 1
// undefined
// 调用了beforeMount
// 1
// <div></div>
// 调用了mounted
// 1
// <div id="app"><p>1</p></div>
// 调用了beforeUpdate
// 2
// <div id="app"><p>2</p></div>
// 调用了updated
// 2
// <div id="app"><p>2</p></div>

代码规范:缩进,一般来说是缩进4个空格,但实际上缩进2个空格会更加的规范。

Vue的基础语法

模板语法

插值操作( {{}} )

  • Mustach语法,也就是双大括号({{}})语法

    • mustach:胡子,胡须

      <div id="app">
      	<h2>{{ message }}</h2>
      </div>
      <script src="./node_modules/vue/dist/vue.js"></script>
      <script>
      	new Vue({
      		el: '#app',
      		data() {
      			return {
      				message: 'hello meustach'
      			}
      		}
      	})
      </script>
      
    • Mustach语法,可以进行数值的计算,数据的绑定,字符串的拼接

常见指令 (v-xxx)

v-once指令
  • 只显示第一次绑定的值,不会跟着用户的操作而改变

  • 元素和组件只渲染一次,不会跟着数据的改变而改变

v-html指令
  • 绑定html标签
<div id="app">
    <!-- 显示 点我  超链接   点击之后就跳转到https://pic.images.ac.cn/image/5e87056c7bec6网页 -->
	<h2 v-html = "url"></h2>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
	const app = new Vue({
		el: '#app',
		data() {
			return {
				url: `<a href = "https://pic.images.ac.cn/image/5e87056c7bec6">点我</a>`
			}
		}
	})
</script>
v-text指令
  • 绑定文本值

    <div id="app">
    	<!-- 显示 hello v-text,good -->
    	<h2>{{ message }},good</h2>
    	<!-- 显示 hello v-text -->
    	<h2 v-text = "message">good</h2>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
    	const app = new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: 'hello v-text'
    			}
    		}
    	})
    </script>
    
  • 不灵活,只能绑定确定的值,不能进行数据拼接,添加拼接值后,拼接值会被v-text的值覆盖

v-pre
  • 标签作用一样,写什么就展示什么

  • 用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法

    <div id="app">
    	<!-- 显示 hello v-text,good -->
    	<h2>{{ message }},good</h2>
    	<!-- 显示 {{ message }},good -->
    	<h2 v-pre>{{ message }},good</h2>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
    	const app = new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: 'hello v-text'
    			}
    		}
    	})
    </script>
    
v-clock
  • 在某些情况下,我们浏览器可能会直接显示出未编译的Mustach标签

  • clock:斗篷

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    	<style>
    		/* v-lock存在的时候不显示内容 */
    		[v-lock] {
    			display: none;
    		}
    	</style>
    </head>
    
    <body>
    
    	<div id="app" v-lock>
    		<!-- 显示 hello v-text,good -->
    		<h2>{{ message }},good</h2>
    		<!-- 显示 {{ message }},good -->
    		<h2 v-pre>{{ message }},good</h2>
    	</div>
    	<script src="./node_modules/vue/dist/vue.js"></script>
    	<script>
    		// 在vue解析之前,div中有一个属性v-clock
    		// vue解析完成后,div中没有v-lock属性
    		setTimeout(function () {
    			const app = new Vue({
    				el: '#app',
    				data() {
    					return {
    						message: 'hello v-text'
    					}
    				}
    			});
    		},1000);
    	</script>
    </body>
    
    </html>
    
  • 页面加载的时候起一个类似缓冲的作用

绑定属性(v-bind)

v-bind
  • 作用:动态绑定属性

  • 缩写: :

  • 预期:any(with argument) | Object(without argument)

  • 参数:attrOrProp(optional)

    
    <div id="app">
    	<!-- 通过图片地址,动态绑定元素属性,显示图片 -->
    	<!-- 图片不会显示 -->
    	<img src="imgUrl">
    	<!-- 图片会显示 -->
        <!-- v-bind的原型 --> 
    	<img v-bind:src="imgUrl" >
        <!-- v-bind的简写形式 -->   
    	<img :src="imgUrl" >
            
    	<!-- 通过 v-html 标签绑定形式,动态显示图片 -->
    	<div v-html="img"></div>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
    	new Vue({
    		el: '#app',
    		data() {
    			return {
    				// 图片地址
    				imgUrl: 'http://imgs.aixifan.com/content/2016_07_10/1468158502.gif',
    				img: '<img src="http://imgs.aixifan.com/content/2016_07_10/1468158502.gif"/>'
    			}
    		}
    	})
    </script>
    
    
v-bind的语法糖
  • 也就是v-bind的简写方式,在绑定属性之前,去掉v-bind,直接用v-bind的省略方式来绑定数据(v-bind:src -----》 :src)
动态绑定对象
class语法
<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
		<style>
			.active{
				color: red;
			}
		</style>
	</head>

	<body>
		<div id="app">
			<!-- 可以通过app.isactive来改变属性的值(布尔值),展示不同的状态 -->
			<!-- <h2 :class="{key1:value1, key2:value2}">{{name}}</h2> -->
			<!-- <h2 :class="{类名1:true, 类名2:boolean}">{{name}}</h2> -->
			<!-- 元素绑定的class和动态绑定的class在浏览器中加载会合并 -->
			<h2 class="title" :class="{active:isactive, line:isline}">{{name}}</h2>
			
			<!-- 简化写法:将绑定的class属性写成一个方法,然后绑定封装后的方法,让页面看起来更加的简洁 -->
			<h2 class="title" :class="getClass()">{{name}}</h2>
			<button type="button" v-on:click="check">按钮</button>
		</div>
		<script src="./node_modules/vue/dist/vue.js"></script>
		<script type="text/javascript">
			var app = new Vue({
				el: '#app',
				data() {
					return {
						name: '小陈',
						isactive: true,
						isline: true
					}
				},
				methods: {
					// 通过按钮点击事件来切换绑定数据的显示样式
					check: function(){
						this.isactive = !this.isactive;
					},
					getClass: function() {
						return {active:this.isactive, line:this.isline}
					}
				}
			})
		</script>
	</body>

</html>

数组语法
<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
		<style>
			.active{
				color: red;
			}
		</style>
	</head>

	<body>
		<div id="app">
			<!-- 通过数组语法绑定多个class-->
			<!-- 绑定的class属性有单引号时,当字符串解析 -->
			<!-- 在页面显示成	<h2 class="active line"></h2> -->
			<h2 :class="['active', 'line']">{{ name }}</h2>
			
			<!-- 绑定的class属性没有单引号时,当变量解析 -->
			<!-- 在页面显示成   <h2 class="aaa bbb"></h2> -->
			<h2 :class="[active, line]">{{ name }}</h2>
			
			<!-- 在页面显示成   <h2 class="aaa bbb"></h2> -->
			<h2 :class="getClass()">{{ name }}</h2>
		</div>
		<script src="./node_modules/vue/dist/vue.js"></script>
		<script type="text/javascript">
			var app = new Vue({
				el: '#app',
				data() {
					return {
						name: '小陈',
						active: 'aaa',
						line: 'bbb'
					}
				},
				methods: {
					getClass: function(){
						// 在使用方法绑定时,绑定数组中的属性值一定要加上this
						// 不加 this 就会显示active line is not defind
						// return [active, line]
						
						return [this.active, this.line]
					}
				}
			})
		</script>
	</body>

</html>
实例
  • 点击列表中的哪一项,哪一项的值就变成红色

    • 步骤
      • 创建html页面
      • 引入vue.js包
      • 添加作用域(
      • 实例化Vue({})
      • 添加挂载点(el)
      • 添加data属性及要操作的movie数组
      • 将data中movie数组中的数据渲染到页面上
      • 给li标签绑定class属性,属性的索引等于点击的li的索引
      • 添加点击事件,将绑定class属性的索引赋值给点击的索引
    <!DOCTYPE html>
    <html lang="en">
    
    	<head>
    		<meta charset="UTF-8">
    		<meta name="viewport" content="width=device-width, initial-scale=1.0">
    		<title>Document</title>
    		<style type="text/css">
    			ul li {
    				/* 鼠标移上去,添加小手样式 */
    				cursor: pointer;
    			}
    			/* 元素绑定的class样式 */
    			.active {
    				color: red;
    			}
    		</style>
    	</head>
    
    	<body>
    		<!-- 2 创建作用域 -->
    		<div id="app">
    			<ul>
    				<!--通过切换索引值改变class-->
    				<!-- 遍历显示在那个标签里面v-for循环就写在那个标签里面 -->
    				<li v-for='(item,index) in movie' @click="change(index)" :class='{ active:index === i }'>{{ item }}</li>       
    			</ul>
    		</div>
    		<!-- 1 引包 -->
    		<script src="./node_modules/vue/dist/vue.js"></script>
    		<script type="text/javascript">
    			// 3 实例化vue
    			var app = new Vue({
    				// 4 添加挂载点 el
    				el: '#app',
    				data() {
    					return {
    						// 页面加载的时候给第一个li标签添加样式
    						// 元素的索引从0开始
    						i: 0, 
    						// 页面显示的数组数据
    						movie: ['寻梦环游记', '肖申克的救赎', '摩登家庭', '老友记']
    					}
    				},
    				methods: {
    					// 元素标签点击事件
    					change: function(index) {
    						// console.log(index);
    						// 将添加class属性的索引变成用户点击的索引
    						this.i = index; 
    					}
    				}
    			})
    		</script>
    	</body>
    
    </html>
    
v-bind绑定style样式
  • 在写css属性名的时候,我们可以采用
    • 驼峰命名法(fontSize)
    • 短横线分割法(‘font-size’)
  • 绑定class有两种语法
对象语法
<h2 :style="{fontSize: end + 'px'}">{{ message }}</h2>
style后面跟的是一个对象类型
对象的key是css属性名称
对象的value是具体赋的值,值可以来自data中的属性
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
</head>
<body>
	<div id="app">
		<!-- <h2 :style="{key: value}"></h2> -->
		<!-- key是css的属性名   value是属性值 -->
		<!-- 这样编译会报错,编译的时候浏览器把50px当成一个变量编译的,但是变量不能以数组开头 -->
		<!-- <h2 :style="{font-size: 50px}">{{ message }}</h2> -->
		<!-- 解决方法: 将 50px  变成  '50px' -->
		<h2 :style="{'font-size': '50px'}">{{ message }}</h2>
		<h2 :style="{fontSize: '50px'}">{{ message }}</h2>
		
		<!-- 直接引用变量finalSize -->
		<!-- 引用的变量的值有单位,直接引用变量 -->
		<h2 :style="{fontSize: finalSize, color: finalColor }">{{ message }}</h2>
		<!-- 通过函数封装来改变样式 -->
		<h2 :style="getStyle()">{{ message }}</h2>
		<!-- 引用变量的值没有单位,在变量后面通过字符串的拼接来添加单位 -->
		<h2 :style="{fontSize: end + 'px'}">{{ message }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					message: 'HerayChen',
					finalSize: '60px',
					end: 60,
					finalColor: 'red'
				}
			},
			methods: {
				getStyle: function() {
					return {fontSize: this.finalSize, color: this.finalColor }
				}
			}
		})
	</script>
</body>
</html>
数组语法
<h2 v-bind:style="[fontColor,backgroundColor]">{{ message }}</h2>
style后面跟的是一个数组类型
多个值,分隔即可
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
	<style type="text/css">
		
	</style>
</head>
<body>
	<div id="app">
		<h2 :style="[fontColor]">{{ message }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					message: 'HerayChen',
					fontColor: {color: 'red'}
				}
			}
		})
	</script>
</body>
</html>

计算属性(computed)

计算属性的基本使用
<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<div id="app">
			<!-- 通过mustach语法实现拼接 -->
			<h2>{{ cname + ' ' + ename }}</h2>
			<h2>{{ cname }} {{ ename }}</h2>
			
			<!-- 通过函数方法实现拼接 -->
			<h2>{{ getFullName() }}</h2>
			
			<!-- 通过计算属性实现拼接 -->
			<!-- 注意:计算属性,在写的时候是通过方法组合而成的,但是在使用的时候是当成属性使用的,直接通过属性名调用即可,不需要加双小括号 -->
			<h2>{{ fullName }}</h2>
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						ename: 'HerayChen',
						cname: '肖晨'
					}
				},
				// computed 计算属性
				computed: {
                    // fullName在这里是一个属性,不是一个函数
					fullName: function() {
						return this.cname + ' ' + this.ename;
					}
				},
				methods: {
					getFullName() {
						return this.cname + ' ' + this.ename;
					}
				}
			})
		</script>
	</body>
</html>

计算属性的复杂操作(通过for循环,计算总价格及es6 中的for-in方法)
<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<div id="app">
			<h2>所有书的总价格为:{{ books[0].price + books[1].price + books[2].price + books[3].price }}</h2>
			<h2>所有书的总价格为:{{ totalPrice }}</h2>
			<h2>所有书的总价格为:{{ tprice }}</h2>
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						books: [{
							id: 202001,
							name: '月亮与六便士',
							price: 50
						}, {
							id: 202002,
							name: '独家的记忆',
							price: 35
						}, {
							id: 202003,
							name: '我在未来等你',
							price: 25
						}, {
							id: 202004,
							name: '爱',
							price: 250
						}]
					}
				},
				computed: {
					totalPrice: function() {
						let result = 0;
						for(let i = 0; i < this.books.length; i++){
							result += this.books[i].price;
						}
						return result;
					},
					tprice: function() {
						let result = 0;
						// es6中的for循环
						for (let i in this.books) {
							result += this.books[i].price;
						}
						return result;
					}
				}
			})
		</script>
	</body>
</html>
计算属性的setter和getter
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
</head>
<body>
	<div id="app">
		<h2>{{ fullName }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					cname: '肖晨',
					ename: 'HerayChen'
				}
			},
			computed: {
				// fullName: function() {
				// 	return this.cname + ' ' + this.ename;
				// },
				
				// 计算属性一般没有set方法,只读属性
				fullName: {
					set: function(newName) {
						// 只有值更改后才会调用set方法
						// 要使用set方法,set方法一定是有参数的
						// 通过空格截取修改后的值
						const names = newName.split(' ');
						// 前面的部分是cname
						this.cname = names[0];
						// 后面的部分是ename
						this.ename = name[1];
						
					},
					get: function() {
						return this.cname + ' ' + this.ename;
					}
				}
			}
		})
	</script>
</body>
</html>
计算属性和methods的对比

计算属性(computed)比methods性能更高,methods加载一次调用一次(每次都会重新计算一次),计算属性只调用一次(只计算一次)。

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
</head>
<body>
	<div id="app">
		<!-- 直接拼接 -->
		<!-- 一般不使用这种方法,语法过于繁琐 -->
		<h2>{{ cname + ' ' + ename }}</h2>
		<!-- 通过函数拼接 -->
		<!-- 调用多次多次,页面加载一次调用一次 -->
		<h2>{{ getFullName() }}</h2>
		<!-- 通过计算属性拼接 -->
		<!-- 调用多次 不管调用多少次,fullName只调用一次 -->
		<h2>{{ fullName }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					cname: '肖晨',
					ename: 'HerayChen'
				}
			},
			computed: {
				fullName: function() {
					return this.cname + ' ' + this.ename;
				}
			},
			methods: {
				getFullName: function() {
					return this.cname + ' ' + this.ename;
				}
			}
		})
	</script>
</body>
</html>

es6语法总结

let和const

var/let:变量,const:常量

  • let

    • 具有块级作用域
    • 在if,for中var,没有块级作用域,(解决作用域方法:使用闭包,使用ES6语法)
    • 使用闭包解决var中没有块级作用域的原理是,函数是一个作用域【在javascript中只有函数是有作用域的】
  • const

    • 将某个变量变成常量

    • 常量是固定值,不可以再次赋值

    • const可以保证数据的安全性

    • 在es6开发中优先使用const,只有需要改变某一个标识符的时候才使用let

      注意:
      1 常量不可以再次修改
      const name = 'herayChen';
      name = 'xiaochen0';	// 错误
      2  常量声明之后必须赋值
      const name;	//错误
      3 常量的含义是指向的对象不能修改,但是可以改变对象内部的属性
      const obj = {
          name: 'xiaochen',
          age: 20
      }
      obj.age = 18;  //正确
      
对象字面量的增强写法

在const obj = {}中,{}就是obj的字面量。

  • 属性的增强写法

    // 属性的增强写法
    const name = 'xiaochen';
    const age = 20;
    const sex = '女';
    
    // ES5中
    const obj = {
    	name: name,
    	age: age,
    	sex: sex
    }
    // ES6中
    const obj = {
    	name,
    	age,
    	sex
    }
    
  • 函数的增强写法

    // 函数的增强写法
    // ES5
    const obj = {
    	add: function() {
    		
    	},
    	reducte: function() {
    		
    	}
    }
    // ES6
    const obj = {
    	add() {
    		
    	},
    	reducte() {
    		
    	}
    }
    

    最近更新:typescript(microsoft),flow(facebook),angular(goole)【框架本身就使用的是typescript】

事件监听(v-on)

  • 作用:绑定事件监听器
  • 缩写:@click
  • 预期:Function | Inline Statement | Object
  • 参数:event
基本使用
<div id="app">
	<h2>{{ counter }}</h2>
	<!-- 
		v-bind的语法糖:
		<h2 v-bind:title="">{{ counter }}</h2>
		<h2 :title="">{{ counter }}</h2>
	 -->
	<!-- 直接操作数据 -->
	<button type="button" @click="counter++">+</button>
	<button type="button" @click="counter--">-</button>
	<!-- 绑定函数,调用 -->
	<button type="button" v-on:click="increment">+</button>
	<!-- v-on中的语法糖:@click  -->
	<button type="button" @click="decrement">-</button>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				counter: 0
			}
		},
		methods: {
			increment() {
				this.counter++;
			},
			decrement() {
				this.counter--;
			}
		}
	})
</script>
v-on参数

情况一:

​ 如果该方法不需要额外参数,那么方法后面的()可以省略,但是如果方法本身中有一个参数,那么则会默认event参数传递进去。

情况二:

​ 如果需要同时传递某一个参数,同时需要event时,剋通过$event传入事件。

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<div id="app">
			<!-- 1.事件调用的方法没有参数 -->
			<!-- @click="btnClick"  或者 @click="btnClick()" -->
			<button @click="btn1Click">按钮1</button>
			
			<!-- 2.有一个参数 -->
			<!-- 在事件定义时,写函数时省略了小括号,但是方法本身是需要一个参数的 -->
			<!-- 打印输出123 -->
			<button @click="btn2Click(num)">按钮2</button>
			<!-- 没有传递参数函数的形参是undefined -->
			<button @click="btn2Click()">按钮2.1</button>
			<!-- 打印输出MouseEvent对象 -->
			<!-- 在进行事件操作的时候浏览器会自动生成一个event对象 -->
			<!-- v-on绑定函数需要传递新参,但是没有传递形参及小括号,就输出event对象 -->
			<button @click="btn2Click">按钮2.2</button>
			
			<!-- 3.有一个及多个参数 -->
			<!-- 在方法定义时,我们需要event对象,还需要其他参数 -->
			<!-- event is not defined -->
			<!-- 在这里浏览器解析的时候是把event当成一个变量来解析的 -->
			<button @click="btn3Click(num, event)">按钮3</button>
			<!-- 在调用方法的时候如何手动获取event对象 ,通过 $event -->
			<button @click="btn3Click(num, $event)">按钮3.1</button>
			
			
		</div> 
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						num: 123
					}
				},
				methods: {
					btn1Click() {
						console.log("btnClick");
					},
					btn2Click(abc) {
						console.log(abc);
					},
					btn3Click(abc, event) {
						console.log(abc, event);
					}
				}
			})
		</script>
	</body>
</html>
v-on修饰符
<!-- 1 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 2 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 3 阻止默认行为,没有表达式 -->
<button @click.stop></button>
<!-- 4 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 5 键修饰符,键别名 -->
<button @click.enter="onEnter"></button>
<!-- 6 键修饰符,键代码 -->
<button @click.13="onEnter"></button>
<!-- 7 点击只会触发一次 -->
<button @click.once="doThis"></button>

示例:

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<div id="app">
			<!-- 1 .stop阻止事件冒泡的使用 -->
			<div @click="divClick">
				<!-- 通过 stop阻止事件向上冒泡 -->
				<button @click.stop="btnClick">按钮</button>
			</div>
			
			<!-- 2 .prevent修饰符的使用 -->
			<form action="baidu.com">
				<!-- 通过prevent阻止表单的默认提交 -->
				<input type="submit" value="提交" @click.prevent="submitClick"/>
			</form>
			
			<!-- 3 监听键盘的键帽 -->
			<input type="text" @keyup.enter="keyUp" />
			
			<!-- 4 once修饰符的使用 -->
			<button type="button" @click.once="btn2Click">提交</button>
			
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				methods: {
					divClick() {
						console.log('divClick');
					},
					btnClick() {
						console.log('btnClick');
					},
					submitClick() {
						// 自定义提交
						console.log('submitClick');
					},
					keyUp() {
						// 监听键帽,按下回车键后才打印
						console.log('keyUp');
					},
					btn2Click() {
						// 只提交一次
						console.log('btn2Click');
					}
				}
			})
		</script>
	</body>
</html>

条件判断

v-if的使用
<div id="app">
	<!--false不显示,true显示 -->
	<h2 v-if="isShow">good morning HerayChen</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				isShow: true
			}
		}
	})
</script>
v-if和v-else的使用
<div id="app">
	<!-- v-if中的条件为true时,显示v-if里面的内容 -->
	<!-- v-if中的条件为false时,显示v-else里面的内容 -->
	<h2 v-if="isShow">{{ message }}</h2>
	<h2 v-else>{{ now }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				message: 'good morning HerayChen',
				now: 'good afternoon HerayChen',
				isShow: false
			}
		}
	})
</script>
v-if,v-else-if,v-else的使用

层层判断

<div id="app">
	<!-- 1 直接在标签上使用 -->
	<h2 v-if="score >= 90">优秀</h2>
	<h2 v-else-if="score >= 80">良好</h2>
	<h2 v-else-if="score >= 60">及格</h2>
	<h2 v-else>继续加油呀!</h2>
	
	<!-- 2 通过计算属性来使用 -->
	<h1> {{ result }}</h1>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				score: 90
			}
		},
		computed: {
			result() {
				let showMessage = '';
				if (this.score >= 90) {
					showMessage = '优秀'
				} else if (this.score >= 80) {
					showMessage = '良好'
				} else if (this.score >= 60) {
					showMessage = '及格'
				} else{
					showMessage = '继续加油呀~'
				}
				return showMessage;
			}
		}
	})
</script>
条件渲染案例(条件判断)

选择不同的方式登录

在此过程中遇到的小问题:

  • 如果我们在输入内容的情况下,切换了类型,我们会发现文字依然显示之前输入的内容
  • 但是按道理讲,我们应该切换到另外一个input元素中了
  • 在另外一个input元素中,我们并没有输入内容
  • 为什么会出现这个问题呢?

问题解答:

  • 这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已存在的元素,而不是重新创建新的元素。
  • 在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。

解决方案:

  • 如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key
  • 并且需要保证key的不同
<div id="app">
	<span v-if="isUser">
		<label for="userName">手机账号</label>
		<!-- 给input标签添加key属性,防止在页面中输入了账号然后想切换登录方式,输入的数据没有清除 -->
		<!-- 这种现象的原因是:vue的虚拟DOM , 元素内部的复用问题-->
		<!-- 给input添加key属性,将key作为一个标识,决定能不能在其他地方进行使用 -->
		<!-- 当不需要在其他地方使用时,key的值一定唯一 -->
		<input type="text" id="userName" placeholder="手机账号" key="userName"/>
	</span>
	<span v-else>
		<label for="email">邮箱</label>
		<input type="text" id="email" placeholder="邮箱" key="email"/>
	</span>
	<button @click="isUser = !isUser">切换类型</button>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				isUser: true
			}
		}
	})
</script>
v-show
<div id="app">
	<!-- v-if条件为false是,包含v-if指令的元素,根本就不会存在DOM-->
	<!-- 创建 删除... -->
	<h2 v-if="isShow">{{ message }}</h2>
	
	<!-- v-show是通过使用display:none来实现的 -->
	<!-- display:block   display:none -->
	<h2 v-show="isShow">{{ message }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				message: '知识改变命运,学习成就未来',
				isShow: true
			}
		}
	})
</script>

v-if和v-show对比:

  • v-if和v-show都可以决定一个元素是否渲染
    • 选择
      • v-if条件为false时,压根不会有对应的元素在DOM中
      • v-show条件为fasle是,仅仅是将元素的display属性设置为none
      • 当需要频繁的切换显示隐藏之间切片很频繁时,使用v-show
      • 当只有一次切换时,通过使用v-if

循环遍历

v-for遍历数组
<div id="app">
	<!-- 在遍历过程中没有使用索引值 -->
	<ul>
		<li v-for="item in movies">{{ item }}</li>
	</ul>
	<!-- 在遍历过程中获取索引值 -->
	<ul>
		<li v-for="(item, index) in movies">{{ index+1 }}.{{ item }}</li>
	</ul>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				movies: ['寻梦环游记', '肖申克的救赎', '爱的冒险', '冰雪奇缘']
			}
		}
	})
</script>

v-for遍历对象
<div id="app">
	<ul>
		<!-- 在遍历对象中  item是对象中的每个属性的值 value -->
		<li v-for="item in movies">{{ item }}</li>
	</ul>
	
	<ul>
		<!-- key 属性名 -->
		<!-- item 属性值 -->
		<li v-for="(item,key) in movies">{{ key }}-{{ item }}</li>
	</ul>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				movies:{
					id: 1,
					name: '动物世界',
					actor: '李易峰',
					time: '2018-05-04'
				}
			}
		}
	})
</script>
组件的key属性
  • 官方推荐使用v-for是,给对应的元素或者组件添加上一个key属性
  • 为什么要使用key属性呢?
    • 这个其实和Vue的Diff算法有关系
  • 当某一层有很多相同的节点时,也就是列表节点是,我们希望插入一个新的节点
    • 我们希望在b和c之间插入f,Diffy算法执行起来是这样的。
    • 即把D更新成C,E更新成D,最后再插入E,这样做效率不太高
  • 所以我们需要使用key来给每一个节点做一个唯一标识
    • Diff算法就可以正确的识别此节点
    • 找到正确的位置插入新的值
  • key的作用是为了高效的更新虚拟DOM


数组中的响应式方法

1 push()
2 pop()
3 shift()
4 unshift()
5 splice()
6 sort()
7 reverse()
8 Vue中的set方法

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<div id="app">
			<ul>
				<li v-for="item in letter" :key="item">{{ item }}</li>
			</ul>
			<button type="button" @click="btnClick">按钮</button>
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						letter: ['A', 'B', 'C', 'D', 'E'],
						nums: [1, 3, 4, 77, 33, 6]
					}
				},
				methods: {
					btnClick() {
						// 1 push()
						// 在数组后添加一个元素
						// Math.random().toString(36).substr(2) 随机生成字母和数字的组合
						this.letter.push(Math.random().toString(36).substr(2));
						// 一次添加多个元素
						this.letter.push('aaa', 'bbb', 'ccc');

						// 2 pop()
						// 删除数组的最后一个元素
						this.letter.pop();

						// 3 shift()
						// 删除数组的第一个元素
						this.letter.shift();

						// 4 unshift()
						// 在数组最前面添加一个元素
						this.letter.unshift(Math.random().toString(36).substr(2));

						// 5 splice()
						// splice方法可以删除元素/插入元素/替换元素
						// 删除
						this.letter.splice(1, 2);
						// splice(1, 2);//从第一个元素开始,向后删除2个元素		显示ADE
						// 如果没有传第二个参数就删除从起始位置后的所有元素

						// 替换
						// 显示AFGHI
						// 从第一个元素开始,向后替换四个元素
						this.letter.splice(1, 4, 'F', 'G', 'H', 'I');
						
						// 插入
						// 显示ABCDFGHIE
						// 在数组的第四个元素后添加'F', 'G', 'H', 'I'
						this.letter.splice(4, 0, 'F', 'G', 'H', 'I');
						
						// 6 sort()
						// 数组的排序
						// 打印输出 [1, 3, 33, 4, 6, 77]
						console.log(this.nums.sort());
						
						// 7 reverse()
						// 翻转数组
						// 显示EDCBA
						this.letter.reverse();

						// 8 Vue中的set方法
						// set(要修改的对象, 索引值, 修改后的值)
						// 显示 bbb B C D E 
						Vue.set(this.letter, 0, 'bbbb')

						// 注意:通过索引值,更改数组中的元素,这个不是响应式的
						// this.letter[0] = 'bbb';

						// 使用可变参数(...)的函数
						// function add(...num) {
						// 	// 打印输出[1, 2, 3, 4, 4, 5, 5]
						// 	console.log(num);
						// }
						// add(1, 2, 3, 4, 4, 5, 5);
					}
				}
			})
		</script>
	</body>
</html>

阶段案例(图书购物车)

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<style type="text/css">
			table {
				border: 1px solid #e9e9e9;
				border-collapse: collapse;
				border-spacing: 0;
			}

			th,
			td {
				padding: 8px 16px;
				border: 1px solid #E9E9E9;
				text-align: center;
			}

			th {
				background-color: #7F7F7F;
				color: #5c6b77;
				font-weight: 600;
			}
		</style>
	</head>
	<body>
		<div id="app">
			<div v-if="books.length">
				<table>
					<thead>
						<tr>
							<th>编号</th>
							<th>书籍名称</th>
							<th>出版日期</th>
							<th>价格</th>
							<th>购买数量</th>
							<th>操作</th>
						</tr>
					</thead>
					<tbody>
						<tr v-for="(item, index) in books">
							<!-- 一次显示所有,不宜于做其他操作 -->
							<!-- <td v-for="value in item">{{ value }}</td> -->
							<td>{{ item.id }}</td>
							<td>{{ item.name }}</td>
							<td>{{ item.data }}</td>
							<!-- 1 使用toFixed直接保留小数 -->
							<!-- <td>{{ '¥' + item.price.toFixed(2) }}</td> -->
							<!-- 2 通过函数的封装调用保留小数 -->
							<!-- <td>{{ getFinalPrice(item.price) }}</td> -->
							<!-- 3 过滤器 -->
							<td>{{ item.price | getFinalPrice }}</td>
							<td>
								<button type="button" @click="increment(index)">+</button>
								{{ item.count }}
								<button type="button" @click="decrement(index)" :disabled="item.count <= 1">-</button>
							</td>
							<td>
								<button type="button" @click="removeHandel(index)">移出</button>
							</td>
						</tr>
					</tbody>
				</table>
				<h2>总价格:{{ totalPrice | getFinalPrice }}</h2>
			</div>
			<div v-else>
				<h2>购物车为空</h2>
			</div>

		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">

		</script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						books: [{
							id: 1,
							name: '《算法导论》',
							data: '2006-9',
							price: 85.00,
							count: 1
						}, {
							id: 2,
							name: '《计算机概论》',
							data: '2018-9',
							price: 35.00,
							count: 1
						}, {
							id: 3,
							name: '《网络技术与基础》',
							data: '2006-9',
							price: 84.00,
							count: 1
						}, {
							id: 4,
							name: '《编程珠玑》',
							data: '2008-9',
							price: 39.00,
							count: 1
						}, {
							id: 5,
							name: '《代码大全》',
							data: '2006-9',
							price: 85.00,
							count: 1
						}]
					}
				},
				computed: {
					totalPrice() {
						// 1 普通的for循环
						// let total = 0;
						// for (let i = 0; i < this.books.length; i++) {
						// 	total += this.books[i].price * this.books[i].count;
						// }
						// return total;

						// 2 es6中的for let in循环
						// let total = 0;
						// for (let i in this.books) {
						// 	// console.log(i);  i是数组的索引
						// 	total += this.books[i].price * this.books[i].count;
						// }
						// return total;

						// 3 es6中拿到数组中每一项的索引值
						// let total = 0;
						// for (let item of this.books) {
						// 	// item 是数组中每一项的值
						// 	// console.log(s);
						// 	total += item.price * item.count;
						// }
						// return total;

						// 高阶函数reduce
						return this.books.reduce(function(preVaule, book) {
							return preVaule + book.price * book.count;
						}, 0);
					}
				},
				methods: {
					// 保留小数  函数
					// getFinalPrice(price) {
					// 	return '¥' + price.toFixed(2)
					// }

					// 加法函数
					increment(index) {
						// console.log('increment',index);
						this.books[index].count++
					},
					// 减法函数
					decrement(index) {
						// console.log('decrement',index);
						this.books[index].count--
					},
					removeHandel(index) {
						this.books.splice(index, 1)
					}
				},
				// 过滤器
				filters: {
					getFinalPrice(price) {
						return '¥' + price.toFixed(2)
					}
				}
			})
		</script>
	</body>
</html>

高阶函数(filter,map,reduce)

  1. 去除所有小于100的数组
  2. 将所有小于100的数组转成: 全部乘以2
  3. 将所有new2Num的数字相加,得到最终的结果

普通js实现:

const num = [10, 20, 30, 40, 50, 200];
// 创建一个空数组
let newNum = [];
// 1 通过for循环将所有小于100的数据,添加到新数组中
for (let s of num) {
    if (s < 100) {
        newNum.push(s)
    }
}
// 创建一个空数组
let new2Num = [];
// 2 通过for循环遍历1中得到的小于100的数据,将1中的数组的每一项都乘以2
for (let n of newNum) {
	new2Num.push(n * 2);
}
console.log(new2Num);

// 3 遍历2中得到的数组,将数组中每项值相加
let total = 0;
for (let n of new2Num) {
	console.log(total += n);
}

通过高阶函数实现:

const num = [10, 20, 30, 40, 50, 200];
	// 高阶函数
	// 1 过滤
	// filter函数  必须返回一个布尔值
	// 当函数返回true时,函数内部会自动将这次回调的n加入到新的数组中
	// 当函数返回false时,函数内部会过滤掉不符合条件的n
	let newNum = num.filter(function(n) {
		// console.log(n); //filter函数会回调6次
		return n < 100;
	});
	console.log(newNum); //打印输出[10, 20, 30, 40, 50]

	// 2 map
	let new2Num = newNum.map(function(n) {
		return n * 2;
	});
	console.log(new2Num); //打印输出[20, 40, 60, 80, 100]

	// 3 reduce
	// 作用: 对数组中所有的值进行汇总
	// preVaule 上一次遍历返回的值
	// 第一次:preVaule: 0  n: 20
	// 第二次: preVaule: 第一次返回的值  n: 40
	let total = new2Num.reduce(function(preVaule, n) {
		return preVaule + n;
	}, 0);
	console.log(total);	// 打印输出300

简化高阶函数:

const num = [10, 20, 30, 40, 50, 200];
let total = num.filter(function(n) {
	return n < 100;
}).map(function() {
	return n * 2;
}).reduce(function(preVaule, n) {
	return preVaule + n;
}, 0);
console.log(total);

高阶函数和es6简化:

let total = num.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
console.log(total);

v-mode(数据的双向绑定)

  • 表单控件在实际开发中是非常常见的,特别是对于用户信息的提交,需要大量的表单

  • Vue中使用v-model指令来实现表单元素和数据的双向绑定

    <div id="app">
    	<input type="text" v-model="message" />
    	<h2>{{ message }}</h2>
    	
    	<!-- v-model的实现原理 --><!-- v-on:input监听用户输入的事件 -->
    	<!-- 1 v-bind绑定value属性 -->
    	<!-- 2 v-on指令给当前元素绑定input事件 -->
    	<input type="text" :value="name" @input="valueChange"/>
    	<input type="text" :value="name" @input="name = $event.target.value"/>
    	<h2>{{ name }}</h2>
    </div>
    <script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript">
    	new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: 'hello xiaochen',
    				name: 'Heray'
    			}
    		},
    		methods: {
    			valueChange(event) {
    				this.name = event.target.value;
    			}
    		}
    	})
    </script>
    
v-model:radio(单选框)
<div id="app">
	<label for="male">
		<!-- 单选按钮只能选择一个  添加name属性 或者 v-model属性-->
		<input type="radio" id="male" name="sex" value="男" v-model="sex"></label>
	<label for="female">
		<input type="radio" id="female" name="sex" value="女" v-model="sex"></label>
	<h2>你选择的性别是:{{ sex }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				// 给sex添加值,让页面加载的时候默认选中男
				sex: '男'
			}
		}
	})
</script>
v-model:checkbox(复选框【单个复选框,多个复选框】)
<div id="app">
	<!-- 1 checkbox单选框 -->
	<label for="agreement">
		<input type="checkbox" id="agreement" v-model="isAgreement" />同意协议
	</label>
	<h2>你的选择是: {{ isAgreement }}</h2>
	<!-- 只有统一协议才能下一步,isAgreement为false时下一步按钮是禁用的状态 -->
	<button type="button" :disabled="!isAgreement">下一步</button>
	<hr />
	<input type="checkbox" value="篮球" v-model="hobbies">篮球
	<input type="checkbox" value="滑冰" v-model="hobbies">滑冰
	<input type="checkbox" value="街舞" v-model="hobbies">街舞
	<input type="checkbox" value="唱歌" v-model="hobbies">唱歌
	<input type="checkbox" value="滑板" v-model="hobbies">滑板
	<h2>你的爱好是:{{ hobbies }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				// 单选框对应的是一个布尔值
				isAgreement: false,
				// 多选框对应的是一个数组
				hobbies: []
			}
		}
	})
</script>
v-model:select(一个,多个)
<div id="app">
	<!-- 1 选择一个 -->
	<select name="selected" v-model="fruit">
		<option value="苹果">苹果</option>
		<option value="香蕉">香蕉</option>
		<option value="梨子">梨子</option>
		<option value="樱桃">樱桃</option>
		<option value="草莓">草莓</option>
		<option value="车厘子">车厘子</option>
	</select>
	<h2>你喜欢的水果是:{{ fruit }}</h2>
	<hr />
	<!-- 2 选择多个 -->
	<select name="selected" v-model="fruits" multiple="multiple">
		<option value="苹果">苹果</option>
		<option value="香蕉">香蕉</option>
		<option value="梨子">梨子</option>
		<option value="樱桃">樱桃</option>
		<option value="草莓">草莓</option>
		<option value="车厘子">车厘子</option>
	</select>
	<h2>你喜欢的水果是:{{ fruits }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				fruit:'草莓',
				fruits:[]
			}
		}
	})
</script>
值绑定
<div id="app">
	<label  v-for="(item, index) in hobbies" :for="index">
		<input type="checkbox"  :value="item" :id="index" v-model="hobbies">{{ item }}
	</label>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				hobbies: ['街舞', 'rapper', '滑板', '拉丁']
			}
		}
	})
</script>
修饰符

lazy修饰符:

  • 默认情况下,v-model默认是在input时间中同步输入框的数据的
  • 一般来说,就是一旦有数据发生改变对应的data中的数据就会自动发生改变
  • lazy修饰符可以让数据在失去焦点或者回车时才会更新

number修饰符:

  • 默认情况下,在输入框中无论我们输入的师叔祖还是字母,都会被当作字符串类型进行处理
  • 但如果我们希望处理的是数字类型,那么最好直接将内容当作数字处理
  • number修饰符可以让输入框中输入的内容自动转成数字类型

trim修饰符:

  • 如果用户输入的内容首尾有跟多空格,通常我们希望去掉
  • trim修饰符可以过滤内容左右两边的空格
<div id="app">
	<!-- 1 lazy修饰符 -->
	<!-- 在失去焦点和回车后更新绑定的数据 -->
	姓名:<input type="text" v-model.lazy="name"/>
	<h2>{{ name }}</h2>
	
	<!-- 2 number修饰符 -->
	<!-- 不管怎么输入,都只显示number类型的数据 -->
	<!-- <input type="number" v-model="age"/>输入的是数字,但是显示出来的数据是string类型的 -->
	年龄:<input type="text" v-model.number="age"/>
	<h2>{{ age }}-{{ typeof age }}</h2>
	
	<!-- 3 trim修饰符 -->
	<!-- 去除输入内容的前后空格 -->
	爱好:<input type="text" v-model.trim="hobby"/>
	<h2>{{ hobby }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				name: 'xiaochen',
				age: 18,
				hobby: ''
			}
		}
	})
</script>

组件开发

什么组件化?

一个完成的页面分成很多个组件,每个组件都用于实现页面的一个功能块,每个组件又能进行细分

Vue组件化思想

  • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
  • 任何应用都会被抽象成一颗组件树
    • 数据结构:数组/栈/堆/树结构

组件化思想的应用:

  1. 有了组件化的思想,我们在之后的开发中就要充分的利用它
  2. 尽可能的将页面拆分成一个个小的,可复用的组件
  3. 这样让我们的代码更加方便组织和管理,并且扩展性也更强

注册组件的基本步骤

组件的使用分成三个步骤:

  1. 创建组件构造器
  2. 注册组件
  3. 使用组件

在这里插入图片描述

<div id="app">
	<!-- 3 使用组件 -->
	<my-info></my-info>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 1 创建组件构造器对象
	const cnp = Vue.extend({
		// 自定义组件的模板
        template: `
			<div>
				<h2>标题</h2>
				<p>内容</p>
				<p>尾部</p>
			</div>
		`
	})
	<!-- // 2 注册组件 -->
    //Vue.component('注册组件的标签名',组件构造器名)
	Vue.component('my-info',cnp)
	new Vue({
		el: '#app',
		data() {
			return {

			}
		}
	})
</script>

全局组件和局部组件

<div id="app">
	<!-- 3 使用全局组件 -->
	<my-info></my-info>
	<!-- 使用局部组件 -->
	<cpns></cpns>
</div>
<div id="app2">
	<!-- 使用全局组件 -->
	<my-info></my-info>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 1 创建组件构造器对象
	const cnp = Vue.extend({
		template: `
			<div>
				<h2>标题</h2>
				<p>内容</p>
				<p>尾部</p>
			</div>
		`
	})
	<!-- // 2 注册组件(全局组件,意味着可以在多个Vue的实力下面使用) -->
	Vue.component('my-info',cnp)
	new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		<!-- 自定义局部组件,局部组件只能在挂载点范围之内使用 -->
		components: {
			<!-- cpns使用组件的标签名 -->
			cpns: cnp
		}
	})
	new Vue({
		el:'#app2'
	})
</script>

父组件和子组件

<div id="app">
	<!-- <dad></dad> -->
	<brother></brother>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 第一个组件构造器
	const dad = Vue.extend({
		template: 
		`
			<div>
				<h2>头部</h2>
				<div>身体</div>
				<div>脚部</div>
			</div>
		`
	})
	<!-- // 第二个组件构造器 -->
	const brother = Vue.extend({
		template: 
		`
			<div>
				<h2>head</h2>
				<div>body</div>
				<div>foot</div>
				<dad></dad>
			</div>
		`,
		components: {
			<!-- 在brother组件里面使用dad组件里面的内容 -->
			<!-- 然后使用brother组件,就可以显示brother组件和dad组件的内容 -->
			<!-- 要想使用组件必须注册组件 -->
			<!-- 组件名:组件模板 -->
			dad: dad
		}
	})
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- dad: dad, -->
			brother: brother
		}
	})
</script>

注册组件的语法糖

<div id="app">
	<!-- 全局组件 -->
	<daD></daD>
	<!-- 局部组件 -->
	<cpn2></cpn2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 注册全局组件
	const dad = Vue.component('dad', {
		template:
		`
			<div>
				<h2>我是dad模板中全局组件的内容</h2>
			</div>
		`
	});
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- 注册局部组件 -->
			'cpn2': {
				template:
				 `
				<div>
					<h2>我是dad模板中局部组件的内容</h2>
				</div>
				`
			}
		}
	});
</script>      

模板的分离写法

模板的分离写法:

<div id="app">
	<!-- 全局组件 -->
	<daD></daD>
	<!-- 局部组件 -->
	<cpn2></cpn2>
</div>

<!-- 1 通过script标签提取模板 -->
<!-- 注意类型必须是:text/x-template -->
<script type="text/x-template" id="dad">
	<div>
		<h2>我是dad模板中全局组件的内容</h2>
	</div>
</script>

<!-- 2 通过template标签 -->
<template id="cpn2">
	<div>
		<h2>我是dad模板中局部组件的内容</h2>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 注册全局组件
	const dad = Vue.component('dad', {
		// 通过id引用模板
		template: `#dad`
	});
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- 注册局部组件 -->
			'cpn2': {
				template:`#cpn2`
			}
		}
	});
</script>

组件可以访问Vue实例数据

  • 组件是一个单独功能模块的封装:
    • 这个模块有属于自己的HTML模板,也应该有属性自己的数据data
  • 组件里面是不能访问vue实例里面的数据的。

组件数据的存放:

  1. 组件对象也有一个data属性(也可以有methods等属性)
  2. 只是这个data属性必须是一个函数
  3. 而且这个函数返回一个对象,对象内部保存着数据
<div id="app">
	<!-- 全局组件 -->
	<daD></daD>
	<!-- 局部组件 -->
	<cpn2></cpn2>
</div>

<!-- 1 通过script标签提取模板 -->
<!-- 注意类型必须是:text/x-template -->
<script type="text/x-template" id="dad">
	<div>
<h2>{{ message }}</h2>
</div>
</script>

<!-- 2 通过template标签 -->
<template id="cpn2">
	<div>
		<h2>{{ message }}</h2>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 注册全局组件
	const dad = Vue.component('dad', {
		// 通过id引用模板
		template: `#dad`,
		data() {
			return {
				message: '我是dad模板中全局组件的内容'
			}
		}
	});
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- 注册局部组件 -->
			'cpn2': {
				template: `#cpn2`,
				data() {
					return {
						message: '我是dad模板中局部组件的内容'
					}
				}
			}
		}
	});
</script>

组件中的数据存放为什么是一个函数(面试可能会问)

函数每次都会返回一个新的新的对象,产生不同的数据。

对象每次都作用在一个属性上,一个改变所有的都改变({}可能会相互影响)。

<div id="app">
	<cpn></cpn>
	<cpn></cpn>
</div>
<!-- 组件模板 -->
<template id="cpn">
	<div>
		<h2>当前计数:{{ num }}</h2>
		<button type="button" @click="increment">+</button>
		<button type="button" @click="decrement">-</button>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 1 注册组件
	Vue.component('cpn', {
		template: `#cpn`,
		// data是一个函数,函数每次都会返回一个新的对象
		// 这样同时使用一个组件,点击一个组件,就不会改变其他组件的值
		
		// 如果data {} 返回的是一个对象,每次作用的都是一个同养的对象,一个改变其他的都改变
		// 使用{},会产生连锁反应
		data() {
			return {
				num: 0
			}
		},
		methods: {
			increment() {
				this.num++
			},
			decrement() {
				this.num--
			}
		}
	})
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		}
	})
</script>

父子组件的通信

通信:相互交流,数据传递。在大组件(父组件)获取所有的后台数据,然后在传递给小组件(子组件),渲染到页面。

如何进行父子组件的通信?

  1. 通过props(是properties属性的缩写)向子组件中传递数据
  2. 通过自定义事件向父组件发送消息

父传子props

<div id="app">
	<!-- 在父组件里面使用子组件 -->
	<!-- 在页面中显示[ "速度与激情", "肖申克的救赎", "友情之上", "摩登少年" ]  和  hello -->
	<cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
	
	<!-- 在传值后,如果没有用v-bind,则会把绑定字段,当成字符串解析 -->
	<!-- 在页面中显示movies message -->
	<cpn cmovies="movies" cmessage="message"></cpn>
</div>

<template id="cpn">
	<div>
		<p>{{ cmovies }}</p>
		<!-- 循环遍历父组件中传递过来的数据 -->
		<ul>
			<li v-for="item in cmovies">{{ item }}</li>
		</ul>
		<h2>{{ cmessage }}</h2>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 自定义组件
	const cpn = {
		template: `#cpn`,
		// 父传子props
		// 1 数组
		props: ['cmovies','cmessage'],
		// 2 对象
		prpos: {
			// 1 类型的限制
			// cmovies: Array,
			// cmessage: String
			
			// 2 提供一些默认值,以及必传值
			cmessage: {
				type: String,
				// 没有传值提供的默认值
				default: 'aa',
				// 传值时cmessage是必须传的值
				required: true
			},
			cmovies: {
				type: Array,
				// 类型是对象或者数组时,默认值必须是一个函数
				default() {
					return []
				}
			}
		}
	}
	
	const app = new Vue({
		el: '#app',
		data() {
			return {
				message: 'hello',
				movies:['速度与激情', '肖申克的救赎', '友情之上', '摩登少年']
			}
		},
		components: {
			cpn
		}
	})
</script>

在这里插入图片描述

props数据验证

验证支持的数据类型:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  • 自定义数据类型
Vue.component('my-component',{
    props: {
        //基础的类型检查(null,匹配任何数)
        propA: Number,
        //多个可能的类型
        propB: [String, Number],
        //必填的字符
        propC: {
            type: String,
            required: true
        },
        //带有默认值的数字
        propD: {
            type: Number,
            default: 100
        },
        // 带有默认值的对象
        propE: {
            type: Object,
            default() {
                // 返回一个默认值
                return { message:'hello' }
            }
        },
        // 自定义验证函数
        propF: {
            validator(value){
                //这个值必须匹配下列字符中的一个
                return ['success', 'warning', 'danger'].indexOf(value) !== -1
            }
        }
    }
})

自定义数据类型:

function Person(firstName, lastName){
    this.firstName = firstName
    this.lastName = lastName
}
Vue.component('blog-post', {
    props: {
        // 自定义数据类型
        author: Person
    }
})

props中的驼峰标识

在子组件中,props中的数据使用了驼峰命名,在父组件中使用必须去掉驼峰(遇到大写字母在前面加-分隔,然后将大写字母变成小写字母)

<div id="app">
	<!-- 子组件是在父组件中有使用的 -->
	<!-- 在子组件上绑定(v-bind)父组件传递过来的数据 -->
	<!-- v-bind不支持驼峰命名 -->
	<!-- <cpn :cinfo = "info"></cpn> -->
	
	<!-- props中绑定的数据是cInfo,使用了驼峰必须 -->
	<!-- 使用是必须将 cInfo  写成 c-info-->
	<!-- 遇到大写字母在前面加-,然后将其变成小写使用 -->
	<cpn :c-info = "info"></cpn>
</div>
<template id="cpn">
	<!-- 子组件的模板中有很多个标签,必须有一个跟标签将其包裹 -->
	<div>
		<!-- 使用父组件中传递过来的数据 -->
		<!-- <h2>{{ cinfo }}</h2> -->
		
		<!-- props中使用了驼峰 -->
		<!-- 在页面中显示 { "name": "萧辰", "age": 18, "hobbie": [ "街舞", "吉他", "滑板" ] } -->
		<h2>{{ cInfo }}</h2>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 子组件
	const cpn = {
		template: `#cpn`,
		props: {
			// cinfo: {
			cInfo: {	
				type:Object,
				default() {
					return {
						
					}
				}
			}
		}
	};
	// 父组件
	const app = new Vue({
		el: '#app',
		data() {
			return {
				info: {
					name: '萧辰',
					age: 18,
					hobbie: ['街舞', '吉他', '滑板']
				}
			}
		},
		components: {
			cpn
		}
	})
</script>

子组件向父组件中传递数据(自定义事件)

自定义事件的流程:

  1. 在子组件中,通过$emit()来触发时间
  2. 在父组件中,通过v-on来监听子组件事件
<!-- 父组件模板 -->
<div id="app">
	<!-- 监听v-bind子组件发射出的时间 -->
	<!-- 子组件监听子组件传递过来的事件时绑定的事件不能使用驼峰命名 -->
	<!--  cpnClick(item) 写成 cpnClick这样也是可以的,从子组件传递过来的方法,会默认将参数也传递过来,就像event一样-->
	<cpn @itemclick="cpnClick(item)"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
	<div>
		<button v-for="item in cartegories" @click="btnClick(item)">{{ item.name }}</button>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 子组件
	const cpn = {
		template: '#cpn',
		data() {
			return {
				cartegories:[
					{
						id: 'a',
						name: '热门推荐'
					},
					{
						id: 'b',
						name: '手机数码'
					},{
						id: 'c',
						name: '家用家电'
					},{
						id: 'd',
						name: '手机办公'
					},{
						id: 'e',
						name: '精选服饰'
					}
				]
			}
		},
		methods: {
			btnClick(item) {
				// 查看用户点击的信息
				// console.log(item);
				// 将用户点击的信息传到父组件中
				// 向父组件中发射自定义事件事件,及传递的参数
				this.$emit('itemclick', item)
			}
		}
	}
	// 父组件
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			cpn
		},
		methods: {
			// 父组件事件
			cpnClick(item) {
				console.log(item);
			}
		}
	})
</script>

在这里插入图片描述

父子组件通信实例

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<div id="app">
			<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
		</div>
		<!-- 子组件模板 -->
		<template id="cpn">
			<div>
				<h2>props1:{{ number1 }}</h2>
				<h2>data:{{ cnumber1 }}</h2>
				<!-- <input type="text" v-model="cnumber1"/> -->
				<input type="text" :value="cnumber1" @input="num1Input" />
				<h2>props2:{{ number2 }}</h2>
				<h2>data:{{ cnumber2 }}</h2>
				<!-- <input type="text" v-model="cnumber2"/> -->
				<input type="text" :value="cnumber2" @input="num2Input" />
			</div>
		</template>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			const app = new Vue({
				el: '#app',
				data() {
					return {
						num1: 1,
						num2: 0
					}
				},
				methods: {
					num1change(value) {
						this.num1 = parseInt(value);
					},
					num2change(value) {
						this.num2 = parseInt(value);
					}
				},
				components: {
					cpn: {
						template: `#cpn`,
						props: {
							number1: Number,
							number2: Number
						},
						data() {
							return {
								cnumber1: this.number1,
								cnumber2: this.number2
							}
						},
						methods: {
							num1Input(event) {
								// 1 将input中的value赋值到cnumber1
								this.cnumber1 = event.target.value;

								// 2 为了让父组件可以修改值,发出一个事件
								this.$emit('num1change', this.cnumber1);

								// 3 同时修改cnumber2的值
								this.cnumber2 = this.cnumber1 * 100;
								this.$emit('num2change', this.cnumber2)
							},
							num2Input(event) {
								this.cnumber2 = event.target.value;
								this.$emit('num2change', this.cnumber2);

								// 3 同时修改cnumber2的值
								this.cnumber1 = this.cnumber2 / 100;
								this.$emit('num1change', this.cnumber1)
							}
						}
					}
				}
			})
		</script>
	</body>
</html>

在这里插入图片描述

父子组件的访问方式

父组件访问子组件 c h i l d r e n 或 children或 childrenrefs

  1. $children 得到的是一个 数组

    this.$children[].访问对象
    
  2. $refs 得到的是一个 对象

    this.$refs.添加ref的数值.访问对象
    
  3. 实例

<div id="app">
	<cpn></cpn>
	
	<cpn ref="aaa"></cpn>
	<button type="button" @click="btnClick">点我</button>
</div>
<template id="cpn">
	<div>
		我是子组件
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		methods: {
			btnClick() {
				// 1 通过$children去拿子组件中的值
				// // 拿到的子组件是一个数组类型的
				// // console.log(this.$children);
				// // 通过$children调用子组件的showMessage方法
				// // this.$children[0].showMessage();
				// // this.$children[0].name;
				// for (let c of this.$children) {
				// 	// 打印输出showMessage
				// 	c.showMessage();
				// 	// 打印输出xiaochen
				// 	console.log(c.name);
				// }
				
				// 2 通过$refs去拿子组件中的值
				// this.$refs默认是一个空对象
				console.log(this.$refs);
				// 要使用$refs取值,需要在标签上面添加ref属性,然后通过this.$refs.ref属性值拿值
				// 打印输出 showMessage
				this.$refs.aaa.showMessage();    
				// 打印输出 xiaochen
				console.log(this.$refs.aaa.name);    
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						name: 'xiaochen'
					}
				},
				methods: {
					showMessage() {
						console.log('showMessage');
					}
				}
			}
		}
	})
</script>

子组件访问父组件 p a r e n t , parent, parentroot

  1. $parent

    this.$parent.访问父组件的属性
    
  2. $root

    // 访问根组件,Vue实例
    this.$root
    

实例:

<div id="app">
	<cpn>
	</cpn>
</div>
<template id="cpn">
	<div>
		<h2>我是cpn组件</h2>
		<ccpn></ccpn>
	</div>
</template>
<template id="ccpn">
	<div>
		<h2>我是子组件</h2>
		<button @click="btnClick">按钮</button>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {
				
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						name: '我是cpn组件的name'
					}
				},
				components: {
					ccpn: {
						template: `#ccpn`,
						methods: {
							btnClick() {
								// 1 访问父组件$parent
								// 打印出来是一个VueComponent对象
								console.log(this.$parent);
								// 打印输出 我是cpn组件的name
								console.log(this.$parent.name);
								
								// 2 访问根组件$root
								// 打印Vue 实例
								console.log(this.$root);
							}
						}
					}
				}
			}
		}
	})
</script>

插槽slot(组件化高级)

为什么使用组件的插槽:

  • 为了让我们封装的组件更加具有扩展性
  • 让使用者可以决定组件内部的一些内容到底展示什么

插槽的基本使用

使用slot插槽,占据位置,在使用的时候传入对应的值,然后传入的值自己会填补插槽占据的位置。

插槽的基本使用:

  1. 插槽的基本使用
  2. 插槽的默认值
  3. 如果有多个值,同时放入组件进行替换时,一起作为替换元素
<div id="app">
	<cpn></cpn>
	<hr />
	<cpn>
		<button type="button">点我</button>
	</cpn>
	<hr />
	<cpn><b>我是传入值</b></cpn>
</div>
<template id="cpn">
	<div>
		<h2>我是组件</h2>
		<slot></slot>
		<h5>content</h5>
		<!-- 给插槽添加一个默认值,调用的时候没有加别的参数,就使用默认值,有参数,就是用传递的值 -->
		<slot><button>我是插槽传递的默认值</button></slot>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			cpn: {
				template: `#cpn`
			}
		}
	})
</script>

如何封装插槽

抽取共性,保留不同。

  • 最好的方式就是将共性抽取到组件中,将不同暴露为插槽
  • 一旦预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容
  • 是搜索框,还是文字,还是菜单,由调用者自己来决定

具名插槽

给插槽设置name值,然后使用的使用根据solt等于的name值进行替换。

<div id="app">
	<cpn>
		<!-- 替换name为center插槽 -->
		<h1 slot="center">标题</h1>
	</cpn>
</div>
<template id="cpn">
	<div>
		<slot name="left">
			<h2>我是左边插槽</h2>
		</slot>
		<slot name="center">
			<h2>我是中间插槽</h2>
		</slot>
		<slot name="right">
			<h2>我是右边插槽</h2>
		</slot>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			cpn: {
				template: `#cpn`
			}
		}
	})
</script>

编译的作用域

父模板的所有东西都会在父级作用域内编译,子组件模板的所有东西都会在自己作用域内编译。

<div id="app">
	<!-- 这个是根据实例里面的isShow来决定的 -->
	<!-- 命令使用时不关心组件的位置,只关心作用域 -->
	<!-- 显示  app的data中的isShow是true -->
	<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
	<div>
		<h2>我是子组件</h2>
		<!-- 不显示 -->
		<!-- cpn组件中的isShow是false -->
		<button v-show="isShow"></button>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {
				isShow: true
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						isShow: false
					}
				}
			}
		}
	})
</script>

作用域插槽

作用域插槽的目的就是:

父组件替换插槽的标签,但是内容由子组件来提供。

实例:

  • 子组件中包括一组数据,比如:pLanguages[‘Javascript’, ‘Python’, ‘Swift’, ‘Go’, ‘C++’]

  • 需要在多个界面进行展示:

    • 某些界面是以水平方向——展示的
    • 某些界面是以列表形式展示的
    • 某些界面直接展示——一个数组
  • 内容在子组件,希望父组件告诉我们如何展示?

    • 利用slot插槽就可以了
<div id="app">
	<!-- 子组件默认展示 -->
	<cpn></cpn>
	
	<!-- 拿到子组件中的数据,然后在父组件中展示成不同的样式  -->
	<!-- 目的:获取子组件中的pLanguages -->
	<cpn>
		<template slot-scope="slot">
			<!-- 通过slot引用插槽对象 -->
			<!-- 通过slot.data     拿到的就是  子组件中传递的:data="pLanguages" -->
			<span v-for="item in slot.data">{{item}}  -</span>
			<div>{{ slot.data.join(' - ')}}</div>
		</template>
	</cpn>
</div>
<template id="cpn">
	<div>
		<!-- 这里data可以为任何值  -->
		<slot :data="pLanguages">
			<ul>
				<!-- 子组件展示 -->
				<li v-for="item in pLanguages">{{ item }}</li>
			</ul>
		</slot>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {
				isShow: true
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						pLanguages: ['Javascript', 'Python', 'Swift', 'Go', 'C++']
					}
				}
			}
		}
	})
</script>

模块化开发

js原始功能:

  • 做表单验证活动话
  • 随着ajax的出现,前后端开始分离,为了应对代码量的与日俱增,将js分成多个js文件来编辑
  • 但这种方式依然不能避免一些灾难性问题:
    • 全局变量同名
    • js文件过多,执行顺序的问题
  • 解决:使用闭包()() ————》代码不可复用
    • 将每一个js文件封装成一个闭包,在闭包中通过创建空对象,将使用的值存入空对象中,然后return回去
    • 在别的js文件使用的时候,通过封装的闭包名.要使用的对象

常见的模块化规范:

CommonJS,AMD,CMD,也有ES6中的Modules

CommonJS只是一个规范,CommonJS的实现是node.js。

CommonJS

模块化的两个核心:导出(export)和导入(require)

CommonJS的导出:

moule.export = {
	name: 'xiaochen',
	doit(a, b) {
		return a + b;
	}
}

CommonJS的导入:

//CommonJS模块
let { name,doit } = require('moduleA');

//等同于
let m = require('moduleA');
let name = m.name;
...

ES6的模块化实现

export

export指令用于导出变量:

export let name = 'xiaochen'
export let age = 18
export let dream = 'free'

也可以:

let name = 'xiaochen'
let age = 18
let dream = 'free'

export {
	name, age, dream
}

导出函数和类:

export function sum(a, b) {
    return a + b;
}

export class Person {
    run() {
        console.log('想上学的第一天');
    }
}

import {sum, Person} from './a.js'
// 实例化类
const p = new Person();
// 调用类里面的函数
p.run();

export default:

某些情况下,一个模块包含某个功能,我们并不希望给这个功能命名,而且让导入这自己来命名

  • 这个时候就可以使用 export default
  • 注意:export default 在同一个模块中,不允许同时存在多个
const time = '二零二零年四月七日'

// 1 
export { time }
// 2
export const time = '二零二零年四月七日'
//要使用上面的数据
import {time} from 'a.js'

// 在一个js中文件中,只能有一个default导出
export default time
import t from 'a.js'

import

export指令导出了模块对外提供的接口后,我们就可以通过import命令来加载这个对应的模块了

  • 首先我们需要在HTML代码中引入两个js文件,并且类型需要设置module

    <script src = "info.js" type = "module"></script>
    <script src = "main.js" type = "module"></script>
    
  • import指令用于导入模块内容,比如mian.js的代码

    import {name, age, sex} from './info.js'
    console.log(name, age, sex);
    
  • 统一全部导入

    import * as info from './info.js'
    console.log(info.name);
    

webpack

认识webpack

什么是webpack?

官网:webpack.js.org

从本质上将,webpack是一个现代的JavaScript应用的静态模块打包工具。

  • 静态
  • 打包

和grunt/gulp的对比

  • grunt/gulp的核心是Task
    • 我们可以配置一系列的task,并且定义task要处理的事物(例如ES6,ts转换,图片的压缩,scss转化成css)
    • 之后让grunt/gulp来依次执行这些task,而且让它流程自动化
    • grunt/gulp也被称为前端自动化任务管理工具
  • 什么时候用到grunt/gulp
    • 如果工程模块依赖非常简单,甚至是没有用到模块化的概念
    • 只要进行简单的合并,压缩,就使用grunt/gulp即可
    • 但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack
  • gulp和webpack的区别
    • grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心
    • webpack更加强调模块化开发管理,而文件压缩合并,预处理功能,是他附带的功能

webpack的安装

  • webpack是基于node.js的
  • node为了可以正常的运行执行很多代码面必须其中包含很多包
  • 在安装node时,会自动安装npm(node package manager)

查看node版本:

node -v

全局安装webpack(这里我们先制定版本号3.6.0,因为vue cli2依赖该版本):

  1. 查看电脑中有没有安装过webpack

    webpack --version  或者    webpack -v
    
  2. 没有 安装

    全局安装webpack:

    npm install webpack@3.6.0 -g
    

    本地安装webpack:

    --save-dev是开发时依赖【devDependencies】,项目打包后不需要继续使用

    npm install webpack@3.6.0 --save-dev
    

webpack的起步

文档解释:

dist文件distribution(发布)
文件解释
src文件主要文件,一般项目的文件都写在src这个文件夹里面

打包:

// webpack  打包文件main.js的路径   打包成dist下面的bundle.js文件
// webpack在打包的时候会自动处理依赖文件
// 所以只需要打包入口文件就好了
webpack ./src/main.js ./dist/bundle.js

webpack的配置

配置webpack让项目在运行的时候,直接webpack就可以运行项目,不需要webpack ./src/main.js ./dist/bundle.js这样运行

步骤:

  1. 在项目文件中创建webpack.config.js文件

  2. 给webpack.config.js添加配置

    // path 依赖了node中的path包,所以要安装node中的所有依赖项
    // npm init --yes  安装所有依赖包
    // 就会去全局找path
    cost path = require('path');
    
    module.exports = {
    	// 入口
    	entry: './src/main/.js',
    	// 出口
    	output: {
    		// 路径  path  是绝对路径
    		// 动态获取路径
    		path: path.resolve(__dirname, 'dist'),
    		// 文件名   bundle  打包
    		filename: 'bundle.js'
    	}
    }
    
  3. pacjage.json中自定义启动

    为了在命令行工具中能够 通过 npm run build 运行项目,在package.json文件的script脚本中添加依赖项

    让我们在执行npm run build命令的时候来package.json文件中找到webpack

    当在命令行工具中执行npm run build的时候相当于执行webpack命令,运行的时候num run build会先去查找本地的webpack,这样防止因webpack版本的问题而引发错误

    "build": "webpack"
    

    通过npm run build打包后的文件是全局的

loader的使用

  • loader是webpack中一个非常核心的概念

  • webpack用来做什么呢?

    • 在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖
    • 但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css,图片,也包括一些高级的将ES6转成ES5的代码,将scss,less转成css,将.jsx,vue文件转成js文件等等。
    • 对于webpack本身的能力来说,对于这些转化是不支持的
    • 那怎么办呢?给webpack扩展对应的loader就可以了。
  • loader使用过程:

    • 步骤一:通过npm安装需要使用的loader

      • css-loader

        npm install --save-dev css-loader
        
      • style-loader

        npm install --save-dev style-loader
        
    • 步骤二:在webpack.config.js中的modules关键字下进行配置

      在module中css-loader只负责将css文件进行加载,style-loader将样式添加到DOM中

      使用多个loader时,是从右向左

      module: {
          rules: [
              {
                  test: /\.css$/
                  use: ['style-loader', 'css-loader']
              }
          ]
      }
      
    • 步骤三:配置,也就是再次打包,npm run build

less文件处理–准备

要想使用less文件更改样式要安装less-loader:

npm install --save-dev less-loader less

在webpack.config.js文件中配置less:

// webpack.config.js
module.exports = {
    ...
    module: {
        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
            }]
        }]
    }
};

图片配置

下载url-loader:

npm install --save-dev url-loader

配置url-loader:

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // 当加载的图片,小于limit时,会将图片编译成base64字符串形式
              // 当加载的图片,大于limit时,会使用file-loader模块进行加载,通过webpack打包后会编译成base64字符串的形式
              limit: 8192	// 8192 8kb
              
              // 打包后webpack自动帮我们生成一个非常长的名字,但真是开发中,我们可能对打包图片的名字有一定的要求,比图将所有的图片放在一个文件夹中,跟图片原来的名称,同时也要防止重复
               // [name]:图片原来的名字   [hash:8]:哈希值保留8位  [ext]:图片的扩展名
               name: 'img/[name].[hash:8].[ext]'
            }
          }
        ]
      }
    ]
  }
}

安装file-loader:

npm install --save-dev file-loader

安装后,文档中使用的图片是打包后dist文件夹中的图片,

在webpack.config.js文件配置图片使用的路径:

在output中加入publicPath

output: {
    publicPath: 'dist/'
}

ES6语法处理

ES6的语法直接转成ES5,需要使用babel。

在webpack中直接使用babel对应的loader就可以了。

安装:

npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

配置webpack.config.js文件:

env:environment环境的缩写。

module: {
  rules: [
    {
      test: /\.js$/,
      // exclude:排除
      // include:包含
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['es2015']
        }
      }
    }
  ]
}

webpack中配置Vue

  • 在项目中使用vue.js进行开发,那么必须对其有依赖,所以我们先要进行安装:

    --save dev是开发时依赖,因为我们后续是在实际项目中使用vue的,所以并不是开发时依赖,使用--save

    npm install vue --save
    
    // 简写
    npm install vue -S
    
  • 使用vue进行开发

    // 导入vue
    import Vue from 'vue'
    
    //使用
    new Vue({
        el: '#app',
        ...
    })
    
  • vue在发布的时候,发布了两个版本

    • runtime-only

      • 代码中,不可以有任何的template
    • runtime-complier

      • 代码中,可以有template,因为有complier可以用于编译template
    • 要解决这个问题,只需要在webpack.config.js中配置

      resolve: {
          // 文件引入时省略后缀名配置
          extensions: ['.js', '.css', '.vue'],
          // alias 别名
          alias: {
              // 如果导入vue后,去vue文件中去找vue.esm.js文件,这个文件中包含了complier
              'vue$':'vue/dist/vue.esm.js'
          }
      }
      

el和template的区别,及vue文件的封装

  1. template在解析的时候会替换el,再去修改html中的代码

  2. 但是如果将所有替换的代码都写在template里面,看着就很感觉很复杂,我们可以将template中的信息提取成一个组件

    // ------------------------------2.app.js文件------------------------------------
    // App对象中的代码可以封装到一个app.js文件里面
    export default {
        template: 
        `
    		<div>
    			....
    		</div>
    	`,
        data() {
            return {
                
            }
        }
        ...   // 将所有的内容抽取到一个组件里面使用
    }
    
    // -----------------------------3.创建App.vue文件--------------------------------
    //让模板于组件分离
    <template>
        <div>
    		....
    	</div>
    </template>
    
    <script>
        export default {
    		name: "App",
            data() {
           		return {
                
            	}
        	}
    		...
    	}
    </script>
    
    <style>
        //样式
    </style>
    
    
    // ------------------------------1.main.js文件------------------------------------
    //const App = {
    //    template: 
    //    `
    //		<div>
    //			....
    //		</div>
    //	`,
    //    data() {
    //        return {
                
    //        }
    //    }
    //    ...   // 将所有的内容抽取到一个组件里面使用
    //}
    
    
    // 引入App组件内容
    //import App from './vue/app'
    import App from './vue/App.vue'
    new Vue({
        el: '#app',
        template: '<App />',
        components: {
            App	// 在组件里面注册App
        }
    })
    

    但是在编译过程中不能识别App.vue文件,所以我们需要对vue文件进行一个封装,就要使用vue-loadervue-template-complier

    安装:

    vue-loader让vue文件进行一个加载,vue-template-complier 对vue文件进行编译

    npm install vue-loader vue-template-complier --save-dev
    

    配置:

    {
        test: /\.vue$/,
        use: ['vue-loader']
    }
    

    因为vue-loader版本的原因,可能会报错,说vue-loader was used without the corresponding plugin,在package.json文件中将vue-loader的版本改成^13.0.0(13到14版本之间),然后在命令行工具中执行npm install重新安装一下

plugin

  • plugin是什么?
    • plugin是插件的意思,通常是用于对某个现有的框架的进行扩展
    • webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等。
  • loader和plugin的区别:
    • loader主要用于转换某些类型的模块,它是一个转换器
    • plugin是插件,它是对webpack本身的一个扩展,是一个扩展器。
  • plugin的使用过程:
    • 通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
    • 在webpack.config.js中的plugins中配置插件

添加版权的plugin

BannerPlugin,属于webpack自带的插件。

修改webpack.config.js的文件:

const path = require('path');
//导入webpack
const webpack = require('webpack');

modeule.exports = {
   ...
   plugins: [
        new webpack.BannerPlugin('最终版权归aaa所有')
   ]
}

重新打包:

npm run build/webpack

打包html的plugin

  • 目前,我们的index.html页面存放在项目的根目录下

    • 在真实发布项目的时候,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js文件也就没有意义了
    • 所以我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件
  • HtmlWebpackPlugin插件可以:

    • 自动生成一个index.html文件(可以指定模板来生成)
    • 将打包的js文件,自动通过script标签插入到body中
  • 安装HtmlWebpackPlugin插件

    npm install html-webpack-plugin --save-dev
    
  • 使用插件,修改webpack.config.js文件中plugins部分的内容

    • 这里的template表示根据什么模板来生成index.html

    • 另外我们需要删除之前在output中添加的publicPath属性

    • 否则插入的script标签中的src可能会有问题

      const path = require('path');
      //导入webpack
      const webpack = require('webpack');
      // 导入HtmlWebpackPlugin插件
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      
      modeule.exports = {
         ...
         plugins: [
              new webpack.BannerPlugin('最终版权归aaa所有'),
             	// 这样使用之后,就会在文件中的dist文件夹下面生成index.html文件
              new HtmlWebpackPlugin({
                  // 让template根据index.html文件来生成index.html
                  template: 'index.html'
              })
         ]
      }
      

js压缩的Plugin

  • 在项目发布之前,我们必须要对js等文件进行压缩处理

    • 对js文件进行压缩,使用的是第三方插件unlifyjs-webpack-plugin,并且版本号指定1.1.1,和cli2保持一致

      npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
      
  • 修改webpack.config.js文件,使用插件:

    const path = require('path');
    //导入webpack
    const webpack = require('webpack');
    // 导入unlifyjs-webpack-plugin插件
    // 在开发阶段不建议使用uglifyJsPlugin插件,代码在修改后不宜调试
    const uglifyJsPlugin = require('unlifyjs-webpack-plugin');
    
    module.exports = {
       ...
       plugins: [
            new webpack.BannerPlugin('最终版权归aaa所有'),
           	new uglifyJsPlugin()
       ]
    }
    
  • 查看打包后的bunlde.js文件,最后已经被压缩过了

搭建本地服务器

  • webpack提供了一个可选的本地服务器,这个本地服务器基于node.js搭建,内部使用express框架,可实现浏览器自动刷新显示修改后的结果。

  • 不过它是一个单独的模块,在webpack中使用之前需要先安装

    npm install --save-dev webpack-dev-server@2.9.1
    
  • devserver也是作为webpack中的一个选项,选项本身可以设置以下属性:

    • contentBase:为那个文件提供本地服务,默认是根文件夹,我们这里要填写./dist
    • port: 端口号
    • inline:页面实时刷新
    • historyApiFallback:在SPA页面中,依赖HTML5的history模式
  • webpack.config.js文件配置修改:

    devServer: {
        contentBase: './dist',
        inline: true
    }
    
  • 我们还可以配置package.json中的script:

    • –open参数标识直接打开浏览器

      "build": "webpack"
      "dev": "webpack-dev-server --open"
      

配置完成后运行:

​ 在命令窗口通过npm run dev运行

配置(webpack.config.js)结构分离

将开发时依赖和发布时依赖分离。

  • 在主文件夹下创建build文件夹,在build文件夹下面创建base.config.js(公共),prod.config.js(开发时依赖包)和dev.config.js(运行时依赖)

  • 安装webpack-merge,将分离的文件进行合并

    npm install webpack-merge --save-dev
    
  • 使用

    base.config.js中:

    const path = require('path');
    //导入webpack
    const webpack = require('webpack');
    
    modeule.exports = {
    	// 入口
    	entry: './src/main/.js',
    	// 出口
    	output: {
    		// 路径  path  是绝对路径
    		// 动态获取路径
    		path: path.resolve(__dirname, 'dist'),
    		// 文件名   bundle  打包
    		filename: 'bundle.js'
    	},
    	module: {
    		rules: [{
    			test: /\.css$/
    			use: ['style-loader', 'css-loader']
    		}]
    	},
    	plugins: [
    		new webpack.BannerPlugin('最终版权归aaa所有')
    		new HtmlWebpackPlugin({
    			// 让template根据index.html文件来生成index.html
    			template: 'index.html'
    		})
    	]
    }
    

    prod.config.js中:

    const uglifyJsPlugin = require('unlifyjs-webpack-plugin');
    
    // 导入webpack-merge
    const webpackMerage = require('webpack-merge');
    
    //拿到baseConfig 
    const baseConfig = require('./base.config');
    
    //将config文件合并导出
    module.exports = webpackMerage(baseConfig, {
       ...
       plugins: [
            new webpack.BannerPlugin('最终版权归aaa所有'),
           	new uglifyJsPlugin()
       ]
    })
    

    dev.config.js中:

    // 导入webpack-merge
    const webpackMerage = require('webpack-merge');
    
    //拿到baseConfig 
    const baseConfig = require('./base.config');
    
    module.exports = webpackMerage(baseConfig,{
        devServer: {
        	contentBase: './dist',
        	inline: true
    	}
    })
    

    最后在使用时候,将package.json文件中的build改为:

    更改一下config的指向:

    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
    

    然后使用:

    run run dev
    

    但是这样打包后的文件在build文件下面生成的dist文件夹里面,为了解决这个问题,就将base.config.js文件中output下的path改为:

    path: path.resolve(__dirname, '../dist')
    

Vue cli脚手架详解

什么是Vue CLI

  • CLI是Command-Line Interface,翻译为命令行界面,俗称脚手架
  • Vue CLI是官方发布的vue.js项目脚手架
  • 使用vue-cli可以快速搭建Vue开发环境及对应的webpack配置

Vue CLI使用前提-Node

脚手架应用于webpack,webpack基于node。

Vue CLI的使用

  • 安装Vue脚手架,-g(global:全局的)

    npm install -g @vue/cli
    
  • 注意:上面安装的是Vue CLI3的版本,如果需要想按照Vue CLI2的方式初始化项目是不可以的。

    安装Vue CLI2:

    npm install -g @vue/cli-init
    
  • 安装完成后,查看是否安装成功:

    vue --version
    
  • Vue CLI2初始化项目:

    • vue init webpack my-project(项目名称)

在这里插入图片描述

  • Vue CLI3初始化项目

    • vue create my-project

      chrome中的 v8引擎(使用c++实现,v8是开源的),将js代码直接编译成二进制代码,在浏览器中执行

      js -》 浏览器

      js -》字节码 -》浏览器

    在这里插入图片描述

视频材料链接: https://pan.baidu.com/s/1dxV3TSKpHoFhZo-oM3stFg
提取码: epuk

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值