前言

主要介绍Vue2.0的基本语法。


项目创建

1
2
vue create 你的项目名称
vue ui

基础语法一

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 文本插值
<p>{{ message }}</p>
<p v-text="message"></p> // 同上效果
<p v-html="message"></p> // 识别标签
// 注意:template中只能放一个标签

// 动态绑定v-bind
<div v-bind:id="myId"></div>
<div :id="myId"></div>
// ---------
// 表达式
{{ ok ? 'YES' : 'NO' }}
<div v-bind:id="'list-' + id"></div>

// 条件渲染
<h1 v-if="isShow">isShow是data的动态数据,不要写出'isShow'字符串会一直显示</h1>
<div v-else-if="sex === 'female'">写上判断条件</div>
<div v-else>显示</div>
// ---------
// v-show
<h1 v-show="active">这里是v-show</h1>
// ---------
// key 管理可复用的元素
<div v-if="loginType === 'username'">
<label>用户名</label>
<input placeholder="请输入用户名" key="username-input">
</div>
<div v-else>
<label>邮箱</label>
<input placeholder="请输入邮箱" key="email-input">
</div>
// 未添加key,input会被复用,只改变placeholder中的内容
// Vue尽可能高效地渲染元素,复用已有元素


// v-for列表渲染
// key不是必须的,添加key以便Vue能跟踪每个节点的身份
<li v-for="item in courses" :key="item.id">{{ item.name }}</li>
// ---------
// 索引值
<li v-for="(item, index) in courses" :key="item.id">
{{ index + 1 }}+'索引值'+{{ item.name }}
</li>
// 自定义索引名,双重v-for注意命名区分
<div v-for="(item1,index1) in list1">
<span v-for="(item2,index2) in item1.list2">{{item2.XXX}}</span>
</div>
// ---------
// 使用v-for渲染对象
// 其中value是键值、name为键名、index是索引值
// course: {}
<li v-for="(value, name, index) in course" :key="value">
{{ index }}. {{ name }}: {{ value }}
</li>
// v-for可以使用v-for="n in 10",也可使用到子组件上
// v-if和v-for不能同时使用,可computed过滤再v-for


// 事件处理v-on
// 点击事件v-on:click或者@click
<button v-on:click="inc(2)">点这里</button>
// ---------
// 事件修饰符
<!-- 阻止单击事件继续传播stop -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面prevent,阻止默认行为 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
// capture,触发方式为捕获
// self,阻止自身冒泡,stop则是停止冒泡,
// 如果子元素触发事件,父元素self则不会触发,但是其父元素的父辈都会触发
// once,触发一次
// passive,执行默认方法,
// 每次事件产生,浏览器都会去查询一下是否有preventDefault阻止该次事件的默认动作
// 滚动监听,移动每个像素都会产生一次事件,每次都使用内核线程查询prevent会使滑动卡顿
// passive和prevent冲突,不能同时绑定在一个监听器上
// ---------
// 按键修饰符
// 如enter,只有按下enter才会触发
<input v-on:keyup.enter="submit">
// 鼠标按钮修饰符left、right、middle
// ---------
// $event 事件对象
<button @click="handleClick($event)">点我</button>


// 表单输入v-model
<input v-model="message" placeholder="请输入">
// checkbox中v-model显示的是value值
<input type="checkbox" id="html" value="HTML" v-model="checked">
// ---------
// 修饰符
// lazy修饰符,点击回车或者失去焦点时才发生数据响应
<input v-model.lazy="msg">
<!--msg不会实时更新,只有点击回车或者失去焦点-->
<p>{{msg}}</p>
// number修饰符,将输入的内容直接作为number类型使用
<input v-model.number="age" type="number">
// trim修饰符,去除左右两边的空格
<input v-model.trim="msg">

基础语法二

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
// 计算属性
// 计算属性不能和data、props重名
<div>{{ inc }}</div>
data(){ return:{ inc : 0 } }
computed: {
inc: function() {
return inc++;
}
}
//---------
// 计算属性缓存
// 计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值;
// inc若绑定了v-model,不管有几个元素渲染inc,只会运行一次,
// 而methods则会运行多次;
computed: {
fullName: function() {
console.log("我运行了")// 只会运行一次,即打印一次
return inc + '0'
}
}
//---------
// getter和setter的使用
你的全名是:{{ fullName }}<br>
<button @click="fullName='关羽'">点我</button>
data() {
return {value: '刘备'}
},
computed: {
fullName: {
get: function () {
return this.value
},
// set不能直接返回改变,还是需要依赖get的值该则改变,
// 可直接设置this.value,不使用setter;
set: function (newValue) {
this.value = newValue
}
}
}

