前端框架Vue

tech2026-10-09  4

VUE简介

Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。同时,Vue 也完全有能力支持采用SPA设计和组合其他Vue生态的系统。 官网:https://cn.vuejs.org/

前端框架三巨头:Vue.js、React.js、AngularJS。vue.js以期轻量易用著称,vue.js和React.js发展速度最快。

Node和NPM

NPM是Node提供的模块管理工具,可以非常方便的下载安装很多前端框架,包括JQuery、AngularJS、VueJs等。为了后面学习方便,我们先安装Node及NPM工具。可以在Node的官网下载Node客户端,自带NPM。Node官网:http://nodejs.cn/

下载Node后安装,安装完成后可以在控制台查看版本信息

# 查看node版本 node -v # 查看npm版本 npm -v

由于NPM的仓库源在国外,国内访问会比较慢,建议使用淘宝镜像源的cnpm。

# 安装cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org 谷歌中可以安装Vue.js Devtools插件,后面就可以在Chrome调试工具中查看组件状态。

idea使用需要引入

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

使用NPM

在IDEA的Terminal中输入 npm init -y

通过npm工具来初始化vue项目的开发环境,此时会在项目中生成一个package.json文件

安装Vue,输入命令

npm install vue --save

会在项目中发现一个node_modules目录,并且在下面有一个vue目录,并且此时json文件会发生变化

需要idea安装 vue

示例

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello Vue</title> </head> <body> <div id="app"> <h1>{{name}},你好。</h1> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: {name: "Tom"} }) </script> </body> </html>

Vue语法

插值绑定

插值表达式

格式:

{{表达式}}

说明:

该表达式支持JS语法,可以调用js内置函数(必须有返回值)表达式必须有返回结果。例如 1 + 1,没有结果的表达式不允许使用,如:var a = 1 + 1;可以直接获取Vue实例中定义的数据或函数

示例:

<div id="app">{{name}}</div> <script type="text/javascript"> var app = new Vue({ el: "#app", data: { name: "Jack" } }) </script>

插值闪烁

​ 使用{{}}方式在网速较慢时会出现问题。在数据未加载完成时,页面会显示出原始的 {{}} ,加载完毕后才显示正确数据,我们称为插值闪烁。 ​ 我们将网速调慢一些,然后试试看刚才的案例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zy5fjEDa-1599211623514)(vue.assets/image-20200616171126746.png)]

v-text和v-html

使用v-text和v-html指令来替代 {{}} 说明:

v-text:将数据输出到元素内部,如果输出的数据有HTML代码,会作为普通文本输出v-html:将数据输出到元素内部,如果输出的数据有HTML代码,会被渲染

示例:

<div id="app"> v-text:<span v-text="hello"></span> <br/> v-html:<span v-html="hello"></span> </div> <script type="text/javascript"> var vm = new Vue({ el:"#app", data:{ hello: "<h1>大家好,我是张三</h1>" } }) </script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Zw03u27-1599211623516)(vue.assets/image-20200616171411611.png)]

属性绑定

指令v-bind

DOM节点的属性基本都可以使用v-bind进行绑定

示例:

<style> .italic { font-style: italic; } </style> <div id="app"> <p v-bind:class="className" v-bind:title="title">点击有奖</p> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { className: 'italic', title: '不要点击' } }) </script>

v-bind也可以省略不写

<div id="app"> <p :class="className" v-bind:title="title">点击有奖</p> </div>

class和style绑定

可以通过字符串、数组和对象三种方式动态绑定,示例如下:

<style> .color-gray { color: gray; } .size-18 { font-size: 18px; } .style-italic { font-style: italic; } </style> <div id="app"> <p :class="class1">字符串绑定</p> <p :class="class2">数组绑定</p> <p :class="class3">对象绑定</p> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { class1: 'italic', class2: ['color-gray', 'size-18'], class3: { 'color-gray': true, 'size-18': true, 'style-italic': true } } }) </script>

事件绑定

