vueReview(黑马)

本文详细介绍了Vue.js的基础知识,包括MVVM原理、生命周期和数据绑定。通过实例展示了Element-UI的布局和表格应用,并深入探讨了如何在Vue项目中实现查询、新增、删除和分页等操作。同时,文章讨论了优化Servlet代码的方法,以及前端代码的优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

入门案例

MVVM(Model-View-ViewModel):数据双向绑定:数据和视图同时变化

//在线cdn
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  1. 创建一个vue对象,监听一个id=app的组件

  2. v-model =“username” = 让vue监听我这个名为username的模型的数据.

  3. vue发现username模型中的数据变化时,data()中的username发生变化,并将新值返回,

  4. 最后渲染给插件表达式

<div id="app">

  <input v-model="username">
  <!-- 插值表达式 -->
  {{username}}

</div>

<!--放在下面先加载好页面再解析js-->
<script>
	<!--创建一个vue对象,监听id=app的组件,-->
  new Vue({
    el:"#app",
    //vue对象中全部都是键值对类型的对象,这是对下面代码的简写
    data(){
      return{
        username:""
      }
    }
    /*data:function (){
        return{
          username:""
        }
      }*/
  })

</script>

常用方法

<div id="app">

  <input v-model="username">

  <!-- 动态绑定 -->
  <!--写()用来传参-->
  <a v-bind:href="username" v-on:click="show()">aaaa</a>

  <div v-if="username == 1">1</div>
  <div v-else-if="username == 2">2</div>
  <div v-else>3</div>

  <div v-show="username == 4">style=display: none</div>

  <!--把这个div标签重复四遍-->
  <div v-for="(addr,index) in address" :key="index">
    索引{{index}} --> 元素{{addr}}
  </div>

</div>

<script>

  new Vue({
    el:"#app",
    data(){
      return{
        username:"",
        address:["长安","洛阳","海口","关东"]
      }
    },
    //所有的函数都写在这里面
    methods:{
      show(){
        alert("胡杰是个s级");
      }
    }
  })

</script>

生命周期

钩子函数

methods:{
show(){
alert("胡杰是个s级");
}
},
//DOM对象和Vue对象加载完成后执行
mounted(){
alert("Hello World!");
}

在这里插入图片描述

案例:数据添加和遍历

springMVC启动之后默认打开的首页webapp/index.jsp。位置和页面名称都不能错,否则将报404错误。

<div id="app">

  <!--表单数据双向绑定-->
  <input v-model:name="name" value="name">
  <input v-model:name="id" value="id">
  <input v-model:name="age" value="age">
  <input v-model:name="money" value="money">

  <!--向vue提交单击函数-->
  <input type="submit" value="提交" @click="show()">
</div>

<script>

  new Vue({
    el:"#app",
    data(){
      return{
        //接收view的双向绑定数据
        name:"",
        id:"",
        age:"",
        money:""
      }
    },
    methods:{
      show(){
        axios.post("http://localhost:8080/students",{name:this.name,id:this.id,age:this.age,money:this.money})
          .then(resp=>{
          alert(resp.data.msg);
          location.href = "html/showList.html"
        })

      }
    }
  })

</script>
<div id="app">

  <table>

    <!--html标签写错,访问不到数据-->
    <tr v-for="(stu,index) in students" :key="index">
      <th>{{index}}</th>
      <td>{{stu.name}}</td>
      <td>{{stu.id}}</td>
      <td>{{stu.age}}</td>
      <td>{{stu.money}}</td>
    </tr>

  </table>

</div>

<script>

  new Vue({
    el:"#app",
    data(){
      return{
        students:[]
      }
    },
    mounted(){
      axios.get("http://localhost:8080/students").then(resp=>{
        this.students = resp.data.data;
        console.log('y',this.students);
      })
    }
  })

</script>

Element-ui

在线cdn

<!--element基于vue,vue.js一定要在element.js前面-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>

<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

<!-- 引入样式:表示它是样式表 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">

布局

<!--  layout每行24个格子-->
<el-row>
  <el-col :span="12"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="4"><div class="grid-content bg-purple-light"></div></el-col>
  <el-col :span="4"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="4"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
return {
  //将item充满Array(20)这个集合
  tableData: Array(20).fill(item)
}

表格

//每列去掉后就是width:auto
width="180"

//居中对齐
align="center"

综合案例

SqlSessionFactoryUtils

package com.itheima.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionFactoryUtils {

  private static SqlSessionFactory sqlSessionFactory;

  static {
    //静态代码块会随着类的加载而自动执行,且只执行一次
    try {
      String resource = "mybatis-config.xml";
      InputStream inputStream = Resources.getResourceAsStream(resource);
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }


  public static SqlSessionFactory getSqlSessionFactory(){
    return sqlSessionFactory;
  }
}

查询所有

mapper层

@Select("select * from tb_brand")

//将xml中的规定的数据库返回的结果集绑定给实体类
@ResultMap("brandResultMap")
List<Brand> selectAll();

字段映射

<resultMap id="brandResultMap" type="brand">
  <result property="brandName" column="brand_name" />
  <result property="companyName" column="company_name" />
</resultMap>

servive层

public class BrandServiceImpl implements BrandService {

  //工具类获取slqSession工厂
  private SqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory();

  @Override
  public List<Brand> selectAll() {
    SqlSession sqlSession = factory.openSession();

    BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);

    try {
      return mapper.selectAll();
    }finally {
      //关闭资源
      sqlSession.close();
    }
  }

}
//调用Service层
List<Brand> brands = brandService.selectAll();

//转成json
String json = JSON.toJSONString(brands);

//返回前端
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);

mysql8.0在连接数据库时需要配置时区引用

useSSL=false&amp;serverTimezone=UTC"

前端接收数据并展示

//页面加载完成后,查询所有数据
mounted(){

  axios.get("http://localhost:8080/brand-case/selectAllServlet").then(resp=>{
  //将返回的json数据绑定给tableData数据
  this.tableData = resp.data;
  })
},

新增用户

@Insert("insert tb_brand value(null,#{brandName},#{companyName},#{ordered},#{description},#{status}) ")
int insertBrand(Brand brand);
@Override
public boolean insertBrand(Brand brand) {

  SqlSession sqlSession = factory.openSession();

  BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);

  try {
    return mapper.insertBrand(brand) > 0;
  }finally {
    //提交事务
    sqlSession.commit();
    sqlSession.close();
  }
  
}

insertServlet

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  //获取前端传来的参数
  BufferedReader reader = request.getReader();
  String s = reader.readLine();

  Brand brand = JSON.parseObject(s, Brand.class);

  boolean b = brandService.insertBrand(brand);

  response.getWriter().write(String.valueOf(b));
}

前端

将查询所有的方法拆分出来

//重置新增弹框的表单数据
resultForm(){
  this.brand = {};
},

//页面加载完成后,查询所有数据
mounted(){
  this.selectAll();
},

// 查询全部数据
selectAll(){
  axios.get("http://localhost:8080/brand-case/selectAllServlet").then(resp=>{
    //将返回的json数据绑定给tableData数据
    this.tableData = resp.data;
  })
},

添加数据

// 添加数据
addBrand() {
  axios.post("http://localhost:8080/brand-case/brand/insert",this.brand).then(resp=>{

    //注意,最后返回来的是个对象
    if(resp.data.toString() == "true"){

      //关闭弹窗
      this.dialogVisible = false;

      //弹出提示框
      this.$message({
        message: '恭喜你,添加成功',
        type: 'success'
      });

    }else if(resp.data.toString() == "false"){
      this.$message.error("添加失败,请检查数据");
    }else {
      //程序报错时:比如输入的字符串长度大于表的设计长度,就会出现系统级异常
      this.$message.error("系统级异常");
    }

    //必定执行
  }).finally(()=>{
    //重新刷新数据
    this.selectAll();
    this.resultForm();
  })
},

优化Servlet代码

将原先HttpServlet根据请求方式进行方法分发,变成根据请求路径进行方法分发

service() 方法是 Servlet 的核心

