# Vue-demo **Repository Path**: CandyPop/vue-demo ## Basic Information - **Project Name**: Vue-demo - **Description**: 对vue2-3的学习笔记 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-03-20 - **Last Updated**: 2022-12-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #### Vue 学习笔记 https://cn.vuejs.org/ 安装devTools,可以使用谷歌应用商店,下载,也可以在本`asset`文件夹下下载 这里以第一种安装的方式,也就是直接往html界面引入`Vue.js`文件 `Vue.js`和`Vue.min.js`前者用于开发模式,会有比较多的错误提示,因此体积也比较大,后者用于生产环境,体积压缩过。 ![1647764585045](./img/1647764585045.png) 去除使用`Vue.js`引入后,产生的浏览器的提示 ![1647764685961](./img/1647764685961.png) 也就是去除这个提示。 ![1647764706648](./img/1647764706648.png) ```javascript ``` ##### 初识Vue * 想让Vue工作,必须创建一个Vue,需要创建一个配置对象 * root容器里的代码已然符合html规范,只不过混入了一些混合的Vue语法 * root容器里面的代码被称为【Vue模版】 * Vue的渲染格式`{{}}`也被称为**插值语法**,里面可以写表达式,可以是数字循环,也可以写方法调用。 ##### 模版语法 ```javascript v-bind 可以指定属性绑定,将后面的值看做是表达式,从data从取值

你好,{{name}}