// 侦听器
watch: {
'$route': 'init'
},
// 第二种写法
watch: {
$route() {
this.init()
}
},
// 可以接受两个参数,第一个是新值、第二个是旧值;
a: function (val, oldVal) {console.log(val,oldVal)}

// computed、watch两者区别
// computed支持缓存,只有依赖数据发生改变,才会重新进行计算,
// computed不支持异步,当computed内有异步操作时无效,无法监听数据的变化
// watch不支持缓存,数据变,直接会触发相应的操作
// watch支持异步


// Class 与 Style 绑定
<div :class="isActive?'active':''">Vue</div>
<div :class="{active:isActive}">Vue</div>
// 多个样式
<div v-bind:class="[isActive ? activeClass : '', underlineClass]">Vue教程</div>
data() {return {isActive: true}}

声命周期

  • 什么是vue生命周期?

​ 生命周期:Vue实例从创建到销毁的过程,也就是从开始创建、初始化数据、编译模板、挂载DOM-渲染、更新-渲染、卸载等一系列的过程。

  • vue生命周期的作用是什么?

​ Vue所有的功能的实现都是围绕其生命周期进行的,在生命周期的不同阶段调用对应的钩子函数可以实现组件数据管理和DOM渲染两大重要功能。

  • 第一次页面加载会触发哪几个钩子?

​ beforeCreate、created、beforeMount、mounted这四个钩子

  • 简述每个周期具体适合哪些场景?

    1. beforeCreate-实例初始化之后,this指向创建的实例,此时的数据观察事件机制都未形成,不能获得DOM节点。data、computed、watch、methods上的方法和数据均不能访问。可以添加loading事件、初始化非响应式变量;
    2. created-实例创建完成,完成数据(data、props、computed)的初始化导入依赖项。可访问data、computed、watch、methods上的方法和数据。常用于异步请求(请求过多会导致白屏),结束loading事件;未挂载DOM,若在此阶段进行DOM操作一定要放在Vue.nextTick()的回调函数中。
    3. beforeMount-挂载开始之前,vue挂载的根节点template已经创建,得不到具体的DOM元素;
    4. mounted-实例挂载到DOM上,完成双向绑定、挂载DOM和渲染,可对DOM进行操作,接口请求;
    5. beforeUpdate-数据更新前,是更新数据之后,还没有渲染的时候执行。可在更新前访问现有的DOM,手动移出添加的事件监听器。
    6. updated-数据更新后,完成虚拟DOM的重新渲染和打补丁。组件DOM已完成更新,可执行依赖的DOM操作。注意:不要在此函数中操作数据(修改属性),会陷入死循环。
    7. beforeDestroy-实例销毁之前,可做一些删除提示、销毁定时器、解绑全局事件、销毁插件对象等操作。
    8. destroyed-实例销毁后,Vue实例指向的所有东西都会解绑定,无法操作里面的任何东西。
  • 父子组件的生命周期

执行顺序:

父组件开始执行到beforeMount 然后开始子组件执行,最后是父组件mounted。

如果有兄弟组件,父组件开始执行到beforeMount,然后兄弟组件依次执行到beforeMount,然后按照顺序执行mounted,最后执行父组件的mounted。

当子组件挂载完成后,父组件才会挂载。销毁父组件时,先将子组件销毁后才会销毁父组件。


组件

组件的定义与使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 新建src/components/Child.vue 文件
// Home.vue 中使用
<script>
import Child from '../components/Child'

export default {
// 必须要注册后才能使用
components: {Child},
// 自定义要使用的子组件的名称
// components: {'my-child': Child},
}
</script>

// 全局注册
// main.js中
import Child from './components/Child'; // 引入Child
Vue.component('Child', Child);

插槽

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
// 父元素的组件上
<One>
<template v-slot:header>
<div>222222</div>
</template>
</One>
// 组件里面
<template>
<div class="container">
<slot name="header"></slot>
<div>下面的内容</div>
</div>
</template>

//--------

// 作用域插槽
// 子组件
<template>
<div>
<slot v-bind:user="user">
{{user}}
</slot>
</div>
</template>

