1

欢迎使用「积木小盒」

01

快速使用

<!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的动态更新

文本插值

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

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

利用指令添加基本的交互

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

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

02

计算属性

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

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

显示HTML内容

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

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

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

侦听器watcher

watch: {
  // 侦听content属性
  content: {
    // 处理函数
    handler(val, oldVal) {
      // ...
    },
  }
}

另外还有两个选项:

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

复用方法

可复用函数可以写在:methods中

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:在实例完全销毁之后调用

例如初始化:

created() {
  this.content = localStorage.getItem('content');
}

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

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

简写

<button @click="callback">

用v-bind绑定属性

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

简写

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

用v-for显示列表

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

也可以使用关键字in

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

动态CSS类

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

=>

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

=>

<div class="one three">

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

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

条件模板v-if

<div v-if="loading">

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

template标签

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

<template v-if="loading">
  <div>
  <div>
</template>

过滤器

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

注册

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

使用

{{ time | date }}

03

模板选项

new Vue({
  name: 'app',
  el: '#root',
  template: `<div id="#app">
    Hello world!
  </div>`
})

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

组件

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

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

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

使用

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

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

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

将prop添加到组件中

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

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

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

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

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

在组件上监听原生事件

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

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

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

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

this.$emit('play')

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

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

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

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

简写

<card @play="handlePlay" />

传参

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

动画过渡效果

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

<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类应用到元素中。

  1. v-leave-active:离开过渡状态被激活时,会应用该类。当离开过渡触发时,添加该类到元素中,并在DOM中移除元素时移除它。应该在该类中添加transition css属性并定义其过渡时长。
  2. v-leave:被移除的开始状态。当离开过渡触发时,添加该类到元素中,并在下一帧移除。
  3. v-leave-to:元素离开过渡的结束状态。在离开过渡触发后的下一帧添加,同时v-leave被移除。当DOM中移除元素时,该类也会被移除。

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

一个基本的淡出动画效果

.hand.v-enter-active,
.hand.v-leave-active {
  transition: opacity 1s;
}
.hand.v-enter,
.hand.v-leave-to {
  opacity: 0;
}

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

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

需要修改css类

.hand.fade-enter-active,
.hand.fade-leave-active {
  transition: opacity 1s;
}
.hand.fade-enter,
.hand.fade-leave-to {
  opacity: 0;
}

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

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

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

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

特殊的key属性

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

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

使用插槽分发内容

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

Vue.component('overlay', {
  template: `<div class="overlay" @click="handleClick">
    <div class="content">
      <slot />
    </div>
  </div>
  `
})

使用

<overlay>
  content
</overlay>

componennt组件

可以把其转换为任意组件

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

在script标签中编写模板

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

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

04

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

vue-cli

可以帮助我们创建vue工程

npm i -g vue-cli

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

  1. simple:不使用构建工具
  2. webpack: 使用webpack(推荐)
  3. browserify: 使用browserify

我们使用webpack-simple,并逐步引入功能

vue init webpack-simple demo

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

创建应用

添加入口文件

import Vue from "vue";

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

运行应用

npm run dev

配置Babel

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

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

在.babelrc文件中添加vue预设

{
  "presets": [
    ["env", { "modules": false }],
    "stage-3",
    "vue"
  ]
}

在src/main.js中添加

import "babel-polyfill"

更新依赖

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

  1. 手动更新

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

  1. 自动更新

npm update会更新最新的兼容版本

构建生产环境资源文件

npm run build

单文件组件

广泛应用于实际开发,它包含3种类型的根块:

  1. template
  2. script
  3. style

JSX

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

样式

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

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

添加预处理器

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

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

组件内的组件

import Movie from './Movie.vue';

export default {
  components: {
    Movie,
  }
}

05

Vue插件vue-router

import Vue from 'vue';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

使用router-view进行布局

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

<template>
  <div class="app-layout">
    <header></header>
    <router-view />
  </div>
</template>

创建路由

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是可选的,方便在路由时不会导致链接失效。

路由器对象

创建路由器对象

const router = new VueRouter({
  routes,
})
export default router;

导入

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,将被迫使用该模式。

路由器链接

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

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

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

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

发送后端请求

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

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的特殊方法

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会自动合并标准选项,如钩子、数据、计算属性、方法和侦听器,最后应用的那个将覆盖之前的那些。组件自有选项最终合并。

export default {
  data () {
    return {
      remoteDataLoading: 0,
    }
  }
}
<script>
import RemoteData from '../mixins/RemoteData'

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

指定prop的更多细节

<script>
export default {
  props: {
    title: {
      type: String,
      required: true,
    }
  }
}
</script>

v-bind的prop修饰符

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

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

v-model

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

<input v-model="value" />

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

路由跳转

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

路由类型

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

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

路由元属性

路由的类型可以添加在路由的meta属性中

