《vue.js项目实战》

《vue.js项目实战》

01

快速使用

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="root">
<p>{{ message }}</p>
</div>
<script>
// 创建vue实例
const app = new Vue({
// 根DOM元素的CSS选择器
el: '#root',
// 一些数据
data () {
return {
message: 'vue',
}
}
})
</script>
</body>
</html>

开发者工具

推荐使用Chrome的Vue.js devtools调试工具,极大提高调试效率

借助模板实现DOM的动态性

模板是描述View最简单的方法,只需少量额外语法就能实现DOM的动态更新

文本插值

1
2
3
<div id="root">
<p>{{ message }}</p>
</div>

此时数据和视图已经绑定了。vue框架有一个非常强大且高效的响应式系统,打开控制台输入app.message=’test’,视图也会自动更新显示test。

利用指令添加基本的交互

使用v-model实现数据和视图的双向绑定

1
2
3
4
<div id="root">
<p>{{ message }}</p>
<input v-model="message" />
</div>

02

计算属性

可以通过它定义一个新属性,该属性可以结合任意多个属性,并做相关转换操作。

  1. 计算属性的值会基于它的依赖进行缓存,如果依赖未改变是不会重新计算的。
  2. 计算属性真正用于应用中时,才会进行计算。
    1
    2
    3
    4
    5
    6
    computed: {
    notePreview () {
    // Makedown渲染为HTML返回
    return marked(this.content)
    }
    }

显示HTML内容

可以使用v-html指令做HTML插值,需要注意避免XSS攻击

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

v-text形同典型的文本插值,会对HTML标签做转义处理

侦听器watcher

1
2
3
4
5
6
7
8
9
watch: {
// 侦听content属性
content: {
// 处理函数
handler(val, oldVal) {
// ...
},
}
}

另外还有两个选项:

  1. deep,布尔类型,会以递归方式侦听嵌套对象内部值的变化。
  2. immediate,布尔类型,会立即触发调用处理函数,而不用等到属性值第一次变化时才调用。

    复用方法

    可复用函数可以写在:methods中
    1
    2
    3
    4
    5
    methods: {
    saveNote(val) {
    localStorage.setItem('content', val);
    }
    }

访问Vue实例

可以使用this访问Vue实例上的属性和方法

生命周期钩子

  1. beforeCreate:vue实例被创建时(如new Vue({}))、完成其他事项之前调用
  2. created:实例准备就绪之后调用。此时实例还没有挂载到DOM中
  3. beforeMount:挂载实例到Web页面之前调用
  4. mounted:实例被挂载到页面并且DOM可见时调用
  5. beforeUpdate:当实例需要更新时(一般来说,是当某个数据或计算属性发生改变时)调用
  6. updated:在把数据变化应用到模板之后调用。注意此时DOM可能还没有更新。
  7. beforeDestroy:在实例销毁之前调用
  8. destroyed:在实例完全销毁之后调用
    例如初始化:
    1
    2
    3
    created() {
    this.content = localStorage.getItem('content');
    }

用v-on实现按钮的单击事件

1
<button v-on:click="callback">

简写

1
<button @click="callback">

用v-bind绑定属性

1
<button v-bind:title="notes.length + ' note(s) already'">

简写

1
<button :title="notes.length + ' note(s) already'">

用v-for显示列表

1
<div v-for="item of items">{{ item.title }}</div>

也可以使用关键字in

1
<div v-for="item in items">{{ item.title }}</div>

动态CSS类

1
<div :class="['one', 'two', 'three']">

=>

1
<div class="one two three">

1
<div :class="{ one: true, two: false, three: true }">

=>

1
<div class="one three">

建议将非动态的类放到静态属性中,因为Vue会对静态值做优化处理

1
<div class="static" :class="{ two: false, three: true }">

条件模板v-if

1
<div v-if="loading">

此外还有v-else和v-else-if,也很好理解

template标签

template标签不会出现在DOM中,可以对实际元素进行包裹

1
2
3
4
<template v-if="loading">
<div>
<div>
</template>

过滤器

主要用于模板内部,在数据展示之前或者传递给一个属性之前对其进行处理。