<script>
export default {
data() {
return {
user: 'moxie'
}
}
</script>

// 父组件
<template>
<div>
<Child>
<template v-slot:default="slotProps">
{{ slotProps.user }}
</template>
</Child>
</div>
</template>

<script>
import Child from '../components/Child'

export default {
components: {Child},
}
</script>

动态组件

通过 Vue 的 元素加一个特殊的is来实现,在不同组件之间进行动态切换

1
2
3
4
5
6
7
8
9
10
11
12
// 父组件
<template>
<div>
<component v-bind:is="Child1"></component>
</div>
</template>

//在动态组件上使用keep-alive
// keep-alive是Vue一个内置的元素,组件缓存。如组件切换会缓存保留数据
<keep-alive>
<component v-bind:is="Child1"></component>
</keep-alive>

异步组件

组件只在需要的时候才从服务器加载一个模块, 组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
export default {
components: {
// 改为箭头函数引用
'Child': () => import('../components/Child')
},
data() {
return {
currentTab: Child1
};
}
};
</script>

过滤器

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
// yarn add moment
<template>
<p>{{ date | format }}</p>
</template>

<script>
import moment from 'moment';

export default {
data() {
return {
date: new Date()
};
},
filters: {
format(value) {
moment.locale('zh-cn');
return moment(value).format('LLL');
}
}
};
</script>

----------

// 全局过滤器
// src/filters/index.js文件
import moment from 'moment';
export function dateFormat(value) {
moment.locale('zh-cn');
return moment(value).format('LLL');
}
// main.js中
// 全局过滤器
import { dateFormat } from './filters';
// 注册全局过滤器,一定要放在new Vue之前
Vue.filter('dateFormat', dateFormat);
//使用循环批量注册
import * as filters from './filters';
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key]);
// Object.keys将对象中所有的属性名,以数组形式返回

mixin 混入

mixin可以将重复的 js 代码拆分出去,在需要使用的文件中引用即可。

  • 相同的生命周期,会先调用mixin中的,然后再调用组件中的;

  • data中的数据会互相合并,如果同名则保留组件中的;

  • 同名的方法,保留组件中的;

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
// myMixin.js文件
const myMixin = {
created() {
this.hello();
},
methods: {
hello() {
console.log('hello from mixin!');
}
}
};
export default myMixin;

// 使用方法
<script>
import myMixin from '../mixins/myMixin';

export default {
mixins: [myMixin]
};
</script>

---------

// 应当避免使用,使用全局混入会影响每一个之后创建的Vue实例。
// 全局混入,修改main.js
import myMixin from './mixins/myMixin';
Vue.mixin(myMixin);

自定义指令

钩子函数:

钩子函数 解释
bind 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
update 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
componentUpdated 指令所在组件的 VNode 及其子 VNode 全部更新后调用
unbind 只调用一次,指令与元素解绑时调用

钩子函数参数:

参数 解释
el 指令所绑定的元素,可以用来直接操作 DOM。
binding 一个对象,包含以下 property:name、value等
vnode Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
oldVnode 上一个虚拟节点,仅在update和componentUpdated钩子中可用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 使用
<template>
<div>
Hello, 长乐未央
<br><br>
<input v-focus>
</div>
</template>

<script>

export default {
directives: {
focus: {
// 指令的定义
inserted(el) {
el.focus();
}
}
}
};
</script>

Vue 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
38
// 路由文件index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')

// 嵌套路由
children: [
{
// List 会被渲染在About的<router-view>中
path: 'list', // ''就是About页面,path不需要加/
name: 'List',
component: () => import('../views/List.vue')
}
]
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})

export default router
// 路由出口
<router-view/>

路由跳转

方法 说明
<router-link to="/about"> HTML 中,链接到/about
<router-link :to="{ path: '/about'}"> HTML 中,链接到/about
<router-link :to="{ name: 'About'}"> HTML 中,链接到命名为About的路由组件
<router-link :to="{ name: 'About', params: { id: 999 }}"> HTML 中,链接到命名为About的路由组件,并传递路由参数
<router-link :to="{ name: 'About', query: { keyword: '长乐未央' }}"> HTML 中,链接到命名为About的路由组件,并传递查询参数
this.$router.push({ path: '/about' }) 通过代码,跳转到/about
this.$router.push({ name: 'About' }) 通过代码,跳转到命名为About的路由组件
this.$router.push({ path: '/about/999' }) 通过代码,跳转到路径为/about的路由组件,并传递路由参数
this.$router.push({ name: 'About', params: { id: '999' } }) 通过代码,跳转到命名为About的路由组件,并传递路由参数
this.$router.push({ name: 'About', query: { keyword: '长乐未央' } }) 通过代码,跳转到命名为About的路由组件,并传递查询参数
this.$route.params.id 获取传递过来的路由参数
this.$route.query.keyword 获取传递过来的查询参数

