核心框架:spring boot、vue.js

一、spring boot

1.1 Spring Boot 概述

现在软件市场已经形成一定的规模,系统架构的复杂度也越来越高(例如有单体架构,分布式架构,微服务架构)。软件的整个架构体系正在发生很大变化,在这种变化中,企业现在更注重技术的开箱即用,更注重技术在生态圈中的深度融合,更注重轻量级的运维。由此spring boot诞生。

说明:学技术一定要了解技术发展史,并对技术的发展有一定的前瞻性。

1.2 Spring Boot 核心特性

Spring boot是一个脚手架,构建于Spring框架(Framework)基础之上,基于快速构建理念,提供了自动配置功能,可实现其开箱即用特性(创建完一个基本的项目以后,可零配置或者少量配置即可运行我们的项目),其核心主要有如下几个方面:

  • 起步依赖(Starter Dependency)-项目创建时底层帮你关联依赖。
  • 自动配置(Auto Configuration)。
  • 健康检查(Actator)-监控。

其中,Spring Boot官方地址为:https://spring.io/projects/spring-boot。

1.3 Spring Boot 环境配置

1.3.1 准备工作

  • 工具下载
    1)下载JDK1.8,并进行环境变量配置(假如已有则无需再次下载和配置)。
    2)下载最新maven(例如apache-maven-3.6.3,网址:http://maven.apache.org/
    3)下载sts最新版(例如sts-4.4.5.RELEASE,网址https://spring.io/tools并解压(新版本下载下来时一个.jar文件,需要在jar文件所在的目录中,以命令行方式执行java -jar 下载的文件名的方式进行解压,如图-1所示(需要首先启动系统自带的命令行客户端,然后切换到软件所在目录,cd指令为切换目录的意思)。

在这里插入图片描述 图-1

  • 工作区准备:定义新的工作区(要求没有中文目录),例如d:/TCGBIII/WORKSPACE。

    建议:将下载的所有文件存储到同一目录并解压,例如d:/TCGBIII/TOOLS目录。要求目录不要有中文,也不要有特殊符号(例如&等)。

1.3.2 Maven基本配置

打开maven中的setting.xml文件,并对其如下选项进行配置。

  • 配置maven本地库(从maven远程服务器下载的资源存储到的位置)

        <localRepository>${user.home}/.m5/repository</localRepository>
    
  • 配置maven私服(配置到mirrors标签内部)。

     <id>aliyun</id>
    
     <name>aliyun for maven</name>
    
     <mirrorOf>*</mirrorOf>
    
     <url>https://maven.aliyun.com/repository/public</url>
    
  • 配置maven中的profile(配置到profiles标签内部),设置JDK编译和运行版本。

<profile>

   <id>jdk-1.8</id>

   <activation>

        <activeByDefault>true</activeByDefault>

      <jdk>1.8</jdk>

   </activation>

   <properties>

           <maven.compiler.source>1.8</maven.compiler.source>

           <maven.compiler.target>1.8</maven.compiler.target>

           <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>

         </properties>

 </profile>

1.3.3 STS整合maven配置

  • 启动STS工具(特殊的eclipse,添加了spring开发插件),如图-2所示
    在这里插入图片描述
    图-2

  • Maven Installations配置, 如图-3所示:
    在这里插入图片描述
    图-3

  • Maven User Settings配置,如图-4所示:
    在这里插入图片描述
    图-4

  • 项目工作区编码设置,如图-5所示
    在这里插入图片描述
    图-5

1.3.4 STS工具使用基本优化(可选)

  • 禁用拼写检查,取消图-6所示的拼写检查选项。
    在这里插入图片描述
    图-6
  • 取消图-7所示的连接选项配置。
    在这里插入图片描述
    图-7
  • 关掉图-8的校验功能。
    在这里插入图片描述

图-8

  • 修改STS工具内存配置,打开SpringToolSuite4.ini文件,修改堆大小,如图-9所示:
    在这里插入图片描述
    图-9

1.3.5 Spring Boot 快速入门

1.3.5.1 项目创建及结构分析

打开STS 集成开发工具,创建spring boot项目,其具体步骤如下:

第一步:打开项目新建窗口(快捷键ctrl+n), 搜索spring,选择spring starter 项目。如图-10所示:
在这里插入图片描述
图-10
第二步:填写项目基本信息。如图-11所示:
在这里插入图片描述
图-11
第三步:选择Spring Boot版本,如图-12所示:
在这里插入图片描述
图-12
在图-12中点击finish之后,项目便开始从maven配置中指定的私服服务器(例如阿里云的maven服务器)去下载起步依赖(SpringBoot项目中默认指定了项目启动时需要的一些jar包依赖),这个过程可能会比较耗时(网络最好不要用手机网络,会导致很多资源下载不下来),假如指定版本的springboot项目以前创建过,它会默认先从本地库查找,假如本地库没有,则从远程库去下载。

项目创建成功以后,其包视图(Package Explorer)结构,如图-13所示:
在这里插入图片描述
图-13

1.3.5.2 项目启动过程分析(了解)

找到项目的入口类(使用了@SpringBootApplication注解描述),然后运行启动类,检测启动过程,SpringBoot在启动时,控制台会出现如图-14标识:
在这里插入图片描述
图-14
项目启动时发生了什么呢?我在这里画了张图帮大家进行一下分析,如图-15所示:
在这里插入图片描述
图-15
SpringBoot 项目在启动时,首先基于启动入口类上的注解描述,进行自动配置并扫描指定包以及子包中的类进行加载,然后检测类上是否有Spring框架中指定的注解描述(例如@Component,@Controller,@Service等)。假如有,则将类交给Spring框架中的BeanFactory工厂接口的实现类对象,此工厂对象会基于反射创建Bean的实例,假如此Bean指定了生命周期方法,还会调用生命周期方法。当实例创建以后,Spring框架还会基于类的作用域描述,将实例存储到不同作用域的容器中。以实现Bean对象的科学应用。

1.3.5.3 项目业务初步实现及测试

业务实现:

基于SpringBoot脚手架(或者架子工),通过Spring框架进行Bean对象的管理实现。

第一步:创建一个DefaultCache类,存储到src/main/java目录,然后交给spring管理。

package com.cy.pj.common.cache;

@Component     
public class DefaultCache {}

其中,@Component是Spring中用于描述Bean类的一个注解。用于告诉Spring这个类的实例由Spring创建,当此对象由Spring创建和管理时,默认会将对象存储到池(Bean池)中。

第二步:添加sringboot 测试类,进行bean的获取及测试,要放在src/test/java目录中:

package com.cy.pj.common.cache;

@SpringBootTest
public class DefaultCacheTests {

        @Autowired
        private DefaultCache defaultCache;

        @Test
        public void testCache() {

                System.out.println(defaultCache);

        }

}


其中:

  • @SpringBootTest 注解用于告诉spring框架,此测试类交给spring管理。
  • @Autowired注解描述属性时,用于告诉spring框架要为此属性注入一个值?(至于注入规则,后面课程慢慢加强)

第三步:代码设计及运行分析,如图-16所示:
在这里插入图片描述
图-16
在图-16中描述了DefaultCacheTests类与DefaultCache类的关系,这两个类通过指定注解(@SpringBootTest,@Component)进行了描述,其意图是告诉spring框架这个两个类的实例的创建由Spring负责,并且由Spring框架基于@Autowired注解的描述完成DefaultCacheTests实例中有关DefaultCache类型的值的注入(DI)。

第四步:为对象设计作用域,设置延迟加载,设置生命周期方法(了解)。

在Spring框架中,Spring为由它创建和管理的对象,设计了一些特性,例如作用域,延迟加载,生命周期方法等,基于这些特性实现对Bean对象的管理。

package com.cy.pj.common.pool;

@Component       
@Scope("singleton")
@Lazy
public class ObjectPool {

    public ObjectPool() {

           System.out.println("ObjectPool()");

        }

    @PostConstruct
    public void init() {

           System.out.println("init()");

        }

    @PreDestroy
    public void destory() {

           System.out.println("destory");

        }
}

其中:

@Lazy注解用于描述类,其目的是告诉spring框架此类支持延迟加载,通常会配合单例作用域使用。
@Scope 是Spring中用于定义Bean对象作用域的一个注解,其常用的值有
singleton(整个内存有一份Bean实例,此实例何时创建与类的延迟加载特性配置有关,此实例创建以后,生命周期会由spring框架管理),prototype(每次获取都会创建新实例,此实例会在需要时创建与lazy特性无关,这个实例创建以后,不会交给spring管理,spring可以对其初始化,但不负责销毁。)等。

@PostConstruct 注解用于描述bean对象生命周期方法中的初始化方法,此方法会在对象的构造方法之后执行(是对象创建以后的初始化)。
@PreDestroy 注解用于描述Bean对象生命周期方法中的销毁方法,此方法会在对象销毁之前执行(当作用域为prototype时,此方法不会执行)。

第五步:通过测试类测试作用域,延迟加载,生命周期方法。

package com.cy.pj.common.cache;

@SpringBootTest
public class ObjectPoolTests {

        @Autowired
        private ObjectPool objectPool;

        @Test
        public void testObjectPool() {
                
        }
}

通过对上面小节的学习,能说说Spring框架管理Bean对象有什么优势吗?(面试)

Spring 是一个资源整合框架(Framework),通过spring可将很多资源(自己写的对象或第三方提供的对象,例如连接池等)整合在一起,然后进行科学应用,以便更好的对外提供服务。如图-17所示:
在这里插入图片描述
图-17

在图-17中,Spring框架可以为由它管理的对象(Bean)提供懒加载策略(对象暂时用不到,则无需加载和实例化),作用域(例如singleton-频繁用时可以考虑内存中只有一份,prototype-使用次数少时可以用时创建,不用时销毁),生命周期方法(更好实现对象的初始化和资源销毁),以实现对象对系统资源的有效使用。同时Spring框架还可以基于用户设计管理对象与对象的依赖关系,以降低对象与对象之间的直接耦合,提高程序的可维护性和可扩展性。

1.3.5.4 项目业务增强实现及测试

第一步:定义业务Cache接口.

package com.cy.pj.common.cache;

public interface Cache {


}

第二步:定义WeakCache实现Cache接口.

package com.cy.pj.common.cache;


import org.springframework.stereotype.Component;


@Component
public class WeakCache implements Cache{


}

第三步:创建或修改SoftCache,让此类也实现Cache接口,关键代码如下:

package com.cy.pj.common.cache;

@Component
public class SoftCache implements Cache{}

第四步:单元测试类

package com.cy.pj.common.cache;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class CacheTests {

        @Autowired
        @Qualifier("softCache")
        private Cache cache;

        @Test
        public void testCache() {
                System.out.println(cache);
        }
}

业务测试运行原理分析,如图-18所示:

图-18

思考:
@Autowired注解应用规则?
@Qualifier注解的作用是什么?

@Autowired由spring框架定义,用于描述类中属性或相关方法(例如构造方法)。Spring框架在项目运行时假如发现由他管理的Bean对象中有使用@Autowired注解描述的属性或方法,可以按照指定规则为属性赋值(DI)。其基本规则是:首先要检测容器中是否有与属性或方法参数类型相匹配的对象,假如有并且只有一个则直接注入。其次,假如检测到有多个,还会按照@Autowired描述的属性或方法参数名查找是否有名字匹配的对象,有则直接注入,没有则抛出异常。最后,假如我们有明确要求,必须要注入类型为指定类型,名字为指定名字的对象还可以使用@Qualifier注解对其属性或参数进行描述(此注解必须配合@Autowired注解使用)。具体过程可参考图-18的设计进行自行尝试和实践。

二、vue.js

开发模式对比

  • MVC模式(JavaEE中的SSH框架,ASP.NET中的ASP.NET MVC框架)
  • MVP模式(ASP.NET中的Web Forms)
  • MVVM模式(Vue.js,WPF,AngularJS)

2.1 安装

1.vue.js

  • 下载js文件,

2.vue-devtools

注:由于使用了ES5,故vue.js不支持IE8及其以下版本
说明:vus.js提供了一个数据的双向绑定功能,当动态更新message中的值时,并不需要刷新网页或更新节点,此节点的值会随着JavaScript代码的变动而改变

2.2 Vue.js

2.2.1 Vue.js主要特性

1)组件

  • 组件是HTML元素的扩展,可自定义其数据与行为。例如通过一个bind操作将JS中的内容绑定在div标签内部:
