Maven

Maven是一个项目管理工具,可以对Java项目进行自动化的构建和依赖管理
SpringBoot + Vue基本知识点荟萃

Maven的作用

SpringBoot + Vue基本知识点荟萃
运行 Maven 的时候,Maven 所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库。
SpringBoot + Vue基本知识点荟萃

本地仓库配置

修改maven安装包中的conf/settings.xml文件,指定本地仓库位置。
SpringBoot + Vue基本知识点荟萃

远程仓库配置

maven默认连接的远程仓库位置并不在国内,因此有时候下载速度非常慢,我们可以配置一个国内站点镜像,可用于加速下载资源。
SpringBoot + Vue基本知识点荟萃

与IDEA集成

SpringBoot + Vue基本知识点荟萃

SpringBoot快速上手

SpringBoot介绍

SpringBoot特点

快速创建SpringBoot应用

SpringBoot + Vue基本知识点荟萃
SpringBoot + Vue基本知识点荟萃
SpringBoot + Vue基本知识点荟萃

开发环境热部署

步骤

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

Optional

Finally

SpringBoot + Vue基本知识点荟萃

系统配置

Web开发基础

Web入门

SpringBoot + Vue基本知识点荟萃

控制器

SpringBoot + Vue基本知识点荟萃

@Controller的用法

SpringBoot + Vue基本知识点荟萃

@RestController的用法

SpringBoot + Vue基本知识点荟萃

@RestController和@Controller的区别

@RestController注解等价于@ResponseBody + @Controller。@RestController和@Controller的共同点是都用来表示Spring某个类是否可以接收HTTP请求,二者区别: @RestController无法返回指定页面,而@Controller可以;前者可以直接返回数据,后者需要@ResponseBody辅助。
① 是否可以返回页面
答:@RestController无法返回指定页面,而@Controller可以。
解析:对于Controller, 如果只是使用@RestController注解,则其方法无法返回指定页面,此时配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是 return 里的内容。 如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
② 返回内容
如果需要返回JSON,XML或自定义mediaType内容到页面,@RestController自己就可以搞定,这个注解对于返回数据比较方便,因为它会自动将对象实体转换为JSON格式。而@Controller需要在对应的方法加上@ResponseBody注解。

import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/difference")
public class DifferenceController {
    // 跳转到上传文件的页面
    @RequestMapping(value = "/goToSuccessPage", method = RequestMethod.GET)
    public String goToSuccessPage() {
        // 跳转到 视图层 success.html失败
        return "success";
    }
    @RequestMapping(value = "findAll", method = RequestMethod.GET)
    public Map<String, String> findAll() {
        Map<String, String> all = new HashMap<>();
        all.put("remark", "可以返回json,xml或自定义mediaType内容到页面");
        return all;
    }
}
@Controller
@RequestMapping("/login")
public class LoginController {
    @GetMapping(value = "/login")
    public String login() {
        // 跳转到 视图层 login.html
        return "login";
    }
    @RequestMapping(value = "/getJson", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, String> getJson() {
        Map<String, String> all = new HashMap<>();
        all.put("remark", "结合注解 @ResponseBody 返回接送");
        return all;
    }
}

路由映射

SpringBoot + Vue基本知识点荟萃
RequestMapping分别和GetMapping、PostMapping的区别
GetMapping和PostMapping可以用RequestMapping表示,例如

  1. 以get的方式请求

@RequestMapping(value=“/login”, method=RequestMethod.GET)等价于@GetMapping(“/login”)

  1. 以post的方式请求

@RequestMapping(value=“/login”, method=RequestMethod.POST)等价于@PostMapping(“/login”)

参数传递

Web开发进阶

静态资源访问

文件上传

原理

SpringBoot + Vue基本知识点荟萃

SpirngBoot实现文件上传功能

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MBA
package com.example.helloworld.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
@RestController
public class FileController {
    @PostMapping("/upload")
    public String upload(String nickname, MultipartFile f, HttpServletRequest request) throws IOException {
        System.out.println(nickname);
        System.out.println("文件大小:"+f.getSize());
        System.out.println(f.getContentType());
        System.out.println(f.getOriginalFilename());
        //ServletContext().getRealPath() 是从当前servlet 在tomcat 中的存放文件夹开始计算起的
        String path = request.getServletContext().getRealPath("/upload/");
        System.out.println(path);
        System.out.println(f);
        saveFile(f, path);
        return "success";
    }
    public void saveFile(MultipartFile f, String path) throws IOException{
        File upDir = new File(path);
        if(!upDir.exists()){
            upDir.mkdir();
        }
        File file = new File(path+f.getOriginalFilename());
        f.transferTo(file);
    }
}

拦截器

SpringBoot定义了HandlerInterceptor接口来实现自定义拦截功能,此接口定义了preHandle, postHandle, afterCompletion三个方法,通过重写这三个方法可以实现请求前、请求后等操作。
SpringBoot + Vue基本知识点荟萃

拦截器的定义

public class LoginInterceptor implement HandlerInterceptor{
    /**
     *在请求处理之前进行调用(Controller方法调用之前)
     */
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             object handler) throws Exception{
        if(条件){
            System.out.println("通过");
            return true;
        } else{
            System.out.println("不通过");
            return false;
        }
    }
}

拦截器注册