注册

1
2
Vue.filter('date', time => moment(time)
.format('DD/MM/YY, HH:mm'))

使用

1
{{ time | date }}

03

模板选项

1
2
3
4
5
6
7
new Vue({
name: 'app',
el: '#root',
template: `<div id="#app">
Hello world!
</div>`
})

并不在之前的#root中嵌入模板

组件

组件是Vue应用的核心概念,是视图的一个个小部分。采用组件构建应用有助于应用的维护和升级,这已经成为了高效、可控地开发大型Web应用的标准方法。

组件有全局组件和局部组件。使用全局函数Vue.component()可以注册全局组件

1
2
3
4
5
Vue.component('top-bar', {
template: `<div class="top-bar">
Top bar
</div>`,
})

使用

1
2
3
4
5
new Vue({
template: `<div id="#app">
<top-bar />
</div>`,
})

其实每个组件都是Vue实例,Vue利用我们为top-bar组件提供的定义创建了Vue实例。

使用prop进行父组件到子组件的通信

将prop添加到组件中

1
2
3
Vue.component('top-bar', {
props: ['currentPlayerIndex'],
})

使用v-bind简写语法将应用的数据绑定到prop值上

1
<top-bar :current-player-index="currentPlayerIndex" />

建议对prop的名字使用短横线命名方法,而在JavaScript代码中使用驼峰式命名方法。
在top-bar组件中使用prop,用法和使用数据属性一样。

1
2
3
4
5
6
Vue.component('top-bar', {
props: ['currentPlayerIndex'],
template: `<div class="top-bar">
{{ currentPlayerIndex }}
</div>`
});

在组件上监听原生事件

Vue针对组件有自己的事件系统,叫做“自定义事件”。为了监听到组件的click事件,需要对v-on指令使用.native修饰符

1
<card @click.native="handlePlay" />

使用自定义事件进行子组件到父组件的通信

在组件内部,使用$emit这个特殊方法触发的事件可以被父组件捕获到。该方法接收一个固定的参数,即事件类型:

1
this.$emit('play')

在同一个vue实例中,可以使用$on监听自定义事件

1
2
3
this.$on('play', () => {
console.log('event');
})

同时,$emit还会触发事件到父组件中,父组件可以使用v-on指令监听该事件

1
<card v-on:play="handlePlay" />

简写

1
<card @play="handlePlay" />

传参

1
this.$emit('play', 1, 2)

动画过渡效果

使用CSS过渡,结合特殊的<transition>组件,使用v-if或v-show指令来帮助过渡。

1
2
3
<transition>
<hand v-if="!show" />
</transition>

当元素被添加到DOM时(进入阶段),<transition>组件会自动将下列CSS类应用到元素中。当然,过渡阶段也有相应的事件可以监听。

  1. v-enter-active:进入过渡状态被激活时,在元素插入DOM之前,并在动画结束时移除它。应该在该类中添加transition css属性并定义其过渡时长。
  2. v-enter:进入过渡的开始状态。在元素插入DOM之前,添加该类到元素中,同时在元素被插入的下一帧移除。例如,你可以在这个类中设置透明度。
  3. v-enter-to:元素进入过渡的结束状态。在元素插入DOM后的下一帧添加,同时v-enter被移除。当动画完成后,v-enter-to会被移除。
    当元素从DOM中移除时(离开阶段),<transition>组件会自动将下列CSS类应用到元素中。
  4. v-leave-active:离开过渡状态被激活时,会应用该类。当离开过渡触发时,添加该类到元素中,并在DOM中移除元素时移除它。应该在该类中添加transition css属性并定义其过渡时长。
  5. v-leave:被移除的开始状态。当离开过渡触发时,添加该类到元素中,并在下一帧移除。
  6. v-leave-to:元素离开过渡的结束状态。在离开过渡触发后的下一帧添加,同时v-leave被移除。当DOM中移除元素时,该类也会被移除。

注:在离开阶段,并不会立即从DOM中移除元素。当过渡结束后,才会将其移除,这样用户可以看到动画效果。

一个基本的淡出动画效果

