[转发+补充]教你如何学好Vue《文档》

重新对博客内容进行了修正,为了避免自己的文字引来不必要的歧义,所以删自己关于技术方面的,计划这一分类更多的采用引用为主。

本文转自简书《教你如何学好vue《文档》》,但对内容有所删减(Vue 历史、作者以及案例)以及补充,若想了解还请移步至原文。


课程目录

  • ECMAScript 6
  • Vue.js
  • Vue CLI
  • Vuex
  • Vue Router
  • axios
  • webpack
  • Vue SSR
  • RESTful

前置知识

  • HTML
  • CSS
  • JavaScript
  • 基本的 ECMAScript 6 语法
  • 基本的 Ajax 使用
  • node命令行操作
  • npm命令
  • 基本的 Git 操作

ECMAScript 6 常用的基础知识点

ES6介绍

ECMAScript 6.0(以下简称 ES6 )是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了也叫 ECMAScript 2015 。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言

let 和 const 命令

ES6 新增了 let 命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在 let 命令所在的代码块内有效。

{
    let a = 10;
    var b = 1;
}
a // ReferenceError: a is not defined.
b // 1

for (let i = 0; i < 10; i++) {
    // ...
}
console.log(i); // ReferenceError: i is not defined

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415;
PI // 3.1415
PI = 3; // TypeError: Assignment to constant variable.

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

constlet不存在变量提升


变量的解构赋值

let a = 1;
let b = 2;
let c = 3;

// ES6 允许写成下面这样。
let [a, b, c] = [1, 2, 3];

// 对象的解构赋值
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"

let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"

let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined

模板字符串

let place = "world"
// 变量place没有声明
let msg = `Hello, ${place}`;

对象的扩展

属性的简洁表示法

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};

function f(x, y) {
    return {x, y};
}
// 等同于
function f(x, y) {
    return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}

函数的扩展

函数的扩展 ES6 允许使用 =>(箭头)定义函数。

var f = v => v;
// 等同于
var f = function (v) {
	return v;
};

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

var sum = (num1, num2) => {
    return num1 + num2;
}

箭头函数有几个使用注意点:

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

Promise 对象是一个构造函数,用来生成Promise实例。下面代码创造了一个Promise实例。

const promise = new Promise(function(resolve, reject) {
	if (/* 异步操作成功 */){
 		resolve(value); // 容器状态为成功
 	} else {
 		reject(error); // 容器状态为失败
 	}
});

promise.then(function() {
    console.log('resolved.');
});

对于错误返回的捕获

// bad
promise.then(function(data) {
    // success
}, function(err) {
    // error
});

// good
promise.then(function(data) { //cb
    // success
}).catch(function(err) {
    // error
});

内容补充:为什么要用 Promise?

由于 Javascript 是单线程的,所以对于普通函数来讲执行顺序是 fun1 > fun2 > fun3 > fun4,但如是多个异步函数,则这种多线程操作就会导致执行顺序不固定,为此我们为了能保障执行顺序就需要在一个异步函数中再去运行另一个异步函数,这个行为称之为 回调地狱

Promise 本身是一个同步函数。它可以理解为一个容器,该容器用来监控其中异步函数的执行结果。

Promise 函数执行后,可以通过链式追加 .then() 决定函数成功或失败后需要操作的内容。

promise.all() 可以将多个 Promise 实例包装成一个新的 Promise 实例。成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。

promise.race() 顾名思义,就是赛跑的意思,Promise.race([p1, p2, p3]) 里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

async/ awite

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 函数是什么?一句话,它就是 Generator 函数的语法糖。

基本用法

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

function timeout(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}
async function asyncPrint(value, ms) {
    await timeout(ms);
    console.log(value);
}
asyncPrint('hello world', 50); // 上面代码指定 50 毫秒以后,输出 hello world。

返回 Promise 对象

async function f() {
 return 'hello world';
}
f().then(v =&gt; console.log(v)) // "hello world"

Module

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD(require.js 库,专门用于在浏览器中进行模块化开发,几乎已经淘汰了) 两种。前者用于服务器(Node.js),后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

为了学习方便,我们这里使用 Node.js 环境来举例说明 ES6 模块规则

这是一个 Node 文件,将其命名为 foo.js ,内容如下:

// export 也用于导出
// 但是可以多次使用
export const a = 1
export const b = 2
export const c = 3
export const d = 4

function add(x, y) {
 return x + y
}

// 等价于 module.exports = add
// export default 只能使用一次
export default add

// 模块中有多个成员,一般都使用 export xxx
// 如果只有一个成员,那就 export default