跳转 跳转
``` ##### 双向数据绑定 ```javascript v-bind只能影响单方面绑定,即data->元素的绑定 v-model可以支持双向数据绑定,即在支持data->元素的绑定,元素的值改变,也就是value改变也会影响data
单向数据绑定 双向数据绑定
``` 但是`v-model`只能用于**表单元素**,其他使用都会报错。 由于v-model只会收集value元素,所以以下还可以这样简写 ``` v-model:value ===> v-model
单向数据绑定 双向数据绑定
``` ##### data 与 el 的2种写法 * el有2种写法 * new Vue的时候配置el属性 * 县创建Vue实例,随后再通过实例对象.$mount('#root')来挂载容器 * data有2种写法 * 函数式 * 对象式 * 两种写法都可以,当运用组件的时候,data必须使用函数式,不然会报错 * 一个重要的选择 * 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue的实例了。 ##### mvvm * 模型model对应data中的数据 * 视图View,模版,也就是html页面 * vm,视图模型viewmodel,vue实例对象 观察发现: * data中的所有属性,最后都出现在了vm身上 * vm身上所有属性,以及vue原型上的所有属性,在Vue模版中都可以直接使用 ![1647781496953](./img/1647781496953.png) ```javascript 所以也意味着你可以用这样的插值表达 {{$attrs}} {{$el}} ``` ##### Object.defineProperty() ```javascript //平常我们给对象新增属性的话,可以直接在对象上通过.来追加属性 let number = 18; let person = { name:'pop', sex:'man', // age:18 可以通过这个方法增加 } // person.age = 18; //也可以通过这种方法增加 但其实,还有一种高级的用法 Object.defineProperty(person,'age',{ value:18, enumerable:true,//控制属性是否可以枚举,默认是false writable:true,//控制属性是否可以被修改,默认是false configurable:true,//控制属性是否可以被删除,默认是false get(){ // 当有人读取person的age属性的时候,get函数(getter)就会被调用 console.log('调用get方法'); return number; }, set(value){ // 当有人修改person的age属性的时候,set函数(setter)就会被调用 console.log('有人调用set方法'); number = value'' } }); /*想额外说一下enumerable这个属性的影响 在控制台上,如果直接在对象上添加,会显示亮色 但是使用defineProperty会变暗、 由于这个属性默认是false的 也就是不可枚举的,则意味着 你在遍历的对象属性的时候,这个属性会被跳过。 */ ``` ![1647783508606](./img/1647783508606.png) age的值是未知的,需要调用对应的方法来获得,同时,由于number是外部的,所以当number有所改变,这边也会同步改变。 ```javascript // value和 get 是同一个作用,只能同时用一个。 // writable和set是同一个作用,用一个。 // 完整实例: Object.defineProperties(obj, { 'address': { //value: true, // writable: true, get:()=>{console.log("get110");return obj._address;}, set:(v)=>{console.log("get110");return obj._address=v;} } // etc. etc. }); ``` ##### 数据的代理 ```javascript /** * 假设有这样一个需求,obj1里有x属性,obj2有y属性 * 我希望从obj2访问obj1的属性 * 相当于obj2做了obj1的代理 */ let obj1 = {x:100}; let obj2 = {y:200}; //你可以这样定义 Object.defineProperty(obj2,'x',{ get(){ return obj1.x; } , set(value){ obj1.x = value; } }); ``` ##### Vue中的数据代理 * Vue中的数据代理 * 通过vm对象来代理data对象中的属性的操作(读/写) * Vue中的数据代理的好处 * 更加方便的操作data中的数据 * 基本原理 * 通过Object.defineProperty来把data对象中所有的属性添加到vm上。为每一个添加到vm上的属性,都制定一个getter/settter。在getter/setter内部去操作(读/写) data中对应的数据 ```javascript const x = new Vue({ el:'#root',//选择容器 data:{ name:'一个Vue', url:'https://www.baidu.com' } }); // 在你通过data属性进行model的数据绑定的时候,vue也会将这个数据源保存起来,他的保存的地方叫做_data的属性 ``` ![1647785483589](./img/1647785483589.png) 所以实际上我们的插值属性 ``` {{name}}也可以等同于{{_data.name}} ``` 当很明显,这样太麻烦了,所以数据代理,让我们直接使用`{{name}}`这种数据代理的方式,快速的访问到变量,实际上是代理我们创建vm的时候,传入的data参数。这里也是使用object.defineProperty的方式来实现的,十分的方便。 ##### 事件绑定 * 使用v-on:xxx 或 @xxx 绑定事件,其中xxx就是事件名 * 事件的回调需要配置在`methods`对象中,最终会在vm上 * ![1648034748111](./img/1648034748111.png) * methods中配置的函数,**不要**使用箭头函数函数,否则this就不是vm了 * methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象 * @click=“demo” 和 @click="demo($event)" 效果一致,但后者可以传参 ```javascript
欢迎{{name}}
``` ##### 事件描述符 Vue 中的事件修饰符 * prevent:阻止默认事件(常用) * stop:阻止事件冒泡()常用 * once:事件只触发一次(常用) * capture:使用事件的捕获模式 * self:只有event.target是当前的元素时才会触发事件 * passive:事件的默认行为立即执行,无需等待时间回调执行完成 ```javascript
欢迎{{name}} 点击阻止跳转
``` ##### 键盘事件 * Vue中常用的按键别名 * 回车=>enter * 删除=>delete * 退出=>esc * 空格=>space * 换行=>tab * 上=>up * 下=>down * 左=>left * 右=>right * ```javascript @keyup.enter="eventName" 这样的绑定,如果这里没有定义的按钮,你也想使用,可以查看e.key来查看,也是同样可以使用的,但是有几个例外的按键,请看第四点。 ``` * Vue 未提供别名的按键,可以使用按键原始的key值去绑定,但要注意kebab-case(短横线命名) * ``` 按键的名字你可以通过 e.key(按键名称)e.keyCode (按键编码)来查找,但是keyCode在不同的键盘也许code码也许不一样,未来这个属性可能会废弃,所以还是以按键名字为主 如键盘上有一个按键叫做CasLock,这个时候有点像是驼峰的命名,你在绑定的时候不能这样写 @keydown.CasLock 而是要 @keydown.cas-lock才可以 ``` * 系统修饰符(用法特殊):ctrl、alt、shift、meta(win/command)、tab * tab在网页上有失去焦点的意思,所以tab的事件是发生在按键按下的时候,当抬起的时候已经失去了事件对象了,所以对tab而言,需要使用keydown。 * 配置keyup使用:按下修饰键的同事,再按下其它键,随后释放其它键,事件才被触发 * 配合keydown使用:正常触发事件 * 也可以使用keyCode指定具体按键,(不推荐) ```html ``` * `Vue.config.keyCodes.自定义键名 = 键盘码`,可以去定制按键别名 ```javascript Vue.config.keyCodes.huiche = 13; ``` ##### 事件的总结 ###### 事件的组合使用 ```html
点击
点击
``` ###### 按键的组合使用 ```html ``` ##### 计算属性 * 定义:要用的属性不存在,要通过已有的**属性**计算得来 ```javascript vue中,属性一词泛指data中定义的属性,例如当你希望data.name="sdfasdf123"的这个属性,用在页面上的我是全部字母大写,且都截取3位置的时候,也许你会写成这样 {{name.slice(0,3).toUpperCase()}}, 当你希望去名字拼接其它属性的时候,你也许也写成这样 {{name.slice(0,3).toUpperCase()}}-{{age.slice(0,3).toUpperCase()}} 或者 {{name.slice(0,3).toUpperCase()-age.slice(0,3).toUpperCase()}} 很明显的是,这样可读性很差,如果有一个可以动态按照我们要求计算出属性的“属性”那就好了 ``` * 原理:底层借助了`Object.defineProperty`方法提供的getter和setter。 * get函数什么时候执行? * 初次读取时会执行一次 * 当依赖的数据发生变化的时候会再次调用 * 对比methods * 同样是定义方法,计算属性内部有缓存机制,可以复用,效率更高,调试方便 * 备注 * 计算属性最终会出现在vm上,直接读取使用即可。 * 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算属性的变化 ```html
姓:
名:
姓名:
``` ##### 监视属性 * 监视属性watch * 当被监视的属性变化时,回调函数自动调用,进行相关操作 * 监视的属性必须**存在**,才能进行监视 ``` 不存在也可以监视,但是没什么意义 ``` * 监视的两种写法 * new Vue时传入watch配置 * 通过`vm.$watch`监视 ```javascript
今天天气{{info}}
``` ##### 深度监视 * `Vue`中的watch默认不检测对象内部的改变(一层) * 配置的deep:true可以检测对象内部值的改变(多层) * `Vue`自身可以检测对象内部值的改变,但`Vue`提供的watch**默认不可以** * 使用`watch`时,根据数据的具体结构,决定是否采用深度监视。 ```javascript ``` ##### computed和watch之间的区别 * computed能完成的任务,watch都可以完成 ``` computed可以用于直接计算出属性,watch如果希望用到data中的某个属性,但是那个属性不存在的情况下,是需要提前声明的。 ``` * watch能完成的功能,computed不一定能完成,例如watch可以异步操作 ``` 你希望在某个值改变后,延迟一秒再执行,这一点computed无法做到,但是watch可以 ``` * 两个重要的原则 * 所被`Vue`管理的函数,最好写成普通函数,这样的this才是vm,或者组件实例对象 * 所有不被Vue所管理的函数(定时器的会回调函数,ajax的回调函数,Promise的回调函数),最好写成箭头函数,这样的this指向的才是vm,而不是组件实例对象。 ##### 绑定class样式 ```html
{{name}}
{{name}}
{{name}}
``` ##### 绑定style 你也在元素上定义内联样式 * class样式 * :class="xxx" xxx 可是是字符串,对象,数组。 * 字符串写法适用于:类名不确定,要动态获取。 * 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定 * 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用 * style样式 * :style=“{fontSize:xxx}”其中是xxx是动态值 * :style="[a,b]"其中a、b是**样式对象** ```html
{{name}}
{{name}}
{{name}}
``` ##### 条件指令 * v-if * v-if=“表达式” * v-else-if="表达式" * v-else="表达式" * 适用于切换评率脚底的场景 * 不展示DOM元素直接被移除 * v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断” * v-show * 写法:v-show=“表达式” * 适用于:切换评率较高的场景 * 特点:不展示dom元素未被移除,仅仅是使用样式隐藏掉 * 备注: * **使用v-if的时候,元素可能无法获取到,而使用v-show一定可以获取到** ```html
{{name}}
{{name}}
{{name}}
{{name}}
{{name}}
{{name}}
{{name}}
``` ##### 列表渲染 * 用于展示列表数据 * 语法: v-for="(item,index)" in xxx :key="yyy" * 可遍历:数组,对象,字符串(用得少),指定次数(用得少) ```html

