0%

VueJS 基础知识

2019年实习时的 VueJS基础知识 备份

介绍

  • Vue 是一套用于构建用户界面的渐进式框架,数据驱动视图,只关注视图层,采用自底向上增量开发的设计。

使用方式

1
2
// 原生引入
<script src="vue.min.js"></script>

开发规范

  • 组件名为多个单词
  • 私有 property 名使用 $_ 前缀,并附带一个命名空间以避免冲突。
  • 单文件组件的文件名始终单词大写开头,或者横线链接。
  • 特定样式和约定组件全部以一个特定的前缀开头
  • 和父组件紧密耦合的子组件应该以父组件名作为前缀命名
  • 组件名倾向完整单词,避免缩写。

生命周期

基本顺序

创建→挂载→更新→销毁

具体步骤

StateDescription
beforeCreate创建前:此阶段为实例初始化,此时的数据观察和事件机制都未形成,不能获得 DOM 节点。
created创建后:实例初始化完毕,页面还没开始渲染,但可以操作数据(data,prop,发送请求获取数据)。
beforeMount挂载前:在这一阶段,我们虽然依然得不到具体的 DOM 元素,但 vue 挂载的根节点已经创建,之后对 DOM 的操作将围绕这个根元素继续进行,这个阶段是过渡性的,一般一个项目只能用到一两次。
mounted挂载后:在这个阶段,数据和 DOM 都已被渲染。
beforeUpdate更新前:这一阶段遵循数据驱动 DOM 的原则,函数在数据更新后虽然没立即更新数据,但是 DOM 中的数据会改变
updated更新后:在这一阶段 DOM 会和更改过的内容同步。
beforeDestroy销毁前:在上一阶段 vue 已经成功的通过数据驱动 DOM 更新,当我们不再需要 vue 操纵 DOM 时,就要销毁 vue ,也就是清除 vue 实例与 DOM 的关联,调 destroy 方法可以销毁当前组件,在销毁前会触发 beforeDestroy 钩子函数。(此时记得解除绑定事件,销毁定时器与全局变量等等。)
destroyed销毁后:在销毁后,会触发 destroyed 钩子函数。
  • 生命周期图示

父子组件执行顺序

  • 加载渲染过程

父beforeCreate→父created→父beforeMount→子beforeCreated→子created→子beforeMount→子mounted→父mounted

  • 更新过程

父beforeUpdate→子beforeUpdate→子updated→父updated

  • 销毁过程

父beforeDestroy→子beforeDestroy→子destroyed→父destroyed

常用指令

v-text 文本填充

示例:

1
2
<span v-text="msg"></span>
<!--{{ 双大括号也会将数据解释为纯文本 }}-->

v-html html填充

示例:

1
<div v-html="rawHtml"></div>

v-bind 动态地绑定一个或多个特性,或一个组件 prop 的表达式。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<!--当data里面定义的isActive等于true时,is-active这个类才会被添加起作用-->
<!--当data里面定义的hasError等于true时,text-danger这个类才会被添加起作用-->
<!--也可以写表达式:例如三元运算符等等-->
<div :class="{'is-active':isActive, 'text-danger':hasError}"></div>
</div>
<!--其他写法-->
<!--<单个类可写 :class="{'bg-danger text-light':true}">-->
<!--<多个类可写 :class="{'bg-danger text-light':true}">-->
<div :class="xxx == xxxxx ? 'bg-danger' : 'bg-light'">
<span :class="['message-item-user', {'order-last': xxx == xxxxx}, {xxx == xxxxx ? 'bg-danger' : 'bg-light'}]">{{ message }}</span>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
isActive: true,
hasError: false
}
});
</script>

v-on 用于监听指定元素的 DOM 事件,绑定事件监听器。

  • 常用 v-on 事件
NameDescriptionNameDescription
click点击元素mouseenter鼠标移入元素
dbclick双击元素mouseleave鼠标移出元素
focus元素获得焦点mousemove鼠标在元素内移动
blur元素失去焦点mousedown在元素上按下鼠标
keydown按下键盘mouseup在元素上释放鼠标
keyup释放键盘submit提交元素
input在元素内输入内容scroll滚动元素

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<button @click="consoleLog"></button>
</div>