<body>
    <div id="app">
        <p> {{ message }} </p>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data:{
                message: 'Hello Vue.js!'
            }
        })
    </script>
</body>
</html>
  • 注册全局组件:
<div id="app">
        <my-component></my-component>
    </div>
    <script>
        Vue.component('my-component',{
            template: '<div>Hello Vue!</div>'
        })
        new Vue({
            el: '#app'
        })
    </script>

2)模板
vue.js使用基于HTML的模板语法,允许开发者将DOM元素与底层vue.js实例中的数据相绑定。

3)响应式设计
响应式网络设计是为了给移动设备提供更好的体验,整合从桌面到手机的各种屏幕尺寸和分辨率,使网页适应不同分辨率的屏幕。响应式界面的四个层次:

  • 同一页面在不同大小和比例上看起来都应该是舒适的;
  • 同一页面在不同分辨率上看起来都应该是合理的;
  • 同一页面在不同操作方式(如鼠标和触屏)下,体验应该是统一的;
  • 同一页面在不同类型的设备(手机、平板、电脑)上,交互方式应该是符合习惯的。

4)过渡效果
vue.js的过渡效果可以让我们的页面元素在出现和消失时实现过渡。vue.js在插入、更新或移除DOM时,提供了多种方式的应用过渡效果,包括:

  • 在CSS过渡和动画中自动应用CSS
  • 配合使用第三方CSS动画库,如Animate.css
  • 在过渡钩子函数中使用JavaScript之间操作DOM
  • 配合使用第三方JavaScript动画库,如Velocity.js

