Vue2学习笔记

一.Vue核心

01.初始Vue

02.Vue模板语法({{name}})

03.数据绑定(单向v-bind和双向v-model)

04.el和data的两种写法(el v.$mount挂载和data对象式和函数式)

对象式:data:{name:‘zs’} 函数式:data(){ return } →必须有return

05.MVVM模型(Vue的设计参考了该模型,模型(data)、视图(模板)、视图模型(vue实例对象),所以vm一般指Vue的实例对象)

(data中所有的属性,最后都出现在vm身上)

*06.数据代理

061.object.defineProperty方法(较高级的添加属性的方法,能给追加的属性进行限制)(给person添加age属性)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dSeOuhB9-1676712361576)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230208223601921.png)]

还有其他配置项,如:get()、set()

get(){ return } 当有人读取person里的age属性时,getter被调用

set(value){ } 当有人修改person里的age属性时,setter被调用

062.何为数据代理(通过一个对象代理对另一个对象中属性的操作(读/写))

(vm._data就是我们使用的data(此处无代理,是数据劫持))、数据代理原理图示

(Vue通过数据代理将_data里的数据直接挂载在vm上,即使用object.defineproperty,定义getter、setter,更加方便的操作data中的数据)

(Vue中的数据代理:通过Vue对象来代理data对象中属性的操作(读写))

基本原理:通过object.definproperty()把data对象中所有属性添加到vm上。为每一个添加到vm上的属性,都指定一个gettersetter。在gettersetter内部去操作(读写)data中的数据。若有人通过vm去修改放到vm身上的属性,setter工作。

代理实质:通过vm读取或修改vm身上的name或address,其实改的就是data中的name和address

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KTVaKev-1676712361577)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230218153101197.png)]

数据劫持简单解释:

把data的数据进行加工才放到_data中,只要修改data中的数据,就会让页面上用到该数据的地方自动更新(响应式),所以必须让vue能检测得到data中的数据改变了

07.事件处理

071.v-on:或@绑定事件。事件的回调需要配置在与data同级的methods对象中,最终会在vm上。methods里配置的函数不要用箭头函数,否则this就不是vm了。methods中配置的函数,都是被Vue管理的函数,里面的this指向的是vm或者组件实例对象。@click=‘demo($event ,…)’,里面定义几个参数,回调函数里就能接收到几个参数)

(只有data里的数据才会做数据代理和数据劫持,methods里的不会,因为只有data里的数据需要直接用到页面中)

(click、scroll(给滚动条加的滚动事件)、wheel(鼠标滚动轮的滚动事件))

072.事件修饰符(如@click.prevent → 阻止默认事件(如点击链接时不要跳转))

(stop阻止事件冒泡,once事件只触发一次,capture使用事件的捕获模式,self只有event.target是当前操作的元素才是触发事件,passive事件的默认行为立即执行,无需等待事件回调执行完毕)

(嵌套关系时先捕获再冒泡,冒泡阶段才是处理事件的,所以使用capture时由捕获阶段处理事件,会由原来的21变为12)

(self一定程度上也能阻止冒泡,)

073.键盘事件(@keyup(常用)、@keydown)

(event.keyCode、event.target.value)
( 使用方法 → @keyup.esc = ‘ showinfo ’ → 即按下esc键并松开时才会触发该事件)

(Vue中常用的按键别名:回车enter、删除delete、退出esc、空格space、换行tab、上up、下、左、右)

(特殊键 tab,本身作用就是使焦点从当前位置切走,所以必须配合keydown使用,同理Ctrl、alt、meta一样)

(为提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为短横线命名法)

(系统修饰键:Ctrl、alt、shift、meta。配合keyup使用:按下修饰键的同时再按下其他键,随后释放其他键,事件才会触发。配合keydown使用:正常触发事件)

(Vue.config.keyCodes.自定义键名=键码,可以定制按键别名)

08.计算属性

data中的数据只要一发生变化,Vue就会重新解析页面

(使用方法:与data同级,computed: { fullname:{ get(){ return } ,set(value){}} } )→set非必要

其中的get什么时候被调用:1.初次读取fullname时,2.所依赖的数据发生变化时

和methods的最大区别:computed内部有缓存机制,值不变时多次读取fullname只会调用一次get(),效率更高,调试方便

定义:要用的属性不存在,要通过已有的属性计算得来

原理:底层借助了object.defineproperty方法提供的getter、setter

注意:计算属性最终会出现在vm上,直接读取使用即可,如果计算属性要被修改,必须写setter去响应修改,且set中要引起计算时依赖的数据发生改变

简写:::当确定不需要set进行修改,只需要get进行读取时,可简写为computed:{ fullname() { return } }

09.监视属性