每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个"请求"(ServletRequest)对象和一个"响应"(ServletResponse)对象作为参数。

**在 HttpServlet 中已存在 service()方法.**缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。
例如,如果 HTTP 请求方法为 GET,则缺省情况下就调用 doGet()。

https://www.cnblogs.com/bdqczhl/p/7086970.html#:~:text=service%20%28%29%20%E6%96%B9%E6%B3%95%E6%98%AF%20Servlet%20%E7%9A%84%E6%A0%B8%E5%BF%83%E3%80%82%20%E6%AF%8F%E5%BD%93%E4%B8%80%E4%B8%AA%E5%AE%A2%E6%88%B7%E8%AF%B7%E6%B1%82%E4%B8%80%E4%B8%AAHttpServlet%20%E5%AF%B9%E8%B1%A1%2C%E8%AF%A5%E5%AF%B9%E8%B1%A1%E7%9A%84service%20%28%29,%E6%96%B9%E6%B3%95%E5%B0%B1%E8%A6%81%E8%A2%AB%E8%B0%83%E7%94%A8%2C%E8%80%8C%E4%B8%94%E4%BC%A0%E9%80%92%E7%BB%99%E8%BF%99%E4%B8%AA%E6%96%B9%E6%B3%95%E4%B8%80%E4%B8%AA%22%E8%AF%B7%E6%B1%82%22%20%28ServletRequest%29%E5%AF%B9%E8%B1%A1%E5%92%8C%E4%B8%80%E4%B8%AA%22%E5%93%8D%E5%BA%94%22%20%28ServletResponse%29%E5%AF%B9%E8%B1%A1%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0%E3%80%82%20%E5%9C%A8%20HttpServlet%20%E4%B8%AD%E5%B7%B2%E5%AD%98%E5%9C%A8%20service%20%28%29%E6%96%B9%E6%B3%95.

根据请求路径进行方法分发

package com.itheima.web.servlets;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class BaseServlet extends HttpServlet {

  //tomcat会调用这个方法来判断应该走get还是post
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    //url是全路径,uri是从项目名开始的短路径
    String requestURI = req.getRequestURI();

    int index = requestURI.lastIndexOf("/");

    //会把index处的字符也截取进去
    String substring = requestURI.substring(index + 1);

    //谁调用this就是谁,这里最后是BrandServlet调用,this就是BrandServlet
    Class<? extends BaseServlet> aClass = this.getClass();

    try {
      //子类的所有方法的传参全部统一
      Method method = aClass.getMethod(substring, HttpServletRequest.class, HttpServletResponse.class);

      try {
        //注意这里,子类的对应的方法要是public修饰的,不然会报找不到方法错误
        method.invoke(this,req,resp);
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      }
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }

  }

}

浏览器监听到访问/brand下的servlet时,会先调用它的父类BaseServlet

package com.itheima.web.servlets;

import com.alibaba.fastjson.JSON;
import com.itheima.pojo.Brand;
import com.itheima.service.BrandService;
import com.itheima.service.impl.BrandServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;

@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {

  private BrandService brandService = new BrandServiceImpl();


  public void selectAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    List<Brand> brands = brandService.selectAll();

    String json = JSON.toJSONString(brands);

    response.setContentType("text/json;charset=utf-8");
    response.getWriter().write(json);
  }


  public void insert(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    BufferedReader reader = request.getReader();
    String s = reader.readLine();

    Brand brand = JSON.parseObject(s, Brand.class);

    boolean b = brandService.insertBrand(brand);

    response.getWriter().write(String.valueOf(b));
  }
}

批量删除

/**
   * 批量删除
   * @param ids
   * @return
   */
int deleteByIds(@Param("ids") int[] ids);
<delete id="deleteByIds">

  delete from tb_brand where id in
  <!-- item是元素迭代时的别名,必写
         把service传过来的名为ids集合迭代,每个元素叫id,然后按条件拼接sql语句
         -->
  <foreach collection="ids" item="id" separator="," open="(" close=")">
    #{id}
  </foreach>

