Vue基础¶
1. vue.js的快速入门使用¶
1.1 vue.js库的下载¶
vue.js是目前前端web开发最流行的工具库/框架,由尤雨溪在2014年2月发布的。
另外几个常见的工具库:react.js、vue.js、angular.js、jQuery
官方网站:https://cn.vuejs.org/
官方文档:https://v3.cn.vuejs.org/guide/introduction.html
vuejs目前有1.x、2.x和3.x 版本,我们学习3.x版本的。
jQuery和vue的定位是不一样的。
1.2 vuejs的使用¶
vuejs的使用官方提供了2种方式:
-
基于脚本导入使用,下载vue.js文件 或者网上别人保存在CDN服务器的vue.js文件,通过script标签引入到html网页。
-
基于项目构建工具来进行使用,需要安装项目构建工具,自动构建成一个独立的项目。
目前官方推荐的项目构建工具:npm、vue-cli、vite。
我们先通过vuejs的第1种方式来完成vue语法和基础内容的学习。这种方式的使用,vue的引入类似于jQuery,开发中可以使用开发版本vue-x.x.x.js,产品上线要换成vue.min.js。
github: https://github.com/vuejs/vue-next/tags
下载地址:https://v3.cn.vuejs.org/guide/introduction.html
https://cdn.jsdelivr.net/npm/vue@next/dist/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 1. 引入vue.js文件 -->
<script src="./assets/vue.global.3.2.20.js"></script>
</head>
<body>
{{message}} <!-- vue的模板标签,也叫指令,专门可以输出data里面的变量 -->
<div id="app"> <!-- 2. 创建或绑定一个vue操作的标签 -->
<p>{{message}}</p>
<a :href="url">{{title}}</a>
</div>
<hr>
<div id="demo">
{{message}}<br>
{{num}}
</div>
<script>
// 3. 基于Vue.createApp实例化一个vue管理对象-vm
var vm = Vue.createApp({
data(){ // 4. data是必填参数,表示vue要输出到控制标签中的内容或属性
return {
message: "hello",
url: "https://www.baidu.com",
title: "百度",
}
}
}).mount("#app"); // 5. 通过vm对象的mount把html标签与vm对象进行捆绑
var vm2 = Vue.createApp({
data(){
return {
message: "nihao!!",
num: 2300,
}
}
}).mount("#demo")
</script>
</body>
</html>
总结:
//1. vue的使用要从创建Vue管理对象开始,一个html页面中可以创建多个vm对象,每个vm之间互不干扰。
var vm = Vue.createApp({});
//2. 创建vue对象的时候,需要传递选项参数,选项参数就是json对象,json对象必须有data成员
var vm = Vue.createApp({
data(){
return {}
},
}).mount("#app");
//mount:设置vue可以操作的html内容范围,值一般就是css的id选择器。
//data: 保存vue.js中要显示到html页面的数据。
//3. vue.js要控制的内容范围,必须先通过id来设置。
<div id="app">
<h1>{{message}}</h1>
<p>{{message}}</p>
</div>
1.2.1 调试工具Vue Devtools的安装¶
- 官网地址:https://v3.cn.vuejs.org/guide/installation.html#%E5%8F%91%E5%B8%83%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
- 下载回来的压缩包,复制到ubuntu或者开发的系统下用户家目录下进行解压,然后打开谷歌浏览器,在地址栏上输入
chrome://extensions/,勾选开发者模式。
- 选择左边的加载已解压的扩展程序,就是刚才复制到ubuntu下的扩展目录,接着点击当前目录右上角的打开。
- 完成了步骤以后,就可以看到谷歌浏览器已经成功安装了。效果如下:
- 在使用vue的页面时,vue图标效果如下:
- 最后要注意!当前vue插件所在的目录绝对不能删除!!删了这个目录,则插件也没有了。所以自己找一个保存文件的目录把插件放进去再安装到谷歌浏览器中。
1.3 vue.js的M-V-VM思想¶
MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,是一种代码分工思想来的。
Model 指代的就是vue对象的data选项里面的数据。这里的数据要显示到HTML页面中。
View 指代的就是vue中数据要显示的HTML页面,也称之为“视图模板” 。
ViewModel 指代的是vue.js中Vue.createApp创建实例对象vm了,它是vue.js的核心,负责连接 View 和 Model,保证视图的变量值和data选项中变量的值一致性,所以前面代码中,data选项里面的数据被显示中p标签中就是vm对象自动完成的。vm对象会时刻的监控View和Model的变化,并保持双方数据的一致性!!!有时候,这个特性也叫双向数据绑定
编写代码,让我们更加清晰的了解MVVM:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<p>{{num}}</p>
<input type="text" v-model="num"><button @click="add">+</button>
</div>
<script>
// M-V-VM中的VM就是Vue.createApp的返回值
var vm = Vue.createApp({
data(){ // data里面的返回值对象就是Model数据模型
return {
num: 10,
}
},
methods:{
add(){
this.num++;
console.log(this.num); // this代表的vm对象,vm对象在运行时会自动把methods和data的变量与函数作为自己的属性和方法
this.test()
},
test(){
console.log("test方法执行了")
}
}
}).mount("#app"); // mount绑定的就是vue的View视图模板
</script>
</body>
</html>
1.4 VM常用属性操作¶
在浏览器中可以在 console.log通过 vm对象可以直接访问el和data属性,甚至可以访问data里面的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<p >{{num}}</p>
<p ref="p3">{{num}}</p>
<p>{{num}}</p>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
num: 100,
msg: "hello"
}
}
}).mount("#app");
console.log(vm)
// 传递到vm对象中的data数据
console.log(vm.$data)
// 实例化了vm对象以后,调用mount绑定的可控视图范围
console.log(vm.$el)
console.log(vm.$el.parentElement); // 获取当前绑定vm对象的HTML标签,<div id="app" data-v-app>100</div>
// 在绑定标签范围内,要抓取元素,通过$refs来抓取
console.log(vm.$refs);
console.log(vm.$refs.p3);
vm.$refs.p3.innerHTML = "hello!!!!";
</script>
</body>
</html>
总结
createApp中的data选项,传递到vm对象内部时,已经变成了使用Proxy对象代理的属性,可以通过
vm.$data来查询。vm对象在经过调用mount方法绑定HTML标签以后,这个标签代表的就是当前vm对象的可控范围,可以通过
vm.$el属性来查询,通过要获取绑定vm的HTML元素,可以通过
vm.$el.parentElement来获取。
- vm对象提供$refs可以让我们开发者直接在vm控制的视图范围,使用
ref属性绑定任意元素,并在vm.$refs中获取。vm.$refs.p3 则表示获取视图代码中的
ref="p3"的标签。
1.5 显示数据¶
-
在双标签中显示纯文本数据一般通过{{ }}来完成数据显示,双花括号中还可以支持js表达式和符合js语法的代码,例如函数调用.
-
如果双标签的内容要显示的数据包含html代码,则使用v-html来完成
-
在表单输入框中显示数据要使用v-model来完成数据显示
整个HTML网页组织起来就是DOM树形结构,那么vue在视图模板的时候,采用了虚拟DOM方式来展示数据的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<p>{{num}}</p>
<p>{{num+200}}</p>
<p>{{(3.3333333+1).toFixed(2)}}</p>
<p>{{parseInt(num+3.3333333)}}</p>
<p>{{Math.random()}}</p>
<p @click="shownum">{{num}}</p>
<hr>
<p>{{message}}</p>
<p><a href="www.baidu.com">百度</a></p>
<p v-html="message"></p>
<hr>
<input type="text" :value="num"><br> <!-- 只读属性,不能在修改时保证所有的变量值同步 -->
<input type="text" v-model="num">
<textarea name="" id="" cols="30" rows="10" v-model="num"></textarea>
<select name="" id="" v-model="num">
<option value="200">上海</option>
<option value="100">天津</option>
<option value="300">北京</option>
</select>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
num: 100,
message: '<a href="http://www.baidu.com">百度</a>',
}
},
methods:{
shownum(){
alert(this.num); // 把js原生的DOM或者BOM操作,全部写在HTML视图以外。
console.log(this.num);
}
}
}).mount("#app");
</script>
</body>
</html>
总结:
1. 可以在普通双标签中使用{{ }} 或者 v-html 来输出data里面的数据
<h1>{{message}}</h1>
<h1 v-html="message"></h1>
2. 可以在表单标签中使用v-model属性来输出data里面的数据,同时还可以修改data里面的数据
<input type="text" v-model="username">
2. vue的常用指令¶
指令 (Directives) 是带有“v-”前缀的特殊属性,由vue提供的。每一个指令在vue中都有固定的作用。
在vue中提供了很多指令,常用的有:{{变量名}},v-if、v-model、v-for、v-html、@事件名或:属性名等等。
指令会在vm对象的data选项数据发生变化时,会同时改变元素中的其控制的内容或属性。
因为vue的历史版本原因,所以有一部分指令都有两种写法:
| vue1.x | vue2.x以后 | 描述 |
|---|---|---|
| v-html | v-html | 输出html内容到双标签中 |
| v-text="普通文本" | {{普通文本}} | 输出纯文本内容到双标签中 |
| v-bind:属性名 | :属性名 | 把属性中的内容当成vue变量进行输出 |
| v-on:事件名 | @事件名 | 绑定事件操作 |
2.1 属性操作¶
格式:
<p :title="str1">{{ str1 }}</p> <!-- 也可以使用v-html显示双标签的内容,{{ }} 是简写 -->
<a :href="url2">淘宝</a>
<a v-bind:href="url1">百度</a> <!-- v-bind是vue1.x版本的写法 -->
2.1.1 显示/隐藏密码¶
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
密码:<input :type="input_type"><button @click="show_password">{{btn_text}}</button>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
input_type: "password",
btn_text: "显示密码",
}
},
methods:{
show_password(){
// 显示密码
if(this.input_type === "password"){
this.input_type = "text";
this.btn_text = "隐藏密码";
}else{
this.input_type = "password";
this.btn_text = "显示密码";
}
}
}
}).mount("#app");
</script>
</body>
</html>
2.2 事件绑定¶
基本写法
vue中常用事件的写法对照:
| vue写法 | 原生javascript写法 | 描述 |
|---|---|---|
| @click | onclick | 鼠标点击事件 |
| @dblclick | ondblclick | 鼠标双击事件 |
| @submit | onsubmit | 表单提交事件 |
| @focus | onfocus | 获取焦点事件 |
| @blur | onblur | 失去焦点事件 |
| @change | onchange | 值发生改变事件 |
| .... | ... | ... |
2.2.1 商品增加减少数量¶
步骤:
- 给vue对象添加操作数据的方法
- 在标签中使用指令调用操作数据的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<button @click="sub">-</button>
<input type="text" v-model.number="num" size="2" @change="check_num">
<button @click="num++">+</button>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
num: 0,
}
},
methods: {
sub(){
if(this.num<=0){
return false;
}
this.num--;
},
check_num(){
if(this.num<0){
this.num = 0;
}
}
}
}).mount("#app");
</script>
</body>
</html>
2.2.2 表单的数据校验¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
<style>
span.tips{
font-size: 12px;
color: red;
margin-left: 5px;
}
</style>
</head>
<body>
<div id="app">
登录账号:<input type="text" placeholder="请输入账号!" v-model="username" @blur="check_username" @focus="clear_tips('username_tip')">
<span class="tips" ref="username_tip"></span><br>
登录密码:<input type="password" placeholder="请输入密码!" v-model="password" @blur="check_password" @focus="clear_tips('password_tip')">
<span class="tips" ref="password_tip"></span><br>
确认密码:<input type="password" placeholder="请输入确认密码!" v-model="password2" @blur="check_password2" @focus="clear_tips('password2_tip')">
<span class="tips" ref="password2_tip"></span><br>
<input type="submit" value="注册" @click="submit_form">
</div>
<script>
var vm = Vue.createApp({
data(){
return {
username: "root",
password: "",
password2: "",
}
},
methods: {
check_username(){
if(this.username.length < 4 || this.username.length > 12){
this.$refs.username_tip.innerHTML = "请输入4-12个字符长度的登录账号!";
return true;
}
},
clear_tips(name){
this.$refs[name].innerHTML = ""
},
check_password(){
if(this.password.length<6 || this.password.length>16){
this.$refs.password_tip.innerHTML = "请输入6-16个字符长度的登录密码!";
return true;
}
},
check_password2(){
// 验证密码与确认密码是否一致
if(this.password !== this.password2){
this.$refs.password2_tip.innerHTML = "登录密码与确认密码必须一致!";
return true;
}
},
submit_form(){
if (this.check_username() || this.check_password() || this.check_password2() ){
console.log("验证失败!无法提交数据");
return false;
}
}
}
}).mount("#app");
</script>
</body>
</html>
2.3 操作样式¶
操作样式,本质就是属性操作,使用冒号:
2.3.1 控制标签class类名¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
<style>
.p1{
background-color: yellow;
}
.p2{
color: red;
}
</style>
</head>
<body>
<div id="app">
<p>用法1::class属性值是一个class类名的变量</p>
<p :class="cls1">一段文本信息</p>
<hr>
<p>用法2::class属性值是一个json对象</p>
<p :class="{p1: b1, p2: b2}">一段文本信息</p>
<hr>
<p>用法3::class属性值是一个对象变量名</p>
<p :class="p_cls1">一段文本信息</p>
<hr>
<p>用法4::class属性值是一个数组,数组的成员是data中声明的用于控制样式的json对象</p>
<p :class="[cls2,cls3]">一段文本信息</p>
<hr>
<p>用法5: :class属性值时一个数组变量名</p>
<p :class="arr">一段文本信息</p>
<p>注意::class属性在同一个标签中,只能出现一次,后面出现的:class属性会被vue忽略掉,但是:class可以和class属性组合使用,class也只能在同一个标签中出现一次</p>
<p class="p1" :class="cls2" :class="cls3">一段文本信息</p>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
cls1: "p1",
b1: true,
b2: true,
p_cls1: {
p1: true,
p2: false
},
cls2: {
p2: true,
},
cls3: {
p1: true,
},
arr: [
{p1: true},
{p2: true},
]
}
},
methods: {
}
}).mount("#app");
</script>
</body>
</html>
代码执行效果:
总结:
1. 给元素绑定class类名,最常用的就是第1,2,3种。
vue对象的data数据:
data(){
return {
cls1: "p1",
b1: true,
b2: true,
p_cls1: {
p1: true,
p2: false
},
cls2: {
p2: true,
},
}
},
html元素:
<div class="p1" :class="cls2">2222</div>
最终浏览器效果:
<div class="p1 p2">2222</div>
2.3.2 控制标签style样式¶
工作中基本很少出现style控制样式,一个规范不允许,另一个就是代码不好维护。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<p>用法1::style的值是一个json对象</p>
<p :style="{'font-size': size, color: col1}">一段文本信息</p>
<hr>
<p>用法2::style的值是一个对象变量名</p>
<p :style="sty1">一段文本信息</p>
<hr>
<p>用法3::style的值是一个数组,数组的成员是样式组成的对象</p>
<p :style="[sty2, sty3]"></p>
<p>用法4::style的值是一个数组变量名</p>
<p :style="sty_list"></p>
</div>
<script>
var obj = {
backgroundColor: "red",
"border-radius": "100%",
}
var vm = Vue.createApp({
data(){
return {
size: '24px',
col1: 'red',
sty1: {
"text-align": "center",
"color": "yellow",
"background-color": "red",
},
sty2: {
"width": '150px',
"height": '150px',
},
sty3: {
"background-color": "red",
},
sty_list: [
obj,
{
"width": '150px',
"height": '150px',
},
]
}
},
methods: {
}
}).mount("#app");
</script>
</body>
</html>
2.3.3 实例-vue版本选项卡¶
基本样式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
<style>
p{
padding: 0;
margin: 0;
}
.title-list span{
display: inline-block; /* 设置元素的显示模式:行级块状元素 */
height: 60px;
line-height: 60px;
width: 120px;
background-color: #bbb;
text-align: center;
cursor: pointer;
}
.title-list .activate{
background-color: yellow;
}
.content-list .content{
background-color: yellow;
display: none;
width: 370px;
height: 280px;
}
.content-list .current{
display: block;
}
</style>
</head>
<body>
<div id="optioncard">
<div class="title-list">
<span class="activate">国内新闻</span>
<span>国际新闻</span>
<span>银河新闻</span>
</div>
<div class="content-list">
<div class="content current">
<p>国内新闻列表</p>
<p>国内新闻列表</p>
<p>国内新闻列表</p>
<p>国内新闻列表</p>
<p>国内新闻列表</p>
</div>
<div class="content current">
<p>国际新闻列表</p>
<p>国际新闻列表</p>
<p>国际新闻列表</p>
<p>国际新闻列表</p>
<p>国际新闻列表</p>
</div>
<div class="content">
<p>银河新闻列表</p>
<p>银河新闻列表</p>
<p>银河新闻列表</p>
<p>银河新闻列表</p>
<p>银河新闻列表</p>
</div>
</div>
</div>
<script>
</script>
</body>
</html>
特效实现,代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
<style>
p{
padding: 0;
margin: 0;
}
.title-list span{
display: inline-block; /* 设置元素的显示模式:行级块状元素 */
height: 60px;
line-height: 60px;
width: 120px;
background-color: #bbb;
text-align: center;
cursor: pointer;
}
.title-list .activate{
background-color: yellow;
}
.content-list .content{
background-color: yellow;
display: none;
width: 370px;
height: 280px;
}
.content-list .current{
display: block;
}
</style>
</head>
<body>
<div id="optioncard">
<div class="title-list">
<span :class="{activate: index === 0}" @click="index=0">国内新闻</span>
<span :class="{activate: index === 1}" @click="index=1">国际新闻</span>
<span :class="{activate: index === 2}" @click="index=2">银河新闻</span>
</div>
<div class="content-list">
<div class="content" :class="{current: index === 0}">
<p>国内新闻列表</p>
<p>国内新闻列表</p>
<p>国内新闻列表</p>
<p>国内新闻列表</p>
<p>国内新闻列表</p>
</div>
<div class="content" :class="{current: index === 1}">
<p>国际新闻列表</p>
<p>国际新闻列表</p>
<p>国际新闻列表</p>
<p>国际新闻列表</p>
<p>国际新闻列表</p>
</div>
<div class="content" :class="{current: index === 2}">
<p>银河新闻列表</p>
<p>银河新闻列表</p>
<p>银河新闻列表</p>
<p>银河新闻列表</p>
<p>银河新闻列表</p>
</div>
</div>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
index: 0,
}
},
methods: {
}
}).mount("#optioncard")
</script>
</body>
</html>
代码运行效果:
2.4 条件渲染指令¶
vue中提供了两个指令可以用于判断是否要显示元素,分别是v-if和v-show。
2.4.1 v-if¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.3.2.20.js"></script>
</head>
<body>
<div id="app">
<!-- vue对象最终会把条件的结果变成布尔值 -->
<p v-if="username">欢迎回到xx网站,{{username}}</p>
</div>
<script>
const vm =Vue.createApp({
data(){
return {
username: "",
}
},
}).mount("#app")
</script>
</body>
</html>
2.4.2 v-else¶
v-else指令来表示 v-if 的“else 块”,v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.3.2.20.js"></script>
</head>
<body>
<div id="app">
<!-- v-else只能作为子语句,跟在上一个v-if后面,而且只能出现一次,否则后面v-else会覆盖前面的v-else -->
<p v-if="username">欢迎回到xx网站,尊敬的{{username}}!</p>
<p v-else>欢迎访问xx网站,尊敬的游客!</p>
<p v-else>欢迎访问xx网站,尊敬的游客!2</p>
</div>
<script>
const vm =Vue.createApp({
data(){
return {
username: "",
}
},
}).mount("#app")
</script>
</body>
</html>
2.4.3 v-else-if¶
v-if后面可以出现0个或多个v-else-if语句,而v-else-if后面可以跟着0个或1个v-else子语句。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.3.2.20.js"></script>
<style>
.l0{
color: #000;
}
.l1{
color: #ffff99;
background: black;
}
.l2{
color: #ffff33;
background: black;
}
.l3{
color: #aaaa00;
background: black;
}
</style>
</head>
<body>
<div id="app">
<p v-if="username">欢迎回到xx网站,尊敬的{{username}}!</p>
<p v-else>欢迎访问xx网站,尊敬的游客!</p>
<p v-if="level === 0" class="l0">您是vip会员!</p>
<p v-else-if="level === 1" class="l1">您是svip会员!</p>
<p v-else-if="level === 2" class="l2">您是mvip会员!</p>
<p v-else-if="level === 3" class="l3">您是msvip会员!</p>
<p v-else>您是签到会员!</p>
</div>
<script>
const vm =Vue.createApp({
data(){
return {
username: "", // 会员账户名
level: 0, // 会员登录
}
},
}).mount("#app")
</script>
</body>
</html>
2.4.4 v-show¶
v-show用法和v-if大致一样,区别在于2点:
-
v-show后面不能v-else或者v-else-if
-
v-show隐藏元素时,使用的是css样式的display:none来隐藏的,
而v-if是直接从HTML文档中移除元素[ DOM操作中的remove ]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<!-- <p v-if="username">[v-if]欢迎回到xx网站,{{username}}</p>-->
<!-- <p v-show="username">[v-show]欢迎回到xx网站,{{username}}</p>-->
<p v-show="username">[v-show]欢迎回到xx网站,{{username}}</p>
<p v-show="!username">欢迎访问xx网站,尊敬的游客!1111</p>
<p v-show="level === 0" class="l0">您是普通会员!</p>
<p v-show="level === 1" class="l1">您是vip会员!</p>
<p v-show="level === 2" class="l2">您是vvip会员!</p>
<p v-show="level === 3" class="l3">您是svip会员!</p>
<p v-show="![0,1,2,3].includes(level)">您是签到会员!</p>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
username: "",
level: 0,
}
}
}).mount("#app")
</script>
</body>
</html>
当HTML页面中有使用了vue以外的其他前端插件,这些插件也操作了和vue控制的内容范围重叠的内容,则需要使用v-show。
2.5 列表渲染指令¶
在vue中,可以通过v-for指令可以将一组数据渲染到页面中,数据可以是数组或者对象。
数据是数组:
<ul>
<!--book是列表的每一个元素-->
<li v-for="book in book_list">{{book.title}}</li>
</ul>
<ul>
<!--book是列表的每一个元素,index是每个元素的下标-->
<li v-for="(book, index) in book_list">第{{ index+1}}本图书:{{book.title}}</li>
</ul>
<script>
var vm1 = new Vue({
el:"#app",
data:{
book_list:[
{"id":1,"title":"图书名称1","price":200},
{"id":2,"title":"图书名称2","price":200},
{"id":3,"title":"图书名称3","price":200},
{"id":4,"title":"图书名称4","price":200},
]
}
})
</script>
数据是对象:
<ul>
<!--i是每一个value值-->
<li v-for="value in book">{{value}}</li>
</ul>
<ul>
<!--i是每一个value值,j是每一个键名-->
<li v-for="attr, value in book">{{attr}}:{{value}}</li>
</ul>
<script>
var vm1 = new Vue({
el:"#app",
data(){
return {
book: {
// "attr":"value"
"id":11,
"title":"图书名称1",
"price":200
},
}
}
})
</script>
课堂代码;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
<style>
.table{
border-collapse: collapse;
border: 1px solid red;
width: 600px;
}
.table td, .table th{
border: 1px solid red;
height: 40px;
line-height: 40px;
font-size: 12px;
text-align: center;
}
.table .attr{
line-height: 18px;
}
</style>
</head>
<body>
<div id="app">
<p>循环显示数组内容</p>
<!-- <table class="table">-->
<!-- <tr>-->
<!-- <th>序号[正序]</th>-->
<!-- <th>序号[倒序]</th>-->
<!-- <th>ID</th>-->
<!-- <th>图书</th>-->
<!-- <th>价格</th>-->
<!-- <th>属性</th>-->
<!-- <th>是否第一行数据</th>-->
<!-- <th>是否最后一行数据</th>-->
<!-- </tr>-->
<!--<!– <tr v-for="book in book_list">–>-->
<!-- <tr v-for="(book, key) in book_list">-->
<!-- <td>{{book_list.length-key}}</td>-->
<!-- <td>{{key+1}}</td>-->
<!-- <td>{{book.id}}</td>-->
<!-- <td>{{book.title}}</td>-->
<!-- <td>{{book.price}}</td>-->
<!-- <td v-if="key === 0">√</td>-->
<!-- <td v-else>×</td>-->
<!-- <td v-if="key === book_list.length-1">√</td>-->
<!-- <td v-else>×</td>-->
<!-- </tr>-->
<!-- </table>-->
<p>循环显示对象属性</p>
<table class="table">
<tr>
<th>ID</th>
<th>图书</th>
<th>价格</th>
<th>属性</th>
</tr>
<tr v-for="(book, key) in book_list">
<td>{{book.id}}</td>
<td>{{book.title}}</td>
<td>{{book.price}}</td>
<!-- <td class="attr">-->
<!-- <div v-for="value in book.attr_data">属性名:{{value}}</div>-->
<!-- </td>-->
<td class="attr">
<div v-for="(value, attr) in book.attr_data">{{attr}}:{{value}}</div>
</td>
</tr>
</table>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
book_list: [
{"id":31,"title":"图书名称1","price":200, "attr_data": {"正文语种": "英文", "页数": "541", "开本": "16开", "出版时间": "2000-11-01", "出版社": "人民邮电出版社"}},
{"id":12,"title":"图书名称2","price":200, "attr_data": {"正文语种": "中文", "页数": "511", "开本": "16开", "出版时间": "2010-11-01", "出版社": "人民邮电出版社"}},
{"id":33,"title":"图书名称3","price":200, "attr_data": {"页数": "501", "出版时间": "2019-12-01", "出版社": "人民邮电出版社"}},
{"id":54,"title":"图书名称4","price":200, "attr_data": { "页数": "671", "开本": "16开", "出版时间": "2019-03-01", "出版社": "人民邮电出版社"}},
{"id":14,"title":"图书名称4","price":200, "attr_data": {}},
]
}
}
}).mount("#app")
</script>
</body>
</html>
练习:
goods_list: [
{"name":"python入门","price":150},
{"name":"python进阶","price":100},
{"name":"python高级","price":75},
{"name":"python研究","price":60},
{"name":"python放弃","price":110},
]
# 把上面的数据采用table表格输出到页面,价格大于100的一行数据需要添加背景色橙色[orange]
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
<style>
.table{
border-collapse: collapse;
border: 1px solid red;
width: 600px;
}
.table td, .table th{
border: 1px solid red;
height: 40px;
line-height: 40px;
font-size: 12px;
text-align: center;
}
.bg{
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<table class="table">
<tr>
<th>序号</th>
<th>商品名</th>
<th>价格</th>
<th>购买数量</th>
<th>单品统计</th>
</tr>
<tr v-for="(goods, key) in goods_list" :class="{bg: goods.price>100}">
<td>{{key+1}}</td>
<td>{{goods.name}}</td>
<td>{{goods.price}}</td>
<td>
<button @click="sub(goods)">-</button>
<input type="text" size="1" v-model="goods.num">
<button @click="goods.num+=10">+</button>
</td>
<td></td>
</tr>
</table>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
goods_list: [
{"name":"python入门","price":150, num: 0},
{"name":"python进阶","price":100, num: 0},
{"name":"python高级","price":75, num: 0},
{"name":"python研究","price":60, num: 0},
{"name":"python放弃","price":110, num: 0},
]
}
},
methods:{
sub(goods){
goods.num-=10;
if(goods.num<0){
goods.num = 0;
}
}
}
}).mount("#app")
</script>
</body>
</html>
上面的练习虽然完成了,但是怎么统计累加的商品数量,则及时的显示总商品的价格等等,就需要我们继续往下学习vue的功能。
3. Vm对象提供的选项API¶
所谓的选项API就是在实例化vm对象时,往createApp中传递进行选项参数,例如,我们已经使用过的data或者methods就是选项api。
3.1 过滤器¶
Vue3.0已经被淘汰了过滤器这个选项,所以我们这里使用2.6.11版本来完成代码的编写。
过滤器,就是vue允许开发者自定义的文本格式化函数,可以使用在两个地方:输出内容和操作数据中。
定义过滤器的方式有两种:全局过滤器和局部过滤器。
3.1.1 使用Vue.filter()进行全局定义¶
js/filters.js,代码:
// 全局范围定义的过滤器
Vue.filter("money1", function(v){
//就是来格式化(处理)v这个数据的
if(v==0){
return v
}
return v.toFixed(2)+"元"
})
3.1.2 在vm对象中通过filters选项api来局部定义¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./assets/vue@2.7.8.js"></script>
<script src="./assets/filter.js"></script>
</head>
<body>
<div id="app">
<p>{{price|money}}</p>
<p>{{price|mon}}</p>
<p>{{money_format(price)}}</p>
</div>
<script>
var vm = new Vue({ // 相当于vue3.x的 Vue.createApp()
el: "#app", // 相当于vue3.x的mount绑定视图方法
data(){
return {
message: "hello, vue2.x",
price: 3.55555
}
},
methods: {
money_format(data){ // 过滤器完全可以被methods选项中定义的函数所代替,所以在vue3.x以后成为了废弃特性
return `¥${data.toFixed(2)}`
}
},
filters: { // 局部过滤器,是当前vm对象的属性方法存在,所以无法被外界使用
mon(data){
return `¥${data.toFixed(2)}`
}
}
})
</script>
</body>
</html>
3.2 计算属性和侦听属性¶
3.2.1 计算属性¶
所谓的计算属性,是vue提供给我们开发者用于编写代码时保存计算出新的数据结果的变量。主要通过computed选项进行声明的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<input type="text" size="1" v-model.number="num1">+
<input type="text" size="1" v-model.number="num2">={{result}}
</div>
<script>
var vm = Vue.createApp({
data(){
return {
num1: 10,
num2: 10,
}
},
computed: { // 计算属性选项API,里面的成员就是一个属性方法,用于保存在vm中其他变量的计算结果
result(){
return this.num1+ this.num2
}
}
}).mount("#app")
</script>
</body>
</html>
3.2.2 侦听属性¶
侦听属性,可以帮助我们侦听data某个数据的变化,从而做相应的自定义操作。
侦听属性是一个对象,它的键是要监听的对象或者变量,值一般是函数,当侦听的data数据发生变化时,会自定执行的对应函数,这个函数在被调用时,vue会传入两个形参,第一个是变化前的数据值,第二个是变化后的数据值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
<style>
input{
outline: none;
border: 1px solid #333;
}
</style>
</head>
<body>
<div id="app">
账户:<input ref="username" type="text" v-model="username" @blur="check_username"><br><br>
口令:<input ref="password" type="text" v-model="password"><br><br>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
username: "",
password: ""
}
},
watch: { // 侦听选项API,成员就是data变量名
username(new_data, old_data){
// 侦听data中的username
console.log(`old_data=${old_data}, new_data=${new_data}`)
let length = this.username.length;
if(length>=6 && length<=16){
this.$refs.username.style.border="1px solid blue";
}else{
this.$refs.username.style.border="1px solid red";
}
},
password(new_value, old_value){ // 参数为:修改前的变量值以及修改后的变量值
if(/^\d+$/.test(this.password)){
this.$refs.password.style.border="1px solid red";
}else{
this.$refs.password.style.border="1px solid blue";
}
}
},
methods:{
check_username(){
console.log("check_username执行了。")
}
}
}).mount("#app")
</script>
</body>
</html>
3.3 vm对象的生命周期¶
每个vm对象在创建时都要经过一系列的初始化过程。在这个过程中Vue.js会自动运行一些叫做生命周期的的钩子函数,我们可以使用这些函数,在对象创建的不同时间阶段加上我们需要的自动执行代码,就实现特定的功能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<p ref="p1">{{message}}</p>
<input type="text" v-model="message">
</div>
<script>
var vm = Vue.createApp({
data(){
return {
message: "hello",
}
},
beforeCreate(){
// 当前函数执行时,vm还没有初始化完成。
console.log("beforeCreate>>>", this.$data);
console.log("beforeCreate>>>", this.$el);
console.log("beforeCreate>>>", this.$refs);
},
created(){
// 当前函数执行时,vm对象已经初始化完成
console.log("created>>> $data=", this.$data); // 此时data选项中的数据已经被注入到vm对象中
console.log("created>>> $el=", this.$el);
console.log("created>>> $refs=", this.$refs);
// 在开发中,我们可以在这个函数中进行初始化数据相关的操作,例如:使用ajax从服务器中读取数据,并赋值给data
},
beforeMount(){
// 已经把对应的vue语法的变量替换成了html内容了,但是并没有挂载到el标签的内容中
console.log("beforeMount>>> $el=", this.$el);
console.log("beforeMount>>> template=", this.$options.template);
console.log("beforeMount>>> $refs=", this.$refs);
},
mounted(){
// vue生成的HTML内容已经挂载到了$el属性中
console.log("mounted>>>", this.$el);
console.log("mounted>>>", this.$refs);
},
beforeUpdate(){
// 变量更新前,data选项中的数据发生了改变,但是没有重新生成虚拟DOM,所以HTML中的变量值没有被同步
console.log("beforeUpdate>>>", this.$data);
console.log("beforeUpdate>>>", this.$el.parentElement.innerHTML);
// 修改数据前,判断本次修改是否合法?发送ajax,
},
updated(){
// 变量更新后,html内容已经与data选项中的数据同步了,因为重新生成了虚拟DOM
console.log("updated>>>", this.$el.parentElement.innerHTML);
// 修改数据后,发送ajax,同步数据库
}
}).mount("#app")
</script>
</body>
</html>
总结:
在vue使用的过程中,如果要初始化操作,把初始化操作的代码放在 mounted 中执行。
mounted阶段就是在vm对象已经把data数据实现到页面以后。一般页面初始化使用。例如,用户访问页面加载成功以后,就要执行的ajax请求。
另一个就是created,这个阶段就是在 vue对象创建以后,把ajax请求后端数据的代码放进 created
3.4 指令修饰符¶
指令修饰符,用于编写在vue执行之后的补充符号,作用是增强当前指令或给当前指令进行功能的调整的。
v-model的指令修饰符。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.3.2.20.js"></script>
</head>
<body>
<div id="app">
<!-- v-model.trim 用于去除字符串两边的空白字符,常用于密码-->
<input type="password" v-model.trim="password"><br>
<!-- v-model.lazy 用于减少触发watch侦听选项的频率,从原来的input输入事件的侦听切换成了change值改变事件的监听-->
<input type="text" v-model.lazy="mobile"><br>
<!-- v-model.number 表示把变量作为数值来进行转换提取,能转的都会转换 -->
<input type="text" size="1" v-model.number="num"> + 500 = <span>{{num+500}}</span>
</div>
<script>
const vm = Vue.createApp({
data(){
return {
password: "",
mobile: "",
num: 0,
}
},
watch:{
password(){
console.log(this.password);
},
mobile(){
console.log("发送http请求验证手机号是否在服务端注册了");
console.log(this.mobile);
}
}
}).mount("#app");
</script>
</body>
</html>
事件指令的修饰符使用.stop和.prevent
.prevent 用于阻止页面跳转,用于a标签和form标签下的button
.stop 用于阻止事件继续冒泡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div id="app">
<div @click="show('div')">
<button @click.stop="show('button')">点击</button>
<a href="http://www.baidu.com" @click.stop.prevent="show('a')">点击</a>
</div>
</div>
<script>
// 子元素的同类事件触发以后,会自动触发父类元素的同类事件,这就是事件冒泡。
// @事件名.stop 用于阻止事件冒泡
// @事件名.prevent 用于阻止标签本身拥有的页面跳转功能,例如:a标签被点击,form标签提交表单
var vm = Vue.createApp({
data(){
return {
}
},
methods:{
show(msg){
console.log(`${msg}被点击了`)
}
}
}).mount("#app")
</script>
</body>
</html>
练习代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.3.2.20.js"></script>
<style>
body{
padding: 0;
margin: 0;
}
#app2{
position: fixed;
top: 0;
left: 0;
background-color: rgba(0,0,0,0.6);
color: #fff;
width: 68vw;
padding-top: 30vh;
padding-bottom: 30vh;
padding-left: 32vw;
}
a{
color: #fff;
text-decoration: none;
}
</style>
</head>
<body>
<div id="app">
<button @click="show_login_box=!show_login_box">登录</button>
<div id="app2" v-if="show_login_box" @click="show_login_box=false">
<div @click.stop="">
账户:<input ref="username" type="text" v-model="username"><br><br>
口令:<input ref="password" type="text" v-model="password"><br><br>
</div>
<a href="http://www.baidu.com" @click.prevent.stop="check_params">登录</a>
<a @click="show_login_box=false">取消</a>
</div>
</div>
<script>
const vm = Vue.createApp({
data(){
return {
show_login_box: false,
password: "",
username: "",
}
},
methods:{
check_params(){
if(this.username != "xiaoming"){
console.log("验证数据失败!!");
return ; // 阻止代码继续往下执行
}
console.log("跳转页面的代码");
}
}
}).mount("#app");
</script>
</body>
</html>
4. 综合案例-todolist¶
我的计划列表,html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>todolist</title>
<style type="text/css">
.list_con{
width:600px;
margin:50px auto 0;
}
.inputtxt{
width:550px;
height:30px;
border:1px solid #ccc;
padding:0px;
text-indent:10px;
}
.inputbtn{
width:40px;
height:32px;
padding:0px;
border:1px solid #ccc;
}
.list{
margin:0;
padding:0;
list-style:none;
margin-top:20px;
}
.list li{
height:40px;
line-height:40px;
border-bottom:1px solid #ccc;
}
.list li span{
float:left;
}
.list li a{
float:right;
text-decoration:none;
margin:0 10px;
}
</style>
</head>
<body>
<div class="list_con">
<h2>To do list</h2>
<input type="text" name="" id="txt1" class="inputtxt">
<input type="button" name="" value="增加" id="btn1" class="inputbtn">
<ul id="list" class="list">
<!-- javascript:; # 阻止a标签跳转 -->
<li>
<span>学习html</span>
<a href="javascript:;" class="up"> ↑ </a>
<a href="javascript:;" class="down"> ↓ </a>
<a href="javascript:;" class="del">删除</a>
</li>
<li><span>学习css</span><a href="javascript:;" class="up"> ↑ </a><a href="javascript:;" class="down"> ↓ </a><a href="javascript:;" class="del">删除</a></li>
<li><span>学习javascript</span><a href="javascript:;" class="up"> ↑ </a><a href="javascript:;" class="down"> ↓ </a><a href="javascript:;" class="del">删除</a></li>
</ul>
</div>
</body>
</html>
特效实现效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>todolist</title>
<style>
.list_con{
width:600px;
margin:50px auto 0;
}
.inputtxt{
width:550px;
height:30px;
border:1px solid #ccc;
padding:0px;
text-indent:10px;
}
.inputbtn{
width:40px;
height:32px;
padding:0px;
border:1px solid #ccc;
}
.list{
margin:0;
padding:0;
list-style:none;
margin-top:20px;
}
.list li{
height:40px;
line-height:40px;
border-bottom:1px solid #ccc;
}
.list li span{
float:left;
}
.list li a{
float:right;
text-decoration:none;
margin:0 10px;
}
</style>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div class="list_con" id="todolist">
<h2>To do list</h2>
<input type="text" v-model="text" id="txt1" class="inputtxt">
<input type="button" value="增加" id="btn1" class="inputbtn" @click="add_item">
<ul id="list" class="list">
<!-- javascript:; # 阻止a标签跳转 -->
<li v-for="(item,key) in item_list">
<span>{{item}}</span>
<a href="" class="up" @click.stop.prevent="up_item(key)"> ↑ </a>
<a href="javascript:;" @click.stop.prevent="down_item(key)"class="down"> ↓ </a>
<a href="" class="del" @click.stop.prevent="remove_item(key)">删除</a>
</li>
</ul>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
text: "",
item_list: [
"学习html",
"学习css",
"学习javascript",
]
}
},
methods: {
add_item(){
// 添加计划
this.item_list.splice(0,0,this.text);
this.text = "";
},
remove_item(key){
// 删除计划
this.item_list.splice(key,1);
},
up_item(key){
// 计划向上移动
if( key === 0) return; // 如果当前计划已经在最上方,则不能继续向上移动
let del_list = this.item_list.splice(key,1);
this.item_list.splice(key-1, 0, del_list[0]);
},
down_item(key){
// 计划向下移动
let del_list = this.item_list.splice(key,1);
this.item_list.splice(key+1, 0, del_list[0])
}
}
}).mount("#todolist")
</script>
</body>
</html>
4.1 本地存储¶
webStorage是HTML5提供的用于在本地实现离线存储的一种技术方案。HTML5中提供了2个全局对象用于保存数据到本地浏览器或者移动端的webapp中,分别是:localStorage和sessionStorage。这2个对象,使用方式一致,区别仅仅是存储数据的有效期不同。
localStorage 永久存储,不主动删除,会一直保存在本地。
sessionStorage 会话存储,当浏览器重启以后,数据则会丢失。
这个对象保存数据时,都是域名进行区分保存的,不同的域名保存在本地存储的相互之间是互不干扰,互不影响的。
localStorage的用法:
// 保存一个数据
localStorage.setItem("变量名", "变量值");
// localStorage.变量名="变量值"
// 获取一个数据
localStorage.getItem("变量名"); // 返回值:变量值
// 变量值 = localStorage.变量名
// 删除一个数据
localStorage.removeItem("变量名");
// 清空当前域名下所有的数据
localStorage.clear();
sessionStorage 的用法:
// 保存一个数据
sessionStorage.setItem("变量名", "变量值");
// sessionStorage.变量名="变量值"
// 获取一个数据
sessionStorage.getItem("变量名"); // 返回值:变量值
// 变量值 = sessionStorage.变量名
// 删除一个数据
sessionStorage.removeItem("变量名");
// 清空当前域名下所有的数据
sessionStorage.clear();
4.2 离线数据保存¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>todolist</title>
<style>
.list_con{
width:600px;
margin:50px auto 0;
}
.inputtxt{
width:550px;
height:30px;
border:1px solid #ccc;
padding:0px;
text-indent:10px;
}
.inputbtn{
width:40px;
height:32px;
padding:0px;
border:1px solid #ccc;
}
.list{
margin:0;
padding:0;
list-style:none;
margin-top:20px;
}
.list li{
height:40px;
line-height:40px;
border-bottom:1px solid #ccc;
}
.list li span{
float:left;
}
.list li a{
float:right;
text-decoration:none;
margin:0 10px;
}
</style>
<script src="assets/vue.global.3.2.20.js"></script>
</head>
<body>
<div class="list_con" id="todolist">
<h2>To do list</h2>
<input type="text" v-model="text" id="txt1" class="inputtxt">
<input type="button" value="增加" id="btn1" class="inputbtn" @click="add_item">
<ul id="list" class="list">
<!-- javascript:; # 阻止a标签跳转 -->
<li v-for="(item,key) in item_list">
<span>{{item}}</span>
<a href="" class="up" @click.stop.prevent="up_item(key)"> ↑ </a>
<a href="javascript:;" @click.stop.prevent="down_item(key)"class="down"> ↓ </a>
<a href="" class="del" @click.stop.prevent="remove_item(key)">删除</a>
</li>
</ul>
</div>
<script>
var vm = Vue.createApp({
data(){
return {
text: "",
item_list: [
// "学习html",
// "学习css",
// "学习javascript",
]
}
},
methods: {
add_item(){
// 添加计划
this.item_list.splice(0,0,this.text);
this.text = "";
},
remove_item(key){
// 删除计划
this.item_list.splice(key,1);
},
up_item(key){
// 计划向上移动
if( key === 0) return; // 如果当前计划已经在最上方,则不能继续向上移动
let del_list = this.item_list.splice(key,1);
this.item_list.splice(key-1, 0, del_list[0]);
},
down_item(key){
// 计划向下移动
let del_list = this.item_list.splice(key,1);
this.item_list.splice(key+1, 0, del_list[0])
},
save_data(){
// 保存数据
let content = JSON.stringify(this.item_list)
localStorage.setItem("todolist", content);
},
load_data(){
// 加载数据
let content = localStorage.getItem("todolist")
try {
this.item_list = JSON.parse(content);
}catch (e) {
console.log("转换json数据出错!")
}
}
},
created(){
this.load_data();
},
updated(){
this.save_data();
}
}).mount("#todolist")
</script>
</body>
</html>