091.(使用监视方法1:和data同级 watch:{ ishot: { handler(newvalue,oldvalue){} },能检测data和computed里的数据

方法2:vm.$watch(‘ishot’:{ handler(…){}} )

区别:当需要监视的对象暂未明确时,可以使用方法二

(其他配置项:immediate:true → 初始化时让handler调用一下)

(当被监视的属性变化时,回调函数自动调用,进行相关操作,监视的属性必须存在,才能进行监视!)

092.深度监视

监视多级结构中数据的变化:watch:{isHot:{ 'number.a ':{}}

当需要监视numbers:{a:1,b:2},number里每个数据的改变时,需要用到深度监视,只需要在watch里加deep:true配置项即可

*Vue中的watch默认不监视对象内部值的改变(一层)

*配置deep:true可以监视对象内部值的改变(多层)

注意:*Vue自身可以检测对象内部值的改变,但Vue提供的watch默认不可以

*使用watch时根据数据的具体结构,决定是否采用深度监视

简写:::watch:{ ishot (newvalue,oldvalue) { } }

方法二:vm.$watch( ‘ishot’ , function(newvalue,oldvalue) {} )

计算属性和监视属性都是computed对象里有一个fullname对象,里面才包着函数

10.计算属性和监视属性的区别

*computed能完成的功能,watch都能完成

*watch能完成的功能,computed不一定能完成,如watch可以进行异步操作,因为computed靠的是return返回值,而返回值不能等一等再返回,所以computed不能进行异步操作,如定时器等

注意:

*所被Vue管理的函数,最好写成普通函数,不被Vue管理的函数(如定时器、Ajax、Promise的回调函数…),最好写成箭头函数(箭头函数没有自己的this,需要往外找),这样的this的指向才是vm或组件实例对象

11.class与style绑定

111.class样式

绑定class样式(需要后期改变class的样式)——字符串写法(适用于样式的类名不确定,需要动态指定)→ :class=‘mood’

(随机数0~3:Math.floor ( Math.random()*3 ) >>> Math.random 取(0,1) >>> .floor 向下取整 >>> .ceil 向上取整)

绑定class样式——数组写法(适用于要绑定的样式个数、名字不确定 >>> :class=“classArr”)

绑定class样式——对象写法(适用于要绑定的样式个数、名字确定,但要动态决定用不用 >>> :class=“classObj”,在data里truefalse控制)

112.style样式

:style=" { fontSize : fsize + ‘px’ } " data: { fsize : 40}

一般写成 :style=‘styleObj’ data: { styleObj : { fontSize : fsize + ‘px’ }}

另外数组写法用的比较少,略

12.条件渲染

(v-show、v-if)

*v-if写法:v-if = ’表达式‘ v-else-if = ’表达式‘ v-else = ’表达式‘ >>>不展示的Dom元素会被移除 >>>适用于切换频率较低的场景,

*v-show写法:v-show = ’表达式‘ >>>不展示的DOM元素display隐藏,不会移除 >>>适用于切换频率较高的场景

*< template > 模板标签 → 不会影响结构,不会出现在页面中,但只能配合 v-if 使用

13.列表渲染

131.v-for

<li v-for="(p,index) in persons" :key="p.id">  {{p.name}}-{{p.age}} </li>     >>>遍历数组 >>>index用不到可以不写
<li v-for="(value,k) of car" :key="k"> {{k}}-{{value}} ></li>			    >>>遍历对象

还能遍历字符串、遍历指定次数

132.key的原理

【面试题1】

1.react、Vue中的key有什么作用?(key的内部原理)

1)虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM和旧虚拟DOM的差异比较,比较规则如下:

2.对比规则:

*旧虚拟DOM中找到了与新虚拟DOM相同的key:

若虚拟DOM中内容没变 >>> 直接使用之前的真实DOM;

若虚拟DOM中内容变了 >>> 生成新的真实的DOM,随后替换掉页面中之前的真实DOM

*旧虚拟DOM中未找到与新虚拟DOM相同的key :

创建新的真实DOM,随后渲染到页面中

3.用index作为key可能会引发的问题:

*若对数据进行:逆序添加、逆序删除等破坏顺序操作时,会产生没有必要的真实DOM更新 >>>页面效果没问题,但效率低

*如果结构中还包含输入类的DOM时,会发生错误DOM更新 >>> 界面有问题

4.开发中如何选择key?

*最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值

*如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没问题的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H1XnO0Mx-1676712361578)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230210163255981.png)]

133.列表过滤(filter)

134.列表排序(arr.sort)

135.Vue检测数据原理

data -> 将data加工(里面每个元素都加上getter和setter) -> vm._data=data

data一变,vm._data里的数据跟着变,setter监测到后重新解析页面,生成虚拟DOM进行新旧对比,最后渲染页面

1)Vue会监视data中所有层次的数据

2)如何监测对象中的数据:通过setter实现监视,且要在new Vue时就传入要监测的数据,即在data里

​ *对象中后续追加的属性,Vue默认不做响应式处理

​ *如需给后添加的属性做响应式,可以使用Vue.set 或 vm.$set

3)如何监测数组中的数据:通过包裹数组更新元素的方法实现,本质就是做了两件事:

​ *调用原生对应的方法对数组进行更新,然后重新解析模板,进而更新页面

4)在Vue修改数组中的某些元素一定要用如下方法:

​ *使用这些API:push()、pop()、shift()、unshift()、splice、sort、reverse

​ *Vue.set() 或 vm.$set()

注意:Vue.set() 或 vm.$set() 不能给vm 或 vm的根数据对象添加属性

136.Vue.set的使用

Vue.set(this.student.sex='男')   or    this.$set(this.student.sex='男')

137.Vue监测数据改变的原理

14.收集表单数据

1)若<input type=“text”,则v-model收集的是value值,用户输入的就是value值

2)若<input type=“radio” ,则v-moedl收集的是value值,且要给标签配置value值

3)若<input type=“checkbox” ,

​ *没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)

​ *配置input的value属性:**v-model的初始值是非数组,则收集的就是checked,,若初始值是数组,收集的是value组成的数组

注意:v-model的三个修饰符:

​ *lazy:失去焦点再收集数据

​ *number:输入字符串再转为有效的数字

​ *trim:输入首尾空格过滤

15.过滤器(filter)

16.内置指令

v-text:向其所在的节点中渲染文本内容,与插值语法的区别:v-text会替换掉几点中的内容,{{xx}}则不会 (<div v-text=“name” )