<script>
var app = new Vue({
el: '#app',
methods: {
consoleLog: function (event) {
console.log(1);
}
}
});
</script>

v-model 实现表单输入和应用状态之间的双向绑定。

示例:

原理:<input type="text" :value="datax" @input="datax = $event.target.value">

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<input v-model.trim="somebody">
<p>hello {{somebody}}</p>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
somebody: 'world'
}
});
</script>

v-for 循环遍历,基于一个数组或者对象渲染一个列表。

示例:

有以下两种遍历方式

1
2
3
<div v-for="(item,index) in items"></div> //使用in,index是一个可选参数,表示当前项的索引。
<div v-for="item of items"></div>//使用of。
//遍历对象时是按照Object.keys()的顺序进行遍历,即ascii顺序。

v-if 根据表达式的值的真假条件渲染元素

示例:

1
<div v-if="ok">yes</div>

v-else 搭配v-if使用

示例:

1
2
<div v-if="ok">yes</div>
<div v-else>No</div>

v-show 根据表达式的真假值展示元素

示例:

1
<h1 v-show="ok">hello world</h1>

v-pre 跳过这个元素和它的子元素的编译过程

示例:

1
2
3
4
<div id="app">
<span v-pre>{{message}}</span> //这条语句不进行编译
<span>{{message}}</span>
</div>

v-once 只渲染元素和组件一次

示例:

1
2
3
4
5
6
7
8
9
<span v-once>This will never change:{{msg}}</span> //单个元素
<div v-once>//有子元素
<h1>comment</h1>
<p>{{msg}}</p>
</div>
<my-component v-once:comment="msg"></my-component> //组件
<ul>
<li v-for="i in list">{{i}}</li>
</ul>

常用于处理 DOM 事件的事件修饰符

InstructionsDescription
.stop阻止事件继续传播
.prevent事件不再重载页面
.capture使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理。
.self只当在 event.target 是当前元素自身时触发处理函数
.once事件将只会触发一次
.passive告诉浏览器你不想阻止事件的默认行为
  • @keyup.enter.native="toNextInput" 常用按键绑定

常用属性

  • el:指示 vue 编译器从什么地方开始解析 vue 语法,相当于一个占位符。
  • data:组织从 view 中抽象出来的属性,将视图的数据抽象出来存放在 data 中。
  • template:设置模板,可以用于替换页面元素。
  • method:放置页面中的业务逻辑,js 方法一般都放在 method 中。
  • render:创建真正的 Virtual Dom。
  • computed:根据已经存在的属性计算出新的属性,对于相同的数据会缓存,当依赖的属性值发生变化时,这个属性的值也会自动更新。
  • watch:监听 data 中的数据变化。

过滤器

  • 在两个大括号中
    • {{message | capitalize}}
  • 在 v-bind 指令中
    • v-bind:id="rawId | formatId"
  • 串联
    • {{message | filterA | filter}}
  • 接受参数
    • {{message | filterA('arg1',arg2)}}

计算属性和监听器

计算属性

  • computed
  • 属性默认只有 getter,不过在需要的时候也可以提供一个 setter。
  • computed 和 methods 的区别:computed 是基于依赖缓存,只有相关依赖发生改变时才会重新取值。methods 是在重新渲染的时候,函数总会重新调用执行。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template>
<div id="app">
<div>first: {{first}}</div>
<div>last: {{last}}</div>
<div>result: {{result}}</div>
<button @click="onclick">更改</button>
</div>
</template>

<script>
export default {
data() {
return{
first: 10,
ast: 10
}
},
computed: {
result: {
// 监听 data 中的 first 和 last, 得到新变量 result。
// 注意:只有当值改变时才会执行。
get() {
return this.first + this.last;
},
// set方法作用:通过参数修改计算的依赖属性 first 和 last 值
set(newValue) {
this.first = newValue;
this.last = newValue;
}
}
},
methods: {
onclick() {
// 调用计算属性的 set 方法,修改 first 和 last 的值。
this.result = 15;
}
}
}
</script>

