Tian Jiale's Blog

vue 基础入门

官网

前置知识

  • 完整的学习 vue:html + css、JavaScript、css3、HTML5、第三方库、网络通信、ES6+、webpack、模块化、包管理、css 预编译器
  • 体验 vue 功能:html + css、JavaScript

vue 是前端的一种开发框架,它主要解决前端开发中的核心痛点——复杂的 DOM 操作

vue 拥有以下特点:

  • 渐进式
  • 组件化
  • 响应式

vue 的应用场景:

  • 前台的部分页面
  • 后台的部分页面

vue 的版本

  • 目前的正式版本是 2.x
  • 3.x 版本即将上线,对 2.x 是兼容的,要完全体验到 3.x 的优势,还要学习 TypeScript

vue 在前端的地位

前端是连接终端设备与人的技术

除了 vue 之外,还需要学习哪些前端知识?

TypeScript、ThreeJS、WebGL、ECharts、NodeJS、Egg、Redis、Mongodb、Sequelize、Express、Koa、移动端……

学习准备

  1. 安装 VSCode

    官网

  2. 安装 VSCode 相应插件:

    • Auto Rename Tag:能够自动更改结束标签
    • Live Server:自动搭建本地服务器
    • Prettier - Code formatter:代码美化
    • Vetur:vue 组件格式支持
    • vscode-icons:美化文件图标

起步

使用 vue 的两种方式:

  • 在页面中引入 vue 的文件
  • 脚手架
    • 官方脚手架 vue-cli
    • 其他民间脚手架,如 webpack-simple

体验 vue

graph LR
A(数据 Model) -->|变动| B(vue View Model)
B(vue View Model) -->|更改| A(数据 Model)
B(vue View Model) -->|渲染| C(界面 View)
C(界面 View) -->|操作| B(vue View Model)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>起步</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 界面模板 -->
      <!-- mustache语法 -->
      <h1>系统名称:{{title}}</h1>
      <ul>
        <li v-for="(item, i) in products">
          名称:{{item.name}}
          <button @click="changeStock(item, item.stock - 1)">-</button>
          <span v-if="item.stock > 0">库存:{{item.stock}}</span>
          <span v-else>无货</span>
          <button @click="changeStock(item, item.stock + 1)">+</button>
          <button @click="deleteProduct(i)">删除</button>
        </li>
      </ul>
    </div>
    <script>
      var vm = new Vue({
        // 配置
        el: '#app', // css选择器
        data: {
          // 和界面相关的数据
          title: '库存管理系统',
          products: [
            { name: 'iphone', stock: 10 },
            { name: '小米', stock: 3 },
            { name: 'vivo', stock: 15 },
            { name: '华为', stock: 32 },
          ],
        },
        methods: {
          // 界面使用到的方法
          deleteProduct(i) {
            this.products.splice(i, 1);
          },
          changeStock(product, newStock) {
            if (newStock < 0) {
              newStock = 0;
            }
            product.stock = newStock;
          },
        },
      });
    </script>
  </body>
</html>

ES6 知识补充

  • 速写属性
var name = "小姐姐"
var age = 18;
var person = {
    name,
    age
}
  • 速写方法
var person = {
    name: "小姐姐"
    age: 18,
    sayHello() {
        console.log("Hello!");
    }
}
  • 模板字符串
var person = {
    name: "小姐姐"
    age: 18,
}

str = `I'm $(person.name),
my age is $(person.age).`;

注入

配置对象中的部分内容会被提取到 vue 实例中:

  • data
  • method

该过程称之为注入

注入目的有两个:

  • 完成数据响应

    vue2.0 是通过 Object.defineProperty 方法完成数据响应式(无法检测到对象属性的新增或删除、不能监听数组的变化),vue3.0 是通过 Class Proxy 完成的数据相应式

  • 绑定 this

虚拟 DOM 树

为了提高渲染效率,vue 会把模板编译成为虚拟 DOM 树(vm._vnode),然后再生成真实的 DOM(vm._vnode.elm)