v-html:向指定节点中渲染包含html结构的内容。与插值语法的区别:v-html会替换掉节点中的所有内容,且v-html能识别html结构

注意:v-html有安全性问题!!!在网站上动态渲染任意HTML是非常危险的,容易导致XXS攻击,一定要在可信的内容上使用v-html,永远不要用在用户提交的内容上

v-cloak:(没有值),本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性;使用css配合v-cloak可以解决网速慢时页面展示出{{×××}}的问题

<h1 v-cloak> {{name}} </h1>       >>>   [v-cloak]:{ display:none }		>>>[v-cloak]选择所有含v-cloak属性的元素

v-once:所在节点在初次动态渲染后,就视为静态内容了,以后的数据改变也不会引起v-once所在结构的更新,可以用于优化性能

<h1 v-once>初始化的n值是:{{n}} </h1>

v-pre:跳过其所在节点的编译过程,可以利用它跳过使用指令语法、没有使用插值语法的节点,会加快编译(也没有值)

17.自定义指令

定义语法:

1)局部指令:new Vue ({ directives: {指令名:配置对象} }) 或 new Vue ({ directives {指令名:回调函数} })

2)全局指令:Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)

配置对象中常用的三个回调:

*bind:指令与元素成功绑定时调用

*inserted:指令所在元素被插入页面时调用

*update:指令所在模板结构被重新解析时调用

注意:

*指令定义时不加 v- ,但是使用时需要加 v-

*指令名如果是多个单词,要使用 kebab-case 命名方式,不要用 camelCase命名

18.生命周期:

(生命周期回调函数、生命周期函数、生命周期钩子)

定义:Vue在关键时刻帮我们调用的一些特殊名称的函数

  • 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的

  • 生命周期函数中的this指向是vm或组件实例对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8i7wAmVy-1676712361580)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230211160026068.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KY6Zx5R2-1676712361581)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230211161243397.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NHgrKmSj-1676712361582)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230211161344309.png)]

常用的生命周期钩子:

1)mounted(){ } >>> Vue完成模板的解析并把初始的真实DOM放入到页面后(挂载完毕),调用mounted

​ >>>发送Ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作

2)beforeDestroy(){}:清除定时器、解绑自定义事件、取消订阅消息等收尾工作

关于销毁Vue实例:

  • 销毁后借助Vue开发者工具看不到任何消息
  • 销毁后自定义事件会失效,但原生DOM事件依然有效
  • 一般不会再beforeDestroy操作数据,因为即使操作数据也不会再触发更新流程了

二.Vue组件化编程

2.1模块与组件、组件与组件化
  • 模块:对外提供特定功能的js程序 >>> 复用js、简化js、提高js运行效率
  • 组件:用来实现局部(特定)功能效果的代码集合(html、css、js、image…) >>> 复用编码、简化项目编码、提高运行效率
  • 模块化:当应用中的js都以模块来编写,那这个应用就是一个模块化的应用
  • 组件化:当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用

**2.2 非单文件组件:**一个文件总包含n个组件

2.2.1 Vue中使用组件的三大步骤:

1)定义组件(创建组件)

2)注册组件

3)使用组件(写组件标签)

2.2.2 如何定义一个组件:使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那份options几乎一样,但区别如下:

  • el不用写,因为最终所有的组件都要经过一个vm管理,由vm中的el决定服务于哪个容器
  • data必须写成函数,为了避免组件被复用时,数据存在引用关系(即每次使用data里的数据都是一样的,不会被改)

*使用template可以配置组件结构

2.2.3 如何注册组件:

  • 局部注册:靠new Vue的时候传入components选项

  • 全局注册:靠Vue.components(‘组件名’,组件)

2.2.4 编写组件标签:

2.2.5 关于VueComponent

1)school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的

2)我们只需要写,Vue解析时会帮我们创建shool组件的实例对象,即Vue帮我们执行的:new VueComponent(options)

3)注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

4)关于this指向:

  • 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是:VueComponent实例对象,即vc

  • new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是:Vue实例对象,即vm

(简单来说,VC就是小型的vm,但是VC里的data必须写成函数式,且不能写el)

2.2.6一个重要的内置关系

(VueComponent.prototype._ _ proto _ _ === Vue.prototype)

为什么要有这个关系:让主讲实例对象(VC)可以访问到Vue原型上的属性、方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bJ5FLUe-1676712361583)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230214152406605.png)]

**2.3 单文件组件:**一个文件中只包含有1个组件

三.使用Vue脚手架

3.1 初始化脚手架
  • Vue脚手架:是Vue官方提供的标准化开发工具(开发平台)

  • 具体步骤:

    1)(仅第一次执行):全局安装:@Vue/cli >>> npm i -g @vue/cli

    2)切换到你要创建项目的目录,然后使用命令创建项目:vue create ×××

    3)启动项目:npm run serve

  • 脚手架文件结构:

    .gitignore:git版本管制忽略的配置,,babel.config.js:babel的配置文件,,package.json:应用包配置文件,,package-lock.json:包版本控制文件

  • 注意:如果下载缓慢,请配置npm淘宝镜像:(电脑终端执行) npm config set registry https://registry.npm.taobao.org

  • Vue脚手架隐藏了所有webpack相关的配置,若想查看具体的webpack配置,请执行:vue inspect > output.js

  • ( babel : es6 转成es5,,eslint:检查语法)

3.2 关于不同版本的Vue:

1)Vue.js和Vue.runtime.×××.js的区别:

  • Vue.js是完整版的Vue,包含:核心功能+模板解析器
  • Vue.runtime.×××.js是运行版的Vue,只包含:核心功能,没有模板解析器
  • (Vue=Vue核心+模板解析器(占三分之一),代码写完后交给webpack打包,而模板解析器并不需要打包)

2)因为Vue.runtime.×××.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容

3.3 render函数

单文件组件创建Vue实例对象vm时,不能使用template

new Vue({															new vue({
	el:'#app',															render: h => h(App)
	render(createElement){											})
		return createElement('h1','你好啊!')      精简后>>>
	}
})
//其中createElement是一个方法,创建html元素

3.4 vue.config.js配置文件

使用vue.config.js对脚手架进行个性化定制,如入口文件修改、关闭语法检查等,详情见:https://cli.vuejs.org/zh

3.5 ref属性

  • 被用来给元素或子组件注册引用信息 (id的替代者)

  • 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)

  • 使用方式:

    打标识:<h1 ref="×××">....</h1>   或    <School ref="×××"></School>
    
    获  取:this.$refs.×××
    

3.6 props配置项

  • 功能:让组件接收外部传过来的数据(父传子,最简单。若要子传父,前提是父先给子一个函数,子在合适的时候调用该函数)

  • 传递数据:(父组件)

    <School name="×××"/>
    
  • 接收数据:(与data同级)(子组件)

    props:['name']					//方式一(传递数据)
    props:{
    	name:String					//方式二(限制类型)
    }
    props:{
    	name:{
    		required:true,
    		type:String,
    		default:'zs'			//方式三(限制类型、限制必要性、指定默认值)
    	}
    }
    
  • 注意:props是只读的,Vue底层会检测你对props的修改,如果进行了修改,就会发出警告(结果也能修改成功,但是控制台会报错且后续可能会引起奇奇怪怪的错误),若业务需求确实需要修改,那么可以复制一份props的内容到data中,然后去修改data中的数据

  • 与数据冲突时,以props为主?

3.7 mixin(混入/合)配置项

  • 功能:可以把多个组件公用的 配置 (如data、methods、生命周期等等)提取成一个混入对象

  • 使用方式:(如student和school两个组件里的method都一样,可以提取到一个mixin.js文件中并对外暴露)

  • 使用混入:1)全局混入:入口文件中import ××× ,并Vue.mixin( ××× )

    ​ 2)局部引入:在需要引入的组件中import ××× from …,与data同级 mixins : [’ ××× ']

  • 数据冲突时,以本身组件为主,声明周期冲突时,都要

3.8 插件

  • 功能:用于增强vue

  • 本质:是一个包含install方法对象,install第一个参数是Vue,第二个以后的参数是插件使用者传递的数据

  • 定义插件:创建plugins.js文件,export default { install (vue,x,y,) {Vue.filter({ }) …}},添加实例方法、添加全局指令、给原型对象添加方法…

  • 使用插件:Vue.use( )

3.9 scoped

  • 作用:让样式在局部生效,防止冲突
  • 写法:<style scoped

3.10.webstorage(浏览器本地存储)(js里就有)(SessionStorage 和 LocalStorage)

1)存储内容大小一般支持5MB左右(不同浏览器可能不同)

2)浏览器端通过Windows.sessionStorage和Windows.localStorage属性来实现本地存储机制

3)相关api:

  • ××××Storage.setItem( ‘ key ', ’ value ') >>> 该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其值
  • ××××Storage.getItem( ‘ person’) >>>读取存储中的数据
  • ××××Storage.removeItem( ‘ key ') >>> 删除存储中的数据
  • ××××Storage.clear( ) >>> 清空存储中的所有数据

4)注意:

  • SessionStorage存储的内容会随着浏览器窗口关闭而消失
  • LocalStorage存储的内容,需要手动清除才会消失
  • ××××Storage.setItem( ‘ key ', ’ value ') 如果××××对应的value获取不到,那么返回值是null
  • JSON.parse(null)的结果依然是null

3.11 组件的自定义事件

1)一种组件间通信的方式,适用于:子组件 ===>父组件

2)使用场景:子组件给父组件传数据,就要在父组件中给子组件绑定自定义事件(事件的回调在父组件中)

3)绑定自定义事件:

  • 方式一:在父组件中:

    <Demo @abc="test"/>   或   <Demo v-on="test"/>
    
  • 方式二:在父组件中:

<Demo ref="demo"/>
....
mounted(){
	this.$refs.×××.$on('abc',this.test)
}
  • 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法

4)触发自定义事件:this.$emit ( ‘abc’ , 数据)

5)解绑自定义事件:this.$off ( ‘abc’ )

6)组件上也可以绑定原生DOM事件,需要使用native修饰符 >>> <student @click.native = ‘demo’

7)注意:通过this. r e f s . × × × . refs.×××. refs.×××.on(‘abc’,回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出现问题( $on >>> 当abc方法一被调用时执行后面的回调函数)

3.12 全局事件总线

1)一种组件间通信的方式,能实现任意组件间通信

2)安装全局事件总线:

new vue({
	....
	beforeCreate(){
		Vue.prototype.$bus = this  //$bus就是当前应用的vm
	}
})

3)使用事件总线:

  • 接收数据:A组件接收数据,则在A组件中给$bus绑定自定义事件,事件的回调函数留在A组件中

    methods:{
    	demo(){ }
    }
    	.....
    	mounted(){
    		this.$bus.$on('hello',this.demo)     //一触发hello的自定义事件,就执行demo的回调函数
    	}
    
  • 提供数据:

    this.$bus.$emit('hello',数据)			>>> 触发hello的自定义事件,并把数据传过去
    

4)最好在beforeDestroy钩子中,用 $off 去解绑当前组件所用到的事件

3.13 消息订阅与发布

1)一种组件间通信的方式,适用于任意组件间通信、

2)使用步骤:

  • 安装pubsub:npm i pubsub-js (还有很多其他的包)

  • 引入:import pubsub from ’ pubsub-js ‘

  • 接收数据:A组件想要接收数据,则在A组件中订阅消息,订阅的回调留在A组件中

    methods:{
    	demo(msgName,data){}	//hello的回调demo能接收到到2个参数(消息名和传递过来的数据)
    }
    mounted(){
    	this.pubId = pubsub.subscribe('hello',this.demo)  //订阅名为hello的消息,一发布就执行demo。pubId为后面销毁用
    }
    
  • 提供数据:

    pubsub.public('hello',数据)
    

4)最好在beforeDestroy钩子中,用pubsub.unsubscribe ( this.pubId ) 取消订阅

3.14 nextTick

(也是生命周期钩子!!)

1)语法:this.$nextTick (回调函数)

2)作用:在下一次DOM更新结束后执行其指定的回调

3)什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

​ (vue在解析回调函数时,不是每执行完一句代码就解析一次页面,而是当整个回调执行完之后才重新解析一遍页面,所以有时候刚创建input输入框但是并没有出现到页面中时,后面执行的输入框自动获得焦点的代码就会失效,因为执行该代码时还没重新渲染页面)

3.15 Vue封装的动画与过渡
  • 作用:在插入时、更新或移除DOM元素时,在合适的时候给元素添加样式类名

  • 图示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QHJYlhPe-1676712361585)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230216150011156.png)]

1)动画:

<transition appear>	//若加了name属性,那下面的样式名是 .名字-enter-active,加了appear属性初始化页面时就有动画
	<h1 @click='isShow = !isShow' > hello! </h1>
</transition>

isShow = true
.v-enter-active{						//进入的过程
	animation : showActive 1s linear	// 调用动画showActive  1s 匀速
}
.v-leave-active{								//离开的过程
	animation : showActive 1s linear reverse   //动画反转
}
@keyframes showActive{			//定义动画,showActive为动画名
	from{
		transform : translateX (-100%)
	}
	to{
		transform : translateX (0px)
	}
}

2)过渡:

<transition name='hello' appear>	
	<h1 @click='isShow = !isShow' > hello! </h1>
</transition>

isShow = true
h1{
	transition : 1s linear		//谁需要过渡效果就往谁身上加
}
hello-enter{					//进入的起点
	transform : translateX (-100%)
}
hello-enter-to{					//进入的终点
	transform : translateX (0)
}
hello-leave{					//离开的起点
	transform : translateX (0)
}
hello-leave-to{					//离开的终点
	transform : translateX (-100%)
}

3)注意:若有多个元素需要过渡,则需要使用:<translate-group , 并且每个元素都要指定key值

四.Vue中的Ajax

4.1常用的发送Ajax请求的方式有:xhr(鼻祖)(麻烦,不常用)、jQuery(有封装Ajax请求,但其专业是封装DOM操作)、axios(用得多)、fetch(直接在Windows身上就有这个方法,和xhr平级。特点是给请求返回的数据包2层Promise,且有兼容性问题IE×)

4.2 vue中的axios

使用方法:npm i axios,import引入

axios.get('url').then(
	response=>{			//请求成功的形参,返回的都是对象
		console.log('请求成功了', response.data)
	},
	error=>{			//失败的形参
		console.log('请求失败了', error.message)
	}
)
4.3 代理服务器

开启代理服务器:(vue.config.js,修改后要重启)

方式一:
devServer:{
	proxy:'需要请求的数据的来源的服务器的地址'
}
缺点:1)不能配置多代理,只能配置一个
	 2)不能灵活地控制走不走代理 (若前端资源与请求的资源重名时,直接从本身取数据,不走代理,优先匹配前端资源)
方式二:
devServer:{
	proxy:{
		'/api':{							//'/api'为请求前缀,请求地址只要含该前缀就走代理
			target:'<url>',					//代理目标的基础路径
			pathRewrite : {'^/api':''},		//重写路径,向目标服务器发送请求时将所有/api前缀去掉
			ws:true,						//用于支持websocket,默认为true
			changeOrigin:true,			   //用于控制请求头中的host值(即请求地址撒不撒谎),默认为true
		},
		'/demo':{
			target:'<url>',
			pathRewrite : {'^/demo':''},	
		},
		.....								//能配置多个代理服务器
	}
}
4.4 vue-resource

(发送Ajax请求的一个vue里的插件库,是对xhr的封装,,现已不维护,慎用!!!)

1)使用方法:npm i vue-resource 入口文件中import 并Vue.use(),,至此所有vm和VC身上都多了一个 $http

this.$http.get('url').then(response=>{},error=>{})
4.5 slot插槽
  • 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件===>子组件

1)默认插槽:

<Category>
<img src='' alt=''
</Category>

Category组件中:
<div>
	<h1></h1>
	<slot>这里可以写内容,当组件标签体里面没有内容时显示,有内容时不显示</slot>
</div>

2)具名插槽:

<Category>
<img slot='center' src='' alt=''/>
<a  slot='footer' href=''></a>
</Category>

Category组件中:
<div>
	<h1></h1>
	<slot name='center'>这里可以写内容,当组件标签体里面没有内容时显示,有内容时不显示</slot>
	<slot name='footer'>这里可以写内容,当组件标签体里面没有内容时显示,有内容时不显示</slot>