</delete>
public void deleteByIds(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  BufferedReader reader = request.getReader();
  String s = reader.readLine();

  //前台传来的是[id:"",id:""]
  int[] ids = JSON.parseObject(s, int[].class);

  boolean b = brandService.deleteByIds(ids);

  response.getWriter().write(String.valueOf(b));
}

前端

// 被选中的id数组
selectedIds:[],
// 复选框选中数据集合
multipleSelection: [],
// 复选框选中后执行的方法:这个val就是所有被选中的复选框的数据
handleSelectionChange(val) {
  this.multipleSelection = val;
},
// 批量删除
deleteByIds(){
  //确认操作弹出框
  this.$confirm('此操作将永久删除, 是否继续?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
    //确认删除
  }).then(() => {

    //得到所有的id this.multipleSelection中存了所有被选中的复选框信息
    for (let i = 0; i < this.multipleSelection.length; i++) {
      //会自动增加长度
      this.selectedIds[i] = this.multipleSelection[i].id;
    }

    axios.post("http://localhost:8080/brand-case/brand/deleteByIds",this.selectedIds).then(resp=>{

      if(resp.data.toString() == "true"){

        this.$message.success("删除成功");
      }else if(resp.data.toString() == "false"){
        this.$message.error("删除失败,请检查数据");
      }else {
        this.$message.error("系统级异常");
      }
    }).finally(()=>{
      this.selectAll();
    })

  }).catch(() => {
    this.$message({
      type: 'info',
      message: '已取消删除'
    });
  });

},

分页查询

返回前端的数据除了集合外还有总条数

//创建对象时告诉说list集合要存入什么样的对象
@Data
@AllArgsConstructor
public class PageBean<T> {

  private int totalCount;

  private List<T> rows;

}
/**
   * 分页查询
   * @param
   * @return
   */
//从哪里开始查,查多少个
@Select("select * from tb_brand limit #{startSize},#{pageSize}")

//千万记住这里要和数据库字段映射
@ResultMap("brandResultMap")
List<Brand> selectPage(@Param("startSize") int startSize,@Param("pageSize")int pageSize);

/**
   * 总条数
   * @param 
   * @return
   */
@Select("select count(*) from tb_brand ")
int totalSize();

service层返回结果集

//当前页与查询几个
@Override
public PageBean<Brand> selectPage(int currentPage, int pageSize) {

  SqlSession sqlSession = factory.openSession();

  BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);

  //算出起始索引
  int startSize = (currentPage - 1) * pageSize;

  //调用mapper层
  List<Brand> brands = mapper.selectPage(startSize, pageSize);

  int count = mapper.totalSize();

  //返回结果集
  PageBean<Brand> pageBean = new PageBean<Brand>(count, brands);

  return pageBean;
}

servlet层接收参数得到pageBean

public void selectPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  int currentPage = Integer.parseInt(request.getParameter("currentPage"));
  int pageSize = Integer.parseInt(request.getParameter("pageSize"));

  PageBean<Brand> pageBean = brandService.selectPage(currentPage,pageSize);

  String json = JSON.toJSONString(pageBean);

  response.setContentType("text/json;charset=utf-8");
  response.getWriter().write(json);
}

前端

更改查询所有为按条件查询分页

// 查询分页数据
selectAll(){

  axios.get("http://localhost:8080/brand-case//brand/selectPage?currentPage="+this.currentPage+"&pageSize="+this.pageSize)
    .then(resp=>{
    console.log(resp.data.rows)
    this.tableData = resp.data.rows;
    this.totalCount = resp.data.totalCount;
  })
},

模型层新增相应数据

//总页数默认100
totalCount:100,
//默认每页显示条数为5
pageSize:5,
//默认当前页为1
currentPage:1,

分页表单更改属性为我们自己新增的模型数据

<!--分页工具条-->
<el-pagination
               @size-change="handleSizeChange"
               @current-change="handleCurrentChange"
               :current-page="currentPage"
               :page-sizes="[5, 10, 15, 20]"
               :page-size="pageSize"
               layout="total, sizes, prev, pager, next, jumper"
               :total="totalCount">
</el-pagination>

