前端学习第三站——Vue2基础篇

目录

1. 环境准备

1.1安装脚手架

1.2 创建项目 

1.3 安装 vue devtools

1.4 运行项目

2 Vue组件 

2.1 文本插值 

2.2 属性绑定 v-bind

2.3 事件绑定 v-on

 2.4 计算属性 computed

2.5 vue 底层展示界面的原理 

 3. Axios

4. 列表渲染v-for和条件渲染 v-if


1. 环境准备

1.1安装脚手架

npm install -g @vue/cli
  • -g 参数表示全局安装,这样在任意目录都可以使用 vue 脚本创建项目

1.2 创建项目 

进入到你要创建服务器的目录下,打开命令窗口,执行命令

vue ui

这个的意思就是使用图形界面向导来创建vue项目,运行后跳出此界面

  选择手动配置项目

 添加 vue router 和 vuex

 选择版本,创建项目

这样就会在文件夹中自动生成vue项目啦~ 

1.3 安装 vue devtools

这是一个可以进行调试vue项目的浏览器插件,很好用

 如果 谷歌应用商店进不去,可以试试以下方法。

1. 先到一个插件下载网址下载插件

下载成功后并解压后就会有下图的.crx后缀文件 。

2. 打开谷歌浏览器, 点击右上角三个点,更多工具——扩展程序 

进入到如下页面,并打开右上角的开发者模式。 

然后将下载好的.crx后缀文件拖进此浏览器页面中即可。 

1.4 运行项目

进入项目目录,prowershell窗口,执行命令

npm run serve

如果报错,尝试用管理员身份运行prowershell,然后进入到项目目录,在执行上述命令。

修改端口

如果前端服务器默认占用了后端 8080 端口,可以进行修改

打开 vue.config.js 文件添加

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  
  // ...
    
  devServer: {
    port: 7070
  }
  
})

添加代理

为了避免前后端服务器联调时, fetch、xhr 请求产生跨域问题,需要配置代理

打开 vue.config.js 文件添加

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
    
  // ...
    
  devServer: {
    port: 7070,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
    
})

 vue的项目结构

  • assets - 静态资源

  • components - 可重用组件

  • router - 路由

  • store - 数据共享

  • views - 视图组件

以后还会添加

  • api - 跟后台交互,发送 fetch、xhr 请求,接收响应

  • plugins - 插件

2 Vue组件 

Vue 的组件文件以 .vue 结尾,每个组件由三部分组成

<template></template>

<script></script>

<style></style>
  • template 模板部分,由它生成 html 代码

  • script 代码部分,控制模板的数据来源和行为

  • style 样式部分,一般不咋关心

入口组件是 App.vue

先删除原有代码,来个 Hello, World 例子

<template>
  <h1>{{msg}}</h1>
</template>

<script>
export default {
  data() {
    return {
      msg: "Hello, Vue!"
    }
  }
}
</script>

解释

  • export default 导出组件对象,供 main.js 导入使用

  • 这个对象有一个 data 方法,返回一个对象,给 template 提供数据

  • {{}} 在 Vue 里称之为插值表达式,用来绑定 data 方法返回的对象属性,绑定的含义是数据发生变化时,页面显示会同步变化

2.1 文本插值 

<template>
    <div>
        <h1>{{ name }}</h1>
        <h1>{{ age > 60 ? '老年' : '青年' }}</h1>
    </div>
</template>
<script>
const options = {
    data: function () {
        return { name: '张三', age: 70 };
    }
};
export default options;
</script>
  • {{}} 里只能绑定一个属性,绑定多个属性需要用多个 {{}} 分别绑定

  • template 内只能有一个根元素

  • 插值内可以进行简单的表达式计算

2.2 属性绑定 v-bind

vue中可以将组件和时间进行绑定。如下代码

<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>
  • 简写方式:可以省略 v-bind 只保留冒号

2.3 事件绑定 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>
  • 简写方式:可以把 v-on: 替换为 @

  • 在 methods 方法中的 this 代表的是 data 函数返回的数据对象

 2.4 计算属性 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;
  • 普通方法调用必须加 (),没有缓存功能

  • 计算属性使用时就把它当属性来用,不加 (),有缓存功能:

    • 一次计算后,会将结果缓存,下次再计算时,只要数据没有变化,不会重新计算,直接返回缓存结果

2.5 vue 底层展示界面的原理 

这里我们那App.vue举例。因为它是vue项目创建出来后自带的

首先,先将App.vue导入到main.js文件中 

 

然后在main.js中执行了new Vue中的render 

那么,这个#app的文件又是啥呢,其实在vue项目中已经给我们默认创建出来了 

如图:

这就是vue将组件渲染到页面上的原理啦~ 

 3. Axios

 axios 它的底层是用了 XMLHttpRequest(xhr)方式发送请求和接收响应,xhr 相对于之前讲过的 fetch api 来说,功能更强大,但由于是比较老的 api,不支持 Promise,axios 对 xhr 进行了封装,使之支持 Promise,并提供了对请求、响应的统一拦截功能

安装

npm install axios -S

导入

import axios from 'axios'
  • axios 默认导出一个对象,这里的 import 导入的就是它默认导出的对象

方法

请求备注
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 - 选项对象、例如查询参数、请求头...

  • data - 请求体数据、最常见的是 json 格式数据

  • get、head 请求无法携带请求体,这应当是浏览器的限制所致(xhr、fetch api 均有限制)

  • options、delete 请求可以通过 config 中的 data 携带请求体

代码演示(这里包含了 发送get、post请求,发送请求头,发送是携带查询参数,用格式urlencode 请求体发送数据,用multipart格式请求体发送数据,用json格式请求体发送数据) 

代码:提示,这里我配置了跨域的问题,/stu 代表localhost:8080,

前端代码:

<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>

后端:

为了方便,代码都写在可Controller层

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可以进行默认配置

创建实例

const _axios = axios.create(config);
  • axios 对象可以直接使用,但使用的是默认的设置

  • 用 axios.create 创建的对象,可以覆盖默认设置,config 见下面说明

常见的 config 项有  

名称含义
baseURL将自动加在 url 前面
headers请求头,类型为简单对象
params跟在 URL 后的请求参数,类型为简单对象或 URLSearchParams
data请求体,类型有简单对象、FormData、URLSearchParams、File 等
withCredentials跨域时是否携带 Cookie 等凭证,默认为 false
responseType响应类型,默认为 json

const _axios = axios.create({
    baseURL: 'http://localhost:8080',
    withCredentials: true
});
await _axios.post('/api/a6set')
await _axios.post('/api/a6get')
  • 生产环境希望 xhr 请求不走代理,可以用 baseURL 统一修改

  • 希望跨域请求携带 cookie,需要配置 withCredentials: true,服务器也要配置 allowCredentials = true,否则浏览器获取跨域返回的 cookie 时会报错

这里提供一个默认写好的axios配置~

myaxios.js文件 (里头有拦截器,包括请求和响应两种拦截器)

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;

我们导入axios就可以这么写

4. 列表渲染v-for和条件渲染 v-if

这里用一个例子来展示

从后端拿去学生数据,展示到前端

<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>

重用组件

主要语法就是在components文件夹中定义一个常用组件,也就是子组件

然后导入到父组件中

<script>
    import MyButton from  '../components/MyButton.vue'
    const options = {
       components:{
           MyButton
       }
    }
    export default options;
</script>

在父组件中打上标签即可。

<template>
<!-- 可重用组件 -->
    <div>
       <h1>父组件</h1>
       <div>
           <h2>子组件</h2>
           <my-button>1</my-button>
       </div>
    </div>
</template>

 具体代码:

子组件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>

父组件:

<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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

芝麻干

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

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

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

打赏作者

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

抵扣说明:

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

余额充值