@Configuration
public class WebConfigurer implements WebMvcConfigurer{
    @override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/user/**")
    }
}

构建RESTful服务

RESTful介绍

RESTful的特点

RESTful API

HTTP Method

SpringBoot + Vue基本知识点荟萃

HTTP状态码

RESTful API中使用HTTP状态码来表示请求执行结果的状态,适用于REST API设计的代码以及对应的HTTP方法。
SpringBoot + Vue基本知识点荟萃

构建RESTful应用接口

Spring Boot实现RESTful API

在RESTful架构中,每个网址代表一种资源,所以URI中建议不要包含动词,只包含名词即可,而且所用的名词往往与数据库的表格名对应。
用户管理模块API示例:
SpringBoot + Vue基本知识点荟萃

package com.example.helloworld.controller;
import com.example.helloworld.entity.User;
import org.springframework.web.bind.annotation.*;
@RestController
    public class UserController {
        @GetMapping("/user/{id}")
        public String getUserById(@PathVariable int id) {
            return "根据ID获取用户";
        }
        @PostMapping("/user")
        public String save(User user) {
            return "添加用户";
        }
        @PutMapping("/user")
        public String update(User user) {
            return "更新用户";
        }
        @DeleteMapping("/user/{ud}")
        public String delete(@PathVariable int id){
            return "根据ID删除用户";
        }
    }

使用Swagger生成Web API文档

什么是Swagger

SpringBoot + Vue基本知识点荟萃

使用Swagger生成Web API文档

在Spring Boot项目中集成Swagger同样非常简单,只需在项目中引入springfox-swagger2和springfox-swagger-ui依赖即可。
SpringBoot + Vue基本知识点荟萃

配置Swagger

SpringBoot + Vue基本知识点荟萃
注意事项
Spring Boot 2.6.X后与Swagger有版本冲突问题,需要在application.properties中加入以下配置:
SpringBoot + Vue基本知识点荟萃

使用 Swagger2 进行接口测试

启动项目访问 http://127.0.0.1:8080/swagger-ui.html ,即可打开自动生成的可视化测试页面
SpringBoot + Vue基本知识点荟萃

Swagger常用注解

Swagger提供了一系列注解来描述接口信息,包括接口说明、请求方法、请求参数、返回信息等
SpringBoot + Vue基本知识点荟萃

MybatisPlus快速上手

ORM介绍

SpringBoot + Vue基本知识点荟萃

MyBatis-Plus介绍

<!--Mybatis Plus 依赖-->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.4.2</version>
</dependency>
<!--MySQL 驱动依赖-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.47</version>
</dependency>
<!--数据连接池druid-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.1.20</version>
</dependency>
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false
spring.datasource.druid.username=root
spring.datasource.druid.password=root
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
@SpringBootApplication
@MapperScan("com.demo.mapper")
public class MybatisPlusDemo Application{
    public static void main(String [] args){
        SpringApplication.run(MybatisPlusApplication.class, args);
    }
}

MyBatis-Plus CRUD操作

SpringBoot + Vue基本知识点荟萃

//package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
    public interface UserMapper {
        @Select("select * from user")
        public List<User> findAll();
        @Insert("insert into user values(#{id}, #{name}, #{age})")
        public int add(User user);
        @Update("update user set id=#{id}, name=#{name}, age=#{age}")
        public int update(User user);
        @Delete("delete from user where id=#{id}")
        public int delete(int id);
        @Select("select * from user where id=#{id}")
        public User findById(int id);
    }
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;
    @GetMapping("/user")
    public List<User> findAll() {
        return userMapper.findAll();
    }
    @PostMapping("/saveUser")
    public String save(User user){
        int flag = userMapper.add(user);
        if (flag > 0)
            return "插入成功";
        else
            return "插入失败";
    }
}

Spring的三种注入方式:

构造注入,getter和setter注入,自动注入

package com.example.demo.entity;
public class User {
    private int id;
    private String name;
    private int age;
    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

数据库字段和实体类字段映射规则
SpringBoot + Vue基本知识点荟萃

MybatisPlus注解

SpringBoot + Vue基本知识点荟萃

package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private int id;
    private String name;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

多表查询及分页查询

多表查询

实现复杂关系映射,可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置。
SpringBoot + Vue基本知识点荟萃

分页查询

编写配置文件
SpringBoot + Vue基本知识点荟萃

package com.example.demo.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor paginationInterceptor(){
        MybatisPlusInterceptor intercepter = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        intercepter.addInnerInterceptor(paginationInnerInterceptor);
        return intercepter;
    }
}

测试
SpringBoot + Vue基本知识点荟萃

package com.example.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
public class UserController {
    @Resource
    private UserMapper userMapper;
    @GetMapping("/user")
    public List<User> findAll() {
        return userMapper.findAll();
    }
    @PostMapping("/saveUser")
    public String save(User user) {
        int flag = userMapper.add(user);
        if (flag > 0)
            return "插入成功";
        else
            return "插入失败";
    }
    @GetMapping("/user-orders")
    public List<User> getUserAllOrders() {
        return userMapper.selectAllUserAndOrders();
    }
    //    条件查询
    @GetMapping("/user/find")
    public List<User> findByCond() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "zhangsan");
        return userMapper.selectList(queryWrapper);
    }
    //    分页查询
    @GetMapping("/user/findByPage")
    public IPage findByPage() {
        // 设置起始值及每页条数
        Page<User> page = new Page<>(0, 2);
        IPage ipage = userMapper.selectPage(page, null);
        return ipage;
    }
}
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
@TableName("orders")
public class Order {
    private int oid;
    private Date date;
    private int cid;
    @TableField(exist = false)
    private User user;
    public int getOid() {
        return oid;
    }
    public void setOid(int oid) {
        this.oid = oid;
    }
    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }
    public int getCid() {
        return cid;
    }
    public void setCid(int cid) {
        this.cid = cid;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    @Override
    public String toString() {
        return "Order{" +
                "oid=" + oid +
                ", date=" + date +
                ", cid=" + cid +
                '}';
    }
}
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.List;
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private int id;
    private String name;
    private int age;
    //告诉Mybatis不做映射
    @TableField(exist = false)
    private List<Order> orders;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public List<Order> getOrders() {
        return orders;
    }
    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.Order;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
    @Select("select * from orders where cid = #{cid}")
    public List<Order> selectByUid(int id);
    //    查询所有的订单,同时查询订单的用户
    @Select("select * from order")
    @Results({
            @Result(column = "oid", property = "oid"),
            @Result(column = "date", property = "date"),
            @Result(column = "uid", property = "user", javaType = User.class,
                    one = @One(select = "com.example.demo.mapper.UserMapper.selectById"))
    })
    public List<Order> selectAllOrdersAndUser();
}
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("select * from user")
    public List<User> findAll();
    @Insert("insert into user values(#{id}, #{name}, #{age})")
    public int add(User user);
    @Update("update user set id=#{id}, name=#{name}, age=#{age}")
    public int update(User user);
    @Delete("delete from user where id=#{id}")
    public int delete(int id);
    @Select("select * from user where id=#{id}")
    public User findById(int id);
    //    查询用户所有订单
    @Select("select * from user")
    @Results(
            {
                    @Result(column = "id", property = "id"),
                    @Result(column = "name", property = "name"),
                    @Result(column = "age", property = "age"),
                    @Result(column = "id", property = "orders", javaType = List.class,
                            many = @Many(select = "com.example.demo.mapper.OrderMapper.selectByUid"))
            }
    )
    public List<User> selectAllUserAndOrders();
}

Vue快速上手

前端环境准备

Vue框架介绍

MVVM模式

SpringBoot + Vue基本知识点荟萃

Vue快速入门

<script src="https://unpkg.com/vue@next"></script>
<div id="app">
  {{message}}
</div>
const Hello = {
  //指定数据源,即MVVM中的Model
  data: function(){
    return {
      "message": "Hello Vue!"
    }
  }
}
const app = Vue.createApp(Hello)
app.mount('#app') //指定当前Vue实例要控制页面的哪个区域
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 1. 导入 vue 的脚本文件 -->
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <!-- 2. 声明要被 vue 所控制的 DOM 区域 -->
    <div id="app">
      {{message}}
    </div>
    <!-- 3. 创建 vue 的实例对象 -->
    <script>
      const hello = {
        // 指定数据源,既MVVM中的Model
        data: function() {
          return {
            message: 'Hello Vue!'
          }
        }
      }
      const app = Vue.createApp(hello)
      app.mount('#app')
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="app">
    <p>姓名:{{username}}</p>
    <p>性别:{{gender}}</p>
    <p>{{desc}}</p>
   <!--将html元素字符串转换成标签-->
    <p v-html="desc"></p>
  </div>
  <script>
    const vm = {
      data: function(){ 
        return {
          username: 'zhangsan',
          gender: '男',
          desc: '<a href="http://www.baidu.com">百度</a>'
        }
      }
    }
    const app = Vue.createApp(vm)
    app.mount('#app')
  </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <a :href="link">百度</a>
      <input type="text" :placeholder="inputValue">
      <img :src="imgSrc" :style="{width:w}" alt="">
    </div>
    <script>
      const vm = {
          data: function(){
            return {
              link:"http://www.baidu.com",
              // 文本框的占位符内容
              inputValue: '请输入内容',
              // 图片的 src 地址
              imgSrc: './images/demo.png',
              w:'500px'
          }
        }
      }
      const app = Vue.createApp(vm)
      app.mount('#app')
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <!-- vue 实例要控制的 DOM 区域 -->
    <div id="app">
      <p>{{number + 1}}</p>
      <p>{{ok ? 'True' : 'False'}}</p>
      <p>{{message.split('').reverse().join('')}}</p>
      <p :id="'list-' + id">xxx</p>
      <p>{{user.name}}</p>
    </div>
    <script>
      const vm = {
        data: function(){
          return {
            number: 9,
            ok: false,
            message: 'ABC',
            id: 3,
            user: {
              name: 'zs',
            }
          }
        }
      }
      const app = Vue.createApp(vm)
      app.mount('#app')
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <h3>count 的值为:{{count}}</h3>
      <button v-on:click="addCount">+1</button>
      <button @click="count+=1">+1</button>
      <!--
        <button @click="addCount">+1</button>
      -->
    </div>
    <script>
      const vm = {
        data: function(){
          return {
            count: 0,
          }
        },
        methods: {
          // 点击按钮,让 count 自增 +1
          addCount() {
            this.count += 1
          },
        },
      }
      const app = Vue.createApp(vm)
      app.mount('#app')
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <button @click="flag = !flag">Toggle Flag</button>
      <!--当状态只更改一次时,建议用v-if,当状态频繁更改时,建议用v-show-->
      <p v-if="flag">请求成功 --- 被 v-if 控制</p>
      <p v-show="flag">请求成功 --- 被 v-show 控制</p>
    </div>
    <script>
      const vm = {
        data: function(){
          return {
            flag: false,
          }
        }
      }
      const app = Vue.createApp(vm)
      app.mount('#app')
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <p v-if="num > 0.5">随机数 > 0.5</p>
      <p v-else>随机数 ≤ 0.5</p>
      <hr />
      <p v-if="type === 'A'">优秀</p>
      <p v-else-if="type === 'B'">良好</p>
      <p v-else-if="type === 'C'">一般</p>
      <p v-else></p>
      <div v-show="a"> 
        测试
      </div>
      <button @click="!a">点击</button>
    </div>
    <script>
      const vm = {
        data: function(){
          return {
            // 生成 1 以内的随机数
            num: Math.random(),
            // 类型
            type: 'A',
            a : false 
          }
        },
        methods:{
          f:function(){
            this.a = !this.a
          }
        }
      }
      const app = Vue.createApp(vm)
      app.mount('#app')
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="(user, i) in userList">索引是:{{i}},姓名是:{{user.name}}</li>
      </ul>
    </div>
    <script>
      const vm = {
        data: function(){
          return {
            userList: [
              { id: 1, name: 'zhangsan' },
              { id: 2, name: 'lisi' },
              { id: 3, name: 'wangwu' },
            ],
          }
        },
      }
      const app = Vue.createApp(vm)
      app.mount('#app')
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="app">
    <!-- 添加用户的区域 -->
    <div>
      <input type="text" v-model="name">
      <button @click="addNewUser">添加</button>
    </div>
    <!-- 用户列表区域 -->
    <ul>
      <li v-for="(user, index) in userlist" :key="user.id">
        <input type="checkbox" />
        姓名:{{user.name}}
      </li>
    </ul>
  </div>
  <script>
    const vm = {
      data: function(){
        return {
          // 用户列表
          userlist: [
            { id: 1, name: 'zhangsan' },
            { id: 2, name: 'lisi' }
          ],
          // 输入的用户名
          name: '',
          // 下一个可用的 id 值
          nextId: 3
        }
      },
      methods: {
        // 点击了添加按钮
        addNewUser() {
          this.userlist.unshift({ id: this.nextId, name: this.name })
          this.name = ''
          this.nextId++
        }
      }
    }
    const app = Vue.createApp(vm)
    app.mount('#app')
  </script>
</body>
</html>

组件化开发

NPM使用

npm简介

nodeJS安装

SpringBoot + Vue基本知识点荟萃

npm使用

SpringBoot + Vue基本知识点荟萃

Vue Cli 使用

组件化开发

SpringBoot + Vue基本知识点荟萃

组件构成

解决eslint报错

module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    'plugin:vue/essential',
    'eslint:recommended'
  ],
  parserOptions: {
    parser: '@babel/eslint-parser'
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    // 在rules中添加自定义规则
    // 关闭组件命名规则
    'vue/multi-word-component-names': 'off'
  },
  overrides: [
    {
      files: [
        '**/__tests__/*.{j,t}s?(x)',
        '**/tests/unit/**/*.spec.{j,t}s?(x)'
      ],
      env: {
        jest: true
      }
    }
  ]
}