模板 —— 虚拟节点树 —— 真实节点树

当数据更改时,将重新编译成虚拟 DOM 树,然后对比前后两棵树(diff 算法),仅将差异部分反映到真实 DOM,这样既可最小程度的改动真实 DOM,又可提高效率

因此,对于 vue 而言,提升效率重点着眼于两个方面:

  • 减少新的虚拟 DOM 的生成
  • 保证对比之后,只有必要的节点有变化

vue 提供了多种方式生成虚拟 DOM 树:

  1. 在挂载的元素内部直接书写,此时将使用元素的 outHTML 作为模板

    <div id="app">
            <!-- 界面模板 -->
            <!-- mustache语法 -->
            <h1>系统名称:{{title}}</h1>
            <ul>
              <li v-for="(item, i) in products">
                  名称:{{item.name}}
                  <button @click="changeStock(item, item.stock - 1)">-</button>
                  <span v-if="item.stock > 0">库存:{{item.stock}}</span>
                  <span v-else>无货</span>
                  <button @click="changeStock(item, item.stock + 1)">+</button>
                  <button @click="deleteProduct(i)">删除</button>
                </li>
            </ul>
        </div>
    
  2. 在 template 配置中书写

    var vm = new Vue({ // 配置
                el: "#app", // css选择器
                templete: `<div id="app">
            <!-- 界面模板 -->
            <!-- mustache语法 -->
            <h1>系统名称:{{title}}</h1>
            <ul>
              <li v-for="(item, i) in products">
                  名称:{{item.name}}
                  <button @click="changeStock(item, item.stock - 1)">-</button>
                  <span v-if="item.stock > 0">库存:{{item.stock}}</span>
                  <span v-else>无货</span>
                  <button @click="changeStock(item, item.stock + 1)">+</button>
                  <button @click="deleteProduct(i)">删除</button>
                </li>
            </ul>
        </div>`
            })
    
  3. 在 render 配置中用函数直接创建节点数,此时,完全脱离模板,将省略编译步骤

    var vm = new Vue({ // 配置
                el: "#app", // css选择器
                render(h) {
                    //return h("h1", "hello");
                    //return h("h1", this.title);
                    return h("div", [
                        h("h1", this.title),
                        h("p", "例子")
                    ]);
                }
            })
    

这些步骤从上到下,优先级逐渐提升,注意:虚拟节点树必须是单根的

挂载

将生成的真实 DOM 树,放置到某个元素位置,称之为挂载

挂载的方式:

  1. 通过 el:“css 选择器"进行配置
  2. 通过 vue 实例.$mount(“css 选择器”)进行配置

完整流程

2021-01-26-09-09-23-15

模板语法

内容

vue 中的元素内容使用 musrache 模板引擎进行解析

mustache

指令

指令会影响元素的渲染行为,指令始终以v-开头

基础指令:

  • v-for:循环渲染元素
<li v-for="(item, i) in arrary">
  arrary:可迭代,下标:i,循环变量:item
</li>
  • v-html:设置元素的 innerHTML,该指令会导致元素的模板内容失效
<div v-html="html">
  html:带标签的HTML字符串
</div>
  • v-on:注册事件
    • 该指令由于十分常用,因此提供了简写@
    • 事件支持一些指令修饰符,如 revent、stop
    • 事件参数会自动传递
<button v-on:click="function()"></button>
<button @click="function()"></button>
<button v-on:click.prevent="function()"></button>
<button v-on:click="function($event)">手动传入事件参数</button>
  • v-bind:绑定动态属性
    • 该指令由于十分常用,因此提供了简写:
<img v-bind:src="imgUrl"></img>
  • v-show:控制元素可见度
<p v-show="true">内容</p>
  • v-if、v-else-if、v-else:控制元素生成
  • v-model:双向数据绑定,常用于表单元素
    • 该指令是 v-on 和 v-bind 的复合版
