「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
Vue的介绍
Vue是用于构建用户界面的渐进式框架,主要特点是易用,灵活,性能优良
有兴趣可以查看一下官方介绍的视频点击这里
Vue的配置
安装node.js
我们需要的是node.js里的npm,安装很简单首先去官网下载安装包node.js官网选择下载最新版本即可,一直下一步进行安装
安装完成后直接win+R打开cmd,输入node -v查看版本号
顺带检查一下npm(包管理工具),输入npm -v
安装Vue CLI(脚手架)
Vue CLI就是Vue工程的升级版,更牛逼一点
执行以下命令(因为npm下载的插件都是国外的服务器中下载,以下代码就是将镜像切换成淘宝镜像)
sudo npm install -g cnpm --registry=https://registry.npm.taobao.org
// 注意这里要用的是cnpm
cnpm install -g @vue/cli
输入vue --version验证是否安装成功
创建Vue工程
- 执行以下命令创建Vue工程
// vue 创建 工程名
vue create vue-01
- 此时会出现几个选项,选择最后一个---自定义配置(键盘上下键选择,回车确认)

- 选中后进入自定义配置选项,勾选Babel,Router即可(空格选中/反选,回车确认)
- 下一步是询问你是否使用历史模式的路由器,根据自己的需要来选择Y OR N,然后回车

- 然后询问你Babel等配置文件放哪,我们选择放在package.json里

- 最后一步是问你是否保存这次配置
完成以上操作后我们成功创建了一个原始的Vue项目,脚手架还送了两个命令
执行这两个命令
如图给了我们两个地址,第二个为你电脑的IP地址,打开两个中任意一个即可

