Vue2.x 快速入门
快速且全量的Vue2.x入门参考,快来看看吧!
前言
最近发现身边的大佬都非常卷,个个都是全栈。作为后端渣渣,势必要走出舒适区,向大佬们学习全栈了。其中重要的一环便是前端技术栈。对于前端还没入门的我,决定从Vue2.x开始学习。
这篇博客是我看B站吴悠老师的视频做的笔记,吴悠老师讲得非常好,既有效率,又非常全面,视频链接在文末,非常感谢吴悠老师!
前提
- 了解过 HTML 、 CSS、 JavaScript 基础三件套
- 了解 Node.js, 会用 npm 、yarn 等工具对前端项目进行管理
学习目标
- 快速学习 Vue2.x 特性
- 快速学习 Vue2.x 相关语法,接下来能逐渐熟练使用并逐步拥有全栈的能力
- 学习Vue2.x 为后续 Vue3.x、 React 等框架打下基础
1. Vue 项目创建
1.1 Script 标签引入
标签引入Vue,就可以直接使用了:
CDN方式
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
vue.js
我们也可以直接从官网上下载 vue.js 或 vue.min.js
<!-- 引入Vue.js -->
<script src="../public/vue.min.js"></script>
1.2 Vue-Cli 创建项目
首先保证自己本地/服务器有node。推荐使用nvm去管理我们的node。
安装 Vue 脚手架
- 卸载已有的脚手架
npm uninstall vue-cli -g
出现以下类似结果即为成功
up to date in 80ms
- 安装脚手架
npm install -g @vue/cli
- 查看vue cli 版本
vue -V
# 或者使用
vue --version
- 创建项目
在目标文件夹,cmd 键入:
vue create [projectname]
例如,当前项目名是:
vue create kcl2024_background_front_end
之后会进入如下选择界面。
选择版本
Vue CLI v5.0.8
? Please pick a preset: (Use arrow keys)
> Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
Manually select features
选择我们需要的版本,默认3、2或自定义。
接下来执行,根据提示选择,然后等待执行即可。
- 尝试启动
本地启动vue cli 创建的项目有如下几种方式,读者按需选用:
进入项目文件夹:
npm run serve
打包并启动:
npm run build
npm i serve -g
serve dist
ui方式:
vue ui
2. Vue2.x 语法
以下,跟着参考教程的步伐,我们快速了解一下 Vue2.x 的语法吧
2.1 插值表达式与响应式数据
响应式数据
响应式数据是一种机制,是指在数据发生变化后,更新依赖这些数据的部分,允许数据绑定和试图更新的自动化,为我们编写代码时省略DOM操作,提升开发效率。
插值表达式
使用此处示例的语法结构,就可用插值表达式将响应式数据渲染,如{{ data_name }}
举例
下面列举一个例子,使用插值表达式就能将title和content响应式数据渲染:
<div id="app">
<!-- 插值表达式 -->
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</div>
<script>
// 创建Vue实例
const vm = new Vue({
// 1. 响应式数据与插值表达式
// el : vue 实例的一个配置属性,指定了 Vue 实例要渲染的位置,可以是 CSS 选择器、HTML 元素或者是 HTMLElement 实例。
el: '#app',
data () {
return {
title: '这是标题',
content: '这是内容'
}
},
})
</script>
其中,插值表达式里也可以调用运算、方法等,例如:
<div id="app">
<!-- 插值表达式 -->
<h1>{{ title }}</h1>
<p>{{ content }}</p>
<!-- 自定义方法 -->
<p>{{ description() }}</p>
</div>
<script>
// 创建Vue实例
const vm = new Vue({
el: '#app',
data () {
return {
title: '这是标题',
content: '这是内容'
}
},
// 方法,和data属性并列
methods: {
// 自定义方法
description () {
return '标题:' + this.title + ',内容:' + this.content
}
},
})
</script>
2.2 计算属性
<!-- 引入Vue.js -->
<script src="../public/vue.min.js"></script>
<div id="app">
<!-- 对比计算属性与普通方法 -->
<p>{{ computedTest }}</p>
<p>{{ computedTest }}</p>
<p>{{ computedTest }}</p>
<p>{{ computedCompare() }}</p>
<p>{{ computedCompare() }}</p>
<p>{{ computedCompare() }}</p>
</div>
<script>
// 创建Vue实例
const vm = new Vue({
el: '#app',
data () {
return {
title: '这是标题',
content: '这是内容'
}
},
// 方法,和data属性并列
methods: {
computedCompare () {
console.log('methods方法被访问了')
return '这是methods方法的输出'
}
},
// 2. 计算属性,可以缓存一些复杂的逻辑,避免每次调用都重复计算
computed: {
// 计算属性的 getter 函数返回一个值,这个值会根据它的依赖跟踪依赖的响应式数据,并自动更新
// 计算属性的 setter 函数会在计算属性的依赖发生变化时调用,并传入新值作为参数
computedTest () {
console.log('计算属性被访问了')
return '这是计算属性的输出'
}
}
})
</script>
说明
计算属性只会在计算属性里面的响应式数据变动了之后再重新计算,如果数据没变就直接使用缓存的上次计算结果,提升了性能。因此在上述例子里我们可以从我们举例的log中去发现该例子的计算属性调用次数确实比methods调用次数要少。
另外,计算属性是属性,因此在调用的时候不需要像方法一样调用,而是使用属性的方式调用即可。
2.3 侦听器
侦听器是 Vue 给我们开放的一个功能,其目的是在我们对响应式数据进行操作的时候,可以做一些其它操作,方便我们对程序进行拓展。
举例
<div id="app">
<!-- 插值表达式 -->
<h1>{{ title }}</h1>
</div>
<script>
const vm = new Vue({
// 1. 响应式数据与插值表达式
// el : vue 实例的一个配置属性,指定了 Vue 实例要渲染的位置,可以是 CSS 选择器、HTML 元素或者是 HTMLElement 实例。
el: '#app',
data () {
return {
title: '这是标题',
content: '这是内容'
}
},
watch: {
// 监听器,可以监听数据的变化,并自动执行一些操作
title (newVal, oldVal) {
console.log('监听器被访问了')
console.log('标题被修改了,新值:' + newVal + ',旧值:' + oldVal)
}
}
</script>
我们可以在f12控制台对上述例子的title进行修改,然后观察输出的值。
2.4 指令
这一小节介绍常见的Vue指令,也就是我们常见的v-for等。
内容指令
内容指令会覆盖掉标签里的内容。
<div id="app">
<!-- 指令 -->
<p v-text="title">此内容会被覆盖</p>
<p v-text="htmlContent">此内容会被覆盖</p>
<p v-html="htmlContent">此内容会被覆盖</p>
</div>
<script>
// 创建Vue实例
const vm = new Vue({
// 1. 响应式数据与插值表达式
// el : vue 实例的一个配置属性,指定了 Vue 实例要渲染的位置,可以是 CSS 选择器、HTML 元素或者是 HTMLElement 实例。
el: '#app',
data () {
return {
title: '这是标题',
content: '这是内容',
htmlContent: '这是一个span标签:<span>这是内容,此标签会被v-html指令渲染</span>'
}
}
})
</script>
渲染指令
- v-for 循环:
举例:
<!-- 渲染指令 -->
<p v-for="item in arr">循环arr:{{item}}}</p>
<p v-for="(value, key) in obj">循环obj:{{key}}:{{value}}</p>
<p v-for="(value, key, index) in obj">循环arr:{{key}} : {{value}} : {{index}}</p>
data () {
return {
arr: ['one', 'two', 'three'],
obj: {
name: '张三',
age: 20,
job: '前端工程师'
}
}
},
- v-if 与 v-show:
举例:
<p v-if="bool">v-if指令渲染</p>
<p v-if="!bool">v-if指令不会渲染</p>
<p v-show="bool">v-show指令渲染</p>
<p v-show="!bool">v-show指令不会渲染</p>
data(){
bool: true
}
注意,v-if 会决定元素是否销毁。一般在不需要销毁元素的场景,我们多数使用 v-show 来决定元素的显示。
属性指令
- 属性绑定
举例:
<!-- 属性指令 -->
<p v-bind:title="title">v-bind指令绑定title属性</p>
<p :title="title">简写v-bind指令绑定title属性</p>
v-bind 还可以绑定 class、style等,例如:
<!-- 属性指令 class -->
<p v-bind:class="{ active: isActive }">v-bind指令绑定class属性</p>
<p :class="{ active: isActive }">简写v-bind指令绑定class属性</p>
<!-- 样式指令 -->
<p v-bind:style="{ color: activeColor }">v-bind指令绑定style属性</p>
<p :style="{ color: activeColor }">简写v-bind指令绑定style属性</p>
事件指令
举例:
<!-- 事件指令 -->
<button v-on:click="computedCompare">按钮触发事件</button>
<button @click="computedCompare">按钮触发事件</button>
表单指令
表单指令主要用于实现双向数据绑定。
举例:
<!-- 表单指令:v-model, 双向数据绑定 -->
<input type="text" v-model="inoutValue">
<p>{{ inoutValue }}</p>
data () {
return {
inoutValue:'输入内容'
}
},
修饰符
我们可以采用一些修饰符,来对我们的数据做一些处理。
举例:
<input type="text" v-model="inputValue">
<p>{{ inputValue }}</p>
<input type="text" v-model.trim="inputValue">
这个例子列举的trim是将两端空格去掉,读者可以参考着试一试。
3. Vue组件化开发
3.1 Vue Cli 项目结构
首先打开我们用Vue Cli创建的项目:
以下列举我们需要重点关注的项目结构:
- dist: build后的文件,服务器里运行的是dist
- node_modules: 所需依赖文件,npm install 的文件
- public:保存一些不需要参与编译的资源
- src:
- assets: 参与编译的一些静态资源
- components:自定义组件
- router:路由
- store: vuex 相关
3.2 Vue 文件结构
一般来说一个 Vue 的一个结构有三部分: template、script 和 style
例如HelloWorld.vue:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
3.3 组件通信
组件化开发指的是一个vue页面可以由多个、存在父子关系的组件组成。这一小节介绍父子通信。
3.3.1 父组件传给子组件
例如,我们刚创建出来的Vue默认页面中,HelloWorld.vue
是 HomeView.vue
的子组件。
子组件中,通过<script>
标签内的 props
参数接收由父组件传递的参数,其中,props值的key是传递值的名字,value是接收类型,例如:
HomeView.vue
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld
msg="Welcome to Your Vue.js App"
count="5"
/>
</div>
</template>
HelloWorld.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h1>Count: {{ count }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
// 接收父组件传递的 msg、count 属性
props: {
msg: String,
count: Number
}
}
</script>
其中,props也可以一个key定义接收多个类型的值,同时,可以有默认值,举个例子:
HomeView.vue
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld
msg="Welcome to Your Vue.js App"
count="5"
more="This is more text"
/>
</div>
</template>
HelloWorld.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h1>Count: {{ count }}</h1>
<h3>More: {{ more }}</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
// 接收父组件传递的 msg、count 属性
props: {
msg: String,
count: Number,
more: {
// 类型
type: [Object, Array, String, Number],
// 是否必填, 决定这个属性是否必须传递给组件
required: true,
// 默认值
default: 100
}
}
}
</script>
执行效果
3.3.2 子组件传给父组件
使用自定义事件的方式将值传递给父组件,以下举例:
我们先在子组件定义一个数据,并给一个触发事件的按钮:
HelloWorld.vue
<template>
<div class="hello">
<button @click="increment">按钮</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
// 接收父组件传递的 msg、count 属性
props: {
childData: Number
},
data() {
return {
childDataCount:0
}
},
methods: {
increment() {
this.childDataCount++
// 触发父组件的 increment 事件,自定义事件
this.$emit('increment', this.childDataCount)
}
}
}
</script>
接着,我们在父组件之间调用子组件的自定义事件,用于接收:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<h1>父组件中接收到的数据: {{ childData }}</h1>
<HelloWorld
:childData="childData"
@increment="handleClick"
/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'HomeView',
components: {
HelloWorld
},
data() {
return {
childData: 10
}
},
methods: {
// 子组件向父组件传递的数据
handleClick(dataFromChild) {
this.childData = dataFromChild
}
}
}
</script>
3.4 插槽
使用插槽可以让我们实现一些相同组件的一些不同显示,而不在组件中传参实现。
3.4.1 默认插槽
我们想让我们自定义的内容在组件标签内显示:
HomeView.vue
<template>
<div class="home">
<SoltDemo> 内容1 </SoltDemo>
<SoltDemo> 内容2 </SoltDemo>
<SoltDemo> </SoltDemo>
</div>
</template>
SoltDemo.vue
<template>
<div>
<slot> Default slot content </slot>
</div>
</template>
示例执行结果
3.4.2 具名插槽
我们在定义插槽时可以指定name,然后通过#
号或者v-slot
让组件使用:
SoltDemo.vue
<template>
<div>
<slot> Default slot content </slot>
<slot name="header"> Header slot content </slot>
</div>
</template>
HomeView.vue
<template>
<div class="home">
<SoltDemo>
<template #header>
header插槽内容1
</template>
</SoltDemo>
<SoltDemo>
内容2
<template v-slot:header>
header插槽内容2
</template>
</SoltDemo>
<SoltDemo> </SoltDemo>
</div>
</template>
3.4.3 作用域插槽
我们可以给插槽绑定一个数据作为作用域插槽,调用方式我们直接如下举例:
SlotDemo.vue
<script>
export default {
name: 'SlotDemo',
data() {
return {
slotData :0
}
}
}
</script>
<template>
<div>
<slot> Default slot content </slot>
<slot name="header"> Header slot content </slot>
<slot name="footer" :slotData="slotData"> Footer slot content </slot>
</div>
</template>
HomeView.vue
<template>
<div class="home">
<SoltDemo>
<template #header>
header插槽内容1
</template>
</SoltDemo>
<SoltDemo>
内容2
<template v-slot:header>
header插槽内容2
</template>
<template v-slot:footer = "slotDataObj">
{{slotDataObj}}
</template>
</SoltDemo>
<SoltDemo> </SoltDemo>
<SoltDemo>
<template #header>
header插槽内容3
</template>
<template #footer = "slotData">
{{slotData.slotData}}
</template>
</SoltDemo>
<SoltDemo>
<template #header>
header插槽内容4
</template>
<template #footer ={slotData}>
{{slotData}}
</template>
</SoltDemo>
</div>
</template>
值得注意的是,作用域插槽使用方式有多种,参考上述例子。其中,直接在template中 = "xxx"
的方式获取的是一个对象。
4. 路由
路由用于Vue单页面跳转,是我们日后开发要常接触的部分。
4.1 基本结构与使用方式
基本结构
我们用脚手架进行项目生成时,可以选择手动选择功能,然后选择路由。当我们选择了路由后,生成的Vue项目中就会有一个router文件夹。这个文件夹下会有一个index.js
文件:
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
基本使用方式
路由index.js
文件定义了基本的路由使用方式,即在routes中加入如:
{
path: '/',
name: 'home',
component: HomeView
}
这样的组件信息配置。其中,component可以直接写组件名,同时要import相应的这个组件;或者直接使用箭头函数import这个组件。
练习
了解了基本用法后,我们可以模仿一个出来:
LearnView.vue:
<template>
<h1>This is a component for learning</h1>
</template>
<script>
export default {
name: 'LearnView'
}
</script>
index.js:
import LearnView from '../views/LearnView.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
},
{
path: '/learn',
name: 'learn',
component: LearnView
}
]
_App.vue_新增如下:
<router-link to="/learn">for learning</router-link>
效果:
4.2 动态路由
我们实际的项目,url后可能会拼接一个id等字符来显示特定某个对象的信息,例如商品详情等。这种url拼接动态参数的方式称为动态路由。
实现动态路由要以下步骤:
冒号拼接参数名
index.js:
{
path: '/item/:id',
name: 'item',
component: () => import('../views/ItemView.vue'),
props:true
}
同时要注意,要加一行props=true
来接收动态参数。
props传参
itemView.vue:
<script>
export default {
name: 'ItemView',
props: [
'id'
]
}
</script>
<template>
<div class="item-view">
<h1>Item View</h1>
<p>item id: {{ id }}</p>
</div>
</template>
params传参或路径传参
App.vue:
<router-link :to="{ name: 'item', params: { id: 123 } }"> item </router-link>
效果:
同时我们观察url,参数已经拼在url末尾。
4.3 嵌套路由
嵌套路由的步骤如下:
设置children子组件列表
index.js:
{
path: '/children/:id',
name: 'children',
children: [
{
path: 'child1',
name: 'child1',
component: () => import('../views/Child1View.vue')
},
{
path: 'child2',
name: 'child2',
component: () => import( '../views/Child2View.vue')
}
],
component: () => import(/* webpackChunkName: "children" */ '../views/ChildrenView.vue'),
props: true
}
嵌套链接:
ChildrenView.vue
<template>
<div>
<h1>Children View</h1>
<router-link to="/child1">儿子1</router-link>
<br>
<router-link to="/child2">儿子2</router-link>
<router-view/>
</div>
</template>
被嵌套的组件及接下来的调用过程略,效果:
4.3 编程式路由
编程式路由允许由我们编码指定跳转。例如,当业务状态发生变更时,跳转道指定页面。
如:
Child1View
export default {
name: 'Child1View',
created () {
setTimeout(() => {
this.$router.push({ name: 'home' })
}, 3000)
}
}
其中,这个例子created函数是一个生命周期函数,具体会在下文生命周期小节有介绍。
4.4 路由守卫
在Vue2中,可以使用全局的路由守卫来监控和响应路由的导航。
我们简单举个例子,在路由_index.js_下添加如下:
// 全局前置守卫
router.beforeEach((to, from, next) => {
console.log('router.beforeEach', to, from)
next()
})
这个路由守卫输出了页面跳转的路径。但不要忘了next()
。
这里只介绍了基本用法。一般来说,路由守卫可以做一些逻辑处理,如身份验证、权限校验、加载数据等。
5. VueX
VueX 状态管理,是一种统一是数据存储方式,是解决多层级组件传值的一种有效手段。
我们在使用脚手架创建Vue项目时就可以选择引入VueX。项目创建后,src目录下会有一个store文件夹,这个文件夹下也有一个_index.js_文件。
其基本结构如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
我们需要注意的是,从 state 到 modules 提供了5个功能,下面逐一介绍。
5.1 state 数据存储
state 可以理解为在一个地方存数据,然后所有组件都可访问。
举例
index.js:
state () {
return {
status: 'loading'
}
}
访问方式:
Cild1View.vue:
export default {
name: 'Child1View',
created () {
console.log(this.$store.state.status)
}
}
5.2 mutations 数据修改
如果我们的VueX全局变量需要修改,则可在mutations进行。
举例
index.js:
state () {
return {
testMutation: 0
}
},
mutations: {
testChangeValue (state, value) {
// 返回 + 1 的值
state.testMutation = value + 1
}
}
调用方式:
Cild1View.vue:
export default {
name: 'Child1View',
created () {
this.handler()
console.log(this.$store.state.status)
},
methods: {
handler () {
console.log(this.$store.commit('testChangeValue', 1))
}
}
}
控制台输出,即可查看结果。
同时,在上面介绍的Demo练习例子中,我们不难发现,VueX中的数据都是在state中拿的,使用过程中需要遵循使用方式,即“state.xxx”这种方式去调用。
5.3 actions 异步处理
VueX中actions的作用是提供异步处理。
和mutations类似,都是方法处理,只不过是异步。因此以下复用了部分上一小节举的例子。
举例
index.js:
actions: {
testAction (store, payload) {
store.commit('testChangeValue', payload)
}
}
调用方式也和mutations类似,注意是使用store的dispatch调用:
console.log(this.$store.dispatch('testAction', 2))
5.4 getters 缓存
getters 提供了 类似于计算属性的缓存。只要getters中的值没有变化,就只执行一次,提高程序效率。
举例
index.js:
testGetters (state) {
console.log('testGetters executed', state.status)
return state.status.length
}
测试示例:
Cild1View.vue:
export default {
name: 'Child1View',
methods: {
handler () {
console.log(this.$store.getters.testGetters)
console.log(this.$store.getters.testGetters)
console.log(this.$store.getters.testGetters)
console.log(this.$store.getters.testGetters)
}
}
}
这里示例调用了好几次,但是在控制台输出不难发现console.log('testGetters executed', state.status)
只调用了一次。
5.5 modules 模块
在 Vuex 中,模块(modules)是一种将 store 分割成更小的、更专注的部分的方式。每个模块可以包含自己的 state、mutations、actions、getters,甚至是嵌套子模块。这种结构使得 Vuex store 可以保持清晰和组织良好,特别是在大型应用中。
举例
index.js:
modules: {
myModule: {
state: {
myModuleStatus: 'inactive',
myModuleCounter: 0
},
getters: {
myModuleStatusLength: (state) => {
console.log('myModuleStatusLength executed', state.myModuleStatus);
return state.myModuleStatus.length;
}
},
mutations: {
incrementCounter (state) {
state.myModuleCounter++;
console.log('myModuleCounter', state.myModuleCounter);
}
},
actions: {
incrementAction ({ commit }) {
commit('incrementCounter');
}
},
modules: {
// 可以继续嵌套子模块,在开发中一般会单独建一个文件,用来管理多个模块之间的数据状态
}
}
使用示例:
this.$store.commit('myModule/incrementCounter')
this.$store.dispatch('myModule/incrementAction')
6. Vue2 生命周期
概念
Vue 2 的生命周期钩子是指在 Vue 实例从创建到销毁的过程中,Vue 提供的一系列特殊的函数,这些函数在实例生命周期的不同阶段被自动调用。以下是 Vue 2 生命周期钩子的列表及其说明:
-
beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。此时,组件实例的 data 和 methods 已经被定义,但是 data 的响应式属性尚未被设置,$el 属性也还未创建。
-
created:在实例创建完成后被立即调用。此时,实例已完成数据观测,属性和方法的运算,watch/event 事件回调。但是,挂载阶段还没开始,$el 属性目前还不可见。
-
beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。此时,$el 还未被渲染,data 仍然是响应式的。
-
mounted:在实例被挂载后调用,此时可以访问到 e l 属性,表示 D O M 已经被渲染。如果根实例挂载到了文档内, el 属性,表示 DOM 已经被渲染。如果根实例挂载到了文档内, el属性,表示DOM已经被渲染。如果根实例挂载到了文档内,el 也在文档内可访问。
-
beforeUpdate:在数据变化时,DOM 重新渲染之前调用,此时可以在这个钩子中进一步地更改状态,这不会触发重绘。
-
updated:在由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。此时可以执行依赖于 DOM 的操作。
-
beforeDestroy:在实例销毁之前调用。在这一步,实例仍然完全可用。
-
destroyed:在实例销毁之后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
-
activated 和 deactivated:这两个钩子只在 keep-alive 组件中使用。activated 在组件被激活时调用,deactivated 在组件被停用时调用。
-
errorCaptured:当捕获一个来自子孙组件的错误时被调用。它有三个参数:错误对象、发生错误的组件实例和错误来源。
这些生命周期钩子可以被用来执行一些特定的操作,比如数据获取、事件监听、清理工作等。每个钩子函数都可以在组件选项中被定义,并且会在相应的生命周期阶段被调用。
简单举例
例如,我们可以将生命周期函数在我们项目的main.js中各做一个简单的输出:
main.js
new Vue({
router,
store,
render: h => h(App),
beforeCreate () {
console.log('beforeCreate: Vue 实例被创建,但数据观测和事件/侦听器还未被设置')
},
created () {
console.log('created: Vue 实例已创建,data() 和 methods 已可用')
},
beforeMount () {
console.log('beforeMount: Vue 实例即将被挂载,$el 属性还不可用')
},
mounted () {
console.log('mounted: Vue 实例已经挂载到DOM,$el 属性已可用')
},
beforeUpdate () {
console.log('beforeUpdate: Vue 实例数据更新,DOM 重新渲染之前')
},
updated () {
console.log('updated: Vue 实例数据更新,DOM 重新渲染完成')
},
beforeDestroy () {
console.log('beforeDestroy: Vue 实例销毁之前')
},
destroyed () {
console.log('destroyed: Vue 实例销毁完成')
}
}).$mount('#app')
7. 总结
我们从基本使用的层面,从Vue2.x 项目创建开始,结合一些Demo,学习了 Vue2.x的基本语法,然后学习了组件化开发中的父子组件传值。之后,我们接着学习日常开发中需要常接触的路由配置,以及可以解决多层级组件传值麻烦的全局状态管理器VueX,最后再介绍了面试常见与日后进一步了解框架所需的Vue2.x生命周期函数。
掌握基本语法是基础,组件化开发的思路是重要思想,路由、VueX也是实际开发中常接触的东西。
本文的示例都只是些Demo,要进一步了解、学习前端,还得找些项目练习、实战。
Vue2.x 是学习前端框架开发的一个良好台阶,可以为后续 Vue3.x、React等框架打下一定的基础。持续学习,共同进步!