补充内容:module.export 与 export 的区别
export 导出的源码逻辑如下
module = {export: {}}
var export = module.export
也就是说 export === module.export
当 module.export 内有多个属性的时候,也可以通过 export 导出
但若想单独导出方法或变量时,则只能使用 export default
因为当 export 重定义方法后就与 module.export 脱离了联系

将 foo.js 注入项目入口文件 main.js:

// 加载 export default 导出的成员
// import foo from './foo'
// console.log(foo)

// 按需加载 export 导出的成员
// import { a, b } from './foo.mjs'
// console.log(a, b)

// 一次性加载所有成员(包括 default 成员)
// import * as foo from './foo.mjs'
// console.log(foo)
// console.log(foo.a)
// console.log(foo.default(10, 3)) // 很少这样去访问 default 成员

// 为了方便,先加载默认 default 成员,然后加载其它 export 成员
// import abc, { a, b } from './foo'
// console.log(abc) // export default 成员
// console.log(a, b) //

// 可以使用 as 起别名
import { a as aa } from './foo'
console.log(aa)
// console.log(foo(1, 2))

补充内容:
import * as xxx from ‘xxx’ // 会将若干 export 导出的内容组合成一个对象返回
import xxx from ‘xxx’ // 只会导出这个默认的对象作为一个对象


Vue 基础

  • 官方文档:https://cn.vuejs.org/

Vue是什么?

它本身只是一个用于数据驱动视图更新的库,但随着衍生生态的发展,如今已经有了 Vue RouterVuexVue CLIVue Server Renderer 等功能库,所以当 Vue 和这些核心功能库结合到一起的时候,我们称之为一个框架(Vue 全家桶)。

Vue.js 不支持 IE8 及其以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。最新稳定版本:2.6.10

安装

  • 直接下载

    • 开发版本:https://vuejs.org/js/vue.js
    • 生产版本:https://vuejs.org/js/vue.min.js
  • CDN

    <script src="https://cdn.jsdelivr.net/npm/vue"></script> // 最新稳定版
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> // 指定版本
    
  • 使用 npm 下载(最好创建一个 package.json 文件,用来存储第三方包依赖信息)

    npm install vue // 最新稳定版
    npm install vue@版本号 // 指定版本
    

创建了第一个 Vue 应用

<meta charset="UTF-8">
<title>Document</title>
<div id="app">
    {{ message }}
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script>
    new Vue({
        el: '#app', // 实例选项
        data: {
            message: 'Hello Vue.js!' // 声明式渲染
        }
    })
</script>

实例选项 - el

  • 类型:string | Element:

  • 详细: 提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。在实例挂载之后,元素可以用 vm.$el 访问

  • 注意:el 不能是 html、body 节点

实例选项 - data

  • 类型:Object | Function
new Vue({
    el: '#app',
    data(){
        return {message:"demo"}
    }
})

new Vue({
    el: '#app',
    data:{message:"demo"}
})

组件的定义只接受 function ,模板中访问的数据必须初始化到 data 中

模板语法

插值 {{}}

// 文本
<p>{{ message }}</p>
<span>{{ message }}</span>
<strong>{{ message }}</strong>

// JavaScript 表达式
<p>{{ number + 1 }}</p>
<p>{{ number + 1 > 10 ? 'number大于10' : 'number小于10' }}</p>
<p>{{ arr }}</p>
<p>{{ message.split('').reverse().join('') }}</p>

v- 指令