打开后看到这个就代表你成功了
Vue工程目录
- assets:存放项目中需要用到的资源文件,css,js,images等
- componets:存放vue开发中的公共组件
- router:vue路由的配置文件
- views:存放页面文件
- app.vue:根组件
- main.js:项目的入口文件,定义了vue实例,并引入根组件app.vue
Vue代码结构
我们可以将代码写在app.vue中,以下是app.vue中的代码
// template即模版的意思,每一个vue文件里必须要有一个,在这里写HTML代码
<template>
<div id="app"></div>
</template>
// 在这里写js逻辑相关的代码
<script>
export default {
name: "app"
};
</script>
// 这里写样式代码
<!-- scope的意思表示这段样式只在本xxx.vue文件中生效,其他xxx.vue文件中不会生效,有锁定的意思 -->
<style scope>
</style>
每一个Vue文件由三部分组成template、script、style,分别对应HTML,JavaScript,CSS
template里面只允许有一个块状元素,其他所有的标签都在这个块标签内
Vue的声明渲染
使用差值表达式来将数据渲染在页面中就是一个两层的大括号{{}}
字符串渲染
JS中我们会这样做
let str = "Vue";
let template = `<h2>${str}</h2>`;
// 后面省略一系列DOM操作
在Vue中首先在template部分确定插值表达式的位置上,并填充变量名:
<template>
<h2>{{title}}</h2>
</template>
然后再在script部分定义字符串变量:
<script>
// export default是固定格式,不需要纠结
export default {
// 模块的名字
name: "app",
// 页面中数据存放的地方
data() {
return {
title: "Vue"
};
}
};
</script>
数组的渲染
填充数据时可以采用数组下标的形式去取数据:
<h2 class="title">{{title}}</h2>
<ul class="list">
<li>{{todoList[0]}}</li>
<li>{{todoList[1]}}</li>
<li>{{todoList[2]}}</li>
<li>{{todoList[3]}}</li>
<li>{{todoList[4]}}</li>
</ul>
然后在script里定义数组
<script>
export default {
name: "app",
data() {
return {
title: "今日待完成事项",
todoList: [
"完成HTML标签学习",
"完成CSS文字样式学习",
"完成CSS盒模型学习",
"完成Flex布局学习",
"完成JavaScript入门学习"
]
};
}
};
</script>
- data方法里面用来存放数据或者全局变量
- scope可以理解为锁将css所在本文件中,只在本文件中有效
对象数组
渲染一个班级名单列表:
| 姓名 | 班级 | 总成绩 |
|---|---|---|
| 张三 | 三年级二班 | 290 |
| 李四 | 三年级二班 | 270 |
| 王五 | 三年级二班 | 270 |
需要定义一个这样的变量:
<script>
export default {
name: "app",
data(){
return {
list:[
{
name:"张三",
grade:"三年级二班",
mark:290
},
{
name:"李四",
grade:"三年级二班",
mark:270
},
{
name:"王五",
grade:"三年级二班",
mark:270
}
]
}
}
};
</script>
处理用户输入
v-model(双向绑定) --input
双向绑定就是我们在input框当中输入内容时,显示内容的地方也会随之改变
定义一个HTML代码
<template>
<!--这里的v-model="message"将input的值绑定-->
<p class="page">{{message}}</p>
<input type="text" v-model="message" placeholder="请输入你想输入的内容" />
</template>
定义要用到的变量
<script>
export default {
name: "app",
data() {
return {
message: ""
};
}
};
</script>
也就是改变input框中的值,message的值也将改变
v-model --checkbox
复选框上使用双向绑定,案例如下:
<template>
<div id="app">
<div class="prepare">
<div class="left">{{checkBox}}</div>
<div class="right">{{plan}}</div>
</div>
<div class="textarea">
<div class="left">
<input type="checkbox" id="run" value="跑步" v-model="checkBox">
<label for="run">跑步</label>
<input type="checkbox" id="yujia" value="瑜伽" v-model="checkBox">
<label for="yujia">瑜伽</label>
<input type="checkbox" id="yangwo" value="仰卧起坐" v-model="checkBox">
<label for="yangwo">仰卧起坐</label>
</div>
<div class="right">
<textarea placeholder="请输入你的健身计划" v-model="plan"></textarea>
</div>
</div>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
checkBox: [],
plan:""
};
}
};
</script>
因为复选框的内容可以看做一个数组,所以定义变量时可以将它定义成数组,每个选项都用v-model绑定
处理用户事件
方法
Vue中进行两步操作,第一步:给元素添加事件,第二步给事件加方法
- 在Vue中添加事件叫做事件绑定使用
@click="方法名"如:
<button @click="add()">按钮</button>```
- methods(方法) 将方法抽离出来放在对应的位置
<script>
export default{
name:"app",
data(){
return {
counter: 0
}; //这里要加分号
}, //这里要加逗号
methods:{
// 在这里存放方法
add:function(){
//方法内容
this.counter++
}
}
}
</script>
使用data定义的变量时要在前面加this,
@click="add()",括号里可以传递参数
事件修饰符(与js中的作用一样)
- 阻止冒泡事件
<div @click.stop="fn2"></div>
- 捕获事件
<div class="div2" @click.capture="fn2"></div>
- 阻止默认事件
<div class="div2" @click.prevent="fn2"></div>
监听数据变化,在Vue中是通过侦听器来实现的,时刻监听某个数据的变化
watch的基本用法
- 侦听器的书写位置
写在export default中与data和methods用
,隔开
<script>
export default {
name: "app",
// 数据 key---data value---Function
data: function () {
return {
count: 1
};
},
// 方法 key---methods value---{}
methods: {},
//在export default中添加即可不用管顺序
watch: {
//监听内容
count() {
console.log("count发生了变化");
}
}
};
</script>
监听器里的方法一定要与被监听的变量名一致
侦听器的进阶用法
获取前一次的值
有的时候需要上一次的数据,再上一个案例中添加一个参数即可获取旧值
watch:{
inputValue(value,oldValue) {
// 第一个参数为新值,第二个参数为旧值,不能调换顺序
console.log(`新值:${value}`);
console.log(`旧值:${oldValue}`);
}
}
handler方法和immediate属性
- immediate: 可以让页面第一次渲染的时候去触发侦听器
- handler: 监听到修改之后这个函数会执行
侦听器实际上是一个对象,里面包含了handler方法和其他属性:
<script>
export default {
name: "app",
watch: {
firstName: {
handler: function (newName, oldName) {
this.fullName = newName + " " + this.lastName;
},
immediate: true
}
}
};
</script>
HTML属性渲染语法
动态绑定--v-bind
在HTML中每个标签都有自己的属性,要让他们随data中定义的值变化而变化,就要用到属性绑定
例如img标签的alt属性:
<template>
<div id="app">
<img src="#" v-bind:alt="imgText" />
</div>
</template>
<script>
export default {
name: "app",
// 数据
data() {
return {
imgText:'周杰伦演唱会图片'
};
}
};
</script>
动态渲染图片
<template>
<div id="app">
<div class="album">
<img :src="imgList[0]" />
<img :src="imgList[1]" />
<img :src="imgList[2]" />
<img :src="imgList[3]" />
</div>
</div>
<script>
export default {
name: "app",
data() {
return {
imgList:[
'http://pic2.zhimg.com/50/v2-4a06728efc99ba874a5d7422fd55aaed_hd.jpg',
'http://img2.imgtn.bdimg.com/it/u=372764256,3394765004&fm=26&gp=0.jpg',
'http://img1.imgtn.bdimg.com/it/u=1898582417,1582081952&fm=26&gp=0.jpg',
'http://b-ssl.duitang.com/uploads/item/201707/10/20170710141316_vVFNh.thumb.700_0.jpeg'
]
};
};
}
</script>
这里用
:src是v-bind:src的简写,这里可以使用循环来减少代码量
模板中使用表达式
在差值表达式中不仅可以写一个变量,还可以进行简单的计算
<template>
<div id="app">
<ul>
<li>{{index + 1 }}</li>
</ul>
</div>
</template>
<script>
export default {
name: "app",
// 数据
data() {
return {
index: 0
};
}
};
</script>
还可以在模板中使用三元表达式
<template>
<div id="app">
<p>{{ flag?'你已经及格':'你还没有及格' }}</p>
<button @click="exchange">转换</button>
</div>
</template>
<script>
export default {
name: "app",
// 数据
data() {
return {
flag:true
};
},
methods:{
exchange(){
this.flag = !this.flag;
}
}
};
</script>
点击按钮切换flag的值,对应的p标签的内容会随之改变
条件渲染语句
可以把它看成if语句,条件满足时会显示标签里的内容
<p v-if="isPassed">{{score}}</p>
<script>
export default {
name: "app",
// 数据
data() {
return {
//当条件满足的时候,显示这里的内容
score: 78
};
},
methods:{
isPassed(){
if(this.score<60)
return false;
return true;
}
}
};
</script>
v-else-if和v-else的用法和if,else一样
列表渲染语句
循环渲染数字
<div id="app">
<ul>
<li v-for="item in 5" :key="item">{{ item }}</li>
</ul>
</div>
从1开始循环遍历数字直到5结束,最终得到5个li标签
:key的作用: 为了确保每一个item是唯一的,所以需要唯一的key
循环渲染数组
<ul>
<li v-for="(item,index) in nameList" :key="index">{{ item }}</li>
</ul>
<script>
export default {
name: "app",
// 数据
data() {
return {
nameList:["张三","李四","王五"]
};
}
};
</script>
这样可以得到一个姓名列表,(item,index)代表元素和对应的下标,in nameList意思是在这个叫nameList的数组里循环
循环渲染对象
<ul>
<!--
value:对象中每一项的值
key:对象中每一项的键
index:索引
-->
<li v-for="(value,key,index) in book" :key="index">值:{{ value }}--键:{{ key }}--索引:{{ index }}</li>
</ul>
<script>
export default {
name: "app",
// 数据
data() {
return {
book:{
bookName:'指环王',
author:'JK 罗琳'
}
};
}
};
</script>
结果如下:

遍历数组中的对象
大多情况都是遍历数组中的对象
<ul>
<li v-for="(item,index) in books" :key="index">
{{ index+1 }}----{{ item.title }}----{{ item.author }}----{{ item.publishedTime }}
</li>
</ul>
<script>
export default {
name: "app",
// 数据
data() {
return {
books: [
{
title: '《魔戒》',
author: '约翰·罗纳德·瑞尔·托尔金',
publishedTime: '1954'
},{
title:'《哈利·波特》',
author:'J·K·罗琳',
publishedTime:'1997'
},{
title:'《人性的弱点》',
author:'戴尔•卡内基',
publishedTime:'2008'
}
]
};
}
};
</script>
结果:

计算属性
计算属性是继data,methods,watch之后的新成员
书写位置:
<script>
export default {
name: 'app',
// 计算属性
computed: {},
};
</script>
计算属性的写法:
<script>
export default {
name: "app",
data:function(){
return {
message:"hello world!"
}
}
// 计算属性
computed:{
//让字符串反转
reverseMessage:function(){
return this.message.split('').reverse().join('')
}
}
};
</script>
计算属性和方法的区别: 上述的案例也可以用methods来实现但二者区别在于:
- 计算属性 当message改变时,reverseMessage计算属性会重新计算,然后返回计算结果,message不改变时,reverseMessage计算属性会返回缓存的值而不会重新计算
- 方法 每次访问时,都会去执行方法体里的逻辑然后返回结果
总结: 计算属性避免了不必要的代码执行,性能更优
动态class
动态绑定
动态绑定的语法:
<div class="base" v-bind:class="{ active: isActive }"></div>
.active {
border: 1px solid green;
}
isActive是boolean类型的值,决定是否应用该类名
-
类名的书写 如果是单个类名的书写就和上面一样,如果是带字符的类名则要加引号将类名括起来
-
引号规则 看类名的引号决定外面大括号的引号:
<!-- 外双内单 -->
<div v-bind:class="{ 'base-active': isActive }"></div>
<!-- 外单内双 -->
<div v-bind:class='{ "base-active": isActive }'></div>
动态样式绑定的条件类型
- 变量形式 通过使用变量的形式来获取布尔值:
<div :class="{ active: isActive }">颜色</div>
在data中定义布尔类型的变量
data:function(){
return {
isActive:false
}
}
通过改变布尔值来改变动态绑定的样式
- 方法形式 可以通过方法来决定渲染,看方法返回的结果得到布尔值
- 表达式形式
<span :class="{ 'new-appear': this.type === 'NEW' }">新</span>
- 计算属性形式 可以将样式对象和判断条件都放在计算属性内:
computed: {
hoverObj: function () {
return {
//后面的值决定hover类是否被添加
hover: this.index === 1,
};
},
},
动态样式绑定的写法
- 对象写法 上述案例都是对象写法
- 数组写法 类名都要带引号
<div v-bind:class="['red-style', 'font-style']"></div>
动态style
:class可以理解为外部样式,行内样式则是:style
对象语法
和:class语法不同的是它的键值对是css样式的属性:值
<div :style="{background:'red','font-weight':700,'font-size':'20px'}"></div>
- 引号的使用 属性:属性为单个字符可以不用引号,如果是带连字符如font-weight,就要用引号值: 除了数字都要加引号要避免使用引号可以去掉连字符
- 改变动态样式的书写位置 要是加的样式很多时可以将动态样式提取出来,定义在data中:
data:function(){
return {
flexStyle: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'no-wrap',
},
}
}
最后在标签中引入变量即可:
<div :style="flexStyle"></div>
数组语法
:class数组里是类名所以要用"",:style数组里是变量所以不用引号
data() {
return {
fontStyle: { color: 'red', fontSize: '33px' },
boxStyle:{width: '200px', height: '200px', border: `1px solid black`}
};
},
在标签的动态样式中引入:
<div :style="[fontStyle,boxStyle]">这里是文字</div>
组件是可复用的Vue实例,我们把重复用到的功能封装成自定义组件
自定义组件
为了能在模板中使用,组件必须先注册
组件的注册分为全局注册和局部注册:
- 全局注册: 用Vue.component来创建组件,注册之后可以在任何新创建的Vue根实例中使用
- 局部注册: 在单个Vue格式的文件中创建组件,在需要用到的地方注册
通常选择局部注册
组件的创建
每个Vue格式的文件都可以作为组件来使用
组件的局部注册
通过vue-cli创建的vue工程默认存在一个组件HelloWorld.vue,以它为例:

这样就形成了组件树,App.vue就是父组件,内部使用的HelloWorld就是子组件
组件中的数据
自定义组件中的data必须是一个函数: 重复使用的组件间data是相互独立的
data: function () {
return {
count: 0
}
}
复用的组件里显示的内容往往是不同的,同一个组件,显示内容不同
prop的使用方法
当父组件给子组件的prop传递一个值的时候,这个值就变成了子组件的实例的属性
- 首先在父组件中传递一个title给子组件:
<template>
<div id="app">
<!-- 注意!title1 和 title2 是父组件的 data 中定义的数据,title 则是子组件中接收数据时的变量名 -->
<HelloVue :title="title1"></HelloVue>
<HelloVue :title="title2"></HelloVue>
</div>
</template>
因为title1,title2是变量,所以title前面需要加 :
- 子组件中,用prop接受title:
<template>
<div class="hello">
<!-- 第二步:在页面上显示 title 的值,写法和显示 data 里定义的数据一样 -->
<h1>{{ title }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloVue',
// 第一步:在 prop 属性中接收 title
props: ['title']
};
</script>
附带类型声明
给值声明类型,类型首字母要大写:
<script>
export default {
name: 'HelloVue',
// 在 prop 属性中接收 title,其类型为 String
props: {
title: String
}
};
</script>
在这里prop是一个对象,当传入的值有多个的时候,可以用逗号隔开可以设置一些要求:
props: {
title: String,
// 多类型
likes: [String, Number],
// 带有默认值
isPublished: {
type: Boolean,
default: true
},
// 必填
commentIds: {
type: Array,
required: true
},
author: Object,
callback: Function,
contactsPromise: Promise
}
单向数据流
单向数据流指的是父子prop之间形成了一个单向下行绑定:父级prop的更新会向下流动到子组件中,但反过来就不行
- prop传入的数据需要处理可以通过计算属性对数据处理:
props: ['initialTitle'],
computed: {
normalizedTitle: function () {
// 对传入的 initialTitle 进行去空格、字母小写化处理
return this.initialTitle.trim().toLowerCase()
}
}
- prop传入的数据作为本地数据使用可以定义一个本地的data属性并将prop作为其初始值:
props: ['initialTitle'],
data: function () {
return {
// 要读取 prop 里的 initialTitle,要在前面加 “this.”
// 将传入的 initialTitle 作为本地属性 title 的初始值
title: this.initialTitle
}
}
当传入的数据是对象时,通过计算属性对对象的各个属性进行改变,而不是改变对象,传入时要判断是否传入过来了,否则数据还未到达就已经开始去取它的属性这样会发生错误
自定义组件绑定原生事件和自定义事件
在自定义组件的根元素上监听一个原生事件在和html原生标签上监听一个原生事件是有区别的
如在App.vue中:
<!-- 给自定义组件添加点击事件 print -->
<Article
v-for="article in articleList"
:key="article.title"
:article="article"
@click="print(article)"
></Article>
在Article.vue中:
<div class="article-title" @click="printTitle">{{ article && article.title }}</div>
点击文章列表的标题部分可以发现只有子组件的事件被执行了
如果要让父组件的方法也执行则需要用到修饰符
要让父组件的print执行,可以添加.native
<Article
v-for="article in articleList"
:key="article.title"
:article="article"
@click.native="print(article)"
></Article>
自定义事件
我们不能直接在子组件中直接修改父组件中传来的prop数据,所以可以通过自定义事件来完成
- 给子组件Article.vue绑定自定义事件:
用
v-on:upVote="handLikes"给Article.vue绑定自定义事件:
<!-- 自定义事件 upVote,调用该事件时会执行 handleLikes 方法 -->
<article
v-for="article in articleList"
:key="article.title"
:article="article"
v-on:upVote="handleLikes"
></article>
- 在Article.vue中调用自定义事件"upVote" :
<!-- 在 template 中直接调用自定义事件 upVote -->
<button @click="$emit('upVote')">点赞</button>
当方法需要参数时:
<button @click="childEvent">点赞</button>
methods: {
childEvent: function() {
// 调用自定义事件 upVote,这里的第二个参数最后会传到父组件中的 handleLikes 方法里
this.$emit('upVote', this.article);
// do other things
}
}
$emit的第一个参数是自定义事件的名称后面的参数会成为自定义事件对应的方法的参数
总结,自定义事件可以:
- 在子组件中调用父组件的方法
- 把子组件的数据通过自定义事件参数的形式传给父组件
双向绑定
父组件App.vue中用修饰符.sync完成count的双向绑定:
<MyCount class="count" :count.sync="count"></MyCount>
// 在 `methods` 对象中定义方法
data: function() {
return {
count: 0
}
}
子组件MyCount.vue中用update:count的模式触发事件,把count+1赋给count:
<div class="my-count">
<button @click="$emit('update:count', count+1)">加一</button>
{{ count }}
</div>
通过Vue提供的ref属性去访问子组件实例,并调用子组件中的方法
调用子组件中的方法
通过ref属性来访问子组件实例,并调用子组件中的方法:
- 给要访问的子组件添加ref属性
<template>
<Modal ref="modal"></Modal>
</template>
- 调用子组件中的方法
通过
this,$refs.modal来访问自定义组件Modal.vue:
<script>
export default {
methods: {
showModal() {
// 调用子组件中的 show 方法
this.$refs.modal.show();
}
}
};
</script>
ref访问子元素
<template>
<div id="app">
<input ref="input" type="text" />
<button @click="focusInput">点击使输入框获取焦点</button>
</div>
</template>
<script>
export default {
name: 'app',
methods: {
focusInput() {
// this.$refs.input 访问输入框元素,并调用 focus() 方法使其获取焦点
this.$refs.input.focus();
}
}
}
</script>