1
2
3
4
5
6
7
8
.hand.v-enter-active,
.hand.v-leave-active {
transition: opacity 1s;
}
.hand.v-enter,
.hand.v-leave-to {
opacity: 0;
}

由于可能需要复用这个动画,我们可以给它取个名字

1
2
3
<transition name="fade">
<hand v-if="!show" />
</transition>

需要修改css类

1
2
3
4
5
6
7
8
.hand.fade-enter-active,
.hand.fade-leave-active {
transition: opacity 1s;
}
.hand.fade-enter,
.hand.fade-leave-to {
opacity: 0;
}

另外一个特殊的组件<transition-group>。当元素被添加、移除或移动时,该组件将对它的子元素做出动画效果。

1
2
3
<transition-group>
<div v-for="item of items" />
</transition-group>

transition-group默认情况下会作为span元素出现在DOM中。当然,也是可以修改的。

1
2
3
<transition-group tag="ul">
<div v-for="item of items" />
</transition-group>

特殊的key属性

当Vue更新存在于v-for循环中的DOM列表中,会尽量最小化DOM操作。尽可能的复用元素,并对DOM中需要修改的地方进行小范围修改。这意味着重复的元素会被打包到一起,不会在添加和移除列表中的项时移动它们。这也意味着对其应用过渡不会有动画效果。这时候就需要用key属性为元素指定唯一标识符。

1
<div v-for="item of items" :key="item.id"/>

使用插槽分发内容

我们使用slot元素可以将额外的布局和逻辑封装到overlay组件中,并且添加任意内容进去。

1
2
3
4
5
6
7
8
Vue.component('overlay', {
template: `<div class="overlay" @click="handleClick">
<div class="content">
<slot />
</div>
</div>
`
})

使用

1
2
3
<overlay>
content
</overlay>

componennt组件

可以把其转换为任意组件

1
2
<component is="h1"></component>
<component is="overlay" />

在script标签中编写模板

当定义组件时,使用这个ID引用模板即可。

1
2
3
<script type="text/x-template" id="banner">
<div></div>
</script>

04

接下来介绍一个更接近实际使用的开发模式。

vue-cli

可以帮助我们创建vue工程

1
npm i -g vue-cli

安装完成后执行vue list可以列出官方项目模板
主要有3种类型:

  1. simple:不使用构建工具
  2. webpack: 使用webpack(推荐)
  3. browserify: 使用browserify
    我们使用webpack-simple,并逐步引入功能
    1
    vue init webpack-simple demo

这个模板具有最小可用的webpack配置。

创建应用

添加入口文件

1
2
3
4
5
6
import Vue from "vue";

new Vue({
el: "#app",
render: h => h("div", "hello world")
});

运行应用

1
npm run dev

配置Babel

默认的babel配置使用名为env的Babel预设,支持ES2015以来所有稳定的js版本,还有一个stage-3的Babel预设,支持即将推出的js特性,如async/await。
我们需要再添加一个,支持JSX。并且还需要包含Babel提供的polyfill,以便Promise和Generator等新特性可以在旧版浏览器中运行。

1
npm i -D babel-preset-vue babel-polyfill

在.babelrc文件中添加vue预设

1
2
3
4
5
6
7
{
"presets": [
["env", { "modules": false }],
"stage-3",
"vue"
]
}

在src/main.js中添加

1
import "babel-polyfill"

更新依赖

如果需要更新依赖,可以进行如下操作

  1. 手动更新
    检查是否有新版本npm outdated
    Wanted是兼容的版本号。手动更新修改一下package.json文件中的版本号在重新安装一下包即可。
  2. 自动更新
    npm update会更新最新的兼容版本

    构建生产环境资源文件

    npm run build

    单文件组件

    广泛应用于实际开发,它包含3种类型的根块:
  3. template
  4. script
  5. style

    JSX

    是一种有助于编写渲染函数的语法,也可以采用。

    样式

  6. 有作用域的样式(scoped)
    1
    2
    <style scoped>
    </style>

原理是会有特殊的属性添加到了模板元素上,使得选择器只会匹配这个组件的模板。

添加预处理器

lang属性可以指定预处理器语言(sass、less、Stylus)