监听属性

  • watch 实时监听数据变化并改变自身的值。
  • 允许执行异步操作,限制执行该操作频率。

示例:

1
2
3
4
5
6
7
8
watch: {
value(newVal, oldVal) {
if (newVal != oldVal) {
this.sum = newVal;
} else {
console.log(newVal, oldVal);
}
}

computed/watch 区别

computed

  • 支持缓存,只有依赖数据发生改变,才会重新进行计算。
  • 不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化。
  • computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于 data 中声明过或者父组件传递的 props 中的数据通过计算得到的值。
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用 computed
  • 如果 computed 属性属性值是函数,那么默认会走 get 方法,函数的返回值就是属性的属性值。在 computed 中的,属性都有一个 get 和一个 set 方法(自己配置),当数据变化时,调用 set 方法。

watch

  • 不支持缓存,发生改变,直接会触发监听事件。
  • watch 支持异步;
  • 监听的函数接收两个参数,第一个参数是最新的值,第二个参数是输入之前的值。
  • 当一个属性发生变化时,需要执行对应的操作。
  • 监听数据必须是data中声明过或者父组件传递过来的 props 中的数据,当数据变化时,触发其他操作,函数有两个参数:
    • immediate:组件加载立即触发回调函数执行。
    • deep:深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变(监听数组的变动不需要这么做)。注意:Vue 2 中 deep 无法监听到数组的变动和对象的新增,参考 Vue 数组更新检测,只有以响应式的方式触发才会被监听到。

响应式原理

  • 官方文档介绍如下

  当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

  这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。

  每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

  • 博主 BB

Vue 2 文档中提到:由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

  其实出现这个问题的主要原因就是 Object.defineProperty,因为 Object.defineProperty是采用数据劫持的方式进行数据监听,即必须提供监听数据的 key,才能进行数据拦截并监听,但是数组对象是变化的,所以存在这个缺陷。而 ES6 中的 Proxy,是通过直接代理数据的方式进行监听,所以没有此问题。

  另外 Vue 3 中,Object.defineProperty 已改用为 ES6 Proxy。官方文档说明,当我们从一个组件的 data 函数中返回一个普通的 JavaScript 对象时,Vue 会将该对象包裹在一个带有 get 和 set 处理程序的 Proxy 中。Proxy 是在 ES6 中引入的,它使 Vue 3 避免了 Vue 早期版本中存在的一些响应性问题

虚拟 DOM

前面响应式原理中有提到虚拟 DOM(Virtual DOM),那么如何理解它呢?

  虚拟 DOM 其实就是用普通 JavaScript 对象来描述 DOM 结构,因为不是真实DOM,所以称之为虚拟 DOM。
  虚拟 DOM 是相对于浏览器所渲染出来的真实 DOM 而言的,在 React/Vue 等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 DOM 树的方式找到需要修改的 DOM 然后修改样式行为或者结构,来达到更新页面的目的。
  DOM 树的实现模块和 JavaScript 模块是分开的,这些跨模块的通讯增加了资源耗费成本,而且这种方式操作会引起浏览器的回流和重绘,使得性能开销巨大,同时每次查询 DOM 几乎都需要遍历整颗 DOM 树。
  但若建立一个与 DOM 树对应的虚拟 DOM 对象( JavaScript 对象),以对象嵌套的方式来表示 DOM 树及其层级结构,那么每次 DOM 的更改就变成了对 DOM 对象的属性的增删改查,这样一来查找 JavaScript 对象的属性变化要比查询 DOM 树的性能开销小。
  所以 React/Vue 都采用虚拟 DOM 的方式来渲染页面,当监测页面触发了渲染事件或者数据变化后,会重新生成一个新的虚拟 DOM,然后对比新旧虚拟 DOM 进行渲染,至于渲染方案与生成方案需要自己去了解啦。

nextTick

将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

  因为 Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,所以若要在视图更新之后,基于新的视图进行操作,则需要用到 nextTick。

  • 使用
1
2
3
4
5
6
7
8
9
this.testText = '更新后文本';
let text = document.getElementById("changeDom").innerHTML;
console.log(text); // '更新前文本'
/* ------------------------------ */
this.testText = '更新后文本';
this.$nextTick(function () {
let text = document.getElementById("changeDom").innerHTML;
console.log(text); // '更新后文本'
});

组件

组件的定义使用

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//定义一个 <vue-demo> 组件
Vue.component('vue-demo', {
data: function () {
//do something
},
//设置显示模板
template: ''
});

//使用 <vue-demo> 组件
//HTML 文件中引入使用
<div id="app">
<vue-demo></vue-demo>
</div>
//JS 文件中初始化
new Vue({ el: '#app' });

父子组件的传值方式

  • props/$emit
    • 父组件传值给子组件:父组件通过一个属性,将其 data 上的值于该属性进行绑定,子组件通过 props 接受这个属性,就能获取这个属性的值。
    • 子组件传值给父组件:子组件通过实践触发的方式向父组件传值,当子组件的数值发生变化时,向外发射一个事件,然后父组件监听该事件名称,并在父组件的 data中去定义这个函数名的函数体
    • 注册组件
    • 全局组件:所有实例都能使用。
    • 局部组件:只能在实例的选项中使用。
    • Prop。
    • Prop 验证:type 可以是原生构造器,也可以是自定义构造器。
    • 自定义事件。
    • 父组件使用 props 传递数据给子组件,子组件将数据传递回去则需要使用到自定义事件。
    • 使用 v-on 绑定自定义事件,每个 Vue 实例都实现了事件接口(Events interface)。
    • 父组件可以在使用子组件的地方直接用 v-on 监听子组件触发的事件。
  • $children/$parent
  • provide/inject
  • $refs
  • eventBus 思否知乎
  • Vuex
  • html5Storage
  • $attrs/$listeners

props/methods/data/computed/watch 优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 通过 src\core\instance\init.js 源码
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}

