尚硅谷Vue2.0+Vue3.0全套教程丨从入门到精通
- Vue 2.0部分
- 核心部分
- 1.1 Vue简介
- 1.1.1 Vue是什么?
- 1.1.2 Vue作者
- 1.1.3 Vue的特点
- 1.1.4 Vue学习的JavaScript前备知识点
- 1.1.5 Vue使用指南
- 1.1.6 搭建Vue开发环境
- 1.2 初识Vue
- 1.2.1 Hello小实例
- 1.2.2 分析Hello案例
- 1.3 Vue模板语法
- 1.4 数据绑定
- 1.5 el挂载器和data数据的两种写法
- 1.5.1 el挂载器的两种写法
- 1.5.2 data数据对象的两种写法
- 1.6 MVVM模型
- 1.7 回顾方法
- 1.8 Vue中的数据代理
- 1.8.1 数据代理是什么?
- 1.8.2 Vue中的数据代理
- 1.9 事件处理
- 1.9.1 事件的基本使用
- 1.9.2 事件修饰符
- 1.9.3 键盘事件
- 1.10 计算属性——computed
- 1.10.1 姓名案例——插值语法实现
- 1.10.2 姓名案例——methods方法实现
- 1.10.3 姓名案例——计算属性实现
- 1.10.4 姓名案例——计算属性简写实现
- 1.11 监视属性——watch
- 1.11.1 天气案例
- 1.11.2 天气案例——监视属性实现
- 1.11.3 天气案例——深度监视实现——deep
- 1.11.4 天气案例——深度监视简写形式实现
- 1.11.5 姓名案例——用监视属性实现
- 1.11.6 计算属性和监视属性的对比
- 1.12 class与style绑定
- 1.12.1 class绑定
- 1.12.2 style绑定
- 1.13 条件渲染
- 1.13.1 v-if 和 v-show
- 1.14 列表渲染
- 1.14.1 基本列表
- 1.14.2 key的原理
- 1.14.3 列表过滤
- 1.14.3.1 watch属性实现列表过滤
- 1.14.3.2 computed属性实现列表过滤
- 1.14.4 列表排序
- 1.14.5 更新时的一个问题
- 1.14.6 Vue检测数据改变的一个原理——对象
- 1.14.7 ()方法的使用
- 1.14.8 Vue检测数据改变的一个原理——数组
- 1.14.9 Vue数据监测总结
- 1.15 收集表单数据
- 1.16 过滤器
- 1.17 Vue内置指令
- 1.17.1 v-text指令
- 1.17.2 v-html指令
- 1.17.3 v-cloak指令
- 1.17.4 v-once指令
- 1.17.5 v-pre指令
- 1.18 Vue自定义指令
- 1.19 生命周期
- 1.19.1 引出生命周期
- 1.19.2 分析生命周期
- 1.19.3 总结声明周期
- 组件化编程
- 2.1 非单文件组件
- 2.1.1 基本使用
- 2.1.2 几个注意点
- 2.1.3 组件的嵌套
- 2.1.4 VueComponent
- 2.2 单文件组件
- 3.使用Vue脚手架
- 3.1 初始化脚手架
- 3.1.1 文档说明
- 3.1.2 操作步骤
- 3.1.3 模板项目结构
- 3.2 ref与props
- 中的ajax
- -router
- UI组件库
- Vue 3.0 部分
- Vue 3.0 简介
- 1. 创建Vue3.0工程
- 1.1 使用vue-cli创建
- 1.2 使用vite创建
- 2. 常用Composition API
- 2.1 setup
- 2.2 ref函数
- 2.2.1 ref函数处理基本类型
- 2.2.2 ref函数处理对象类型
- 2.3 reactive
- 2.4 Vue3.0中的响应式原理
- 2.4.1 的响应式
- 2.4.2 Vue3.0的响应式
- 2.5 reactive对比ref
- 2.6 setup的两个注意点
- 2.6.1 **测试beforeCreate()和setup()谁先启动**
- 2.6.2 **setup的参数**
- 2.7 计算属性与监视
- 2.7.1 计算属性
- 2.7.2 监视
- 2.7.2.1 情况一:监视ref所定义的一个响应式数据
- 2.7.2.2 情况二:监视ref所定义的多个响应式数据
- 2.7.2.3 watch的其他参数
- 2.7.2.4 情况三:监视reactive所定义的一个响应式的全部数据
- 2.7.2.5 情况四:监视reactive所定义的一个响应式数据中的某个属性
- 2.7.2.6 情况五:监视reactive所定义的一个响应式数据中的某些数据
- 2.7.2.7 特殊情况:
- 2.7.3 watchEffect函数
- 2.8 生命周期
- 2.9 自定义hook函数
- 2.10 toRef与toRefs
- 2.10.1 toRef
- 2.10.2 toRefs
- 3. 其他Composition API
- 3.1 shallowReactive与shallowRef
- 3.1.1 shallowReactive
- 3.1.2 shallowRef
- 3.2 readonly与shallowReadonly
- 3.2.1 readonly
- 3.2.2 shallowReadonly
- 3.3 toRaw与markRaw
- 3.3.1 toRaw
- 3.3.2 markRaw
- 3.4 customRef
- 3.5 provide与inject
- 3.6 响应式数据的判断
- 4. Composition API的优势
- 4.1 Options API存在的问题
- 4.2 Composition API的优势
- 5. 新的组件
- 5.1 Fragment
- 5.2 Teleport
- 5.3 Suspense
- 6. 其他
- 6.1 全局API的转移
- 6.2 其他的改变
Vue 2.0部分
核心部分
1.1 Vue简介
1.1.1 Vue是什么?
Vue是一套用于构建用户界面的渐进式JavaScript框架。
- 渐进式:Vue可以自底向上逐层的应用;
- 简单应用:只需一个轻量小巧的核心库。
- 复杂应用:可以引入各式各样的Vue插件。
1.1.2 Vue作者
尤雨溪
1.1.3 Vue的特点
- 采用组件化模式,提高代码复用率,且让代码更好维护。
- 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
- 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点。
1.1.4 Vue学习的JavaScript前备知识点
- ES6语法规范
- ES6模块化
- 包管理器
- 原型、原型链
- 数组常用方法
- axios
- promise
1.1.5 Vue使用指南
Vue官网:/
官网导航菜单栏中最重要的是教程和API。
1.1.6 搭建Vue开发环境
1.2 初识Vue
1.2.1 Hello小实例
<!--
初识Vue:
1. 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2. root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
3. root容器里的代码被称为【Vue模板】;
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>初识Vue</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<div id="root">
<!-- 插值语法插入name=尚硅谷:{{name}} -->
<h1>Hello,{{name}}</h1>
</div>
<script>
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
//创建Vue实例
new Vue({
el:'#root', //el用于指定当前Vue实例为那个容器服务,值通常为css选择器字符串。
data:{ //data中用于存储数据,数据供el所指定的容器使用,值我们暂时先写成一个对象。
name:'尚硅谷'
}
})
</script>
</body>
</html>
1.2.2 分析Hello案例
<!--
初识Vue:
4. Vue实例和容器是一一对应的;
5. 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6. {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7. 一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新。
---------------------------------------------------------------------------
注意区分js表达式和js代码(语句)
1. 表达式:一个表达式会产生一个值,可以放到任何一个需要值的地方。
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? 'a' : 'b'
2. js代码(语句)
(1). if(){}
(2). for(){}
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>初识Vue</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<div id="root">
<h1>Hello,{{()}},{{address}},{{()}}</h1>
</div>
<script>
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
//创建Vue实例
new Vue({
el:'#root',
data:{
name:'shangguigu',
address:'北京'
}
})
</script>
</body>
</html>
1.3 Vue模板语法
<!--
Vue模板语法有两大类:
1.插值语法
功能:用于解析标签体内容。
写法:{{xxx}}, xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件......)。
举例:v-bind:href="xxx" 或者简写为 :href="xxx" xxx同样要写js表达式,且可以直接读取到data中的所有属性。
---------------------------------------------------------------------------
备注:Vue中有很多指令,且形式都是:v-xxx,此处我们只是拿v-bind举例子。
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模板语法</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>1-插值语法:</h1>
<h3>你好,{{name}}</h3>
<hr>
<h1>2-指令语法:</h1>
<a v-bind:href="">点我去{{}}学习</a>
</div>
</body>
<script>
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
name:'jack',
school:{
name:'尚硅谷',
url:''
}
}
})
</script>
</html>
1.4 数据绑定
<!--
Vue中两种数据绑定的方式:
1.单项数据绑定(v-bind):数据只能从data流向页面。
2.双向数据绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
-----------------------------------------------------------------------------
备注:
1.双向绑定一般都应用在表单类元素上(如:input、select等);
-mode:value可以简写为v-model,因为v-model默认收集的就是value值。
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据绑定</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<!-- 普通写法 -->
1-单项数据绑定:<input type="text" v-bind:value="name">
<br>
2-双项数据绑定:<input type="text" v-model:value="name">
<hr>
<!-- 简写写法 -->
1-单项数据绑定(简写方式实现):<input type="text" :value="name">
<br>
2-双项数据绑定(简写方式实现):<input type="text" v-model="name">
<hr>
<!-- 如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素)上使用 -->
<!-- <h2 v-model:x="name">你好啊</h2> -->
</div>
</body>
<script>
new Vue({
el:"#root",
data:{
name:"尚硅谷"
}
})
</script>
</html>
1.5 el挂载器和data数据的两种写法
Vue中一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
1.5.1 el挂载器的两种写法
<!DOCTYPE html>
<!--
el挂载器的两种写法:
Vue时候配置el属性。
2.先创建Vue实例,随后通过v.$mount(‘#root’)指定el的值。
-->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>el的两种写法</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>你好,{{name}}</h1>
</div>
</body>
<script>
var v = new Vue({
// 容器挂载第一种写法
// el:'#root',
data:{
name:'尚硅谷'
}
})
// 输出Vue实例
console.log(v);
// 1秒后执行
setTimeout(()=>{
// 容器挂载第二种写法
v.$mount('#root');
},1000)
</script>
</html>
1.5.2 data数据对象的两种写法
<!--
data数据对象的两种写法:
1.对象式
2.函数式
---------------------------------------------------------------------------------
如何选择:目前哪一种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>data的两种写法</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>你好,{{name}}</h1>
</div>
</body>
<script>
var v = new Vue({
el:'#root',
// data的第一种写法:对象式
/* data:{
* name:'尚硅谷'
* }
*/
// data的第一种写法:函数式
data:function(){
console.log('@@@',this) //此处的this是Vue实例对象
return {
name:'尚硅谷'
}
}
})
</script>
</html>
1.6 MVVM模型
<!--
MVVM模型:
M:模型(Model):对应data中的数据;
V:视图(View):模板;
VM:视图模型(ViewModel):Vue实例对象;
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MVVM模型</title>
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷',
address:'北京'
}
})
</script>
</html>
1.7 回顾方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>回顾方法</title>
</head>
<body>
<script>
let person = {
name:'张三',
sex:'男'
}
Object.defineProperty(person,'age',{
value:18,
enumerable:true, //控制属性是否可以枚举,默认值是false
writable:true, //控制属性是否可以修改,默认值是false
configurable:true //控制属性是否可以被删除,默认值是false
})
console.log(Object.keys(person));
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>回顾方法</title>
</head>
<body>
<script>
let number = 18;
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以修改,默认值是false
// configurable:true, //控制属性是否可以被删除,默认值是false
// 当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值
get:function(){
console.log('有人读取了age属性了')
return number;
},
// 当有人修改person的age属性时,set函数就会被调用,且会收到修改的值
set(value){
console.log('有人修改了age属性,且值是30');
number = value;
}
})
// ((person));
console.log(person);
</script>
</body>
</html>
1.8 Vue中的数据代理
1.8.1 数据代理是什么?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>何为数据代理</title>
</head>
<body>
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写) -->
<script>
let obj = {x:100};
let obj2 = {y:200};
Object.defineProperty(obj2,'x',{
get(){
return obj.x;
},
set(value){
obj.x = value;
}
})
</script>
</body>
</html>
1.8.2 Vue中的数据代理
网页中vm中的_data 和 代码中data是相同的
<!--
Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
Vue中数据代理的好处:
更加方便的操作data中的数据。
基本原理:
通过()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue中的数据代理</title>
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</body>
<script>
let data = {
name:'尚硅谷',
address:'宏福科技园'
}
const vm = new Vue({
el:'#root',
data
// data:{
// name:'尚硅谷',
// address:'宏福科技园'
// }
})
</script>
</html>
数据代理图示:
1.9 事件处理
1.9.1 事件的基本使用
<!--
1.使用v-on:xxx或者@xxx绑定事件,其中xxx是事件名。通常使用@xxx,例如:@click;
2.事件的回调需要配置在methods对象中,最终会在vm上;
中配置的函数,不要用箭头函数!否则this就不是vm实例化对象了;
中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象;
5.@click=”demo” 和 @click=”demo($event)” 效果一致,但是后者可以传参数。
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件的基本使用</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 普通写法 -->
<button v-on:click="showInfo1">点我提示信息1(不传参)</button>
<!-- 简写写法 传参数方法 event事件 -->
<button @click="showInfo2(66,$event)">点我提示信息2(传参数)</button>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo1(event){
console.log(this); //此处的this是vm实例化对象
console.log(event.target.innerText);
alert('信息1:同学你好!');
},
showInfo2(number,event){
console.log(event);
alert('信息2:同学你好!!'+number);
},
}
})
</script>
</html>
1.9.2 事件修饰符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件修饰符</title>
<!-- 引入Vue -->
<script src="../js/"></script>
<style>
*{
margin-top: 20px;
}
.demo1{
height: 50px;
background-color: skyblue;
}
.box1{
padding: 5px;
background-color: skyblue;
}
.box2{
padding: 5px;
background-color: pink;
}
.list{
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li{
height: 100px;
}
</style>
</head>
<body>
<!--
Vue中的事件修饰符:
:阻止默认事件(常用);
:阻止事件冒泡(常用);
:事件只触发一次(常用);
:使用事件的捕获模式;
:只有是当前操作的元素时才触发事件;
:事件的默认行为立即执行,无需等待事件回调执行完毕;
-->
<!-- 准备好一个容器 -->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- @对事件修饰,阻止默认事件发生 -->
<a href="" @="showInfo">点我提示信息</a>
<!-- 阻止事件冒泡 -->
<div class="demo1" @click="showInfo">
<!-- <button @="showInfo">点我提示信息</button> -->
<!-- 事件修饰符可以连着写 -->
<a href="" @="showInfo">点我提示信息</a>
</div>
<!-- 事件只触发一次 -->
<button @="showInfo">点我提示信息</button>
<!-- 使用事件捕获模式 -->
<div class="box1" @="showMessage(1)">
div1
<div class="box2" @click="showMessage(2)">
div2
</div>
</div>
<!-- 只有是当前操作的元素时才触发事件 -->
<div class="demo1" @="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件的回调执行完毕 -->
<!-- wheel:鼠标滚轮滚动事件;
scroll:滚动条滚动事件; -->
<ul class="list" @="demo">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo(e){
alert('同学你好!');
// (); //阻止默认事件
// (); //阻止事件冒泡
console.log(e.target);
},
showMessage(msg){
console.log(msg);
},
demo(){
for (let i = 0; i < 100000; i++) {
console.log('@'); //滚动输出@
}
console.log('累坏了');
}
}
})
</script>
</html>
1.9.3 键盘事件
键盘事件
-
keydown
:按下按键不用抬起来就可以触发事件; -
keyup
:按下按键抬起来才会触发事件;
Vue中常用的按键别名:
- 回车:
enter
- 删除:
delete
- 退出:
esc
- 空格:
space
- 换行:
tab
(必须配合keydown使用) - 上:
up
- 下:
down
- 左:
left
- 右:
right
Vue未提供别名的按键,可以使用按键原始key值去绑定,但注意要转为kebab-case(短横线命名)
系统修饰键(用法特殊):ctrl
、alt
、shift
、meta(win键)
- 配合和
keyup
使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发; - 配合
keydown
使用,正常触发事件。
也可以使用keyCode去指定具体的按键(不推荐)。
.自定义键名 = 键码
,可以去定制按键别名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>键盘事件</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 键盘事件:
keydown:按下按键不用抬起来就可以触发事件;
keyup:按下按键抬起来才会触发事件; -->
Enter键:<input type="text" placeholder="按下回车提示输入" @="showInfo">
<hr>
当按下Ctrl+Y键:<input type="text" placeholder="按下Ctrl+Y键提示输入" @="showInfo">
<hr>
<!-- CapsLock转换为caps-lock -->
CapsLock键:<input type="text" placeholder="按下CapsLock键提示输入" @-lock="showInfo">
<hr>
<!-- 自定义键名称 -->
Enter键:<input type="text" placeholder="按下huiche键提示输入" @="showInfo">
</div>
</body>
<script>
Vue.config.keyCodes.huiche = 13; //自定义键名称
new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo(e){
console.log(e.key);
console.log(e.keyCode);
// // 13为enter回车键
// if( != 13) return;
console.log(e.target.value);
}
}
})
</script>
</html>
1.10 计算属性——computed
1.10.1 姓名案例——插值语法实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>姓名案例_插值语法实现</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
全名:<span>{{(0,3)}}-{{lastName}}</span>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
})
</script>
</html>
1.10.2 姓名案例——methods方法实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>姓名案例_methods实现</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
全名:<span>{{fullName()}}</span>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
methods:{
fullName(){
return this.firstName+'-'+this.lastName;
}
}
})
</script>
</html>
1.10.3 姓名案例——计算属性实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>姓名案例_计算属性实现</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!--
计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了方法提供的getter和setter。
函数什么时候执行?
(1)初次读取时会执行一次;
(2)当依赖的数据发生变化时会被再次调用;
4.优势:与methods实现相比较,内部有缓存机制(复用),效率更高,调试更方便。
5.备注:
(1)计算属性最终会出现在vm上,直接读取使用即可。
(2)如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生。
-->
<div id="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
全名:<span>{{fullName}}</span>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
// get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值。
// get什么时候调用?1.初次读取fullName时,get会被调用。2.所依赖的数据发生变化时,get会被调用。
get(){
console.log('get就被调用了。');
return this.firstName + '-' + this.lastName;
},
// set()什么时候调用?当fullName被修改时,set就会被调用、
set(value){
console.log('set被调用了');
const arr = value.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
</html>
1.10.4 姓名案例——计算属性简写实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>姓名案例_计算属性简写</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
全名:<span>{{fullName}}</span>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
/* 1-完整写法
fullName:{
get(){
('get就被调用了。');
return + '-' + ;
},
set(value){
('set被调用了');
const arr = ('-');
= arr[0];
= arr[1];
}
} */
// 2-简写写法:只考虑读取不考虑修改的时候用简写方式
fullName(){
console.log('get被调用了');
return this.firstName + '-' + this.lastName;
}
}
})
</script>
</html>
1.11 监视属性——watch
1.11.1 天气案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气案例</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气1</button>
<!-- 绑定事件的时候@xxx="yyy" yyy可以写一些简单的语句 -->
<button @click="isHot = !isHot">切换天气2</button>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽';
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
},
})
</script>
</html>
1.11.2 天气案例——监视属性实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气案例_监视属性</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!--
当被监视的属性变化时,回调函数自动调用,进行相关操作;
监视的属性必须存在,才能进行监视;
监视的两种写法:
Vue时传入watch配置;
2.通过vm.$watch监视;
-->
<!-- 准备好一个容器 -->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽';
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
},
// watch监视的第一种写法
watch:{
isHot:{
immediate:true, // 初始化时让handler调用一下。
// handler什么时候调用?当isHot发生改变时调用。
handler(newValue,oldValue){
console.log('isHot修改了',newValue,oldValue);
}
},
info:{
immediate:true, // 初始化时让handler调用一下。
// handler什么时候调用?当isHot发生改变时调用。
handler(newValue,oldValue){
console.log('info修改了',newValue,oldValue);
}
}
}
})
// watch监视第二种写法
vm.$watch(
'isHot',
{
immediate:true,
handler(newValue,oldValue){
console.log('isHot修改了',newValue,oldValue);
}
}
)
</script>
</html>
1.11.3 天气案例——深度监视实现——deep
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气案例_监视属性</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!--
中的watch默认不监测对象内部值的变化(一层);
2.配置deep:true可以监测对象内部值改变(多层);
------------------------------------------------------------------
备注:
(1)Vue自身可以监测对象内部值的改变,但是Vue提供的watch默认不可以;
(2)使用watch时根据数据的具体结构,决定是否采用深度监视;
-->
<!-- 准备好一个容器 -->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<hr>
<h3>a的值是{{}}</h3>
<button @click="++">点我a+1</button>
<hr>
<h3>b的值是{{}}</h3>
<button @click="++">点我b+1</button>
<hr>
<h3>彻底改变numbers</h3>
<button @click="numbers = {a:11,b:22}">彻底改变numbers</button>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1
}
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽';
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot;
},
},
watch:{
isHot:{
// immediate:true,
handler(newValue,oldValue){
console.log('isHot修改了',newValue,oldValue);
}
},
// 监视多级结构中某个属性的变化
'':{
handler(){
console.log('a变了');
}
},
// 监视多级结构中所有属性的变化
numbers:{
deep:true,
handler(){
console.log('numbers彻底被改变了');
}
}
}
})
</script>
</html>
1.11.4 天气案例——深度监视简写形式实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气案例_监视属性_简写形式</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1
}
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽';
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot;
},
},
watch:{
// 正常写法
/* isHot:{
immediate:true, //初始化时让hanlder调用一下
deep:true, //深度监视
handler(newValue,oldValue){
('isHot修改了',newValue,oldValue);
}
}, */
// 简写
isHot(newValue,oldValue){
console.log('isHot修改了',newValue,oldValue);
}
}
})
// 正常写法
/* vm.$watch('isHot',{
immediate:true, //初始化时让hanlder调用一下
deep:true, //深度监视
handler(newValue,oldValue){
('isHot修改了',newValue,oldValue);
}
}) */
// 简写形式
/* vm.$watch('isHot',function(newValue,oldValue){
('isHot修改了',newValue,oldValue);
}) */
</script>
</html>
1.11.5 姓名案例——用监视属性实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>姓名案例_监视属性</title>
<!-- 引入Vue -->
<script src="../js/"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
全名:<span>{{fullName}}</span>
</div>
</body>
<script>
// 监视属性
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
fullName:'张-三'
},
watch:{
firstName(newValue){
setTimeout(() => {
this.fullName = newValue + '-' + this.lastName;
}, 1000);
},
lastName(newValue){
this.fullName = this.firstName + '-' + newValue;
}
}
})
</script>
</html>
1.11.6 计算属性和监视属性的对比
computed
和watch
之间的区别:
-
computed
能完成的功能,watch
都能完成; -
watch
能完成的功能,computed
不一定能完成,例如:watch
可以进行异步操作;
两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或者组件实例对象;
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或者组件实例对象;
1.12 class与style绑定
1.12.1 class绑定
写法:class=”xxx”
xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定;
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但是不确定用还是不用;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绑定样式</title>
<!-- 引入VUE -->
<script src="../js/"></script>
<style>
.basic{
width: 400px;
height: 100px;
border:1px solid black;
}
.happy{
background-color: red;
}
.sad{
background-color: green;
}
.normal{
background-color: pink;
}
.mod1{
font-family: '宋体';
font-size: 25px;
}
.mod2{
color: red;
}
.mod3{
background-color: pink;
}
</style>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<!-- 绑定class样式——字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div id="mode" class="basic" :class="mood" @click="changeMood">{{name}}</div>
<hr>
<!-- 绑定class样式——数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div>
<hr>
<!-- 绑定class样式——对象写法,适用于:要绑定的样式个数确定、名字也确定,但是要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷',
mood:'normal',
classArr:['mod1','mod2','mod3'],
classObj:{
mod1:false,
mod2:true
}
},
methods:{
changeMood(){
// ('mode').className = 'basic happy';
const arr = ['happy','sad','normal'];
const index = Math.floor(Math.random()*3);
this.mood = arr[index];
}
}
})
</script>
</html>
1.12.2 style绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绑定样式</title>
<!-- 引入VUE -->
<script src="../js/"></script>
<style>
.basic{
width: 400px;
height: 100px;
border:1px solid black;
}
</style>
</head>
<body>
<!--
绑定style样式
:style=”{fontSize:xxx}”,其中xxx是动态值;
:style=”[a,b]”,其中a,b是样式对象;
-->
<!-- 准备好一个容器 -->
<div id="root">
<!-- 绑定style样式 -->
<div class="basic" :style="styleObj">{{name}}</div>
<br><hr><br>
<!-- 绑定style样式 -->
<div class="basic" :style="[styleObj1,styleObj2]">{{name}}</div>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷',
styleObj:{
fontSize:'40px',
color:'red',
backgroundColor:'green'
},
styleObj1:{
fontSize:'40px',
color:'red',
},
styleObj2:{
backgroundColor:'green'
}
},
})
</script>
</html>
1.13 条件渲染
1.13.1 v-if 和 v-show
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>条件渲染</title>
<!-- 引入VUE -->
<script src="../js/"></script>
</head>
<body>
<!--
v-if:
写法:
v-if=”表达式”
v-else-if=”表达式”
v-else=”表达式”
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能够被“打断”。
v-show:
写法:
v-show=”表达式”
适用于:切换频率比较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉。
备注:使用v-if时,元素可能无法获取到,而使用v-show时一定是可以获取到的。
-->
<!-- 准备好一个容器 -->
<div id="root">
<!-- 使用v-show做条件渲染 -->
<h2 v-show="true">1欢迎来到{{name}}</h2>
<h2 v-show="a">2欢迎来到{{name}}</h2>
<h2 v-show="1==1">3欢迎来到{{name}}</h2>
<!-- 使用v-if做条件渲染 -->
<h2 v-if="false">4欢迎来到{{name}}</h2>
<h2 v-if="1==1">5欢迎来到{{name}}</h2>
<br><hr>
<h2>当前的n值是{{n}}</h2>
<button @click="n++">点我n++</button>
<hr>
v-show
<div v-show="n==1">1</div>
<div v-show="n==1">11</div>
<div v-show="n==3">3</div>
<hr>
v-if
<div v-if="n==1">1</div>
<div v-if="n==1">11</div>
<div v-if="n==3">3</div>
<hr>
v-else-if
<!-- 如果第一个成立,就不会往下再执行了 -->
<!-- 下面四个div不允许被打断,否则打断后面的就不会生效了 -->
<div v-if="n==1">1</div>
<div v-else-if="n==1">11</div>
<div v-else-if="n==3">3</div>
<div v-else>v-else其他</div>
<h2 v-show="n==1">你好</h2>
<h2 v-show="n==1">尚硅谷</h2>
<h2 v-show="n==1">北京</h2>
<!-- template不影响结构,template只能配合v-if使用 -->
<template v-if="n==1">
<h2>你好</h2>
<h2>尚硅谷</h2>
<h2>北京</h2>
</template>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷',
a:true,
n:1
}
})
</script>
</html>
1.14 列表渲染
1.14.1 基本列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.基本列表</title>
<script src="../js/"></script>
</head>
<body>
<!--
v-for指令:
用于展示列表数据
语法:v-for=”(item,index) in xxx” :key=”yyy”
可以遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
-->
<div id="root">
<!-- 遍历数组 -->
<h2>遍历数组:人员列表</h2>
<ul>
<li v-for="p in persons" :key="">
{{}}-{{}}-{{}}
</li>
</ul>
<hr>
<!-- 遍历对象 -->
<h2>遍历对象:汽车信息</h2>
<ul>
<li v-for="(value,key) of car">
{{key}}--{{value}}
</li>
</ul>
<hr>
<!-- 遍历字符串 -->
<h2>遍历字符串</h2>
<ul>
<li v-for="(char,index) of str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<hr>
<!-- 遍历指定次数 -->
<h2>遍历指定次数</h2>
<ul>
<li v-for="(num,index) of 5">
{{num}}-{{index}}
</li>
</ul>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20},
],
car:{
name:'奥迪A8',
price:'70w',
color:'黑色'
},
str:'HELLO'
}
})
</script>
</html>
1.14.2 key的原理
- 面试题: react、vue中的key有什么作用?(key的内部原理)
- 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,vue会根据新数据生成新的虚拟DOM,随后vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下。
对比规则: 旧虚拟DOM中找到了与新虚拟DOM相同的key: 若虚拟DOM中内容没变,直接使用之前的真实DOM;
若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。 旧虚拟DOM中未找到与新虚拟DOM相同的key:
创建新的真实DOM,随后渲染到页面上。
- 用index作为key可能会引发的问题:
若对数据进行:逆序添加、逆序删除等破坏顺序的操作: 会产生没有必要的真实DOM更新→界面效果没有问题,但是效率低。
如果结构中还包含输入类DOM: 会产生错误DOM更新→界面有问题。
- 开发中如何去选择key?
最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>的原理</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>遍历数组:人员列表</h2>
<!-- 点击按钮后,输入框内容会出现问题 -->
<button @="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{}}-{{}}-{{index}}
<input type="text">
</li>
</ul>
<hr>
<h2>遍历数组:人员列表</h2>
<!-- 点击按钮后,输入框内容不会出现不匹配 -->
<button @="add">添加一个老刘</button>
<ul>
<li v-for="p in persons" :key="">
{{}}-{{}}-{{}}
<input type="text">
</li>
</ul>
<hr>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20},
],
},
methods:{
add(){
const p = {id:'004',name:'老刘',age:40};
this.persons.unshift(p); //unshift()方法将新项添加到数组的开头,并返回新的长度。
}
}
})
</script>
</html>
1.14.3 列表过滤
当watch属性和computed属性都可以实现的时候,最好是使用过computed属性,用起来比较方便一些。
1.14.3.1 watch属性实现列表过滤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3.列表过滤_watch写法</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>遍历数组:人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="p in filterPersons" :key="">
{{}}-{{}}-{{}}-{{}}
</li>
</ul>
<hr>
</div>
</body>
<script>
// 用watch实现
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'马冬梅',age:18,sex:'女'},
{id:'002',name:'周冬雨',age:19,sex:'女'},
{id:'003',name:'周杰伦',age:20,sex:'男'},
{id:'004',name:'温兆伦',age:21,sex:'男'},
],
filterPersons:[]
},
watch:{
/* // 简写监视出现bug
keyWord(val){
('keyWord被改变为:'+val); //使用监听事件拿到变化的数据
// 使用filter生成新的数组替换掉原来的persons就可以实现页面效果,但是更改了原来的数据有bug
= ((p)=>{
// indexOf方法是判断字符串中存在第几位,如果返回-1则表示不存在
// indexOf一个空字符串是0
return (val) != -1;
})
} */
// 这种情况不要写成简写形式的监视
keyWord:{
immediate:true,
handler(val){
this.filterPersons = this.persons.filter((p)=>{
return p.name.indexOf(val) != -1;
})
}
}
}
})
</script>
</html>
1.14.3.2 computed属性实现列表过滤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>4.列表过滤_computed写法</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>遍历数组:人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="p in filterPersons" :key="">
{{}}-{{}}-{{}}-{{}}
</li>
</ul>
<hr>
</div>
</body>
<script>
// 用computed实现
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'马冬梅',age:18,sex:'女'},
{id:'002',name:'周冬雨',age:19,sex:'女'},
{id:'003',name:'周杰伦',age:20,sex:'男'},
{id:'004',name:'温兆伦',age:21,sex:'男'},
],
},
computed:{
filterPersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) != -1;
})
}
}
})
</script>
</html>
1.14.4 列表排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>5.列表排序</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>遍历数组:人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">原顺序</button>
<ul>
<li v-for="p in filterPersons" :key="">
{{}}-{{}}-{{}}-{{}}
</li>
</ul>
<hr>
</div>
</body>
<script>
// 用computed实现
new Vue({
el:'#root',
data:{
keyWord:'',
sortType:'0', //排序类型:0:原顺序,1:降序,2:升序。
persons:[
{id:'001',name:'马冬梅',age:40,sex:'女'},
{id:'002',name:'周冬雨',age:30,sex:'女'},
{id:'003',name:'周杰伦',age:10,sex:'男'},
{id:'004',name:'温兆伦',age:20,sex:'男'},
],
},
computed:{
filterPersons(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) != -1;
})
// 判断下是否需要排序
if(this.sortType != 0){
arr.sort((p1,p2)=>{
return this.sortType == 1 ? p2.age-p1.age : p1.age-p2.age;
})
}
return arr;
}
}
})
</script>
</html>
1.14.5 更新时的一个问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>6.更新时的一个问题</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>遍历数组:人员列表</h2>
<button @click="updateMa">更新马冬梅信息</button>
<ul>
<li v-for="p in persons" :key="">
{{}}-{{}}-{{}}-{{}}
</li>
</ul>
<hr>
</div>
</body>
<script>
// 用computed实现
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'马冬梅',age:40,sex:'女'},
{id:'002',name:'周冬雨',age:30,sex:'女'},
{id:'003',name:'周杰伦',age:10,sex:'男'},
{id:'004',name:'温兆伦',age:20,sex:'男'},
],
},
methods: {
updateMa(){
// 以下代码生效
// [0].name = '马老师',
// [0].age = 50,
// [0].sex = '男'
// 以下代码未生效
// [0] = {id:'001',name:'马老师',age:50,sex:'男'}
// 以下代码奏效
this.persons.splice(0,1,{id:'001',name:'马老师',age:50,sex:'男'});
}
},
})
</script>
</html>
1.14.6 Vue检测数据改变的一个原理——对象
1.14.7 ()方法的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>的使用</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<hr>
<h1>学生信息</h1>
<button @click="addSex">添加一个性别属性</button>
<h2>学生姓名:{{}}</h2>
<h2 v-if="">学生性别:{{}}</h2>
<h2>学生年龄:真实:{{}},对外:{{}}</h2>
<h2>朋友们</h2>
<ul>
<li v-for="f in " :key="index">
{{}}---{{}}
</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
address:'北京',
student:{
name:'tom',
age:{
rAge:40,
sAge:29,
},
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addSex(){
// 两种方法都可以实现
// (,'sex','男')
this.$set(this.student,'sex','男')
}
},
})
</script>
</html>
1.14.8 Vue检测数据改变的一个原理——数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>监测数据改变的原理_数组</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<hr>
<h1>学生信息</h1>
<button @click="addSex">添加一个性别属性</button>
<h2>学生姓名:{{}}</h2>
<h2 v-if="">学生性别:{{}}</h2>
<h2>学生年龄:真实:{{}},对外:{{}}</h2>
<h2>爱好:</h2>
<ul>
<li v-for="(h,index) in ">
{{h}}
</li>
</ul>
<h2>朋友们</h2>
<ul>
<li v-for="f in " :key="index">
{{}}---{{}}
</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
address:'北京',
student:{
name:'tom',
age:{
rAge:40,
sAge:29,
},
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addSex(){
// 两种方法都可以实现
// (,'sex','男')
this.$set(this.student,'sex','男')
}
},
})
</script>
</html>
1.14.9 Vue数据监测总结
- Vue会监视data中所有层次的数据.
- 如何监测对象中的数据?
如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1)对象中后追加的属性,vue默认不做响应式处理。
(2)如需要给后添加的属性做响应式,请使用如下的API:
(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
- 如何监测数组中的数据?
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新。
(2)重新解析模板,进而更新页面。
- 在Vue修改数组中的某个元素一定要用如下方法:
在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)() 或 vm.$set()
特别注意:() 和 vm.$set() 不能给vm的根数局对象添加属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>10.总结Vue数据监测</title>
<script src="../js/"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1>
<button @click="++">年龄+1</button><br>
<button @click="addSex">添加性别属性,默认值:男</button><br>
<button @click=" ='未知' ">修改性别</button><br>
<button @click="addFriends">在列表首页添加一位朋友</button><br>
<button @click="updateFristFriendName">修改第一个朋友的名字为:张三</button><br>
<button @click="addHobby">添加一个爱好</button><br>
<button @click="updateFristHobby">修改第一个爱好为:开车</button><br>
<h3>姓名:{{}}</h3>
<h3>年龄:{{}}</h3>
<h3 v-if="">性别:{{}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in " ::key="index">
{{h}}
</li>
</ul>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in " ::key="index">
{{}}---{{}}
</li>
</ul>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
student:{
name:'tom',
age:18,
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addSex(){
// (,'sex','男')
this.$set(this.student,'sex','男')
},
addFriends(){
this.student.friends.unshift({name:'jack',age:'70'})
},
updateFristFriendName(){
this.student.friends[0].name = '张三'
},
addHobby(){
this.student.hobby.push('学习')
},
updateFristHobby(){
// (0,1,'开车')
// (,0,'开车')
this.$set(this.student.hobby,0,'开车')
}
},
})
</script>
</html>
1.15 收集表单数据
收集表单数据:
(1)若:<input type="text"/> ,则v-model收集的是value值,用户输入的就是value值。
(2)若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
(3)若:<input type="checkbox"/>
1)没有配置input的value属性,那么收集的就是checked(勾选or未勾选,值是布尔值)
2)配置input的value属性:
[1]v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,值是布尔值)
[2]v-model的初始值是数组,那么收集的就是value组成的数组。
v-model的三个修饰符:
(1)lazy:失去焦点再收集数据;
(2)number:输入字符串转为有效的数字;
(3)trim:输入首尾空格过滤;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>收集表单数据</title>
<script src="../js/"></script>
</head>
<body>
<!-- 收集表单数据:
若:<input type="text"/> ,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选or未勾选,值是布尔值)
2.配置input的value属性:
(1) v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,值是布尔值)
(2) v-model的初始值是数组,那么收集的就是value组成的数组。
备注:
v-model的三个修饰符:
lazy:失去焦点再收集数据;
number:输入字符串转为有效的数字;
trim:输入首尾空格过滤; -->
<div id="root">
<form @="demo">
<!-- 这样写点击账号标签也能将光标聚集到输入框 -->
<label for="demo1">账号:</label>
<input type="text" id="demo1" =""><br>
<label for="demo2">密码:</label>
<input type="password" id="demo2" v-model=""><br>
性别: <br>
男<input type="radio" value="male" v-model="">
女<input type="radio" value="female" v-model=""><br>
年龄:<input type="number" ="">
爱好:<br>
抽烟<input type="checkbox" value="抽烟" v-model="">
喝酒<input type="checkbox" value="喝酒" v-model="">
烫头<input type="checkbox" value="烫头" v-model=""><br>
所属校区:
<select name="" id="" v-model="">
<option value="">请选择校区</option>
<option value="beijing" >北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select><br>
其他信息:
<!-- 失去焦点的瞬间收集信息 -->
<textarea =""></textarea><br>
<input type="checkbox" v-model=""> 阅读并接受<a href="">用户协议</a>
<button>提交</button>
</form>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
sex:'female',
age:'',
hobby:[],
city:'',
other:'',
isAgree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this._data));
}
},
})
</script>
</html>
1.16 过滤器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>过滤器</title>
<script src="../js/"></script>
<script src="../js/"></script>
</head>
<body>
<!-- dayjs()
.startOf('month')
.add(1, 'day')
.set('year', 2018)
.format('YYYY-MM-DD HH:mm:ss') -->
<!--
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
1.注册过滤器:(name,callback) 或 new Vue(filters:{})
2.使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
1.过滤器也可以接受额外参数、多个过滤器也可以串联。
2.并没有改变原本的数据,是产生新的对应的数据。
-->
<div id="root">
<h2>显示格式化后的时间</h2>
<h3>computed</h3><br>
<h3>现在时间:{{timeFormatComputed}}</h3><hr>
<h3>methods</h3>
<h3>现在时间:{{timeFormatMethods()}}</h3><hr>
<h3>filters</h3>
<h3>现在时间:{{time | timeFormatFilters}}</h3><hr>
<h3>filters(传参)</h3>
<h3>现在时间:{{time | timeFormatFilters('YYYY-MM-DD')}}</h3><hr>
<h3>现在时间:{{time | timeFormatFilters}}</h3><hr>
<h3>filters(传参)串联</h3>
<h3>现在时间:{{time | timeFormatFilters('YYYY-MM-DD') | mySlice}}</h3><hr>
</div>
<div id="root2">
<h3>{{str | mySlice}}</h3><hr>
<h3 :x="str | mySlice">看属性,过滤器不只是适用于插值</h3>
</div>
</body>
<script>
Vue.filter('mySlice',function(value){
return value.slice(0,4);
})
new Vue({
el:'#root',
data:{
time:1621561377603 //时间戳,()获取到,注意time不能赋值成字符串,不然时间不对。
},
computed:{
timeFormatComputed(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
methods: {
timeFormatMethods(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
// 局部过滤器
filters:{
timeFormatFilters(value,str='YYYY-MM-DD HH:mm:ss'){
return dayjs(value).format(str);
},
// mySlice(value){
// return (0,4);
// }
}
})
new Vue({
el:'#root2',
data:{
str:'Hello! nihao!'
}
})
</script>
</html>
1.17 Vue内置指令
1.17.1 v-text指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-text指令</title>
<script src="../js/"></script>
</head>
<body>
<!--
学过的指令:
v-bind:单向绑定解析表达式,可简写为:xxx
v-model:双向数据绑定
v-for:遍历数组、对象、字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存在)
v-else:条件渲染(动态控制节点是否存在)
v-show:条件渲染(动态控制节点是否展示)
-->
<!--
v-text指令:
1.作用:向其所在的节点中渲染文本内容。
2.与插值语法的区别:v-text会替换掉节点中的内容,{{xxx}}插值语法则不会替换掉。
-->
<div id="root">
<div>{{name}}</div>
<div v-text="name">这段内容不显示,显示的是name</div>
<div v-text="str"></div>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷',
str:'<h3>你好</h3>'
}
})
</script>
</html>
1.17.2 v-html指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-html指令</title>
<script src="../js/"></script>
</head>
<body>
<!--
v-html指令:
1.作用:向指定节点中渲染包含html结构的内容。
2.与插值语法的区别:
(1) v-html会替换掉节点中的所有内容,{{xxx}}则不会。
(2) v-html可以识别html结构。
3.严重注意,v-html有安全性的问题。
(1) 在网站上动态渲染任意HTML是非常危险的,容易导致KSS攻击。
(2) 一定要在可信的内容上使用v-html,永远不要在用户提交的内容区使用v-html。
-->
<div id="root">
<div>{{name}}</div>
<!-- v-html支持结构性解析与v-text不同 -->
<div v-html="str"></div>
<div v-html="str2"></div>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷',
str:'<h3>你好</h3>',
str2:'<a href=javascript:="?"+>兄弟我找到你想要的东西了</a>'
}
})
</script>
</html>
1.17.3 v-cloak指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-cloak指令</title>
<!-- 此处应该给一个延时的来测试 -->
<script src="../js/"></script>
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<!--
v-cloak指令:
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删除掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
-->
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
1.17.4 v-once指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-once指令</title>
<script src="../js/"></script>
</head>
<body>
<!--
v-once指令:
-once指令所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
-->
<div id="root">
<h2 v-once="n">初试的n值是:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
1.17.5 v-pre指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-pre指令</title>
<script src="../js/"></script>
</head>
<body>
<!--
v-pre指令:
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
-->
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2 v-pre>当前的n值是:{{n}}</h2>
<button v-pre @click="n++">点我n+1</button>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
1.18 Vue自定义指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义指令</title>
<script src="../js/"></script>
</head>
<body>
<!--
需求1:定义一个v-big指令,和v-text功能类似,但是会把绑定的数值放大10倍。(函数式)
需求2:定义一个v-fbind指令,和v-bind功能类似,但是可以让其所绑定的input元素默认获取焦点。(对象式)
-->
<!--
自定义指令总结:
一、定义语法:
(1)局部指令:
new Vue({
directive:{指令名:配置对象}
})
或者
new Vue({
directives{指令名:回调函数}
})
(2)全局指令:
(指令名,配置对象)
或者
(指令名,回调函数)
二、配置对象中常用的3个回调:
(1)bind:指令与元素成功绑定时回调。
(2)inserted:指令所在元素被插入页面时调用。
(3)update:指令所在模块结构被重新解析时调用。
三、备注:
(1)指令定义时不加v-,但是使用的时候要加v-;
(2)指令名如果是多个单词,要使用kebab-case命名方式,不要用小驼峰cameCase命名。
-->
<div id="root">
<h2>当前的n值是:<span v-text="n"></span></h2>
<h2>放大10倍的n值是:<span v-big="n"></span></h2>
<h2>放大10倍的n值是:<span v-big-number="n"></span></h2>
<button @click="n++">n+1</button>
<hr>
<input type="text" v-bind:value="n">
<input type="text" v-fbind2:value="n">
</div>
<div id="root2">
<hr>
<h2>全局的自定义指令</h2>
<input type="text" v-fbind:value="x">
<h2>全局的自定义指令——函数写法</h2>
<h2>放大10倍的n值是:<span v-big-all="x"></span></h2>
</div>
</body>
<script>
// 全局的自定义指令
Vue.directive('fbind',{
// 指令与元素成功绑定时
bind(element,binding){
console.log('fbind-bind',this);
console.log('bind');
element.value = binding.value;
},
// 指令所在元素被插入页面时。
inserted(element,binding){
console.log('fbind-inserted',this);
console.log('insert');
element.focus();
},
// 指令所在模板被重新解析时
update(element,binding){
console.log('fbind-update',this);
console.log('update');
element.value = binding.value;
}
})
Vue.directive('big-all',function(element,binding){
console.log('big',this); //指令的this都是window
console.log('big');
element.innerText = binding.value * 10;
})
new Vue({
el:'#root',
data:{
n:1
},
directives:{
// big函数何时会被调用?
// 1、指令与元素成功绑定时调用(一开始)。
// 2、指令所在的模板被重新解析时。
big(element,binding){
console.log('big',this); //指令的this都是window
console.log('big');
element.innerText = binding.value * 10;
},
// 不建议使用驼峰命名法bingNumber,建议使用big-number,但是要加引号
'big-number'(element,binding){
console.log('big');
element.innerText = binding.value * 10;
},
fbind2:{
// 指令与元素成功绑定时
bind(element,binding){
console.log('fbind-bind',this);
console.log('bind');
element.value = binding.value;
},
// 指令所在元素被插入页面时。
inserted(element,binding){
console.log('fbind-inserted',this);
console.log('insert');
element.focus();
},
// 指令所在模板被重新解析时
update(element,binding){
console.log('fbind-update',this);
console.log('update');
element.value = binding.value;
}
}
}
})
new Vue({
el:'#root2',
data:{
x:1
}
})
</script>
</html>
1.19 生命周期
1.19.1 引出生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>引出声明周期</title>
<script src="../js/"></script>
</head>
<body>
<!--
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么?Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.声明周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm或组件实例对象。
-->
<div id="root">
<!-- :style="{opacity: opacity}"动态绑定需要写冒号,里面的数据需要写成对象的格式 -->
<h2 :style="{opacity: opacity}">欢迎学习Vue</h2>
</div>
</body>
<script>
/*
// 通过外部的定时器可以实现,但是不推荐
const vm = new Vue({
el:'#root',
data:{
opacity:1
}
})
setInterval(()=>{
-= 0.1;
if( <= 0){
= 1;
}
},100)
*/
new Vue({
el:'#root',
data:{
opacity:1
},
methods: {
},
// Vue完成模板的解析并把初试的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted() {
setInterval(()=>{
this.opacity -= 0.1;
if(this.opacity<=0){
this.opacity = 1;
}
},100)
},
})
</script>
</html>
1.19.2 分析生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
1.19.3 总结声明周期
vm的生命周期:
将要创建==>调用beforeCreate函数
创建完毕==>调用Created函数
将要挂载==>调用beforeMount函数
挂载完毕==>调用Mounted函数==>重要钩子
将要更新==>调用beforeUpdate函数
更新完毕==>调用update函数
将要销毁==>调用beforeDestory函数==>重要钩子
销毁完毕==>调用Destory函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<!--
常用的生命周期钩子:
:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】
:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】
关于销毁Vue实例:
1.销毁后借助Vue开发者工具看不到任何消息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即使操作数据,也不会再出发更新流程了。
-->
<!-- 准备好一个容器 -->
<div id="root">
<h2 :style="{opacity}">欢迎学习Vue</h2>
<button @click="opacity = 1">透明度设置为1</button>
<button @click="stop">点我停止变换</button>
</div>
</body>
<script>
new Vue({
el:'#root',
data:{
opacity:1
},
methods: {
stop(){
this.$destroy();
}
},
// Vue完成模板的解析并把初试的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted(){
console.log('mounted',this);
this.timer = setInterval(() => {
this.opacity -= 0.1;
if (this.opacity<=0)
{
this.opacity = 1;
}
},16)
},
beforeDestroy() {
console.log('vm即将被销毁了');
// 清除定时器
clearInterval(this.timer)
},
})
</script>
</html>
组件化编程
2.1 非单文件组件
2.1.1 基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<!--
Vue中使用组件的三大步骤:
1.定义组件(创建组件)
2.注册组件
3.使用组件
一、如何定义一个组件?
使用(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别。
区别如下:
不要写。原因:最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
必须要写成函数。原因:避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
二、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项。
2.全局注册:靠('组件名',组件)
三、编写组件标签
例如:<school></school>
-->
<!-- 准备好一个容器 -->
<div id="root">
<student></student>
<hr>
<school></school>
<hr>
<hello></hello>
<hr>
</div>
<div id="root2">
<hello></hello>
</div>
</body>
<script>
// 创建学校组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showSchoolName">点我提示学校名<button>
</div>
`,
// 一定不要写el配置项,因为最终所有的组件都要被vm管理,由vm决定服务于哪个对象
// el:'#root',
data(){
return {
schoolName:'尚硅谷',
address:'北京昌平',
}
},
methods: {
showSchoolName(){
alert(this.schoolName)
}
},
})
// 创建学生组件
const student = Vue.extend({
template:`
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
// 一定不要写el配置项,因为最终所有的组件都要被vm管理,由vm决定服务于哪个对象
// el:'#root',
data(){
return {
studentName:'张三',
age:18
}
}
})
// 创建hello组件
const hello = Vue.extend({
template:`
<div>
<h2>你好啊!{{name}}</h2>
</div>
`,
data(){
return {
name:'Tom'
}
}
})
// 注册组件(全局注册)
Vue.component('hello',hello)
// 注册组件
new Vue({
el:'#root',
// 注册组件(局部注册)
components:{
school,
student
}
})
new Vue({
el:'#root2',
})
</script>
</html>
2.1.2 几个注意点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<!--
几个注意点:
1.关于组件名:
一个单词组成时:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成时:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool(需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注:不用使用脚手架时,<school/>会导致后序组件不能渲染。
3.一个简写方式:
const school = (options) 可简写为:const school = options
-->
<!-- 准备好一个容器 -->
<div id="root">
<my-school></my-school>
</div>
</body>
<script>
const school = Vue.extend({
name:'atguigu',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<hr>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
new Vue({
el:'#root',
data:{
},
components:{
'my-school':school,
}
})
</script>
</html>
2.1.3 组件的嵌套
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 -->
<script src="../js/"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
</div>
</body>
<script>
// 定义一个student组件
const student = Vue.extend({
name:'student',
template:`
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
<hr>
</div>
`,
data(){
return {
name:'张三',
age:18
}
}
})
// 定义school组件
const school = Vue.extend({
name:'school',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
<hr>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
},
components:{
student
}
})
// 定义一个hello组件
const hello = Vue.extend({
template:`
<h1>{{msg}}</h1>
`,
data(){
return {
msg:'Hello'
}
}
})
// 定义app组件
const app = Vue.extend({
template:`
<div>
<school></school>
<hello></hello>
</div>
`,
components:{
school,
hello
}
})
// 创建Vue
new Vue({
template:`
<app></app>
`,
el:'#root',
// 注册组件
components:{
app
}
})
</script>
</html>
2.1.4 VueComponent
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>VueComponent</title>
<!-- 引入 -->
<script src="../"></script>
</head>
<body>
<!--
关于VueComponent:
1、school组件的本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue。extend生成的。
2、我们只需要写<school/>或者<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options).
3、特别注意:每次调用,返回的都是一个全新的VueComponent。
4、关于this指向:
(1)组件配置中
data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是【VueComponent实例对象】。
(2)new Vue()配置中:
data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是【Vue实例对象】。
5、VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm。
-->
<!-- 准备好一个容器 -->
<div id="root">
<school></school>
<hello/>
</div>
</body>
<script>
Vue.config.productionTip = false
// 定义school组件
const school = Vue.extend({
name:'school',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我显示this是谁</button>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
},
methods:{
showName(){
console.log('showName',this)
}
}
})
// 定义hello组件
const hello = Vue.extend({
template:`<h2>{{msg}}</h2>`,
data(){
return{
msg:'你好啊!'
}
}
})
console.log('@',school);
console.log('#',hello);
// 创建vm
new Vue({
el:'#root',
components:{school,hello}
})
</script>
</html>
2.2 单文件组件
文件
<template>
<!-- 组件的结构 -->
<div class="demo">
<h2>学校名称:{{schoolName}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
//组件交互相关的代码(数据、方法等等)
export default {
name:'School',
data(){
return {
schoolName:'尚硅谷'
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
}
</script>
<style>
/* 组件的样式 */
.demo{
background-color: orange
}
</style>
文件
<template>
<div>
<school></school>
</div>
</template>
<script>
// 引入组件
import School from './'
export default {
name:'App',
components:{
School
}
}
</script>
<style>
</style>
文件
import App from './'
new Vue({
el:'#root',
template:`<App></App>`,
component:{App},
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>测试一下单文件组件语法</title>
</head>
<body>
<div id='root'></div>
<script src="../"></script>
<script src="./"></script>
</body>
</html>
3.使用Vue脚手架
3.1 初始化脚手架
3.1.1 文档说明
/zh/
3.1.2 操作步骤
- 安装淘宝镜像
npm config set registry
- 全局安装@vue/cl
npm install -g @vue/cli
- 切换到创建项目的目录下,创建项目
vue create demo
- 启动项目
npm run serve
3.1.3 模板项目结构
3.2 ref与props
-
ref
- 作用:用于给节点打标识。
- 读取方式:
-
props
- 作用:用于父组件给子组件传递数据
- 读取方式一:只指定名称
- 读取方式二:指定名称和类型
- 读取方式三:指定名称、类型、必要性、默认值
中的ajax
-router
UI组件库
Vue 3.0 部分
Vue 3.0 简介
1. 创建Vue3.0工程
1.1 使用vue-cli创建
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
1.2 使用vite创建
官方文档:/guide/#vite
vite官网:
- 什么是vite?—— 新一代前端构建工具。
- 优势如下:
- 开发环境中,无需打包操作,可快速的冷启动。
- 轻量快速的热重载(HMR)。
- 真正的按需编译,不再等待整个应用编译完成。
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
2. 常用Composition API
2.1 setup
- 理解:Vue3.0中一个新的配置项,值为一个函数。
- setup是所有Composition API(组合API)“ 表演的舞台 ”。
- 组件中所用到的:数据、方法等等,均要配置在setup中。
- setup函数的两种返回值:
- 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
- 若返回一个渲染函数:则可以自定义渲染内容。(了解)
- 注意点:
- 尽量不要与配置混用
- 配置(data、methos、computed…)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到配置(data、methos、computed…)。
- 如果有重名, setup优先。
- setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
- 尽量不要与配置混用
<template>
<h1>我是一个人</h1>
<h2>name:{{ name }}</h2>
<h2>age:{{ age }}</h2>
<button @click="sayHello()">点我说话</button>
</template>
<script>
export default {
name: 'App',
// 测试setup,暂时不考虑响应问题
setup() {
// 数据
let name = '张三'
let age = 18
// 方法
function sayHello() {
alert(`我叫${name},我今年${age}岁了!`)
}
// 返回一个对象
return {
name,
age,
sayHello
}
}
}
</script>
<style>
</style>
运行结果:
返回函数(渲染函数)
加上返回一个渲染函数以后,页面中的渲染全部失效,变为渲染函数中的渲染结果。
<template>
<h1>我是一个人</h1>
<h2>name:{{ name }}</h2>
<h2>age:{{ age }}</h2>
<button @click="sayHello()">点我说话</button>
</template>
<script>
import { h } from 'vue'
export default {
name: 'App',
// 测试setup,暂时不考虑响应问题
setup() {
// 数据
let name = '张三'
let age = 18
// 方法
function sayHello() {
alert(`我叫${name},我今年${age}岁了!`)
}
// 返回一个对象
// return {
// name,
// age,
// sayHello
// }
// 返回一个函数(渲染函数)
return () => h("h1", '你好,尚硅谷')
}
}
</script>
<style>
</style>
运行结果:
2.2 ref函数
- 作用: 定义一个响应式的数据
- 语法:
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据:
- 模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
- 备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
()
的get
与set
完成的。 - 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
2.2.1 ref函数处理基本类型
<template>
<h1>我是一个人</h1>
<h2>name:{{ name }}</h2>
<h2>age:{{ age }}</h2>
<button @click="changeInfo()">点我修改个人信息</button>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
// 测试setup,暂时不考虑响应问题
setup() {
// 数据
let name = ref('张三')
let age = ref(18)
// 方法
function changeInfo() {
name.value = '李四'
age.value = 20
}
// 返回一个对象
return {
name,
age,
changeInfo
}
}
}
</script>
<style>
</style>
运行结果:
点击按钮后,显示的“张三 18”变成了“李四 20” 。
2.2.2 ref函数处理对象类型
<template>
<h1>我是一个人</h1>
<h2>name:{{ name }}</h2>
<h2>age:{{ age }}</h2>
<h3>工作种类:{{ }}</h3>
<h3>工作薪资:{{ }}</h3>
<button @click="changeInfo()">点我修改个人信息</button>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
// 测试setup,暂时不考虑响应问题
setup() {
// 数据
let name = ref('张三')
let age = ref(18)
let job = ref({
type:'前端工程师',
salary:'30K'
})
// 方法
function changeInfo() {
name.value = '李四'
age.value = 20
job.value.type = 'UI设计师'
job.value.salary = '60K'
}
// 返回一个对象
return {
name,
age,
job,
changeInfo
}
}
}
</script>
<style>
</style>
运行结果:
2.3 reactive
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) - 语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) - reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
ref结合reactive使用的写法
<template>
<h1>我是一个人</h1>
<h2>name:{{ name }}</h2>
<h2>age:{{ age }}</h2>
<h3>工作种类:{{ }}</h3>
<h3>工作薪资:{{ }}</h3>
<button @click="changeInfo()">点我修改个人信息</button>
</template>
<script>
import { reactive, ref } from 'vue'
export default {
name: 'App',
setup() {
// 数据
let name = ref('张三')
let age = ref(18)
let job = reactive({
type:'前端工程师',
salary:'30K'
})
// 方法
function changeInfo() {
name.value = '李四'
age.value = 20
job.type = 'UI设计师'
job.salary = '60K'
}
// 返回一个对象
return {
name,
age,
job,
changeInfo
}
}
}
</script>
<style>
</style>
运行结果:
只使用reactive的一种写法
<template>
<h1>我是一个人</h1>
<h2>姓名:{{ }}</h2>
<h2>年龄:{{ }}</h2>
<h3>工作种类:{{ }}</h3>
<h3>工作薪资:{{ }}</h3>
<h3>爱好:{{ }}</h3>
<button @click="changeInfo()">点我修改个人信息</button>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
// 数据
let person = reactive({
name: '张三',
age: 18,
job: {
type: '前端工程师',
salary: '30K'
},
hobby: ['抽烟', '喝酒', '烫头']
})
// 方法
function changeInfo() {
person.name = '李四'
person.age=20
person.job.type='UI设计师'
person.job.salary = '60K'
person.hobby[0] = '学习'
}
// 返回一个对象
return {
person,
changeInfo
}
}
}
</script>
<style>
</style>
运行结果
2.4 Vue3.0中的响应式原理
2.4.1 的响应式
-
实现原理:
-
对象类型:通过
()
对属性的读取、修改进行拦截(数据劫持)。 -
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', { get () {}, set () {} })
-
-
存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
2.4.2 Vue3.0的响应式
-
实现原理:
- 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射): 对源对象的属性进行操作。
- MDN文档中描述的Proxy与Reflect:
-
Proxy:/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
-
Reflect:/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
-
<template>
<h1>我是一个人</h1>
<h2 v-show="">姓名:{{ }}</h2>
<h2 v-show="" >性别:{{ }}</h2>
<button @click="addSex()">添加一个sex属性</button><br>
<button @click="deleteName()">删除一个name属性</button>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
// 数据
let person = reactive({
name: '张三',
sex:'',
})
// 方法
function addSex(){
person.sex = '男'
}
function deleteName(){
delete person.name
}
// 返回一个对象
return {
person,
addSex,
deleteName
}
}
}
</script>
<style>
</style>
运行结果:
2.5 reactive对比ref
- 从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive
转为代理对象。
- 从原理角度对比:
- ref通过
()
的get
与set
来实现响应式(数据劫持)。 - reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- ref通过
- 从使用角度对比:
- ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
。 - reactive定义的数据:操作数据与读取数据:均不需要
.value
。
- ref定义的数据:操作数据需要
2.6 setup的两个注意点
-
setup执行的时机
- 在beforeCreate之前执行一次,this是undefined。
-
setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 - slots: 收到的插槽内容, 相当于
this.$slots
。 - emit: 分发自定义事件的函数, 相当于
this.$emit
。
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
2.6.1 测试beforeCreate()和setup()谁先启动
<template>
</template>
<script>
export default {
name: 'App',
beforeCreate() {
console.log('---beforeCreate---')
},
setup() {
console.log('---setup---')
}
}
</script>
<style>
</style>
运行结果:
2.6.2 setup的参数
<template>
<Demo @hello="showhelloMsg" msg="你好啊" school="尚硅谷">
<template v-slot:qwe>
<span>尚硅谷</span>
</template>
</Demo>
</template>
<script>
import Demo from './components/'
export default {
name: 'App',
components: {
Demo
},
setup() {
function showhelloMsg(value) {
alert(`你好,你触发了hello事件,我收到的参数是:${value}`)
}
return {
showhelloMsg
}
}
}
</script>
<style>
</style>
<template>
<h1>Demo</h1>
<button @click="test()">测试触发一下Demo组件的Hello事件</button>
</template>
<script>
export default {
name: 'Demo',
props: ['msg', 'school'],
emits: ['hello'],
setup(props, context) {
console.log('---setup---', props)
console.log('---setup---', context)
console.log('---setup---', context.attrs) //相当于Vue2中的$attrs
console.log('---setup---', context.emit) //触发自定义事件
console.log('---setup---', context.slots) //插槽
function test() {
context.emit('hello', 666)
}
// 返回一个对象
return {
test
}
}
}
</script>
<style>
</style>
运行结果:
2.7 计算属性与监视
2.7.1 计算属性
<template>
<h1>一个人</h1>
姓:<input type="text" v-model=""> <br>
名:<input type="text" v-model=""> <br>
<span>全名:{{ }}</span>
</template>
<script>
import { reactive, computed } from 'vue';
export default {
name: 'App',
setup() {
let person = reactive({
firstName: '张',
lastName: '三',
})
// 计算属性
person.fullName = computed(() => {
return person.firstName + person.lastName
})
return {
person
}
}
}
</script>
<style>
</style>
运行截图:
2.7.2 监视
-
两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
2.7.2.1 情况一:监视ref所定义的一个响应式数据
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点我+1</button>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let sum = ref(0)
// 情况一:监视ref所定义的一个响应式数据
watch(sum,(newValue,oldValue)=>{
console.log(`sum的值变了${oldValue}=>${newValue}`)
})
return {
sum
}
}
}
</script>
<style>
</style>
运行结果:
2.7.2.2 情况二:监视ref所定义的多个响应式数据
(1)分开监视
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点我+1</button>
<hr>
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg += '!'">点我修改信息</button>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let sum = ref(0)
let msg = ref('你好')
// 情况二:监视ref所定义的多个响应式数据
// (1)分开监视
watch(sum,(newValue,oldValue)=>{
console.log(`sum的值变了${oldValue}=>${newValue}`)
})
watch(msg,(newValue,oldValue)=>{
console.log(`sum的值变了${oldValue}=>${newValue}`)
})
return {
sum,
msg
}
}
}
</script>
<style>
</style>
运行结果:
(2)合并起来监视
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点我+1</button>
<hr>
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg += '!'">点我修改信息</button>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let sum = ref(0)
let msg = ref('你好')
// 情况二:监视ref所定义的多个响应式数据
// (2)合起来监视
watch([sum,msg],(newValue,oldValue)=>{
console.log(`sum或msg的值变了${oldValue}=>${newValue}`)
})
return {
sum,
msg
}
}
}
</script>
<style>
</style>
运行结果:
2.7.2.3 watch的其他参数
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点我+1</button>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let sum = ref(0)
// 其他参数
watch(sum, (newValue, oldValue) => {
console.log(`sum的值变了${oldValue}=>${newValue}`)
}, { immediate: true })
return {
sum
}
}
}
</script>
<style>
</style>
运行结果:
2.7.2.4 情况三:监视reactive所定义的一个响应式的全部数据
<template>
<h2>姓名:{{ }}</h2>
<h2>年龄:{{ }}</h2>
<h2>薪资:{{ . }}</h2>
<button @click=" += '!'">点我修改姓名</button> <br>
<button @click="++">点我修改年龄</button>
<button @click=".++">点我涨工资</button>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let sum = ref(0)
let msg = ref('你好')
let person = reactive({
job:{
job1:{
salary:20
}
}
})
/*
情况三:监视reactive所定义的一个响应式的全部数据
注意:
1、无法正确的获得oldValue
2、强制开启了深度检测(deep配置无效)
*/
watch(person, (newValue, oldValue) => {
console.log('person变化了', newValue, oldValue)
}, { deep: false })
return {
person
}
}
}
</script>
<style>
</style>
运行截图:
2.7.2.5 情况四:监视reactive所定义的一个响应式数据中的某个属性
<template>
<h2>姓名:{{ }}</h2>
<h2>年龄:{{ }}</h2>
<h2>薪资:{{ . }}</h2>
<button @click=" += '!'">点我修改姓名</button> <br>
<button @click="++">点我修改年龄</button> <br>
<button @click=".++">点我涨工资</button>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: '18',
job: {
job1: {
salary: 20
}
}
})
/*
情况四:监视reactive所定义的一个响应式数据中的某个属性
*/
watch(
() => person.age,
(newValue, oldValue) => {
console.log('person变化了', newValue, oldValue)
}
)
return {
person
}
}
}
</script>
<style>
</style>
运行截图:
2.7.2.6 情况五:监视reactive所定义的一个响应式数据中的某些数据
<template>
<h2>姓名:{{ }}</h2>
<h2>年龄:{{ }}</h2>
<h2>薪资:{{ . }}</h2>
<button @click=" += '!'">点我修改姓名</button> <br>
<button @click="++">点我修改年龄</button> <br>
<button @click=".++">点我涨工资</button>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: '18',
job: {
job1: {
salary: 20
}
}
})
/*
情况五:监视reactive所定义的一个响应式数据中的某些数据
*/
watch(
[() => person.name,() => person.age],
(newValue, oldValue) => {
console.log('person变化了', newValue, oldValue)
}
)
return {
person
}
}
}
</script>
<style>
</style>
运行结果:
2.7.2.7 特殊情况:
<template>
<h2>姓名:{{ }}</h2>
<h2>年龄:{{ }}</h2>
<h2>薪资:{{ . }}</h2>
<button @click=" += '!'">点我修改姓名</button> <br>
<button @click="++">点我修改年龄</button> <br>
<button @click=".++">点我涨工资</button>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: '18',
job: {
job1: {
salary: 20
}
}
})
// 特殊情况
watch(
()=>person.job.job1.salary,
(newValue, oldValue) => {
console.log('person变化了', newValue, oldValue)
},
{deep:true} // 由于监视的是reactive定义的对象中的某个属性,所以deep配置有效
)
return {
person
}
}
}
</script>
<style>
</style>
运行结果:
2.7.3 watchEffect函数
-
watch的套路是:既要指明监视的属性,也要指明监视的回调。
-
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
<template>
<h2>姓名:{{ }}</h2>
<h2>年龄:{{ }}</h2>
<h2>薪资:{{ . }}</h2>
<button @click=" += '!'">点我修改姓名</button> <br>
<button @click="++">点我修改年龄</button> <br>
<button @click=".++">点我涨工资</button>
</template>
<script>
import { reactive, ref, watch, watchEffect } from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: '18',
job: {
job1: {
salary: 20
}
}
})
// watchEffect()函数里面用到的数据发生改变时监视谁
watchEffect(()=>{
const x1 = person.job.job1.salary
console.log('watchEffect所指定的回调执行了')
})
return {
person
}
}
}
</script>
<style>
</style>
运行结果:
2.8 生命周期
- Vue3.0中可以继续使用中的生命周期钩子,但有有两个被更名:
-
beforeDestroy
改名为beforeUnmount
-
destroyed
改名为unmounted
-
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与中钩子对应关系如下:
-
beforeCreate
===>setup()
-
created
=======>setup()
-
beforeMount
===>onBeforeMount
-
mounted
=======>onMounted
-
beforeUpdate
===>onBeforeUpdate
-
updated
=======>onUpdated
-
beforeUnmount
==>onBeforeUnmount
-
unmounted
=====>onUnmounted
-
2.9 自定义hook函数
-
什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
-
类似于中的mixin。
-
自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
-
封装目录:src\hooks\
代码举例
把setup函数中使用的Composition API封装到别的文件中,然后通过导入引用的方式,提高代码复用性。
(1)
<template>
<button @click="isShowDemo = !isShowDemo">切换隐藏或者显示Demo</button>
<Demo v-if="isShowDemo"></Demo>
</template>
<script>
import Demo from './components/'
import {ref} from 'vue'
export default {
name: 'App',
components:{
Demo
},
setup(){
let isShowDemo = ref(true)
return {
isShowDemo
}
}
}
</script>
<style>
</style>
(2)
<template>
<h2>当前点击鼠标时的坐标为:x={{ }},y={{ }}</h2>
</template>
<script>
import usePoint from '../hooks/usePoint'
export default {
name: 'Demo',
setup() {
let point = usePoint()
return {
point
}
}
}
</script>
<style>
</style>
(3)
import { reactive, onMounted, onBeforeUnmount } from "vue"
export default function savePoint() {
// 实现鼠标打点的相关数据
let point = reactive({
x: 0,
y: 0
})
// 实现鼠标打点的相关方法
function savePoint(event) {
=
=
(, )
}
// 实现鼠标打点的相关生命周期钩子
onMounted(() => {
('click', savePoint)
})
onBeforeUnmount(() => {
('click', savePoint)
})
return point
}
运行截图
2.10 toRef与toRefs
- 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
- 语法:
const name = toRef(person,'name')
- 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
- 扩展:
toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
2.10.1 toRef
代码示例
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ salary }}K</h2>
<button @click="name += '!'">修改姓名</button>
<button @click="age++">修改年龄</button>
<button @click="salary += 10">修改薪资</button>
</template>
<script>
import { reactive, toRef } from 'vue';
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: 18,
job: {
job1: {
salary: 20
}
}
})
return {
name:toRef(person,'name'),
age:toRef(person,'age'),
salary:toRef(person.job.job1,'salary')
}
}
}
</script>
<style>
</style>
运行结果
2.10.2 toRefs
示例代码
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job. }}K</h2>
<button @click="name += '!'">修改姓名</button>
<button @click="age++">修改年龄</button>
<button @click="job. += 10">修改薪资</button>
</template>
<script>
import { reactive, toRefs } from 'vue';
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: 18,
job: {
job1: {
salary: 20
}
}
})
return {
// ...为扩展运算符
...toRefs(person)
}
}
}
</script>
<style>
</style>
运行结果:
3. 其他Composition API
3.1 shallowReactive与shallowRef
-
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
-
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
-
什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
3.1.1 shallowReactive
代码示例
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job. }}K</h2>
<button @click="name += '!'">修改姓名</button>
<button @click="age++">修改年龄</button>
<button @click="job. += 10">修改薪资</button>
</template>
<script>
import { reactive, toRefs, shallowReactive } from 'vue';
export default {
name: 'App',
setup() {
// shallowReactive只考虑第一层数据的响应式
let person = shallowReactive({
name: '张三',
age: 18,
job: {
job1: {
salary: 20
}
}
})
return {
// ...为扩展运算符
...toRefs(person)
}
}
}
</script>
<style>
</style>
运行结果:
点击修改姓名和修改年龄会增加,但是点击深层次的修改薪资就不会增加。
此处还有一个注意点和疑问点:点完修改薪资不会变动,但是点完修改薪资以后,再次点击修改姓名或者修改年龄,则薪资会增加。
3.1.2 shallowRef
代码示例
<template>
<h2>当前x下的y的值为{{ }}</h2>
<button @click="++">x+1</button>
</template>
<script>
import { reactive, toRefs, shallowRef } from 'vue';
export default {
name: 'App',
setup() {
let x = shallowRef({
y:0
})
return {
x
}
}
}
</script>
<style>
</style>
运行结果:
点击x+1没有变化
3.2 readonly与shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
3.2.1 readonly
代码示例
<template>
<h2>当前求和为{{ sum }}</h2>
<button @click="sum++">sum+1</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job. }}</h2>
<button @click="name+='!'">修改姓名</button> <br>
<button @click="age++">修改年龄</button><br>
<button @click="job.++">修改薪资</button><br>
</template>
<script>
import { reactive, toRefs, ref, readonly } from 'vue';
export default {
name: 'App',
setup() {
let sum = ref(0)
let person = reactive({
name:'张三',
age:18,
job:{
job1:{
salary:20
}
}
})
// readonly: 让一个响应式数据变为只读的(深只读)
person= readonly(person)
return {
sum,
...toRefs(person)
}
}
}
</script>
<style>
</style>
运行截图:
点击修改姓名、修改年龄和修改薪资都不会发生变化
3.2.2 shallowReadonly
代码示例
<template>
<h2>当前求和为{{ sum }}</h2>
<button @click="sum++">sum+1</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job. }}</h2>
<button @click="name += '!'">修改姓名</button> <br>
<button @click="age++">修改年龄</button><br>
<button @click="job.++">修改薪资</button><br>
</template>
<script>
import { reactive, toRefs, ref, shallowReadonly } from 'vue';
export default {
name: 'App',
setup() {
let sum = ref(0)
let person = reactive({
name: '张三',
age: 18,
job: {
job1: {
salary: 20
}
}
})
// shallowReadonly:让一个响应式数据变为只读的(浅只读)。
person = shallowReadonly(person)
return {
sum,
...toRefs(person)
}
}
}
</script>
<style>
</style>
运行截图
3.3 toRaw与markRaw
- toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
3.3.1 toRaw
代码示例
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job. }}</h2>
<button @click="name += '!'">修改姓名</button> <br>
<button @click="age++">修改年龄</button><br>
<button @click="job.++">修改薪资</button><br>
<button @click="showRawPerson">输出最原始的person</button>
</template>
<script>
import { reactive, toRefs, ref, shallowReadonly, toRaw } from 'vue';
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: 18,
job: {
job1: {
salary: 20
}
}
})
// 输出最原始的person
function showRawPerson(){
console.log('代理对象的person',person)
const p = toRaw(person)
console.log('原始的对象的person',p)
}
return {
...toRefs(person),
showRawPerson
}
}
}
</script>
<style>
</style>
运行截图
3.3.2 markRaw
代码示例
<template>
<h2>姓名:{{ }}</h2>
<h2>年龄:{{ }}</h2>
<h2>薪资:{{ . }}</h2>
<h2>资产:{{ }}</h2>
<h2></h2>
<button @click=" += '!'">修改姓名</button> <br>
<button @click="++">修改年龄</button><br>
<button @click=".++">修改薪资</button><br>
<button @click="addCar">给人添加一台车</button><br>
<button v-if="" @click=" += '!'">修改车名</button><br>
<button v-if="" @click="changePrice()">修改车价格</button><br>
</template>
<script>
import { reactive, markRaw } from 'vue';
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: 18,
job: {
job1: {
salary: 20
}
}
})
// 响应式对象身上追加一个car属性
function addCar() {
let car = {
name: '奔驰',
price: 40
}
// 标记对象不会成为响应式对象,对其更改以后,页面不会发生变法,但是他的值是变化的。
person.car = markRaw(car)
}
// 更改车的价格
function changePrice() {
person.car.price++
console.log('车的价格变为', person.car.price)
}
return {
person,
addCar,
changePrice
}
}
}
</script>
<style>
</style>
运行结果
3.4 customRef
-
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
-
实现防抖效果
代码示例
<template>
<input type="text" v-model="keyWord">
<h3>{{ keyWord }}</h3>
</template>
<script>
import { customRef, ref } from 'vue';
export default {
name: 'App',
setup() {
// let keyWord = ref('hello') //使用vue提供的内置的ref
let keyWord = myRef('hello') //使用程序员自定义的ref
// 自定义一个ref
function myRef(value) {
let timer
return customRef((track, trigger) => {
return {
get() {
console.log(`从容器中读取了数据${value}`)
// 通知vue追踪value的变化
track()
return value
},
set(newValue) {
console.log(`myRef容器中的数据修改为了${newValue}`)
// 定时器防抖
clearTimeout(timer)
timer = setTimeout(() => {
value = newValue
// 通知vue重新解析模板
trigger()
}, 500)
}
}
})
}
return {
keyWord
}
}
}
</script>
<style>
</style>
3.5 provide与inject
-
作用:实现祖组件与后代组件间通信
-
套路:父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据
代码示例
(1)
<template>
<div class="app">
<h3>我是App组件(祖组件)</h3>
<h4>{{ name }}---{{ price }}</h4>
<ChildCom></ChildCom>
</div>
</template>
<script>
import { provide, reactive, toRefs } from 'vue';
import ChildCom from './components/';
export default {
name: 'App',
components: {
ChildCom
},
setup() {
let car = reactive({
name: '奔驰',
price: '40W'
})
// provide() 给自己的后代组件传递数据
provide('car',car)
return {
...toRefs(car)
}
}
}
</script>
<style>
.app {
background-color: #ccc;
padding: 10%;
}
</style>
(2)
<template>
<div class="child">
<h3>我是Child组件(子组件)</h3>
<h4>{{ name }}--{{ price }}</h4>
<SonCom></SonCom>
</div>
</template>
<script>
import { inject, toRefs } from 'vue';
import SonCom from './';
export default {
name: 'child',
components: {
SonCom
},
setup(){
let car = inject('car')
console.log('child--',car)
return {
...toRefs(car)
}
}
}
</script>
<style>
.child {
background-color: skyblue;
padding: 10%;
}
</style>
(3)
<template>
<div class="son">
<h3>我是Son组件(孙组件)</h3>
<h4>{{ }}--{{ }}</h4>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
name: 'son',
setup(){
let car = inject('car')
console.log('son--',car)
return {
car
}
}
}
</script>
<style>
.son {
background-color: orange;
padding: 10%;
}
</style>
运行截图
3.6 响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由
reactive
创建的响应式代理 - isReadonly: 检查一个对象是否是由
readonly
创建的只读代理 - isProxy: 检查一个对象是否是由
reactive
或者readonly
方法创建的代理
代码示例
<template>
<div class="app">
<h3>我是App组件</h3>
</div>
</template>
<script>
import { isProxy, isReactive, isReadonly, isRef, reactive, readonly, ref, toRefs } from 'vue';
export default {
name: 'App',
setup() {
let car = reactive({
name: '奔驰',
price: '40W'
})
let car2 = readonly(car)
let sum = ref(0)
console.log('判断sum是不是ref类型',isRef(sum))
console.log('判断car是不是reactive类型',isReactive(car))
console.log('判断car2是不是readonly类型',isReadonly(car2))
console.log('判断car是不是代理对象类型',isProxy(car),car)
console.log('判断car2是不是代理对象类型',isProxy(car2),car2)
return {
...toRefs(car),
car2,
sum
}
}
}
</script>
<style>
</style>
运行截图
4. Composition API的优势
4.1 Options API存在的问题
使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
4.2 Composition API的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。
5. 新的组件
5.1 Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
5.2 Teleport
什么是Teleport?—— Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。
代码示例
模拟弹窗组件,使弹窗内容位居页面中心,而不是在组件中显示。
(1)
<template>
<div class="app">
<h3>我是App组件</h3>
<ChildCom></ChildCom>
</div>
</template>
<script>
import ChildCom from './components/';
export default {
name: 'App',
components: {
ChildCom
},
}
</script>
<style>
.app {
background-color: #ccc;
padding: 10%;
}
</style>
(2)
<template>
<div class="child">
<h3>我是Child组件</h3>
<SonCom></SonCom>
</div>
</template>
<script>
import SonCom from './';
export default {
name: 'child',
components: {
SonCom
},
}
</script>
<style>
.child {
background-color: skyblue;
padding: 10%;
}
</style>
(3)
<template>
<div class="son">
<h3>我是Son组件</h3>
</div>
<Dialog></Dialog>
</template>
<script>
import Dialog from './';
export default {
name: 'son',
components:{
Dialog
}
}
</script>
<style>
.son {
background-color: orange;
padding: 10%;
}
</style>
(4)
<template>
<div>
<button @click="isShow = true">点我弹个窗</button>
<teleport to="body">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<h4>写一些内容</h4>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'Dialog',
setup() {
let isShow = ref(false)
return {
isShow
}
}
}
</script>
<style>
/* 遮罩层 */
.mask{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgb(0,0,0,0.5);
}
.dialog {
position:absolute;
top: 50%;
left: 50%;
text-align: center;
transform: translate(-50%,-50%);
width: 300px;
height: 300px;
background-color: green;
}
</style>
运行截图
5.3 Suspense
-
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
- 异步引入组件
import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/'))
- 使用
Suspense
包裹组件,并配置好default
与fallback
<template> <div class="app"> <h3>我是App组件</h3> <Suspense> <template v-slot:default> <Child/> </template> <template v-slot:fallback> <h3>加载中.....</h3> </template> </Suspense> </div> </template>
使用步骤:
6. 其他
6.1 全局API的转移
-
Vue 有许多全局 API 和配置。
-
例如:注册全局组件、注册全局指令等。
//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }
-
-
Vue3.0中对这些API做出了调整:
-
将全局的API,即:
调整到应用实例(
app
)上全局 API( Vue
)实例 API ( app
)移除
-
6.2 其他的改变
-
data选项应始终被声明为一个函数。
-
过度类名的更改:
-
写法
.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }
-
写法
.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }
-
-
移除keyCode作为 v-on 的修饰符,同时也不再支持
-
移除
修饰符
-
父组件中绑定事件
<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
-
子组件中声明自定义事件
<script> export default { emits: ['close'] } </script>
-
-
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
-
…