1
2
<style lang="sass" scoped>
</style>

组件内的组件

1
2
3
4
5
6
7
import Movie from './Movie.vue';

export default {
components: {
Movie,
}
}

05

Vue插件vue-router

1
2
3
4
import Vue from 'vue';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

使用router-view进行布局

它将渲染匹配当前路由的组件

1
2
3
4
5
6
<template>
<div class="app-layout">
<header></header>
<router-view />
</div>
</template>

创建路由

1
2
3
4
5
6
7
import Home from './components/Home.vue'
import FAQ from './components/FAQ.vue'

const routes = [
{ path: '/', name: 'home', component: Home },
{ path: '/faq', name: 'faq', component: FAQ }
]

路由名称name是可选的,方便在路由时不会导致链接失效。

路由器对象

创建路由器对象

1
2
3
4
const router = new VueRouter({
routes,
})
export default router;

导入

1
2
3
4
5
6
7
8
import router from './router';
import AppLayout from './components/AppLayout.vue'

new Vue({
el: "#app",
render: h => h(AppLayout),
router,
})

路由模式

有hash(默认)、history、abstract。
hash与任何浏览器和服务器都兼容
history 浏览器需要支持HTML5 API。服务器必须配置为当访问如/faq时发送主页,而不是404。
abstract 可以在任何JavaScript环境中使用(包括Node.js)。如果没有可用的浏览器API,将被迫使用该模式。

路由器链接

1
<router-link to="/faq">FAQ</router-link>


1
<router-link :to="{ name:'faq' }">FAQ</router-link>

组件默认会使用router-link-active CSS类,你可以使用它改变激活时的样式。
exact 可以设置路径完全匹配

1
<router-link :to="{ name:'faq' }" exact>FAQ</router-link>

发送后端请求

vue官方推荐使用axios,初始渲染可以在created生命周期中发送后端请求,再设置相应组件里的data。(接入vuex后,数据可以由vuex来管理)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async created() {
try {
this.loading = true;
const res = await axios.get('url..');
if (res.ok) {
this.questions = res.data;
} else {
...
}
} catch (e) {
...
}
this.loading = false;
}

用自己的插件扩展Vue

请求服务器数据的功能是自定义Vue插件的好例子。
创建一个插件,只有一个规则,插件应该是一个带有install方法的对象,该方法接受Vue构造函数作为第一个参数以及一个可选的options参数。然后,该方法通过修改构造函数为框架添加新特性。
这里,我们的插件将在所有组件上添加一个$fetch的特殊方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let baseUrl = '';

export default {
install (Vue, options) {
baseUrl = options.baseUrl;
Vue.prototype.$fetch = $fetch
}
}

export async function $fetch (method, url) {
const res = await axios[method](`${baseUrl}${url}`);
if (res.ok) {
return res.data;
} else {
throw new Error('error');
}
}

使用mixin复用代码

mixin可以在多个组件中复用组件定义(如计算属性、方法或侦听器)
mixin是可应用于其他定义对象(包括其他mixin)的组件定义对象。它看起来和普通组件定义完全一样。
Vue会自动合并标准选项,如钩子、数据、计算属性、方法和侦听器,最后应用的那个将覆盖之前的那些。组件自有选项最终合并。

1
2
3
4
5
6
7
export default {
data () {
return {
remoteDataLoading: 0,
}
}
}

1
2
3
4
5
6
7
8
9
10
<script>
import RemoteData from '../mixins/RemoteData'

export default {
mixins: [
RemoteData,
],
...
}
</script>

指定prop的更多细节

1
2
3
4
5
6
7
8
9
10
<script>
export default {
props: {
title: {
type: String,
required: true,
}
}
}
</script>

v-bind的prop修饰符

可以直接设置DOM节点的属性。而不是HTML属性。适合在处理输入框元素的属性(如value)中使用。

1
<input :value.prop="value" />

v-model

可实现数据与视图的双向绑定

1
<input v-model="value" />

默认使用的value prop和input事件。

路由跳转

1
this.$router.push({name: 'home'});

路由类型

