一、安装
二、基础练习
1)引入vue.js
vue.js可以在这里获取
链接:https://pan.baidu.com/s/1069sWN6owQRKQXMkONBfkQ
提取码:g09p
解释:
注意:被挂载的div要放在vue的上面,否则vue管不到
2)v-for
3)做一个计数器(v-on:click)
方法一:直接在标签里实现
<div id="app">
<h2>当前计数: {{count}}</h2>
<button v-on:click="count++">+</button>
<button v-on:click="count--">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const bpp=new Vue({
el: "#app",
data : {
count: 0,
movices: ['one','two','three']
}
})
</script>
方法二:采用函数(如果代码太多的话)
三、vue中的mvvm
四、vue的生命周期
五、各种标签
5.1)mustache语法(双大括号语法)
5.2)v-once
该指令后面不需要跟任何表达式
该指令表示元素和组件只渲染一次,不会随着数据的改变而改变。
加了v-once后,在控制台中修改值,页面中的并不会改变。
5.3)v-html
会将传递过来的html进行解析
5.4)v-pre
用于跳过这个元素和它子元素的编译过程
执行结果:
你好啊
{{message}}
5.5)v-cloak
在某些情况下,浏览器可能会直接显然出未编译的Mustache标签。
加了v-cloak属性之后可以解决这个情况,可以让div在vue没解析之前不显示
v-cloak在vue解析完之后就会消失
5.6)v-bind
v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值(下面会介绍组件)
v-bind的语法糖:可以简写成 : 例如将 v-bind:src 改成 :src
6.1绑定url
可以将vue中的属性挂载到标签的属性中
6.2绑定class
动态绑定class:可以绑定是否将class属性添加到标签中
另一种比较简洁的写法
6.3绑定style
6.4)绑定disable
<!--当item为true时,button不起作用-->
<button :disable="item">按钮</button>
5.7)v-on
用于进行事件监听
可以监听点击、拖拽、键盘事件等等
语法糖:@
参数:event
7.1)v-on的简单使用
<body>
<div id="app">
<h2>当前计数: {{count}}</h2>
<button v-on:click="add">+</button>
<button v-on:click="mid">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const bpp=new Vue({
el: "#app",
data : {
count: 0,
movices: ['one','two','three']
},
methods: {
add: function () {
console.log("add被执行");
this.count++
},
mid: function () {
console.log("mid被执行");
this.count--
}
}
})
</script>
</body>
7.2)v-on的语法糖
<button @cick="add">+</button>
<button @cick="incr">-</button>
7.3)v-on的参数问题
<body>
<div id="app">
<button @click="print('hello')">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const bpp=new Vue({
el: "#app",
data : {
count: 0,
movices: ['one','two','three']
},
methods: {
print(name){
console.log(name)
}
}
})
</script>
</body>
7.3.1)如果函数不是写在{{}}里面或者函数没有参数,则()可以省略
<button @click="print">按钮</button>
7.3.2)如果函数中有参数,但是没有传入参数的话,那么就将undefined传进去
<button @click="print()">按钮</button>
7.3.3)如果函数中有参数,但是没有写()那么将点击事件传进去
<button @click="print">按钮</button>
7.3.4)如果既要传递参数,又要获得event
<body>
<div id="app">
<button @click="print('hello',$event)">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const bpp=new Vue({
el: "#app",
data : {
count: 0,
movices: ['one','two','three']
},
methods: {
print(name,event){
console.log(name+'-----'+event)
}
}
})
</script>
</body>
7.4)v-on的修饰符问题
.stop停止该按钮的作用
停止某个事件的作用,如果不加stop修饰的话,点击按钮那某divClick也会起作用,这不是想要的,所以可以加上stop就阻止了
.prevent阻止默认事件的发生
该例子阻止了表单的提交
监听某个键帽的作用
当按下回车的时候,才让函数起作用
.once
让点击事件只起一次作用
5.8) v-if,v-else-if,v-else
5.9)v-show和v-if
<body>
<div id="app">
<h2 v-if="isShow">你好呀</h2>
<h2 v-show="isShow">你好呀</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
</body>
当isShow为false时,v-if是直接被清除了,
而v-show则是多了个style=“display:none”
5.10)v-for
遍历数组
<body>
<div id="app">
<ul>
<li v-for="item in movices">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
movices:['喜羊羊与灰太狼','熊出没','海贼王']
}
})
</script>
</body>
遍历对象
<body>
<div id="app">
<ul>
<li v-for="(value,key) in info">{{value}}-{{key}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
movices:['喜羊羊与灰太狼','熊出没','海贼王'],
info:{
name:"张三",
sex:"男",
age:180
}
}
})
</script>
</body>
v-for绑定和非绑定key的区别
<div id="app">
<ul>
<li v-for="item in movices" key="item">{{item}}</li>
</ul>
</div>
如果有绑定key,那么每个item都会和key一一对应,如果要进行添加或者删除操作时,就会像链表的添加删除一样快;
如果没有绑定key,那么要进行添加或者删除操作时,就会像数组的添加删除一样慢。
5.11)作业
先将数组遍历到页面上,然后点击
5.12)v-model
用来实现表单元素和数据的双向绑定
什么是双向绑定
<input type="text" v-model="message">
1、因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。
2、当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。
v-mode的原理
原理:v-model其实就是一个语法糖,它的背后本质上是包含两个操作:
1、v-bind绑定一个value属性
2、v-on指令给当前元素绑定input事件
<input type="text" v-model="message">
等同于
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
绑定text类型:
<body>
<div id="app">
<input type="text" :value="message" @input="btnClick">
{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:"你好啊"
},
methods:{
btnClick(event){
this.message=event.target.value
}
}
})
</script>
</body>
v-model绑定radio类型:
<body>
<div id="app">
<input type="radio" v-model="sex" id="sex" value="男">男
<input type="radio" v-model="sex" id="sex" value="女">女<br/>
<h2>您选择的性别是:{{sex}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:"你好啊",
sex:''
}
})
</script>
</body>
绑定checkbox:
<body>
<div id="app">
<h1>单选框</h1>
<input type="checkbox" v-model="agree">同一协议<br/>
<h2>您的选择是{{agree}}</h2>
<button :disable="!agree">下一步</button>
<h1>多选框</h1>
<input type="checkbox" value="篮球" v-model="hobbies">篮球<br/>
<input type="checkbox" value="足球" v-model="hobbies">足球<br/>
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球<br/>
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球<br/>
<h2>您的选择是:{{hobbies}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
agree:false,
hobbies:[]
}
})
</script>
</body>
绑定select:
<body>
<div id="app">
<select v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="菠萝">菠萝</option>
<option value="橘子">橘子</option>
</select>
<h2>您的选择是:{{fruit}}</h2>
<select v-model="fruits" 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>
</body>
值绑定:
就是动态地给value赋值,可以通过v-bind动态地给value绑定值
<body>
<div id="app">
<label v-for="item in books">
<input type="checkbox" :value="item" v-model="books">{{item}}<br/>
</label>
<h2>书籍有:{{books}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
books:['数据结构与算法','算法导论','编译原理','计算机网络','计算机组成原理']
}
})
</script>
</body>
v-model修饰符
.lazy
这个修饰符让v-model不用实时绑定,而是等到回车的时候才进行绑定
<input type="text" v-model.lazy="message">{{message}}
.number
这个修饰符让输入框中的值保持是数字,如果没有加这个修饰符那么即使让它的初始化是数字,输入多个数字时它会自动转为string类型
<input type="number" v-model.number="age">{{age}}--{{typeof age}}
.trim
这个修饰符用于去除左右两边的空格
<input type="text" v-model.trim="name">
六、计算属性
对于一些需要进行复杂操作的属性,可以先使用计算属性操作完之后再展示到页面。
使用它时不需要加(),因为它是当做一个属性来使用的
对于函数也可以对属性进行复杂操作为什么还要再使用计算属性呢?
原因:计算属性是有缓存的
1)计算属性的简单使用
computed属性的复杂操作
2)计算属性的setter和getter
计算属性本质上是长这样的
computed:{
fullName:{
setter:function () {
},
getter:function () {
}
}
}
但是,一般是不写setter方法的,所以只是这样的
computed:{
fullName:{
getter:function () {
}
}
}
为了简写,所以一般看到的都是这样的
computed:{
fullName:function () {
}
}
}
3)计算属性和methods的比较
计算属性是有缓存的,如果要多次显示计算属性的内容,计算属性只会被调用一次,而methods是没有缓存的,显示多少次,methods就被调用多少次。
4)var和let的比较
在JavaScript中,使用var定义的变量除了function其他都是没有作用域的(例如:if,for等)
以下这个例子
它内部是这样的
{
var i=0;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击')
})
}
{
var i=1;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击')
})
}
{
var i=2;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击')
})
}
而var是没有作用域的,所以每次每个块中的i都被5重新赋值
不管点击哪个button,控制台都是打印5,因为for是没有作用域的,所以i会一直加到5,因此,不管点击哪个button都是打印5
而function就是有作用域的,可以在函数中执行
就不会总是打印5了
而如果使用let的话,就是有作用域的,就不会出现上面的问题。
5)闭包
对于下面这段代码,它是加了闭包的
for (var i=0;i<5;i++){
(function (num) {
btns[i].addEventListener('click',function () {
console.log('第'+num+'个按钮被点击')
})
})(i)
}
每次执行的时候就在function里面创建了个i,而function是有作用域的,因此,当每次点击button时,打印的是function里面的值,和外面的i没有关系,所以可以打印正确
6)ES6字面量增强写法
6.1)对象的增强
对于ES5中的字面量对象的书写形式
<script>
const name='hello'
const age=20
const height=1.80
const person={
name:name,
age:age,
heigth:height
}
console.log(person)
</script>
ES6中的书写形式
<script>
const name='hello'
const age=20
const height=1.80
const person={
name,
age,
height
}
console.log(person)
</script>
6.2)函数的增强
ES5的写法:
const demo={
run:function () {
}
}
ES6中的写法:
const demo={
run(){
}
}
七、数组中哪些方法是响应式的
响应式就是直接在控制台上对数组中的元素进行增删改查时,页面中的展示会跟着变化。
1)push方法
从最后一位添加元素,可以一次性添加多个值
push(…num)
2)pop()
从最后一位删除元素
3)shift()
从第一位删除元素
4)unshift()
从第一位添加元素,可以一次性传入多个值unshift(…num)
5 )splice()
splice的作用:
删除:
splice(start,删除的元素个数) ,表示从start开始,要删除多少个元素;
splice(start)表示删除start开始后面的元素
替换:
splice(start,end,替换的元素)表示从start开始删除end个元素,然后再插入替换元素,可以替换多个元素
6)sort()
排序
7)reverse()
反转
八、过滤器
可以对内容进行修饰
格式:{{原本的内容 | 过滤器}}
例子:想要对价格的显示进行一些修饰,在前面添加¥符号和让它有两位小数显示
filters:{
priceFilter(price){
return '¥'+price.toFixed(2)
}
}
九、JavaScript高阶函数
1)filter
对数组中的数据进行过滤,满足条件的则放到新数组中
const nums=[20,40,80,100,111,212,212]
//filter
const newNum1=nums.filter(function (n) {
return n<100
})
2)map
对数组中的数据进行处理后放到新数组里面
const nums=[20,40,80,100,111,212,212]
//2、map
const newNum2=newNum1.map(function (n) {
return n*2
})
3)reduce
对数组中的数据进行累加
const nums=[20,40,80,100,111,212,212]
//3、reduce
const newNum3=nums.reduce(function (previousValue,n) {
return previousValue+n
},0) //初始值是0
十、组件
- 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
- 但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
10.1)组件使用的步骤:
1)创建组件构造器
2)注册组件
3)使用组件
例子:
<body>
<div id="app">
<!--步骤3:使用组件-->
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 步骤1:创建组件(注册组件构造器)
const conC=Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈</p>
<p>我是内容,呵呵呵呵</p>
</div>`
})
//步骤2、注册组件
Vue.component('my-cpn',conC)
const app=new Vue({
el:"#app",
data:{
}
})
</script>
</body>
这里的步骤都代表什么含义呢?
1.Vue.extend():
调用Vue.extend()创建的是一个组件构造器。
通常在创建组件构造器时,传入template代表我们自定义组件的模板。
该模板就是在使用到组件的地方,要显示的HTML代码。
事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
2.Vue.component():
调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
所以需要传递两个参数:1、注册组件的标签名 2、组件构造器
3.组件必须挂载在某个Vue实例下,否则它不会生效。
我们来看下面我使用了三次
而第三次其实并没有生效:
<div id="app">
<my-cpn></my-cpn>
<div>
<my-cpn></my-cpn>
</div>
</div>
<!--这个不会生效,因为它没挂载在Vue实例下-->
<my-cpn></my-cpn>
10.2)全局组件和局部组件
它们的区别:
1、在于注册的方式
2、全局组件可以作用在被不同Vue实例挂载的dom里,而局部组件只能作用在注册这个组件的dom里
全局组件的注册方式:
Vue.component('my-cpn',conC)
局部组件的注册方式:
在vue实例中注册
const app=new Vue({
el:"#app",
data:{
cpn:conC
}
})
10.3)父组件和子组件
子组件在父组件的组件构造器中注册
3)组件注册的语法糖格式
不再需要单独写template,而是将组件构造器里的东西写到注册里面
4)模板抽离
前面注册的时候模板都是写在注册里的,非常难看,我们可以通过标签将模板写在这里面
5)组件中的数据访问
5.1)子组件不能直接访问Vue实例里面的数据
5.2)vue组件应该有自己保存数据的地方
5.2)组件中的data是function格式的
6)为什么组件中的data只能是函数
因为一个组件一般都是被使用多次的,而使用函数的话它是会返回一个对象的,然后不同函数返回的对象就是不一样的,就不会导致每个组件都是在操作同一个数据,避免了数据的错误。如果它是用对象的话,那么多个组件就是在操作同一个数据,造成数据的错误。
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数:{{counter}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
Vue.component('cpn',{
template:"#cpn",
data(){
return{
counter:0
}
},
methods:{
increment(){
this.counter++
},
decrement(){
this.counter--
}
}
})
const app=new Vue({
el:"#app"
})
</script>
</body>
7)父子组件通信
- 通过props向子组件传递数据
- 通过事件向父组件发送消息
7.1)父传子
通过props将父组件中的数据传递到子组件
7.2)子传父
通过自定义时间向父组件发送数据
自定义事件的流程:
- 在子组件中,通过$emit()来触发事件。
- 在父组件中,通过v-on来监听子组件事件。
7.3)父组件访问子组件
有两种方式:
10.1)通过 $ children(很少用,一般在获取所有子组件的时候才会使用)
10.2)通过 $ refs:
$refs的使用:
- $refs和ref指令通常是一起使用的。
- 首先,我们通过ref给某一个子组件绑定一个特定的ID。
- 其次,通过this.$refs.ID就可以访问到该组件了。
<body>
<div id="app">
<cpn></cpn>
<cpn ref="child"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script src="../../js/vue.js"></script>
<script>
const cpn={
template:"#cpn",
data(){
return {
name:"我是子组件中的name"
}
},
methods:{
showmessage(){
console.log('showmessage')
}
}
};
const app=new Vue({
el:"#app",
data:{
message:'你好啊'
},
methods:{
btnClick(){
//console.log(this.$children[0].name)
console.log(this.$refs.child.name)
}
},
components:{
cpn
}
})
</script>
</body>
8)props中的驼峰标识问题
如果组件中的data是驼峰标识的,那么dom中需要用 - 来表示,否则会出错。
9)父子组件通信-结合双向绑定
要求:将父组件中的数据传递到子组件中,然后在子组件中对数据进行改变后,可以将改变后的数据传递到父组件中。同时,要求父组件中显示的数据是子组件的2倍。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父子组件通信-双向绑定</title>
</head>
<body>
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1change="num1change"
@num2change="num2change"></cpn>
</div>
<template id="cpn">
<div>
<p>data:{{dnumber1}}</p>
<p>props:{{number1}}</p>
<input type="text" :value.number="dnumber1" @input="number1input" />
<p>data:{{dnumber2}}</p>
<p>props:{{number2}}</p>
<input type="text" :value.number="dnumber2" @input="number2input" />
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
num1:0,
num2:1
},
components:{
cpn:{
template:"#cpn",
props:{
number1:Number,
number2:Number
},
data(){
return{
dnumber1:this.number1,
dnumber2:this.number2
}
},
methods:{
number1input(event){
this.dnumber1=event.target.value;
this.$emit('num1change',this.dnumber1);
this.dnumber2=this.dnumber1*100;
this.$emit('num2change',this.dnumber2)
},
number2input(event){
this.dnumber2=event.target.value;
this.$emit('num2change',this.dnumber2);
this.dnumber1=this.dnumber2/100;
this.$emit('num1change',this.dnumber1)
}
}
}
},
methods:{
num1change(value){
this.num1=parseFloat(value)
},
num2change(value){
this.num2=parseFloat(value)
}
}
})
</script>
</body>
</html>
十一、编译作用域
各种标签,属性的使用都是在本模板内起作用的
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
最终结果是:
我是子组件的标题
哈哈哈哈
看得到我吗
在使用<cpn v-show="isShow"></cpn>
的时候,整个组件的使用过程是相当于在父组件中出现的。
十二、插槽
vue中的插槽就是在组件的<template></template>
标签中加入<slot></slot>
标签,然后如果需要在这里面加入新的内容就可以直接添加
具名插槽
如果没有对插槽指定具体的名称,那么所有的插槽都会替换
结果
加了名称,就会替换了那些加了名称的
替换指定名称的插槽
十三、插槽作用域
一般在组件中有默认的显示数据的方式。如果父组件调用了子组件,但是父组件并不想要子组件显示数据的方式,而是想要按照自己的方式显示数据,但是在父组件中是不能直接访问子组件中的数据的。所以可以采用插槽的方式来处理。
十四、commonjs
模块化有两个核心:导出和导入
Commonjs的导出
module.exports={
flag:true,
test(a,b){
return a+b
},
demo(a,b){
return a*b
}
}
Commonjs的导入
//Commonjs的导入
let{test,demo,flag}=require('moduleA');
//等同于
let _mA=required('moduleA');
let test=_mA.test;
let demo=_mA.demo;
let flag=_mA.flag;
十五、ES6的导出和导入
可以通过在<script>
标签中加入type="module"
属性来标志这个一个模块,就不会出现模块间数据冲突的问题
1)变量的导出和导入
2)函数/类的导出和导入
导入
3)export default
一个模块只能有一个export default,使用这个东西之后在导入时就可以自定义名称
十六、webpack
1)什么是webpack
webpack是一个前端模块化打包工具
对于有些技术,浏览器是识别不了的,而通过webpack打包之后就可以转化为浏览器可以识别的东西
2)webpack的安装
node和webpack和npm的关系:
1、webpack模块化打包;
2、webpack为了可以正常运行,必须依赖node环境;
3、node环境为了可以正常的执行化很多代码,必须其中包含各种依赖的包;
4、npm工具就是用来管理这些包的。
1)安装node
node安装完之后附带安装了npm
2) 安装webpack
2.1)先安装淘宝镜像,因为npm的源在国外,速度很慢
https://blog.csdn.net/qq_38232598/article/details/91346993
可以通过npm get registry
来获取npm源
2.2)全局安装webpack
npm install webpack@3.6.0 -g
2.3)局部安装webpack
–save-dev 是开发时依赖,项目打包后不需要继续使用
cd 对应目录
npm install webpack@3.6.0 --save-dev
3)webpack的基本使用
一般创建两个文件夹dist和src,dist放的是打包后的代码,src放的是我们原始代码
进入src所在的目录,然后执行以下指令进行打包
webpack ./src/main.js ./dist/bundle.js
打包后生成的bundle.js代码就是最终使用的代码
注意:main.js会调用很多其他代码,这里不用管它,webpack会自动将它们打包
4)webpack的配置
4.1)对入口和出口进行映射
上一个例子中打包js需要写打包的入口和出口,比较麻烦,可以用一个配置文件来记录入口和出口,以后就不用在打包时写入口和出口了。
因为要使用到node中的一些包,所以需要用npm来加载这些包,每次使用npm前,建议都先执行npm init
然后就生成了这个文件
4.2)对webpack进行映射
在package.json中可以对指令进行映射,然后执行npm run 映射的名称,如果在这里映射了webpack,那么使用映射后的指令默认优先使用本地安装的webpack,而不是全局的,而只要是使用webpack指令,那么使用的就是全局的。
5)loader
我们除了对js文件进行打包之外,还需要对其他文件进行打包,所以需要将其他文件导入到mian.js中,但是打包其他文件需要一些加载器
https://webpack.docschina.org/loaders/css-loader/
5.1)打包css文件
注意:安装时指定以下版本,否则可能会出错
安装css-loader
npm install css-loader@2.0.2 --save-dev
安装style-loader
npm install style-loader@0.23.1 --save-dev
可以在package.json中看到安装的具体版本信息
5.2)打包less文件
安装less-loader和less,其中less不是一个loader,而是为了将less解析为css
老规矩,指定具体版本
npm install less-loader@4.1.0 less@3.9.0 --save-dev
5.3)处理图片文件
安装url-loader
npm install url-loader@1.1.2 --save-dev
当图片小于limit时,图片会被编译成base64的格式
当图片的大于limit时,需要再下载file-loader
6)将ES6转为ES5
在使用webpack进行打包时,还是不能将ES6转为ES5,所以需要使用相应的loader对其进行转化。
安装babel-loader,babel-core和babel-preset-es2015(es2015也就是es6)
npm install babel-loader@7 babel-core babel-preset-es2015 --save-dev
在webpack.config.js中添加如下配置
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/, //排除掉那些不是我们自己写的代码
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
打包后的bundle.js中的相关代码简单介绍
7)vue的配置过程
7.1)安装vue
npm install vue@2.5.21 --save-dev
7.2)导入vue
哦豁,报错!
7.3)使用runtime-only,使用template的问题
这是因为我们使用的是runtime-only版本的,它不允许有template,因为我们引入了vue实例,所以就有template,所以报错。
解决方法:在webpack.config.js中进行配置,让它可以找到compiler可以编译template
8)template和el的区别
template里面的内容会被添加到el挂载的dom里面
十六、vue的终极解决方案
进一步的抽取
将App中的内容抽取之后写在一个js文件里
export default{
template: `
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
data(){
return{
message:"我是message",
name:"我是name"
}
},
methods:{
btnClick(){
console.log(this.message,this.name)
}
}
};
进一步抽取
将app.js文件内容放到App.vue里面(然后就不需要app.js了)
<template>
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
</template>
<script>
export default {
name: "App" ,
data(){
return{
message:"我是message",
name:"我是name"
}
},
methods:{
btnClick(){
console.log(this.message,this.name)
}
}
}
</script>
<style scoped>
</style>
然后需要加载vue-loader
和vue-template-compiler
因为版本太大的vue-loader需要安装相应的vue-loader应该低一点
npm install vue-loader@13.0.0 vue-template-compiler@2.5.21 --save-dev
在webpack.config.js中进行相应的配置
执行结果
十七、plugin
1)什么是plugin
- [ ]plugin是插件的意思,通常是用于对某个现有的架构进行扩展。
- webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等。
2)loader和plugin的区别
- loade主要浓郁转换某些类型的模块,它是一个转换器
- plugin是插件,它是对webpack本身的扩展,是一个扩展器
3)plugin的使用过程
步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
步骤二:在webpack.config.js中的plugins中配置插件
例子:
3.1)横幅plugin的使用
这个是webpack自带的plugin,所以直接引入就行了,不需要安装
在webpack.config.js中进行配置
在bundle.js中就多了这个东西
3.2)htmlwebpackplugin插件
在使用webpack打包后,html并不会打包进去,但是这个html确是我们需要使用的,所以就用到了htmlwebpackplugin插件
安装
npm install html-webpack-plugin@3.2.0 --save-dev
在webpage.config.js中进行相应的配置
问题:没有<div id="app"></div>
这个模板
解决:在new htmlwebpackplugin()添加一个模板
更改index.html
在webpack.config.js中导入template
然后就能生成我们想要的index.html了
3.3)压缩js的plugin
安装uglifyjs-webpack-plugin
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
在webpack.config.js中进行相应的配置
十八、配置文件的抽离
将配置文件分为公共、开发和运行配置文件
需要安装webpack-merge
npm install webpack-merge@4.1.5 --save-dev
base.config.js
const path = require('path'); //动态地获取路径
const webpack=require('webpack');
const htmlwebpackplugin=require('html-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, '../dist'), //__dirname是一个全局变量,是node里面的东西
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader","css-loader"],
},
{
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
}]
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 13000,
},
},
],
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015'],
}
}
}
],
},
resolve:{
alias:{ //配置别名
'vue$': 'vue/dist/vue.esm.js' //让它可以去这个路径中找到compiler编译template
}
},
plugins:[
new webpack.BannerPlugin('最终版权归aaa所有!'),
new htmlwebpackplugin({
template:'index.html'
})
]
};
prod.config.js
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig, {
plugins: [
new UglifyjsWebpackPlugin()
]
})
然后在package.json中配置路径
十九、脚手架
1)脚手架的安装
CLI是什么意思:
- CLI是command-Line Interface,翻译为命令行界面,但是俗称脚手架。
- Vue Cli是一个官方发布vue.js项目脚手架。
- 使用vue-cli可以快速搭建Vue开发环境以及webpack配置。
脚手架的前提:node
安装脚手架vue cli,这里进行全局安装
cnpm install -g @vue/cli@3.2.1
查看版本
C:\Users\lenovo>vue --version
3.2.1
如果想要使用脚手架2,可以拉取脚手架2
npm install -g @vue/cli-init
Vue CLI2初始化项目
vue init webpack my-project
Vue CLI3初始化项目
vue create my-project
2)安装某个项目的脚手架
2.1)使用cli2初始化项目
vue init webpack my-project
2.2)cli目录解析
2.3)运行vue cli2
npm run dev
2.4)使用vue cli3创建项目
vue cli2和vue cli3的区别
vue create vuecli3
按空格键可以选择或者取消
生成的目录结构
2.5)vue cli3目录结构
2.6)运行vue cli3
npm run serve
输入以下网址就可以在网页上显示
3)runtime-compile和runtime-only的区别
使用runtime-compile和runtime-only执行后的mian.js的区别:
3.1)runtime-compile的执行过程:
template->ast->render->vdom->ui
3.2)runtime-only的执行过程:
render->vdom->ui
区别:
1)runtime-only的性能更高
2)runtime-only的代码量更少
4)配置文件的查看和修改(使用vue ui管理)
4.1)配置文件的查看
启动配置服务器(在哪个目录启动都可以)
vue ui
就打开了这个界面
导入一个项目,进入到管理页面
看vue真实的版本要去打包之后的文件中看,而不是看源码里面的,可以在以下两个文件中查看
4.2)配置文件的修改
创建vue.config.js(只能是这个名称)
二十、箭头函数
是ES6中一种定义函数的方式
1)参数问题
1.1)两个参数问题
const sum=(num1,num2)=>{
return num1+num2;
}
console.log(sum(2,3));
1.2)一个参数问题
如果只有一个参数,可以省略()
const power2=num=> {
num*num;
}
2)代码中函数数量问题
2.1)行数>=2
需要写{}
2.2)行数只有1行
可以省略{}
const power2=num=> num*num
console.log(power2(3));
没有参数,且只有一行代码
const demo=()=>console.log("没有参数,且只有一行代码");
demo();
3)this指向问题
在箭头函数中,this是向外层作用域中,一层一层向外找,直到有this
const obj={
aaa(){
setTimeout(function () {
console.log(this);
},1000);
setTimeout(()=>console.log(this),1000);
}
};
obj.aaa();
执行结果:
const obj={
aaa(){
setTimeout(function () {
setTimeout(function () {
console.log(this);
});
setTimeout(()=>{
console.log(this);
});
});
setTimeout(()=>{
setTimeout(function () {
console.log(this);
});
setTimeout(()=>{
console.log(this);
});
});
}
};
obj.aaa();
二十一、html5的history模式
1)pushstate()
history.push({},title,url)
它可以改变url,类似于栈,浏览器中地址栏显示的总是栈顶url
2)back()
history.back()
它类似于出栈操作,每次执行这个函数,都相当于执行浏览器中的返回操作
3)go()
history.go(数值)
数值如果是负数,则相当于浏览器中的返回操作,如果是正数,则相当于浏览器中的前进
4)forward()
history.forward()
相当于history.go(1)
5)关系
histrory.back()相当于history.go(-1)
history.forward()相当于history.go(1)
6)replaceState
history.replaceState({},title,url)
会替代上一个url,并且浏览器中不能通过前进或后退来回到之前的url
二十二、路由
1)前端渲染和后端渲染
1.1)后端渲染
就是将页面在服务器渲染完之后整个渲染好的页面传到浏览器,在浏览器中只能看到html+css
1.2)后端路由
1.3)前端渲染
浏览器中显示的网页中的大部分内容,都是由前端写的js代码在浏览器中执行,最终渲染出来的网页。
1.4)前端路由
管理前端的页面,一个url对应一个组件
2)安装和配置vue-router
搭建vue-router步骤:
(一)安装vue-router
我这里使用脚手架自动安装了vue-router了,所以就不需要这一步了
npm install vue-router@3.0.1 --save-dev
(二)在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)
第一步:导入路由对象,并且调用 Vue.use(VueRouter)
第二步:创建路由实例,并且传入路由映射配置
第三步:在Vue实例中挂载创建的路由实例
index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
//通过Vue.use(插件) 来安装插件
Vue.use(Router)
//创建并导出router,vue-router只有挂载到vue实例里面,才能生效
export default new Router({
routes: [ //在这里面配置相关的映射信息
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
main.js
import Vue from 'vue'
import App from './App'
import router from './router' //导入router
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router, //挂载router
render: h => h(App)
})
3)使用vue-router步骤
第一步: 创建路由组件
第二步: 配置路由映射: 组件和路径映射关系
第三步: 使用路由: 通过<router-link>
和<router-view>
<router-view>
用于显示组件中的内容,如果没有<router-view>
那么组件中的内容就显示不出来<router-view>
写在哪,组件的内容就显示在哪
4)设置默认首页的路径
在index.js中的路由映射中进行配置
5)更改路径为history模式
在index.js中设置如下:
5)<router-link>
属性
5.1)to属性
这个用于跳转的,相当于<a></a>
5.2)tag属性
这个用于指定这是个什么标签
5.3)replace
这个的作用相当于history.replaceState(),加了这个东西之后浏览器就不能进行前进或后退操作
5.4)active-class
当<router-link>
对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.
在index.js中进行设置
6)通过代码进行路由跳转
注意:这里不能用history.pushState(),因为它会绕过路由,而我们需要的是进行路由跳转
7)动态路由的使用
也就是在路径中进行传参
1)传值
在index.js的路由映射中配置
然后在App.vue中设置要传递的值
2)获取值
这个this.$route是在index.js中的routes[]中配置的其中一个,表示当前活跃的route
8)打包文件的解析
先打包项目
npm run build
生成dist文件夹
js文件解析
9)路由懒加载
- 当打包构建应用时,Javascript 包会变得非常大,影响页面加载。
- 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
路由懒加载做了什么?
- 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.
- 只有在这个路由被访问到的时候, 才加载对应的组件
使用懒加载之前
组件在注册时,是采用这种方式注册的
打包后的js,应用程序的代码全部都放在app.801c2ae2e69a05c33dfc65f8.js里面
使用懒加载之后
注册组件时,采用这种方式
打包后,每一个路由一个文件
10)路由嵌套
在一个组件中通过路由嵌套访问另一个组件的内容
使用步骤:
(一)创建被嵌套的组件
homeMessage.vue
<template>
<div>
<ul>
<li>message1</li>
<li>message2</li>
<li>message3</li>
<li>message4</li>
<li>message5</li>
</ul>
</div>
</template>
<script>
export default {
name:'message'
}
</script>
homeNew.vue
<template>
<div>
<ul>
<li>new1</li>
<li>new2</li>
<li>new3</li>
<li>new4</li>
<li>new5</li>
</ul>
</div>
</template>
<script>
export default {
name:'new'
}
</script>
(二)在index.js中进行路由映射配置
(三)使用路由: 通过<router-link>
和<router-view>
,<router-view>
用于显示组件中的内容,如果没有那么组件中的内容就显示不出来写在哪,组件的内容就显示在哪
11)参数传递
主要有两种方式:params和query
params:
- 配置路由格式: /router/:id
- 传递的方式: 在path后面跟上对应的值
- 传递后形成的路径: /router/123, /router/abc
query:
- 配置路由格式: /router, 也就是普通配置
- 传递的方式: 对象中使用query的key作为传递方式
- 传递后形成的路径: /router?id=123, /router?id=abc
12)$ route和$router的区别
- $ router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
- $route为当前router跳转对象里面可以获取name、path、query、params等
13)vue-router的全局导航守卫
13.1)几个生命周期的回调函数
13.2)为什么要使用导航守卫
一个例子:
普通的修改方式:
在每个页面中使用进行添加,这样比较麻烦
通过导航卫士:
13.4)前置守卫
导航钩子的三个参数解析:
- to: 即将要进入的目标的路由对象.
- from: 当前导航即将要离开的路由对象.
- next: 调用该方法后, 才能进入下一个钩子
在index.js的路由映射中添加这个东西
13.5)后置钩子
router.afterEach()
不需要主动调用next()函数
14)vue的生命周期
**created:**一个dom创建并初始化之后就回调这个函数
mounted: 将组件编译并渲染完成后回调这个函数
updated: 对组件有什么更新都会回调这个函数,只要不跳转到其他页面,就会一直循环
destoryed: 如果跳转到其他页面,就会回调这个函数
15)keep-alive
<keep-alive>
<router-view/>
</keep-alive>
keep-alive可以让组件不被destroy
举个栗子:
在home这个页面中有两个组件,如果它现在页面中显示的是消息组件,而如果从home页面跳转到关于页面,那么这个home页面就会被destroy,如果再跳转回home页面,它自然就不会再显示消息组件了,因为它会重新被创建,而且它默认不是显示到消息组件的。
而如果是不被destory,并且它被够在跳转回home页面时,记住home页面中的那个最后记录的地址,自然就能跳转回home页面跳转到其他页面前的最后的模样。
keep-alive的属性:
exclude属性:可以排除掉不想一直被保留的组件
二十三、tabbar
1)tabbar的实现思路
2)创建一个简单的tabbar
App.vue
<template>
<div id="app">
<tab-bar>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="图片不见了"/>
<div slot="item-text">首页</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="图片不见了"/>
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="图片不见了"/>
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="图片不见了"/>
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar';
import TabBarItem from './components/tabbar/TabBarItem';
export default {
name: 'App',
components:{
TabBar,
TabBarItem
}
}
</script>
<style>
@import "./assets/css/base.css";
</style>
TabBar.vue
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: "TabBar"
}
</script>
<style scoped>
#tab-bar{
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100,100,100,.08);
}
</style>
TabBarIItem
<template>
<div class="tab-bar-item">
<slot name="item-icon"></slot>
<slot name="item-text"></slot>
</div>
</template>
<script>
export default {
name: "TabBarItem"
}
</script>
<style scoped>
.tab-bar-item{
flex: 1;
text-align: center;
height: 49px;
}
.tab-bar-item img{
height: 24px;
width: 24px;
}
</style>
3)插槽中设置一些属性
如果直接将属性添加在<slot></slot>
上,那么在使用时,这些属性有些是会显示不出的,所以一般把这些属性写在它外面的<div></div>
上
4)tabbar与路由的配合
1、创建不同tabbar要显示的视图
2、将不同的视图在index.js中进行映射配置
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
const home=()=>import('../view/home/home');
const category=()=>import('../view/category/category');
const profile=()=>import('../view/profile/profile');
const shopcart=()=>import('../view/shopcart/shopcart');
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
redirect:'/home'
},
{
path:"/home",
component:home
},
{
path:"/category",
component: category
},
{
path:"/shopcart",
component:shopcart
},
{
path:"/profile",
component:profile
}
]
})
3、在TabBarItem中配置配置路径跳转
4、在App.vue中传入不同TabBarItem对应的path
二十四、promise
1)基本介绍
是一种异步编程的解决方案
在异步编程中,当网络请求非常复杂时,就会出现回调地狱。
我们来考虑下面的场景(有夸张的成分):
- 我们需要通过一个url1从服务器加载一个数据data1,data1中包含了下一个请求的url2
- 我们需要通过data1取出url2,从服务器加载数据data2,data2中包含了下一个请求的url3
- 我们需要通过data2取出url3,从服务器加载数据data3,data3中包含了下一个请求的url4
- 发送网络请求url4,获取最终的数据data4
promise就可以用来解决这个问题
2)promise的简单使用
需求:在控制台上,每隔1s进行打印
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>promise的基本使用</title>
</head>
<body>
<script>
new Promise((resolve, reject) => {
//第一次网络请求的代码
setTimeout(()=>{
resolve()
},1000)
}).then(()=>{
//第一次拿到结果的处理代码
console.log('hello world');
console.log('hello world');
console.log('hello world');
console.log('hello world');
console.log('hello world');
console.log('hello world');
return new Promise((resolve, reject) => {
//第二次网络请求的代码
setTimeout(()=>{
resolve()
},1000)
})
}).then(()=>{
//第二次处理的代码
console.log('hello promise');
console.log('hello promise');
console.log('hello promise');
console.log('hello promise');
console.log('hello promise');
console.log('hello promise');
return new Promise((resolve,reject)=>{
//第三次网络请求的代码
setTimeout(()=>{
resolve()
},1000)
})
}).then(()=>{
console.log('hello vue');
console.log('hello vue');
console.log('hello vue');
console.log('hello vue');
console.log('hello vue');
console.log('hello vue');
})
</script>
</body>
</html>
执行结果: