文章目录
- 第 1 章:Vue 核心
- 1、 Vue 简介
- 1.官网
- 2.介绍与描述
- 3. Vue 的特点
- 4. 与其它 JS 框架的关联
- 5. Vue 周边库
- 2、初始Vue
- 3、模板语法
- 1、Vue模板语法有2大类:
- 2、插值语法和指令语法
- 4、数据绑定
- 1. 单向数据绑定
- 2. 双向数据绑定
- 5、el与data的两种写法
- 1.e1有2种写法
- 2.data有2种写法
- 3.一个重要的原则:
- 6、 MVVM 模型
- 7、数据代理
- 1、 Object.defineProperty方法:
- 2、数据代理
- 1、概念
- 2、Vue中的数据代理:
- 3、Vue中数据代理的好处:
- 4、基本原理:
- 8、事件处理
- 1、事件的基本使用:
- 2、事件修饰符
- 3、键盘事件
- 9、计算属性与监视
- 1、计算属性 computed
- 2、监视属性
- 1、监视属性 watch及简写方式:
- 1、深度监听 deep
- 3、计算属性computed和监听属性watch的对比
- 10、class 与style的绑定
- 11、条件渲染
- 1.V-if
- 2.V-show
- 3.备注:
- 12、列表渲染
- v-for指令
- 1、遍历列表时key的作用(index作为key)
- 2、遍历列表时key的作用(id作为key)
- 3、面试题: react、vue中的key有什么作用? (key的内部原理)
- 13、列表过滤
- 14、列表排序
- 15、vue检测数据的原理
- 16、收集表单数据
- 17、过滤器
- 18、内置指令与自定义指令
- 1、常用内置指令
- 19、自定义指令
- 1、自定义指令总结:
- 2、自定义指令案例(函数式及对象式)
- 20、生命周期
- 1、vm的生命周期
- 2、常用的生命周期钩子:
- 3、关于销毁Vue实例
- 常用指令总结
- 第 2 章:Vue 组件化编程
- 1、 模块与组件、模块化与组件化
- 1. 模块
- 2. 组件
- 3. 模块化
- 4. 组件化
- 2、非单文件组件
- 1、概念
- 3、Vue中使用组件的三大步骤:
- 一、 如何定义一个组件?
- 二、如何注册组件?
- 三、编写组件标签:
- 4、组件的几个注意事项
- 1.关于组件名:
- 2.关于组件标签:
- 3.一个简写方式:
- 5、组件的嵌套
- 6、关丁VueComponent:
- 7、一个重要的内置关系:
- 1.一个重要的内置关系:
- 2.为什么要有这个关系:
- 8、单文件组件
- 1. 一个.vue 文件的组成(3 个部分)
- 第 3 章:使用 Vue 脚手架
- 1 初始化脚手架
- 1、说明
- 2 、具体步骤
- 第一步(仅第一次执行):全局安装@vue/cli。
- 第二步:切换到你要创建项目的目录,然后使用命令创建项目
- 第三步:启动项目
- 备注:
- 3 模板项目的结构
- 4、关于不同版本的Vue:
- 2 ref 与 props
- 1、ref
- 2、props
- 3 混入minxin
- 4 插件
- 1、功能:
- 2、本质:
- 3、定义插件:
- 4、再main.js中使用插件:
- 5、scoped样式
- 6、组件的自定义事件![在这里插入图片描述](https://img-blog.csdnimg.cn/c6a95b336c154d3d9d524a4876250754.png)
- 7、全局事件总线 (GlobalEventBus):任意组件间通信
- 8、消息订阅与发布
- 1 理解
- 2 使用 PubSubJS
- 3、总结:
- nextTick
- 9 、Vue封装的过度与动画
- 第 4 章:Vue 中的 ajax
- 1 解决开发环境 Ajax 跨域问题(代理服务器)
- 2、vue脚手架配置代理
- 3、插槽
- 1.作用:
- 2.分类:
- 3.使用方式:
- 1、默认插槽
- 2、具名插槽
- 3.作用域插槽:
- 1.理解:
- 2.具体编码
- 第 5 章:vuex
- 1、理解 vuex
- 1 vuex 是什么
- 2、什么时候使用 Vuex
- 3、搭建环境
- 1、创建文件:src/store/index.js
- 2、在mian.js中创建vm时传入store配置项
- 4、基本使用
- 1、初始化数据、配置actions、配置mutations,操作文件store.js
- 2、组件中读取vuex中的数据
- 3.组件中修改vuex中的数据:
- 5、getters的使用
- 1.概念:
- 2.在store.js 中追加getters 配置
- 3.组件中读取数据:
- 6、四个map方法的使用
- 1.mapstate方法:
- 2.mapGetters方法:
- 3.mapActions方法:
- 4.mapMutations方法:
- 7.模块化+命名空间
- 1.目的:
- 2.修改store.js
- 第 6 章:vue-router路由
- 1 vue-router 的理解
- 2 对 SPA 应用的理解
- 3 路由的理解
- 1. 什么是路由?
- 2. 路由分类
- 1. 后端路由:
- 2. 前端路由:
- 4 路由的基本使用
- 1.安装vue-router,命令:
- 2.应用插件:
- 3.编写router配置项:
- 4.实现切换(active-class可配置高亮样式)
- 5.指定展示位置
- 6.几个注意点
- 5、嵌套(多级)路由
- 1.配置路由规则,使用children配置项:
- 2.跳转 (要写完整路径) :
- 6、路由的query传参
- 1、传递参数
- 2、接收参数
- 7、命名路
- 1.作用:
- 2.如何使用
- 1.给路由命名
- 2.简化跳转:
- 8、路由的params参数
- 1、配置路由,声明接收 params参数
- 2、传递参数
- 3、接收参数
- 9.路由的props配置
- 10.```<router-link>```的replace属性
- 11.编程式路由导航
- 11、缓存路由组件
- 12.两个新的生命周期钩子
- 13、路由守卫
- 1. 作用:
- 2. 分类:
- 3. 全局守卫:
- 4. 独享守卫:
- 5. 组件内守卫:
- 14、路由器的两种工作模式
- vue3快速上手
- 一、创建Vue3.0工程
- 1.使用 vue-cli 创建
- 2.使用 vite 创建
- 3.分析工程结构
- 二、常用 Composition API
- 1.拉开序幕的setup
- 2.ref函数
- 3.reactive函数
- 4.Vue3.0中的响应式原理
- vue2.x的响应式
- Vue3.0的响应式
- 5.reactive对比ref
- 6.setup的两个注意点
- 7.计算属性与监视
- 1.computed函数
- 2.watch函数
- 3.watchEffect函数
- 8.生命周期
- 9.自定义hook函数
- 10.toRef
- 三、其它 Composition API
- 1.shallowReactive 与 shallowRef
- 2.readonly 与 shallowReadonly
- 3.toRaw 与 markRaw
- 4.customRef
- 5.provide 与 inject
- 6.响应式数据的判断
- 四、Composition API 的优势
- 1.Options API 存在的问题
- .Composition API 的优势
- 五、新的组件
- 1.Fragment
- 2.Teleport
- 3.Suspense
- 六、其他
- 1.全局API的转移
- 2.其他改变
- 第三方库
- 浏览器本地存储
第 1 章:Vue 核心
1、 Vue 简介
1.官网
- 英文官网: https://vuejs.org/
- 中文官网: https://cn.vuejs.org/
2.介绍与描述
1、动态构建用户界面的渐进式 JavaScript 框架
构造用户界面:将数据展示到用户可以看见的界面
渐进式:vue可以自底向上逐层的应用
简单应用:只需一个轻量小巧的核心库
复杂应用:可以引入各式各样的vue插件
2、 作者: 尤雨溪
3. Vue 的特点
- 遵循 MVVM 模式
- 编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发
- 它本身只关注 UI, 也可以引入其它第三方库开发项目
- 采用组件化模式,提高代码复用率、且让代码更好维护
- 声明式编码,让编码人员无需直接操作DOM,提高开发效率
4. 与其它 JS 框架的关联
- 借鉴 Angular 的模板和数据绑定技术
- 借鉴 React 的组件化和虚拟 DOM 技术
5. Vue 周边库
- vue-cli: vue 脚手架
- vue-resource
- axios
- vue-router: 路由
- vuex: 状态管理
- element-ui: 基于 vue 的 UI 组件库(PC 端)等
2、初始Vue
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.root容器里的代码依然符合htm1规范,只不过混入了一些特殊的Vue语法;
3.root容器里的代码被称为[Vue模板]:
4.Vue实例和容器是一 一对应的;
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7.一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新:
注意区分:JS表达式和JS代码
1、表达式:一个表达式会产成一个值,可以放在任何一个需要值的地方,如:a+b 等
2、JS代码:如for循环,if语句
<!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 type="text/javascript" src="../js/vue.js"></script></head>
<body><div id="root"><h1>插值语法</h1><h1>你好,我是:{{name}}</h1></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip=false//创建Vue实例new Vue({el:'#root',//el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象name:'vue'}})</script>
</body>
</html>
3、模板语法
1、Vue模板语法有2大类:
1.插值语法:
功能:用于解析标签体内容。(双标签中间的内容)
写法: {{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法:
功能:用于解析标签(包括: 标签属性、标签体内容、绑定事件…)
举例:v-bind:href="xxx”或 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性。
备注:Vue中有很多的指令,且形式都是: v-???,此处我们只是拿v-bind举个例子。
2、插值语法和指令语法
<body><div id="root"><h1>插值语法</h1><h2>Hello,{{name}}</h2><h1>指令语法</h1><!-- 原始写法 --><a v-bind:href="school.url">点击我去{{school.name}}</a><a :href="school.url"></a></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip=false//创建Vue实例new Vue({el:'#root',//el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象name:'vue',school:{name:'学习',url:'https:www.baidu.com'}}})</script>
</body>
4、数据绑定
1. 单向数据绑定
- 语法:v-bind:href =“xxx” 或简写为 :href
- 特点:数据只能从 data 流向页面
2. 双向数据绑定
- 语法:v-mode:value=“xxx” 或简写为 v-model=“xxx”
- 特点:数据不仅能从 data 流向页面,还能从页面流向data
备注:
1.双向绑定一般都应用在表单类元素上 (如: input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值.
如:
<body><div id="root"><!-- 普通写法 -->单向数据绑定:<input type="text" v-bind:value="name"><br>双向数据绑定:<input type="text" v-model:value="name"><!-- 简写 -->单向数据绑定:<input type="text" :value="name"><br>双向数据绑定:<input type="text" v-model="name"></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip=false//创建Vue实例new Vue({el:'#root',//el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象name:'vue'}})</script>
</body>
5、el与data的两种写法
1.e1有2种写法
(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。
2.data有2种写法
(1).对象式
(2).函数式
如何选择: 目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错
3.一个重要的原则:
出Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
如:
<body><div id="root"><h1>你好:{{name}}</h1></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip=false//el的两种写法const v= new Vue({//第一种写法 // el:'#root', data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象name:'vue'}})console.log(v)v.$mount('#root')//第二种写法// data的两种写法new Vue({el:'#root',//第一种写法 // data的第一种写法:对象式data:{ name:'vue'}//data的第二种写法:函数式data:function(){console.log(this)//此处的this是vue实例对象return{name:'Vue'}}//函数式可以简写成data(){console.log(this)//此处的this是vue实例对象return{name:'Vue'}}})</script>
</body>
6、 MVVM 模型
- M:模型(Model) :对应 data 中的数据
- V:视图(View) :模板
- VM:视图模型(ViewModel) : Vue 实例对象
观察发现:
1.data中所有的属性,最后都出现在了vm身上。
2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
7、数据代理
1、 Object.defineProperty方法:
Object.defineProperty方法:是给一个对象添加(定义)属性用的
语法格式:
Object.defineProperty(要添加的对象,添加的属性名,{配置项(基本配置项和高级配置项)})
<body><script type="text/javascript">let obj={x:100}let obj2={y:200}Object.defineProperty(obj2,'x',{//基本配置项value:18,enumerable:true,//控制属性是否可以枚举(遍历),默认为falsewritable:true,//控制属性是否可以被修改,默认值为falseconfigurable:true,//控制属性是否可以被删除,默认值是false//高级配置项//当有人读取对象的的x属性是,get函数就会被调用,且返回值就是x的值get(){return obj.x},//当有人修改对象的的x属性是,set函数就会被调用,且会收到修改的具体值set(value){obj.x=value}})</script>
</body>
2、数据代理
1、概念
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
2、Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
3、Vue中数据代理的好处:
更加方便的操作data中的数据
4、基本原理:
通过object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作 (读/写) data中对应的属性
8、事件处理
1、事件的基本使用:
1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名:
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了,是windows:
4.methods中配置的函数,都是被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>Document</title><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h1>Hello,{{name}}</h1><button v-on:click="showInfo">点我提示信息</button><!-- 简写 --><button @click="showInfo1">点我提示信息(不传参数)</button><button @click="showInfo2(66,$event)">点我提示信息( 传参数)</button></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {//data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象name: "vue"},methods:{showInfo(event){console.log(event.target.innerText)alert("dddd")},showInfo2(number,event){console.log(event.target.innerText)alert("dddd",number)}}});</script></body>
</html>
2、事件修饰符
1.prevent: 阻止默认事件 (常用) :
2.stop:阻止事件冒泡 (常用) ;
3.once: 事件只触发一次 (常用) :
4.capture:使用事件的捕获模式;
5.self: 只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕:
注意: 修饰符可以连续写 如 @click.prevent.stop=“showInfo”
<!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 type="text/javascript" src="../js/vue.js"></script><style>* {margin-top: 20px;}.demo1 {height: 50px;background-color: skyblue;}.box1 {padding: 5px;background-color: skyblue;}.box2 {padding: 5px;background-color: antiquewhite;}.list{width: 200px;height: 200px;background-color: bisque;overflow: auto;}li{height: 100px;}</style></head><body><!--Vue中的事件修饰符:1.prevent: 阻止默认事件 (常用) :2.stop:阻止事件冒泡 (常用) ;3.once: 事件只触发一次 (常用) :4.capture:使用事件的捕获模式;5.self: 只有event.target是当前操作的元素时才触发事件;6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕:--><div id="root"><h1>Hello,{{name}}</h1><!-- 阻止默认事件(常用) --><a href="www.baidu.com" @click.prevent="showInfo">点我提示信息</a><!-- 阻止事件冒泡(常用) --><div class="demo1" @click="showInfo"><button @click.stop="showInfo">点我提示信息</button></div><!-- once: 事件只触发一次 (常用) --><button @click.once="showInfo">点我提示信息</button><!-- capture:使用事件的捕获模式 --><div class="box1" @click.capture="showMsg()1">div1<div class="box2" @click="showMsg(2)">div2</div></div><!-- elf: 只有event.target是当前操作的元素时才触发事件 --><div class="demo1" @click.elf="showInfo"><button @click="showInfo">点我提示信息</button></div><!-- passive:事件的默认行为立即执行,无需等待事件回调执行完毕 --><!-- scroll 滚动条滚动事件,wheel鼠标滚轮滚事件--><ul class="list" @scroll="demo"><li>1</li><li>2</li><li>3</li><li>4</li></ul></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {//data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象name: "vue",},methods: {showInfo(event) {console.log(event.target.innerText);alert("dddd");},showMsg(msg) {alert(msg);},demo(){alert("滚动了")}},});</script></body>
</html>
3、键盘事件
1.Vue中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab(特殊,必须配合keydown使用)
上 => up
下 => down
左 => left
右 => right
2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case (短横线命名)
3.系统修饰键(用法特殊) : ctr1、alt、shift、meta
(1),配合keyup使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用: 正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐) @keyup.13=“showInfo”
5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
//自定义别名按键
Vue.config.keyCodes.huiche=13
<!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 type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h1>Hello,{{name}}</h1><!-- keyup鼠标按下抬起时触发keydown鼠标按下时触发--><input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo"/></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//自定义别名按键Vue.config.keyCodes.huiche=13//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {//data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象name: "vue",},methods: {showInfo(e) {// if(e.keyCode!==13) returnconsole.log(e.target.value);}},});</script></body>
</html>
9、计算属性与监视
1、计算属性 computed
1.定义:要用的属性不存在,要通过已有属性计算得来。
在 computed 对象中定义计算属性。 在页面中使用{{方法名}}来显示计算的结果。
2.原理: 底层借助了objcet,defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用) ,效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
<!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 type="text/javascript" src="../js/vue.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><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//自定义别名按键Vue.config.keyCodes.huiche=13//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {//data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象firstName: "vue",lastName:'三'},computed:{// 完整写法fullName:{//get的作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值//get什么时候调用?1、初次读取fullName时。2、所依赖的数据发生变化时get(){return this.firstName+'-'+this.lastName},//set什么时候调用?当fullName被修改时set(value){const arr =value.split('-')this.firstName=arr[0]this.lastName=arr[1]}}//简写 只考虑读取,不考虑修改时才能用fullName(){return this.firstName+'-'+this.lastName}}})</script></body>
</html>
2、监视属性
- 通过 vm 对象的$watch()或 watch 配置来监视指定的属性
- 当属性变化时, 回调函数自动调用, 在函数内部进行计算
1、监视属性 watch及简写方式:
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视
3.监视的两种写法:
(1).new Vue时传入watch配置;当创建实例时很明确监视谁时用
(2).通过vm.$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>Document</title><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h2>今天天气很{{info}}</h2><!-- 绑定事件的时候 @xxx='yyy' yyy可以写一些简单的语句 --><button @click="isHot=!isHot">切换天气1</button><button @click="changWeather">切换天气</button></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//自定义别名按键Vue.config.keyCodes.huiche=13//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {isHot:true},methods: {changWeather(){this.isHot=!this.isHot}},computed:{info(){return this.isHot?'炎热':'凉爽'}},//当创建实例时很明确监视谁时用//正常写法isHot:{//immediate:false,//初始化时让handler调用一下//deep:true,//深度监视//handler什么时候调用?当isHot发生改变时handler(newValue,oldValue){console.log("isHot被修改了",newValue,oldValue)}, },//简写形式 前提是没有其他配置项,只有hander是才能用isHot(newValue,oldValue){console.log("isHot被修改了",newValue,oldValue)},});//正常写法//当创建实例时很不明确监视谁时用vm.$watch('isHot',{immediate:false,//初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时handler(newValue,oldValue){console.log("isHot被修改了",newValue,oldValue)}})//简写vm.$watch('isHot',function(newValue,oldValue){console.log("isHot被修改了",newValue,oldValue)})</script></body>
</html>
1、深度监听 deep
(1).Vue中的watch默认不监测对象内部值的改变 (一层)
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue自身可以监测对象内部值的改变,但vue提供的watch默认不可以!
(2).使用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>Document</title><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h2>今天天气很{{info}}</h2><!-- 绑定事件的时候 @xxx='yyy' yyy可以写一些简单的语句 --><button @click="isHot=!isHot">切换天气1</button><button @click="changWeather">切换天气</button><hr/><h3>a的值是:{{numbers.a}}</h3><button @click="numbers.a++">点我让a+1</button><hr/><h3>b的值是:{{numbers.b}}</h3><button @click="numbers.b++">点我让b+1</button></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//自定义别名按键Vue.config.keyCodes.huiche=13//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {isHot:true,numbers:{a:1,b:1}},methods: {changWeather(){this.isHot=!this.isHot}},computed:{info(){return this.isHot?'炎热':'凉爽'}},//当创建实例时很明确监视谁时用watch:{isHot:{//immediate:false,//初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时handler(newValue,oldValue){console.log("isHot被修改了",newValue,oldValue)}, },//监视多级结构中某个属性的变化'number.a':{handler(){console.log("a被改变了")}, },//监视多级结构中所有属性的变化numbers:{deep:true,handler(){console.log("a被改变了")},}}});</script></body>
</html>
3、计算属性computed和监听属性watch的对比
computed利lwatch之间的区别:
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。
两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
10、class 与style的绑定
1。class样式
写法:class="xxxxx可以是宁符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名宁也确定,但不确定用不用,
2。style样式
:style="(fontsize: xxx]“其中xxx是动态值。
:style=”[a,b]"其中a、b是样式对象。
<!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 type="text/javascript" src="../js/vue.js"></script><style>.basic{}.happy{}.sad{}</style></head><body><div id="root"><!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 --><div class="basic" :class="mood" @click="chanageMood">{{name}}</div><!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 --><div class="basic" :class="classArr">{{name}}</div>I<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --><div class="basic" :class="calssObj">{{name}}</div>I<!-- 绑定style样式--对象写法 --><div class="basic" :style="styleObj">{{name}}</div>I</div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {name:'Vue',mood:'normal',classArr:['style1','style2','style3'],classObj:{class1:false,class2:false},styleObj:{fontSize:'40px',color:'red',backgroundColor:'orange'}},methods: {chanageMood(){this.mood='变换后的class名'}},});</script></body>
</html>
11、条件渲染
1.V-if
写法:
(1).v-if=“表达式”
(2).V-else-if=“表达式”
(3).v-else=“表达式”
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”
2.V-show
写法:v-show="表达式"适用于:切换频率较高的场景。
特点:不展示的DOM元素术被移除,仅仅是使用样式隐藏掉
3.备注:
使用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>Document</title><script type="text/javascript" src="../js/vue.js"></script><style></style></head><body><div id="root"><!-- 使用v-show做条件渲染 --><h2 v-show="false">欢迎来到{{name}}</h2><!-- 使用v-if做条件渲染 --><h2 v-if="false">欢迎来到{{name}}</h2><!-- v-else利v-else-if --><div v-if="n === 1">Angular</div><div>@</div>//等于打断了<div v-else-if="n === 2">React</div><div v-else-if="n === 3">Vue</div><div v-else>哈哈</div><!-- template模板,不影响代码结构 v-if与template的配合使用 --><template v-if="n===1"><h2>dddd</h2><h2>dddd</h2><h2>dddd</h2></template></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {name:'Vue',}});</script></body>
</html>
12、列表渲染
v-for指令
1.用于展示列表数据
2.语法: v-for="(item,index) in xxx”:key=“index” 或 v-for="item in xxx”:key=“item.id”
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 type="text/javascript" src="../js/vue.js"></script><style></style></head><body><div id="root"><!-- 遍历数组 --><h2>人员列表(遍历数组)</h2><ul><!-- key的写法一:<li v-for="person in persons" :key="person.id"> --><!-- key的写法二: <li v-for="(person,index) in persons" ::key="index"> --><li v-for="(person,index) in persons" :key="index">{{person.name}}-{{person.age}}</li></ul><!-- 遍历对象 --><h2>汽车信息(遍历对象)</h2><ul><li v-for="(value,k) in car" :key="k">{{k}}----{{value}}</li></ul><!-- 遍历字符串 --><h2>测试遍历字符串(用的少)</h2><ul><li v-for="(value,k) in str" :key="k">{{k}}----{{value}}</li></ul><!-- 遍历指定次数 --><h2>测试遍历指定次数(用的少)</h2><ul><li v-for="(value,k) in 5" :key="k">{{k}}----{{value}}</li></ul></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//创建Vue实例const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {persons:[{id:'001',name:'张三1',age:'18'},{id:'002',name:'张三2',age:'188'},{id:'003',name:'张三3',age:'198'}],car:{name:'奥迪A3',price:'18万'},str:'hello world'}});</script></body>
</html>
1、遍历列表时key的作用(index作为key)
2、遍历列表时key的作用(id作为key)
3、面试题: react、vue中的key有什么作用? (key的内部原理)
1。虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM]随后Vue进行[新虚拟DOM] 与[旧虚拟DOM] 的差异比较,比较规则如下:
2.对比规则:
- (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
若虚拟DOM中内容没变,直接使用之前的真实DOM!
若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉贞面中之前的真实DOM.
- (2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
3。用index作为key可能会引发的问题:
- 1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低。
- 2。如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
4。开发中如何选择key?:
- 1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
- 2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
13、列表过滤
<!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 type="text/javascript" src="../js/vue.js"></script><style></style></head><body><div id="root"><!-- 遍历数组 --><h2>人员列表(遍历数组)</h2><input type="text" placeholder="请输入名字" v-model="keyworkd"/><ul><li v-for="(person,index) in filPersons" :key="index">{{person.name}}-{{person.age}}</li></ul></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//watch实现/* const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {keyworkd:'',filPersons:[],persons:[{id:'001',name:'张三1',age:'18'},{id:'002',name:'张三2',age:'188'},{id:'003',name:'张三3',age:'198'}]},watch:{keyworkd:{immediate:true,handler(val){this.filPersons = this.persons.filter((p)=>{return p.name.indexOf(val) !== -1})}}}}); *///用computed实现const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {keyworkd:'',persons:[{id:'001',name:'张三1',age:'18'},{id:'002',name:'张三2',age:'188'},{id:'003',name:'张三3',age:'198'}]},computed:{filPerons(){return this.persons.filter((p)=>{return p.name.indexOf(this.keyworkd) !== -1})}}});</script></body>
</html>
14、列表排序
<!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 type="text/javascript" src="../js/vue.js"></script><style></style></head><body><div id="root"><!-- 遍历数组 --><h2>人员列表(遍历数组)</h2><input type="text" placeholder="请输入名字" v-model="keyworkd"/><button @click='sortType=2'>升序</button><button @click='sortType=1'>降序序</button><button @click='sortType=0'>原序</button><ul><li v-for="(person,index) in filPersons" :key="index">{{person.name}}-{{person.age}}</li></ul></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//watch实现/* const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {keyworkd:'',filPersons:[],persons:[{id:'001',name:'张三1',age:'18'},{id:'002',name:'张三2',age:'188'},{id:'003',name:'张三3',age:'198'}]},watch:{keyworkd:{immediate:true,handler(val){this.filPersons = this.persons.filter((p)=>{return p.name.indexOf(val) !== -1})}}}}); *///用computed实现const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {keyworkd:'',sortType:0,//0代表原顺序 1 降序 2 升序persons:[{id:'001',name:'张三1',age:'18'},{id:'002',name:'张三2',age:'188'},{id:'003',name:'张三3',age:'198'}]},computed:{filPerons(){const arr= this.persons.filter((p)=>{return p.name.indexOf(this.keyworkd) !== -1})if(this.sortType){arr.sort((p1,p2)=>{return this.sortType===1?p2.age-p1.age:p1.age-p2.age})}return arr}}});</script></body>
</html>
15、vue检测数据的原理
1,vue会监视data中所有层次数据。
2。如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index, value) 或
vm.$set(target,propertyName/index,value)
3,如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意: Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性
16、收集表单数据
若:<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:输入首尾空格过滤
<!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 type="text/javascript" src="../js/vue.js"></script><style></style></head><body><div id="root"><form @submit='demo'> //绑定提交事件账号: <input type="text" v-model.trim="account"/><br>密码:<input type="password" v-model="password"/><br>年龄:<input type="number" v-model.number="age"/><br>性别:男:<input type="radio" name="sex" value="男" checked v-model="sex"/>女:<input type="radio" name="sex" value="女" v-model="sex"/><br>爱好:学习:<input type="checkbox" value="学习" v-model="hobby">学习1:<input type="checkbox" value="学习1" v-model="hobby">学习2:<input type="checkbox" value="学习2" v-model="hobby"><br>所属校区:<select v-model="city"><option value="">请选择校区</option><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><br>其他:<textarea v-model.lazy="other"></textarea><br><input type="checkbox" v-model="agree"> 阅读并接受<a href="www.baidu.com">用户协议</a><br><button>提交</button></form></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {account:'',password:'',sex:'',age:'',hobby:[],city:'',other:'',agree:''},methods: {demo(){console.log(JSON.stringify(this._data))}},});</script></body>
</html>
17、过滤器
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名
备注: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 type="text/javascript" src="../js/vue.js"></script><script type="text/javascript" src="../js/dayjs.min.js"></script><style></style></head><body><div id="root"><h1>显示格式化后的时间</h1><!-- 计算属性实现 -->现在是:<h2>{{time}}</h2><!-- methods实现 -->现在是:<h2>{{getFmtTime()}}</h2><!-- 过滤器实现 (不传参数)-->现在是:<h2>{{time | timeFormater}}</h2><!-- 过滤器实现 (传参数)-->现在是:<h2>{{time | timeFormater('yyyy_MM_DD') |mySlice }}</h2></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//全局过滤器Vue.filter('mySlice',function(value){return value.slice(0,4)})const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {time:1621561377603},//计算属性实现computed:{fmtTime(){return dayjs(this.time).format('yyyy-MM-DD HH:mm:ss')}},//methods实现 methods: {getFmtTime(){return dayjs(this.time).format('yyyy-MM-DD HH:mm:ss') }},//过滤器实现,(局部过滤器)filters:{ //过滤器配置项timeFormater(value,str='yyyy-MM-DD HH:mm:ss'){return dayjs(value).format(str) },mySlice(value){return value.slice(0,4)}}});</script></body>
</html>
18、内置指令与自定义指令
1、常用内置指令
v-text指令:
1.作用:向其所在的节点中渲染文本内容。(会替换内容,可以解析html标签)
2.与插值语法的区别: v-text会替换掉节点中的内容,{[xx}}则不会
v-html: 和v-text一样
v-html: 和v-text一样,但能解析html标签
1.作用:向指定节点中渲染包含htm1结构的内容。
2.与插值语法的区别:
(1).v-htm1会替换掉节点中所有的内容,{{xx}}则不会
(2).v-htm1可以识别htm1结构。
3.严重注意: v-html有安全性问题!!! !
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-htm1,永不要用在用户提交的内容上!
v-cloak (没有值) :
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会厕掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{[xxx}}的问题。
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
V-pre(没有值)
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 type="text/javascript" src="../js/vue.js"></script><script type="text/javascript" src="../js/dayjs.min.js"></script><style>/* v-cloak用 */[v-cloak]{display: none;}</style></head><body><div id="root"><div v-text="name"></div><div v-html="str"></div><h1 v-cloak>{{name1}}</h1><h2 v-once>初始的n值是:{{n}}</h2><h2>当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button><h1 v-pre>Vue不简单</h1></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {name:'vue',str:'<h1>xxxxx</h1>',name1:'pppp',n:1},});</script></body>
</html>
19、自定义指令
1、自定义指令总结:
2、自定义指令案例(函数式及对象式)
需求1:定义个v-big指令,莉v-text功能类似,但会把绑定的数值放大10倍。
需求2: 定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
<!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 type="text/javascript" src="../js/vue.js"></script><script type="text/javascript" src="../js/dayjs.min.js"></script><style></style></head><body><div id="root"><h1>当前的n值是:<span v-text="n"></span></h1><h1>放大10倍后的值<span v-big="n"></span></h1><button @click="n++">点我n+1</button></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//自定义全局指令Vue.directives('fbind',{//指令与元素成功绑定时bind(element,binding){element.value=binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value=binding.value}})const vm = new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串data: {n:1},//自定义big指令 局部指令directives:{//big何时调用?1、指令与元素成功绑定时 2、指令所在的模板被重新解析时(数据发生改变时)//函数式big(element,binding){element.innerText=binding.value*10 },//对象式fbind:{//指令与元素成功绑定时bind(element,binding){element.value=binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value=binding.value}}}});</script></body>
</html>
20、生命周期
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm 或 组件实例对象。
1、vm的生命周期
将要创建:调用beforeCreate函数
创建完毕:调用created函数。
将要挂载:调用beforeMount函数。
挂载完毕:调用mounted函数。
将要更新:调用beforeUpdate函数
更新完毕:调用updated函数。
将要销毁:调用beforeDestroy函数
销毁完毕:调用destroyed函数。
2、常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等[初始化操作]
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。
3、关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
常用指令总结
v-bind:单向绑定解析表达式,可简写为 :xxxv-model:双向数据绑定v-for:遍历数组/对象/字符串v-on:绑定事件监听,可简写为@v-if:条件渲染(动态控制节点是否存存在)V-else:条件渲染(动态控制节点是否存存在)V-show:条件渲染 (动态控制节点是否展示)v-text:向其所在的标签插入文本v-text1.作用:向其所在的节点中渲染文本内容。(会替换内容)2.与插值语法的区别: v-text会替换掉节点中的内容,{[xx}}则不会v-html: 和v-text一样,但能解析html标签1.作用:向指定节点中渲染包含htm1结构的内容。2.与插值语法的区别:(1).v-htm1会替换掉节点中所有的内容,{{xx}}则不会(2).v-htm1可以识别htm1结构。3.严重注意: v-html有安全性问题!!! !(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。(2).一定要在可信的内容上使用v-htm1,永不要用在用户提交的内容上!v-cloak (没有值) :1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会厕掉v-cloak属性。2.使用css配合v-cloak可以解决网速慢时页面展示出{[xxx}}的问题。v-once(没有值)1.v-once所在节点在初次动态渲染后,就视为静态内容了。2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。V-pre(没有值)1.跳过其所在节点的编译过程。2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
第 2 章:Vue 组件化编程
1、 模块与组件、模块化与组件化
1. 模块
1.理解: 向外提供特定功能的 js 程序, 一般就是一个 js 文件
2. 为什么: js 文件很多很复杂
3. 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率
2. 组件
1.理解: 用来实现局部(特定)功能效果代码和资源的集合(html/css/js/image……)
2. 为什么: 一个界面的功能很复杂
3. 作用: 复用编码, 简化项目编码, 提高运行效率
3. 模块化
当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。
4. 组件化
当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用,。
2、非单文件组件
1、概念
非单文件组件:一个文件中包含有n个组件
单文件组件:一个文件中只包含有1一个组件
3、Vue中使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组体(写组件标签)
一、 如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:区别如下:
1、el不要写,为什么? 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2、data必须写成函数,为什么? 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
二、如何注册组件?
1.局部注册: 靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名,组件)
三、编写组件标签:
<school></schoo1>
例如:
<!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 type="text/javascript" src="../js/vue.js"></script><script type="text/javascript" src="../js/dayjs.min.js"></script><style></style></head><body><div id="root"><!-- 第三步:编写组件标签 --><school></school></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//第一步:创建school组件const school = Vue.extend({// el: "#root",//组件定义时一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器data() {return {schoolName: "学校",address: "xxxx",};},template:`<div><h2>学习名称:{{schoolName}}</h2><h2>学习地址:{{address}}</h2><button @click="showName">点我提示学校名</button></div>`,methods: {showName(){alert(this.schoolName)}},});//第一步:创建hello组件const he = VUe.extend({name:'cy',//可以使用name配置项指定组件在开发者工具中呈现的名字。template:`<div><h2>你好呀!{{name}}</h2> </div>`,data(){return {name:'TOM'}}})//第二步:注册组件(全局注册)Vue.component('hello',he)new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串//第二步:注册组件(局部注册)components:{school:school//可以简写// school}})</script></body>
</html>
4、组件的几个注意事项
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写): school
多个单词组成:
第一种写法(kebab-case命名): my-school
第二种写法(CamelCase命名): MySchool (需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中己有的元素名称,例如: h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:
第二种写法:
备注:不用使用脚手架时,会导致后续组件不能渲染
3.一个简写方式:
options:表示配置项 { }
const school = Vue,extend(options) 可简写: const school = options
如
//第一步:创建hello组件const he ={name:'cy',//可以使用name配置项指定组件在开发者工具中呈现的名字。template:`<div><h2>你好呀!{{name}}</h2> </div>`,data(){return {name:'TOM'}}}
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>Document</title><script type="text/javascript" src="../js/vue.js"></script><script type="text/javascript" src="../js/dayjs.min.js"></script><style></style></head><body><div id="root"><!-- 第三步:编写组件标签 --><school></school><hr /></div><script type="text/javascript">//阻止vue 在启动时生成生产提示Vue.config.productionTip = false;//第一步:创建student组件const student = Vue.extend({data() {return {studentname: "名字",age: 18,};},template:`<div><h2>学生名称:{{studentname}}</h2><h2>学习年龄:{{age}}</h2></div>`,});//第一步:创建school组件const school = Vue.extend({// el: "#root",//组件定义时一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器data() {return {schoolName: "学校",address: "xxxx",};},template:`<div><h2>学习名称:{{schoolName}}</h2><h2>学习地址:{{address}}</h2><student></student> //引入student组件<button @click="showName">点我提示学校名</button></div>`,methods: {showName(){alert(this.schoolName)}},//注册组件(局部注册)嵌套组件components:{student}});new Vue({el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串//第二步:注册组件(局部注册)components:{school:school,//可以简写// school,}})</script></body>
</html>
6、关丁VueComponent:
1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue,extend生成的。
2.我们只需要写<school/>或<schoolx/school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的: new VueComponent(options)。
3.特别注意: 每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
4.关于this指向:(1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是[VueComponent实例对象](2).new Vue()配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是[Vue实例对象]
5.VueComponent的实例对象,以后简称vc (也可称之为: 组件实例对象)。Vue的实例对象,以后简称vm。
7、一个重要的内置关系:
1.一个重要的内置关系:
VueComponent.prototype._proto_=== Vue.prototype
2.为什么要有这个关系:
让组件实例对象 (vc) 可以访问到 Vue原型上的属性、方法。
8、单文件组件
1. 一个.vue 文件的组成(3 个部分)
// 1、模板页面<template><!-- 组件的结构 --><div></div></template>// 2、JS模板对象<script>//组件交互相关的代码(数据、方法)</script>// 3、样式<style >/* //组件的样式 */</style>
如:School.vue
// 1、模板页面
<template><!-- 组件的结构 --><div class="demo"><h2>学习名称:{{schoolName}}</h2><h2>学习地址:{{address}}</h2><button @click="showName">点我提示学校名</button></div>
</template>
// 2、JS模板对象
<script>//组件交互相关的代码(数据、方法)export default {name:'School',data() {return {schoolName: "学校",address: "xxxx",};},methods: {showName(){alert(this.schoolName)}},}
</script>// 3、样式
<style >/* //组件的样式 */.demo{background-color: aliceblue;}
</style>
Student.vue
// 1、模板页面
<template><!-- 组件的结构 --><div class="demo"><h2>学生姓名{{name}}</h2><h2>学生年龄{{age}}</h2><button @click="showName">点我提示学名字</button></div>
</template>
// 2、JS模板对象
<script>//组件交互相关的代码(数据、方法)export default {name:'School',data() {return {name: "谢谢谢",age: 19,};},methods: {showName(){alert(this.schoolName)}},}
</script>// 3、样式
<style >/* //组件的样式 */.demo{background-color: aliceblue;}
</style>
App.vue
<template><div><School></School><Student></Student></div>
</template><script>//引入组件import School from '01初始vue/单文件组件/School'import Student from '01初始vue/单文件组件/Student'export default {name:'App',//注册组件components:{School,Student}}
</script><style></style>
main.js
import App from '01初始vue/单文件组件/App'new Vue({el:'#root',template:`<App></App>`,components:{App}
})
index.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>Document</title>
</head>
<body><!-- 准备一个容器 --><div id="root"></div><!-- <script type="text/javaScript" src="../../js/vue.js"></script><script type="text/javaScript" src="./main.js"></script> -->
</body>
</html>
第 3 章:使用 Vue 脚手架
1 初始化脚手架
1、说明
1.Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)。
2. 最新的版本是 4.x。
3. 文档: https://cli.vuejs.org/zh/。
2 、具体步骤
第一步(仅第一次执行):全局安装@vue/cli。
npm install -g @vue/cli
第二步:切换到你要创建项目的目录,然后使用命令创建项目
vue create xxxx
第三步:启动项目
npm run serve
备注:
1.如出现下载缓慢请配置 npm 淘宝镜像:
npm config set registry https://registry.npm.taobao.org
2.Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的webpakc 配置,请执行:
vue inspect > output.js
3 模板项目的结构
4、关于不同版本的Vue:
1.vue.js与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的Vue,包含: 楼心功能+模板解析器。
(2).vue.runtime.xxx.js是运行版的Vue,只包含: 核心功能;没有模板解析器.
2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
2 ref 与 props
1、ref
1.被用来给元素或子组件注册引用信息I(id的替代者)
2.应用在htm1标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象 (vc)
3.使用方式:
打标识: <h1 ref="xxx">.....</h1> 或<School ref="xxx"></Schoo1>
获取: this.$refs.xxx
如:
<template><div><School></School><Student ref="stud"></Student><h1 v-text="msg" ref="title"></h1><button @click="showDOM">点我输入h2的DOM元素</button></div>
</template><script>//引入组件import School from '@/components/School'import Student from '@/components/Student'export default {name:'App',//注册组件components:{School,Student},data(){return{msg:'欢迎回来!'}},methods:{showDOM(){console.log(this.$refs.title) //真实的DOM元素<h1>欢迎回来!</h1>console.log(this.$refs.stud) //VC的组件实例对象}}}
</script><style></style>
2、props
1.作用:用于父组件给子组件传递数据
2. 读取方式一: 只指定名称
props: ['name', 'age', 'setName']
3.读取方式二: 指定名称和类型
props: {name: String,age: Number, setNmae: Function
}
4.读取方式三: 指定名称/类型/必要性/默认值
props: {name: {type: String, required: true, default:xxx},
}
5.传递数据:
<Student name="xxx" age=18/>
备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
如:
传值
<template><div><Student name='学生1' age='19'/> //传值</div>
</template><script>//引入组件import Student from '@/components/Student'export default {name:'App',//注册组件components:{Student}}
</script><style></style>
接收值
// 1、模板页面
<template><!-- 组件的结构 --><div class="demo" ><h2 v-text="msg"></h2><h2>学生姓名:{{name}}</h2><h2>学生年龄:{{myAge}}</h2><button @click="updateage">点我修改年龄</button> </div>
</template>
// 2、JS模板对象
<script>//组件交互相关的代码(数据、方法)export default {name:'Student',data() {return {msg:'我是学生',myAge:this.age};},methods:{updateage(){this.myAge++}},//方式一:简单接受// props:['name','age'] //方式二:接收的同时对数据进行类型限制/* props:{name:String,age:Number} *///方式三:接收的同时指定名称/类型/必要性/默认值props:{name:{type:Stirng,//name的类型是字符串required:true //name是必要的},age:{type:Number,default:99//默认值}}}
</script>// 3、样式
<style >/* //组件的样式 */.demo{background-color: aliceblue;}
</style>
3 混入minxin
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步自定义混合,例如:
{data()(....},methods:{....]
}
第二步使用混入,例如:
(1).全局混入: Vue.mixin(xxx)
(2).局部混入: mixins:[‘xxx’]
如:
第一步:自定义混合mixin.js文件
export const hunhe={methods: {showName(){alert(this.schoolName)}}
}
第二步:使用混入
<script>// 局部引用import {hunhe} from '../minxin'//组件交互相关的代码(数据、方法)export default {name:'School',data() {return {schoolName: "学校",address: "xxxx",};},mixins:[hunhe]}
</script>
再main.js中全局引入
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
import { hunhe } from './minxin'
//关闭Vue的生产提示
Vue.config.productionTip = false//全局引入
Vue.mixin(hunhe)//创建VM
new Vue({render: h => h(App),
}).$mount('#app')
4 插件
1、功能:
用于增强Vue
2、本质:
包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
3、定义插件:
对象.install = function (Vue, options) {// 1。添加全局过滤器Vue.filter(....)// 2.添加全局指令Vue.directive(....)// 3。配置全局混入(合)Vue.mixin(....)// 4。添加实例方法Vue.prototype.$myMethod = function () [...}Vue.prototype.$myProperty = xxxx
}
如:
export default {install(Vue,x,y,z){console.log(x,y,z)//全局过滤器Vue.filter('mySlice',function(value){return value.slice(0,4)})//定义全局指令Vue.directive('fbind',{//指令与元素成功绑定时(一上来)bind(element,binding){element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value = binding.value}})//定义混入Vue.mixin({data() {return {x:100,y:200}},})//给Vue原型上添加一个方法(vm和vc就都能用了)Vue.prototype.hello = ()=>{alert('你好啊')}}
}
4、再main.js中使用插件:
Vue.use(插件名)
如:
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false//引入插件
import plugins from './plugins'
//使用插件
Vue.use(plugins)//创建VM
new Vue({render: h => h(App),
}).$mount('#app')
5、scoped样式
作用:让样式在局部生效,防止冲突。
写法:
style scoped>//样式
</style>
6、组件的自定义事件
App.vue
<template><div><!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 --><School :getSchoolName='getSchoolName'/><!-- 写法一:使用@或v-on:通过父组件给子组件绑定一个自定义事件实现:子给父传递数据 --><Student v-on:cy="getStudentName"/><!-- 只在第一次起作用 --><Student v-on:cy.once="getStudentName"/><!-- 写法二:使用ref:通过父组件给子组件绑定一个自定义事件实现:子给父传递数据 --><Student ref='student'/></div>
</template><script>//引入组件import Student from '@/components/Student'import School from './components/School.vue'export default {name:'App',//注册组件components:{Student,School,},methods:{getSchoolName(name){return this.name},getStudentName(name){console.log("cy事件被触发了")return this.name}},mounted(){// 绑定自定义事件this.$refs.Student.$on('cy',this.getStudentName)// 只在第一次起作用this.$refs.Student.$once('cy',this.getStudentName)}}
</script><style></style>
Student.vue
<template><div class="test"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2><button @click="sendStudentName">把学生名给App</button><button @click="unbind">解绑cy事件</button><button @click="death">销毁了当前组件就的实例</button></div>
</template><script>export default {name:'Student',data() {return {name:'张三',sex:'男'}},methods:{sendStudentName(){//触发Student组件实例身上的cy事件this.$emit('cy',this.name)},unbind(){//解绑自定义事件(只使用与一个自定义事件)this.$off('cy')//解绑多个自定义事件this.$off(['cy','自定义事件2','xxx'])this.$off()//所有的自定义事件全部解绑},death(){this.$destroy()//销毁了当前组件就的实例,销毁后该组件实例的自定义事件全部不奏效}}}
</script><style scoped>.test{background-color: bisque;}
</style>
7、全局事件总线 (GlobalEventBus):任意组件间通信
1.一种组件间通信的方式,适用于任意组件间通信。
2.安装全局事件总线:
new Vue({。。。。。。。beforeCreate(){Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm},......
})
3.使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给Sbus绑定自定义事件,事件的回调留在A组件自身.
methods(){demo(data){......} } .... mounted() {this.$bus.$on("xxxx',this .demo) }
-
提供数据:
this.$bus.$emit("xxxx’,数据)
4.最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
如:
在main.js中 安装全局事件总线
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false//创建VM
new Vue({render: h => h(App),beforeCreate(){Vue.prototype.$bus=this //安装全局事件总线}
}).$mount('#app')
将学生名传递给学校组件
Student.vue
<template><div class="test"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2><button @click="sendStudentName">把学生名给School组件</button></div>
</template><script>export default {name:'Student',data() {return {name:'张三',sex:'男'}},methods:{sendStudentName(){//触发Student组件实例身上的cy事件this.$bus.$emit('hello',this.name)}}}
</script><style scoped>.test{background-color: bisque;}
</style>
School.vue组件
<template><div class="demo"><h2>学校名称:{{name | mySlice}}</h2><h2>学校地址:{{address}}</h2></div>
</template><script>export default {name:'School',data() {return {name:'尚硅谷atguigu',address:'北京',}},mounted(){this.$bus.$on("hell",(data)=>{console.log("我是School组件,收到了数据",data)})},//beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。beforeDestroy(){this.$bus.$off('hello')}}
</script>
<style >.demo{background-color: aqua;}
</style>
8、消息订阅与发布
1 理解
- 这种方式的思想与全局事件总线很相似
- 它包含以下操作:
(1) 订阅消息 --对应绑定事件监听
(2) 发布消息 --分发事件
(3) 取消消息订阅 --解绑事件监听 - 需要引入一个消息订阅与发布的第三方实现库: PubSubJS
2 使用 PubSubJS
- 在线文档: https://github.com/mroderick/PubSubJS
- 下载: npm install -S pubsub-js
- 相关语法
(1) import PubSub from 'pubsub-js' // 引入
(2) PubSub.subscribe(‘msgName’, functon(msgName, data){ })
(3) PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用
(4) PubSub.unsubscribe(token): 取消消息的订阅
3、总结:
如:
Student.vue
<template><div class="test"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2><button @click="sendStudentName">把学生名给School组件</button></div>
</template><script>import pubsub from 'pubsub-js'export default {name:'Student',data() {return {name:'张三',sex:'男'}},methods:{sendStudentName(){// //触发Student组件实例身上的cy事件// this.$bus.$emit('hello',this.name)pubsub.publish('hello',666)}}}
</script><style scoped>.test{background-color: bisque;}
</style>
School.vue
<template><div class="demo"><h2>学校名称:{{name | mySlice}}</h2><h2>学校地址:{{address}}</h2></div>
</template><script>// 引入import pubsub from 'pubsub-js'export default {name:'School',data() {return {name:'尚硅谷atguigu',address:'北京',}},mounted(){// this.$bus.$on("hell",(data)=>{// console.log("我是School组件,收到了数据",data)// })this.pubId= pubsub.subscribe('hello',(msgName,data)=>{console.log("有人发布了hello消息,hello消息的回调执行了",msgName,data)})},beforeDestroy(){// this.$bus.$off('hello')pubsub.unsubscribe(this.pubId)}}
</script>
<style >.demo{background-color: aqua;}
</style>
nextTick
1.语法: this.$nextTick(回调函数)
2.作用: 在下一次 DOM 更新结束后执行其指定的回调
3.什么时候用: 当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行.
9 、Vue封装的过度与动画
1、作用:在插入、跟新或移除DOM元素时,在合适的时候给元素添加样式类名
2、图示
3、写法
- 准备好样式:
元素进入的样式1、v-enter:进入的起点2、v-enter-axtive:进入过程中3、v-enter-to:进入的终点
元素离开的样式1、v-leave:离开的起点2、v-leave-active:离开过程中3、v-leave-to:离开的终点
- 使用包裹要过渡的元素,并配置name属性:
<transition appear name='hello'><h1 v-show="isShow">你好呀!</h1></transition>
- 备注:若有多个元素需要过度,则需要使用 transition-group,且每个元素都要指定key值
如:
<template><div><button @click="isShow =!isShow">显示/隐藏</button><!-- <h1 v-show="isShow" class="come">你好呀!</h1> --><transition appear name='hello'><h1 v-show="isShow">你好呀!</h1></transition><!-- transition-group 可以实现多个元素过度 --><transition-group appear name='hello '><h1 v-show="isShow" key="1">你好呀!</h1><h1 v-show="isShow" key='2'>辰逸</h1></transition-group></div>
</template>
<script>export default ({name:'Test2',data(){return{isShow:true}}
})
</script><style scoped>h1{background-color: orange;/* transition: 05s linear; */}//封装的过度/* 进入的起点、离开的终点*/.hello-enter,.hello-leave-to{transform: translateX(-100%);}/* 进入的终点 、离开的起点*/.hello-enter-to,.hello-leave{transform: translateX(0);}.hello-enter-active,.hello-leave-active{transition: 05s linear;}//封装的动画,过度和动画使用一个就可以.v-enter-active{animation:cy 1s}.v-leave-active{animation: cy 1s reverse; }@keyframes cy{from{transform: translateX(-100px);}to{transform: translateX(-0px);}}</style>
- 第三方动画库
https://www.npmjs.com/网站上搜索animate.css
<template><div><button @click="isShow =!isShow">显示/隐藏</button><!-- <h1 v-show="isShow" class="come">你好呀!</h1> --><transition appear name='hello'><h1 v-show="isShow">你好呀!</h1></transition><!-- transition-group 可以实现多个元素过度 --><transition-group appear name='animate_animated animate_bounce'enter-active-class="animate_swing"leave-active-class="animate_backOutUp"><h1 v-show="isShow" key="1">你好呀!</h1><h1 v-show="isShow" key='2'>辰逸</h1></transition-group></div>
</template>
<script>
//引入animate
import 'animate.css'
export default ({name:'Test2',data(){return{isShow:true}}
})
</script><style scoped>h1{background-color: orange;/* transition: 05s linear; */}</style>
第 4 章:Vue 中的 ajax
1 解决开发环境 Ajax 跨域问题(代理服务器)
2、vue脚手架配置代理
方法一
在vue.config.s中添加如下配置:
devServer:{proxy:'http://localhost:5000'}
说明:
- 优点:配置简单,请求资源时直接发给前端 (8080) 即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)
方法二:
编写vue.config.js配置具体代理规则
devServer: {proxy: {'/cy': { //匹配所有以'/cy'开头的请求路径target: 'http://localhost:5000',//代理目标的基础路径pathRewrite:{'^/cy':''}, //去掉请求地址中的/cyws: true, //用于支持websocketchangeOrigin: true //用于控制请求头中的host值},'/demo': {//匹配所有以'/demo'开头的请求路径target: 'http://localhost:5001',//代理目标的基础路径pathRewrite:{'^/demo':''},//去掉请求地址中的/demows: true, //用于支持websocketchangeOrigin: true //用于控制请求头中的host值 为true时请求头的host为:localhost:5000,为false时,localhost:8080 默认为true},}}
如:
vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,lintOnSave:false , //关闭语法检查//开启代理服务器(方式一)/* devServer:{proxy:'http://localhost:5000'}, *///开启代理服务器(方式二)devServer: {proxy: {'/cy': {target: 'http://localhost:5000',pathRewrite:{'^/cy':''}, //去掉请求地址中的/cyws: true, //用于支持websocketchangeOrigin: true //用于控制请求头中的host值},'/demo': {target: 'http://localhost:5001',pathRewrite:{'^/demo':''},//去掉请求地址中的/demows: true, //用于支持websocketchangeOrigin: true //用于控制请求头中的host值},}}
})
App.vue
<template><div><button @click="getSutdents">获取学生信息</button><button @click="getCars">获取汽车信息</button></div><!-- http://localhost:5000/studentshttp://localhost:5001/cars-->
</template><script>import axios from 'axios'export default {name:'App',methods:{getSutdents(){axios.get('http://localhost:8080/cy/students').then(response=>{console.log("请求成功了",response.data)},error=>{console.log('请求失败了',error.message)})},getCars(){axios.get('http://localhost:8080/demo/cars').then(response=>{console.log("请求成功了",response.data)},error=>{console.log('请求失败了',error.message)})}}}
</script>
3、插槽
1.作用:
让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式 适用于 父组件 ===>子组件
2.分类:
默认插槽、具名插槽、作用域插槽
3.使用方式:
1、默认插槽
父组件中:<Category><div>html结构1</div></Category>
子组件中:<template><div><!-- 定义插槽 --><slot>插槽默认内容...</slot></div></template>
2、具名插槽
父组件中<Category><template slot="center"》<div>htm1结构1</div></template><template v-slot:footer><div>html结构2</div></template></Category>
子组件中:<template><div><!--定义插槽 --><slot name="center">插槽默认内容...</slot><slot name="footer">插槽默认内容...</slot></div></template>
3.作用域插槽:
1.理解:
数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。 (games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
2.具体编码
父组件中:<Category><template scope="scopeData"><!-- 生成的是u1列表 --><ul><li v-for="g in scopeData.games” :key="g">((g)}</1i></u1></template></Category><Category><template slot-scope="scopeData"><!-- 生成的是h4标题 --><h4 v-for="g in scopeData.games” :key="g">(g]]</h4></template></Category>
子组件中,<template><div><slot :games="games"></slot></div></template><script>export default {name:'Category",props:['title'],//数据在子组件自身data(){return {games:['红色警戒”,穿越火线,劲舞团,超级玛丽]},</script>
第 5 章:vuex
1、理解 vuex
1 vuex 是什么
- 概念:专门在 Vue 中实现集中式状态(数据)管理的一个Vue 插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
- Github 地址: https://github.com/vuejs/vuex
2、什么时候使用 Vuex
1.多个组件依赖于同一状态(数据)
2.来自不同组件的行为需要变更同一状态(数据)
3、搭建环境
1、创建文件:src/store/index.js
//该文件用于创建vuex中最为核心的store// 引入vue核心库
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vuex.usr(Vuex) //准备action-用于响应组件中的动作
const actions={}
//准备mutations-用于操作数据(state)
const mutations={}
//准备state-用于存储数据
const state ={}// 创建并暴露store
export default new Vuex.Store({actions,mutations,state
})
2、在mian.js中创建vm时传入store配置项
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
//引入插件
import vueResource from 'vue-resource'// 引入store
import store from './store/index'// 使用插件
Vue.use(vueResource)//创建vm
new Vue({el:'#app',render: h => h(App),store
})
4、基本使用
1、初始化数据、配置actions、配置mutations,操作文件store.js
//该文件用于创建vuex中最为核心的store// 引入vue核心库
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vuex.usr(Vuex) //准备action-用于响应组件中的动作
const actions={// jia(miniStore,value){// miniStore.commit('JIA',value)// },// jian(miniStore,value){// miniStore.commit('JIAN',value)// },jiaOdd(miniStore,value){if(miniStore.state.sum % 2){miniStore.commit('JIA',value)}},jiaWait(miniStore,value){setTimeout(() => {miniStore.commit('JIA',value)}, 500);},
}
//准备mutations-用于操作数据(state)
const mutations={JIA(state,value){state.sum+=value},JIAN(state,value){state.sum-=value}
}
//准备state-用于存储数据
const state ={sum:0,//当前和
}// 创建并暴露store
export default new Vuex.Store({actions,mutations,state
})
2、组件中读取vuex中的数据
$store.state.sum
3.组件中修改vuex中的数据:
$store.dispatch('action中的方法名,数据) 或 $store.commit('mutations中的方法名',数据)
备注: 若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispath,直接编写commit
如:
<template><div><h1>当前求和为:{{$store.state.sum}}</h1><select v-model='n'><option :value="1">1</option><option :value="2">2</option><option :value="3">3</option></select><button @click='increment'>+</button><button @click='decrement'>-</button><button @click='incrementOdd'>当前求和为奇数时加</button><button @click='incrementWait'>等一等再加</button></div>
</template>
<script>
export default {name:'Category',date(){return{// sum:0,//当前和n:1,//用户选择的数字}},methods:{increment(){// this.sum+this.n//this.$store.dispatch('jia',this.n)//没有业务逻辑可以dispatch 直接写成this.$store.commit('JIA',n)},decrement(){// this.sum-=this.nthis.$store.dispatch('jian',this.n)},incrementOdd(){// if(this.sum % 2){// this.sum+=this.n// }this.$store.dispatch('jiaOdd',this.n)},incrementWait(){// setTimeout(() => {// this.sum+this.n// }, 500);this.$store.dispatch('jiaWait',this.n)},}
}
</script><style></style>
5、getters的使用
1.概念:
当state中的数据需要经过加工后再使用时,可以使用getters加工。
2.在store.js 中追加getters 配置
......const getters = {bigSum(state){return state.sum * 10}}//创建并暴露storeexport default new Vuex.Store({......getters})
3.组件中读取数据:
$store.getters.bigSum
如:
<h3>当前求和放大十倍后为:{{$store.getters.bigSum}}</h3>
6、四个map方法的使用
1.mapstate方法:
用于帮助我们映射state 中的数据为计算属性
computed:{//借助mapState生成计算属性: sum、schoo1、subject (对象写法)...mapState((sum:'sum",school: 'school",subject:'subject"}),//借助mapstate生成计算属性: sum、schoo1、subject (数组写法)...mapState(["sum',"school',"subject"]),
}
2.mapGetters方法:
用于帮助我们映射getters 中的数据为计算属性
computed:{//借助mapGetters生成计算属性。bigSum (对象写法)...mapGetters({bigSum;"bigSum"}).//借助mapGetters生成计算属性: bigSum (数组写法)...mapGetters(['bigSum"])
}
3.mapActions方法:
用于帮助我们生成与actions 对话的方法,即: 包含$store.dispatch(xxx)的函数
methods:{//靠mapActions生成:incrementOdd、incrementWait (对象形式)..mapActions(fincrementOdd:'jiaOdd',incrementWait:"jiaWait'})//靠mapActions生成:incrementOdd、incrementWait (数组形式)...mapActions(["jiaOdd","jiawait"])}
4.mapMutations方法:
用于帮助我们生成与mutations对话的方法,即:包含$store,commit(xxx)的函数
methods:(//靠mapActions生成: increment、decrement (对象形式)...mapMutations({increment:'JIA',decrement:'JIAN'}),//靠mapMutations生成:JIA、JIAN (对象形式)...mapMutations(['JIA',JIAN']),
}
备注: mapActions与mapMutations使用时,若需要传递参数需要: 在模板中绑定事件时传递好参数,否则参数是事件对象。
如:
<template><div><h1>当前求和为:{{sum}}</h1><h3>当前求和放大10倍为:{{bigSum}}</h3><h3>我在{{school}},学习{{subject}}</h3><select v-model.number="n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="increment(n)">+</button><button @click="decrement(n)">-</button><button @click="incrementOdd(n)">当前求和为奇数再加</button><button @click="incrementWait(n)">等一等再加</button></div>
</template><script>import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'export default {name:'Count',data() {return {n:1, //用户选择的数字}},computed:{//靠程序员自己亲自去写计算属性/* sum(){return this.$store.state.sum},school(){return this.$store.state.school},subject(){return this.$store.state.subject}, *///借助mapState生成计算属性,从state中读取数据。(对象写法)// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),//借助mapState生成计算属性,从state中读取数据。(数组写法)...mapState(['sum','school','subject']),/* ******************************************************************** *//* bigSum(){return this.$store.getters.bigSum}, *///借助mapGetters生成计算属性,从getters中读取数据。(对象写法)// ...mapGetters({bigSum:'bigSum'})//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)...mapGetters(['bigSum'])},methods: {//程序员亲自写方法/* increment(){this.$store.commit('JIA',this.n)},decrement(){this.$store.commit('JIAN',this.n)}, *///借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)...mapMutations({increment:'JIA',decrement:'JIAN'}),//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)// ...mapMutations(['JIA','JIAN']),/* ************************************************* *///程序员亲自写方法/* incrementOdd(){this.$store.dispatch('jiaOdd',this.n)},incrementWait(){this.$store.dispatch('jiaWait',this.n)}, *///借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)// ...mapActions(['jiaOdd','jiaWait'])},mounted() {const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'})console.log(x)},}
</script><style lang="css">button{margin-left: 5px;}
</style>
7.模块化+命名空间
1.目的:
让代码更好维护,让多种数据分类更加明确
2.修改store.js
const countAbout = {namespaced;true,//开启命名空间state:(x:1),mutations:(...}actions:[..},getters: {bigSum(state){return state.sum * 10}}
}const personAbout = {namespaced;true,//开启命名空间state:{ ... },mutations: { ...},actions:{ ...}
}
const store = new Vuex.Store(modules:{countAbout.personAbout}
})
如:
第 6 章:vue-router路由
1 vue-router 的理解
vue 的一个插件库,专门用来实现 SPA 应用
2 对 SPA 应用的理解
- 单页 Web 应用(single page web application,SPA)。
- 整个应用只有一个完整的页面。
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
- 数据需要通过 ajax 请求获取。
3 路由的理解
1. 什么是路由?
- 一个路由就是一组映射关系(key - value)
- key 为路径, value 可能是 function 或 component
2. 路由分类
1. 后端路由:
- 理解:value 是 function, 用于处理客户端提交的请求。
- 工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
2. 前端路由:
- 理解:value 是 component,用于展示页面内容。
- 工作过程:当浏览器的路径改变时, 对应的组件就会显示。
4 路由的基本使用
1.安装vue-router,命令:
npm i vue-router@3
2.应用插件:
Vue.use(VueRouter)
如:
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
//引入vueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router/index'//应用插件
Vue.use(VueRouter)//创建vm
new Vue({el:'#app',render: h => h(App),router:router
})
3.编写router配置项:
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '@/components/About'
import Home from '../components/Home'//创建并暴露路由器,去管理一组一组的路由规则export default new VueRouter({routes:[{path:'/about',component:About},{path:'/home',component:Home}]
})
4.实现切换(active-class可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link>
5.指定展示位置
<router-view></router-view>
如:
<template><div><div class="row"><div class="col-xs-offset-2 col-xs-8"><div class="page-header"><h2>Vue Router Demo</h2></div></div></div><div class="row"><div class="col-xs-2 col-xs-offset-2"><div class="list-group"><!-- 原始html中我们使用a标签实现页面的跳转 --><!-- <a class="list-group-item active" href="./about.html">About</a><a class="list-group-item" href="./home.html">Home</a> --><!--Vue中借助router-lick标签实现路由的切换 --><router-link class="list-group-item " active-class="active" to="/about">About</router-link><router-link class="list-group-item" active-class="active" to="/home">Home</router-link></div></div><div class="col-xs-6"><div class="panel"><div class="panel-body"><!-- 指定组件的呈现位置 --><router-view></router-view></div></div></div></div></div>
</template><script>export default {name:'App'}
</script>
<style scoped></style>
6.几个注意点
1.路由组件通常存放在pages 文件夹,一般组件通常存放在components 文件夹
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3.每个组件都有自己的 $route 属性,里面存储着自己的路由信息
4.整个应用只有一个router,可以通过组件的 $router 属性获取到。
5、嵌套(多级)路由
1.配置路由规则,使用children配置项:
routes:[{path:'/about"component :About .},{path : '/home",component :Home,children:{//通过children配置子级路由{path:'news',//此处一定不要写: /newscomponent:News}, {path:'message’,//此处一定不要写,/messagecomponent:Message}}}
]
2.跳转 (要写完整路径) :
<router-link to="/home/news">News</router-link>
6、路由的query传参
1、传递参数
<ul><li v-for="m in messageList" :key="m.id"><!-- 跳转路由并携带query参数,to的字符串写法 --><!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link> --><!-- 跳转路由并携带query参数,to的对象写法 --><router-link :to="{path:'/home/message/detail',query:{id:m.id,title:m.title}}"></router-link></li>
</ul>
2、接收参数
{{$route.query.id}}
{{$route.query.title}}
7、命名路
1.作用:
可以简化路由的跳转
2.如何使用
1.给路由命名
2.简化跳转:
8、路由的params参数
1、配置路由,声明接收 params参数
2、传递参数
3、接收参数
9.路由的props配置
作用: 让路由组件更方便的收到参数
{name:'xiangqing',path:'detail/:id',component:Detail,//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件// props:{a:900}//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件// props:true//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件props(route){return {id:route.query.id,title:route.query.title}}
}
接收
<template><ul><li>消息编号:{{id}}</li><li>消息标题:{{title}}</li></ul>
</template><script>export default {name:'Detail',props:['id','title'],}
</script>
10.<router-link>
的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为
push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
- 如何开启
replace
模式:<router-link replace .......>News</router-link>
11.编程式路由导航
-
作用:不借助
<router-link>
实现路由跳转,让路由跳转更加灵活 -
具体编码:
//$router的两个API this.$router.push({name:'xiangqing',params:{id:xxx,title:xxx} })this.$router.replace({name:'xiangqing',params:{id:xxx,title:xxx} }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退
如:
<template><div><ul><li v-for="m in messageList" :key="m.id"><!-- 跳转路由并携带params参数,to的字符串写法 --><!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{m.title}}</router-link> --><!-- 跳转路由并携带params参数,to的对象写法 --><router-link :to="{name:'xiangqing',query:{id:m.id,title:m.title}}">{{m.title}}</router-link><button @click="pushShow(m)">push查看</button><button @click="replaceShow(m)">replace查看</button></li></ul><hr><router-view></router-view></div>
</template><script>export default {name:'Message',data() {return {messageList:[{id:'001',title:'消息001'},{id:'002',title:'消息002'},{id:'003',title:'消息003'}]}},methods: {pushShow(m){this.$router.push({name:'xiangqing',query:{id:m.id,title:m.title}})},replaceShow(m){this.$router.replace({name:'xiangqing',query:{id:m.id,title:m.title}})}},}
</script>
<template><div class="col-xs-offset-2 col-xs-8"><div class="page-header"><h2>Vue Router Demo</h2><button @click="back">后退</button><button @click="forward">前进</button><button @click="test">测试一下go</button></div></div>
</template><script>export default {name:'Banner',methods: {back(){this.$router.back()// console.log(this.$router)},forward(){this.$router.forward()},test(){this.$router.go(3)}},}
</script>
11、缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁。
-
具体编码:
<!-- 缓存多个路由组件 --><!-- <keep-alive :include="['News','Message']"><router-view></router-view></keep-alive>--><!-- 缓存一个路由组件 --><keep-alive include="不被销毁的组件名"> <router-view></router-view></keep-alive>
12.两个新的生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
- 具体名字:
activated
路由组件被激活时触发。deactivated
路由组件失活时触发。
<template><ul><li :style="{opacity}">欢迎学习Vue</li><li>news001 <input type="text"></li><li>news002 <input type="text"></li><li>news003 <input type="text"></li></ul>
</template><script>export default {name:'News',data() {return {opacity:1}},//beforeDestroy和mounted对路由不起作用/* beforeDestroy() {console.log('News组件即将被销毁了')clearInterval(this.timer)}, *//* mounted(){this.timer = setInterval(() => {console.log('@')this.opacity -= 0.01if(this.opacity <= 0) this.opacity = 1},16)}, */activated() {console.log('News组件被激活了')this.timer = setInterval(() => {console.log('@')this.opacity -= 0.01if(this.opacity <= 0) this.opacity = 1},16)},deactivated() {console.log('News组件失活了')clearInterval(this.timer)},}
</script>
13、路由守卫
1. 作用:
对路由进行权限控制
2. 分类:
全局守卫、独享守卫、组件内守卫
3. 全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{console.log('beforeEach',to,from)if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则next() //放行}else{alert('暂无权限查看')// next({name:'guanyu'})}}else{next() //放行}
})//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{console.log('afterEach',to,from)if(to.meta.title){ document.title = to.meta.title //修改网页的title}else{document.title = 'vue_test'}
})
如:
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'//创建并暴露一个路由器
const router = new VueRouter({routes:[{name:'guanyu',path:'/about',component:About,meta:{title:'关于'}},{name:'zhuye',path:'/home',component:Home,meta:{title:'主页'},children:[{name:'xinwen',path:'news',component:News,meta:{isAuth:true,title:'新闻'}},{name:'xiaoxi',path:'message',component:Message,meta:{isAuth:true,title:'消息'},children:[{name:'xiangqing',path:'detail',component:Detail,meta:{isAuth:true,title:'详情'},}]}]}]
})//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{console.log('前置路由守卫',to,from)if(to.meta.isAuth){ //判断是否需要鉴权if(localStorage.getItem('school')==='atguigu'){next()}else{alert('学校名不对,无权限查看!')}}else{next()}
})//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{console.log('后置路由守卫',to,from)document.title = to.meta.title || '硅谷系统'
})export default router
4. 独享守卫:
beforeEnter(to,from,next){console.log('beforeEnter',to,from)if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制if(localStorage.getItem('school') === 'atguigu'){next()}else{alert('暂无权限查看')// next({name:'guanyu'})}}else{next()}
}
如:
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'//创建并暴露一个路由器
const router = new VueRouter({routes:[{name:'guanyu',path:'/about',component:About,meta:{title:'关于'}},{name:'zhuye',path:'/home',component:Home,meta:{title:'主页'},children:[{name:'xinwen',path:'news',component:News,meta:{isAuth:true,title:'新闻'},beforeEnter: (to, from, next) => {console.log('独享路由守卫',to,from)if(to.meta.isAuth){ //判断是否需要鉴权if(localStorage.getItem('school')==='atguigu'){next()}else{alert('学校名不对,无权限查看!')}}else{next()}}},{name:'xiaoxi',path:'message',component:Message,meta:{isAuth:true,title:'消息'},children:[{name:'xiangqing',path:'detail',component:Detail,meta:{isAuth:true,title:'详情'},}]}]}]
})
export default router
5. 组件内守卫:
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
如:
<template><h2>我是About的内容</h2>
</template><script>export default {name:'About',//通过路由规则,进入该组件时被调用beforeRouteEnter (to, from, next) {console.log('About--beforeRouteEnter',to,from)if(to.meta.isAuth){ //判断是否需要鉴权if(localStorage.getItem('school')==='atguigu'){next()}else{alert('学校名不对,无权限查看!')}}else{next()}},//通过路由规则,离开该组件时被调用beforeRouteLeave (to, from, next) {console.log('About--beforeRouteLeave',to,from)next()}}
</script>
14、路由器的两种工作模式
- 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
- hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
- hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
- history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
如:
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'//创建并暴露一个路由器
const router = new VueRouter({mode:'history',//选择路由模式routes:[]
})export default router
vue3快速上手
一、创建Vue3.0工程
1.使用 vue-cli 创建
官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 查看@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
2.使用 vite 创建
官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网:https://vitejs.cn
- 什么是vite?—— 新一代前端构建工具。
- 优势如下:
- 开发环境中,无需打包操作,可快速的冷启动。
- 轻量快速的热重载(HMR)。
- 真正的按需编译,不再等待整个应用编译完成。
- 传统构建 与 vite构建对比图
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
3.分析工程结构
main.js
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'//创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)//挂载
app.mount('#app')
二、常用 Composition API
官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html
1.拉开序幕的setup
- 理解:Vue3.0中一个新的配置项,值为一个函数。
- setup是所有Composition API(组合API)“ 表演的舞台 ”。
- 组件中所用到的:数据、方法等等,均要配置在setup中。
- setup函数的两种返回值:
- 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
- 若返回一个渲染函数:则可以自定义渲染内容。(了解)
- 注意点:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
- 如果有重名, setup优先。
- setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
- 尽量不要与Vue2.x配置混用
2.ref函数
- 作用: 定义一个响应式的数据
- 语法:
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据:
xxx.value
- 模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
- 备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 - 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
如:
<template><h1>一个人的信息</h1><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h3>工作种类:{{job.type}}</h3><h3>工作薪水:{{job.salary}}</h3><button @click="changeInfo">修改人的信息</button>
</template><script>import {ref} from 'vue'export default {name: 'App',setup(){//数据let name = ref('张三')let age = ref(18)let job = ref({type:'前端工程师',salary:'30K'})//修改方法function changeInfo(){name.value = '李四'age.value = 48console.log(job.value)job.value.type = 'UI设计师'job.value.salary = '60K'console.log(name,age)}//返回一个对象(常用)return {name,age,job,changeInfo}}}
</script>
3.reactive函数
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) - 语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) - reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
如:
<template><h1>一个人的信息</h1><h2>姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h3>工作种类:{{person.job.type}}</h3><h3>工作薪水:{{person.job.salary}}</h3><h3>爱好:{{person.hobby}}</h3><h3>测试的数据c:{{person.job.a.b.c}}</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',a:{b:{c:666}}},hobby:['抽烟','喝酒','烫头']})//方法function changeInfo(){person.name = '李四'person.age = 48person.job.type = 'UI设计师'person.job.salary = '60K'person.job.a.b.c = 999person.hobby[0] = '学习'}//返回一个对象(常用)return {person,changeInfo}}}
</script>
4.Vue3.0中的响应式原理
vue2.x的响应式
-
实现原理:
-
对象类型:通过
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。 -
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {get () {}, set () {} })
-
-
存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
Vue3.0的响应式
-
实现原理:
-
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
-
通过Reflect(反射): 对源对象的属性进行操作。
-
MDN文档中描述的Proxy与Reflect:
-
Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
-
Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
new Proxy(data, {// 拦截读取属性值get (target, prop) {return Reflect.get(target, prop)},// 拦截设置属性值或添加新属性set (target, prop, value) {return Reflect.set(target, prop, value)},// 拦截删除属性deleteProperty (target, prop) {return Reflect.deleteProperty(target, prop)} })proxy.name = 'tom'
-
-
5.reactive对比ref
- 从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive
转为代理对象。
- 从原理角度对比:
- ref通过
Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。 - reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- ref通过
- 从使用角度对比:
- ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
。 - reactive定义的数据:操作数据与读取数据:均不需要
.value
。
- ref定义的数据:操作数据需要
6.setup的两个注意点
-
setup执行的时机
- 在beforeCreate之前执行一次,this是undefined。
-
setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 - slots: 收到的插槽内容, 相当于
this.$slots
。 - emit: 分发自定义事件的函数, 相当于
this.$emit
。
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
7.计算属性与监视
1.computed函数
-
与Vue2.x中computed配置功能一致
-
写法
import {computed} from 'vue'setup(){//数据let person = reactive({firstName:'张',lastName:'三'})//计算属性——简写(没有考虑计算属性被修改的情况)/* person.fullName = computed(()=>{return person.firstName + '-' + person.lastName}) *///计算属性——简写let fullName = computed(()=>{return person.firstName + '-' + person.lastName})//计算属性——完整let fullName = computed({get(){return person.firstName + '-' + person.lastName},set(value){const nameArr = value.split('-')person.firstName = nameArr[0]person.lastName = nameArr[1]}}) }
如:
<template><h1>一个人的信息</h1>姓:<input type="text" v-model="person.firstName"><br>名:<input type="text" v-model="person.lastName"><br><span>全名:{{person.fullName}}</span><br>全名:<input type="text" v-model="person.fullName">
</template><script>import {reactive,computed} from 'vue'export default {name: 'Demo',setup(){//数据let person = reactive({firstName:'张',lastName:'三'})//计算属性——简写(没有考虑计算属性被修改的情况)/* person.fullName = computed(()=>{return person.firstName + '-' + person.lastName}) *///计算属性——完整写法(考虑读和写)person.fullName = computed({get(){return person.firstName + '-' + person.lastName},set(value){const nameArr = value.split('-')person.firstName = nameArr[0]person.lastName = nameArr[1]}})//返回一个对象(常用)return {person}}}
</script>
2.watch函数
-
与Vue2.x中watch配置功能一致
-
两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
//情况一:监视ref定义的响应式数据 watch(sum,(newValue,oldValue)=>{console.log('sum变化了',newValue,oldValue) },{immediate:true})//情况二:监视多个ref定义的响应式数据 watch([sum,msg],(newValue,oldValue)=>{console.log('sum或msg变化了',newValue,oldValue) }) /* 情况三:监视reactive定义的响应式数据若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 */ watch(person,(newValue,oldValue)=>{console.log('person变化了',newValue,oldValue) },{immediate:true,deep:false}) //此处的deep配置不再奏效//情况四:监视reactive定义的响应式数据中的某个属性 watch(()=>person.job,(newValue,oldValue)=>{console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //情况五:监视reactive定义的响应式数据中的某些属性 watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true})//特殊情况 watch(()=>person.job,(newValue,oldValue)=>{console.log('person的job变化了',newValue,oldValue) },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
如:
<template><h2>当前求和为:{{sum}}</h2><button @click="sum++">点我+1</button><hr><h2>当前的信息为:{{msg}}</h2><button @click="msg+='!'">修改信息</button><hr><h2>姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h2>薪资:{{person.job.j1.salary}}K</h2><button @click="person.name+='~'">修改姓名</button><button @click="person.age++">增长年龄</button><button @click="person.job.j1.salary++">涨薪</button>
</template><script>import {ref,reactive,watch} from 'vue'export default {name: 'Demo',setup(){//数据let sum = ref(0)let msg = ref('你好啊')let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})//情况一:监视ref所定义的一个响应式数据/* watch(sum,(newValue,oldValue)=>{console.log('sum变了',newValue,oldValue)},{immediate:true}) *///情况二:监视ref所定义的多个响应式数据/* watch([sum,msg],(newValue,oldValue)=>{console.log('sum或msg变了',newValue,oldValue)},{immediate:true}) *//* 情况三:监视reactive所定义的一个响应式数据的全部属性1.注意:此处无法正确的获取oldValue2.注意:强制开启了深度监视(deep配置无效)*//* watch(person,(newValue,oldValue)=>{console.log('person变化了',newValue,oldValue)},{deep:false}) //此处的deep配置无效 *///情况四:监视reactive所定义的一个响应式数据中的某个属性/* watch(()=>person.name,(newValue,oldValue)=>{console.log('person的name变化了',newValue,oldValue)}) *///情况五:监视reactive所定义的一个响应式数据中的某些属性/* watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{console.log('person的name或age变化了',newValue,oldValue)}) *///特殊情况/* watch(()=>person.job,(newValue,oldValue)=>{console.log('person的job变化了',newValue,oldValue)},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效 *///返回一个对象(常用)return {sum,msg,person}}}
</script>
3.watchEffect函数
-
watch的套路是:既要指明监视的属性,也要指明监视的回调。
-
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。 watchEffect(()=>{const x1 = sum.valueconst x2 = person.ageconsole.log('watchEffect配置的回调执行了') })
如:
<template><h2>当前求和为:{{sum}}</h2><button @click="sum++">点我+1</button><hr><h2>当前的信息为:{{msg}}</h2><button @click="msg+='!'">修改信息</button><hr><h2>姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h2>薪资:{{person.job.j1.salary}}K</h2><button @click="person.name+='~'">修改姓名</button><button @click="person.age++">增长年龄</button><button @click="person.job.j1.salary++">涨薪</button>
</template><script>import {ref,reactive,watch,watchEffect} from 'vue'export default {name: 'Demo',setup(){//数据let sum = ref(0)let msg = ref('你好啊')let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})//监视/* watch(sum,(newValue,oldValue)=>{console.log('sum的值变化了',newValue,oldValue)},{immediate:true}) */watchEffect(()=>{const x1 = sum.valueconst x2 = person.job.j1.salaryconsole.log('watchEffect所指定的回调执行了')})//返回一个对象(常用)return {sum,msg,person}}}
</script>
8.生命周期
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
注意:组合式API比配置项优先级高
如:
<template><h2>当前求和为:{{sum}}</h2><button @click="sum++">点我+1</button>
</template><script>import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'export default {name: 'Demo',setup(){console.log('---setup---')//数据let sum = ref(0)//通过组合式API的形式去使用生命周期钩子onBeforeMount(()=>{console.log('---onBeforeMount---')})onMounted(()=>{console.log('---onMounted---')})onBeforeUpdate(()=>{console.log('---onBeforeUpdate---')})onUpdated(()=>{console.log('---onUpdated---')})onBeforeUnmount(()=>{console.log('---onBeforeUnmount---')})onUnmounted(()=>{console.log('---onUnmounted---')})//返回一个对象(常用)return {sum}},//通过配置项的形式使用生命周期钩子//#region beforeCreate() {console.log('---beforeCreate---')},created() {console.log('---created---')},beforeMount() {console.log('---beforeMount---')},mounted() {console.log('---mounted---')},beforeUpdate(){console.log('---beforeUpdate---')},updated() {console.log('---updated---')},beforeUnmount() {console.log('---beforeUnmount---')},unmounted() {console.log('---unmounted---')},//#endregion}
</script>
9.自定义hook函数
-
什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
-
类似于vue2.x中的mixin。
-
自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
如:
1、创建usePoint.js
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){//实现鼠标“打点”相关的数据let point = reactive({x:0,y:0})//实现鼠标“打点”相关的方法function savePoint(event){point.x = event.pageXpoint.y = event.pageYconsole.log(event.pageX,event.pageY)}//实现鼠标“打点”相关的生命周期钩子onMounted(()=>{window.addEventListener('click',savePoint)})onBeforeUnmount(()=>{window.removeEventListener('click',savePoint)})return point
}
2、在需要的地方引入调用即可
<template><h2>我是Test组件</h2><h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template><script>import usePoint from '../hooks/usePoint'export default {name:'Test',setup(){const point = usePoint()return {point}}}
</script>
10.toRef
-
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
-
语法:
const name = toRef(person,'name')
-
应用: 要将响应式对象中的某个属性单独提供给外部使用时。
-
扩展:
toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
如:
<template><h4>{{person}}</h4><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪资:{{job.j1.salary}}K</h2><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="job.j1.salary++">涨薪</button>
</template><script>import {ref,reactive,toRef,toRefs} from 'vue'export default {name: 'Demo',setup(){//数据let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})// const name1 = person.name// console.log('%%%',name1)// const name2 = toRef(person,'name')// console.log('####',name2)const x = toRefs(person)console.log('******',x)//返回一个对象(常用)return {person,// name:toRef(person,'name'),// age:toRef(person,'age'),// salary:toRef(person.job.j1,'salary'),...toRefs(person)}}}
</script>
三、其它 Composition API
1.shallowReactive 与 shallowRef
-
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
-
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
-
什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
如:
<template><h4>当前的x.y值是:{{x.y}}</h4><button @click="x={y:888}">点我替换x</button><button @click="x.y++">点我x.y++</button><hr><h4>{{person}}</h4><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪资:{{job.j1.salary}}K</h2><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="job.j1.salary++">涨薪</button>
</template><script>import {ref,reactive,toRef,toRefs,shallowReactive,shallowRef} from 'vue'export default {name: 'Demo',setup(){//数据// let person = shallowReactive({ //只考虑第一层数据的响应式let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})let x = shallowRef({y:0})console.log('******',x)//返回一个对象(常用)return {x,person,...toRefs(person)}}}
</script>
2.readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
如:
<template><h4>当前求和为:{{sum}}</h4><button @click="sum++">点我++</button><hr><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪资:{{job.j1.salary}}K</h2><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="job.j1.salary++">涨薪</button>
</template><script>import {ref,reactive,toRefs,readonly,shallowReadonly} from 'vue'export default {name: 'Demo',setup(){//数据let sum = ref(0)let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})person = readonly(person)// person = shallowReadonly(person)// sum = readonly(sum)// sum = shallowReadonly(sum)//返回一个对象(常用)return {sum,...toRefs(person)}}}
</script>
3.toRaw 与 markRaw
- toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
<template><h4>当前求和为:{{sum}}</h4><button @click="sum++">点我++</button><hr><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪资:{{job.j1.salary}}K</h2><h3 v-show="person.car">座驾信息:{{person.car}}</h3><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="job.j1.salary++">涨薪</button><button @click="showRawPerson">输出最原始的person</button><button @click="addCar">给人添加一台车</button><button @click="person.car.name+='!'">换车名</button><button @click="changePrice">换价格</button>
</template><script>import {ref,reactive,toRefs,toRaw,markRaw} from 'vue'export default {name: 'Demo',setup(){//数据let sum = ref(0)let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})function showRawPerson(){const p = toRaw(person)p.age++console.log(p)}function addCar(){let car = {name:'奔驰',price:40}person.car = markRaw(car)}function changePrice(){person.car.price++console.log(person.car.price)}//返回一个对象(常用)return {sum,person,...toRefs(person),showRawPerson,addCar,changePrice}}}
</script>
4.customRef
-
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
-
实现防抖效果:
<template><input type="text" v-model="keyword"><h3>{{keyword}}</h3></template><script>import {ref,customRef} from 'vue'export default {name:'Demo',setup(){// let keyword = ref('hello') //使用Vue准备好的内置ref//自定义一个myReffunction myRef(value,delay){let timer//通过customRef去实现自定义return customRef((track,trigger)=>{return{get(){track() //告诉Vue这个value值是需要被“追踪”的return value},set(newValue){clearTimeout(timer)timer = setTimeout(()=>{value = newValuetrigger() //告诉Vue去更新界面},delay)}}})}let keyword = myRef('hello',500) //使用程序员自定义的refreturn {keyword}}}</script>
5.provide 与 inject
-
作用:实现祖与后代组件间通信
-
套路:父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据 -
具体写法:
-
祖组件中:
setup(){......let car = reactive({name:'奔驰',price:'40万'})provide('car',car)...... }
-
后代组件中:
setup(props,context){......const car = inject('car')return {car}...... }
-
如:
App.vue
<template><div class="app"><h3>我是App组件(祖),{{name}}--{{price}}</h3><Child/></div>
</template><script>import { reactive,toRefs,provide } from 'vue'import Child from './components/Child.vue'export default {name:'App',components:{Child},setup(){let car = reactive({name:'奔驰',price:'40W'})provide('car',car) //给自己的后代组件传递数据return {...toRefs(car)}}}
</script><style>.app{background-color: gray;padding: 10px;}
</style>
Child.vue组件
<template><div class="child"><h3>我是Child组件(子)</h3><Son/></div>
</template><script>import {inject} from 'vue'import Son from './Son.vue'export default {name:'Child',components:{Son},/* setup(){let x = inject('car')console.log(x,'Child-----')} */}
</script><style>.child{background-color: skyblue;padding: 10px;}
</style>
Son组件(孙)
<template><div class="son"><h3>我是Son组件(孙),{{car.name}}--{{car.price}}</h3></div>
</template><script>import {inject} from 'vue'export default {name:'Son',setup(){let car = inject('car')return {car}}}
</script><style>.son{background-color: orange;padding: 10px;}
</style>
6.响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由
reactive
创建的响应式代理 - isReadonly: 检查一个对象是否是由
readonly
创建的只读代理 - isProxy: 检查一个对象是否是由
reactive
或者readonly
方法创建的代理
<template><h3>我是App组件</h3>
</template><script>import {ref, reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'export default {name:'App',setup(){let car = reactive({name:'奔驰',price:'40W'})let sum = ref(0)let car2 = readonly(car)console.log(isRef(sum))console.log(isReactive(car))console.log(isReadonly(car2))console.log(isProxy(car))console.log(isProxy(sum))return {...toRefs(car)}}}
</script><style>.app{background-color: gray;padding: 10px;}
</style>
四、Composition API 的优势
1.Options API 存在的问题
使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
.Composition API 的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。(但要利用hook函数)
五、新的组件
1.Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
2.Teleport
-
什么是Teleport?——
Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。<teleport to="移动位置"><div v-if="isShow" class="mask"><div class="dialog"><h3>我是一个弹窗</h3><button @click="isShow = false">关闭弹窗</button></div></div> </teleport>
如:
<template><div><button @click="isShow = true">点我弹个窗</button><teleport to="body"><div v-if="isShow" class="mask"><div class="dialog"><h3>我是一个弹窗</h3><h4>一些内容</h4><h4>一些内容</h4><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: rgba(0, 0, 0, 0.5);}.dialog{position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);text-align: center;width: 300px;height: 300px;background-color: green;}
</style>
3.Suspense
-
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
-
使用步骤:
-
异步引入组件
import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
-
使用
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>
-
如:
App.vue
<template><div class="app"><h3>我是App组件</h3><Suspense><template v-slot:default><Child/></template><template v-slot:fallback><h3>稍等,加载中...</h3></template></Suspense></div>
</template><script>// import Child from './components/Child'//静态引入import {defineAsyncComponent} from 'vue' //定义一个异步组件const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入export default {name:'App',components:{Child},}
</script><style>.app{background-color: gray;padding: 10px;}
</style>
clild.vue
<template><div class="child"><h3>我是Child组件</h3>{{sum}}</div>
</template><script>import {ref} from 'vue'export default {name:'Child',async setup(){let sum = ref(0)let p = new Promise((resolve,reject)=>{setTimeout(()=>{resolve({sum})},3000)})return await p}}
</script><style>.child{background-color: skyblue;padding: 10px;}
</style>
六、其他
1.全局API的转移
-
Vue 2.x 有许多全局 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,即:
Vue.xxx
调整到应用实例(app
)上2.x 全局 API( Vue
)3.x 实例 API ( app
)Vue.config.xxxx app.config.xxxx Vue.config.productionTip 移除 Vue.component app.component Vue.directive app.directive Vue.mixin app.mixin Vue.use app.use Vue.prototype app.config.globalProperties
-
2.其他改变
-
data选项应始终被声明为一个函数。
-
过度类名的更改:
-
Vue2.x写法
.v-enter, .v-leave-to {opacity: 0; } .v-leave, .v-enter-to {opacity: 1; }
-
Vue3.x写法
.v-enter-from, .v-leave-to {opacity: 0; }.v-leave-from, .v-enter-to {opacity: 1; }
-
-
移除keyCode作为 v-on 的修饰符,同时也不再支持
config.keyCodes
-
移除
v-on.native
修饰符-
父组件中绑定事件
<my-componentv-on:close="handleComponentEvent"v-on:click="handleNativeClickEvent" />
-
子组件中声明自定义事件
<script>export default {emits: ['close']} </script>
-
-
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
-
…
第三方库
提供第三方库的网站:https://www.bootcdn.cn/
好用的第三方库:
dayjs是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的 API 设计保持完全一样
浏览器本地存储
webStorage: SessionStorage和LocalStorage都称为webStorage
1.存储内容大小一般支持5MB左右 (不同浏览器可能还不一样)
2.浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
3.相关API:
1. xxxxxStorage.setItem(' key", 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
2. xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。
3.xxxxxStorage.removeItem( " key");
该方法接受一个键名作为参数,并把该键名从存储中删除。
4.xxxxxStorage.clear()
该方法会清空存储中的所有数据
备注:
1.SessionStorage存储的内容会随着浏览器窗口关闭而消失
2.LocalStorage存储的内容,需要手动清除才会消失。
3.xxxxxStorage.getItem(xxx) 如果xxx对应的value获取不到,那么getltem的返回值是null。
4.JsoN.parse(nu11)的结果依然是null。