我们可以看到优先级是 props > methods > data > computed > watch

自定义指令

除了默认设置的核心指令( v-model 和 v-show),Vue 也允许注册自定义指令。

钩子函数

FuntionDescription
bind只调用一次,指令第一次绑定到元素时调用.
inserted被绑定元素插入父节点时调用
update被绑定元素所在的模板更新时调用
componentUpdated被绑定元素所在模板完成一次更新周期时调用
unbind只调用一次,指令与元素解绑时调用。

钩子函数参数

ParameterDescription
el指令所绑定的元素,可以直接用来操作 DOM。
binding一个对象
vnodeVue 编译生成的虚拟节点
oldVnode上一个虚拟节点,仅在 update 和 componentUpdateed 中可用。

binding属性

AttributeDescription
name指令名
value指令的绑定值
oldValue指令绑定的前一个值
expression绑定值的表达式或变量名
arg传给指令的参数
modifiers一个包含修饰符的对象

路由(Route)

router-link 是一个用于设置一个导航链接的组件,实现路由的跳转。

相关属性

AttributeDescription
to目标路由的链接
replace/push调用 router.replace(),导航后不会留下 history 记录。
append在当前(相对)路径前添加其路径
tag渲染成某种标签
active-class设置链接激活时使用的 CSS 类名
exact-active-class配置当链接被精确匹配的时候应该激活的 class
event声明可以用来除法导航的事件

router-view 是一个用于渲染页面的组件,实现指定路由对应组件的渲染,相当于一个占位的作用,配合 router-link 使用

配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import Vue from 'vue';
import VueRouter from 'vue-router';
import A from '@/views/a.vue';
import B from '@/views/b.vue';
import C from '@/views/c.vue';

Vue.use(VueRouter);

const routes = [
{
path: '/',
name: 'home',
component: () => import('@/views/a') //可去后缀//其他:require('./views/index').default
},
{
path: '/a',
name: 'a',
component: A,
meta: { requireAuth: true }
},
{
path: '/b',
name: 'b',
component: B
},
{
path: '/c',
name: 'c',
component: C
},
{
path: '/login',
component: () => import('@/views/Login.vue'),
children: [
{
path: '',
name: 'register',
component: () => import('@/views/Register'),
meta: { requireGuest: true }
},
{
path: '/helper',
name: 'login.helper',
component: () => import('@/views/Helper')
}
],
meta: { requireGuest: true }
},
{
path: '*',
name: '404',
component: () => import('@/views/Error') //命名规范最好大写,驼峰。
}
];