vue.js提供了transition的封装组件,在下列情形中可以给任何元素和组件添加entering/leaving过渡:

  • 条件渲染(使用v-if)
  • 条件展示(使用v-show)
  • 动态组件
  • 组件根节点

简单而言就是,你可以使用vue的组件,结合css的动画(animation),过渡(transition),或者javascript操作DOM来让你的元素或者组件动起来。而在引用css和javascript中,你可以自己写,也可以利用现成的css或者javascript的第三方库。

页面交互举例,当单击按钮后,hello Vue会渐变地消失,再次单击后又会渐变地显示:

<style>
    .fade-enter-active, .fade-leave-active {
        transition: opacity .5s
    }
    .fade-enter, .fade-leave-to /* .fade-leave-active in below version 2.1.8 */ {
        opacity: 0
    }
    </style>
    <div id="app">
        <button v-on:click="show = !show">
            click
        </button>
        <transition name="fade">
            <p v-if="show">hello Vue</p>
        </transition>
    </div>
    <script>
        // 创建根实例
        new Vue({
            el: '#app',
            data: {
                show: true
            }
        })
    </script>

5)单文件组件

为了更好适应复杂的项目,vue.js支持以.vue为扩展名的文件来定义一个完整组件,用以替代使用Vue.component注册组件的方式。