指令v-on

v-on指令用于给页面元素绑定事件。

语法:

v-on:事件名="js片段或函数名"

简写语法:

@事件名="js片段或函数名"

示例:

<div id="app"> <!--事件中直接写js片段--> <button @click="num++">增加</button><br/> <!--事件指定一个回调函数,必须是Vue实例中定义的函数--> <button @click="decrement">减少</button><br/> <h1>num: {{num}}</h1> </div> <script type="text/javascript"> var app = new Vue({ el:"#app", data:{ num:1 }, methods:{ decrement(){ this.num--; } } }) </script>

事件修饰符

在事件处理程序中调用 event.preventDefault() (阻止节点默认行为)或 event.stopPropagation() (阻止事件冒泡)是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理DOM 事件细节。 为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。修饰符是由点开头的指令后缀来表示的。

.stop :阻止事件冒泡.prevent :阻止默认事件发生.capture :使用事件捕获模式.self :只有元素自身触发事件才执行。(冒泡或捕获的都不执行).once :只执行一次

示例:

<div id="app"> <button @click.once="add">点赞</button><br/> <h1>num: {{num}}</h1> </div> <script type="text/javascript"> var app = new Vue({ el:"#app", data:{ num:1 }, methods:{ add(){ setTimeout(function () { },1000); this.num++; } } }) </script>

这里设置点赞按钮只能点击一次,再次点击没有不会新增num。

双向绑定

指令v-model

可以用 v-model 指令在表单元素上创建双向数据绑定。支持的元素如下:

inputtexttextareacheckboxradioselect

示例:

<div id="app"> <input type="text" v-model="language" value="Java" /><br/> <input type="textarea" v-model="language" value="PHP" /><br/> <h1> 你选择了:{{language}} </h1> </div> <script type="text/javascript"> var vm = new Vue({ el:"#app", data:{ language: "Java" } }) </script>

条件渲染和列表渲染

指令v-if和v-show

v-if,顾名思义,条件判断。当得到结果为true时,所在的元素才会被渲染。

示例:

<div id="app"> <!--事件中直接写js片段--> <button v-on:click="show = !show">点击切换</button> <br/> <h1 v-if="show"> 你好 </h1> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { show: true } }) </script>

v-else-if和v-else ,配合v-if 使用

<div id="app"> <select v-model="selected"> <option disabled value="">请选择</option> <option>Java</option> <option>Python</option> <option>PHP</option> </select> <h2 v-if="selected == 'Java'">明智的选择</h2> <h2 v-else-if="selected == 'Python'">不错的选择</h2> <h2 v-else="selected == 'PHP'">老掉牙的选择</h2> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { selected: 'Java' } }) </script>

v-show

带有 v-show 的元素始终会被渲染并保留在 DOM 中,v-show 只是简单地切换元素的 CSS 属性 display 。

<div id="app"> <!--事件中直接写js片段--> <button v-on:click="show = !show">点击切换</button> <br/> <h1 v-show="show"> 你好 </h1> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { show: true } }) </script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7NgQsF0k-1599211623518)(vue.assets/image-20200616201949315.png)]

指令v-for

遍历数据渲染页面是非常常用的需求,Vue中通过v-for指令来实现。

语法:

v-for=item in items v-for=item of items items:要遍历的数组,需要在vue的data中定义好。item:迭代得到的数组元素的别名

示例:

<div id="app"> <ul> <li v-for="user in users"> {{user.name}} : {{user.gender}} : {{user.age}} </li> </ul> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { users: [ {name: '柳岩', gender: '女', age: 21}, {name: '张三', gender: '男', age: 30}, {name: '范冰冰', gender: '女', age: 24}, {name: '刘亦菲', gender: '女', age: 18}, {name: '古力娜扎', gender: '女', age: 25} ] } }) </script>

也可以使用数组下标

语法

v-for="(item,index) in items" items:要迭代的数组item:迭代得到的数组元素别名index:迭代到的当前元素索引,从0开始。