第三方组件

组件间的传值

Demo

<template>
  <div id="app">
    <Movie
      v-for="movie in movies"
      :key="movie.id"
      :title="movie.title"
      :rating="movie.rating"
    ></Movie>
    <Hello></Hello>
  </div>
</template>
<script>
import Movie from "./components/Movie.vue";
import Hello from "./components/Hello.vue";
export default {
  name: "App",
  data: function () {
    return {
      movies: [
        { id: 1, title: "金刚狼", rating: 8.7 },
        { id: 2, title: "雷神", rating: 7.6 },
        { id: 3, title: "蜘蛛侠", rating: 8.9 },
      ],
    };
  },
  components: {
    Movie,
    Hello,
  },
};
</script>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
<template>
    <div>
        <h1>{{title}}</h1>
        <span>{{rating}}</span>
        <button @click="fun">点击收藏</button>
    </div>
</template>
<script>
export default {
    name:"Movie",
    props: ["title", "rating"],
    data: function(){
        return {
        }
    },
    methods: {
        fun(){
            alert("收藏成功")
        }
    }
}
</script>
<template>
    <h3>hello</h3>
</template>

element-ui介绍

组件的使用

SpringBoot + Vue基本知识点荟萃

图标的使用

SpringBoot + Vue基本知识点荟萃
SpringBoot + Vue基本知识点荟萃

Axios网络请求

Axios的使用

Axios简介

发送网络请求

发送get请求

SpringBoot + Vue基本知识点荟萃

发送post请求

SpringBoot + Vue基本知识点荟萃

异步回调问题

async/await
SpringBoot + Vue基本知识点荟萃

其他请求方式

参考:https://axios-http.com/zh/docs/req_config
SpringBoot + Vue基本知识点荟萃

与Vue整合

SpringBoot + Vue基本知识点荟萃

跨域

为什么会出现跨域问题

跨域问题解决

简单请求

满足以下条件的请求即为简单请求:

简单请求的服务器处理

