Vue2
Vue2笔记
Vue 使用一种基于 HTML 的模板语法,所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。
一、Vue基本语法
文本插值
使用双括号 {{}} 插入值
双大括号会将数据解释为纯文本,而不是 HTML
<span>Message: {{ msg }}</span>
Attribute 绑定
要响应式地绑定一个 attribute,应该使用 v-bind
指令:
<span v-bind:title="message">
鼠标悬浮在这里会有提示信息!
</span>
vue3出现了简易写法:直接使用 : 代替v-bind
<span :title="message">
鼠标悬浮在这里会有提示信息!
</span>
结论
<!-- 与 :id="id" 相同 -->
<span :title></span>
<!-- 这也同样有效 -->
<span v-bind:title></span>
这里我们遇到了一个新的概念。这里看到的 v-bind
attribute 被称为一个指令。指令由 v-
作为前缀,表明它们是一些由 Vue 提供的特殊 attribute,你可能已经猜到了,它们将为渲染的 DOM 应用特殊的响应式行为。在这里的指令意思是:将该元素节点的title特性和Vue实例的message属性保持一致。
Vue判断
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染;
v-else
为 v-if
添加一个“else 区块”;
v-else-if
提供的是相应于 v-if
的“else if 区块”。它可以连续多次重复使用。
<div id="app">
<h1 v-if="type==='A'">YES</h1>
<h1 v-else-if="type==='B'">B</h1>
<h1 v-else-if="type==='D'">D</h1>
<h1 v-else>C</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
type:'A'
}
});
</script>
通过在浏览器中设置vm.type=?来改变值得状态,从而呈现不同得效果。
Vue循环
v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名:
<div id="app">
<li v-for="item in items">
{{item.message}}
</li>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
items:[
{message:"Vue"},
{message:"JavaScript"},
{message:"Java"}
]
}
});
</script>
在 v-for
块中可以完整地访问父作用域内的属性和变量。v-for
也支持使用可选的第二个参数表示当前项的位置索引。
将上面v-for
的部分修改
<li v-for="(itesm,index) in items">
{{item.message}} - {{index}}
</li>
二、Vue事情处理
监听事件
v-on
指令 (简写为 @
) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler"
或 @click="handler"
。
事件处理器 (handler) 的值可以是:
- 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与
onclick
类似)。 - 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。
方法与内联事件判断
模板编译器会通过检查 v-on
的值是否是合法的 JavaScript 标识符或属性访问路径来断定是何种形式的事件处理器。举例来说,foo
、foo.bar
和 foo['bar']
会被视为方法事件处理器,而 foo()
和 count++
会被视为内联事件处理器。
实例
<div id="app">
<button @click="sayHi">Click Me</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"Vue"
},
methods:{
sayHi:function () {
alert(this.message)
}
}
});
</script>
三、Vue双向绑定
什么是双向数据绑定
Vue.js 是一个 MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js 的精髓之处了。
值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定。单向数据绑定是使用状态管理工具的前提。如果我们使用vue
,那么数据流也是单项的,这时就会和双向数据绑定有冲突。
为什么要实现数据的双向绑定
在Vue.js
中,如果使用vue
,实际上数据还是单向的,之所以说是数据双向绑定,这是用的UI控件来说,对于我们处理表单,Vue.js 的双向数据绑定用起来就特别舒服了。即两者并不互斥,在全局性数据流使用单项,方便跟踪;局部性数据流使用双向,简单易操作。
在表单中使用双向数据绑定
你可以用v-model指令在表单<input>
、<textarea>
及<select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
注意: v-model 会忽略所有表单元素的value
、checked
、selected
特性的初始值而总是将Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data
选项中声明初始值!
v-model 组件
v-model
可以在组件上使用以实现双向绑定。
(1)在输入框中进行双向绑定
<div id="app">
输入数字:<input type="text" v-model="message"> {{message}}
<hr>
输入的文本框:<textarea v-model="message"></textarea>{{message}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"1234567"
}
});
</script>
效果图
(2)在单选框中进行双向绑定操作
单选框
<div id="app">
性别:
<input type="radio" name="sex" value="男" v-model="gender">男
<input type="radio" name="sex" value="女" v-model="gender">女
<p>
您选中了:{{gender}}
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
gender:''
}
});
</script>
效果图
(3)下拉框中进行双向绑定
<div id="app">
下拉框:
<select v-model="selected">
<option>---请选择---</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>您选择的是:{{selected}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
selected:''
}
});
</script>
效果图
[!NOTE]
注意:如果
v-model
表达式的初始值未能匹配任何选项,<select>
元素将被渲染为“未选中”状态。在 iOS中,这会使用户无法选择第个选项。因为这样的情况下,iOS不会触发change
事件。因此,更推荐像上面这样提供一个值为空的禁用选项。
四、Vue组件
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
组件注册
一个 Vue 组件在使用前需要先被“注册”,这样 Vue 才能在渲染模板时找到其对应的实现。组件注册有两种方式:全局注册和局部注册。
(1)全局注册
我们可以使用 .component()
方法,让组件在当前 Vue 应用中全局可用。
.component()
方法可以被链式调用:
app
.component('ComponentA', ComponentA)
.component('ComponentB', ComponentB)
.component('ComponentC', ComponentC)
全局注册的组件可以在此应用的任意组件的模板中使用:
<!-- 这在当前应用的任意组件中都可用 -->
<ComponentA/>
<ComponentB/>
<ComponentC/>
(2)局部注册
全局注册虽然很方便,但有以下几个问题:
- 全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)。
- 全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。
相比之下,局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。
在使用 <script setup>
的单文件组件中,导入的组件可以直接在模板中使用,无需注册:
<script setup>
import ComponentA from './ComponentA.vue'
</script>
<template>
<ComponentA />
</template>
传递props
props是用于在父组件中向子组件传递数据。
props 必须以 props
选项的方式声明,props 对象会作为 setup()
函数的第一个参数被传入:
export default {
props: ['title'],
setup(props) {
console.log(props.title)
}
}
实例:简单实现编程语言的列表展示
<div id="app">
<!-- 组件:传递给组件中的值:props -->
<test v-for="item in items" v-bind:program="item"></test>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
<!-- 自定义一个Vue组件component -->
Vue.component("test",{
props:['program'],
template:'<li>{{program}}</li>'
})
var vm = new Vue({
el:"#app",
data:{
items:["Java","Linux","Vue"]
}
});
</script>
说明:
-
v-for="item in itemis"
:遍历Vue实例中定义的名为items
的数组,并创建同等数量的组件 -
v-bind:program="item"
:将遍历的item
项绑定到组件中props
定义的名为program
属性上 ;=
号左边的program
为props
定义的属性名,右边的为item in items
中遍历的item
项的值
效果图
五、Vue:Axios异步通信
什么是Axios
Axios是一个开源的可以用在浏览器端和NodeJs
的异步通信框架,她的主要作用就是实现 AJAX异步通信,其功能特点如下:
- 从浏览器中创建
XMLHttpRequests
- 从
node.js
创建http
请求 - 支持
Promise API
[JS中链式编程] - 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御
XSRF
(跨站请求伪造)
为什么要使用Axios
由于Vue.js
是一个视图层框架并且作者(尤雨溪)严格准守SoC(关注度分离原则),所以vue.js
并不包含AJAX的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource
的插件,不过在进入2.0版本以后停止了对该插件的维护并推荐了Axios 框架。少用jQuery,因为它操作Dom太频繁!
Vue2的生命周期
Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载DOM、渲染→更新→渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue 实例从创建到销毁的过程,就是生命周期。
在Vue的整个生命周期中,它提供了一系列的事件,可以让我们在事件触发时注册JS方法,可以让我们用自己注册的JS方法控制整个大局,在这些事件响应方法中的this直接指向的是Vue 的实例。
Vue2的生命周期是指Vue实例从创建到销毁的整个过程中,会经历一系列的阶段和回调函数。它分为8个阶段,包括了组件的创建、挂载、更新和销毁等过程。
- beforeCreate:
- 在实例初始化之后,但在数据观测和事件配置之前被调用。此时,data和methods等选项尚未初始化,并且无法访问this。
- created:
- 实例已经完成数据观测和事件配置,但挂载阶段还未开始。在这个阶段,可以访问data和methods,并且可以进行一些异步操作。
- beforeMount:
- 在挂载开始之前被调用。此时,模板编译已经完成,但尚未将模板渲染到DOM中。
- mounted:
- 挂载完成时被调用。此时,实例已经将模板渲染到DOM中,并且可以对DOM进行操作。通常在这个阶段进行一些初始化的工作,比如获取远程数据。
- beforeUpdate:
- 数据更新时调用,但在DOM重新渲染之前。在这个阶段,可以对数据进行一些处理或做一些其他操作。
- updated:
- 数据更新完成时调用。此时,DOM已经重新渲染,可以对更新后的DOM进行操作。
- beforeDestroy:
- 实例销毁之前调用。在这个阶段,实例仍然完全可用,可以进行一些清理工作。
- destroyed:
- 实例销毁之后调用。在这个阶段,实例已经被销毁,所有的事件监听器和子组件也都被移除。
在Vue的整个生命周期中,它提供了一系列的事件,可以让我们在事件触发时注册JS方法,可以让我们用自己注册的JS方法控制整个大局,在这些事件响应方法中的this直接指向的是Vue 的实例。
Vue2的生命周期图