示例:

<div id="app"> <ul> <li v-for="(user,index) in users"> {{index}} - {{user.name}} : {{user.gender}} : {{user.age}} </li> </ul> </div>

v-for除了可以迭代数组,也可以迭代对象。

语法

v-for="value in object" v-for="(value,key) in object" v-for="(value,key,index) in object"

示例:

<div id="app"> <ul> <li v-for="(value,key,index) in user"> {{index}} - {{key}} : {{value}} </li> </ul> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { user:{name:'柳岩', gender:'女', age: 21} } }) </script>

列表渲染中的key

​ 当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。 ​ 这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。

​ 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的且唯一的 id。

示例:

<div id="app"> <button @click="reverse">倒转数组</button> <ul id="userList"> <li v-for="(user,index) in users" :key="user.uid"> {{user.uid}} - {{user.name}} : {{user.gender}} : {{user.age}} </li> </ul> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { users: [ {uid:1,name: '柳岩', gender: '女', age: 21}, {uid:2,name: '张三', gender: '男', age: 30}, {uid:3,name: '范冰冰', gender: '女', age: 24}, {uid:4,name: '刘亦菲', gender: '女', age: 18}, {uid:5,name: '古力娜扎', gender: '女', age: 25} ] }, methods:{ reverse:function () { this.users.reverse(); } } }) </script>

​ 这里使用了一个特殊语法: :key="" 可以读取vue中的属性,并赋值给key属性,这里我们绑定的key是数组的索引,是唯一的 。

计算属性

在插值表达式中使用js表达式是非常方便的,而且也经常被用到。 但是如果表达式的内容很长,就会显得不够优雅,而且后期维护起来也不方便,例如下面的场景,我们 有一个日期的数据,但是是毫秒值:

data:{ birthday:1529032123201 // 毫秒值 }

我们在页面渲染,希望得到yyyy-MM-dd的样式:

<h1>您的生日是:{{ new Date(birthday).getFullYear() + '-'+ new Date(birthday).getMonth()+ '-' + new Date(birthday).getDay() }} </h1>

虽然能得到结果,但是非常麻烦。 Vue中提供了计算属性,来替代复杂的表达式:

<div id="app"> <h1>您的生日是:{{birth}} </h1> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { birthday: 1429032123201 // 毫秒值 }, computed: { birth() {// 计算属性本质是一个方法,但是必须返回结果 var d = new Date(this.birthday); return d.getFullYear() + "-" + d.getMonth() + "-" + d.getDay(); } } }) </script>

侦听属性

watch可以让我们监控一个值的变化。从而做出相应的反应。

示例:

<div id="app"> <input type="text" v-model="message"> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { message: "" }, watch: { message(newVal, oldVal) { console.log(newVal, oldVal); } } }) </script>

如果监控的是一个对象,需要进行深度监控,才能监控到对象中属性的变化,例如:

<div id="app"> 姓名:<input type="text" v-model="person.name"> <br> 年龄:<input type="text" v-model="person.age"> <button @click="person.age++">+ </button> <br> <h1> {{person.name}}今年{{person.age}}岁了。 </h1> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { person: { name: "Jack", age: 21 } }, watch: { // // 无效 // person(newVal, oldVal) { // console.log(newVal, oldVal); // } person: { deep: true, // 开启深度监控,可以监控到对象中属性变化 handler(val) { // 定义监控到以后的处理方法 console.log(val.name + ": " + val.age); } } } }) </script>

组件化

​ 在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会 有相同的头部导航。 ​ 但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以我们会把页面的不同部分拆分成独 立的组件,然后在不同页面就可以共享这些组件,避免重复开发。

定义全局组件

我们通过Vue的component方法来定义一个全局组件。

<div id="app"> <!--使用定义好的全局组件--> <counter></counter> </div> <script src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> // 定义全局组件,两个参数:1,组件名称。2,组件参数 Vue.component("counter", { template: '<button v-on:click="count++">你点了我 {{ count }} 次,我记住了. </button>', data() { return { count: 0 } } }) var vm = new Vue({ el: "#app" }) </script> 组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。组件渲染需要html模板,所以增加了template属性,值就是HTML模板全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。data的定义方式比较特殊,必须是一个函数。

组件的复用

定义好的组件,可以任意复用多次:

<div id="app"> <!--使用定义好的全局组件--> <counter></counter> <counter></counter> <counter></counter> </div>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FkCGpHzA-1599211623519)(vue.assets/image-20200616212533468.png)]