我们可以有不同类型的路由

  1. 公开路由(都可以)
  2. 私有路由(仅登录用户)
  3. 访客路由(仅未登录用户)

    路由元属性

    路由的类型可以添加在路由的meta属性中
    1
    { path: '/tickets', ... , meta: { private: true } }

路由器导航守卫

当路由变化时会调用函数钩子,它们可以改变路由器的行为。

1
2
3
4
5
6
7
8
9
10
11
12
router.beforeEach((to, from, next) => {
if (to.meta.private && !state.user) {
next({
name: 'login',
params: {
wantedRoute: to.fullPath,
}
});
return;
}
next();
})

在login组件中使用wantedRoute参数重定向

1
this.$router.replace(this.$router.params.wantedRoute || { name: 'home' })

router.replace和push的区别是将当前条目替换为新路由,而不是添加条目。

嵌套路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const routes = [
{
path: '/tickets',
component: TicketsLayout,
children: [
{
path: '',
name: 'tickets',
component: Tickets
},
{
path: 'new',
name: 'new-ticket',
component: newTicket
}
]
}
]

对于嵌套路由,导航守卫中的条件需要修改:

1
2
3
4
5
6
7
8
router.beforeEach((to, from, next) => {
if (to.matched.some(r => r.meta.private) && !state.user) {

}
if (to.matched.some(r => r.meta.guest) && state.user) {

}
})

绑定属性

$attrs特殊属性,可以获取组件上所有非prop属性作为对象。

1
<FormTextArea rows="4">

1
2
3
4
<textarea
...
v-bind="$attrs"
>

动态路由

/tickets/:id -> /tickets/abc -> { id: ‘abc’ }
/tickets/:id/components/:comId -> /tickets/abc/components/42 -> { id: ‘abc’, comId: ‘42’ }

router.params中可以获取动态参数

404页面

放置路由的最后

1
2
3
4
5
6
7
const routes = [
...
{
path: '*',
component: NotFound
}
]

滚动行为

history模式允许我们在路由改变时管理页面滚动。
路由改变时滚动到页面的顶部:

1
2
3
4
5
6
7
const router = new VueRouter({
routes,
mode: 'history',
scrollBehavior(to, from, savedPosition) {
return { x: 0, y: 0 }
},
})

每次滚动到<h1>元素

1
return { selector: 'h1' }

更完善的

1
2
3
4
5
6
7
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
return { x: 0, y: 0 }

06

使用Vuex进行状态管理

Vuex可以让我们使用一个集中式store来管理应用的全局状态。

  1. 为什么使用集中式的状态管理
    随着组件之间的联系越来越复杂,太多的组件需要同步数据,应用的状态变得难以控制。Vuex从Flux获得灵感。Flux由一系列指导原则构成,阐明了如何使用集中式store来实现组件之间的单向数据流,可以很容易地推算出应用的逻辑和流程,从而极大地提升应用的可维护性。

Vuex store

Vuex的核心元素是store,它是一个特殊的对象,允许你将应用中的数据集中在一个设计良好的模型中。

store包含如下信息:
state,存储应用状态的响应式数据对象
getter,等价于store的计算属性
mutation,用来改变应用状态的函数
action,通常用来调用异步API的函数,然后使用mutation改变数据。

下面我们来创建一个store来熟悉这些概念。

安装Vuex插件

1
2
3
4
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

使用Vuex.Store构造函数创建store

1
2
3
const store = new Vuex.store({
// TODO 选项
});

在应用中注入store

1
2
3
4
5
6
7
8
import store from './store';

new Vue({
...App,
el: '#app',
router,
store,
})

现在可以在所有组件中使用$store这个特殊的属性来访问store了。

state是唯一数据源

Vuex的第一个原则就是,state是共享数据的唯一数据源。state是store的主要组成部分,它展示了应用中组件的所有共享数据。
我们在state中添加一个user属性

1
2
3
4
5
6
7
const store = new Vue.Stroe({
state() {
return {
user: null,
}
},
})

state是只读的,您不应该直接修改它。改变状态的唯一途经就是通过mutation,这样可以让共享状态易于预测。

读取状态

