JavaWeb项目之尚硅谷书城:(六)Vue与前后端分离

6.1 Vue简介

Vue (读音 /vjuː/,类似于view) 是一套基于JavaScript,用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链 (opens new window)以及各种支持类库 (opens new window)结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

导入Vue的外部js文件就能够使用Vue框架了。

6.2 声明式渲染

6.2.1 概念

声明式是相对于编程式而言的。

  • 声明式:告诉框架做什么,具体操作由框架完成
  • 编程式:自己编写代码完成具体操作

渲染

./images

上图含义解释:

  • 蓝色方框:HTML标签
  • 红色圆形:动态、尚未确定的数据
  • 蓝色圆形:经过程序运算以后,计算得到的具体的,可以直接在页面上显示的数据、
  • 渲染:程序计算动态数据得到具体数据的过程

6.2.2 Demo

HTML代码

		<!-- 使用{{}}格式,指定要被渲染的数据 -->
		<div id="app">{{message}}</div>

vue代码

// 1.创建一个JSON对象,作为new Vue时要使用的参数
var argumentJson = {
	
	// el用于指定Vue对象要关联的HTML元素。el就是element的缩写
	// 通过id属性值指定HTML元素时,语法格式是:#id
	"el":"#app",
	
	// data属性设置了Vue对象中保存的数据
	"data":{
		"message":"Hello Vue!"
	}
};

// 2.创建Vue对象,传入上面准备好的参数
var app = new Vue(argumentJson);

./images

