前言
这是一篇帮助读者快速复盘Vue2有关实践知识点的博客。
先说说笔者的情况吧,写这篇文章的一个月前,我刚帮老师做完他的公司的官方网站,这是一个展示形的网站,写了两天,内容基本写完。
这一个月时间里,我没有碰Vue2的相关内容,而是复习了跟前端请求的相关内容。所以一个月后要我改这个网站,我看着代码内心是懵的。因此想花一个下午时间去复盘一下和Vue2实践相关的内容。
注意事项:本文针对的是和我情况类似,之前系统学过Vue2但是因为很久没碰忘记了的朋友,目的不是重新学习,而是复盘。所以很多内容我都是一笔带过的,重点内容我会有详细的步骤讲解。希望读者体谅。
最后我来说一下本文的大致内容,分为以下几块:脚手架创建vue项目及其基本配置,vue的基础部分,重要的生命周期函数,组件化编程,组件之间的信息传递,以及vue路由。
当然,这也不是一个简单的回顾文章,内部还会包含很多我做项目的经验。希望能对您有帮助!!
一,Vue2项目的创建及基本配置
1.1 用脚手架创建项目
选择合适的文件夹,打开终端,输入命令行语句:
Vue create 文件名
选择合适的配置即可。
1.2 项目结构
上面的图片是用脚手架创建的一个最初的vue2项目的结构。需要了解,src内部放置的是自己写的代码,assets存放图片,components存放组件,main.js是入口文件,App.vue可以理解为项目最初渲染的组件。
1.3 入口文件main.js
main.js一开始的配置是固定的。
首先,引入Vue文件
import Vue from 'vue'
接着,引入App.vue
import App from './App.vue'
然后,创建Vue的实例对象。Vue本质上是一个构造函数,所以我们创建Vue构造函数的实例对象
new Vue({
el: '#app',
render: h => h(App)
})
还有一种写法,这种写法也是脚手架给我们的写法:
new Vue({
render: h => h(App)
}).$mount('#app')
这里简单说一下为什么需要render。render的作用在于:弥补非完整版Vue的模板解析作用,render是一个函数,参数就是一个h函数。==作用是把App的内容放入一开始的页面中。==有了这一步,我们打开项目后,看到的就是App组件中的内容。
1.4 组件配置
这里要说一下App组件的基本配置。
<template>
<div></div>
</template>
<script>
export default {
name: 'App',
data() {
return {}
}
}
</script>
<style scoped>
</style>
一般情况下,Vue2项目中的组件,会以以上格式呈现。其中,template内部需要有一个div,这个div又叫作根结点,在Vue2的组件中,一定要有一个根结点(Vue3不用)。
接着是name,每个组件可以有一个组件名,这一点在路由规则中会用到。
最后是data,data中的数据在vue2中一定要以这种形式来写。data分为函数形与对象形,如果在vue中写成对象形式,则多个组件都使用data,会对data进行改动,一个组件对data的改动会影响所有组件中的data,所以必须使用函数形式来写data
最后再提一句style中的scoped。scoped的存在在于,如果组件A与组件B的内容中,都有一个叫box的类,如果没有scoped,则在一个项目中类名相同,元素样式会互相影响。scoped存在,则不会有这类问题,每个组件的样式都是独立的,不受其他组件影响。
1.4 运行项目
打开终端
npm run serve
如果要打包项目
npm run build
当然,这些知识脚手架下的命令语句,可以改的但是需要配置。有些vue2模板的语句也是有不一样的,通常下面这个也比较常用:
npm run dev
二,Vue的基础知识
这一节涵盖的内容有:数据显示、数据绑定、事件处理、循环遍历、判断语法、计算属性、监控属性。
再次提醒,由于内容较多,所以很多地方一笔带过!
2.1 数据显示
数据显示,这里介绍三个,分别是v-text、v-html与插值语法。
指令格式:
<div v-text='123'></div>
<div v-html="<h1>123</h1>"></div>
注意,v-html要少用,尤其忌讳放置a链接。容易引起XSS攻击
比较重要的就是插值语法了,请看下面代码:
<template>
<div>
<h1>个人信息</h1>
name: {{ person.name }}
age: {{ person.age }}
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
person: {
name: '巧克力小猫猿',
age: 20
}
}
}
}
</script>
这段代码中,下面是data,上面用插值语法把下面的内容给引出来了。
这里我要说的是,在vue2中,如果需要对数据进行操作,需要获取数据,请看下面代码:
<template>
<div>
<h1>个人信息</h1>
name: {{ person.name }}
age: {{ person.age }}
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
person: {
name: '巧克力小猫猿',
age: 20
}
}
},
methods: {
add() {
this.person.age ++;
}
},
}
</script>
在上面这段代码中,实现的是对年龄加1,首先要获取age,插值语法中,直接写的是person.age,但是在函数中,获取age则需要用到this。
2.2 数据绑定
数据绑定分为两种,一种是数据的单向绑定,一种是数据的双向绑定。
单向绑定:v-bind
双向绑定:v-model
从表单开始介绍,请看以下代码:
<br>
班级1: <input type="text" v-bind:value="person.class">
班级2: <input type="text" v-model="person.class2">
最后结果:
最后结果描述:由于表单被单向绑定和双向绑定,所以value都是提前设置好的值。不同的是,对于单向绑定,在页面上修改值数据无法同步,但是在下面的控制台中修改数据,正确是数据能够被渲染;
双向绑定的话,在页面修改值,data中的数据会改变;改变data中的数据,页面的值也会改变。
双向绑定用于表单,但是单向绑定,也就是v-bind,除了表单之外,还有很多用处,比如组件之间传递数据(后面会写),比如在标签中想要读取js代码:
单向绑定需要注意的问题是:用了单向绑定,引号内部的是js代码。
最后要说的是,单向绑定的简写,在前面加冒号,就是上一张图片那样。
2.3 事件处理
事件处理相当于原生中点击,鼠标经过,鼠标离开等事件。在触发事件后会有一个回调函数,在vue中叫作事件处理。
还是刚刚那个例子,设置一个按钮,点击按钮年龄往上加,这里了解简写就好:
age: {{ person.age }}
<button @click="add">+1</button>
这里其实是为button绑定了add事件;add事件在methods中写:
methods: {
add() {
this.person.age ++;
}
上面这个也是用的最多的。
2.4 循环遍历
这是一个很重要的点。并且有很多关键的地方。
首先,在data中给出一个数组:
arr: [1, 2, 3, 4, 5]
接着开始遍历。
这里需要注意的是,在vue中,遍历一般用的都是ul和li:
<ul>
<li v-for="a in arr" :key="a.id">{{ a }}</li>
</ul>
ul是固定的,有多少条数据,就会自动生成多少个li。在li中有一个v-for,v-for有两个内容,a是我们自定义的一个变量,它来存放我们的数据,arr是需要遍历的数组。a in arr的意思就是,遍历数组arr,每条数据由对应的a来存放。
每个数据都会自动生成一个id,id可以理解为数据的身份证。这个:key必须要有,不然会报错。
由于是复盘文章所以原理略过,感兴趣可以去查看跟响应式有关的博客了解原理。后面会出一个专题专门记录Vue中的原理部分,可以关注一波。
在li的内容中用插值语法来写出我们需要的数据。
最后的效果:
2.5 判断语法
判断语法分为v-if和v-show。
它们能够根据一定的判断条件来设置页面中的元素是否能看见。其中,v-if的原理是,如果满足条件显示,不满足条件则删掉页面结构;v-show是根据display:none来设置的,只是隐藏页面内容而已,页面结构依旧存在。
2.6 计算属性
计算属性的本质是个函数。
这里我们来看一个姓名案例,我会结合代码进行分析:
<template>
<div class="box">
姓氏: {{ firstName }}
<br>
名字: {{ lastName }}
<br>
合计: {{ fullName }}
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
firstName: '巧',
lastName: '克力小猫猿',
}
},
computed: {
fullName() {
return this.firstName + this.lastName
}
}
}
</script>
上面是计算属性的简写部分。用到最多的其实也是简写。computed是计算属性的标志,后面的fullName函数,是对fullName进行了加工。在页面结构中可以直接使用插值语法:
复杂版本的计算属性中还会有get和set,这里就略了可自行了解。
2.7 监视属性
这里同样也是介绍简洁版本的监视属性,监视属性与计算属性非常类似,在刚刚的基础上加上了这段代码:
watch: {
fullName(newvalue, oldvalue) {
console.log(newvalue)
console.log(oldvalue)
}
}
监视属性有两个参数,一个是newvalue一个是oldvalue,分别是fullName的最新值,和旧的值。
三,重要的生命周期函数
如果不了解生命周期,可以去了解下。本节只讲一些重要的生命周期函数。
3.1 整体复习
如果需要了解vue2的全部生命周期函数,可以把这段代码复制下来,打断点运行体验。(不用在脚手架上跑,直接跑就可以)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<h2>当前的n值是: {{ n }}</h2>
<button @click="add">点我n+1</button>
<button @click="bye">点我销毁</button>
</div>
<script>
new Vue({
el: '#root',
data: {
n: 1
},
methods: {
add() {
this.n++
},
bye() {
console.log('bye')
this.$destroy()
}
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
},
beforeupdate() {
console.log('beforeupdate')
},
updated() {
console.log('updated')
},
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
}
})
</script>
</body>
</html>
3.2 重要的生命周期钩子
一个是mounted:发送ajax,axios请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
一个是beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
四,组件化编程
4.1 概述
组件化编程这一节,涉及到组件的相关配置。
这里就要了解父组件,子组件了。
这两张图片可以说明一定的问题:
总结一下组件的标准定义:实现应用中局部功能代码和资源的集合。
4.2 组件的局部注册
需求:在App组件中导入Son组件。
先来说说部分导入吧。在一个组件中引用另一个组件。
请看以下代码:
<template>
<div class="box">
子组件:<Son></Son>
</div>
</template>
<script>
import Son from '@/components/Son.vue'
export default {
name: 'App',
components: {
Son
}
}
</script>
一共三个点,首先,引入组件。这里我想提醒下,引用地址的时候能用@尽量用@,因为如果是用./或者…/的话,后面很容易因为一个文件位置变了报很多错;
接着就是,注册组件,注册组件的时候如果组件名和组件本身的名字是一样的话,那就可以简写;
最后就是用组件的使用了,使用组件的时候,把它写成类似标签的样式即可。
4.3 组件的全局注册
全局注册,顾名思义,就是所有的组件都可以用这个全局注册过的组件,所以现在我们来看一下如何全局注册一个组件,还是以Son为例吧。
一般注册一些全局的东西,都在入口文件,也就是main.js上。
这就要使用Vue中的一个api,Vue.component(),但是api的参数一定是一个键值对,左边是组件名称,右边是组件(不然会报错)。组件要先经过引入才可以使用。
代码如下:
import Vue from 'vue'
import App from './App.vue'
// 全局组件
import Son from '@/components/Son.vue'
Vue.component('Son', Son)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
注册完毕后直接使用即可。
4.4 插槽
关于插槽我之前写过一篇很详细的文章:vue插槽之插槽的用法及作用域插槽详解
这里就简单概括以下几点吧,概括的前提是,接着刚刚的案例,父组件是App,子组件是Son。
接着我们再来回顾下插槽的理解,其实就是,在子组件中空出一个位置,在父组件中填这个位置。我们做的其实就是这样的事情。
4.4.1 默认插槽
首先,在子组件中放一个位置:
接着,在父组件中放置内容:
最后的效果:
刚刚上面这个插槽,我们称之为默认插槽。
4.4.2 具名插槽
具名插槽意为,取名字的插槽。
这里给个需求:需要两个插槽,一个名字叫one,一个名字叫two。两个插槽内容不同。
所以我们先在子组件中放置插槽,并且给插槽取名字。
接着在父组件中书写插槽内容,并对应上插槽:
最后效果:
五,组件之间的信息传递
组件之间的通讯有多种,本节复习的内容有:父传给子利用props(最基本)、子传给父(自定义事件)、ref传递数据、事件车、以及Vuex。算是一个比较综合的概括。
详情可以看这两篇文章,下面的只是复盘不会特别详细:
Vue组件之间的数据共享详解
vue事件车的原理与标准写法实现兄弟组件的传值
vue中利用ref实现更灵活的子向父传值
5.1 父向子传值
父向子传值的方式非常简单:利用props。
父组件利用单向绑定,把数据传给子元素,子元素利用props接收,以下是代码部分。
父组件:
<template>
<div class="box">
<Son :students="students"></Son>
</div>
</template>
<script>
import Son from '@/components/Son.vue'
export default {
name: 'App',
data() {
return {
students: [
{name: 'zxd', age: 20},
{name: 'wxy', age: 20}
]
}
},
components: {
Son
}
}
</script>
子组件:
<template>
<div class="box">
<ul>
<li v-for="s in students" :key="s.id">
{{ s.name }}--{{ s.age }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Son',
props: ['students']
}
</script>
5.2 子向父传值
子向父传值用的是自定义事件。
在子组件中定义事件,在父组件中触发。
这里要了解下$emit这个方法,这里可以理解为触发,触发自定义事件。当然,触发自定义事件是有前提的自定义事件被定义了。
我们在父组件中写一个自定义事件:
这里要注意,这个自定义事件是绑在子组件上面的:给谁绑定的自定义事件,就去找谁触发。
接着再看看看看子组件代码:点击按钮,add函数被调用,触发了父组件中定义的自定义事件fun。于是在子组件中,自定义事件被调用。
<button @click="add">发送信息</button>
</ul>
</div>
</template>
<script>
export default {
name: 'Son',
props: ['students'],
data() {
return {
baby: {
age: 20,
name: 'wxy'
}
}
},
methods: {
add() {
this.$emit('fun', this.baby);
}
}
子组件中的自定义事件被触发,或者被调用后,父组件会执行一个回调函数:在这个回调函数中可接收到子组件传过来的参数:
methods: {
get(value) {
this.message = value;
console.log(value)
}
}
步骤就算完成了:
可能有些读者没办法理解步骤为什么是这样的,这里来个解释:我们在父组件中,利用事件绑定,给子组件绑定了一个自定义事件fun。由于给谁绑定自定义事件就找谁触发。所以在子组件中用$emit触发自定义事件。自定义事件触发需要两个参数,一个是自定义事件名称,一个是自定义事件需要用到的参数。事件被触发后,会有一个回调函数。回调函数在父组件中,于是父组件得以接收到子组件传递过来的参数。
如果还没有明白,那我来说一下触发和回调。这就要聊到原生的js,看一下下面的代码,在下面代码中,实际上是触发了click事件,然后有一个触发后的回调。在上面的例子中也是,在子组件中触发,在父组件中回调:
<button class="btn"></button>
<script>
var btn = document.querySelector('btn')
btn.addEventlistner('click', () => {
alert('123')
})
</script>
如果再看不懂的话就去复习自定义函数这一块内容。
5.3 事件车
关于事件车,上文链接的博客其实写的很清楚。但是这里也会尽量讲清楚滴。
还是来复习一下原理吧:
我们把x称之为事件车,把x看成一个联络方,D可以把数据给x,A可以从x中拿取数据。这一节需要利用上一节的思路——自定义事件。我们可以通过x触发自定义事件,把参数传递给x,还可以通过x执行回调,拿到参数。这对x的要求就很高:x需要能够被所有组件看见(所有组件都能访问到x),x还需要能够使用on和emit。
什么样的x符合上面的条件呢?在原型上。原型中的方法是可以被所有组件看到的。这里写一下标准写法,如果要剖析原理,请移步上面链接的博客。
首先配置以下入口文件main.js:
import Vue from 'vue'
import App from '@/App.vue'
Vue.use(App)
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototypes.$bus = this
}
}).$mount('#app')
这里,$bus就是刚刚的x,可以称之为数据总线,也可称之为事件车。
接着就可以用到它传递数据了。这里设置两个组件,组件A,组件B。我们在这两个平级组件中传递数据。A传向B。
先来看一下A的代码,在A中,利用emit触发自定义事件:
<template>
<div class="box"></div>
</template>
<script>
export default {
name: 'A',
data() {
return {
me: {
name: 'qklxmy',
age: 20,
job: 'zhongruan'
}
}
},
created() {
this.$bus.$emit('toB', this.me)
}
}
</script>
<style scoped></style>
再看看B的,B中用到on,on的作用实际上就是,接收自定义事件中的参数并回调:
<template>
</template>
<script>
export default {
name: 'B',
mounted() {
this.$bus.$on('toB', (value) => {
console.log(value)
})
}
}
</script>
<style scoped></style>
以上,就是事件车的用法。
六,路由
本节会复盘路由的配置、路由的基本用法、嵌套路由。本节内容需要对路由有一定理解:在单页面应用中,起到与链接相同作用的就是路由。路由可以理解为key-value的对应关系,就像链接,点击后就会到某个新的页面。路由也起到这样的作用,只不过,规则写在了路由规则里面。
6.1 路由的配置
6.1.1 入口文件配置
先配置入口文件main.js
import Vue from 'vue'
import App from '@/App.vue'
//引入路由器
import VueRouter from 'vue-router'
//引入路由规则
import router from '@/router/index'
Vue.use(VueRouter)
new Vue({
render: h => h(App),
router: router
}).$mount('#app');
我们来分析下入口文件:
排去之前最基本的配置,导入了路由器和路由规则。并且路由器需要注册引入以下;在Vue中也要配置router文件。
6.1.2 路由规则配置
路由规则在router文件夹里面:
接着我们对路由规则进行配置:
import VueRouter from 'vue-router'
export default new VueRouter({
})
在这段代码中,首先引入路由器,接着创建一个VueRouter对象,内部放置的是路由规则,路由规则我们后面说。但是写的路由规则需要能被路由器访问到,所以要export default 出去。
6.2 路由的基本使用
我们先看一下效果吧,效果出来了之后再一步步分析。
运行后,首先看到的是:
点击Home后:
点击About后:
请看下上面的链接变化:当我们点击Home,链接对应到/home,下面展示Home;当我们点击About,链接对应到/about,下面展示About。
上面说的就是链接,链接规则(路由,路由规则)。所以我们先从路由规则入手,在上面引入组件,下面写路由规则:
import VueRouter from 'vue-router'
//组件引入
import About from '@/Pages/About.vue'
import Home from '@/Pages/Home.vue'
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home
}
]
})
并且在app中写出链接和展示链接的地方(路由和展示路由的地方),router-link和router-view。其中,router-link需要加上一个to,to后面是地址。
<template>
<div class="box">
<router-link to="/home"><button>Home</button></router-link>
<router-link to="/about"><button>About</button></router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style scoped>
</style>
6.3 嵌套路由
还是先看效果。
初始页面:
点击About:
点击about中任意一个按钮:
可以看到,是about中嵌套了路由。
看路由规则,内部有一个children。并且需要记住,路由规则中,子组件的规则无需写完整。
import VueRouter from 'vue-router'
//组件引入
import About from '@/Pages/About.vue'
import Home from '@/Pages/Home.vue'
//嵌套组件
import Messages from '@/Pages/Messages'
import News from '@/Pages/News'
export default new VueRouter({
routes: [
{
path: '/about',
component: About,
children: [
{
path: 'messages',
component: Messages
},
{
path: 'news',
component: News
}
]
},
{
path: '/home',
component: Home
}
]
})
再看一下about组件:router-link的地址中,地址需要写完整。
<template>
<div class="box">
<router-link to="/about/news"><button>News</button></router-link>
<router-link to="/about/messages"><button>Messages</button></router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'About'
}
</script>
<style scoped>
</style>
后记
本篇文章阐述了Vue2的一些基本知识,与其说基本,不如说基础——必须要掌握的东西。
还有很多东西需要在做项目中用到的。如果想了解更多可以关注我,关注我的一些博客。
就这么多啦,爆肝不易,每天都在摸鱼时间写博客。希望能收获一波点赞关注。虽然不会给我带来很多收益,但是每每点开看到点赞与收藏的地方有小红点,会很开心。
谢谢大家,祝大家都找到好工作!