指令 (Directives) 是带有 v- 前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)

  • v-if :指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值的时候被渲染。

    <h1 v-if="awesome">Vue is awesome!</h1>
    
  • v-else

    <div v-if="Math.random() > 0.5">
        Now you see me
    </div>
    <div v-else>
        Now you don't
    </div>
    
  • v-else-if

    <div v-if="type === 'A'">
        A
    </div>
    <div v-else-if="type === 'B'">
        B
    </div>
    <div v-else>
        Not A/B
    </div>
    
  • v-show

    <h1 v-show="ok">Hello!</h1>
    

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好

  • v-text

    <div v-text="text"></div> // 带标签的内容不会被解析
    
  • v-html

    <div v-text="text"></div> // 带标签的内容会被解析
    
  • v-model(表单输入绑定) :

    <input v-model="test"/> //这个指令是一个语法糖,一般只用于input标签
    
  • v-for(列表渲染)

    v-for="item in todos"
    v-for="(item, index) in todos"
    
  • v-bind(绑定指令)

    绑定指令能绑定元素上的任何属性,常用的比如 id, style, class, src, … 也能绑定自定义属性。

    操作元素的 class 列表和内联样式是数据绑定的一个常见需求,因为它们都是属性,所以我们可以用 v-bind 处理它们,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组

    Class 绑定

    // 一般使用
    // class 的值就是变量 className 的值
    <div v-bind:class="className"></div>
    <div v-bind:class="className?'a':'b'"></div>
    
    // 对象语法
    // 如果变量isActive 值为真则有active这个类,否则没有
    <div v-bind:class="{ active: isActive }"></div>
    <script>
    data: {
        isActive: true
    }
    </script>
    // 渲染为
    <div class="active"></div>
    
    // 数组语法(使用偏少)
    <div v-bind:class="[activeClass, errorClass]"></div>
    <script>
    data: {
        activeClass: 'active',
        errorClass: 'text-danger'
    }
    </script>
    // 渲染为
    <div class="active text-danger"></div>
    

    Style 绑定

    // 对象语法
    <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
    <script>
    data: {
        activeColor: 'red',
        fontSize: 30
    }
    </script>
    // 直接绑定到一个样式对象通常更好,这会让模板更清晰
    <div v-bind:style="styleObject"></div>
    <script>
    data: {
        styleObject: {
            color: 'red',
            fontSize: '13px'
        }
    }
    </script>
    

    缩写

    <a v-bind:href="url">...</a> // 完整语法
    <a :href="url">...</a> // 缩写
    
  • v-on(监听指令)

    可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

    <div id="example-1">
        <button v-on:click="counter += 1">Add 1</button>
        <p>The button above has been clicked {{ counter }} times.</p>
    </div>
    
    var example1 = new Vue({
        el: '#example-1',
        data: {
            counter: 0
        }
    })
    

    许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称

    <div id="example-2">
        <!-- `greet` 是在下面定义的方法名 -->
        <button v-on:click="greet">Greet</button>
        <!-- DOM的原生事件,可以用特殊变量 $event 把它传入方法 -->
        <button v-on:click="say('hi')">Say hi</button>
    </div>
    var example2 = new Vue({
        el: '#example-2',
        data: {
            name: 'Vue.js'
        },
        methods: { // 在 `methods` 对象中定义方法
            greet: function (event) {
                alert('Hello ' + this.name + '!') // `this` 在方法里指向当前 Vue 实例
                if (event) { // `event` 是原生 DOM 事件
                    alert(event.target.tagName)
                }
            },
            say: function (message,ev) {
                alert(message)
                console.log(ev)
            }
        }
    })
    

    缩写

    <a v-on:click="doSomething">...</a> // 完整语法
    <a @click="doSomething">...</a> // 缩写
    

    补充内容:修饰符
    .stop 阻止事件冒泡
    .self 当事件在该元素本身触发时才触发事件
    .capture 添加事件侦听器是,使用事件捕获模式
    .prevent 阻止默认事件
    .once 事件只触发一次

slot 插槽(补充)

补充内容:《vue中的slot(插槽)》

插槽(Slot)是 Vue 提出来的一个概念,正如名字一样,插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。

插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。

<template>
<div>
    <slotOne1>
        <p style="color:red">我是父组件插槽内容</p>
    </slotOne1>
</div>
</template>

在父组件引用的子组件中写入想要显示的内容,

我是父组件插槽内容

为组件插槽显示的内容
<template>
    <div class="slotOne1">
        <slot></slot>
    </div>
</template>

在子组件中写入slot,slot所在的位置承接的就是父组件中

我是父组件插槽内容

具名插槽

<template>
    <div class="slottwo">
        <slot name="header"></slot>
        <slot></slot>
        <slot name="footer"></slot>
    </div>
</template>

在子组件中定义了三个 slot 标签,其中有两个分别添加了 name 属性 header 和 footer

<template>
  <div>
    <slot-two>
        <p>啦啦啦,啦啦啦,我是卖报的小行家</p>
        <template slot="header">
            <p>我是name为header的slot</p>
        </template>
        <p slot="footer">我是name为footer的slot</p>
    </slot-two>
  </div>
</template>

在父组件中使用 template 并写入对应的 slot 值来指定该内容在子组件中现实的位置(当然也不用必须写到 template ),没有对应值的其他内容会被放到子组件中没有添加 name 属性的 slot 中

计算属性 ( computed )

  • 计算属性是根据源数据衍生出来的新数据,既然是衍生的数据,那只要源数据在 data 中声明过即可,同时,计算属性的试图更新必须依赖于data属性的更新,此外衍生出来的新数据不能在data中有定义
  • 计算属性的名称就是计算属性处理函数的名称,使用时可以在模版中绑定计算属性,绑定方法与data中的普通属性一致
  • 计算结果并返回,只有当被计算的值发生改变时才会触发
  • 计算属性是在 DOM 执行完成后执行,该属性有缓存功能