const router = new VueRouter({
mode: 'hash', //history
routes
});

const VueRouterPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(to) {
return VueRouterPush.call(this, to).catch((err) => err);
};

router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) {
//do something
return next({ name: 'login' });
}
if (from.meta.requireGuest) {
//do something
return next({ name: 'home' });
}
return next();
});

export default router;

使用模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<div id="app">
<transition name="fade" mode="out-in">
<keep-alive>
<router-view />
</keep-alive>
</transition>
</div>
</template>

<script>
export default {
name: 'App',
methods: {}
};
</script>

<style>
html,
body {
width: 100%;
height: 100%;
}
#app {
width: 100%;
height: 100%;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.15s;
}
</style>
/* 此处过渡与动画效果参考后文 */

路由跳转

模板调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 不带参数
<router-link :to="{name: 'home'}">
<router-link :to="{path: '/home'}"> // name,path 都行,建议用 name。
// 注意:router-link 中链接如果是 '/' 开始就是从根路由开始,如果开始不带'/',则从当前路由开始。

// 带参数
<router-link :to="{name:'home', params: {id: 99}}">
// params 传参数 (类似 post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置 path,第一次可请求,刷新页面 id 会消失。
// 配置 path,刷新页面 id 会保留。
// html 取参 $route.params.id
// script 取参 this.$route.params.id

<router-link :to="{name:'home', query: {id: 99}}">
// query 传参数 (类似 get,url 后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id

函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 不带参数
this.$router.push('/home');
this.$router.push({name:'home'});
this.$router.push({path:'/home'});

// query传参
this.$router.push({name:'home',query: {id: 99}});
this.$router.push({path:'/home',query: {id: 99}});
// query 传参数 (类似 get,url 后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id

// params传参
this.$router.push({name:'home',params: {id: 99}}); // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置 path,第一次可请求,刷新页面 id 会消失。
// 配置 path,刷新页面 id 会保留。
// html 取参 $route.params.id
// script 取参 this.$route.params.id

// query 和 params 区别
query 类似 get,跳转之后页面 url 后面会拼接参数,例如 ?id=1,非重要性数据的可以这样传,刷新后数据还在,密码之类还是用 params。
params 类似 post,跳转之后页面 url 后面不会拼接参数,但是刷新后数据消失。

// 其他方法,用法同上 push。
this.$router.replace();
this.$router.go(n); // -1 为退回上一页

// 区别
push:跳转到指定 url 路径,并向 history 栈中添加一个记录,点击后退会返回到上一个页面。
replace:跳转到指定 url 路径,但是 history 栈中不会有记录,点击返回会跳转到上上个页面(就是直接替换了当前页面)。
go:向前或者向后跳转 n 个页面,n 可为正整数或负整数。

过渡与动画

语法格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<transition name="nameoftransition">
<div></div>
</transition>

// 定义进入前与离开后状态
.nameoftransition-enter, .nameoftransition-leave-to {
...
}
// 定义离开前与进入后状态
.nameoftransition-leave, .nameoftransition-enter-to {
...
}
// 定义进出过程
.nameoftransition-enter-active, .nameoftransition-leave-active {
...
}

切换类

ClassDescription
v-enter定义进入过渡的开始状态
v-enter-active定义进入过渡生效时的状态
v-enter-to定义进入过渡的结束状态
v-leave定义离开过渡的开始状态
v-leave-active定义离开过渡生效时的状态
v-leave-to定义离开过渡的结束状态

自定义过渡的类名

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)

同时使用过渡和动画

  • 必须设置相应的时间监听器来知道过渡的完成
  • 监听器可以是 transitionend 或 animationend
  • 同时设置两种过渡效果时,需使用 type 特性设置 animation 或 transition 来明确声明需要监听的类型