路由守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
path: "/users",
name: "UsersHome",
component: () => import("../views/users/Home.vue"),
meta: { requiresAuth: true },
},

// 路由守卫
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
const token = localStorage.token;

if (token) {
next();
} else {
next({
path: "/sign_in"
});
}
} else {
next(); // 确保一定要调用 next()
}
});

vue动态路由和权限控制

1
2
3
4
5
// 添加路由
xxx.forEach(item => {
this.$router.addRoute(item)
})
this.$router.addRoutes(xxx) // xxx为数组,该方法可能被废弃。

权限动态路由使用

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// router/index.js中
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

import { staticRouter, } from '../utils/data' //引入静态路由
const routes = staticRouter //静态路由保存
const router = new Router({ //创建路由
routes
})
export default router

// ---------

// utils/data.js文件
//静态路由
export const staticRouter = [
{
path: "/",
component: () => import("../views/dr/1.vue"),
meta: { title: '1', isTitle: true }
},
{
path: '*', redirect: '/', meta: { title: '错误', hidden: true },
}
]

//动态路由
export const dynamicRouter = [
{
path: "/2",
meta: { title: '4' },
component: 'dr/4.vue',
},
]

// ---------

// vuex的index.js中
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const state = {
is: false,//判断时候登录 登录true 没有false
routerArr: [] //静态路由+动态路由
}

const mutations = {
//点击了登录退出
CHANGESTORE(state, bool) {
state.is = bool
},
//存放路由
HEARDER(state, data) {
state.routerArr = data
},
}
const actions = {
updateStoreChange(context, state) {
context.commit('CHANGESTORE', state)
},
Nav(context, state) {
context.commit('HEARDER', state)
},

}
const getters = {
updateStore() {
return state.is
},
updateNav() {
return state.routerArr
}
}
const store = new Vuex.Store({
state, mutations, actions, getters
})
export default store

// ---------

// utils/loadingRouter.js文件中
import { staticRouter, dynamicRouter } from './data.js' //引入静态路由
import router from '../router/index'
import store from '../store/index'
const routes = staticRouter //静态路由保存
export default function useaddRoute() {
try {
// 满足store 有数据 已登录 没有缓存
if (store.state.is && store.state.routerArr.length === 4) {
store.dispatch('Nav', staticRouter)
//模拟数据请求
setTimeout(() => {
let data = routesData(dynamicRouter)
//将动态路由放放入总路由中
store.dispatch('Nav', data)
// 将路由放置缓存中
// router.addRoutes(data) //废弃 只是会警告但是还可以使用 数组形式
data.forEach(item => {
router.addRoute(item)
// 也可自定义路由路径、子路由等
})
}, 2000)
} else {
store.dispatch('Nav', staticRouter)
}
} catch (error) {
console.log(error)
}

}
function routesData(result) {
result.forEach(item => {
routes.push({
path: item.path,
name: item.name,
meta: item.meta,
component: () => import(`../views/${item.component}`),
})
});
return routes
}

// ---------

// APP.vue中使用
<template>
<div id="app">
<template v-for="(item, index) in arr">
<div :key="index" v-if="!item.meta.hidden">
<router-link :to="item.path"> {{ item.meta.title }}</router-link>
</div>
</template>
<button @click="login">登录</button>
<button @click="esc">退出</button>
<router-view></router-view>
</div>
</template>

<script>
import useaddRoute from "./utils/loadingRouter";
export default {
data() {
return {
arr: [],
};
},
created() {
useaddRoute();
this.arr = this.$store.getters.updateNav;
},
methods: {
async login() {
await this.$store.dispatch("updateStoreChange", true);
let isLogin = await this.$store.getters.updateStore;
isLogin ? useaddRoute() : "";
},
async esc() {
await this.$store.dispatch("updateStoreChange", false);
let isLogin = await this.$store.getters.updateStore;
isLogin ? "" : location.reload();
},
},
};
</script>