2.2.2 Vue.js实例

1)构造器
对于Vue.js项目来说,每个应用的起步都需要使用Vue.js的构造函数创建一个根实例,如下:

// 建立Vue实例
var vm = new Vue({
    // 选项
})

2)实例的属性和方法

每个Vue.js实例在被创建之前都要经过一系列的初始化过程,在初始化过程中加入一些data属性,即表示此实例的一些响应事件或数据属性等,如:


var data = { a:1 }
var vm = new Vue({
    el: "#example",
    data: data
})
 
vm.$data === data;    // true
 
vm.$watch('a', function(newValue, oldValue){
    // 这个回调将在 vm.a 改变后调用
})

3)生命周期、钩子函数

钩子函数主要对于某个实例事件发生后需要响应已经预设好的代码。钩子的this指向调用它的Vue.js实例

2.2.3 Vue.js路由

通过对不同路由路径的定义,可以将Vue.js中可供访问的路径标明。一般采用一个路径对应一个功能的形式来定义页面

1)安装vue-router(提供了Vue.js的路由控制和管理)

  • 直接引入CDN
<script src="https://cdn.bootcss.com/vue-router/3.0.6/vue-router.common.js"></script>
  • npm安装
npm install vue-router

2)动态路由匹配
动态路由匹配经常需要把某种模式匹配到的所有路由全都映射到同一个组件上。例如,所有ID不同的用户都要使用User组件来渲染:

export default new Router({
    routes:[
    {
        path: '/user/:id',
        component: User
    }
    ]
})

一个路径的定义参数使用冒号进行连接,当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件中使用

3)嵌套路由

export default new Router({
    routes:[
    {
        path: '/user/:id',
        component: User,
        children: [
            {
             path: 'vip',
             component: VIP
            }
        ]
    }
    ]
})

  • 访问 /user/xxx/ 时,页面不会渲染任何东西,因为没有匹配到合适的子路由
  • 访问 /user/xx/vip 时,则会正常渲染

4)编程式导航

1<router-link to="...">

支持用户在具有路由功能的应用中(单击)导航,通过to属性指定目标地址,默认渲染成标签

2、router.push(location, onComplete?, onAbort?)

这个方法会向history栈添加一个新的记录,当点击“后退”按钮时,则回到之前的URL