全局状态管理(Vuex)

基础示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);

const state = {
//相当于Vuex中的data,使用:
//this.store.state.test;
test: 'test'
};
const mutations = {
//相当于vuex中的数据处理methods,只能处理同步事件,异步操作使用actions配合进行。
//也可以同模组管理一样,统一使用mutations_type管理Mutations。[import * as types from '@/mutation-type';]
//使用即可type.MUTATIONS_TYPE。
//使用:
// this.store.commit('MUTATIONS_TYPE','hello world');
// this.store.commit({
// type: 'MUTATIONS_TYPE',
// payload: 'hello world'//推荐写成对象
// });
['MUTATIONS_TYPE'](state, payload) {
//也可以不写成常量,自己定义成普通方法。
state.test = payload;
}
};
const actions = {
//Action 提交的是 mutation,而不是直接变更状态。
//Action 可以包含任意异步操作。使用:
// this.store.dispatch('actionsName', {...});
// this.store.dispatch({
// type: 'actionsName',
// payload: {...}
// });
async actionsName(store, payload = {}) {
const { commit, dispatch, state, rootState, rootGetters } = store; //也可直接写到方法参数中解构赋值
console.log(rootGetters['others/get']); // 打印其他模块的 getters
try {
const {
data: { code, data }
} = await api.post('api/example', payload);
if (code === 200) {
commit('MUTATIONS_TYPE', data);
}
} catch (error) {
console.log(error);
}
}
};
const getters = {
//相当于vuex中的computed,使用:
//this.store.getters.testMore;
testMore(state, getters, rootState, rootGetters) {
//使用namespaced后,通过rootState可获取其他模块state等数据。
return state.test;
}
};

export default {
namespaced: true,
state,
mutations,
actions,
getters
};

模块化示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//参考 [https://gitee.com/doubleam/biugle/tree/main/resources/js/store]
//modules更多使用请参考 [https://vuex.vuejs.org/zh/guide/modules.html]
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
//也可通过引入js文件的方式,独立此文件为index.js/建立同级文件夹modules管理模块/统一一个mutation_type管理Mutations
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
};
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
};

const store = new Vuex.Store({
namespaced: true,//访问与引入不同模块需加上命名空间 state.moduleA / [actions]('moduleA/xxx')
modules: {
moduleA: moduleA,
moduleB: moduleB
}
});

引入与使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';

export default {
computed: {
...mapState({
a: (state) => state.a,
b: (state) => state.b
}),
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters(['test1', 'test2'])
},
methods: {
...mapActions(['foo', 'bar']),
...mapMutations({
add: 'mutationsType' // 将 `this.add()` 映射为 `this.store.commit('mutationsType')`
})
}
};

其他

main 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store.js'; // js 可省略
import './assets/css/reset.css';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import locale from 'element-ui/lib/locale/lang/zh-CN'; // lang i18n
import Axios from 'axios';

Vue.use(ElementUI, { locale });

Vue.prototype.axios = Axios;

Vue.config.productionTip = false;
Vue.config.debug = false;
Vue.config.devtools = false;

new Vue({
router,
store,
render: (h) => h(App)
}).mount('#main-container');

basic.vue 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<div>{{ msg }}</div>
</template>

<script>
export default {
name: 'HelloWorld',
props: {},
data() {
return {
msg: 'HelloBiugle'
};
},
components: {
xxx: xxx
},
computed: {
firstMsg: {
// getter
get: function () {
return 'first msg is' + this.msg;
},
// setter
set: function (newValue) {
this.msg = newValue;
}
}
},
watch: {
msg(newMsg, oldMsg) {
this.msg = newMsg + '=>' + this.oldMsg;
}
},
created() {},
mounted() {},
updated() {},
destroyed() {},
methods: {}
};
</script>

<style scoped></style>

待补充

  • 自定义指令
  • 更多传值方式(前面提到未[举栗子])
  • 混入
  • 插槽
  • 路由钩子与鉴权机制实现
  • SSR
  • Vue-cli

篇幅有限,这些需自己深入了解啦。

bulb