人员列表

``` ##### key的作用和原理 ![1648195629962](./img/1648195629962.png) key在遍历的时候,作为更新元素的唯一凭证,vue会拿到对应的key所指定的元素进行对比渲染,当key所关联的元素完全一致,将会复用以前已经渲染完成的,当不一致时,将会重新渲染。 * 虚拟dom中的key的作用 * key是虚拟DOM对象的标识,当数据发生变化时,vue会根据【新规则】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异对比,比较规则如下 * 对比规则 * 旧虚拟DOM找到了与新虚拟DOM相同的key * 若虚拟DOM中内容没变,直接使用之前的真实DOM * 若虚拟DOM中内容变了,则生成新的真实DOM,**随后替换掉页面中之前的真实DOM** * 旧虚拟DOM中未找到与新虚拟DOM相同的key,创建新的真实DOM,随后渲染到页面 * 用index作为key可能会引发的问题 * 若对数据进行:逆序添加,逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新,界面看起来没有问题,但是效率低。 * 如果结构中还包含输入类的DOM,会产生错误的DOM更新,界面有问题,因为如果输入控件没有变化,将会被直接替换,造成数据错位。 * 开发中如何选择key * 最好使用每天数据的唯一标识 * 如果你一定要用index来作为标识,请保证不要打乱前面已经渲染好的数据 ##### 关于更新数据时候一个问题 调试vue代码的时候,会安装一个Vue的开发者工具在自己电脑上,如果你在更新一个列表的时候,是这样写的 ```javascript let vm = new Vue({ data:{ persons:[ {name:'123',age:18,id:1} ] }, methods:{ changeProfile(){ this.persons[0].age=16;//奏效 this.persons[0]={name:'546',age:30,id:2};//不奏效 } } }); ``` 可以看到我们写了两种方式,第一种方式是可以在页面中体现的,但是第二种却不行,不过第二种的不行并不是说数据没有修改成功,而是界面上没有显示,如果你在点了changeProfile方法后,再打开浏览器的开发者工具,你会其实数据有体现,不过你在点击了changePrile之前点开,会发现数据并没有变化,无论哪种方式,界面上的数据都不会有变化。 ##### Vue 监测数据的原理 之前我们提及过数据代理,也就是我们在data属性中定义过的数据,最终会在Vue实例vm身上找到,并且最后还伴随着许多关于他们的set,get方法,当我们尝试去修改,就会触发vue对模版的重新解析,那么 ```javascript this.persons[0].age=16;//奏效 this.persons[0]={name:'546',age:30,id:2};//不奏效 ``` persons的修改为什么只有第一种可以,而第二种不可以呢,主要是因为,vue只会对对象的**属性**进行检测,并不会对对象本身进行检测。如果要解释Vue生成的代理原理进行解释,这里有一段简单的代码。 ```javascript let data = { name:'pop', age:26 }; const obs = new Observer(data); //假设这是一个vm对象 let vm = {}; vm._data = data=obs; //Vue实例中的再生成实例之前,也就是对_data之前还做了另一个操作 function Observer(data){//传入需要被代理的对象 //获得这个对象所有可以获得的属性名 let keys = Object.keys(data); keys.forEach((k)=>{ //为每个属性属性进行代理 //此时的this就是Observer对象 Object.defineProperty(this,k,{ get(){ return obj[k]; }, set(val){ obj[k] = val; } }); }); } ``` 所以直接修改对象本身vm是无法检测的,所以修改并不会触发vue重新解析模版,即便数据真的已经修改了。 ##### Vue中的Set与$set 如果你希望在后续的开发中,追加属性的话,可以使用这个api ```javascript //希望在student属性上追加性别属性 Vue.set(vm._data.student,'sex','男') //数据代理也是一样的 Vue.set(vm.student,'sex','男') // 这样使用也是可以的 vm.$set(vm.student,'sex','男') ``` 但是有一个问题,这个set方法只能用于Vue实例身上已经存在的属性,意思是 ```javascript let vm = new Vue({ data:{ student:{ name:"pop" } } }); //这样追加可以 Vue.set(vm._data.student,'sex','男') Vue.set(vm._data,'sex','男') //一个新的属性,这样添加一个全新的响应式对象是不可以的 ``` ![1648392762716](./img/1648392762716.png) ##### Vue中的数组监控 vue中根数据(data)中如果去修改数组,你不能直接根据索引去修改元素,这样vue无法监测到数组里元素的改变,也就是这样。 ```javascript this.persons[0]={name:'546',age:30,id:2};//不奏效 ``` ![1648393512631](./img/1648393512631.png) Vue的作者,将常用的用于修改数组的方法进行了封装,你可以使用 ``` this.persons.push({....}) ``` 的方法来更新数组,这样Vue可以被监听到,从而触发重新解析模版。当然除了官网的方法,你也可以用这种,但是不多。 ```javascript // 属性名,可以是数组的索引值 Vue.set(vm.student,1,'男') ``` * Vue 会监视data中所有层次的数据 * 如何监视对象中的数据? * 通过setter实现监视,且要求new Vue时转入要检测的数据 * 对象中后追加的属性,Vue默认不做响应式处理 * 如需要给后添加的熟悉做响应式,请使用如下API ``` Vue.set(target,propertyName/index,value) 或者 vm.$set(target,propertyName/index,value) ``` * 如何检测数组中的数据? * 通过包装数组更新元素的方法实现,本质就是做了两件事 * 重新解析模板,从而更新数据 * 在Vue修改数组中某个元素,一定要如下方式 * ``` push pop shift unshift splice sort reverse ``` * ``` Vue.set()或vm.$set() ``` **特别注意:**Vue.set()和vm.$set() 不能给vm或vm的根数据(_data)对象,追加属性。 ##### Vue收集表单数据 * ``` ,则 v-model收集的是value值,用户输入的就是value值。 ,则 v-model收集的是value值,且要给标签配置value,否则由于没有value值,收集的信息将一直是空。 1.没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值) 2.配置input的value属性: 1.v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,是布尔值) 2.v-model的初始值是数组,那么收集的就是value组成的数组 备注:v-model的三个修饰符 如。v-model.lazy lazy:失去焦点再收集数组 number:输入字符串转为有效数字 trim:输入首尾空格过滤 ``` 如果你希望表单提交前,进行某个方法的提交 ```html
``` ##### Vue中的时间戳 https://www.bootcdn.cn/ 有一些好用的js组件,可以上这里使用。`dayjs.min.js` ![1648475020179](./img/1648475020179.png) ![1648475062131](./img/1648475062131.png) ```javascript new Vue({ el:"#root", data:{ time:Date.now()//一个时间戳 }, computed:{ fmtTime(){ //不传,默认是当前时间 return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss'); } } }); ``` ##### Vue中的过滤器 ```java //全局过滤器配置 Vue.filter('mySlice',function(value){ return value.slice(0,4); }); //单独vue实例配置 new Vue({ el:"#root", data:{}, filters:{ mySlice(value){ return value.slice(0,4) } } }); ``` * 对要显示的数据进行特定格式化再显示(**适用于一些简单的逻辑处理,如果逻辑过于复杂,推荐使用计算属性和方法**) * 语法 * 注册过滤器:`Vue.filter(name,callback)`或者 `new Vue(filters:{})` * 使用过滤器:使用插值语法,可以利用`管道符|进行连接`,达到链式调用,上一个过滤器的返回值将会作用于下一个过滤器的第一个参数。 ``` {{ xxx | 过滤器名1 | 过滤器2 }} 或 v-bind:属性 = "xxx | 过滤器名" xxx 定义的变量总是会作用于 过滤器的第一个参数,当然,你的过滤本身也是一个方法,如果你在过滤器上又定义其它参数,他们总是按定义的顺序排在管道顶端第一个变量后面。 ``` * 备注: * 过滤器也可以接受额外参数,多个过滤器也可以串联 * 并没有改变原本的数据,是产生新的对应数据。 ##### Vue 中的一些标签 * v-bind * 单向绑定解析表达式,可简写为 :xxx * v-model * 双向数据绑定 * v-for * 遍历数组/对象/字符串 * v-on * 绑定事件监听,可简写为@ * v-if * 条件渲染,动态控制节点是否存在 * v-else v-else-if * 条件渲染,动态控制节点是否存在,和v-if配合使用,**中间不可以断开,插入其他标签** * v-show * 条件渲染,动态控制节点是否展示 * v-text * 作用:向其所在的节点中渲染的文本内容 * 与插值语法的区别,v-text会替换掉节点中的内容,{{xx}}则不会。 * v-html * 作用:向指定节点中渲染包含html结构的内容 * 与插值语法的区别 * v-html会替换节点中所有的内容,{{xx}}则不会 * v-html可以识别html结构 * 严重注意,v-html有安全问题 * 在网站上动态渲染任意的html是非常危险的,容易导致xss攻击 * 一定要在可信的内容上使用v-html,永远不要用在用户提交的内容上 * v-cloak * 是一个特殊的属性,Vue实例创建完毕并接管容器后,会删除掉v-cloak属性 * 使用css配合v-cloak可以解决网速慢时,页面展示出{{xxx}}的问题 * ```html

{{name}}

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

初始化的n值是:{{n}}

当前的n值是:{{n}}

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

一段不需要vue解析的模板,使用v-pre,vue可以不再解析,加快渲染速度

当前的n值是:{{n}}

``` ##### Vue中自定义指令函数 Vue中有一个v-show函数,可以控制所绑定dom元素的显示隐藏,本质上是操作dom元素,在行内样式上增加了display:none,我们是否也可以定义自己的指令函数呢,答案是可以的 ```html

{{name}}

当前的n值是:

当前放大10倍的n值是:

``` ![1648568160911](./img/1648568160911.png) ![1664893155634](./img/1664893155634.png) ##### Vue 中的生命周期 看源码 ![1664894616940](./img/1664894616940.png) ![生命周期](./img/生命周期-1664896159506.png) ![1664978526982](./img/1664978526982.png) ##### Vue中的组件 ![1664979570268](./img/1664979570268.png) ![1664979591178](./img/1664979591178.png) ###### 定义组件的几种方式 * 非单文件组件 * 一个文件中包含了n个组件 * 单文件组件 * 一个文件中只包含1个组件 ![1664981854467](./img/1664981854467.png) ![1665047032676](./img/1665047032676.png) ![1665049038142](./img/1665049038142.png) 一个重要的内置关系 ``` VueComponent.prototype.__proto__ === Vue.prototype 让组件实例对象vc,可以访问到Vue原型上的属性、方法 ``` ![1665051313841](./img/1665051313841.png) ##### Vue 的脚手架 安装脚手架 ``` npm install -g @vue/cli 切换到你要创建项目的目录,然后使用命令创建 vue create xxxx 启动项目 npm run serve ``` 这个操作需要用管理员来操作,但是有的时候下载好的vue-cli没办法使用,你需要用 ``` get-ExecutionPolicy 查看当前PowerShell的权限是什么,如果是Restricted,说明是限制的 接着我们更改一下 Set-ExecutionPolicy -Scope CurrentUser 该模式会变成,RemoteSigned (受限移除) ``` ![1665059796588](./img/1665059796588.png) 一个正确的流程是,`main.js`作为整个项目的入口文件,他创建了这个项目的唯一VM对象,也就是Vue实例对象,`main.js`中需要引入vue,和一个所有容器的父级,就是App.vue他管理着所有的容器,然后接着就是App.vue的定义,他里面注册了所有的容器。 然后就是作为网页的展示,是在public文件下的index.html文件。 ![1665064876017](./img/1665064876017.png) 脚手架的默认配置无法修改,虽然vue的脚手架依靠webpack,但是我们没看到webpack的配置文件,主要是因为这个配置被隐藏了,我们可以使用 ``` vue inspect > output.js ``` 来获得参数,不过,在这个js里修改后,也是不生效的,这个只是展示作用。 如果你希望修改脚手架的默认配置,可以自己创建一个`vue.config.js`文件,这里使用的语法是commonJS语法,也就是module.exports的暴露方式而不是es6,原因在于这个文件会被webpack解析,将参数和默认参数合并,而webpack是使用Nodejs,nodejs打包用的是commonjs。 关于vue.config.js的语法这里可以查到 https://cli.vuejs.org/zh/config/#vue-config-js ###### ref 看源码 ###### props 看源码 ###### mixin 看源码 ###### plugin 插件 ![1665322429325](./img/1665322429325.png) ###### scoped 看源码 ##### 自定义一个Todo List ![1665324728301](./img/1665324728301.png) ![1665500396817](./img/1665500396817.png) ##### LocalStorage 和 SessionStorage ![1665926698364](./img/1665926698364.png) 只会存储属于同一域名下的存储内容 ![1665927567551](./img/1665927567551.png) ##### Vue 中的组件自定义事件 给组件定义的事件,不是针对html元素的 ![1666017691051](./img/1666017691051.png) ##### 全局事件总线 可以实现任意组件通信,这不是一个新的技术,而是总结出来的经验 ![1666019081651](./img/1666019081651.png) 某个组件X,所有的其它组件都可以找到这个组件X,且可以往X身上注册他们的事件,然后可以通过X来互相传递信息。 x组件需要满足两个条件 * 可以被所有的组件看到 * 支持注册事件,解绑事件,触发事件的方法 $on $off $emit 要实现能被所有的组件看到,我们需要这样一个以前的知识点 ``` VueComponent.prototype.__proto__=Vue.prototype ``` 所以我们只需要往Vue的显示实例对象上添加就行了,可以保证所有的vc实例都可以看到。 ![1666020003707](./img/1666020003707.png) ![1666104367577](./img/1666104367577.png) 需要注意的是,并不是所有的组件交流都需要使用全局事件总线的,例如,父组件给子组件传递,可以用props属性,子给父也可以用自定义事件来传递。 但是如果涉及关系到达到多层,例如孙子组件和爷爷传递,父亲也只是中间商而已,这个时候就可以用全局事件总线。 ##### 消息订阅与发布 ![1666106102314](./img/1666106102314.png) 谁订阅了消息,然后谁往那个订阅了消息的地方发布了东西,谁就会收到 原生js无法实现消息订阅和方法,所以需要借助第三方库。 这里使用的是`pubsub.js`,你有其他的也可以选择其他的 ``` npm i pubsub-js --save ``` ![1666107579614](./img/1666107579614.png) 但是用的少,我们还是比较常用全局事件总线 ##### nextTick ![1666623815062](./img/1666623815062.png) ##### 过度动画 ![1666707388624](./img/1666707388624.png) vue中,对于动画有几个阶段,一个是进入的动画enter,即表示被定义的元素会以什么样的方式从enter过度到leave,也就是从登场到离场的状态。enter的阶段也有对应的,就是从v-enter到v-enter-to,表示从enter这个大动画的开始到结束可以算作一个完整的动画过程。leave也是同理 ![1666707399282](./img/1666707399282.png) ##### 代理服务器 看源码 ##### 插槽 ![1666940114885](./img/1666940114885.png) ![1666940127548](./img/1666940127548.png) ![1666940169323](./img/1666940169323.png) ![1666940190868](./img/1666940190868.png) ##### VueX * 多个组件依赖于同一个状态(数据), * 来自不同的组件行为需要变更同一状态 ![1666944786523](./img/1666944786523.png) ![vuex](./img/vuex.png) Vuex有三个模块需要了解,对于案例-计算器,我们可以选择数字和运算符来对结果进行把控,这个实在一个组件里面就可以完成的,而对于Vuex来说,我们需要三个步骤来进行完成结果。 * Actions * 以VueComponents举例,我们自己写的也是这个,当你请求发送一个加2的请求的时候,需要使用一个Dispatch的Api。将这个请求发送给Actions,他会将这个请求发送给Mutations,Actions本质是一个对象,有一个加2的回调来响应这个dispatch操作。接着在Actions中,你可以使用Commit操作来提交给Mutations * Mutations * 用于操作和修改数据,也就是状态(State)的对象,他同样也是对象,他可以直接使用State中的数据进行更改 * State * 用于存储数据,他也是一个对象,所有需要操作的数据和都在这个对象中,当他的数据被修改的时候,Vue会重新渲染模版来进行展示 这个里面有一个也许会有一个多余的操作,就是Actions,因为他貌似只是将操作告诉给Mutations,真正修改状态的操作其实是Mutations,其实Actions还是有存在的必要,之前我们是dispatch给Actions一个准确的2的数字,如果当要加的数据不确定的时候,需要向其他的服务器请求的时候,Actions就有了用武之地。所以当你希望在操作数据之前需要额外的逻辑的时候,Actions就可以做到。 但是如果你就是明确了加2,其实也可以直接在VC步骤的时候提交给Mutations。 这所有的对象,都是归Store对象管理,所以当你调用dispatch的时候,其实调用的是store.dispatch 准备工作,你需要安装vuex,这里需要注意的是,由于官方已经将Vue3设置为默认版本,并且Vuex也更新到了4版本,所以你如果现在直接使用vuex安装的就是vue4,有因为vuex4只能用于vue3,目前项目使用的是vue2,所以你直接使用会报错,所以我们这里需要直接指定版本安装,vue2->vuex3,vue3->vuex4 ``` npm i vuex@3 ``` ##### 路由 * 路由就是一组key-value的对应关系 * 多个路由,需要经过路由器的管理 ``` npm i vue-router@3 版本和vuex是一个意思,默认是4,用于vue3 ``` 路由的配置规则 key-> 路径 value -> 组件,当vue-router检测到你的路径发生了变化,就会寻找路径对应的组件,选择暂时,如果没有找到,则不展示。 路由分类 * 后端路由 * 理解value是function,用于处理客户端提交的请求。 * 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。 * 因为使用nodejs我们可以编写一个基于js的服务器,来响应请求,编写方法也是一个路径对应一个函数来响应请求。 * 前端路由 * 理解:value是component,用于展示页面内容。 * 工作过程:当浏览器的路径改变时,对应的组件就会显示。 文件规范 * 普通组件,我们定义的在component文件下的vue组件,由App.vue管理在components项中的组件 * 路由组件,由vue-router管理,且他会匹配跳转的,一般我们会将这些路由组件放在pages文件夹下。 关于路由的几个注意点 * 路由组件通常存放在pages文件夹,一般组件通常存放在componets文件夹 * 通过切换,隐藏了的路由组件,默认是被销毁掉的,需要的时候再去挂载。 * 每个组件都有自己的$route属性,里面存储着自己的路由信息 * 整个应用只有一个router,可以通过组件的$router属性获取到。 ##### 路由守卫 决定路由的内容是否展示,与权限绑定。 路由的hash值 如果你的一个请求是这样的 ``` http://localhost:8080/student ``` 是请求student资源,如果你在这后面增加#号,那么#号包括后面的东西不会包括在请求资源中,而#后面的东西就叫做hash值 ``` http://localhost:8080/student/#/123/123/123/123dfsadf ``` hash与history的区别 * hash * 会在url中存在#,看上去不美观 * hash值不会包含在http请求中,即hash值不会带给服务器 * 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记不合法 * 兼容性好 * history * 和平时使用的url一直,只是有单纯的资源名,没有# * 兼容性和hash模式相比略差 * 应用部署上线时需要后端人员支持,解决刷新页面服务端404问题(可使用第三方类库解决) 其实这最主要的问题是会影响项目部署。 我们在router/index.js中配置你的模式 ```javascript const router = new VueRouter({ mode:'history',// 默认hash模式 // ... }) ``` 然后打包 ``` npm run build ``` 打包完成 ![1668956841982](./img/1668956841982.png) 接下来,我们需要将他们部署到一个服务器中,这里我们使用nodejs中的express框架搭建一个简单的服务器。 ``` 在一个文件夹下使用 npm init 初始化,创建一个package.json 然后用安装express npm i express ``` ```javascript const express = require('express') const app = express() // 指定可以访问的资源路径 app.use(express.static(__dirname+'/static')) app.get('/person',(req,res)=>{ res.send({ name:'pop', age:18 }) }) app.listen(5005,(err)=>{ if(!err) console.log('服务器启动了') }) ``` 然后到目录下,`node server.js`启动服务器。 启动完成后,我们尝试访问`http://localhost:5005/person`也可以拿到数据表示启动成功。接着我们在将打包好的项目,放到服务器的static文件下,static目录下,默认访问index.html文件。重启服务器 但是,这里存在的是,如果你是history,点击菜单是ok的,但是你刷新页面就会报错,因为你刷新,你的路径是会请求后台的,但是你的后台是没有对应的资源。 例如,你请求的是/home/news,你通过点击菜单只是路由的切换,不会发送网络请求,但是你一旦刷,这个就会去找我们启动的这台服务器,那么我们现在这台服务器只只支持一个person的get请求,所以会404。但是如果你使用hash就不会有这个问题。 如果解决,你可以使用一个第三方的类库 ``` npm i connect-history-api-fallback ``` 安装完成后,你在加入如下代码 ```javascript const express = require('express') // 解决history部署的404问题 const history = require('connect-history-api-fallback') const app = express() //一定要写在静态资源前面 app.use(history()) // 指定可以访问的资源路径 app.use(express.static(__dirname+'/static')) app.get('/person',(req,res)=>{ res.send({ name:'pop', age:18 }) }) app.listen(5005,(err)=>{ if(!err) console.log('服务器启动了') }) ``` 这样,他就会将你的资源映射回已经有的资源,而不会请求后台了。 但其实,用nginx也可以解决,因为他只是去请求了不存在的资源而已,我们只要用nginx把请求转发回来就没问题了。nginx来判断他是前端路由还是后端路由。 #### Vue 3