<input type="text" :value="text" @input="text=$event.target.value">
<input type="text" v-model="text">

进阶指令:

  • v-slot
  • v-text
  • v-pre
  • v-cloak

特殊属性

最重要的特殊属性:key

该属性可以干预 diff 算法,在同一层级,key 值相同的节点会进行对比,key 值不同则不会

在循环生成的节点中,vue 强烈建议给予每个节点唯一且稳定的 key 值

其他特殊属性:

  • ref
  • is
  • slot
  • slot-scope
  • scope

计算属性

<div id="app">
    <p>姓:{{firstName}}</p>
    <p>名:{{lastName}}</p>
    <p>全名:{{fullName}}</p>
</div>
<script>
    var vm = new Vue({
      // 配置
      el: "#app", // css选择器
      data: {
          // 和界面相关的数据
          firstName: "张",
          lastName: "三",
      },
      computed: {
          //fullName() {
          //    return this.firstName + this.lastName;
          //}
          fullName: {
              get() {
                  return this.firstName + this.lastName;
              },
              set(val) {
                  // 数据处理
              },
          },
      },
  });
</script>

计算属性和方法的区别:

  • 计算属性可以赋值,而方法不行
  • 计算属性会进行缓存,如果依赖不变,则直接使用缓存结果,不会重新计算
  • 凡是根据已有数据计算得到新数据的无参函数,都应该尽量写成计算属性,而不是方法

知识补充

  1. 箭头函数

    • 任何可以书写匿名函数的地方均可书写箭头函数
    • 箭头函数将会绑定 this 为函数书写位置的 this 值
    var sum = (a, b) => { // 一个参数可以省略小括号
      return a + b;
    };// 只有一个返回值,可以省略大括号和return
    
  2. 模块化

    • 没有模块化的世界:全局变量污染、难以管理的依赖
    • 常见的模块化标准:CommonJS、ES6 Module、AMD、CMD、UMD
./utl.js
export default {
  test1() {},
  test2() {}
}

./main.js
import utl from "./utl.js"
utl.test1();

组件概念

一个完整的页面是复杂的,如果将其作为一个整体来开发,将会遇到下面的困难

  • 代码凌乱臃肿
  • 不宜协作
  • 难以复用

vue 推荐使用一种更加精细的控制方案——组件化开发

所谓组件化开发,即把一个页面中区域功能细分,每一个区域作为一个组件,每各组件包含:

  • 功能(JS 代码)
  • 内容(模板代码)
  • 样式(CSS 代码)

由于没有构建工具的支持,css 代码暂时无法放到组件中

组件开发

创建组件

组件是根据一个普通的配置对象创建的,所以要开发组件,只需要写一个配置对象即可

该配置对象和 vue 实例是几乎一样

var myComp = {
    data() {
        return {
            // ...
        }
    },
    computed: {
        //...
    },
    methods: {
        //...
    },
    template: `......`
}

值得注意的是,组件配置对象和 vue 实例有以下几点差异:

  • 无 el
  • data 必须是一个函数,该函数返回的对象最为数据
  • 由于没有 el 配置,组建的虚拟 DOM 树必须定义子啊 template 或 render 中

注册组件

注册组件分为两种方式,一种是全局注册,一种是局部注册

全局注册

一旦全局注册了一个组件,整个应用中任何地方都可以只用该组件

全局注册的方式是:

// 参数1:组件名称。将来在模板中使用组件时,会使用该名称
// 参数2:组件配置对象
// 该代码运行后,即可在模板中使用组件
Vue.component('MyComp', myComp);

在模板中,可以只用组件了

<myComp />
<!-- 或 -->
<myComp></myComp>

但在一些工程化的大型项目中,很多组件都不需要全局使用

比如一个登陆组件,只有在登陆相关的页面中使用,如果全局注册,将导致构建工具无法优化打包

因此,除非组件特别通用,否则不建议使用全局注册

局部注册

