Table of contents
2.4 Calculated attribute computed
2.5 The principle of Vue's underlying display interface
4. List rendering v-for and conditional rendering v-if
1. Environment preparation
1.1 Install scaffolding
npm install -g @vue/cli
-
The -g parameter means global installation, so that you can use vue scripts to create projects in any directory
1.2 Create a project
Go to the directory where you want to create the server, open the command window, and execute the command
vue ui
This means to use the graphical interface wizard to create a vue project, and jump out of this interface after running
Select manual configuration items
Add vue router and vuex
Select version, create project
This will automatically generate the vue project in the folder~
1.3 install vue devtools
This is a browser plug-in that can debug vue projects, very easy to use
If you can't access the Google Play Store, you can try the following methods.
1. Go to a plug-in download website first to download the plug-in
After successful download and decompression, there will be a .crx suffix file as shown below.
2. Open Google Chrome, click the three dots in the upper right corner, more tools - extensions
Go to the following page and turn on the developer mode in the upper right corner.
Then drag the downloaded .crx suffix file into this browser page.
1.4 Running the project
Enter the project directory, prowershell window, execute the command
npm run serve
If an error is reported, try to run prowershell as an administrator, then enter the project directory, and execute the above command.
modify port
If the front-end server occupies the back-end port 8080 by default, it can be modified
Open the vue.config.js file to add
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
// ...
devServer: {
port: 7070
}
})
add agent
In order to avoid cross-domain problems caused by fetch and xhr requests when the front-end and back-end servers are jointly debugged , a proxy needs to be configured
Open the vue.config.js file to add
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
// ...
devServer: {
port: 7070,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
})
Vue's project structure
-
assets - static resources
-
components - Reusable components
-
router - router
-
store - data sharing
-
views - the view components
will be added later
-
api - Interact with the background, send fetch and xhr requests, and receive responses
-
plugins - plugins
2 Vue components
Vue's component files end with .vue, and each component consists of three parts
<template></template>
<script></script>
<style></style>
-
template template part, which generates html code
-
The script code section controls the data source and behavior of the template
-
style The style part, generally don't care about it
The entry component is App.vue
First delete the original code, let's take a Hello, World example
<template>
<h1>{
{msg}}</h1>
</template>
<script>
export default {
data() {
return {
msg: "Hello, Vue!"
}
}
}
</script>
explain
-
export default export component object for main.js import
-
This object has a data method that returns an object that provides data to the template
-
{ {}}
In Vue, it is called an interpolation expression , which is used to bind the object properties returned by the data method . The meaning of binding is that when the data changes, the page display will change synchronously
2.1 Text Interpolation
<template>
<div>
<h1>{
{ name }}</h1>
<h1>{
{ age > 60 ? '老年' : '青年' }}</h1>
</div>
</template>
<script>
const options = {
data: function () {
return { name: '张三', age: 70 };
}
};
export default options;
</script>
-
{ {}}
Only one property can be bound here, multiple properties need to be{ {}}
bound separately -
There can only be one root element in the template
-
Simple expression calculations can be performed within interpolation
2.2 Attribute binding v-bind
Vue can bind components and time. The following code
<template>
<div>
<!-- 属性绑定 v-bind -->
<div><input type="text" v-bind:value="name"/></div>
<div><input type="date" v-bind:value="birthday"/></div>
<!-- v-bind简写 v-bind可以省略 -->
<div><input type="text" :value="age"/></div>
</div>
</template>
<script>
const options = {
data:function(){
return {name: '张三',birthday: '1995-01-01',age: 18}
}
}
export default options;
</script>
-
Shorthand: you can omit v-bind and only keep the colon
2.3 Event binding v-on
<!-- 事件绑定 -->
<template>
<div>
<div><input type="button" value="点我执行m1" v-on:click="m1"></div>
<div><input type="button" value="点我执行m2" @click="m2"></div>
<div>{
{count}}</div>
</div>
</template>
<script>
const options = {
data: function () {
return { count: 0 };
},
methods: {
m1() {
this.count ++;
console.log("m1")
},
m2() {
this.count --;
console.log("m2")
}
}
};
export default options;
</script>
-
Shorthand: You can replace v-on: with @
-
This in the methods method represents the data object returned by the data function
2.4 Calculated attribute computed
<!-- 计算属性 -->
<template>
<div>
<h2>{
{fullName}}</h2>
<h2>{
{fullName}}</h2>
<h2>{
{fullName}}</h2>
</div>
</template>
<script>
const options = {
data: function () {
return { firstName: '三', lastName: '张' };
},
/* methods: {
fullName() {
console.log('进入了 fullName')
return this.lastName + this.firstName;
}
},*/
computed: {
fullName() {
console.log('进入了 fullName')
return this.lastName + this.firstName;
}
}
};
export default options;
-
Ordinary method calls must add (), there is no caching function
-
When the calculated attribute is used, it is used as an attribute without adding (), and it has a cache function:
-
After a calculation, the result will be cached. When the calculation is performed next time, as long as the data has not changed, the calculation will not be recalculated, and the cached result will be returned directly.
-
2.5 The principle of Vue's underlying display interface
Here is an example of our App.vue. Because it comes with the vue project after it is created
First, import App.vue into the main.js file
Then execute the render in new Vue in main.js
So, what is this #app file? In fact, it has been created by default for us in the vue project
As shown in the picture:
This is how vue renders components to the page~
3. Axios
The bottom layer of axios is to use XMLHttpRequest (xhr) to send requests and receive responses. Compared with the fetch api mentioned earlier, xhr has more powerful functions, but because it is an older api, it does not support Promise. Axios implements xhr for xhr Encapsulate, make it support Promise, and provide a unified interception function for requests and responses
Install
npm install axios -S
import
import axios from 'axios'
-
Axios exports an object by default, and the import here imports the object it exports by default
method
ask | Remark |
---|---|
axios.get(url[, config]) | ⭐️ |
axios.delete(url[, config]) | |
axios.head(url[, config]) | |
axios.options(url[, config]) | |
axios.post(url[, data[, config]]) | ⭐️ |
axios.put(url[, data[, config]]) | |
axios.patch(url[, data[, config]]) |
-
config - options object, e.g. query parameters, request headers...
-
data - request body data, most commonly in json format
-
Get and head requests cannot carry the request body, which should be caused by browser restrictions (xhr and fetch api have restrictions)
-
options and delete requests can carry the request body through the data in config
Code demonstration (this includes sending get and post requests, sending request headers, sending with query parameters, sending data with a request body in urlencode format, sending data with a request body in multipart format, and sending data with a request body in json format)
Code: Prompt, here I have configured cross-domain issues, /stu represents localhost:8080,
Front-end code:
<template>
<!-- axios -->
<div>
{
{data}}
<input type="button" value="点击获取数据" @click="sendAsync">
</div>
</template>
<script>
/* 导入axios */
import axios from '../util/myaxios'
const options = {
data:function(){
return { data:''}
},
methods: {
async sendAsync(){
// 发送 get请求
// const resp = await axios.get('stu/a1');
//发送 post请求,需要携带一个参数对象,这里为空对象
// const resp = await axios.post('stu/a2',{});
//发送请求头,除了请求地址,参数,还有config对象
// const resp = await axios.post('stu/a3',{},{
// headers:{
// Authorization: 'authorization'
// }
// });
//发送请求时携带查询参数, url?name=xxx&age=xxx
/* 第一种方法,拼接字符串 */
// const name = encodeURIComponent('&&&');
// const age = 18;
// const resp = await axios.post(`stu/a4?name=${name}&age=${age}`);
/* 只是这种方法对于特殊字符串,需要重新编码 ,例如&&&*/
/*
第二种方法,就是在config对象中设置param对象
这种方法会自动对特殊字符进行重新编码
*/
// const resp = await axios.post('stu/a4',{},{
// params:{
// name: '&&&',
// age: 18
// }
// });
//用请求体发送数据,格式为urlencoded
// const param = new URLSearchParams();
// param.append('name','张三');
// param.append('age','18');
// const resp = await axios.post('stu/a4',param);
//用请求体发送数据,格式为 multipart
// const param = new FormData();
// param.append('name','李四');
// param.append('age','19');
// const resp = await axios.post('stu/a4',param);
// //用请求体发送数据,格式为json,需在后端加上@RequestBody注解
// const resp = await axios.post('stu/a5',{
// name: '张三',
// age: 18
// });
this.data = resp.data
console.log(resp)
},
sendSync(){
axios.get('stu/student')
.then(resp =>{
console.log(resp)
this.data = resp.data
})
}
}
}
export default options;
</script>
rear end:
For convenience, the code is written in the Controller layer
package com.hua.controller;
import com.hua.domain.A5;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
/**
* @Author 华子
* @Date 2023/1/8 15:51
* @Version 1.0
*/
@Controller
@RequestMapping("stu")
public class AxiosController {
@GetMapping("/a1")
@ResponseBody
public String a1(){
return "get request";
}
@PostMapping("/a2")
@ResponseBody
public String a2(){
return "post request";
}
@PostMapping("/a3")
@ResponseBody
public String a3(@RequestHeader("Authorization") String authorization){
System.out.println("Authorization:"+authorization);
return "post request";
}
@PostMapping("/a4")
@ResponseBody
public String a4(String name, Integer age){
System.out.println("name:"+name+",age:"+age);
return "post request";
}
@PostMapping("/a5")
@ResponseBody
public String a5(@RequestBody A5 a5){
System.out.println("name:"+a5.getName()+",age:"+a5.getAge());
return "post request";
}
@PostMapping("/a6Set")
@ResponseBody
public String a6Set(HttpSession session){
System.out.println("==========a6Set==============");
System.out.println(session.getId());
session.setAttribute("name","张三");
return "post request";
}
@PostMapping("/a6Get")
@ResponseBody
public String a6Get(HttpSession session){
System.out.println("==========a6Get==============");
System.out.println(session.getId());
System.out.println(session.getAttribute("name"));
return "post request";
}
}
Axios can be configured by default
create instance
const _axios = axios.create(config);
-
The axios object can be used directly, but the default settings are used
-
Objects created with axios.create can override the default settings, see the description below for config
Common config items are
name | meaning |
---|---|
baseURL | will be automatically added in front of the url |
headers | request header, the type is a simple object |
params | The request parameters following the URL, of type Simple Object or URLSearchParams |
data | Request body, types include simple object, FormData, URLSearchParams, File, etc. |
withCredentials | Whether to carry credentials such as cookies when crossing domains, the default is false |
responseType | Response type, default is json |
example
const _axios = axios.create({
baseURL: 'http://localhost:8080',
withCredentials: true
});
await _axios.post('/api/a6set')
await _axios.post('/api/a6get')
-
In the production environment, it is hoped that the xhr request will not go through the proxy, and can be modified uniformly with baseURL
-
If you want to carry cookies in cross-domain requests, you need to configure withCredentials: true, and the server must also configure allowCredentials = true, otherwise the browser will report an error when obtaining the cookie returned by cross-domain
Here is a default written axios configuration~
myaxios.js file (there are interceptors inside, including request and response interceptors)
import axios from 'axios'
const _axios = axios.create({
// baseURL: 'http://localhost:8080',
withCredentials: true
});
// 9. 拦截器
_axios.interceptors.request.use(
function (config) {
// 比如在这里添加统一的 headers
config.headers = {
Authorization: 'aaa.bbb.ccc'
}
return config;
},
function (error) {
return Promise.reject(error);
}
);
_axios.interceptors.response.use(
function (response) {
// 2xx 范围内走这里
return response;
},
function (error) {
if (error.response?.status === 400) {
console.log('请求参数不正确');
return Promise.resolve(400);
} else if (error.response?.status === 401) {
console.log('跳转至登录页面');
return Promise.resolve(401);
} else if (error.response?.status === 404) {
console.log('资源未找到');
return Promise.resolve(404);
}
// 超出 2xx, 比如 4xx, 5xx 走这里
return Promise.reject(error);
}
);
export default _axios;
We can write this by importing axios
4. List rendering v-for and conditional rendering v-if
Here is an example to show
Take the student data from the back end and display it to the front end
<template>
<div>
<!-- <input type="button" value="点击获取数据" @click="sendAsync"> -->
<div class="title">学生列表</div>
<div class="thead">
<div class="row bold">
<div class="col">编号</div>
<div class="col">姓名</div>
<div class="col">性别</div>
<div class="col">年龄</div>
</div>
</div>
<div class="tbody">
<!-- 条件渲染v-if:如果读取到学生数据不为空,就展示 -->
<div v-if="students.length > 0">
<!-- 列表渲染v-for,循环得到的学生数据并展示 -->
<div class="row" v-for="student of students" :keys="student.id">
<div class="col">{
{student.id}}</div>
<div class="col">{
{student.name}}</div>
<div class="col">{
{student.sex}}</div>
<div class="col">{
{student.age}}</div>
</div>
</div>
<!-- 如果为空 -->
<div class="row" v-else>暂无学生数据</div>
</div>
</div>
</template>
<script>
import axios from '../util/myaxios'
const options = {
mounted: function(){
this.sendAsync();
},
data: function(){
return {
students: []
}
},
methods:{
async sendAsync(){
const resp = await axios.get('stu/student');
console.log(resp.data.data)
this.students = resp.data.data
}
}
}
export default options;
</script>
<style scoped>
div {
font-family: 华文行楷;
font-size: 20px;
}
.title {
margin-bottom: 10px;
font-size: 30px;
color: #333;
text-align: center;
}
.row {
background-color: #fff;
display: flex;
justify-content: center;
}
.col {
border: 1px solid #f0f0f0;
width: 15%;
height: 35px;
text-align: center;
line-height: 35px;
}
.bold .col {
background-color: #f1f1f1;
}
</style>
Reuse components :
The main syntax is to define a common component in the components folder, that is, a subcomponent
Then import it into the parent component
<script>
import MyButton from '../components/MyButton.vue'
const options = {
components:{
MyButton
}
}
export default options;
</script>
Label it in the parent component.
<template>
<!-- 可重用组件 -->
<div>
<h1>父组件</h1>
<div>
<h2>子组件</h2>
<my-button>1</my-button>
</div>
</div>
</template>
Specific code:
Subcomponent MyButtons:
<template>
<div class="button" :class="[type,size]">
<slot></slot><!-- 加入插槽,可以在父组件中添加按钮名称 -->
</div>
</template>
<script>
const options = {
props:["type","size"]
};
export default options;
</script>
<style scoped>
.button {
display: inline-block;
text-align: center;
border-radius: 30px;
margin: 5px;
font: bold 12px/25px Arial, sans-serif;
padding: 0 2px;
text-shadow: 1px 1px 1px rgba(255, 255, 255, .22);
box-shadow: 1px 1px 1px rgba(0, 0, 0, .29), inset 1px 1px 1px rgba(255, 255, 255, .44);
transition: all 0.15s ease;
}
.button:hover {
box-shadow: 1px 1px 1px rgba(0, 0, 0, .29), inset 1px 1px 2px rgba(0, 0, 0, .5);
}
.button:active {
box-shadow: inset 1px 1px 2px rgba(0, 0, 0, .8);
}
.primary {
background-color: #1d6ef9;
color: #b5e3f1;
}
.danger {
background-color: rgb(196, 50, 50);
color: white;
}
.success {
background-color: #a5cd4e;
;
color: #3e5706;
}
.small {
width: 40px;
height: 20px;
font-size: 10px;
line-height: 20px;
}
.middle {
width: 50px;
height: 25px;
font-size: 14px;
line-height: 25px;
}
.large {
width: 60px;
height: 30px;
font-size: 18px;
line-height: 30px;
}
</style>
parent component:
<template>
<!-- 可重用组件 -->
<div>
<h1>父组件</h1>
<div>
<h2>子组件</h2>
<my-button type="primary" size="small">1</my-button>
<my-button type="danger" size="middle">2</my-button>
<my-button type="success" size="large">3</my-button>
</div>
</div>
</template>
<script>
import MyButton from '../components/MyButton.vue'
const options = {
components:{
MyButton
}
}
export default options;
</script>
<style scoped>
.button {
display: inline-block;
text-align: center;
border-radius: 30px;
margin: 5px;
font: bold 12px/25px Arial, sans-serif;
padding: 0 2px;
text-shadow: 1px 1px 1px rgba(255, 255, 255, .22);
box-shadow: 1px 1px 1px rgba(0, 0, 0, .29), inset 1px 1px 1px rgba(255, 255, 255, .44);
transition: all 0.15s ease;
}
.button:hover {
box-shadow: 1px 1px 1px rgba(0, 0, 0, .29), inset 1px 1px 2px rgba(0, 0, 0, .5);
}
.button:active {
box-shadow: inset 1px 1px 2px rgba(0, 0, 0, .8);
}
.primary {
background-color: #1d6ef9;
color: #b5e3f1;
}
.danger {
background-color: rgb(196, 50, 50);
color: white;
}
.success {
background-color: #a5cd4e;
;
color: #3e5706;
}
.small {
width: 40px;
height: 20px;
font-size: 10px;
line-height: 20px;
}
.middle {
width: 50px;
height: 25px;
font-size: 14px;
line-height: 25px;
}
.large {
width: 60px;
height: 30px;
font-size: 18px;
line-height: 30px;
}
</style>