// 基础用法
data() {
    return {
        str: 'string'
    }
}
computed: {
    beautifyStr() {
        return this.str.split('');
    }
}

监听器( watch )

对 data 属性的监听,说明属性是在 data 中声明过的属性更新时调用监听函数,可选参数分别为新值和旧值,对属性重新设置值只要跟原来的值相等就不会触发函数调用,这一点跟计算属性是相似的,监听某一个值,当被监听的值发生变化时,执行对应的操作,与 computed 的区别是,watch 更加适用于监听某一个值的变化并做对应的操作,比如请求后台接口等,而 computed 适用于计算已有的值并返回结果

// 基础用法
new Vue({
    el: '#id',
    data: {
        firstName: 'Leo',
        lastName: 'Alan',
        obj1: {
            a: 0
        }
    },
    watch: {
        // 监听firstName,当firstName发生变化时就会执行该函数
        firstName(newName, oldName) {
            // 执行需要的操作...
            // 注:初始化不会执行,只有当被监听的值(firstName)发生变化时才会执行
        },
        // 监听lastName
        lastName: {
            handler(newName, oldName) {
                // 执行需要的操作...
            },
            immediate: true // true: 初始化时就会先执行一遍该监听对应的操作
        },
        obj1: {
            handler() {
                // 执行需要的操作...
            },
            deep: true // 该属性默认值为 false.
            // 当被监听的值是对象,只有 deep 为 true 时,对应属性的值( obj1.a )发生变化时才能触发监听事件,但是这样非常消耗性能
        },
        // 监听对象具体的属性,deep 就不需要设置为 true 了
        'obj1.a': {
            handler() {
                // 执行需要的操作...
            }
        }
    }
})

Vue-Cli脚手架

Vue CLI 是 Vue 的脚手架工具,它可以帮助我们快速生成 Vue 基础项目代码,提供开箱即用的功能特性。

  • 官方文档:https://cli.vuejs.org/
  • GitHub:https://github.com/vuejs/vue-cli

安装

// > 依赖要求:Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。
npm install -g @vue/cli

在终端中输入 vue --version 若可以打印出版本号,则代表安装成功。

创建项目

运行以下命令来创建一个新项目

vue create my-project

内容补充:输入命令后,会跳出如下问题

Project name (baoge) 项目名称(注意这里的名字不能有大写字母,如果有会报错)
Project description (A Vue.js project) 项目描述,也可直接点击回车,使用默认名字
Author () 作者
Runtime + Compiler: recommended for most users 运行加编译,既然已经说了推荐,就选它了
Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specificHTML) are ONLY allowed in .vue files - render functions are required elsewhere 仅运行时,已经有推荐了就选择第一个了
Install vue-router? (Y/n) 是否安装vue-router,这是官方的路由,大多数情况下都使用,这里就输入 y 后回车即可
Use ESLint to lint your code? (Y/n) 是否使用 ESLint 管理代码,ESLint 是个代码风格管理工具
Pick an ESLint preset (Use arrow keys) 选择一个 ESLint 预设,编写 vue 项目时的代码风格,直接 y 回车
Setup unit tests with Karma + Mocha? (Y/n) 是否安装单元测试
Setup e2e tests with Nightwatch(Y/n)? 是否安装 e2e 测试

配置完成后,可以看到目录下多出了一个项目文件夹,然后 cd 进入这个文件夹

启动开发模式

npm run serve

项目打包

npm run build

代码检查

npm run lint

开发期间不要关闭命令窗口,如果关了,就重新 npm run serve
浏览器中打开 http://localhost:8080/ 看到 vue 初始页面,则说明初始化创建成功了

项目的目录结构

node_modules // 第三方包存储目录
public // 静态资源,已被托管
src // 源代码
    assets // 资源目录,存储静态资源,例如图片等
    components // 存储其它组件的目录
    App.vue // 根组件
    main.js // 入口文件
.gitignore // git 忽略文件
babel.config.js // 不用关心
package.json // 包说明文件
README.md // 项目说明文件
package-lock.json // 包的版本锁定文件

项目的启动过程

找到 main.js 入口 > 加载 Vue > 加载 App 组件 > 创建 Vue 实例 > 将 App 组件替换到入口节点

创建单文件组件

组件 Component 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。

推荐把通用组件创建到 components 目录中

单文件组件只是承载组件的容器而已,既不是全局也不是局部,如果要使用这个单文件组件,必须注册

单文件组件模板结构如下:

<template>
    <div>foo 组件</div>
</template>

<script>
export default {
    data() {},
    methods: {}
}
</script>

<style></style>
  • template 只能有一个根节点(template 本身不算)
  • script 用来配置组件的选项(data、methods、watch……)
  • style