</div>

注意:1)多个内容可以放在同一个插槽里,不会覆盖,可以追加

​ 2)如果使用了<template标签,可以使用 v-slot : footer。

3)作用域插槽

数据在组件自身,但根据数据生成的结构需要组件的使用者来决定

App组件: (1.往插槽里放结构>>>是插槽的使用者,而需要用到的数据games在Category组件中)
<Category>
<template scope='demo'>			//3.用scope接收传过来的数据(是一个对象),命名为demo
	<Ul>
		<li>g in demo.games</li>
	</ul>
</template>
</Category>

Category组件:
<div>
	<h1></h1>
	<slot :games='games'></slot>	//2.将数据games传给插槽的使用者,(这里可以传多个数据,所以使用者接收的是一个对象)
</div>
data:games:[]

注意:

  • 上面的scope 也可以用 slot-scope

  • 作用域插槽也可以有名字

五.Vuex

5.1 vuex是什么

  • 专门在Vue中实现集中式状态 (数据) 管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间的通信

  • 什么时候使用Vuex >>> 1) 多个组件依赖于同一状态 2)来自不同组件的行为需要变更同一状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tADO9ydo-1676712361586)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230216211907805.png)]

5.2 使用Vuex

1)npm i vuex@3 (vue2用vuex3,vue3用vuex4)

2)src目录下新建store文件>index.js,配置index.js :

import Vuex from 'vuex'

import Vue from ’vue'
Vue.use(Vuex)   			//默认需要先使用Vuex才能new出vuex实例,而不放在入口文件中是因为顺序问题

const actions={ }			//用于响应组件中的动作
const mutations = { }		//用于操作数据
const state = { }			//用于存储数据

export default new Vuex.Store({		//创建并暴露store
	actions,
	mutations,
	state
})

3)并在入口文件中import引入store,并在new的vm中添加配置项store

4)例子:

//组件中
data里:  n:1
methods:{
	add(){
		this.$store.dispatch('jia',this.n)		//如果不需要经过actions,此处直接调commit,越过dispatch
   }
}
store的index.js里:
const actions={ 
	jia(context,value) => {				//context(上下文)其实是迷你store,actions里的函数名一般用小写
		context.commit('JIA',value)
	}
}			
const mutations = {						//mutations里的函数名一般用大写
	JIA(state,value)=>{
		state.sum += value
	}
}		
const state = { sum : 0 }			//用于存储数据
5.3 store里的另一个配置项getters
  • 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工
  • 在store.js中追加getters配置
const state = { sum:0 }
const getters = {
	bigSum(state){ return state.sum * 10}    //注意必须有返回值return
}
export default new Vuex.Store({		
	......,
	getters
})
  • 组件中读取数据:$store.getters.bigSum
5.4 四个map方法的使用

1)mapState方法:用于帮助我们映射state中的数据为计算属性

2)mapGetters方法:用于帮助我们映射getters的数据为计算属性

在标签里总是写 {{ $store.state.sum }} {{ $store.getters.bigSum }}太长太麻烦,可以利用计算属性解决:


import {mapState,mapGetters} from 'vuex'

computed:{
	he(){
		return this.$store.state.sum									//法1.写计算属性
	}
															// 法2.mapState生成计算属性,读取state中的数据
	...mapState({ he:'sum',xuexiao:'school',xueke:'subject' })			//对象写法
	...mapState(['sum','school','subject'])								//数组写法
	//即帮我们批量生成了he(){return this.$store.state.sum}、xuexiao(){...}等方法
}
mapGetters同理!!!

3)mapActions方法:

用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(×××)的函数

<button @click='add(n)'>点我加一</button>	//!!!这里的add方法必须小括号传参,否则使用mapActions时默认为鼠标点击事											       件,而无法获取需要用到的数据n
import {mmapActions,mapMutations} from 'vuex'
data里有 n:1
methods:{
	add(){
		this.$store.dispatch('jia',this.n)				//法一.亲自使用方法调用dispatch提交数据
	} 
												  //法二.借助mapActions生成对应的方法,调用dispatch去联系Actions
	...mapActions({add:'jia'},{subtract:'jian'})   //此为对象写法,同样还有数组写法
}

4)mapMutations方法:(同理)

用于帮助我们生成与mutations对话的方法,即:包含$store.commit(×××)的函数

5.5 vuex的模块化写法

1)目的:让代码更好维护,让多种数据分类更加明确

2)修改store.js配置:

const countOptions={			//这样就有2个名字分别为countOptions和personOptions的Store
	namespaced:true,			//后续凡是需要精确到哪个Store里就必须要开启命名空间
	actions:{},
	mutations:{},
	state:{}
}
const personOptions={...}

export default new Vuex.Store({
	modules:{
		countOptions,
		personOptions
	}
})

3)使用mapState等方法的写法:

组件中:
import {mapState} from 'vuex'

computed:{
	sum(){
		...mapState('countOptions',['sum','school',subject])	//直接到名为countOptions的Store里的state找数据
	}
}
methods:{
	...mapMutations('countOptions',{ add:'JIA' })		//这里也需要告诉map..去哪个Store里找数据
}

4)如果不用mapState等方法,而是自己写:

computed:{		
	sum(){													 	//state里包含countOptions和personOptions
		return this.$store.state.countOptions.sum				//注意:名字写在state后面!!
	},
	firstPersonName(){
		return this.$store.getters['personOptions/firstPersonName']
		//getters里面包含的是 personOptions/firstPersonName:"zs",countOptions/sum:0
		//原本是getters.personOptions/firstPersonName 但其写法不符合规范,需要换成上面的写法
	}
}
methods:{
	add(){
		this.$store.commit('personOptions/ADD_PERSON',personObj)		//注意名字写在前面!!!!
	}
}

5)也可以把不同名字的Store拆分成文件,并对外分别暴露,在相关文件引入

六.vue路由(Vue-router)

​ >>>路由就是一组key-value的对应关系 >>> 多个路由,需要经过路由器的管理

6.1 vue-router 是 vue的一个插件库,专门用来事项SPA应用

6.2 SPA是什么?

1)单页web应用

2)整个应用只有一个完整的页面

3)点击页面中的导航链接不会刷新页面,只会做页面的局部更新

4)数据需要通过Ajax请求获取

6.3 路由是什么?

1)概念:一个路由就是一组映射关系(key-value),key为路径,value可能是function或component

2)路由的分类:

  • 后端路由:* value是function,用于处理客户端提交的请求

    ​ *工作过程:服务器接收到一个请求时,根据请求的路径找到匹配的函数来处理请求,返回响应数据

  • 前端路由:* value是component,用于展示页面内容

    ​ *工作过程:当浏览器的路径改变时,对应的组件就会显示

6.4 路由的基本使用

1)安装:npm i vue-router@3 (vue2用vue-router@3,vue3用vue-router@4)

2)引入并应用:import VueRouter from , Vue.use(VueRouter)

3)配置router:src下新建router,下index.js

import VueRouter from 'vue-router'
import Home from '...'

const router=new VueRouter({
	routes:[
		{ 
			path:'/home',
			component:Home     //还可以有name配置项
		},
		{},
		......
	]
})
export default router

4)在入口文件中引入router,并加入配置

import router from '...'
......
new Vue({
	el:'#app',
	render:h=>h(app),
	......
	router
})

5)实现切换:(<router-link,,,to=‘ ’)(active-class可配置高亮样式)!!!最终<router-link标签会变成 a标签

<router-link active-class='active' to='/about'>About</router-link>

6)指定展示位置:

<router-view></router-view>

注意:

  • 路由组件通常放在pages文件夹下,一般组件放在components文件夹下

  • 通过切换,‘隐藏’ 了的路由组件,默认是被销毁了的,需要的的时候再去挂载

  • 每个组件都有自己的 r o u t e 属性,里面存储这自己的路由信息 ( 各自 route属性,里面存储这自己的路由信息 (各自 route属性,里面存储这自己的路由信息(各自route不同)

  • 整个应用只有一个router,可以通过组件的 r o u t e r 属性获取到( router属性获取到( router属性获取到(router都相同)

6.5 多级(嵌套)路由

1)配置路由规则,使用children配置项,子级路由的path里不需要加斜杠 ‘/ ’

2)跳转 <router-link to = ’ /home/news ’ => 这里要写完整路径!!!

6.6 路由的query参数

1)传递参数

//跳转路由并携带query参数,to的字符串写法
<router-link :to="`/home/message/detail?id={m.id}&title={m.title}`"></router-link>

//to的对象写法
<router-link :to="{
	path:'/home/message/detail',
	query:{
		id:m.id,
		title:m.title
	}
}"

2)接收参数:$route.query.id

6.7 路由的params参数

1)配置路由,需要声明接收params参数,使用占位符

{
	name:'xiangqing',
	path:'detail/:id/:title',		//使用占位符声明接收params参数
	component:Detail
}

2)传递参数

<router-link :to="`/home/message/detail/{m.id}/{m.title}`">   //跳转路由并携带query参数,to的字符串写法

<router-link :to="{											  //to的对象写法
	name:'detail',				//注意!!!如果使用params的对象写法,必须靠name实现跳转,不能用path
	params:{
		id:m.id,
		title:m.title
	}
}"

接收:$route.params.id

6.8 路由的props参数

1)作用:让路由组件更方便地收到参数,不用再多次在标签体中使用$route.query.id

{
	name:'xiangqing',
	path:'detail',		
	component:Detail,
	
	props:{ id:1, title:'hello'}			//props的第一种写法:值为对象,所有key-value值都会以props形式传给组件
	props:true								//props的第二种写法:值为布尔值,为真则把该路由收到的所有params参数,以												  props的形式传给组件
	props($route){
		return {id:$route.query.id, title:$route.query.title}
	}										//props的第三种写法:值为函数,返回一个对象,同样以props传给组件
}
6.9 <router-link的replace属性

1)作用:控制路由跳转时操作浏览器历史记录的模式

2)浏览器的历史记录有2中写入方式:分别是push和replace,push是追加历史记录,replace是替换当前记录,路由跳转时候默认为push

3)如何开启replace模式:

<router link replace ......> </router-link>
6.10 编程式路由导航

$router里的api:push、replace、back、forward、go(n) => 路由走n步(n可以为负)

1)作用:不借助<router-link实现路由跳转,让路由跳转更加灵活

2)使用方式:

this.$router.push({
	name:'xiangqing',
	params:{
		id:×××,
		title:'×××'
	}
})
6.11缓存路由组件

因为路由切走后,原来的路由组件会被销毁,所以原本在路由组件里输入的数据会被清空

1)作用:让不展示的路由组件保持挂载,不被销毁

2)使用方式:

找到需要继续挂载的路由组件的根展示区,用 <keep-alive标签包裹起来,并加上include=‘组件名’属性,否则该展示区的所有组件都会缓存