查看声明式渲染的响应式效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mJFdRcrU-1658074883893)(https://s2.loli.net/2022/07/17/K6T3lfZhku5cSm2.png)]

通过验证Vue对象的『响应式』效果,我们看到Vue对象和页面上的HTML标签确实是始终保持着关联的关系,同时看到Vue在背后确实是做了大量的工作。

6.3 Vue基本语法

6.3.1 绑定元素属性

v-bind:HTML标签的原始属性名

HTML代码

<div id="app">
	<!-- v-bind:value表示将value属性交给Vue来进行管理,也就是绑定到Vue对象 -->
	<!-- vueValue是一个用来渲染属性值的表达式,相当于标签体中加{{}}的表达式 -->
	<input type="text" v-bind:value="vueValue" />
	
	<!-- 同样的表达式,在标签体内通过{{}}告诉Vue这里需要渲染; -->
	<!-- 在HTML标签的属性中,通过v-bind:属性名="表达式"的方式告诉Vue这里要渲染 -->
	<p>{{vueValue}}</p>
</div>

Vue代码

// 创建Vue对象,挂载#app这个div标签
var app = new Vue({
	"el":"#app",
	"data":{
		"vueValue":"太阳当空照"
	}
});

6.3.2 双向数据绑定

页面上数据被修改后,Vue对象中的数据属性也跟着被修改

HTML代码

<div id="app">
	<!-- v-bind:属性名 效果是从Vue对象渲染到页面 -->
	<!-- v-model:属性名 效果不仅是从Vue对象渲染到页面,而且能够在页面上数据修改后反向修改Vue对象中的数据属性 -->
	<input type="text" v-model:value="vueValue" />
	
	<p>{{vueValue}}</p>
</div>

Vue代码

// 创建Vue对象,挂载#app这个div标签
var app = new Vue({
	"el":"#app",
	"data":{
		"vueValue":"太阳当空照"
	}
});

:value可以省略

<input type="text" v-model="vueValue" />

去除前后空格:trim修饰符

实际开发中,要考虑到用户在输入数据时,有可能会包含前后空格。而这些前后的空格对我们程序运行来说都是干扰因素,要去掉。在v-model后面加上.trim修饰符即可实现。

<input type="text" v-model.trim="vueValue" />

Vue会帮助我们在文本框失去焦点时自动去除前后空格。

6.3.3 条件渲染

根据Vue对象中,数据属性的值来判断是否对HTML页面内容进行渲染。

  1. v-if

    HTML代码

    <div id="app">
    	<h3>if</h3>
    	<img v-if="flag" src="/pro03-vue/./images/one.jpg" />
    	<img v-if="!flag" src="/pro03-vue/./images/two.jpg" />
    </div>
    

    Vue代码

    var app = new Vue({
        "el":"#app",
        "data":{
            "flag":true
        }
    });
    
  2. v-if和v-else

    HTML代码

    <div id="app02">
    	<h3>if/else</h3>
    	<img v-if="flag" src="/pro03-vue/./images/one.jpg" />
    	<img v-else="flag" src="/pro03-vue/./images/two.jpg" />
    </div>
    

    Vue代码

    var app02 = new Vue({
        "el":"#app02",
        "data":{
            "flag":true
        }
    });
    
  3. v-show

    HTML代码

    <div id="app03">
    	<h3>v-show</h3>
    	<img v-show="flag" src="/pro03-vue/./images/mi.jpg" />
    </div>
    

    Vue代码

    var app03 = new Vue({
        "el":"#app03",
        "data":{
            "flag":true
        }
    });
    

6.3.4 列表渲染

  1. 迭代一个简单的数组

    HTML代码

    <div id="app01">
    	<ul>
    		<!-- 使用v-for语法遍历数组 -->
    		<!-- v-for的值是语法格式是:引用数组元素的变量名 in Vue对象中的数组属性名 -->
    		<!-- 在文本标签体中使用{{引用数组元素的变量名}}渲染每一个数组元素 -->
    		<li v-for="fruit in fruitList">{{fruit}}</li>
    	</ul>
    </div>
    

    Vue代码

    var app01 = new Vue({
    	"el":"#app01",
    	"data":{
    		"fruitList": [
    			"apple",
    			"banana",
    			"orange",
    			"grape",
    			"dragonfruit"
    		]
    	}
    });
    
  2. 迭代一个对象数组

    HTML代码

    <div id="app">
    	<table>
    		<tr>
    			<th>编号</th>
    			<th>姓名</th>
    			<th>年龄</th>
    			<th>专业</th>
    		</tr>
    		<tr v-for="employee in employeeList">
    			<td>{{employee.empId}}</td>
    			<td>{{employee.empName}}</td>
    			<td>{{employee.empAge}}</td>
    			<td>{{employee.empSubject}}</td>
    		</tr>
    	</table>
    </div>
    

    Vue代码

    var app = new Vue({
    	"el":"#app",
    	"data":{
    		"employeeList":[
    			{
    				"empId":11,
    				"empName":"tom",
    				"empAge":111,
    				"empSubject":"java"
    			},
    			{
    				"empId":22,
    				"empName":"jerry",
    				"empAge":222,
    				"empSubject":"php"
    			},
    			{
    				"empId":33,
    				"empName":"bob",
    				"empAge":333,
    				"empSubject":"python"
    			}
    		]
    	}
    });
    

6.3.5 事件驱动

  1. 字符串顺序反转

    HTML代码

    <div id="app">
    	<p>{{message}}</p>
    	
    	<!-- v-on:事件类型="事件响应函数的函数名" -->
    	<button v-on:click="reverseMessage">Click me,reverse message</button>
    </div>
    

    Vue代码

    var app = new Vue({
    	"el":"#app",
    	"data":{
    		"message":"Hello Vue!"				
    	},
    	"methods":{
    		"reverseMessage":function(){
    			this.message = this.message.split("").reverse().join("");
    		}
    	}
    });
    
  2. 获取鼠标移动时的坐标信息

    HTML代码

    <div id="app">
    	<div id="area" v-on:mousemove="recordPosition"></div>
    	<p id="showPosition">{{position}}</p>
    </div>
    

    Vue代码

    var app = new Vue({
    	"el":"#app",
    	"data":{
    		"position":"暂时没有获取到鼠标的位置信息"
    	},
    	"methods":{
    		"recordPosition":function(event){
    			this.position = event.clientX + " " + event.clientY;
    		}
    	}
    });
    

6.3.6 侦听属性

我们希望Vue对象的属性发生变化时,能够做出一些行为,此时需要对Vue对象的属性进行侦听,调用我们准备好的函数。

HTML代码

<div id="app">
	<p>尊姓:{{firstName}}</p>
	<p>大名:{{lastName}}</p>
	尊姓:<input type="text" v-model="firstName" /><br/>
	大名:<input type="text" v-model="lastName" /><br/>
	<p>全名:{{fullName}}</p>
</div>

Vue代码

在watch属性中声明对firstName和lastName属性进行侦听的函数:

var app = new Vue({
	"el":"#app",
	"data":{
		"firstName":"jim",
		"lastName":"green",
		"fullName":"jim green"
	},
	"watch":{
		"firstName":function(inputValue){
			this.fullName = inputValue + " " + this.lastName;
		},
		"lastName":function(inputValue){
			this.fullName = this.firstName + " " + inputValue;
		}
	}
});

6.3.7 简化写法

  1. v-bind的简化写法

    正常写法:

    <input type="text" v-bind:value="message" />
    

    简化以后:

    <input type="text" :value="message" />
    
  2. v-on的简化写法

    正常写法:

    <button v-on:click="sayHello">SayHello</button>
    

    简化以后:

    <button @click="sayHello">SayHello</button>
    

6.4 Vue对象生命周期

6.4.1 概念

在我们各种语言的编程领域中,生命周期都是一个非常常见的概念。一个对象从创建、初始化、工作再到释放、清理和销毁,会经历很多环节的演变。比如我们在JavaSE阶段学习过线程的生命周期,今天学习Vue对象的生命周期,将来还要学习Servlet、Filter等Web组件的生命周期。

6.4.2 Vue对象的生命周期

./images

6.4.3 生命周期钩子函数

Vue允许我们在特定的生命周期环节中通过钩子函数来加入我们的代码。

<div id="app">
	<p id="content">{{message}}</p>
	<button @click="changeValue">点我</button>
</div>
new Vue({
	"el":"#app",
	"data":{
		"message":"hello"
	},
	"methods":{
		"changeValue":function(){
			this.message = "new hello";
		}
	},
	
	// 1.实例创建之前
	"beforeCreate":function(){
		console.log("beforeCreate:"+this.message);
	},
	
	// 2.实例创建完成
	"created":function(){
		console.log("created:"+this.message);
	},
	
	// 3.数据挂载前
	"beforeMount":function(){
		console.log("beforeMount:"+document.getElementById("content").innerText);
	},
	
	// 4.数据已经挂载
	"mounted":function(){
		console.log("mounted:"+document.getElementById("content").innerText);
	},
	
	// 5.数据更新前
	"beforeUpdate":function(){
		console.log("beforeUpdate:"+document.getElementById("content").innerText);
	},
	
	// 6.数据更新之后
	"updated":function(){
		console.log("updated:"+document.getElementById("content").innerText);
	}
});

6.5 书城前后端分离的部分代码

Java代码:

package book.controller;

import book.pojo.Book;
import book.pojo.Cart;
import book.pojo.CartItem;
import book.pojo.User;
import book.service.CartItemService;
import com.google.gson.Gson;

import javax.servlet.http.HttpSession;

public class CartController {

    CartItemService cartItemService;

    public String index(HttpSession session){
        User user = (User) session.getAttribute("currUser");
        Cart cart = cartItemService.getCart(user);
        user.setCart(cart);
        session.setAttribute("currUser", user);
        return  "cart/cart";
    }

    public String addCart(Integer bookId, HttpSession session){

        User currUser = (User) session.getAttribute("currUser");
        Cart cart = currUser.getCart();

        cartItemService.addOrUpdateCartItem(new CartItem(new Book(bookId), 1, currUser), cart);

        return "redirect:cart.do";
    }

    public String editCart(Integer cartItemId, Integer buyCount){
        cartItemService.updateCartItem(new CartItem(cartItemId, buyCount));
        return "";
    }
	/**
	 * 通过只发送json字符串与前端进行交互,实现前后端分离
	 */
    public String cartInfo(HttpSession session){
        User user = (User) session.getAttribute("currUser");
        Cart cart = cartItemService.getCart(user);
        Gson gson = new Gson();
        String cartJsonStr = gson.toJson(cart);
        return "json:" + cartJsonStr;
    }
}

JavaScript代码(Vue+Axios):

window.onload=function (){
    let vue = new Vue({
        el:"#cart_div",
        data:{
            cart:{}
        },
        methods:{
            getCart:function (){
                axios({
                    method:"POST",
                    url:"cart.do",
                    params:{
                        operate:'cartInfo'
                    }
                })
                    .then(function (value){
                        vue.cart = value.data
                    })
                    .catch(function (reason){
                    });
            },
            editCart:function(cartItemId, buyCount){
                axios({
                    method:"POST",
                    url:"cart.do",
                    params:{
                        operate:'editCart',
                        cartItemId:cartItemId,
                        buyCount:buyCount
                    }
                })
                    .then(function (value){
                        vue.getCart();
                    })
                    .catch(function (reason){
                    });
            }
        },
        mounted:function (){
            this.getCart();
        }
    });
}

中央控制器代码:

package myssm.servlet;

import myssm.exception.DispatcherServletException;
import myssm.ioc.BeanFactory;
import myssm.util.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {

    private BeanFactory beanFactory;

    public DispatcherServlet(){

    }

    @Override
    public void init() throws ServletException {
        super.init();
        ServletContext servletContext = getServletContext();
        Object beanFactoryObj = servletContext.getAttribute("beanFactory");
        if (beanFactoryObj != null){
            this.beanFactory = (BeanFactory) beanFactoryObj;
        } else {
            throw new RuntimeException("IOC容器获取失败...");
        }
    }

   @Override
   protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       request.setCharacterEncoding("UTF-8");
       String servletPath = request.getServletPath();
       servletPath = servletPath.substring(1);
       servletPath = servletPath.substring(0, servletPath.lastIndexOf(".do"));

       // 获取要执行的方法的controller类对象
       Object controllerBeanObj = beanFactory.getBean(servletPath);

       String operate = request.getParameter("operate");
       if(StringUtils.isEmpty(operate)){
           operate = "index";
       }
       try {
           // 获取controller类对象中要执行的方法
           Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
           for (Method method : methods) {
               if (operate.equals(method.getName())){
                   Parameter[] parameters = method.getParameters();
                   Object[] parametersValues = new Object[parameters.length];
                   for (int i = 0; i < parameters.length; i++) {
                       if ("request".equals(parameters[i].getName())){
                           parametersValues[i] = request;
                       } else if ("response".equals(parameters[i].getName())){
                           parametersValues[i] = response;
                       } else if ("session".equals(parameters[i].getName())){
                           parametersValues[i] = request.getSession();
                       } else {
                           String parameterName = parameters[i].getName();
                           String parameterValue = request.getParameter(parameterName);
                           String typeName = parameters[i].getType().getName();
                           if (parameterValue != null){
                               if ("java.lang.Integer".equals(typeName)){
                                   parametersValues[i] = Integer.parseInt(parameterValue);
                               } else {
                                   parametersValues[i] = parameterValue;
                               }
                           }
                       }
                   }

                   method.setAccessible(true);
                   String methodReturn = (String) method.invoke(controllerBeanObj, parametersValues);
                   if (StringUtils.isEmpty(methodReturn)){
                       return;
                   }
                   if (methodReturn.startsWith("redirect:")){
                       String redirectStr = methodReturn.substring("redirect:".length());
                       response.sendRedirect(redirectStr);
                   } else if (methodReturn.startsWith("json:")){
                       // 此处添加返回值为json的判断,直接发送给前端
                       response.setCharacterEncoding("UTF-8");
                       response.setContentType("application/json;charset=UTF-8");
                       String jsonStr = methodReturn.substring("json:".length());
                       PrintWriter writer = response.getWriter();
                       writer.print(jsonStr);
                       writer.flush();
                   } else{
                       processTemplate(methodReturn, request, response);
                   }
               }
           }
       } catch (Exception e) {
           e.printStackTrace();
           throw new DispatcherServletException("DispatcherServlet出现问题...");
       }
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值