实例:本地伪造json数据。
Axios异步通信.html
<head>
<meta charset="UTF-8">
<title>Axios异步通信</title>
<!-- v-clock:解决闪烁问题-->
<style>
[v-clock]{
display:none;
}
</style>
</head>
<body>
<div id="vue" v-cloak>
<div>{{info.name}}</div>
<div>{{info.address.city}}</div>
// 使用 v-bind 绑定 info.url
<a v-bind:href="info.url">点我跳转</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var vm = new Vue({
el:"#vue",
// data方法
data(){
return{
// 请求的返回参数,必须和json字符串一样
info:{
name:null,
address:{
street:null,
city:null,
country:null
},
links:[],
url:null
}
}
},
mounted(){ // 钩子函数 链式编程
axios.get('data.json').then(response=>(console.log(this.info=response.data)))
}
});
</script>
data.json
{
"name":"Java" ,
"url": "https://www.java.com/",
"page": 1,
"isNonProfit": true,
"address": {
"street":"中山路",
"city":"广州",
"country":"中国"
},
"links":[
{
"name":"bilibili",
"url":"https://www.bilibili.com/"
},
{
"name":"java",
"url" : "https://www.java.com/"},
{
"name" : "百度",
"url" : "https://www.baidu.com/"
}
]
}
六、计算属性
什么是计算属性?
计算属性的重点突出在 属性
两个字上(属性是名词),首先它是个 属性
其次这个属性有 计算
的能力(计算是动词),这里的 计算
就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而;可以想象为缓存!
实例:
<div id="app">
<p>currentTime1 {{currentTime1()}}</p> <!--调用方法需加()-->
<p>currentTime2 {{currentTime2}}</p> <!--调用属性不需要加()-->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"Hello,Vue!"
},
methods:{
currentTime1:function () {
return Date.now(); // 返回一个时间戳
}
},
computed:{
currentTime2:function () {
this.message;
return Date.now(); // 返回一个时间戳
}
}
});
</script>
注意:methods和computed里的东西不能重名
说明:
- methods:定义方法,调用方法使用currentTime1(),需要带括号
- computed:定义计算属性,调用属性使用currentTime2,不需要带括号; this.message是为了能够让currentTime2观察到数据变化而变化
- 如果在方法中的值发生了变化,则缓存就会刷新。可以在控制台使用vm.message="123",改变下数据的值,再次测试观察效果!
结论:
- 调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销。
七、内容分发(插槽)
在Vue.js中我们使用<slot>
元素作为承载分发内容的出口,称其为插槽
,可以应用在组合组件的场景中;
测试
比如准备制作一个待办事项组件 (todo),该组件由待办标题 (todo-title) 和待办内容 (todo-items) 组成,但这三个组件又是相互独立的,该如何操作呢?
第一步:定义一个待办事项的组件todo
<div id="app">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
Vue.component("todo",{
template: '<div>\
<div>待办事项</div>\
<ul>\
<li>学习Vue</li>\
</ul>\
</div>'
});
var vm = new Vue({
el:"#app"
});
</script>
第二步:我们需要让,待办事项的标题和值实现动态绑定,所以可以留出一个插槽。
(1)将上面的代码留出一个插槽,即使用<slot>
Vue.component("todo",{
template: '<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
(2)定义一个名为todo-title的待办标题组件和todo-items的待办内容组件
// todo-title的待办标题组件
Vue.component("todo-title",{
props: ['title'],
template: '<div>{{title}}</div>'
});
// todo-items的待办内容组件
Vue.component("todo-items",{
props:['item'],
template:'<li>{{item}}</li>'
});
(3)实例化 Vue并初始化数据
var vm = new Vue({
el:"#app",
data:{
title:"编程语言列表",
todoItems:['Vue','Java','Python']
}
});
(4)将上面的数据值,通过插槽插入
<div id="app">
<todo>
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" v-bind:item="item"></todo-items>
</todo>
</div>
(5)总程序
<div id="app">
<todo>
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" v-bind:item="item"></todo-items>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
// slot:插槽
Vue.component("todo",{
template: '<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
// todo-title的待办标题组件
Vue.component("todo-title",{
props: ['title'],
template: '<div>{{title}}</div>'
});
// todo-items的待办内容组件
Vue.component("todo-items",{
props:['item'],
template:'<li>{{item}}</li>'
});
var vm = new Vue({
el:"#app",
data:{
title:"编程语言列表",
todoItems:['Vue','Java','Python','c','#c','c++']
}
});
</script>
效果图
八、组件事件
在组件的模板表达式中,可以直接使用 this.$emit()
方法触发自定义事件 。
通过上一张的代码不难发现,数据项是在Vue的实例中,但删除操作要在组件中完成,那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递与事件分发了,这个过程需要在前端进行。
Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题,使用this.$emit('自定义事件名',参数)
,操作过程如下:
实例:自定义事件
(1)在vue的实例中,增加了methods对象
并定义了一个名为removeTodoltems()
的方法
var vm = new Vue({
el:"#app",
data:{
title:"编程语言列表",
todoItems:['Vue','Java','Python','c','#c','c++']
},
methods:{
// 该方法可以被模板中自定义事件触发
removeTodoItems:function (index) {
console.log("删除 "+ this.todoItems[index] + " 成功了!");
this.todoItems.splice(index,1);
}
}
});
(2)修改 todo-items
待办内容组件的代码,增加一个删除按钮,并且绑定事件。
- 需要在
props
中添加index
索引下标,以此来删除位置 - 在
template
中添加<button>
按钮,并绑定click
鼠标点击事件 - 还需要一个
methods对象
中设置一个删除的方法名为remove_component
- 最后就是使用
this.$emit('remove')
来触发删除这个名为remove
自定义事件
// todo-items的待办内容组件
Vue.component("todo-items",{
props:['item','index'],
template:'<li>{{index}}---{{item}} <button v-on:click="remove_component">删除</button></li>',
methods: {
remove_component:function () {
// 这里的 remove 是自定义事件的名称,需要在HTML使用v-on:remove方式绑定监听
this.$emit('remove');
}
}
});
(3)修改todo-items
待办内容组件的HTML代码,增加一个自定义事件,比如叫 remove
事件,可以和组件的方法绑定,然后绑定到vue的方法中。
- 需要重新在
v-for
循环中添加index
下标便于循环 - 使用
v-bind
绑定index
下标 - 使用
v-on
绑定监听remove
事件,并且需要触发removeTodoItems(index)
这个删除方法
<todo-items slot="todo-items" v-for="(item,index) in todoItems" v-bind:item="item"
v-bind:index="index"
v-on:remove="removeTodoItems(index)"></todo-items>
逻辑理解
- Vue实例下的
removeItems()
可以被前端调用,因为前端和Vue实例是绑定的,通过el:#app
进行绑定; - 组件与前端通过
slot
插槽进行绑定; - 需要
删除的对象
在Vue实例里,不能直接通过组件直接去删除Vue实例里的对象
,需要通过前端去实现删除; - 由于组件与前端绑定事件,在组件需要删除事件时通过前端
v–on:remove
去调用Vue实例中的removeItems
方法进行删除 - 由此可简单的知道组件的
this.$emit
将事件分发回前端,而前端v–on:remove
再将事件给removeItems
处理。
(5)总程序
<div id="app">
<todo>
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<todo-items slot="todo-items" v-for="(item,index) in todoItems"
v-bind:item="item"
v-bind:index="index"
v-on:remove="removeTodoItems(index)"></todo-items>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
Vue.component("todo",{
template: '<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
// todo-title的待办标题组件
Vue.component("todo-title",{
props: ['title'],
template: '<div>{{title}}</div>'
});
// todo-items的待办内容组件
Vue.component("todo-items",{
props:['item','index'],
template:'<li>{{index}}---{{item}} <button @click="remove_component">删除</button></li>',
methods: {
remove_component:function () {
// 这里的 remove 是自定义事件的名称,需要在HTML使用v-on:remove方式绑定监听
this.$emit('remove');
}
}
});
var vm = new Vue({
el:"#app",
data:{
title:"编程语言列表",
todoItems:['Vue','Java','Python','c','#c','c++']
},
methods:{
// 该方法可以被模板中自定义事件触发
removeTodoItems:function (index) {
console.log("删除 "+ this.todoItems[index] + " 成功了!");
this.todoItems.splice(index,1);
}
}
});
</script>
九、第一个vue-cil程序
什么是vue-cli?
vue-cli官方提供的一个脚手架,用于快速生成一个vue的项目模板;
预先定义好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速。
主要的功能:
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
需要的环境
Node.js : http://nodejs.cn/download/ 下载v12.22.12版本
Git : https://git-scm.com/downloads
确认nodejs安装成功
- cmd 下输入
node -v
,查看是否能够正确打印出版本号; - cmd 下输入
npm -v
,查看是否能够正确打印出版本号; - 这个npm,就是一个软件包管理工具。
安装Node.js镜像加速器
# -g 就是全局安装
npm install cnpm@7.1.1 -g
安装位置在 C:\Users\lovey\AppData\Roaming\npm
安装 vue-cli
cnpm install vue-cli -g
# 测试是否安装成功
# 查看可以基于哪些模板创建vue 应用程序,通常我们选择webpack
vue list
第一个 vue-cli 应用程序
- 创建一个Vue项目,我们随便建立一个空的文件夹在电脑上,在D盘下新建一个目录
D:\Project\vue-study
;
- 创建一个基于 webpack 模板的 vue 应用程序
# 这里的 myvue 是项目名称,可以根据自己的需求起名
vue init webpack myvue
一路都选择no即可;
说明:
- Project name:项目名称,默认回车即可
- Project description:项目描述,默认回车即可
- Author:项目作者,默认回车即可
- lnstall vue-router:是否安装 vue-router,选择n不安装(后期需要再手动添加)
- Use ESLint to lint your code:是否使用ESLint做代码检查,选择n不安装(后期需要再手动添加)
- Set up unit tests:单元测试相关,选择n不安装(后期需要再手动添加)
- Setup e2e tests with Nightwatch:单元测试相关,选择n不安装〈后期需要再手动添加
- Should we run npm install for you after the project has been created:创建完成后直接初始化,选择n,我们手动执行;运行结果!
-
初始化并运行
在cmd下运行这些代码
cd myvue
cnpm install
npm run dev
-
效果图
十、Webpack
什么是Webpack ?
webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
安装Webpack
WebPack是一款模块加载器兼打包工具,它能把各种资源,如JS、JSX、ES6、SASS、LESS.图片等都作为模块来处理和使用。
安装:
npm install webpack -g
npm install webpack-cli -g
测试安装成功
webpack -v
webpack-cli -v
配置Webpack
创建webpack.config.js
配置文件
- entry:入口文件,指定WebPack 用哪个文件作为项目的入口
- output:输出,指定WebPack 把处理完成的文件放置到指定路径
- module:模块,用于处理各种类型的文件
- plugins:插件,如:热更新、代码重用等
- resolve:设置路径指向
- watch:监听,用于设置文件改动后直接打包
使用Webpack
-
创建一个名为 Webpack_study 项目
-
使用IDEA打开 Webpack_study 项目,创建一个名为
modules
的目录,用于放置JS模块等资源文件 -
在
modules
下创建模块文件,如hello.js
,用于编写JS模块相关代码// 暴露一个方法:sayHi exports.sayHi = function (){ document.write("<h1>Hello Webpack</h1>") };
-
在
modules
下创建一个名为main.js
的入口文件,用于打包时设置entry
属性// require 导入hello.js模块,可调用模块中的方法 var hello = require("./hello"); hello.sayHi();
-
在项目目录下创建
webpack.config.js
配置文件,使用webpack
命令打包module.exports = { // 程序的入口 entry:"./modules/main.js", output:{ // 程序打包输出的位置 filename:"./js/bundle.js" } };
-
在命令行执行
webpack
这个命令执行打包。此时会打包到dist/js
这个文件里的bundle.js
-
可以在项目下创建一个index.html文件来导入这个包,验证是否成功
<body> <script src="../dist/js/bundle.js"></script> </body>
十一、Vue-Router路由
什么是Vue Router?
Vue Router 是 Vue.js 的官方路由管理器。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括:
- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue.js 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
参考官方手册:https://router.vuejs.org/zh/guide/
安装
基于第一个vue-cli
进行测试学习;先查看node_modules
文件中是否存在vue-router
vue-router
是一个插件包,所以我们还是需要用npm/cnpm来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。
npm install vue-router@3.5.2 --save-dev
如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能:
import vue from 'vue'
import VueRouter from 'vue- router'
vue.use(VueRouter);
测试
-
先删除没有用的东西
-
components目录下存放我们自己编写的组件
-
定义一个
Content.vue
的组件<template> <h1>内容页</h1> </template> <script> export default{ name:"Content" } </script> <style scoped> </style>
-
安装路由,在src目录下,新建一个文件夹:
router
,专门存放路由,创建index.js文件import Vue from "vue"; // 导入路由插件 import VueRouter from "vue-router"; // 导入上面定义的组件 import Content from "../components/Content.vue"; // 专门在这里配置组件 // 安装路由 Vue.use(VueRouter); // 配置导出路由 export default new VueRouter({ routes:[ { // 路由路径 path:'/content', // 路由名称 name:'content', // 跳转到组件 component:Content } ] });
-
在
main.js
中配置路由import Vue from 'vue' import App from './App' // 导入上面创建的路由配置目录 import router from "./router"; // 关闭生产模式下给出的提示 Vue.config.productionTip = false new Vue({ el: '#app', // 配置路由 router, components: { App }, template: '<App/>' });
-
在
App.vue
中使用路由<template> <div id="app"> <h1>Vue-Router</h1> <router-link to="/content">内容页</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
总结
-
需要创建特定文件去保存路由;
-
main.js
文件一般是写死的,只需要引入路由的配置目录; -
组件可以在
components
文件下创建,而这个组件需要在router
路由文件的index.js
文件里去配置这个组件的路由; -
展示的内容需要在
App.vue
展示文件里去给这个组件配置链接和显示。
十一、Vue+ElementUI
Element是一个UI的组件库,可以方便的进行使用
创建工程
-
创建一个名为hello-vue的工程
vue init webpack hello-vue
-
安装依赖,我们需要安装 vue-router、element-ui、sass-loader和node-sass四个插件
#进入工程目录 cd hello-vue #安装vue-router npm install vue-router@3.5.2 --save-dev #安装element- ui npm i element-ui -s #安装依赖 npm install #安装SASS加载器 cnpm install sass-loader@7.3.1 node-sass@4.12.0 --save-dev #启动测试 npm run dev
-
Npm命令解释:
npm install moduleName
:安装模块到项目目录下npm install -g moduleName
: -g 的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看npm config prefix的位置npm install -save moduleName
:--save 的意思是将模块安装到项目目录下,并在patkage 文件的dependencies节点写入依赖,-S为该命令的缩写npm install -save-dev moduleName
:--save-dev的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖,-D为该命令的缩写
-
在
src
文件下新创建一个view
文件来管理展示页面,并新创建Login.vue
登录组件,登陆组件可以在ELementUI去获取;其中带el-*
都是ELementUI的组件。<template> <div> <el-form ref="loginForm" :model="form" :rules="rules" label-width="8epx" class="login-box"> <h3 class="login-title">欢迎登录</h3> <el-form-item label="账号" prop="username"> <el-input type="text" placeholder="请输入账号" v-model="form.username"/> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" placeholder="请输入密码" v-model="form.password"/> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="onSubmit( 'loginForm' )">登录</el-button> </el-form-item> </el-form> <!--提示窗口--> <el-dialog title="温馨提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose"> <span>请输入账号和密码</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="dialogVisible = false">确 定</el-button> </span> </el-dialog> </div> </template> <script> export default { name: "Login", data() { return { form: { username: '', password: '' }, //表单验证,需要在el-form-item 元素中增加prop属性 rules: { username: [ {required: true, message: '账号不可为空', trigger: 'blur'} ], password: [ {required: true, message: '密码不可为空', trigger: 'blur'} ] }, //对话框显示和隐藏 dialogVisible: false } }, methods: { onSubmit(formName) { // 为表单绑定验证功能 this.$refs[formName].validate((valid) => { if (yalid) { // 使用 vue-router 路由到指定页面,该方式称之为编程式导航 this.$router.push("/main"); } else { this.dialogVisible = true; return false; } }); } } } </script> <style lang="scss" scoped> .login-box { border: 1px solid #DCDFE6; width: 350px; margin: 180px auto; padding: 35px 35px 15px 35px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; box-shadow: 0 0 25px #909399 ; } .login-title { text-align: center; margin: 0 auto 40px auto; color: #303133; } </style>
-
新建一个
router
文件夹并创建index.js
文件来管理路由,将上面创建的登录组件导入这个路由文件并配置登陆组件的路由。import Vue from "vue"; import Router from "vue-router"; // 导入登录组件 import Login from "../views/Login.vue"; Vue.use(Router); export default new Router({ routes:[ { // 配置登陆组件路由 // 路由路径 path:'/login', // 跳转到组件 component:Login } ] })
-
上面的路由配置完后,就需要在
main.js
文件里导入路由配置目录,还需要导入ELement-UI和ElementUI的CSSimport Vue from 'vue' import App from './App' // 导入路由配置目录 import router from "./router"; // 导入Element UI import ElementUI from "element-ui"; // 导入ElementUI的css import 'element-ui/lib/theme-chalk/index.css'; // 显式声明使用router Vue.use(router); // 显式声明使用ElementUI Vue.use(ElementUI) new Vue({ el: '#app', router, render: h => h(App) // ELementUI })
-
配置完
main.js
文件后,在配置App.vue
文件<template> <div id="app"> <router-view></router-view> </div> </template> <script> export default { name: 'App' } </script>
-
配置以上,可以在命令行执行
npm run dev
效果图

十二、路由嵌套
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:
/user/johnny/profile /user/johnny/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
路由嵌套的具体实现还是那上一章的代码实现
-
在
views
视图文件下创建Main.vue
主页组件<script setup> export default { name:"Main" } </script> <template> <h1>展示系统</h1> </template> <style scoped> </style>
-
在
views
视图文件下创建一个user
文件夹,并在新创建用户信息和用户列表组件; -
用户信息组件,在views/user目录下创建一个名为Profile.vue的文件
<script setup> export default { name:"UserProfile" } </script> <template> <h1>个人信息</h1> </template> <style scoped> </style>
-
用户列表组件,在views/user目录下创建一个名为List.vue的文件
<script setup> export default { name:"UserList" } </script> <template> <h1>用户列表</h1> </template> <style scoped> </style>
-
在Element-UI组件库中查找侧边栏的有关代码,并将代码放入Main.vue文件中
<template> <el-container> <el-aside width="200px"> <el-menu :default-openeds="['1']"> <!--侧边栏--> <el-submenu index="1"> <template slot="title"><i class="el-icon-caret-right"></i>用户管理</template> <el-menu-item-group> <el-menu-item index="1-1"> <router-link to="/user/profile">个人信息</router-link> </el-menu-item> <el-menu-item index="1-2"> <router-link to="/user/list">用户列表</router-link> </el-menu-item> </el-menu-item-group> </el-submenu> <!--侧边栏--> <el-submenu index="2"> <template slot="title"><i class="el-icon-caret-right"></i>内容管理</template> <e1-menu-item-group> <el-menu-item index="2-1">分类管理</el-menu-item> <el-menu-item index="2-2">内容列表</el-menu-item> </e1-menu-item-group> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="text-align: right; font-size: 12px"> <el-dropdown> <i class="el-icon-setting" style="margin-right:15px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人信息</el-dropdown-item> <el-dropdown-item>退出登录</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </el-header> <el-main> <router-view/> </el-main> </el-container> </el-container> </template> <script> export default { name: "Main" } </script> <style lang="scss" scoped> .el-header { background-color: #02a9ff; color: #333; line-height: 60px; } .el-aside { color: #333; } </style>
-
在
router
路由文件下分别配置上面的三个组件,从而实现主页组件嵌套用户信息和用户列表组件import Vue from "vue"; import Router from "vue-router"; // 导入主页组件 import Main from "../views/Main.vue"; // 导入登录组件 import Login from "../views/Login.vue"; // 导入用户信息组件 import UserProfile from "../views/user/Profile.vue"; // 导入用户列表组件 import UserList from "../views/user/List.vue"; // 显式声明router Vue.use(Router); export default new Router({ routes:[ { // 路由路径 path:'/login', // 跳转到组件 component:Login }, { // 路由路径 path:'/main', // 跳转到组件 component:Main, // 路由嵌套,嵌套了用户信息和用户列表组件 children:[ {path:"/user/profile",component:UserProfile}, {path:"/user/list",component:UserList} ] } ] })
-
在命令行执行
npm run dev
效果图

十三、编程式导航及重定向
1. 导航到不同的位置两种方式
(1)不使用props传参
还是使用上一章的代码实现
-
需要将
main.vue
主页组件中的<router-link>
的内容稍作修改原本接收的/user/profile的个人信息
<router-link to="/user/profile">个人信息</router-link>
修改为
<!-- 需要对象v-bind: 绑定, name:组件名字 params:传递参数--> <router-link v-bind:to="{name:'UserProfile', params:{id:31}}">个人信息</router-link>
-
需要修改
router
目录下的index.js
文件,将个人信息的路由路径添加/:id
获取主页组件中的id
信息,并添加组件名字name="UserProfile"
// 路由嵌套 children:[ {path:"/user/profile/:id",name:"UserProfile",component:UserProfile}, {path:"/user/list",component:UserList} ]
-
需要将
Profile.vue
文件里稍作修改,使用$route
属性访问路由<template> <!--所有元素不能在根节点下--> <div> <h1>个人信息</h1> {{ $route.params.id }} </div> </template>
效果图

(2)使用props传参
沿用上面的代码,main.js
文件不做修改
-
需要在
router
目录下的index.js
路由文件进行添加props:true
// 路由嵌套 children:[ {path:"/user/profile/:id",name:"UserProfile",component:UserProfile,props:true}, ]
-
因为路由文件已经
props
允许传参,那么需要在Profile.vue
稍作修改,相当于之前所需的在组件中取得参数<template> <div> <h1>个人信息</h1> {{ id }} </div> </template> <script> export default { props:['id'], name:"UserProfile" } </script> <style scoped> </style>
效果跟第一个一样,但是通过
props
传参数会更方便,如果还想添加name
,age
等属性直接加在props
的数组中,然后再去路由文件添加相应的:name
或者age
2. 重定向
URL不改变的称为转发,URL改变的称为重定向
案例:实现重定向
沿用上面的代码
-
在需要在
router
目录下的index.js
路由文件进行添加重定向路径routes:[ { // 重定向路由路径 path:"/goLogin", // 返回登陆界面 redirect:'/login' } ]
-
为了效果明显,需要在
main.vue
视图组件里新添加返回登陆页面的侧边栏<el-menu-item index="1-3"> <router-link to="/goLogin">返回登陆页面</router-link> </el-menu-item>
-
可以在主页点击返回登陆页面查看是否重定向成功

重定向成功!

十四、路由模式与404
路由模式的两种
- hash:路径带#符号,如http://localhost/#/login
- history:路径不带#符号,如http://localhost/login
设置路由函数下mode
属性就可以使用history
export default new Router({
// 设置 mode 为 history 即可
mode:'history',
routes:[
...
]
})
处理404,在views
目录下创建一个名为NotFound.vue
的视图组件
<script>
export default {
name:"NotFound"
}
</script>
<template>
<div>404,你的页面无法访问!</div>
</template>
<style scoped>
</style>
为NotFound.vue
的视图组件配置路由
export default new Router({
mode:'history',
routes:[
{
// 404接收任何请求
path:'*',
component:NotFound
}
]
})
路由钩子与异步请求
beforeRouteEnter:(to,from,next)=>{}
:在进入路由前执行beforeRouteLeave:(to,from,next)=>{}
:在离开路由前执行
export default {
props:['id'],
name:"UserProfile",
// 钩子函数
beforeRouteEnter:(to,from,next)=>{
console.log("在路由进来之前");
next();
},
beforeRouteLeave:(to,from,next)=>{
console.log("在路由离开之前");
next();
}
}
参数说明:
- to:路由将要跳转的路径信息,相当于request
- from:路径跳转前的路径信息,相当于response
- next:路由的控制参数
- next() 跳入下一个页面
- next('/path') 改变路由的跳转方向,使其跳到另一个路由,与拦截器相似
- next(false) 返回原来的页面
- next((vm)=>{})仅在
beforeRouteEnter
中可用,vm
是组件实例
在钩子函数中使用异步请求
-
安装Axios
cnpm install axios@0.16.2 vue-axios@2.0.2 -s
-
main.js
引用Axiosimport Vue from 'vue' import App from './App' // 导入Axios异步请求 import axios, {Axios} from "axios"; // 导入VueAxios import VueAxios from "vue-axios"; Vue.use(VueAxios,axios);
-
在
static
静态文件里创建mock
测试文件,在创建data.json
模拟数据{ "name":"Java" , "url": "https://www.java.com/", "page": 1, "isNonProfit": true, "address": { "street":"中山路", "city":"广州", "country":"中国" }, "links":[ { "name":"bilibili", "url":"https://www.bilibili.com/" }, { "name":"java", "url" : "https://www.java.com/"}, { "name" : "百度", "url" : "https://www.baidu.com/" } ] }
-
修改
Profile.vue
文件,进行axios异步请求<template> <div> <h1>个人信息</h1> {{ id }} </div> </template> <script> export default { props:['id'], name:"UserProfile", // 钩子函数 beforeRouteEnter:(to,from,next)=>{ console.log("在路由进来之前"); // 加载数据 next(vm =>{ vm.getData(); // 进入路由之前执行getData }); }, beforeRouteLeave:(to,from,next)=>{ console.log("在路由离开之前"); next(); }, methods:{ getData:function () { this.axios({ method:'get', url:'http://localhost:8080/static/mock/data.json' }).then(function (response) { console.log(response); }) } } } </script> <style scoped> </style>
-
进入个人信息界面获得数据
Vue小结
1. 常用的7个属性
- el属性
- 用来指示vue编译器从什么地方开始解析vue的语法,可以说是一个占位符。
- data属性
- 用来组织从view中抽象出来的属性,可以说将视图的数据抽象出来存放在data中。
- template属性
- 用来设置模板。会替换页面元素,包括占位符。
- methods属性
- 放置页面中的业务逻辑,js方法一般都放置在methods中
- render属性
- 创建真正的Virtual Dom
- computed属性
- 用来计算
- watch属性
- watch:function(new,old){}
监听data中数据的变化
两个参数。一个返回新值,一个返回旧值
- watch:function(new,old){}
2. 常用内置指令
- v-text:更新元素的 textContent
- v-html:更新元素的 innerHTML
- v-if:如果为 true,当前标签才会输出到页面
- v-else:如果为 false,当前标签才会输出到页面
- v-show:通过 display 样式来控制显示 / 隐藏
- v-for:遍历数组 / 对象
- v-on:绑定监听事件,可以简写为
v-on:click=''
简写为@click=''
- v-bind:绑定解析表达式,可以简写为
:
- v-model:双向数据绑定
- v-cloak : 防止闪现,与 css 配合: [v-cloak]
3. 组件化
- 组合组件
slot
插槽 - 组件内部绑定事件需要使用到
this.$emit("事件名",参数)
; 参数可不加 - 计算属性的特色,缓存计算数据
遵循SoC关注度分离原则, Vue是纯粹的视图框架,并不包含,比如Ajax之类的通信功能,为了解决通信问题,我们需要使用Axios框架做异步通信;
解决方案
1、vue闪烁问题
Vue 中的闪烁问题通常发生在使用v-if或v-show指令时,尤其是在元素首次加载时。这种闪烁通常是因为Vue 在初次渲染时将元素直接显示或隐藏,导致页面布局产生变化,从而引起闪烁。
解决方法:
- 使用
v-cloak
指令:可以防止在Vue处理前显示原始的花括号语法。
<div v-cloak>
{{ message }}
</div>
然后在CSS样式中添加:
<style>
[v-cloak] {
display: none;
}
</style>
-
使用v-if和v-else-if,避免使用v-show。
-
使用CSS来隐藏元素,而不是v-show指令。
-
使用Vue的
<transition>
元素包裹需要动画的元素,并定义相应的CSS过渡效果。
<!-- 使用 v-cloak 防止闪烁 -->
<div v-cloak>
{{ message }}
</div>
<!-- 使用 transition 包裹需要动画的元素 -->
<transition name="fade">
<div v-if="show">This will fade in and out</div>
</transition>
<!-- 在样式中定义 transition -->
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
opacity: 0;
}
</style>
2、Webpack打包出错问题
1. 错误描述
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
2.错误原因
没有设置webpack的模式,是生产还是开发(production or development)
3、解决办法
1、在package.json文件里进行修改:增加如下:
"dev": "webpack --mode development",
"build": "webpack --mode production"
2、也可以在写webpack.config.js
中增加一个选项:mode;
module.exports = {
mode:"development",
...
}
3、Vue路由报错
报错信息:
export ‘default‘ (imported as ‘VueRouter‘) was not found in ‘vue-router‘
问题分析:
在package.json里面查看到vue版本和vue-router版本
发现vue版本和vue-router版本不兼容
- vue2搭配vue-router3
- vue3搭配vue-router4
这里使用用vue-cli脚手架构建的是vue2项目,安装的路由版本最好是3
解决办法:
卸载这个不兼容的路由版本,重新安装对应的版本:
npm uninstall vue-router
安装3版本:
npm install vue-router@3.5.2
4、关于使用Element-UI遇到的问题
1. Error: Node Sass version 9.0.0 is incompatible with ^4.0.0.
建议:查看 Nodejs、sass-loader和node-sass各版本相适应版本。
下面是常见的node版本和对应的node-sass版本

本文作者:粤先生
本文链接:https://www.cnblogs.com/magicYue/p/18174189
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。