<keep-alive include='News'>           //需要缓存多个组件时::include="['News','message']"
	<router-view></router-view>
</keep-alive>
6.12 两个新的生命周期钩子

路由组件所独有的两个钩子,用于捕获路由组件的激活状态

activated(){					//路由组件被激活时触发(即组件展示出来的时候)
	console.log('组件被激活了')
}
deactivated(){					//组件失活时触发(组件消失在我们面前的时候)
	console.log('组件失活了')
}
6.13 路由守卫
  • 作用:对路由进行权限控制
  • 分类:全局守卫、独享守卫、组件内守卫

1)全局前置路由守卫 —— 初始化的时候、每次路由切换之前被调用

router.beforeEach((to,from,next)={
	if(to.path == '/home/news' || to.path == '/home/message'){		//这里除了path还可以用name作为路由标识
		if(localStorage.getItem('school')){							 若需要判断的路径较多可以使用meta配置项
			next()												    if(to.meta.isAuth)→去往的组件是否需要权限
		}else{
			alert('无权限查看!!')
		}
	}else{
		next()
	}
})

2)全局后置路由守卫 ——初始化时、每次路由切换之后被调用

router.after( (to,from) => {					//修改网页的title
	document.title = to.meta.title				//前提先在各个组件的meta配置项里配置相应的title名
})

3)独享路由守卫

只对当前路由做出限制

beforeEnter((to,from,next)=>{})    			//没有afterEnter

4)组件内路由守卫

进入路由守卫:通过路由规则,进入该组件时被调用

beforeRouterEnter((to,from,next)=>{})

离开路由守卫:通过路由规则,离开该组件时被调用

beforeRouterLeave((to,from,next)=>{})
6.14 hash和history模式

对于一个url来说,hash值就是#及其后面的内容

浏览器路径中从#开始到后面的都是hash值,不会作为路径的一部分发送给服务器

整个路由器有两种工作模式:默认hash。在路由配置文件中添加配置项 mode:‘history’ 切换为history工作模式(路径比较干净)

区别

hash模式:地址不美观,兼容性好,若将地址通过第三方手机分享,若app校验严格,则地址会被标记为不合法

history模式:地址美观干净,兼容性较差,应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

项目上线:打包生成最纯粹的 .html、.css、.js文件。。终端:npm run build >>> (只打包src和public)打包完成后出现一个dist文件夹,打包好的文件必须放到服务器上进行部署(把html、css、js等静态资源文件放到后端新建static文件夹中,并在server.js中使用app.use(express.static(__dirname + ‘/static’ ))),此时就能通过访问该服务器整个项目了,即项目上线啦!

history模式下若访问服务器,切换了好几个路由之后,路径就会很长,此时一刷新就会出现报错(该路径下没有相关资源),一般后端解决。若是node服务器,可以通过connect-history-api-fallback的中间件解决

七.Vue UI组件库

移动端常用UI组件库:

Vant:https://youzan.github.io/vant

Cube UI :https://didi.github.io/cube-ui

Mint UI : https://mint-ui.github.io

NutUI

PC端常用UI组件库

Element UI : https://element.eleme.cn

IView UI : https://www.iviewui.com

Element UI按需引入:…

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pcmnqzc4-1676712361587)(C:\Users\17748\AppData\Roaming\Typora\typora-user-images\image-20230218151108501.png)]


**2)全局后置路由守卫** ——初始化时、每次路由切换之后被调用

router.after( (to,from) => { //修改网页的title
document.title = to.meta.title //前提先在各个组件的meta配置项里配置相应的title名
})




**3)独享路由守卫**

只对当前路由做出限制

beforeEnter((to,from,next)=>{}) //没有afterEnter




**4)组件内路由守卫**

进入路由守卫:通过路由规则,进入该组件时被调用

beforeRouterEnter((to,from,next)=>{})


离开路由守卫:通过路由规则,离开该组件时被调用

beforeRouterLeave((to,from,next)=>{})




#### **6.14 hash和history模式**

对于一个url来说,hash值就是#及其后面的内容

浏览器路径中从#开始到后面的都是hash值,不会作为路径的一部分发送给服务器

整个路由器有两种工作模式:默认hash。在路由配置文件中添加配置项 mode:'history'  切换为history工作模式(路径比较干净)

**区别**:

hash模式:地址不美观,兼容性好,若将地址通过第三方手机分享,若app校验严格,则地址会被标记为不合法

history模式:地址美观干净,兼容性较差,应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

**项目上线**:打包生成最纯粹的 .html、.css、.js文件。。终端:npm run build  >>> (只打包src和public)打包完成后出现一个dist文件夹,打包好的文件必须放到服务器上进行<u>部署</u>(把html、css、js等静态资源文件放到后端新建static文件夹中,并在server.js中使用app.use(express.static(__dirname + '/static' ))),此时就能通过访问该服务器整个项目了,即项目上线啦!

history模式下若访问服务器,切换了好几个路由之后,路径就会很长,此时一刷新就会出现报错(该路径下没有相关资源),一般后端解决。若是node服务器,可以通过connect-history-api-fallback的中间件解决



### 七.Vue UI组件库

**移动端常用UI组件库:**

Vant:https://youzan.github.io/vant

Cube UI :https://didi.github.io/cube-ui

Mint UI : https://mint-ui.github.io

NutUI

**PC端常用UI组件库**:

Element UI : https://element.eleme.cn

IView UI  : https://www.iviewui.com



Element UI按需引入:......

[外链图片转存中...(img-pcmnqzc4-1676712361587)]











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值