修改对应的当前页和每页条数变化后的方法

//每页显示条数变化之后
handleSizeChange(val) {
	this.pageSize = val;
	this.selectAll();
},

//当前页数变化之后
handleCurrentChange(val) {
	this.currentPage = val;
	this.selectAll();
},

如果这行在项目运行后会莫名其妙自动添加,那就是在导入element-ui,直接注释

// import sl from "./element-ui/src/locale/lang/sl";

按条件查询

改造分页查询的后端代码

mapper查询时增加条件

/**
   * 按条件分页查询
   * @param
   * @return
   */
//从哪里开始查,查多少个,按什么条件
List<Brand> selectPageAndCondition(@Param("startSize") int startSize
                                   ,@Param("pageSize")int pageSize
                                   ,@Param("brand") Brand brand);

/**
   * 按条件查询总条数
   * @param
   * @return
   */
int totalSizeAndCondition(@Param("brand")Brand brand);

sql变成有条件的动态sql

<sql id="brandCondition">
  <where>
    <if test="brand.brandName != null and brand.brandName != '' ">
      <!--注意这里要写的是数据库字段名-->
      and brand_name like concat('%',#{brand.brandName},'%')
    </if>

    <if test="brand.companyName != null and brand.companyName != ''">
      and company_name like concat('%',#{brand.companyName},'%')
    </if>
    <if test="brand.status != null">
      and status = #{brand.status}
    </if>
  </where>
</sql>

<select id="selectPageAndCondition" resultMap="brandResultMap">
  select * from tb_brand

  <include refid="brandCondition"></include>

  limit #{startSize},#{pageSize}
</select>
<select id="totalSizeAndCondition" resultType="int">

  select count(*) from tb_brand
  <include refid="brandCondition"></include>

</select>

service原本要处理brand数据,但是直接由mapper的动态sql来处理了

@Override
public PageBean<Brand> selectPageAndCondition(int currentPage,int pageSize,Brand brand) {

  SqlSession sqlSession = factory.openSession();

  BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);

  //算出起始索引
  int startSize = (currentPage - 1) * pageSize;


  /*//处理brand的条件
    String brandName = brand.getBrandName();
    String companyName = brand.getCompanyName();

    if(brandName != "" && brandName.length()>0){
      brand.setBrandName("%"+brandName+"%");
    }*/

  //调用mapper层
  List<Brand> brands = mapper.selectPageAndCondition(startSize, pageSize,brand);
  int count = mapper.totalSizeAndCondition(brand);

  //返回结果集
  PageBean<Brand> pageBean = new PageBean<Brand>(count, brands);

  sqlSession.close();
  return pageBean;
}

servlet需要额外处理前端传来的brand数据

public void selectPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  int currentPage = Integer.parseInt(request.getParameter("currentPage"));
  int pageSize = Integer.parseInt(request.getParameter("pageSize"));


  //处理前端传来的数据中的brand对象
  BufferedReader reader = request.getReader();
  String s = reader.readLine();

  Brand brand = JSON.parseObject(s, Brand.class);


  PageBean<Brand> pageBean = brandService.selectPageAndCondition(currentPage,pageSize,brand);

  String json = JSON.toJSONString(pageBean);

  response.setContentType("text/json;charset=utf-8");
  response.getWriter().write(json);
}

前端

前端页面中默认已经绑定好了数据

// 查询方法
onSubmit() {
  this.selectAll();
},

selectAll()变成post,多提交一个brand对象的json数据

axios.post("http://localhost:8080/brand-case//brand/selectPage?currentPage="+this.currentPage+"&pageSize="+this.pageSize
           ,this.brand)
  .then(resp=>{
  this.tableData = resp.data.rows;
  this.totalCount = resp.data.totalCount;
})

brand实体中有个statusStr()将1,0变成了对应的"启用,禁用"字符串

prop="statusStr"
align="center"
label="当前状态">

前端代码优化

//钩子函数中会判断this属于谁,这里代表的vue对象
.then(resp=>{
  this.tableData = resp.data.rows;
  this.totalCount = resp.data.totalCount;
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helloses

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值