对于简单请求,CORS的策略是请求时在请求头中增加一个Origin字段,
SpringBoot + Vue基本知识点荟萃
服务器收到请求后,根据该字段判断是否允许该请求访问,如果允许,则在HTTP头信息中添加Access-Control-Allow-Origin字段。
SpringBoot + Vue基本知识点荟萃

非简单请求

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

前端路由管理

Vue Router 的安装及使用

一. 路由(Vue2)

1. SPA 与前端路由

路由是根据不同的url地址来显示不同的页面或内容的功能,这个概念很早是由后端提出的,既浏览器向不同的地址发送请求,后端返回相应的内容。

SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。此时,不同组件之间的切换需要通过前端路由来实现。

前端路由通常是通过监听URL hash属性值的变化,切换页面,hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。

前端路由的工作方式

前端路由,指的是 Hash 地址与组件之间的对应关系。

2. vue-router基本使用

vue-router 是 vue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。

vue-router 目前有 3.x 的版本和 4.x 的版本。其中:

官方文档:https://router.vuejs.org/zh/installation.html

vue-router的基本使用步骤:

vue-router安装

npm install vue-router@3

创建路由组件

在项目中定义 Discover.vue、Friends.vue、My.vue 三个组件,将来要使用 vue-router 来控制它们的展示与切换:

Discover.vue:

<template>
    <div>
        <h1>发现音乐</h1>
    </div>
</template>

Friends.vue:

<template>
    <div>
        <h1>关注</h1>
    </div>
</template>

My.vue:

<template>
    <div>
        <h1>我的音乐</h1>
    </div>
</template>

声明路由链接和占位标签

可以使用 <router-link> 标签来声明路由链接,并使用 <router-view> 标签来声明路由占位符。示例代码如下:

App.vue:

<template>
  <div>
    <h1>APP组件</h1>
    <!-- 声明路由链接 -->
    <router-link to="/discover">发现音乐</router-link>
    <router-link to="/my">我的音乐</router-link>
    <router-link to="/friend">关注</router-link>
    <!-- 声明路由占位标签 -->
    <router-view></router-view>
  </div>
</template>

创建路由模块

在项目中创建 index.js 路由模块,加入以下代码:

import VueRouter from 'vue-router'
import Vue from 'vue'
import Discover from '@/components/Discover.vue'
import Friends from '@/components/Friends.vue'
import My from '@/components/My.vue'
//将VueRouter设置为Vue的插件
Vue.use(VueRouter)
const router = new VueRouter({
     // 指定hash属性与组件的对应关系
     routes: [
        { path: '/discover', component: Discover },
        { path: '/friends', component: Friends },
        { path: '/my', component: My },
    ]
})
export default router

挂载路由模块

在main.js中导入并挂载router

import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  router:router
}).$mount('#app')

3. vue-router进阶

路由重定向

路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。

通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:

const router = new VueRouter({
     // 指定hash属性与组件的对应关系
     routes: [
         // 当用户访问 / 时,跳转到 /discover
        { path: '/', redirect: '/discover'},
        { path: '/discover', component: Discover },
        { path: '/friends', component: Friends },
        { path: '/my', component: My },
    ]
})

嵌套路由

在 Discover.vue 组件中,声明 toplist和 playlist的子路由链接以及子路由占位符。示例代码如下:

<template>
    <div>
        <h1>发现音乐</h1>
        <!-- 子路由链接 -->
        <router-link to="/discover/toplist">推荐</router-link>
        <router-link to="/discover/playlist">歌单</router-link>
        <hr>
        <router-view></router-view>
    </div>
</template>

在 src/router/index.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则:

const router = new VueRouter({
     // 指定hash属性与组件的对应关系
     routes: [
        { path: '/', redirect: '/discover'},
        { 
            path: '/discover', 
            component: Discover,
            // 通过children属性,嵌套声明子路由
            children: [
                {path:"toplist",component:TopList},
                {path:"playlist",component:PlayList},
            ]
        },
        { path: '/friends', component: Friends },
        { path: '/my', component: My },
    ]
})

动态路由

思考:有如下 3 个路由链接:

<router-link to="/product/1">商品1</router-link>
<router-link to="/product/2">商品2</router-link>
<router-link to="/product/3">商品3</router-link>
const router = new VueRouter({
    // 指定hash属性与组件的对应关系
    routes: [
        { path: '/product/1', component: Product },
        { path: '/product/2', component: Product },
        { path: '/product/3', component: Product },
    ]
})

上述方式复用性非常差。

动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用英文的冒号(:)来定义路由的参数项。示例代码如下:

{ path: '/product/:id',component:Product }

通过动态路由匹配的方式渲染出来的组件中,可以使用 $route.params 对象访问到动态匹配的参数值,比如在商品详情组件的内部,根据id值,请求不同的商品数据

<template>
    <h1>Product组件</h1>
    <!-- 获取动态的id值 -->
    <p>{{$route.params.id}}</p>
</template>
<script>
export default {
//   组件的名称
  name: 'Product'
}
</script>

为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:

{ path: '/product/:id',component: Product, props: true }
<template>
    <h1>Product组件</h1>
    <!-- 获取动态的id值 -->
    <p>{{id}}</p>
</template>
<script>
export default {
//   组件的名称
  name: 'Product',
  props : [id]
}
</script>

编程式导航

声明式 编程式
<router-link :to="..."> router.push(...)

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)

<template>
    <button @click="gotoProduct(2)">跳转到商品2</button>
</template>
<script>
export default {
    methods:{
        gotoProduct: function(id){
            this.$router.push('/movie/${id}')
        }
    }
}
</script>

导航守卫

导航守卫可以控制路由的访问权限。示意图如下:

全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制。

你可以使用 router.beforeEach 注册一个全局前置守卫:

router.beforeEach((to, from, next) => {
    if (to.path === '/main' && !isAuthenticated) {
        next('/login')
    }
    else {
        next()
    }
})

二. 状态管理

1. 简单状态管理

经常被忽略的是,Vue 应用中响应式 data 对象的实际来源——当访问数据对象时,一个组件实例只是简单的代理访问。所以,如果你有一处需要被多个实例间共享的状态,你可以使用一个 reactive 方法让对象作为响应式对象。

const { createApp, reactive } = Vue
const sourceOfTruth = reactive({
  message: 'Hello'
})
const appA = createApp({
  data() {
    return sourceOfTruth
  }
}).mount('#app-a')
const appB = createApp({
  data() {
    return sourceOfTruth
  }
}).mount('#app-b')
<div id="app-a">App A: {{ message }}</div>
<div id="app-b">App B: {{ message }}</div>

现在当 sourceOfTruth 发生变更,appAappB 都将自动地更新它们的视图。虽然现在我们有了一个真实数据来源,但调试将是一场噩梦。应用的任何部分都可以随时更改任何数据,而不会留下变更过的记录。

const appB = createApp({
  data() {
    return sourceOfTruth
  },
  mounted() {
    sourceOfTruth.message = 'Goodbye' // both apps will render 'Goodbye' message now
  }
}).mount('#app-b')

为了解决这个问题,可以采用一个简单的 store 模式