{ path: '/tickets', ... , meta: { private: true } }

路由器导航守卫

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

router.beforeEach((to, from, next) => {
  if (to.meta.private && !state.user) {
    next({
      name: 'login',
      params: {
        wantedRoute: to.fullPath,
      }
    });
    return;
  }
  next();
})

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

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

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

嵌套路由

const routes = [
  {
    path: '/tickets',
    component: TicketsLayout,
    children: [
      {
        path: '',
        name: 'tickets',
        component: Tickets
      },
      {
        path: 'new',
        name: 'new-ticket',
        component: newTicket
      }
    ]
  }
]

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

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属性作为对象。

<FormTextArea rows="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页面

放置路由的最后

const routes = [
  ...
  {
    path: '*',
    component: NotFound
  }
]

滚动行为

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

const router = new VueRouter({
  routes,
  mode: 'history',
  scrollBehavior(to, from, savedPosition) {
    return { x: 0, y: 0 }
  },
})

每次滚动到<h1>元素

return { selector: 'h1' }

更完善的

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插件

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

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

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

在应用中注入store

import store from './store';

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

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

state是唯一数据源

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

const store = new Vue.Stroe({
  state() {
    return {
      user: null,
    }
  },
})

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

读取状态

<template>
...
</template>
<script>
export default {
  computed: {
    user () {
      return this.$store.state.user
    }
  }
}
</script>

使用mutation修改状态

const store = new Vuex.Store({
  state () { /* ... */ },

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

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

store.commit('user', userData);

严格模式

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

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

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

使用getter计算和返回数据

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

const store = new Vuex.Store({
  // ...
  getters: {
    user: state => state.user,
  },
})

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

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

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

使用action操作store

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

  1. context,它提供commit、dispatch、state以及链接到store的getters工具函数
  2. payload,它是dispatch分发时带上的参数

创建action

const store = new Vuex.Store({
  // ...
  actions: {
    logout ({ commit }) {
      commit('user', null);
    }
  }
})

分发action

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

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

辅助函数

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

  1. 类型的数组,其中的每一个元素对应于组件中的同名数据
  2. 对象,其中的键是组件中数据的别名,值则是类型

例如:

mapGetters(['a', 'b'])

等价于

{
  a () { return this.$store.getters.a },
  b () { return this.$store.getters.b },
}
mapGetters({ x: 'a', y: 'b' });

等价于

{
  x () { return this.$store.getters.a },
  y () { return this.$store.getters.b }
}

让我们在组件中使用它吧

import { mapGetters, mapActions } from 'vuex';

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

同步store和路由

import { sync } from 'vuex-router-sync';

sync(store, router);

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

Vuex模块

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

// maps.js
export default {
  namespaced: true,

  state () {
    return {
      center: {},
    }
  }
}
import maps from './maps';

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

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

带命名空间的模块

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

mapGetters({
  center: 'maps/center',
})

也可以指定命名空间

...mapGetters('maps', [
  'center'.
  'zoom',
]),

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

import { createNamespacedHelpers } from 'vuex';
const { mapGetters } = createNamespacedHelpers('maps');

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

访问全局元素

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

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完整编程能力来编写组件界面的情况。

Vue.component('my-title', {
  props: ['level'],
  render (h) {
    return h(
      `h${this.level}`,
      this.$slots.default,
    )
  }
})

JSX

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

export default {
  props: ['message'],
  render (h) {
    return <p class="content">
      { this.message }
    </p>
  }
}

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

import LocationInfo from './LocationInfo.vue';

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

更完善的vuex结合方式

<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

setBounds ({ dispatch }, value) {
  dispatch('posts/fetchPosts', {
    mapBounds: value,
  }, {
    root: true,
  })
}

作用域插槽

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

<template>
  <div class="search">
    <slot :result="results" />
  </div>
</template>

<script>
export default {
  computed: {
    results () {
      return /* ... */
    },
  },
}
</script>
<Search>
  <template slot-scope="props">
    <div>{{ props.result.length }} results</div>
  </template>
</Search>

还可以结合循环使用

<slot v-for="r of results" :result="r" />
<Search>
  <div slot-scope="props" class="result">{{props.result.label}}<div>
</Search>

函数式组件

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

export default {
  functional: true,
  render (h, { props, children }) {
    return h(`h${props.level}`, children)
  }
}

使用模板

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

使用PostCSS为CSS自动添加前缀

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

module.exports = {
  plugins: [
    require('autoprefixer'),
  ],
}

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

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

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

命令行ESLint

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

在Webpack中使用ESlint

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

"module": {
  "rules": [
    {
      "test": /\.(jsx?|vue)$/,
      "loader": "eslint-loader",
      "enforce": "pre"
    }
  ]
}

Jest单元测试

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

国际化

可以使用vue-i18n

服务端渲染(SSR)

配置较多,这里不赘述。


小番茄
67 声望5 粉丝