会发现每个组件互不干扰,都有自己的count值。怎么实现的?

组件的data属性必须是函数!

当我们定义这个 组件时,它的data 并不是像这样直接提供一个对象:

data: { count: 0 }

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () { return { count: 0 } }

如果 Vue 没有这条规则,点击一个按钮就会影响到其它所有实例!

Spring Boot + Vue 项目案例

搭建SpringBoot项目

添加pom依赖

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>

添加启动类

@SpringBootApplication public class DemoSpringBootVueApplication { public static void main(String[] args) { SpringApplication.run(DemoSpringBootVueApplication.class, args); } }

添加配置文件application.yml

server: port: 8080 spring: application: name: user-service jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

添加实体类

@Data @NoArgsConstructor @AllArgsConstructor public class User { private Long id; private String userName; private String password; private String sex; private Integer age; private Date createTime; }

添加Controller

@RestController @RequestMapping("/user") public class UserController { static User[] userArray = { new User(1L, "欧阳娜娜", "123456", "女", 18, new Date()), new User(2L, "迪丽热巴", "123456", "女", 19, new Date()), new User(3L, "古力娜扎", "123456", "女", 20, new Date()), new User(4L, "易烊千玺", "123456", "男", 21, new Date()), new User(5L, "诸葛孔明", "123456", "男", 50, new Date()) }; static List<User> users = Arrays.asList(userArray); @GetMapping public List<User> list() { return users; } @GetMapping("/{id}") public User getById(@PathVariable Long id) { Stream<User> userStream = users.stream().filter(s -> s.getId().equals(id)); Optional<User> optionalUser = userStream.findAny(); if (optionalUser.isPresent()) { return optionalUser.get(); } return null; } @PutMapping("/{id}") public void updateById(@PathVariable Long id, @RequestBody User user) { for(int i=0 ;i<users.size();i++){ User user1 = users.get(i); if(user1.getId()==id){ BeanUtils.copyProperties(user,user1); } } } }

将资料中的static文件夹复制到项目的resources中,打开user.html,

看下最外层的div

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U5gpenZi-1599211623521)(vue.assets/image-20200617103931354.png)]

列表的绑定如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V9zlwfZ8-1599211623522)(vue.assets/image-20200617103837618.png)]

编辑页面绑定如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-98FyP6vq-1599211623523)(vue.assets/image-20200617104501066.png)]

页面引用了user.js,目前是空的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4lC2wEYm-1599211623530)(vue.assets/image-20200617104051461.png)]

在user.js中编写代码

var vue = new Vue({ //可以给Vue对象起名 el: "#app", data: { userList: {}, user: {} }, methods: { findAll() { //axios是发送ajax的工具,get就是get请求 post... axios.get("/user").then(function (result) { //then是返回值,封装一个回调函数 //result.data 才是真正controller返回的数据 vue.userList = result.data; }) }, findById(id) { $("#myModal").modal("show"); //把模态窗口 弹出框 显示出来 axios.get("/user/" + id).then(function (result) { vue.user = result.data; }) }, update() { axios.put("/user/"+vue.user.id, vue.user).then(function (result) { console.log(result.data); vue.findAll();//重新调用列表接口 ,就刷新页面了 }) } }, created() { //页面加载之前,就调用controller,把userList中的数据填充完整。。。。 this.findAll(); } })

打开浏览器访问:http://localhost:8080/user.html

最新回复(0)