const store = {
  debug: true,
  state: reactive({
    message: 'Hello!'
  }),
  setMessageAction(newValue) {
    if (this.debug) {
      console.log('setMessageAction triggered with', newValue)
    }
    this.state.message = newValue
  },
  clearMessageAction() {
    if (this.debug) {
      console.log('clearMessageAction triggered')
    }
    this.state.message = ''
  }
}

需要注意,所有 store 中 state 的变更,都放置在 store 自身的 action 中去管理。这种集中式状态管理能够被更容易地理解哪种类型的变更将会发生,以及它们是如何被触发。当错误出现时,现在也会有一个 log 记录 bug 之前发生了什么。

此外,每个实例/组件仍然可以拥有和管理自己的私有状态:

<div id="app-a">{{sharedState.message}}</div>
<div id="app-b">{{sharedState.message}}</div>
const appA = createApp({
  data() {
    return {
      privateState: {},
      sharedState: store.state
    }
  },
  mounted() {
    store.setMessageAction('Goodbye!')
  }
}).mount('#app-a')
const appB = createApp({
  data() {
    return {
      privateState: {},
      sharedState: store.state
    }
  }
}).mount('#app-b')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1KSsN0eb-1664286794923)(2_路由与状态管理.assets/state.png#crop=0&crop=0&crop=1&crop=1&id=g9T6Z&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]

随着我们进一步扩展约定,即组件不允许直接变更属于 store 实例的 state,而应执行 action 来分发 (dispatch) 事件通知 store 去改变,最终达成了 Flux 架构。这样约定的好处是,能够记录所有 store 中发生的 state 变更,同时实现能做到记录变更、保存状态快照、历史回滚/时光旅行的先进的调试工具。

2. Vuex介绍

由于状态零散地分布在许多组件和组件之间的交互中,大型应用复杂度也经常逐渐增长。为了解决这个问题,Vue 提供 Vuex:我们有受到 Elm 启发的状态管理库。

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

示例

让我们从一个简单的 Vue 计数应用开始:

const Counter = {
  // 状态
  data () {
    return {
      count: 0
    }
  },
  // 视图
  template: `
    <div>{{ count }}</div>
  `,
  // 操作
  methods: {
    increment () {
      this.count++
    }
  }
}
createApp(Counter).mount('#app')

这个状态自管理应用包含以下几个部分:

以下是一个表示“单向数据流”理念的简单示意:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kR1BZHTv-1664286794923)(2_路由与状态管理.assets/flow.png#crop=0&crop=0&crop=1&crop=1&id=EmKeb&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力

对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。

这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux 和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

什么情况下应该使用 Vuex?

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:

Flux 架构就像眼镜:您自会知道什么时候需要它。

安装

npm install vuex@3

3. 最简单的 Store

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

安装Vuex 之后,让我们来创建一个 store。创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutation:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

现在,你可以通过 store.state 来获取状态对象,并通过 store.commit 方法触发状态变更:

store.commit('increment')
console.log(store.state.count) // -> 1

为了在 Vue 组件中访问 this.$store property,你需要为 Vue 实例提供创建好的 store。Vuex 提供了一个从根组件向所有子组件,以 store 选项的方式“注入”该 store 的机制:

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

在 Vue 组件中, 可以通过 this.$store 访问store实例。现在我们可以从组件的方法提交一个变更:

methods: {
  increment() {
    this.$store.commit('increment')
    console.log(this.$store.state.count)
  }
}

再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。

由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。

4. State

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

单状态树和模块化并不冲突——在后面的章节里我们会讨论如何将状态和状态变更事件分布到各个子模块中。

在 Vue 组件中获得 Vuex 状态

那么我们如何在 Vue 组件中展示状态呢?由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:

// 创建一个 Counter 组件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。

然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入,并且在测试组件时需要模拟状态。

Vuex 通过 Vue 的插件系统将 store 实例从根组件中“注入”到所有的子组件里。且子组件能通过 this.$store 访问到。让我们更新下 Counter 的实现:

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

mapState 辅助函数

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:

// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
  // ...
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,
    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',
    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

对象展开运算符

mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符,我们可以极大地简化写法:

computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}

组件仍然保有局部状态

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

5. Getter

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo => todo.done).length
  }
}

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。

Getter 接受 state 作为其第一个参数:

const store = createStore({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: (state) => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

通过属性访问

Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值:

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

Getter 也可以接受其他 getter 作为第二个参数:

getters: {
  // ...
  doneTodosCount (state, getters) {
    return getters.doneTodos.length
  }
}
store.getters.doneTodosCount // -> 1

我们可以很容易地在任何组件中使用它:

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

通过方法访问

你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

mapGetters 辅助函数

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:

import { mapGetters } from 'vuex'
export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

如果你想将一个 getter 属性另取一个名字,使用对象形式:

...mapGetters({
  // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
  doneCount: 'doneTodosCount'
})

6. Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

const store = createStore({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

你不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,你需要以相应的 type 调用 store.commit 方法:

store.commit('increment')

提交载荷(Payload)

你可以向 store.commit 传入额外的参数,即 mutation 的载荷(payload)

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

对象风格的提交方式

提交 mutation 的另一种方式是直接使用包含 type 属性的对象:

store.commit({
  type: 'increment',
  amount: 10
})

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此处理函数保持不变:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

使用常量替代 Mutation 事件类型

使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = createStore({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [SOME_MUTATION] (state) {
      // 修改 state
    }
  }
})

用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。

Mutation 必须是同步函数

一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子:

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。

在组件中提交 Mutation

你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。

import { mapMutations } from 'vuex'
export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

在 mutation 中混合异步调用会导致你的程序很难调试。例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。在 Vuex 中,mutation 都是同步事务

store.commit('increment')
// 任何由 "increment" 导致的状态变更都应该在此刻完成。

为了处理异步操作,让我们来看一看Action。

7. Action

Action 类似于 mutation,不同在于:

让我们来注册一个简单的 action:

const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。

实践中,我们会经常用到 ES2015 的参数解构来简化代码(特别是我们需要调用 commit 很多次的时候):

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

分发 Action

Action 通过 store.dispatch 方法触发:

store.dispatch('increment')

乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

Actions 支持同样的载荷方式和对象方式进行分发:

// 以载荷形式分发
store.dispatch('incrementAsync', {
  amount: 10
})
// 以对象形式分发
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

来看一个更加实际的购物车示例,涉及到调用异步 API分发多重 mutation

actions: {
  checkout ({ commit, state }, products) {
    // 把当前购物车的物品备份起来
    const savedCartItems = [...state.cart.added]
    // 发出结账请求,然后乐观地清空购物车
    commit(types.CHECKOUT_REQUEST)
    // 购物 API 接受一个成功回调和一个失败回调
    shop.buyProducts(
      products,
      // 成功操作
      () => commit(types.CHECKOUT_SUCCESS),
      // 失败操作
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

注意我们正在进行一系列的异步操作,并且通过提交 mutation 来记录 action 产生的副作用(即状态变更)。

在组件中分发 Action

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):

import { mapActions } from 'vuex'
export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
      // `mapActions` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }
}

组合 Action

Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?

首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

现在你可以:

store.dispatch('actionA').then(() => {
  // ...
})

在另外一个 action 中也可以:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

最后,如果我们利用 async / await,我们可以如下组合 action:

// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。

8. Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}
const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

模块的局部状态

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象

const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },
  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

模块动态注册

在 store 创建之后,你可以使用 store.registerModule 方法注册模块

import { createStore } from 'vuex'
const store = createStore({ /* 选项 */ })
// 注册模块 `myModule`
store.registerModule('myModule', {
  // ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})

之后就可以通过 store.state.myModulestore.state.nested.myModule 访问模块的状态。

参数传递

子路由

导航守卫

SpringBoot + Vue基本知识点荟萃

状态管理VueX

Vuex介绍

状态管理

SpringBoot + Vue基本知识点荟萃

State

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

Getter

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

Mutation

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

Action

SpringBoot + Vue基本知识点荟萃

SpringBoot + Vue基本知识点荟萃

Module

SpringBoot + Vue基本知识点荟萃

Vuex安装与使用

前端数据模拟MockJS

mockjs介绍

mockjs基本使用

1. 什么是mockjs

Mock.js 是一款前端开发中拦截Ajax请求再生成随机数据响应的工具.可以用来模拟服务器响应. 优点是非常简单方便, 无侵入性, 基本覆盖常用的接口数据类型.

安装

npm install mockjs

2. 快速入门

在项目中创建mock目录,新建index.js文件

//引入mockjs
import Mock from 'mockjs'
//使用mockjs模拟数据
Mock.mock('/product/search', {
    "ret":0,
    "data":
      {
        "mtime": "@datetime",//随机生成日期时间
        "score|1-800": 1,//随机生成1-800的数字
        "rank|1-100":  1,//随机生成1-100的数字
        "stars|1-5": 1,//随机生成1-5的数字
        "nickname": "@cname",//随机生成中文名字
        //生成图片
        "img":"@image('200x100', '#ffcc33', '#FFF', 'png', 'Fast Mock')"
      }
});

main.js里面引入

import './mock'

xxx.vue文件中调用mock.js中模拟的数据接口,这时返回的response就是mock.js中用Mock.mock(‘url’,data)中设置的data了

import axios from 'axios'
export default {
  mounted:function(){
    axios.get("/product/search").then(res => {
      console.log(res)
    })
  }
}

注意,如果get请求如果带参数,会以?a=b&c=d,形式拼接到url上,这时mock请把接口url写为正则匹配,否则匹配不到就报错Mock.mock(RegExp(API_URL + ".*")),如:

import axios from 'axios'
export default {
  mounted:function(){
    ///product/search?id=2
    axios.get("/product/search",{params:{id:2}}).then(res => {
      console.log(res)
    })
  }
}

在url后面加上.*

mockjs.mock(RegExp("/product/search"+ ".*"),'get',option=>{
    console.log(option.url) // 请求的url
    console.log(option.body)// body为post请求参数
    return {
        status:200,
        message:"获取数据成功"
    }
})

3. 核心方法

Mock.mock( rurl?, rtype?, template|function( options ) )

这是mock的核心方法,根据数据模板生成模拟数据。

具体使用:

4. 设置延时请求到数据

不设置延时很有可能遇到坑,这里需要留意,因为真实的请求是需要时间的,mock不设置延时则是马上拿到数据返回,这两个情况不同可能导致在接口联调时出现问题。所以最好要先设置延时请求到数据。

//延时400ms请求到数据
Mock.setup({
    timeout: 400
})
//延时200-600毫秒请求到数据
Mock.setup({
    timeout: '200-600'
})

数据生成规则

1. 数据模板DTD

mock的语法规范包含两层规范:

基本语法

数据模板中的每个属性由 3 部分构成:属性名name、生成规则rule、属性值value:

'name|rule': value

属性名和生成规则之间用竖线 | 分隔,生成规则是可选的

生成规则 有 7 种格式:

'name|min-max': value
'name|count': value
'name|min-max.dmin-dmax': value
'name|min-max.dcount': value
'name|count.dmin-dmax': value
'name|count.dcount': value
'name|+step': value

生成规则和示例

  1. 属性值是字符串 String
//通过重复 string 生成一个字符串,重复次数大于等于 min,小于等于 max。
'name|min-max': string
//通过重复 string 生成一个字符串,重复次数等于 count。
'name|count': string
var data = Mock.mock({
    'name1|1-3':'a',       //重复生成1到3个a(随机)
    'name2|2':'b'          //生成bb
})
  1. 属性值是数字 Number
//属性值自动加 1,初始值为 number。
'name|+1': number
//生成一个大于等于 min、小于等于 max 的整数,属性值 number 只是用来确定类型。
'name|min-max': number
//生成一个浮点数,整数部分大于等于 min、小于等于 max,小数部分保留 dmin 到 dmax 位。
'name|min-max.dmin-dmax': number
Mock.mock({
    'number1|1-100.1-10': 1,
    'number2|123.1-10': 1,
    'number3|123.3': 1,
    'number4|123.10': 1.123
})
//结果:
{
    "number1": 12.92,
    "number2": 123.51,
    "number3": 123.777,
    "number4": 123.1231091814
}
var data = Mock.mock({
    'name1|+1':4,         //生成4,如果循环每次加1
    ‘name2|1-7':2,        //生成一个数字,1到7之间
    'name3|1-4.5-8':1     生成一个小数,整数部分1到4,小数部分5到8位,数字1只是为了确定类型
})
  1. 属性值是布尔型 Boolean
//随机生成一个布尔值,值为 true 的概率是 1/2,值为 false 的概率同样是 1/2。
'name|1': boolean
//随机生成一个布尔值,值为 value 的概率是 min / (min + max),值为 !value 的概率是 max / (min + max)。
'name|min-max': value
var data = Mock.mock({
    'name|1':true,          //生成一个布尔值,各一半
    'name1|1-3':true        //1/4是true,3/4是false
})
  1. 属性值是对象 Object
//从属性值 object 中随机选取 count 个属性。
'name|count': object
//从属性值 object 中随机选取 min 到 max 个属性。
'name|min-max': object
var obj = {
    a:1,
    b:2,
    c:3,
    d:4
}
var data = Mock.mock({
    'name|1-3':obj,      //随机从obj中寻找1到3个属性,新对象
    'name|2':obj         //随机从onj中找到两个属性,新对象
})
  1. 属性值是数组 Array
//从属性值 array 中随机选取 1 个元素,作为最终值。
'name|1': array
//从属性值 array 中顺序选取 1 个元素,作为最终值。
'name|+1': array
//通过重复属性值 array 生成一个新数组,重复次数大于等于 min,小于等于 max。
'name|min-max': array
//通过重复属性值 array 生成一个新数组,重复次数为 count。
'name|count': array
Mock.mock({
    //通过重复属性值 array 生成一个新数组,重复次数为 1-3次。
    "favorite_games|1-3": [3,5,4,6,23,28,42,45],
});
var arr = [1,2,3];
var data = Mock.mock({
    'name1|1':arr,          //从数组里随机取出1个值
    'name2|2':arr,          //数组重复count次,这里count为2
    'name3|1-3':arr,        //数组重复1到3次
})
  1. 属性值是函数 Function
执行函数 function,取其返回值作为最终的属性值,函数的上下文为属性 'name' 所在的对象。
'name': function
var fun = function(x){
    return x+10;
}
var data = Mock.mock({
    'name':fun(10)         //返回函数的返回值20
})
  1. 属性值是正则表达式 RegExp
根据正则表达式 regexp 反向生成可以匹配它的字符串。用于生成自定义格式的字符串。
'name': regexp
Mock.mock({
    'regexp1': /[a-z][A-Z][0-9]/,
    'regexp2': /\w\W\s\S\d\D/,
    'regexp3': /\d{5,10}/
})
// =>
{
    "regexp1": "pJ7",
    "regexp2": "F)\fp1G",
    "regexp3": "561659409"
}

2. 数据占位符DPD

占位符只是在属性值字符串中占个位置,并不出现在最终的属性值中。

占位符的格式为:

@占位符
@占位符(参数 [, 参数])

关于占位符需要知道以下几点

//引入mockjs
import Mock from 'mockjs'
//使用mockjs模拟数据
Mock.mock('/api/msdk/proxy/query_common_credit', {
    "ret":0,
    "data":
      {
        "mtime": "@datetime",//随机生成日期时间
        "score":  "@natural(1, 800)",//随机生成1-800的数字
        "rank":  "@natural(1, 100)",//随机生成1-100的数字
        "stars": "@natural(0, 5)",//随机生成1-5的数字
        "nickname": "@cname",//随机生成中文名字
      }
});

3. 用例

基础随机内容的生成

{
  "string|1-10": "=", // 随机生成1到10个等号
  "string2|3": "=", // 随机生成2个或者三个等号
  "number|+1": 0, // 从0开始自增
  "number2|1-00.1-3": 1, // 生成一个小数,小数点前面1到10,小数点后1到3位
  "boolean": "@boolean( 1, 2, true )", // 生成boolean值 三个参数,1表示第三个参数true出现的概率,2表示false出现的概率
  "name": "@cname", // 随机生成中文姓名
  "firstname": "@cfirst", // 随机生成中文姓
  "int": "@integer(1, 10)", // 随机生成1-10的整数
  "float": "@float(1,2,3,4)", // 随机生成浮点数,四个参数分别为,整数部分的最大最小值和小数部分的最大最小值
  "range": "@range(1,100,10)", // 随机生成整数数组,三个参数为,最大最小值和加的步长
  "natural": "@natural(60, 100)", // 随机生成自然数(大于零的数)
  "email": "@email", // 邮箱
  "ip": "@ip" ,// ip
  "datatime": "@date('yy-MM-dd hh:mm:ss')" // 随机生成指定格式的时间
  // ......
}

列表数据

{
  "code": "0000",
  "data": {
    "pageNo": "@integer(1, 100)",
    "totalRecord": "@integer(100, 1000)",
    "pageSize": 10,
    "list|10": [{
      "id|+1": 1,
      "name": "@cword(10)",
      "title": "@cword(20)",
      "descript": "@csentence(20,50)",
      "price": "@float(10,100,10,100)"
    }]
  },
  "desc": "成功"
}

图片

mockjs可以生成任意大小,任意颜色块,且用文字填充内容的图片,使我们不用到处找图片资源就能轻松实现图片的模拟展示

{
"code": "0000",
  "data": {
    "pageNo": "@integer(1, 100)",
    "totalRecord": "@integer(100, 1000)",
    "pageSize": 10,
    "list|10": [{
      // 参数从左到右依次为,图片尺寸,背景色,前景色(及文字颜色),图片格式,图片中间的填充文字内容
      "image": "@image('200x100', '#ffcc33', '#FFF', 'png', 'Fast Mock')" 
    }]
  },
  "desc": "成功"
}

4. Mock.Random

Mock.Random 是一个工具类,用于生成各种随机数据。

Mock.Random 的方法在数据模板中称为『占位符』,书写格式为 @占位符(参数 [, 参数]) 。
用法示例:

var Random = Mock.Random
Random.email()
// => "n.clark@miller.io"
Mock.mock('@email')
// => "y.lee@lewis.org"
Mock.mock( { email: '@email' } )
// => { email: "v.lewis@hall.gov" }

Mock.Random 提供的完整方法(占位符)如下:

Type Method
Basic boolean, natural, integer, float, character, string, range, date, time, datetime, now
Image image, dataImage
Color color
Text paragraph, sentence, word, title, cparagraph, csentence, cword, ctitle
Name first, last, name, cfirst, clast, cname
Web url, domain, email, ip, tld
Address area, region
Helper capitalize, upper, lower, pick, shuffle
Miscellaneous guid, id

Basic

随机生成布尔值

var bool1 = Random.boolean();      //true false各一半
var bool2 = Random.boolean(1,2false)    //1/3的可能性是false 2/3是true

随机生成一个自然数,什么叫自然数,就是大于等于0的

var natural1 = Random.natural();       //默认值最大为 9007199254740992
var natural2 = Random.natural(4);         //随机出来的最小值是4
var natural3 = Random.natural(6,9);

生成一个随机的整数,可以是负数。

var integer1 = Random.integer();
var integer2 = Random.integer(-10);        //随机最小值是-10
var integer3 = Random.integer(-10,20);

随机生成一个小数浮点数,四个参数分别为,整数部分最小值最大值,小数部分最小值最大值。

var float1 = Random.float();
var float2 = Random.float(3,8);
var float3 = Random.float(1,3,5,7)

随机生成一个字符,pool的值可以是:

  1. upper: 26个大写字母
  2. lower: 26个小写字母
  3. number: 0到9十个数字
  4. sympol: “!@#$%^&*()[]”
var character1 = Random.character();
var character2 = Random.character('lower');
var character3 = Random.character('upper');
var character4 = Random.character('symbol');

随机生成一个字符串,pool的值同上边四个。

var str1 = Random.string();                //长度3到7位
var str2 = Random.string(5);               //长度5位
var str3 = Random.string('lower',7);       //长度7位,小写
var str4 = Random.string(4,6);             //长度4到
var str5 = Random.string('新的字符串会从这里选择45位',4,6);   //从第一个参数里选择4到5位

返回一个整型数组

  1. start,可选,数组起始值,闭区间
  2. stop,必选,数据结束值,开区间
  3. step,可选,数据每一项间隔值
var range1 = Random.range(10);     //[0,1,2,3,4,5,6,7,8,9]
var range2 = Random.range(14,20);  //[14,15,16,17,18,19]
var range3 = Random.range(3,13,2); //[3,5,7,9,11]

返回一个随机日期的字符串
format的格式是‘yyyy-MM-dd’,可以随机组合

var date1 = Random.date();
var date2 = Random.date('yyyy-MM-dd');
var date3 = Random.date('y-M-d');
var date4 = Random.date('yy-MM-dd');

返回时间字符串
format的格式是‘HH-mm-ss’

var time1 = Random.time();
var time2 = Random.time('HH-mm-ss');
var time3 = Random.time('J-m-s');

上边两个的结合版

var dt1 = Random.datetime();
var dt2 = Random.datetime('yyyy-MM-dd HH-mm-ss');
Random.now(unit?,format?)

返回当前时间的字符串

Image

一般情况下,使用dataImage更好,因为更简单,但是如果要生成高度自定义的图片,则最好用image。另外,dataImage生成的是base64编码

  1. size 图片宽高,格式是’宽x高’
  2. background:图片的背景色,默认值#000000
  3. foreground:图片的文字前景色,默认#FFFFFF
  4. format:图片的格式,默认’.png’
  5. text:图片上的默认文字,默认值为参数size

其中size的取值范围是

[
  '300x250', '250x250', '240x400', '336x280', 
  '180x150', '720x300', '468x60', '234x60', 
  '88x31', '120x90', '120x60', '120x240', 
  '125x125', '728x90', '160x600', '120x600', 
  '300x600'
]

图片的格式可以选择.png.gif.jpg

var image1 = Random.image();
var image2 = Random.image('128x90');
var image3 = Random.image('120x660','#ccc');    //前景色#ccc
var image4 = Random.image('226x280','#eee','第三个参数是文字不是前景色');
var image5 = Random.image('66x31','#ddd','#123456','四个参数的时候第三个参数是前景色');
var image6 = Random.image('240x400','#333','#1483dc','.gif','全部参数的情况下');

返回一段base64编码,两个参数同上。

var di1 = Random.dataImage();
var di2 = Random.datImage('300x600');
var di3 = Random.dataImage('180x150','hahahaha');

Color

有好几个相关的方法

var color = Random.color(); //格式'#rrggbb'
var hex = Random.hex();   //好像和color没什么不同
var rgb = Random.rgb();   //生成格式如rgb(133,233,244)
var rgba = Random.rgba(); //生成个事如rgba(111,222,233,0.5)
var hsl = Random.hsl();   //生成格式(345,82,71)

Text

随机生成一段文本,

var para1 = Random.paragraph();    //随机生成一短文本,范围3到7
var para2 = Random.paragraph(10);  //随机生成长度是10的文本
var para3 = Random.paragraph(9,12); //随机生成9到11位长度的文本

随机生成一个句子,第一个单词的首字母大写

var sen1 = Random.sentence();        //默认长度12到18
var sen2 = Random.sentence(10);      //随机生成一个单词个数为10的句子
var sen3 = Random.sentence(5,10);    //随机生成一个5到9单词个数的句子

随机生成一个单词

var word1 = Random.word();          //默认长度3到10
var word2 = Random.word(7);         //随机生成长度是7的单词
var word3 = Random.word(2,12);      //随机生成2到11位长度的单词

随机生成一段标题,每个单词的首字母大写

var title1 = Random.title();        //title中的单词个数
var title2 = Random.title(6);       //title六个单词
var title3 = Random.title(7,12);    //title7到11个单词

另外还有四个方法,四个方法前边加一个**c**,返回中文内容

  1. Random.cparagraph, 返回中文文本
  2. Random.csentence, 返回中文句子
  3. Random.cword, 返回中文文字
  4. Random.ctitle, 返回中文标题

Name

var first = Random.first()         随机生成常见英文名
var last = Random.last()           随机生成常见英文姓
var name = Random.name()           随机生成常见英文姓名
var cfirst = Random.cfirst()       随机生成常见中文姓
var clast = Random.clast()         随机生成常见中文名
var cname = Random.cname()         随机生成一个常见中文姓名

Web

随机生成一个url

  1. protocol可选参数,表示网络协议,如http
  2. host表示域名和端口号
var url1 = Random.url();
var url2 = Random.url('http');
var url3 = Random.url('http','58.com');

随机生成一个域名

var protocol = Random.protocol()

protocol可以选的值,‘http’、‘ftp’、‘gopher’、‘mailto’、‘mid’、‘cid’、‘news’、‘nntp’、‘prospero’、‘telnet’、‘rlogin’、‘tn3270’、‘wais’。

随机生成一个域名

随机生成一个顶级域名

var domain = Random.domain()
var tld = Random.tld()

随机生成一个email地址,domain表示域名

var email1 = Random.email();
var email2 = Random.email('58.com')     //生成xxxx@58.com

随机生成一个ip地址

var ip = Random.ip()

Address

随机生成一个中国的大区,如华北,西南

var region = Random.region();

随机生成一个中国省直辖市自治区特别行政区

var province = Random.province()

随机生成一个中国城市,prefix布尔值,表示是否标注所在省

var city1 = Random.city();
var city2 = Random.city(ture);
Random.country(prefix?)

随机生成一个中国县,prefix布尔值,表示是否显示所属的省市

var county1 = Random.county();
var county2 = Random.county(ture);

随机生成一个六位数邮政编码

var zip = Random.zip();

Helper

把第一个字母转成大写

var capitalize = Random.capitalize('hello')

转成大写

var upper = Random.upper('zhang');

转成小写

var lower = Random.lower('JINGWEI');

从数组中随机选取一个元素

var arr = [1,4,5,6,7,8];
var pick = Random.pick(arr);

打乱数组的顺序并返回

var arr  = [1,2,3,4,6];
var shuffle = Random.shuffle(arr);

Miscellaneous

随机生成一个GUID

随机生成一个18位身份证id

var guid = Random.guid();
var id = Random.id();

扩展

Mock.Random 中的方法与数据模板的 @占位符 一一对应,在需要时还可以为 Mock.Random 扩展方法,然后在数据模板中通过 @扩展方法 引用。例如:

Random.extend({
  constellation: function(date) {
    var constellations = ['白羊座'`, '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座', '水瓶座', '双鱼座']
        return this.pick(constellations)
    }
})
Random.constellation()
// => "水瓶座"
Mock.mock('@CONSTELLATION')
// => "天蝎座"
Mock.mock({
    constellation: '@CONSTELLATION'
})
// => { constellation: "射手座" }

企业级后台集成方案

vue-element-admin介绍

安装与使用

注意事项

源码解读

跨域认证

Session认证

互联网服务离不开用户认证。一般流程是下面这样。

SpringBoot + Vue基本知识点荟萃
session 认证的方式应用非常普遍,但也存在一些问题,扩展性不好,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session,针对此种问题一般有两种方案:

Token认证

Token 是在服务端产生的一串字符串,是客户端访问资源接口(API)时所需要的资源凭证,流程如下:

SpringBoot + Vue基本知识点荟萃

token认证的特点

JWT认证

SpringBoot + Vue基本知识点荟萃

JWT

JWT 的由三个部分组成,依次如下:

三部分最终组合为完整的字符串,中间使用 . 分隔,如下:

SpringBoot + Vue基本知识点荟萃

JWT的特点

JWT的实现

SpringBoot + Vue基本知识点荟萃

生成Token

SpringBoot + Vue基本知识点荟萃

解析Token

SpringBoot + Vue基本知识点荟萃

发表回复