1
2
3
4
5
6
7
8
9
10
11
12
<template>
...
</template>
<script>
export default {
computed: {
user () {
return this.$store.state.user
}
}
}
</script>

使用mutation修改状态

1
2
3
4
5
6
7
8
9
const store = new Vuex.Store({
state () { /* ... */ },

mutations: {
user: (state, user) => {
state.user = user;
},
},
})

使用commit方法触发mutation处理函数

1
store.commit('user', userData);

严格模式

mutation的同步特性是出于调试的目的,可以借助开发者工具生成快照方便地调试应用。为了避免在mutation中使用异步调用,你可以在开发环境开启严格模式:

1
2
3
4
const store = new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
// ...
})

调试利器——时间旅行
使用Vuex你可以结合devtools追踪状态的每一次修改,这将极大地提升你的调试效率。

使用getter计算和返回数据

可以将getter看成store的计算属性

1
2
3
4
5
6
const store = new Vuex.Store({
// ...
getters: {
user: state => state.user,
},
})

可以使用getter代替之前直接获取状态的方法:

1
2
3
user () {
return this.$store.getters.user
},

我们使用getter,它可以让你在修改获取数据的方式时无需修改使用此数据的组件。

使用action操作store

action不仅可以提交mutation,还能做异步操作。
action的处理函数接受两个参数:

  1. context,它提供commit、dispatch、state以及链接到store的getters工具函数
  2. payload,它是dispatch分发时带上的参数
    创建action
    1
    2
    3
    4
    5
    6
    7
    8
    const store = new Vuex.Store({
    // ...
    actions: {
    logout ({ commit }) {
    commit('user', null);
    }
    }
    })

分发action

1
store.dispatch('action-type', payload);

你应该总是使用action而不是mutation。因为修改action中的代码会比修改组件中的代码更好,要把action看成应用逻辑的抽象。

辅助函数

Vuex还提供了辅助函数mapGetters和mapActions,它帮我们生成了相应的getter计算属性和action方法。辅助函数的参数可以是以下两者之一:

  1. 类型的数组,其中的每一个元素对应于组件中的同名数据
  2. 对象,其中的键是组件中数据的别名,值则是类型
    例如:
    1
    mapGetters(['a', 'b'])

等价于

1
2
3
4
{
a () { return this.$store.getters.a },
b () { return this.$store.getters.b },
}

1
mapGetters({ x: 'a', y: 'b' });

等价于

1
2
3
4
{
x () { return this.$store.getters.a },
y () { return this.$store.getters.b }
}

让我们在组件中使用它吧

1
2
3
4
5
6
7
8
9
10
import { mapGetters, mapActions } from 'vuex';

export default {
computed: mapGetters([
'user',
]),
methods: mapActions({
logout: 'logout',
}),
}

同步store和路由

1
2
3
import { sync } from 'vuex-router-sync';

sync(store, router);

你可以使用state.router对象获取当前路由信息,还可以使用时间旅行调试它。

Vuex模块

我们可以将状态划分为不同的模块,以便更好地管理。模块和store很像,store和其中的每一个模块都可以包含任意数量的模块,如何组织出最有利于项目的store模块结构需要你来斟酌。

1
2
3
4
5
6
7
8
9
10
// maps.js
export default {
namespaced: true,

state () {
return {
center: {},
}
}
}

1
2
3
4
5
6
7
8
import maps from './maps';

const store = new Vuex.Store({
// ...
modules: {
maps,
},
})

你可以通过store.state.maps使用这个模块。

带命名空间的模块

上面模块中的namespaced选项告诉Vuex在该模块的所有getter、mutation和action前添加maps命名空间。

1
2
3
mapGetters({
center: 'maps/center',
})

也可以指定命名空间

1
2
3
4
...mapGetters('maps', [
'center'.
'zoom',
]),

还可以使用createNamespacedHelpers生成基于某个命名空间的辅助函数:

1
2
3
4
5
6
7
8
import { createNamespacedHelpers } from 'vuex';
const { mapGetters } = createNamespacedHelpers('maps');

export default {
computed: mapGetters([
'center',
])
}

访问全局元素

你可以在命名空间模块的getter中访问到根getter(即所有的getter)