局部注册就是哪里要用到组件,就在哪里注册

局部注册的方式是,在要使用的组件的组件或实例中加入一个配置:

var otherComp = {
  components: {
    // 属性名为组件名称,模板中将使用该名称
    // 属性值为组件配置对象,两个名字相同时可简写
    myComp: myComp,
  },
  template: `<myComp></myComp>`,
};

应用组件

在模板中使用组件特别简单,把组件当作 HTML 元素名使用即可

但要注意以下几点:

  1. 组件必须有结束

    组件可以自结束,也可以用结束标记结束,但必须要有结束

  2. 组件的命名

    无论你是用哪种方式注册组件,组建的明明需要遵循规范

    组件可以使用 [^kebab-case 短横线命名法] ,也可以使用 [^PascalCase 大驼峰命名法]

    以下两种命名法都是可以的

    var otherComp = {
      components: {
        'my-comp': myComp, // 短横线命名法
        MyComp: myComp, // 大驼峰命名法
      },
    };
    

    实际上使用小驼峰命名法也是可以识别的,只不过不符合官方要求的规范

    使用大驼峰命名法,在模板中可以使用大驼峰命名法和短横线命名法两种名称

因此,在组建使用时,常采用如下方式:

var MyComp = {
  // 组件配置
};
var OtherComp = {
  Components: {
    MyComp, // ES6速写属性
  },
};

组件树

一个组件创建好后,往往会在各种地方使用它。它可能多次出现在 vue 实例中,也可能出现在其他组件中。

于是就形成了一个组件树

向组件传递数据

大部分组件要完成自身的功能,都需要一些额外的信息

比如一个头像组件,需要告诉它头像的地址,这就需要在使用组件时向组件传递数据

传递数据的方法有很多种,主唱兼的一种是使用组件属性 component

var MyComp = {
  props: ['p1', 'p2', 'p3'],
  // 和vue实例一样,使用组件时,也会创建组建的实例
  // 而组件的属性会被提取到组件实例中,因此可以在模板中使用
  template: `<div>{{p1}},{{p2}},{{p3}}</div>`,
};

var OtherComp = {
  components: {
    MyComp,
  },
  data() {
    return {
      a: 1,
    };
  },
  template: `<my-comp :p1="a" p2="2" p3="3" />`,
};

注意:在组件中,属性是只读的,绝对不可以更改,这叫做单向数据流。

vue-cli 的使用

vue-cli 是一个脚手架工具,它集成了诸多前端技术,包括但不仅限于:

  • webpack
  • babel
  • eslint
  • http-proxy-middleware
  • typescript
  • css pre-prosessor
  • css module
  • ……

这些工具,他们大部分都要依赖两个东西:

  • node 环境:很多工具的运行环境
  • npm:包管理器,用于下载各种第三方库

目前,npm 已和 node 继集成,当安装 node 后,会自动安装 npm

安装 node

下载 node:https://nodejs.org/zh-cn

验证安装

打开终端,查看 node 和 npm 版本,验证是否安装成功:

node -v
npm -v

如果安装之前打开终端,需要在安装后重新打开

配置源地址

默认情况下,npm 安装包会从国外的地址下载,速度很慢,容易导致安装失败,因此需要先配置 npm 的源地址

使用下面的命令更改 npm 的源地址为淘宝源

npm config set registry http://registry.npm.taobao.org/

更改好之后,查看源地址是否正确修改

npm config get registry

安装 vue-cli

使用下面的命令安装 vue-cli 工具

npm install -g @vue/cli

安装好之后,检查 vue-cli 是否安装成功

vue --version

使用 vue-cli

在终端进入某个目录

选择一个目录,该目录将放置你的工程文件夹

在终端中进入该目录

搭建工程

使用 vue-cli 提供的命令搭建工程

vue create 工程名

注意工程名只能出现英文、数字和下划线

启动开发环境

cd bilibili-app
npm run serve

打包应用

cd bilibili-app
npm run build