1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象。
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的vue语法。
3.root容器里的代码被称为“Vue模板”。
4.容器和实例的关系:容器和实例只能是一一对应的关系。开发中只会出现一个vue实例
a.多个容器对应一个实例,只有第一个容器会绑定。
b.多个实例对应一个容器,只有第一个实例会生效。
5.JS表达式和JS代码
a.JS表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,例如:a,a+b,demo(1),x==y?"A":"B".
b.js代码(语句):只控制走向不产生值
if(){},for(){}
6.{{xxx}}中xxx要写表达式,且xxx可以自动读取到data中的所有属性。
7.一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新。
Document
{{name}}
{{name}}
差值语法只管标签内容,例如:上方代码中的yyyy是标签体内容。
写法{{yyyy}},yyyy是js表达式,且可以读取到data中的所有属性。
yyyy
指令语法只管标签属性,例如:上方代码中的xxxx是标签属性。
功能:用于解析标签(包括:标签属性,绑定事件等等),xxxx也是是js表达式,且可以读取到data中的所有属性。
如果属性前面加了v-bind,那么属性后面的“xx”内容,就会被当做表达式来执行。
v-bind:可以简写为:
备注:vue中有很多指令,且形式都是:v-***,此处v-bind只是举例子使用。
vue有两种绑定方式:单向绑定和双向绑定
单向数据绑定
双向数据绑定
单向数据绑定
双向数据绑定
数据只能从data流向页面,无法从页面影响data
数据可以从data流向页面,页面也可以流向data
1.双向绑定一般都应用在表单类元素上如:input,select,redio等等,有value的元素,因为只有用户有操做的元素,才有可能改变vaule,才有双向绑定的意义。类似于
等标签,用户无法修改value,自然也没有双向绑定的意义。
2.v-model:value可以简写为v-model,因为v-model默认收集的就是value属性值。
v-bind:value简写为:value
v-model:value简写为:v-model:
(1)new Vue的时候配置el属性
(2)先创建Vue实例,接收以后通过v.$mount("#id")对el进行绑定
(1)对象式
(2)函数式
在组件中,data必须使用函数式否则会报错。
在Vue管理的函数中,一定不要用箭头函数,否则this就不再是Vue实例了,而是Window。
{{name}}
1.M:模型(Model)对应data中的数据
2.V:视图(View)对应模板
3.VM:视图模型(ViewMode)对应Vue实例对象
学校名称:{{name}}
学校地址:{{address}}
VM属性:{{$createElement}}
(1)data中的所有属性最后都会出现在vm上。
(2)vm上的所有属性以及vue圆形上的所有属性,在vue模板中都可以直接使用
通过一个对象代理,对另一个对象中属性的操作(读/写)
(1)Vue中的数据代理:通过vm对象来代理data中属性的操作(读/写)。
(2)Vue中数据代理的好处:更加方便的操作(读/写)data中的数据。
(3)基本原理:通过Object.defineProperty()把data对象中的所有属性都加到vm上,为每一个添加到vm身上的属性都制定一个getter/setter方法,在getter/setter方法内部去操作(读/写)data中的属性。
具体内容参考后面的数据劫持。
事件的基本使用:
(1)使用v-on:xxx 或者@xxx绑定事件,其中xxx是事件名
(2)事件的回调需要配置在methods对象中,最终会在vm桑。
(3)methods中配置的函数,不要使用箭头函数,否则this指向的不是vue而是window
(4)methods中配置的函数都是被vue所管理的函数,this指向vm或者组件实例对象
(5)@click="demo"和@click="demo($event)"效果是一样的,但是后者有传参。
欢迎来到{{address}}
Vue中的事件修饰符
(1)prevent:阻止默认事件(常用)。
(2)stop:组织事件冒泡(常用)。
(3)once:事件只能触发一次(常用)。
(4)capture:使用事件的捕获模式。
(5)self:只有event.target是当前操作的元素才会触发的时间。
(6)passive:时间的默认行为立即执行,无需等待时间回调执行完毕
(1)Vue中常用的按键别名:
回车=》enter
删除=》delete(捕获删除和退格键)
退出=》esc
空格=》space
换行=》tab(特殊,必须配合keyDown使用)
上=》up
下=》down
左=》left
右=》right
(2)Vue未提供别名的按键可以用原始的key值去绑定,但要注意转为kebab-case(短横线命名)
(3)系统修饰键(用法特殊):ctrl、alt、shift,meta(win)
(3.1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(3.2)配合keydown使用:正常触发事件。
(4)也可以用keyCode去指定按键(不推荐)。
(5)Vue.config.keyCodes.自定义键名=键码,可以定制按键别名。
1.7.2的修饰符可以连续使用:@click.passive.stop="demo"
1.7.3系统修饰键后面也可以连续使用:@keyUp.ctrl.y="demo"
当页面有两个输入框,且另一个位置需要计算两个框之内的内容时:
姓
名
姓名:{{firstName}}{{lastName}}
计算属性:
(1)定义:要用的属性不存在,要通过已有属性进行计算。
(2)原理:底层借助了Object.defineproperty方法提供的getter和setter方法。
(3)get函数什么时候执行:
(3.1)初次读取属性时会执行一次。
(3.2)当以来的属性,数据发生改变时,会再次调用。
(4)优势:与methods实现相比,内部有缓存机制(可服复用),效率更高,调试方便。
(5)备注:
(5.1)计算属性最终会出现在vm上,直接读取即可使用。
(5.2)如果计算属性要被修改,那么必须写set函数去响应修改,且
set中要引起计算式依赖的数据发生改变。
姓
名
姓名:{{fullName}}
当计算属性只考虑读取,不考虑编辑时计算属性可以简写:
姓
名
姓名:{{fullName}}
监视属性watch:
(1)当贝坚实的属性发生变化是,回电好书自动调用,执行相关代码。
(2)监视的属性必须存在才能监视
(3)监视属性的两种写法:
(3.1).new Vue是传入watch配置
(3.2)通过vm.$watch监视
今天天气很{{info}}
深度监视:
(1)VUe中的watch默认部件是对象内部值得改变。(一层)
(2)配置deep:true可以监测对象内部值改变。(多层)
备注:
(1)Vue自身可以检测对象内部指的改变,但是Vue提供的watch默认不可以。
(2)使用watch根据数据具体结构,决定是否采用深度监视。
今天天气很{{info}}
a的值为:{{numbers.a}}
b的值为:{{numbers.b}}
computed和watch之间的区别:
(1)computed能完成的功能,watch都可以完成。
(2)watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
(1)所有被Vue管理的函数最好写成普通函数,这样this指向的是vm或者组件实例对象。
(2)所有不被VUe所管理的函数(定时器回调函数,ajax回调函数等),做好使用箭头函数,这样this指向的才是vm
今天天气很{{info}}
(1)在应用界面中某个(些)元素的样式是变化的。
(2)class/style绑定就是专门实现动态样式效果的技术
(1):class='xxx'。
(2)表达式是字符串‘classA’。
(3)表达式是对象{classA:isA,classB:isB}。
(4)表达式是数组['classA','classB']。
{{name}}
{{name}}
{{name}}
(1):style="{{fontSize:xxx}}"其中xxx是动态值
(2):style="[a,b]"其中ab是样式的对象
{{name}}
{{name}}
(1)v-if 与 v-else/v-else-if
写法:
(1.1)v-if="表达式"。
(1.2)v-else-if="表达式"。
(1.3)v-else。
适用于:切换频率较低的场景
特点:不展示的dom元素直接移除。
注意:v-if可以和v-else-if,v-else一起使用。但是结构不能被“打断”。
(2)v-show
写法:v-show="表达式"
适用于:切换频率较高的场景
特点:不展示的dom元素未被移除,仅仅使用样式隐藏
(3)备注:
(3.1)使用v-if时,元素可能无法获取到,而v-show元素一定会获取到。
(3.2)template不能和v-show配合使用,v-if可以
{{name}}
{{name}}
(1)如果频繁切换用v-show。(页面不显示,但是元素还在)
(2)当条件不成立时,v-if所有子节点都不会解析。(页面不显示,元素也不加载)
v-for指令:
(1)用于展示列表数据。
(2)语法:v-for="(item,index) in xxx" :key="yyy"。
(3)可以遍历:数组,对象,字符串,指定次数。
人员列表
- {{person.name}}-{{person.age}}
汽车信息
- {{param}}-{{index}}
字符串信息
- {{char}}-{{index}}
循环指定次数
- {{char}}-{{index}}
面试题:react,vue中key有什么作用?(key的内部原理)
(1)虚拟dom中key的作用:
key是虚拟dom对象的标识,当状态中的数据发生变化时,vue会根据【新数据】生成【新虚拟dom】,随后vue对【新虚拟dom】与【旧虚拟dom】进行差异比较,比较规则如下:
(2)比较规则:
(2.1)旧虚拟dom中找到的新虚拟dom相同的key。
(2.1.1)若虚拟dom中内容没变,直接使用之前的真是dom。
(2.2.2)若虚拟dom中的内容变了,则生成新的真是dom,随后替换掉页面中
真实dom。
(2.2)旧虚拟dom中未找到与新虚拟dom中相同的key
(2.2.1)创建新的真是dom,然后渲染到页面。
(3)用index作为key可能会引发的问题:
(3.1)若对数据进行逆序添加,逆序删除等破坏顺序的操作
会产生没有必要的真是dom更新==》界面效果没问题,但是效率低
(3.2)如果结构中含有输入类dom:
会产生错误的dom更新==》页面有问题
(4)开发中如何选择key:
(4.1)最好使用每条数据的唯一标识作为key,比如id,手机号,身份证号等等。
(4.2)如果不存在对数据的逆序删除,逆序添加等破坏顺序的操作,仅用作渲染,
使用index作为key没有任何问题。
人员列表
-
{{person.name}}-{{person.age}}
可以使用计算实行或者监视属性实现,优先使用计算属性。
人员列表
-
{{person.name}}-{{person.age}}-{{person.sex}}
增加排序属性,计算属性fliterPersonList返回搜索之前,进行一次数组排序,然后返回排序后的数组。
人员列表
-
{{person.name}}-{{person.age}}-{{person.sex}}
如果以对象的形式更新personList[0],Vue无法检测到数据的改变,vue开发工具也不会体现,但是如果先点击按钮,再打开Vue开发工具,则可以看到修改后的数据,但是页面并未修改,故:如果按第二种形式修改data里的属性的内容,Vue无法检测到属性的修改。
人员列表
-
{{person.name}}-{{person.age}}-{{person.sex}}
Vue中监视数据的原理:
(1)vue会监视data中所有层次的数据。
(2)如何监测对象中的数据:
通过setter实现监测,且要在new Vue时就要传入检测的数据。
(2.1)对象中后追加的属性Vue默认不做响应式处理。
(2.2)如需给后添加的属性做响应式处理请使用如下API:
(2.2.1)Vue.set(targer,propertyName/index,value)
(2.2.2)vm.$set(targer,propertyName/index,value)
(3)如何检测数组中的数据:
通过包括数组更新元素的方法实现,本质就做了两件事:
(3.1)调用原生对应的方法,操作数组中的元素。
(3.2)重新解析模板,进行页面更新。
(4)在Vue中修改数组中的某个元素一定要调用如下方法:
(4.1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(4.2)Vue.set()、vm.$set(),不能给vm或vm跟数据对象(data)添加属性。
学生信息:
姓名:{{student.name}}
年龄:{{student.age}}
性别:{{student.sex}}
爱好
- {{h}}
朋友
- {{f.name}}--{{f.age}}
收集表单数据:
(1) v-model收集的是value的值,用户输入的value。
(2) v-model收集的是value的值,选中标签所配置的的value。
(3)
(3.1)若没有配置input的value属性,那么v-model收集的是checked勾选状态(true/false)
(3.2)若配置input的value属性:
(3.2.1)如果v-model绑定非数组,那么v-model收集的是checked勾选状态(true/false)
(3.2.2)如果v-model绑定数组,那么v-model收集的是checked勾选选择框对应的value
v-model的三个修饰符:
(1)lazy:失去焦点后收集数据
(2)number:输入字符串转为有效数字,一般配合type="number"使用。
(3)trim:过滤收尾所有的空格,字符串中间空格无法过滤。
当我们获得了一个时间戳,我们并不需要这个获取到的时间戳,需要这个时间戳代表的时间,此时我们可以用过滤器将其过滤,然后获取对应的时间后使用。
过滤器:
定义:对要显示的数据进行特定格式化后在显示(适用于一些简单的逻辑处理)。
语法:
(1)注册过滤器:Vue.filter(name,callback)或者new Vue(filters:{})。
(2)使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性="xxx | 过滤器名"。
备注:
(1)过滤器也可以接受额外的参数,多个过滤器也可以串联
(2)并没有改变原本的数据,只是产生了新的数据。
显示格式化后的时间
{{fmtTime}}
{{getFMTime()}}
{{time | timeFormater }}
{{time | timeFormater('YYYY_MM_DD') |mySlice}}
显示格式化后的时间
{{time }}
{{time | mySlice}}
v-bind:单项绑定解析表达式,可简写为:xxx
v-model:双向数据绑定
v-for:遍历数组,对象,字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存在)
v-else:条件渲染(动态控制节点是否存在)
v-show:条件渲染(动态控制节点是否显示)
(1)作用:向所在的节点中渲染文本内容
(2)与差值语法不同,v-text会替换掉节点中所有的内容,{{xxx}}则不会替换
你好,{{name}}
你好,
你好,
v-html指令:
(1)作用:向指定节点渲染包含html结构的内容。
(2)与差值语法的区别:
(2.1)v-html 会替换掉节点中的所有内容,{{xx}}则不会。
(2.2)v-html 可以识别html结构
(3)严重注意:v-html有安全性问题!!!
(3.1)在网站上动态渲染任意HTML是非常文献的容易导致XSS攻击。
(3.2)一定要在可信的内容上使用v-html,永远不要用在提交的内容上。
你好,{{name}}
v-cloak指令(没有值)
(1)本质是一个特殊的属性,Vue创建完毕后,会删掉c-cloak属性。
(2)使用css配合v-cloak可以解决网速慢导致的页面站输出{{xxx}}的问题。
你好,{{name}}
v-once指令:
(1)v-once所在的节点在初次动态渲染之后就视为静态内容了。
(2)以后数据的改变不会引起v-once所在结构更新,可以用于优化性能。
初始化n={{n}}
当前n={{n}}
v-pre指令:
(1)跳过其所在节点的编译过程。
(2)可利用它条过没有使用指令语法,没有使用差值语法的节点,可以加快编译。
静态内容
初始化n={{n}}
当前n={{n}}
参数:
(1)指令所绑定的元素
(2)绑定指令的具体实现
当前的n=
放大10倍的n=
放大10倍的n=
(1)定义语法:
(1.1)局部指令:
//创建vue实例 const vm = new Vue({ directives:{ //函数式 big(element,binding){ }, //对象式 fbind:{ //1.指令与元素成功绑定时(页面刚加载) bind(element,binding){ element.value=binding.value }, //2.指令所在元素插入页面时 inserted(element,binding){ element.focus() }, //3.指令所在的模板被重新解析时 update(element,binding){ element.value=binding.value element.focus() } } } } })
(1.2)全局指令:
(1.2.1)Vue.directive(指令名:配置对象)
(1.2.2)Vue.directive(指令名:回调函数)
(2)配置对象中常用的三个回调:
(2.1)bind:指令与元素成功绑定时调用。
(2.2)inserted:指令所在元素被插入到页面中时调用。
(2.3)update:指令所在模板被更新时调用。
(3)备注:
(3.1)定义指令时不加v-,但是使用时加v-
(3.2)指令如果是多个单词要用kebab-case方式命名,不要用canelCase命名
生命周期:
(1)又名:生命周期回调函数,生命周期函数,生命周期钩子。
(2)是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
(3)生命周期函数的名字不可更改,但是函数的具体内容是根据具体需求编写的。
(4)生命周期函数中的this指向的是vm或组件实例的对象。
vm的生命周期:
创建之前==》调用brforeCreate()函数
创建完毕==》调用created()函数
挂载之前==》调用beforeMount()函数
挂在完毕==》调用mounted()函数==================》【重要的钩子】
更新之前==》调用beforeUpdate()函数
更新完毕==》调用updateed()函数
销毁之前==》调用beforeDestory()函数==============》【重要的钩子】
销毁完毕==》调用destoryed()函数
欢迎来打山东省
常用的生命周期狗子:
(1)mounted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息等初始化操作。
(2)beforeDestroy:清除定时器,解绑自定义时间,取消消息订阅等收尾工作。
关于vue销毁:
(1)销毁后的Vue借助开发者工具看不到任何信息。
(2)销毁后的自定义事件会失效,但是原生dom时间依然有效。
(3)一般不会beforeDestroy中操作数据,因为操作了也不会再触发更新,而且立刻销毁。
组件的定义:实现应用中局部功能代码和资源的集合
问题:
(1)依赖关系混乱。
(3)代码复用率不高。
(1)概念:向外提供特定功能的js程序,一般是一个js文件。
(2)使用原因:js文件很多很复杂。
(3)作用:复用js,简化js的编写,提高js运行效率。
(1)概念:用来实现局部(特定)功能效果的代码集合(html/css/js/image...)。
(2)使用原因:一个界面功能很复杂。
(3)作用:复用代码,简化项目编码,提高运行效率。
应用中的js都已模块来编写,这个应用就是一个模块化应用。
当应用中的功能都是多组件的方式来编写,那么这个应用就是组件化应用
定义:一个文件中有多个组件。
(1)Vue组件使用三大步骤:
(1.1)定义组件(创建组件)
(1.2)注册组件(全局注册/局部注册)
(1.3)使用组件(写组件标签)
(2)如何定义一个组件
(2.1)使用VUe.extend(options)创建,其中options和new Vue(options)传入的options
几乎一样,但也有点区别:
(2.1.1)不写el:组中所有组件都由vm管理,有vm指定el服务的容器。
(2.1.2)data必须写函数式:避免组件复用时,数据引用造成的数据修改错误。
(2.2)备注:template可以配置组件结构。
(3)如何注册组件:
(3.1)局部注册:new Vue()时,传入入参conponents:{}参数。
(3.2)全局注册:Vue.component('组件名',组件)。
(4)使用组件:使用引入组件时指定的组件名===><组件名><组件名/>
root1star
root1end
root2
(1)关于组件名:
(1.1)一个单词组成:
(1.1.1)首字母小写(school)。
(1.1.2)首字母小写(School)。
(1.2)多个单词组成:
(1.2.1)kebab-case命名方式:my-school。
(1.2.2)CsmelCase命名方式:MySchool(需要脚手架支持)。
(1.3)备注:
(1.3.1)组件名尽可能回避HTML已存在的元素名,例如:h1,H1等等。
(1.3.2)可以使用name配置指定组件在开发者工具中的名字(不影响调用)。
(2)关于组建标签:
(2.1)单标签
。 (2.2)双标签
。 (2.3)不适用脚手架单标签
多个连续使用会导致后面的无法渲染。 (3)简写方式:
const school={},当变量等于一个对象不调用Vue.compent()方法时,在vm引入 组件时会做判断,如果没有调用,或自动去调用Vue.compent()。
(3.1)简写:
const schoolConst = { template: `
`, data() { return { school: { name: "schoolName", address: "schoolAddress" } } }, methods: { alertSchoolName() { alert(this.school.name) } }, }学校名称:{{school.name}}
学校地址:{{school.address}}
(3.2)Vue自检:
function extend(to, _from) { for (var key in _from) { to[key] = _from[key]; } return to; }
root1star
root1end
关于 VueConponent:
(1)shcool组件本质是一个名为VueConponent的构造函数,且不是程序员地赢的,是 Vue.extent生成的。
(2)我们只需要写
或者 ,Vue解析时会帮我们创建school组件的 实例对象。 (3)特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent。
(4)关于this指向:
(4.1)组件配置中:data函数,methods中的函数,watch中的函数,computed中的函
数,他们的this指向的是【VueComponent实例对象】
(4.2)new Vue配置中:data函数,methods中的函数,watch中的函数,computed中
的函数,他们的this指向的是【Vue实例对象】
(5)一般情况下:
(5.1)VueComponent实例对象简称==》vc(组件实例对象)
(5.2)Vue对象简称==》vm
function demo(){
this.a=1
this.b=2
}
const d=new demo();
console.log(demo.prototype);//显示原型属性
console.log(d.__proto__);//隐式原型属性
console.log(d.__proto__===demo.prototype,"demo.prototype===d.__proto__");//true 他们都是原型对象
//对原型对象添加一个属性99
demo.prototype.x=99
(1)一个重要的内置关系:Vue.prototype===schoolConst.prototype.__proto__(true)。
(2)为什么要有这个关系:让组件实例对象vc可以访问到Vue原型上的属性,方法。
//1.创建组件school
const schoolConst = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `
学校名称:{{school.name}}
学校地址:{{school.address}}
`,
//组件变量,必须用函数式
data() {
return {
school: {
name: "schoolName",
address: "schoolAddress"
}
}
}
})
//创建vm
new Vue({
el: '#root',
//2.注册组件(局部注册)
template:" ",
components: {
//key为注册后的真实key,value为创建的const组件变量名,若两个一样可以简写为一个单词
school: schoolConst,
},
})
console.log("Vue.prototype===schoolConst.prototype.__proto__:",Vue.prototype===schoolConst.prototype.__proto__)//true
定义:一个文件只有一个组件。
学校名称:{{ school.name }}
学校地址:{{ school.address }}
学生姓名:{{ student.name }}
学生年龄:{{ student.age }}
import Vue from 'vue'
import App from './App'
new Vue({
el:"#root",
template:` `,
components:{App}
})
Document
(1)Vue脚手架时Vue官方提供的标准化开发工具(开发平台)。
(2)文档 Vue CLI
(1)配置淘宝镜像:npm config set registry https://registry.npm.taobao.org
(2)全局安装@vue/cli。(仅第一次执行)(npm install -g @vue/cli)
(3)切换到项目路径,使用指令创建项目:vue create xxx
(4)启动项目:npm run serve
(1).gitignore:配置不接受git管理的文件。
(2)babel.config.js:es6转es5要用到的babel配置文件(不需要修改)。
(3)package.json:
"scripts": { "serve": "vue-cli-service serve",//运行命令 "build": "vue-cli-service build",//代码完成后构建命令,将vue文件转为js,css,html文件 "lint": "vue-cli-service lint"//代码检查 },
(4)package-lock.json:包管理器版本管理。
(5)src:
(5.1)main.js:项目入口文件
/* 项目入口文件 */ //引入vue import Vue from 'vue' //引入App组件,App是所有组件的父组件 import App from './App.vue' //关闭vue生产提示 Vue.config.productionTip = false //创建vue实例对象 new Vue({ //将app组件放入容器中 render: h => h(App), }).$mount('#app')//等于el:"#app"
(5.2)App.vue
(5.3)index.html
<%= htmlWebpackPlugin.options.title %>
备注:详细参考3.1.5
启动失败原因为:语法检查不通过。
在项目的根目录找到(没有就自行创建)
vue.config.js
文件,关闭语法检查即可const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, lintOnSave:false /*关闭语法检查*/ })
(1)vue.js与vue.runtime.xx.js的区别:
(1.1)vue.js是完整版的VUE,包含核心功能+模板解析器。
(1.2)vue.runtime.xx.js是运行版VUE,只包含核心功能,没有模板解析器。
(2)因为vue.runtime.xx.js没有模板解析器,所以不可以用template配置项,需要使用
render函数,将接受到的createElement函数去指定内容。
vue隐藏了webpack相关的配置,需要查看可以执行:vue inspect > output.js
可以看到但是无法修改,具体修改方式请参考:
配置参考 | Vue CLI
(1)被用来给元素或子组件注册引用信息(代替id)。
(2)应用在html不爱券商,获取真是dom元素,应用在组件标签上是组件实例对象(vc)。
(3)使用方式:
(3.1)打标识:
Ref属性测试
/。 (3.2)获取:this.$refs.school/this.$refs.title。
(3.3)获取到子组件之后也可以修改其内部属性。
Ref属性测试
功能:让组件接收外部传入数据。
(1)传递数据
(2)接受数据:
(2.1)props:['name']
(2.2)限制类型
props:{ id:String, name:String, size:String },
(2.3)限制类型,限制是否必填,设置默认值
props: { id: { type: String, //类型 required: true, //是否必填 }, name: String, size: { type: String, //类型 default: "型号(默认)", //是否必填 }, }
(3)备注:props事制度的,Vue底层会检测你对props的修改,如果进行了修改会发出警
告,如果业务需要,那么应该将props的内容复制到data中一份然后修改data中的据。
Props属性测试
物料信息
Msg:{{ msg }}
物料ID:{{ id }}
物料名称:{{ name }}
物料尺寸:{{ mySize }}
功能:可以把多个组件共用的配置提取成一个混入对象。
使用方式:
(1)定义混合
export const mixin = { methods: { showName() { alert(this.name) } }, mounted() { //alert("你好啊"+this.name) }, data(){ return { size:"mixinSIze" } } }
(2)使用混入
(2.1)全局混入:import {hunhe} from './mixin'; Vue.mixin(hunhe)。
(2.2)局部混入:import {hunhe} from './mixin';mixins:['hunhe']
(3)注意:data中的数据以源文件为主,methods中的数据都会执行,而且优先执行混入的
物料信息
Msg:{{ msg }}
仓库ID:{{ id }}
仓库Size:{{ size }}
仓库名称:{{ name }}
物料信息
Msg:{{ msg }}
物料ID:{{ id }}
物料名称:{{ name }}
物料尺寸:{{ mySize }}
//引入Vue
import Vue from 'vue'
//引入app
import App from './App'
import {hunhe} from './mixin'
// Vue.mixin(hunhe)
//关闭提示
Vue.config.productionTip=false
new Vue({
el:"#app",
render:h=>h(App)
})
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue(vm的原型),第二个以后的
参数是插件使用者传递的数据。
定义插件:
export default { install(Vue,a,b,c){ console.log('install',Vue) //拿到Vue的构造方法,可以进行操作,Vue原型上的方法和属性vm和vc都能使用 //全局过滤器 //Vue.filter() //全局指令 // Vue.directive() //全局混入 // Vue.mixins() //给Vue原型增加方法,属性等等 // Vue.prototype.$myMethod=function(){} // Vue.prototype.prototype="" } }
使用插件:Vue.use()。
作用:让样式在局部生效防止冲突。
写法:
(1)组件化编码流程
(1.1)拆分静态组件:组件按照功能点拆分,命名不要与html元素冲突。
(1.2)实现动态组件:考虑好数据存放位置,数据是一个组件在用,还是一些都在用。
(1.2.1)一个组件再用:放在自身即可。
(1.2.2)一些组件在用,放在他们共同的父组件上。(状态提升)
(1.3)实现交互:绑定事件。
(2)props适用于:
(2.1)父组件==》子组件 通信。
(2.2)子组件==》父组件 通信(要求父组件先给子组件一个函数)。
(3)v-model使用时要切记,v-model绑定的不能是props传过来的值,因为props内传入的
值是不可以修改的。
(4)props传过来的若是对象型的值,修改对象属性时vue不会报错,但不推荐这样做。
webStorage:
(1)储存内容大小一般支持5m左右(浏览器不同也可能不一样)
(2)浏览器通过Window.sessionStorage和window.localStorage属性来实现本地存储机制。
(3)相关API:
(3.1)xxxxxxStorage.setItem(key,vaule);
该方法会把一个兼职顿添加到存储中,如果已有该键,则会更新内容。
(3.2)xxxxxxStorage.getItem(key);
该方法接收一个key作为参数,获取对应的value。
(3.3)xxxxxxStorage.removeteam(key);
该方法接收一个key作为参数,删除对应的key和value。
(3.4)xxxxxxStorage.clear();
清除存储中所有数据。
(4)备注:
(4.1)sessionStorage存储的内容会随着浏览器窗口关闭而消失。
(4.2)localStorage存储的内容需要手动调用相应方法才会消失。
(4.3)xxxxxxStorage.getItem(key);获取不到会返回一个null
(4.4)保存对象时可用:JSON.stringify(person),
解析时用:JSON.parse(localStorage.getItem("personToJson"))
JSON.stringify(null)依然是null
saveData() {
let person = { name: "张三", age: 18 }
localStorage.setItem("msg", "hello!!");
localStorage.setItem("numberMsg", 666);
localStorage.setItem("person",person);
localStorage.setItem("personToJson", JSON.stringify(person));
},
readData() {
let person = { name: "张三", age: 18 }
console.log( localStorage.getItem("msg", "hello!!"));
console.log(localStorage.getItem("numberMsg", 666));
// console.log(localStorage.getItem("person",person));
console.log(JSON.parse(localStorage.getItem("personToJson")));
},
deleteData() {
localStorage.removeItem("msg")
localStorage.removeItem("numberMsg")
localStorage.removeItem("personToJson")
},
clearData(){
localStorage.clear();
}
(1)一种组件捡的通信方式,适用于:子组件===》父组件
(2)适用场景:A是父组件,B是子组件,B想给A传值,那么就要在A中给B绑定自定义事
件,事件回调在A中。
(3)绑定自定义事件:
(3.1)再父组件中使用
(3.2)再父组件中使用
(3.3)若想让自定义事件只触发一次,可以用once修饰符或者$once方法。
(4)触发自定义事件: this.$emit(name,value)。
(5)解绑滴定仪事件: this.$off(name)。
(6)组件上也可以绑定原生DOM事件,要用native修饰符。
(7)注意,通过this.$refs.student.$on(name,回调),板顶自定义事件时,回调要么要么在
methods中,要么用箭头函数,否则this会指向调用函数的实体。
(1)全局事件总线(GlobalEventBus)是一种组件之间通信的方式,适用于任意组件之间
的通信。
(2)安装全局事件总线:
beforeCreate(){ Vue.prototype.$bus=this }
(3)使用全局事件总线:
(3.1)接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,回调由A
组件自身实现。
mounted(){ this.$bus.$on('hello',(data)=>{ console.log('School组件收到了',data) }) }
(3.2)提供数据
this.$bus.$emit('hello',this.student.name)
(4)使用完毕后,组件销毁之前最好用$off解绑当前组件所使用的事件
beforeDestroy(){ this.$bus.$off('hello') },
(1)消息订阅预发布(pubsub)是一种组件间的通信方式,适用于任意组件通信。
(2)使用步骤:
(2.1)安装pubsub:npm i pubsub-js。
(2.2)引入import pubsub from 'pubsub-js'
(2.3)接收数据:A组件接收数据则在A组件内订阅消息,回调函数由A实现::
this.pubId=pubsub.subscribe('getStudentName',(msgName,data)=>{ })
第一个参数为消息的名称,第二个参数为传入的参数。
(2.4)提供数据:pubsub.publish('getStudentName',this.student.name)
(2.5)容器销毁之前,取消消息订阅: pubsub.unsubscribe(this.pubId)
(1)语法:this.$refs.inputTitle.focus()。
(2)作用:在下一次dom更新后在执行指定的回调函数。
(3)适用:数据改变后,基于更新后的dom执行某些操作时,在nextTick中指定回调函数。
(1)作用:在插入,更新,或者移除dom元素时,在合适的时候给元素添加样式类名。
(2):
(3)写法:
(3.1)准备好样式
元素进入:
.1)v-enter:进入的起点。
.2)v-enter-active:进入的过程中。
.3)v-enter-to:进入的重点。
元素移除入:
.1)v-leave:离开的起点。
.2)v-leave-active:离开的过程中。
.3)v-leave-to:离开的重点。
(3.2)使用
包裹要过度的元素,并配置name属性。 (3.3)备注:若有多个元素使用transition,要用
,且每个元素都要 指定key。
transition
显示/隐藏动画测试
transition-group
显示/隐藏动画测试1
显示/隐藏动画测试2
transition 引入css
在vue.config.js中添加配置
devServer: { proxy: 'http://localhost:8090' }
优点:配置简单,请求资源是直接发给前端(8080)即可。
缺点:不能配置多个单例,不能灵活的控制请求是否走代理。
工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么请求会自动转发,但是若前端有那么就不会转发。
在vue.config.js中添加配置
devServer: {
proxy: {
//请求前缀为erp的走代理
'/test/erp': {
//转发地址
target: 'http://localhost:8090',
pathRewrite:{'^/test':''},//重写地址
ws: true,//用于支持webstocket,不写默认true
changeOrigin: true//请求头中的host是否为虚拟地址,不写默认true
},
'/demo/erp': {
//转发地址
target: 'http://localhost:8090',
pathRewrite:{'^/demo':''},//重写地址
ws: true,//用于支持webstocket,不写默认true
changeOrigin: true//请求头中的host是否为虚拟地址,不写默认true
},
}
}
changeOrigin:
true:服务器收到的host为虚拟的,与服务器地址相同。
false:服务器收到的host为真实的,与请求地址相同。
默认为true(react默认为false)。
优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
缺点:配置略微繁琐,请求资源必须加前缀。(可以配置路径重写)。
axio,vue-resource
(1)作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信额方式,适用于父组件===》子组件。
(2)分类:默认插槽,具名插槽,作用域插槽。
(3)使用方式:
(3.1)默认插槽:父组件:
HTML结构子组件:
插槽默认内容.... (3.2)具名插槽
父组件:
HTML结构1HTML结构2子组件:
插槽默认内容.... 插槽默认内容.... (3.3)作用域插槽
(3.3.1)作用:数组在组件自身,但是数据生成的结构由组件的使用者决定。
(父组件决定结构,子组件提供数据)
父组件:
- {{g}}
{{g}}
子组件:
插槽默认内容....
(1)概念:专门在Vue中实现集中式状态(也叫:数据、状态数据,之后统称:状态)管理的VUe插件,对vue应用中多个组件共享状态进行集中式的管理(读/写),也是一种组件通信的方式,适用于任何组件。
(2)地址:GitHub - vuejs/vuex: ️ Centralized State Management for Vue.js.
当某个属性,所有组件都需要使用:
(1)当多个组件依赖同一个状态。
(2)来自不同组件的行为要变更同一个状态。
(1)创建 文件夹:src/store/index.js
//该文件用于vuex中最为核心的store //引入Vue import Vue from 'vue' //引入vuex import Vuex from 'vuex' //准备actions 用于响应组件里的动作 const actions = { } //准备mutations 用于真正操作state中的数据 const mutations = { } //准备state 用于存储数据 const state = { } Vue.use(Vuex) //创建并暴露store export default new Vuex.Store({ actions, mutations, state, })
(2)在main.js中创建vm时传入store:
import store from './store' const vm=new Vue({ el:"#app", render:h=>h(App), store, beforeCreate(){ Vue.prototype.$bus=this } })
(3)注意:必须在index.js中引入Vue并使用插件vuex,否则new Vuex.Store()会报错。
(1)初始化数据(state),配置actions,配置mutations,操作文件store.js。
//该文件用于vuex中最为核心的store //引入Vue import Vue from 'vue' //引入vuex import Vuex from 'vuex' //准备actions 用于响应组件里的动作 const actions = { /* add(context,value){ // console.log("store.actions.add,context=",context,"value=",value) context.commit("ADD",value) }, sub(context,value){ context.commit("SUB",value) }, */isOdd(context, value) { console.log("store.actions.isOdd",value) context.dispatch("demo1", value) }, demo1(context, value) { console.log("store.actions.demo1",value) context.dispatch("demo2", value) }, demo2(context, value) { console.log("store.actions.demo2") if (context.state.sum % 2 != 0) { console.log("store.actions.isOdd") context.commit("ADD", value) } }, sleepAdd(context, value) { console.log("store.actions.sleepAdd") setTimeout(() => { context.commit("ADD", value) }, 1000); } } //准备mutations 用于真正操作state中的数据 const mutations = { ADD(state, value) { // console.log("store.mutations.ADD,state=",state,"value=",value) console.log("store.mutations.ADD") state.sum += value }, SUB(state, value) { console.log("store.mutations.state") // console.log("store.mutations.ADD,state=",state,"value=",value) state.sum -= value } } //准备state 用于存储数据 const state = { sum: 0, } Vue.use(Vuex) //创建并暴露store export default new Vuex.Store({ actions, mutations, state, })
(2)组件读取vuex中的数据:$taore.state.sum
(3)修改vuex中的数据:$store.dispatch('action中的方法名',参数)或者$store.commit('mutations中的方法名',参数)。
当前求和为:{{ $store.state.sum }}
备注:若没有网络请求或者其他业务逻辑,组件们可以越过actions直接访问mutations中的方法,既直接写commit()。
(1)概念:当state中的数据需要加工后在使用时,可以使用getters参数。
(2)配置getters属性:
//将state中的数据进行加工 const getters={ bigSum(state){ return state.sum*10 } } //创建并暴露store export default new Vuex.Store({ actions, mutations, state, getters })
(3)组件中读取数据:$store.getters.bigSum。
用于帮助我们映射state中的数据为计算属性:
computed:{ //借助mapState生成计算属性,从state中读取数据(对象写法) // ...mapState({school:'school',subject:'subject',sum:'sum'}) // 数组写法 ...mapState(['school','subject','sum']), }
用于帮助我们映射getters中的数据为计算属性
computed:{ //借助mapState生成计算属性,从state中读取数据(对象写法) // ...mapGetters({bigSum:'bigSum'}) // 数组写法 ...mapGetters(['bigSum']), }
用于帮助我们生成与actions对话的方法,既:包含$store.dispatch(xxx)的函数。
//对象式 // ...mapActions({sleepAdd:'sleepAdd',isOdd:'isOdd'}) //数组式 ...mapActions(['sleepAdd','isOdd'])
用于帮助我们生成与mutations对话的方法,既:包含$store.commit(xxx)的函数。
//对象式 // ...mapMutations({add:'ADD',sub:'SUB'}), //数组式 ...mapMutations(['ADD','SUB']),
mapActions与mapMutations使用时,若需要参数,应在模板绑定事件时传递参数,否则参数是事件对象。
(1)目的:让代码更好维护,多种数据分类更加明确。
(2)修改store.js,拆分为两个:person.js,count.js
person.js
//人员功能相关的 export default { namespaced:true, actions: { addPerson(context, value) { console.log("store.actions.addPerson") if (value != "") { context.commit("ADDPERSON", value) } else { alert("请输入姓名") } } }, mutations: { ADDPERSON(state, value) { state.personList.push(value) } }, state: { personList: ['张三'] }, getters: {} }
conut.js
//求和功能相关的 export default { namespaced:true, actions: { isOdd(context, value) { console.log("store.actions.isOdd", value) if (context.state.sum % 2 != 0) { console.log("store.actions.isOdd") context.commit("ADD", value) } }, sleepAdd(context, value) { console.log("store.actions.sleepAdd") setTimeout(() => { context.commit("ADD", value) }, 1000); }, }, mutations: { ADD(state, value) { // console.log("store.mutations.ADD,state=",state,"value=",value) console.log("store.mutations.ADD") state.sum += value }, SUB(state, value) { console.log("store.mutations.state") // console.log("store.mutations.ADD,state=",state,"value=",value) state.sum -= value }, }, state: { sum: 0, }, getters: { bigSum(state) { return state.sum * 10 } } }
index.js
//该文件用于vuex中最为核心的store //引入Vue import Vue from 'vue' //引入vuex import Vuex from 'vuex' import sumOptions from './count' import personOptions from './person' Vue.use(Vuex) //创建并暴露store export default new Vuex.Store({ modules:{ coundAbout:sumOptions, personAbout:personOptions, } })
(3)namespaced:true开启命名空间。
(4)开启命名空间后组件,读取state中的数据:
//方式1 直接读取 this.$store.state.personAbout.personList //方式2 借助mapState读取 ...mapState('countAbout',[sum])
(4)开启命名空间后,组件读取getters中的数据:
//方式1 直接读取 this.$store.getters['personAbout/firstName'] //方式2 借助mapGetters读取 ...mapState('countAbout',['bigSum'])
(5)开启命名空间后,组件调用dispatch:
//方式1 直接调用dispatch this.$store.dispatch('personAbout/addPerson',{name:'王'}) //方式2 借助mapActions读取 ...mapActions('countAbout',['isOdd','sleepAdd'])
(6)开启命名空间后,组件调用commit:
//方式1 直接调用commit this.$store.commit('personAbout/ADD_PERSON',{name:'王'}) //方式2 借助mapMutations读取 ...mapMutations('countAbout',['ADD','SUB'])
vue中的一个插件库,专门用来实现SPA应用。
(1)单页Web应用(single page web application 既:SPA)。
(2)整个应用只有一个完整的页面。
(3)点击页面中的导航链接,不会刷新页面,只会做局部更新。
(4)通过ajax获取数据。
(1)路由的概念:
(1.1)一个路由就是一组映射关系(key-value)。
(1.2)key为路径,value是function或者component。
(2)路由分类:
(2.1)后端路由:
(2.1.1)概念:value是function,用于处理客户端提交的请求。
(2.1.2)工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来
处理请求,返回响应数据。
(2.2)前端路由:
(2.2.1)概念:value是一个component,用于展示页面内容。
(2.1.2)工作过程:当浏览器路径发生改变时,就会展示对应的组件。
(1)安装vue-router:命令:npm i vue-router(如果是vue2则使用npm i vue-router@3)。
(2)应用插件:Vue.use(VueRouter)。
(3)编写router配置项:创建router/index.js。
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Class from "../compnents/Class.vue"; import Subject from "../compnents/Subject.vue"; //创建并暴露一个路由 export default new VueRouter({ routes:[ { path:'/class', component:Class },{ path:'/subject', component:Subject } ] })
(4)实现切换:active-class表示点击后触发的样式,to表示跳转的路由连接。
(5)指定点击导航后展示的内容位置:
(1)通过路由配置实现展示的组件(既通过
实现展示的组件)称之为路由组件,通过编写标签实现展示的,称之为一般组件,一般组件通常存放在components文件夹,路由组件一般存放在pages文件夹。 (2)通过切换实现展示后,不展示的路由组件默认是被销毁的,当切换路由展示路由组件时,会再去重新挂载。
(3)每个组件都有自己的$route属性,里面存储当前组件的路由信息(每个组件$route属性不相同)。
(4)整个组件只有一个router,可以通过$router属性获取到(每个组件$router属性相同)。
(1)配置路由规则时使用children配置项:
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Class from "../pages/Classes"; import Subject from "../pages/Subject"; import OneClass from '../pages/OneClass' import TwoClass from '../pages/TwoClass' //创建并暴露一个路由 export default new VueRouter({ routes: [ { path: '/class', component: Class, children: [ { path: 'oneClass', component: OneClass, }, { path: 'twoClass', component: TwoClass, }, ] }, { path: '/subject', component: Subject } ] })
(2)使用时要写完整路径:
一班 二班 (3)注意:二级路由配置时不要写"/",但是访问时路径要写全路径(加上父路径)。
(1)传递参数:
(1.1)直接拼接:
{{item.name}} (1.2)对象写法:
{{item.name}} (2)接收参数:
名称:{{$route.query.name}}
内容:{{$route.query.content}}
(1)作用:简化路由的跳转。
(2)使用:
(2.1)给路由命名:
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Classes from "../pages/Classes"; import Subject from "../pages/Subject"; import Class from '../pages/Class' //创建并暴露一个路由 export default new VueRouter({ routes: [ { name:'班级', path: '/classes', component: Classes, children: [ { name:'班内信息', path: 'class', component: Class, }, { path: 'subject', component: Subject } ] } ] })
(2.2)使用:
{{item.name}} {{item.name}} 若是路径过于繁琐,可以不使用路径,转而使用name属性。
(1)配置路由,声明接收params的参数(path中使用占位符):
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Classes from "../pages/Classes"; import Subject from "../pages/Subject"; import Class from '../pages/Class' //创建并暴露一个路由 export default new VueRouter({ routes: [ { name:'班级', path: '/classes', component: Classes, children: [ { name:'班内信息', path: 'class/:name/:content', component: Class, }, { path: 'subject', component: Subject } ] } ] })
(2)传递参数:
{{item.name}} {{ item.name }} 注意:如果用对象写法一定要用name属性跳转,而不是path属性。
(3)接收参数:
名称:{{$route.params.name}}
内容:{{$route.params.content}}
(1)作用:让路由组件更加方便的接受到参数:
{ name: '班内信息', path: 'class/:name/:content', component: Class, // 第一种写法 值为对象 局限性比较大一般不用 /*props:{ name:"name", content:"content" } */ // 第二种写法 值为Boolean 如果值为true,则将所有收到的param参数以props的形式传递给该组件,query接收不到 // props:true // 第三种写法 值为函数 props($route){ return{ name:$route.query.name, content:$route.query.content } } }
(1)作用:控制路由跳转时操作浏览器历史记录的模式。
(2)浏览器历史记录写入模式有两种,push和replace,push是追加历史记录,replace是替换当前记录,默认是push。
(3)
或 开启replace模式。
(1)作用:不借助
实现跳转,让路由跳转更加灵活。 (2)编码:
pushShow(item){ this.$router.push({ name: '班内信息', query: { name: item.name, content: item.content, }, }) }, replaceShow(item){ this.$router.replace({ name: '班内信息', query: { name: item.name, content: item.content, }, }) }, //返回 back() { this.$router.back(); }, //前进 forward() { this.$router.forward(); }, //前进或后退指定步数 testGo() { this.$router.go(-1); },
(1)作用:让不展示的路由组件保持挂载,不被销毁。
(2)具体编码:
(3)注意:include如果不写则默认全部,内容为组件的name属性,并非router内配置的name属性。缓存多个写法为:
(1)作用:路由组件所独有的生命周期钩子,用于捕获组件是否处于激活状态
(2)使用:
activated() { console.log("Class被激活") this.timeId= setInterval(() => { this.opacity -= 0.01; if (this.opacity < 0) { this.opacity = 1; } },15); }, deactivated() { console.log("Class失活") clearInterval(this.timeId) },
(3)补充:$nextTick也属于生命舟曲钩子,当更新数据的dom更新完毕再执行$nextTick内的代码。
(1)作用:对路由进行权限控制。
(2)分类:全局守卫,独享守卫,组件内守卫。
(3)全局守卫:
//全局前置路由守卫,每次初始化之前或者路由切换之前, router.beforeEach((to, from, next) => { console.log("前置路由守卫",from,to) let user = localStorage.getItem("user") if (to.meta.isAuth) { if (user == 'user') { next() }else{ alert(user+'无权查看') } }else{ next() } }) //全局前置路由守卫,每次初始化之前或者路由切换之后, router.afterEach((to, from) => { console.log("后置路由守卫",from,to) document.title=to.name })
(4)独享守卫:
独属于某一个路由的路由守卫,且没有afterEnter路由守卫。
{ name: '班内信息', path: 'class/:name/:content', meta:{ isAuth:true }, component: Class, beforeEnter:(to, from, next) => { console.log("前置路由守卫",from,to) let user = localStorage.getItem("user") if (to.meta.isAuth) { if (user == 'user') { next() }else{ alert(user+'无权查看') } }else{ next() } } }
(5)组件内路由守卫:
通过路由规则,访问或离开该组件时,会调用。(直接使用标签访问组件不会调用)
//通过路由规则进入该组件时调用 beforeRouteEnter(to, from, next){ next() }, //通过路由规则离开该组件时调用 beforeRouteLeave (to, from, next) { next() }
(1)对于一个url来说,#及其后面的内容就是hash值。
(2)hash值不会包含在http请求中。(既不会发送给服务器)
(3)hash模式:
(3.1)地址中永远带有#,不美观。
(3.2)若以后通过第三方手机app分享,若app校验较为严格,则会被认定为不合法。
(3.3)兼容性好。
(4)history模式:
(4.1)地址干净,美观。
(4.2)兼容性比起hash模式略差。
(4.3)应用部署上线时,需要后端人员支持,解决刷新页面在服务器404的问题。
vue3地址
地址:awesome vue 3 & vue-cli 3+ · GitHub
8.2 Vue3优势
(1)性能的提升。
(2)源码升级。
(3)支持TypeScript。
(4)新特性。
官方文档:快速上手 | Vue.jsVue.js - 渐进式的 JavaScript 框架https://cn.vuejs.org/guide/quick-start.html
##1.查看vue-cli版本: vue --version vue -V ##2.如果版本在4.5.0以下升级或安装执行: npm install -g @vue/cli ##3.创建: vue create vue3_test ##4.启动: cd vue_test npm run serve
vite 官网:
Vite | 下一代的前端工具链下一代前端工具链https://cn.vitejs.dev/weback打包模式:
vite打包模式:
##1.创建工程: npm init vite-app
##2.进入工程目录: cd ##3.安装依赖: npm install ##4.启动: npm run serve
组合式API
(1)概念:Vue3中的一个新的配置项,值为一个函数。
(2)setup是所有Compositon API的“表演的舞台”。
(3)组件中所用到的:数据,方法等等,均要在setup中配置。
(4)setup函数中的两种返回值:
(4.1)若返回一个对象,则对象中的属性,方法,在模板中均可以直接使用(常用)。
(4.2)若返回一个渲染函数,则可以自定义渲染内容(了解)。
(5)注意:
(5.1)尽量不要与Vue2.x混用:
(5.1.1)Vue2.x中的(data,methods,computed等等)可以访问到setup的方
法,属性。
(5.1.2)setup中不能访问到Vue2.x中的(data,methods,computed等等)。
(5.1.3)如果重名,setup优先级高。
(5.2)setup不能是一个async函数,因为返回值不再是一个return对象,而是
promise,模板看不到return中的属性
总结:setup用于替代 Vue2 中的 beforeCreate 和 created 钩子函数。setup 选项是一个函数,它在组件实例被创建之前执行,并返回一个包含状态和方法等配置信息的对象。
import { h } from "vue"; export default { name: "App", setup() { let person = { name: "张三", age: 18, }; function sayHello() { alert(`${person.name}:hello word,年龄:${person.age}`); } //返回一个对象 return { person, sayHello, }; //返回一个渲染对象 /* return ()=>{ return h('h1',person.name) } */ }, };
(1)作用:用于定义一个响应式的数据。
(2)语法:const xxx=ref(initValue)。
(2.1)创建一个包含响应式数据的引用对象(reference对象)。
(2.2)js中操作数据用xxx.value操作。
(2.3)模板中读取:直接{{xxx}}。
(3)备注:
(3.1)接收基本类型:响应式还是依靠Object.defineProperty()的get与set实现。
(3.2)接收对象类型:内部使用了Vue3中的一个新函数reactive函数,底层是用Proxy
实现的。
import { ref } from "vue"; export default { name: "App", setup() { /* let person = { name: ref("张三"), age: ref(18), }; */ let person =ref({ name: "张三", age: 18 }); function changeInfo(){ console.log(person) person.value.age=25 } return { person, changeInfo }; }, };
(1)作用:定义一个响应式数据(基本数据不用他,用ref函数)。
(2)语法:const 代理对象=reactive(源对象) 接受一个对象或数组,返回一个代理对象(proxy对象)。
(3)reactive定义的响应式数据是深层次的。
(4)内部基于es的proxy实现,通过代理对象操作源对象内部的数据。
let person = reactive({ name: "张三", age: 18, hobby: [1, 2, 3, 4, 5], a: { b: { c: { d: 666, }, }, }, }); function changeInfo() { person.hobby[0]=111; } return { person, changeInfo, };
对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截(数据劫持)。
数组类型:通过重写数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
问题:
(1)新增属性,删除属性,界面不会更新。(监测不到数据修改)
(2)不通过制定方法修改数组,界面不会自动更新。(监测不到数据修改)
Document 当我们创建一个响应式对象,修改其中的属性会触发相应的方法,而我们添加的属性,并不是响应式的,并非通过get和set方法操作属性,而vue2的更新dom操作就是在set方法中实现的,所以监控不到属性的新增和删除。
具体参考:1.Vue核心-CSDN博客
Vue核心的1.5小结数据代理以及1.10.5 Vue无法检测到的数据改变和解决方案。
(1)了解Reflect(反射):
let person = { name: "张三", age: 18, }; let p={}
当出现如下代码时,会出现语法错误导致页面无法正常渲染。
Object.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } }); Object.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } });
当我们使用Reflect操作时,失败并不会出现错误,而是返回一个false。
const a= Reflect.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } }); const b= Reflect.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } }); console.log('a',a,'b',b)
a true b false
所以,当我们使用第一种方法,去操作一个属性有可能会出现异常,导致页面出现问题,而是用第二种方式去操作,会返回一个操作结果是否成功。站在代码兼容性考虑,是有必要使用Reflect来操作属性的。
(2)了解Proxy:
p=new Proxy(person,{ //读取属性调用 get(target,propName){ console.log('有人读取了p的',propName,"属性,值",person.propName) return target[propName] }, //修改,新增属性调用 set(target,propName,value){ console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value) target[propName]=value }, //删除属性调用 defineProperty(target,propName){ console.log('有人删除了了p的',propName,"属性") return delete target[propName] }, })
创建一个Proxy对象当我们对该对象进行属性操作时:
当我们对对象的属性进行增删改查操作时,都会被相应的监控的,并且执行的操作会在person上执行。
(3)总结:当结合了Proxy和Reflect后就形成了vue3的响应式:
Proxy(代理):拦截对象中任意属性的变化(增删改查)。
Reflect(反射):对被代理对象的属性执行操作(不会出现语法错误)。
p=new Proxy(person,{ //读取属性调用 get(target,propName){ console.log('有人读取了p的',propName,"属性,值",person.propName) return Reflect.get(target,propName) }, //修改,新增属性调用 set(target,propName,value){ console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value) Reflect.set(target,propName,value) }, //删除属性调用 defineProperty(target,propName){ console.log('有人删除了了p的',propName,"属性") return Reflect.defineProperty(target,propName) }, })
Proxy和Reflect的浅层理解:Proxy和Reflect-CSDN博客
从定义角度相比:
ref:用来定义基本数据类型。
reactive:用来定义对象或数组类型数据。
从原理角度相比:
ref:通过Object.defineProperty()的get和set来实现响应式(数据劫持)。
reactive:通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内数据。
从使用角度相比:
ref:操作数据需要用xxx.value,读取时不需要xxx.value。
reactive:操作和读取数据都不用xxx.value
(1)setup执行时机在beforeCreate之前,且只执行一次,this是undefined。
(2)setup的参数:
(2.1)props:值对象,包含:组件外传递过来的,且组件内部生命接受了的属性。
(2.2)context:上下文对象:
(2.2.1)attrs:值对象,外部传递进来,但props未接收的属性(this.$attrs)。
(2.2.2)slots:收到的插槽内容,相当于this.$slots。(最好用v-slot命名)。
(2.2.3)emit:分发自定义事件的函数(this.$emit)。
(1)与vue2.x的computed配置功能一致。
(2)写法:
import { computed } from "vue"; setup(props, context) { const data = reactive({ person: { name: { firstName: "张", lastName: "三", }, age: 18, }, }); //计算属性--简写--只考虑回写没考虑修改 /* data.person.fullName=computed(()=>{ return data.person.name.firstName+"--"+ data.person.name.lastName }) */ //计算属性--完整写法 data.person.fullName = computed({ get() { return data.person.name.firstName + "-" + data.person.name.lastName; }, set(value) { const nameArr = value.split("-"); data.person.name.firstName = nameArr[0]; data.person.name.lastName = nameArr[1]; }, }); return { data, }; },
与vue2中的watch功能一致。
问题:
(1)监视reactive定义的响应式数据时,oldValue无法正确获取(由于旧值和新值实际
上是同一个引用,因此旧值和新值看起来总是相同的。),且默认开启了深度监
视,无法关闭(deep配置失效)。
(2)监视reactive定义的响应式数据中的某个对象属性时,deep配置又生效了。
//情况1:监视ref定义的一个响应式数据 watch(sum,(newValue, oldValue)=>{ console.log("sum被修改了", newValue, oldValue); }) //情况2:监视ref定义的多个响应式数据 watch([sum,msg],(newValue, oldValue)=>{ console.log("sum或msg被修改了", newValue, oldValue); }) //传入第三个参数配置对象 watch(sum,(newValue, oldValue)=>{ console.log("sum被修改了", newValue, oldValue); },{immediate:true}) /* 情况3:监视reactive定义的对象 全部属性 1.reactive定义的数据无法正确获取oldValue 2.强制开启了深度监视且无法关闭(deep配置失效了) */ watch(data,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 情况4:监视reactive定义的对象 某个属性 */ watch(()=>data.age,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 情况5:监视reactive定义的对象 某些属性 */ watch([()=>data.age,()=>data.name],(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 特殊情况:监视reactive定义的对象 中的对象属性 */ watch(()=>data.job,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{deep:true})
(1)监视ref定义的基本数据类型的值时,不可以用xxx.value否则无法监视到。
(2)监视ref定义的对象属性时,需要用xxx.value否则由于内存中地址未改变,无法监视到ref请求reactive生成的Proxy的代理类,也可以开深度监视来实现,监视对象属性中的内容。
watcg:既要指明监视的属性,也要指明监视的回调。
watchEffect:不指明监视的属性,在回调中用到了哪个属性就监视哪个属性。
watchEffect优点类似于computed:
computed注重的是结果(回调函数的返回值),所以必须return。
watchEffect注重的是过程(回调的函数体),所以不用写返回值。
watch(data, (newValue, oldValue) => { console.log("person变化了", newValue, oldValue); },{immediate:true}); watchEffect(()=>{ console.log('watchEffect的回调') const x=data.age })
Vue3.0中可以继续使用Vue2.x的生命周期钩子,但是有两个被更名:
beforeDestroy===》beforeUnmount destroyed ===》unmounted
Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x对应关系如下:
beforeCreate ===> setup() created ===> setup() beforemount ===> onBeforeMount mounted ===> onMounted befoerUpdate ===> onBeforeUpdate updated ===> onUpdated beforeUnmount ===> onBeforeUnmount unmounted ===> onUnmounted
App:
Demo:
姓名:{{ data.name }}
年龄:{{ data.age }}
定义:本质是一个函数,把steup函数中使用的Composition API进行的封装。
类似于Vue2.x中的mixin。
自定义hook的优势:服用代码,让setup中的逻辑更加清楚。
新建hooks文件夹,并创建功能对应的js文件(以点击获取鼠标当前xy轴位置为例):
userPoint.js
import {
reactive,
onMounted,
onBeforeUnmount
} from "vue";
export default function () {
let point = reactive({
x: 0,
y: 0
})
function savePiont(event) {
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX)
console.log(event.pageY)
}
onMounted(() => {
window.addEventListener('click', savePiont)
}),
onBeforeUnmount(() => {
window.removeEventListener('click', savePiont)
})
return point
}
Demo.vue
当前鼠标X:{{point.x}},Y:{{point.y}}
作用:创建一个ref对象,其value指向另一个对象中的某个属性。
语法:const name=toRef(person,'name')。
应用:将响应式对象中的某个属性单独提供给外部使用时。
扩展:toRefs与toRef功能一致,但是可以创建多个ref对象,已发toRefs(person)。
Vue3
姓名: {{ name }}
年龄:{{ person.age }}
性别:{{ person.sex }}
爱好:{{ person.hobby }}
爱好:{{ person.job.j1.pay }}
8.3.1 shallowReactive和shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式,不进行对象响应式的处理。
使用场景:
如果有一个对象,结构比较深,但是变化时值是外层属性变化===》shallowReactive。
如果有一个对象,后续不会修改对象属性,而是产生新的对象替换===》shallowRef。
readonly:让一个响应式数据变为只读的(深只读)。
shallowReadonly:让一个响应式数据变为只读的(浅只读)。
使用场景:不希望数据被修改时。
toRaw:
作用:讲一个有reactive生成的响应式对象转为普通对象。(ref的不行)
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作不会引起
页面的更新。
markRaw:
作用:标记一个对象,使其永远不会在成为响应式对象。
使用场景:
(1)如果我们声明了一个reactive的对象,后续对这个对象进行属性添加的操作,
添加上的属性默认就是响应式的,而有些值不应该设置为响应式的,例如:
异常复杂的第三方类库等等。
(2)当显然具有不可变数据源的大列表时,条过响应式转换可以提高性能。
作用:创建一个自定义的ref,并对其依赖项跟踪和更新出发进行显示控制(可以实现自定义的控制页面更新时机,track告诉get方法这个参数需要跟踪,triger通知vue更新dom)。
实现防抖:
Vue3
{{ keyWords }}
作用:实现祖与后代组件之间的通信。
父组件中有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据。
具体写法:
祖组件:
setup() { let car=reactive({ name:'奔驰', price:400000 }) //给自己的后代组件传递数据 provide('car',car)
后代组件:
let car=inject('car') console.log("二级子组件",car) },
isRef:检查一个值是否为ref对象。
isReactive:检查一个值是否是由reactive创建的响应式代理。
isReadonly:检查一个对象是否是由readonlu创建的只读代理。
isProxy:检查一个对象是否由reactive或者readonly方法创建。
传统的OptionsAPI(配置API)中,新增或修改一个需求,要分别在data,methods,computed里修改
让相同功能的变量,函数等等更加有序的组织在一起(借助hook函数)。
在Vue2.x中,组件必须有一个跟标签。
在Vue3中,组件可以没有跟标签,内部会将多个标签包含在一个Fragment的虚拟内存中。
优势:减少标签层级,减小内存占用。
概念:Teleprot是一种能够将我们组件中的html结构移动到指定位置的技术。
Dialog 弹框 内容将代码直接传送到html的body内。
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
使用步骤:
异步引入组件:
import { defineAsyncComponent } from "vue"; const Demo = defineAsyncComponent(() => import("./componets/Demo1.vue"));
使用Supense包裹组件,并配置好default与fallback:
加载中....
Vue2.x中有序地全局API和配置。
例如:注册全局组件,注册全局指令等等
//全局组件 Vue.component('MyButton',{ data:()=>({ cont:0 }), template:'' }) //全局指令 Vue.directive('focus',{ inserted:el=>el.focus() })
Vue3对这些API做出了调整:
将全局API(Vue.xxx)调整到应用实例app上
data选项始终都要被声明为一个函数。
Vue2.x写法:
v-enter, v-leave-to{ opacity:0 } v-leave, v-enter-to{ opacity:1 }
Vue3写法:
.v-enter-from, v-leave-to{ opacity:0 } .v-leave-from, v-enter-to{ opacity:1 }
移除keyCode作为v-on修饰符,同时也不再支持config.keyCodes
移除native作为v-on修饰符:
父组件绑定事件:
子组件js声明自定义事件(不声明则被认为是js原生事件):
export default { emits:['close'] }
过滤器虽然看起来很方便,但是他需要一个自定义语法,打破大括号内表达式只是JavaScript的假设,不仅有学习成本还有实现成本,建议用方法或计算属性替换过滤器。
7.Vue UI库
(1) Vant:Vant 4 - A lightweight, customizable Vue UI library for mobile web apps.A lightweight, customizable Vue UI library for mobile web apps.https://vant-ui.github.io/vant/#/zh-CN
(2)Cube UI:cube-ui: cube-ui 是由滴滴开源的基于 Vue.js 实现的移动端组件库https://gitee.com/mirrors/cube-ui
(3)Mint UI:
Mint UIhttps://mint-ui.github.io/#!/zh-cn
(1)Element UI:
Element - The world's most popular Vue UI frameworkElement,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库https://element.eleme.io/(2)IView UI:
iView / View Design 一套企业级 UI 组件库和前端解决方案基于 Vue.js 的 UI 组件库,用于研发企业级中后台产品。iView 官网。https://www.iviewui.com/
使用UI组件库一般按需引入。