router.push('home')    // 参数:字符串路径
router.push({ path: 'home' })    // 参数:对象
router.push({ name: 'user', params: { userId: 123 }})    // 参数:命名的路由
router.push({ path: 'register', query: { plan: 'private'}})    // 带查询参数:/register?plan=private
router.push({ path: 'user', params: { userId } })    // 如果提供了path参数,params传递的参数会被忽略:/user
3、 router.replace(location, onComplete?, onAbort?)

此方法和router.push()很像,唯一不同的是,它不会向history添加新纪录,而是替换掉当前的history记录

<router-link to="..." replace> 
4、 router.go(n)

n是一个整数,表示在history记录中向前或后退多少步,类似window.history.go(n)

router.go(1)    // 在浏览器记录中前进一步,等同于 history.forward()
router.go(-1)   // 在浏览器记录中后退一步,等同于 history.back()
router.go(100)   // 如果记录不够用,则自动失效

5)命名视图
一个工程可能需要同时(同级)展示多个视图,页面不复杂时,可以用视图的别名

<router-view class="view one"></router-view>    // 未指定名称,为默认组件
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

6)重定向和别名

  • 重定向
const router = new VueRouter({
    routes: [
        { path: "/a", redirect: "/b" }            
    ]
})
 
const router = new VueRouter({
    routes: [
        { path: "/a", redirect: { name: 'foo' } }            
    ]
})
 
// 动态返回重定向目标
const router = new VueRouter({
    routes: [
        { path: "/a", redirect: to => {
            // return 重定向的字符串路径/路径对象
        }}            
    ]
})
  • 别名

例如, /a的别名是/b,意味着用户访问/b时,URL会保持会/b,但路由匹配则为/a:

const router = new VueRouter({
    routes: [
        { path: "/a", component: A, alias: "/b" }            
    ]
})

7)路由组件传递参数

// UserProps.vue
<template>
  <div>    Hello, {{ id }}    </div>
</template>
 
<script>
  export default {
    // props传递数据
    props: ['id']
  }
</script>
 
//index.js
{
    path: '/UserProps/:id',
    component: UserProps,
    props: true
}

8)history模式

const router = new VueRouter({
    mode: history,
    routes: [...]
})

2.2.4.Vue.js模板

1)文本

// 无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。
<span>Message: {{ msg }}</span>
// 一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定
<span v-once>这个将不会改变: {{ msg }}</span> 

2)HTML

// 为了输出真正的 HTML,你需要使用 v-html 指令:
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

3) JavaScript 表达式

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
// 这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
 
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}

模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。

4)指令
指令 (Directives) 是带有 v- 前缀的特殊特性。

<p v-if="seen">现在你看到我了</p>
// 这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素。

5)参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。

<a v-bind:href="url">...</a>
// 在这里 href 是参数,告知 v-bind 指令将该元素的 href 特性与表达式 url 的值绑定。
 
<a v-on:click="doSomething">...</a>
// 在这里参数是监听的事件名。

2.2.5 条件渲染

1)v-if, v-else-if, v-else

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

用 key 管理可复用的元素

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

2) v-show

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

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。

注意:v-show 不支持 元素,也不支持 v-else。

3)v-if vs v-show

v-ifv-show
“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只是简单地切换元素的 CSS 属性 display
惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
更高的切换开销,如果在运行时条件很少改变,则使用 v-if 较好。更高的初始渲染开销,如果需要非常频繁地切换,则使用 v-show 较好;

2.2.6列表渲染

1)用 v-for 把一个数组对应为一组元素

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
// 在 v-for 块中,我们可以访问所有父作用域的属性。v-for 还支持一个可选的第二个参数,即当前项的索引。
<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
  • 可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>

2) 用 v-for 来遍历一个对象的属性

<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>
new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

3)在 v-for 里使用值范围

<div>
  <span v-for="n in 10">{{ n }} </span>
</div>

4)v-for 与 v-if 一同使用

当 v-for 与 v-if一起使用时,v-for 具有比 v-if 更高的优先级。

注意:不推荐在同一元素上使用 v-if 和 v-for

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 )上。如:

<ul v-if="todos.length">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>
<p v-else>No todos left!</p>

5)在组件上使用 v-for
2.2.0+ 的版本里,当在组件上使用 v-for 时,key 现在是必须的。

<my-component
  v-for="(item, index) in items"
  v-bind:item="item"
  v-bind:index="index"
  v-bind:key="item.id"
></my-component>

转载:Vue.js快速入门+项目实战(源码)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值