1
2
3
4
5
6
7
8
myAction ({ dispatch, commit, getters, rootGetters }) {
getters.a // store.getters['map/a']
rootGetters.a // store.getters['a']
commit('someMutation') // 'maps/someMutation'
commit('someMutation', null, { root: true }) // 'someMutation'
dispatch('someAction') // 'maps/someAction'
dispatch('someAction', null, { root: true }) // 'someAction'
}

使用JavaScript渲染函数编写视图

大多数情况下使用模板就够了,但你也可能遇到需要使用JavaScript完整编程能力来编写组件界面的情况。

1
2
3
4
5
6
7
8
9
Vue.component('my-title', {
props: ['level'],
render (h) {
return h(
`h${this.level}`,
this.$slots.default,
)
}
})

JSX

JSX语言是为了在render函数中编写更类似于HTML形式的代码。

1
2
3
4
5
6
7
8
export default {
props: ['message'],
render (h) {
return <p class="content">
{ this.message }
</p>
}
}

在JSX中,首字母大写很重要。

1
2
3
4
5
6
7
import LocationInfo from './LocationInfo.vue';

export default {
render (h) {
return <LocationInfo />
}
}

更完善的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
<script>
import { createNamespacedHelpers } from 'vuex';

const {
mapGetters: postsGetters,
mapActions: postsActions
} = createNamespacedHelpers('posts')

export default {
computed: {
...postsGetters([
'draft',
]),
title: {
get() {
return this.draft.title
},
set(value) {
this.updateDraft({
...this.draft,
title: value,
})
}
}
},
methods: {
...postsActions([
'updateDraft'
])
}
}
</script>

分发无命名空间的action

1
2
3
4
5
6
7
setBounds ({ dispatch }, value) {
dispatch('posts/fetchPosts', {
mapBounds: value,
}, {
root: true,
})
}

作用域插槽

我们可以通过插槽将属性传递给外部视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div class="search">
<slot :result="results" />
</div>
</template>

<script>
export default {
computed: {
results () {
return /* ... */
},
},
}
</script>
`

1
2
3
4
5
<Search>
<template slot-scope="props">
<div>{{ props.result.length }} results</div>
</template>
</Search>

还可以结合循环使用

1
<slot v-for="r of results" :result="r" />

1
2
3
<Search>
<div slot-scope="props" class="result">{{props.result.label}}<div>
</Search>

函数式组件

每个组件实例在创建时都需要做一些设置,比如数据响应系统、组件生命周期等。函数式组件自身没有状态(无法使用this关键字),也不会在开发者工具中显示。但在速度更快、使用内存更少。

1
2
3
4
5
6
export default {
functional: true,
render (h, { props, children }) {
return h(`h${props.level}`, children)
}
}

使用模板

1
2
3
<template functional>
<div class="my-component">{{ props.message }}</div>
</template>

使用PostCSS为CSS自动添加前缀

为CSS自动添加前缀可以提高样式对浏览器的兼容性。PostCSS是一个专门用于CSS后处理的库。它拥有一个非常模块化的架构,通过添加插件来使用各种方式处理CSS。PostCSS不需要额外安装,vue-loader中已经包含了它,我们只需要按需安装插件即可,我们需要安装autoprefixer这个包。
在根目录添加postcss.config.js配置

1
2
3
4
5
module.exports = {
plugins: [
require('autoprefixer'),
],
}

这样autoprefixer就会自动处理我们的CSS代码。

通过ESLint提升代码质量和风格

ESLint提供了一系列可以开启和关闭的lint规则,有助于保持源代码的整洁性和一致性。

命令行ESLint

1
eslint --ext .js, .jsx, .vue src

在Webpack中使用ESlint

添加一条新的ESLint加载器规则。

1
2
3
4
5
6
7
8
9
"module": {
"rules": [
{
"test": /\.(jsx?|vue)$/,
"loader": "eslint-loader",
"enforce": "pre"
}
]
}

Jest单元测试

我们需要都重要的代码进行单元测试,推荐使用Jest。

国际化

可以使用vue-i18n

服务端渲染(SSR)

配置较多,这里不赘述。

0%