vue基础语法 初始化 1 2 3 4 5 6 var app = new Vue ({ el : '#app' , data : { message : 'Hello Vue!' } })
1 2 3 <div id ="app" > {{message}} </div >
el 为css 选择器,data 数据信息,数据信息可使用目标语法获取变量的值
模板语法 模板语法 1 2 3 4 5 {{xx}} 双大括号的语法获取变量的值 {{ message.split('').reverse().join('') }} 模板语法中可以写表达式
指令语法 v- 开头的是指令语法
v-text v-html 原样渲染html文本 v-once 一次执行,即使数据更新也不再更新 v-bind:id=”dynamicId” 绑定属性,可以是id 可以是其他的属性 v-bind:href=”url” 设置href属性 v-on:click=”doSomething” 绑定点击事件 v-bind:[attributeName]=”url” 绑定动态参数 v-cloak 当vue 没有初始化的时候此执行会存在html 上,一旦vue 渲染完毕,那么此执行将被移除,一般配置css 做vue 没有初始化完的显示样式。 v-pre 跳过节点的vue编译,当节点不需要处理任何vue 相关的编译的时候,可写此指令,那么vue 将不会遍历渲染此节点 语法缩写
v-bind缩写
:href=”url” v-bind:href 缩写 :[key]=”url” v-on 缩写
@click=”doSomething” @[event]=”doSomething” 数据绑定 单向数据绑定 非表单类的元素的值或属性使用单向数据绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 <div id="app" > <h2 v-text ="message" > </h2 > </div> <script > var vm = new Vue ({ el : "#app" , data :{ message : 'xx' } }) </script >
双向数据绑定 1 2 3 4 5 6 7 8 9 10 11 12 13 <div id="app" > <input type ="input" v-model:value ="message" /> {{message}} </div> <script > new Vue ({ el : "#app" , data :{ message : '' } }) </script >
双向数据绑定主要支持输入的表单元素做绑定。
input 绑定value值 textarea 绑定value值 checkbox 绑定一个数组 radio 绑定一个值 select 绑定一值,多选绑定一个数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <div id="app" > <form> <input type="input" v-model:value="name" /> <br/> <textarea v-model="info"></textarea> <br/> <input type="checkbox" id="ball" v-model="hobby" value="ball" v-model="hobby"> <label for="ball">打球</label> <input type="checkbox" id="eat" v-model="hobby" value="eat" v-model="hobby"> <label for="eat">吃</label> <br> <input type="radio" id="male" name="sex" v-model="sex" value="male" ></input> <label for="male">男</label> <input type="radio" id="female" name="sex" v-model="sex" value="female" ></input> <label for="female">女</label> <br/> <select name="age" v-model="age"> <option>1</option> <option>2</option> <option>3</option> <option>4</option> </select> </form> <br/> {{name}} <br/> {{info}} <br/> {{hobby}} <br/> {{sex}} <br/> {{age}} <br/> </div> new Vue({ el: "#app", data:{ name: '', info:'', hobby: [], sex: '', age: 1 } })
vue 原理 //TODO Object.defined
修饰符 修饰符可以对默认的行为做增强和限制。
修饰符有
.lazy 元素 change事件后才同步数据,input框中不会马上更新。 .number 输入的值转成数值类型 .trim 输入的值做trim处理 可以加到v-model 的后面 v-model.lazy=”xx” 对当前的数据绑定做限制和增强。
事件处理 使用 v-on 指令监听元素的事件。
v-on:click=”clickMethod”
简写: @click=”clickMethod”
@click="clickMethod('param')"
对应的触发的方法定义在Vue 实例的method上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <div id="app" > <button type ="button" @click ="btnClick('1')" > 安安</button > </div> Vue .config .productionTip = false new Vue ({ el : "#app" , data :{ }, methods :{ btnClick : function (param ){ console .log (param); } } })
当调用无参数时,可用event 获取原始事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <button @click="btnClick" > new Vue ({ el : "#app" , data :{ }, methods :{ btnClick : function (event ){ console .log (event); } } })
注意: methods 方法不要使用箭头函数,使用普通函数那么this 指向 vue对象,如果使用箭头函数 this就指向了window
事件修饰符 .stop 阻止向上冒泡 .prevent 阻止浏览器默认事件,比如a 标签的跳转,比如 form表单的刷新页面 .capture 捕获,使用此修饰符后将通过捕获触发事件。 页面元素是先由外向内 捕获,由内向外冒泡。 默认事件是由冒泡触发的,当使用此后,那么外部的元素将先触发,因为捕获是从外向内。可以用来控制 内外元素的事件的触发顺序。 .self 点击本元素触发 .once 只触发一次 .passive 无需等待js的执行结果,立即执行默认的浏览器行为,不会等待js执行完成后,比如滚动事件,不会因为监听滚动事件的js执行,导致滚动出现类型卡顿的现象。 按键修饰符 监听键盘指定按钮的触发函数或方法。
//别名监听 //按键code监听(不推荐,不同键盘的code可能有区别)
默认定义的一些键盘按钮符别名
.enter .tab .delete (捕获“删除”和“退格”键) .esc .space .up .down .left .right .ctrl .alt .shift .meta 计算属性 通过一个函数计算获得的一个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <div id="app"> <input v-model="firstName"> <input v-model="lastName"> {{fullName}} </div> new Vue({ el: "#app", data:{ firstName: '', lastName: '' }, computed: { fullName: function(){ return this.firstName + " " + this.lastName; } } })
同样的,计算属性中的function 不能用箭头函数,不然会影响this的指向。
计算属性是有缓存的,会将计算的结果存储在内部的一个值上,只有计算属性内部的依赖的值发生变化了,才会再次调用。
set 和get 支持set属性方法,如果只定义一个那么只有一个get
1 2 3 4 5 6 7 8 9 10 computed: { fullName: { get: function(){ return this.firstName + " " + this.lastName; }, set: function(newValue){ console.log(newValue); } } }
简写模式
1 2 3 4 5 6 7 computed : { fullName ( ){ return this .firstName + " " + this .lastName ; } }
监视属性 显示的监听属性的变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <div id="app" > <input v-model ="firstName" > </div > new Vue ({ el : "#app" , data :{ firstName : '' , lastName : '' }, watch : { firstName : function (newValue,oldValue ){ console .log (newValue,oldValue); } } })
简写模式 1 2 3 4 5 watch : { firstName (newValue ){ console .log (newValue); } }
后绑定监听函数 1 2 3 4 5 6 7 8 9 10 11 12 13 let vm = new Vue ({ el : "#app" , data :{ firstName : '' } }) vm.$watch('firstName' ,{ handler : function (newValue,oldValue ){ console .log (newValue,oldValue); } })
深度绑定 针对内部嵌套属性的监听,通过加引号的属性名进行绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let vm = new Vue ({ el : "#app" , data : { user : { firstName : '1' } }, watch : { 'user.firstName' : { handler : function (newValue, oldValue ) { console .log (oldValue, newValue); } } } })
computed 能够完成的功能 用watch 也可以,但是 watch 能做比computed 更加灵活的功能。
class 与 style 绑定 class 绑定 :class=”classVar” 对class 进行追加
:class=”classArr” classArr 为数组,追加多个class
:class=”{ active: isActive, ‘text-danger’: hasError }” isActive 是变量 hasError 是变量。当变量满足的时候生效。避免了class 混杂在vue js中。
style 绑定 v-bind:style=”{ color: activeColor, fontSize: fontSize + ‘px’ }” activeColor 和 fontSize 为变量
整个style为一个对象
v-bind:style=”styleObj”
styleObj:{ color:red, fontSize: 10px }
vue 管理的函数写普通函数,vue不管理的函数写监听函数,让this 指向vm
条件指令控制 语法 1 2 3 4 5 6 7 8 9 10 11 <div v-if ="" > </div > <div v-else-if ="" > </div > <div v-else > </div > <template v-if ="" > </template > <div v-show ="" > </div >
v-if 不满足条件直接将元素删除 v-show 不满足条件加css 隐藏属性,v-show 在频繁切换的时候性能好些 列表循环 用于遍历数组,对象等。
遍历示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 <ul> <li v-for ="(item ,index) in listData" :key ="'_'+index" > {{item}}-{{index}} </li > </ul> <div v-for ="itemValue,key in info" > {{itemValue}}-{{key}}</div > itemValue 是对应的值。 key是属性名 new Vue ({ el : "#app" , data : { listData : ['1' ,'2' ,'3' ,'4' ,'5' ], info :{ userName : '张三' , age : 10 } } }) `` `` > 在遍历数组的列表的时候 :key 尽量要指定,如果数据只是用于展示,那么key 可以使用索引或固定前缀+ 索引值。如果数据在列表中涉及到一些更新和编辑操作,最好使用数据本身的唯一标识,因为如果往数组前面加新的数据会有一定的性能问题或导致界面有问题。 ## 数据的更新监听 数据被vue管理和监听 1. new Vue 时 写在data的数据。2. 手动调用vue的api 动态添加要监听的数据。Vue .set (target,prop/index,value);vm.$set(target,prop/index,value); > 注意Vue .set 和vm.$set 只能动态绑定 data 内部的对象,不能直接在data 上追加属性。 这2 种方式可以对data中的数据进行监听达到响应式的效果,会为数据生成Observer 包装的对象,为监听的属性生成get 和set 方法。 需要注意的是如果要对数组元素进行监听,那么只有数组元素执行特定的方法后才能触发。底层对这些数组元素的方法进行了包装。 - push () - pop () - shift () - unshift () - splice () - sort () - reverse () > 注意:直接指定索引更新数据元素是无法更新的,因为不符合监听规则 datas[0 ] = {xx :xx} ## 过滤器 对数据进行过滤和处理转换的方法。 示例: `` `js <div id="app"> <div>{{info.userName | delSymbol | upperCase}}</div> </div> new Vue({ el: "#app", data: { info:{ userName: 'zhang@san', } }, filters:{ delSymbol: function(str){ return str.replace("@","-"); }, upperCase: function(str){ return str.toUpperCase(); } } }) ` `` ` 定义了2个过滤器。过滤器多次使用,前一个的结果为下一个的入参,成链式调用。 过滤器分为局部过滤器和全局过滤器。 定义在 vue 实例上的为局部过滤器。 > 过滤函数使用的时候也可以加参数,默认第一个参数一定是前一个的值或原始数据,第二个 为其他的参数: {{info.userName | delSymbol('44') | upperCase('22')}} ### 全局过滤器 ` `` js Vue .filter ('delSymbol' ,function (str ){ return str.replace ("@" ,"-" ); }) Vue .filter ('upperCase' ,function (str ){ return str.toUpperCase (); })
自定义指令 通过directives 可以自定义v- 指令。 比如定义一个小写转大写的指令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <div id="app" > <div v-upper ="info.userName" > </div > </div> let vm = new Vue ({ el : "#app" , data : { info :{ userName : 'zhang@san' , } }, directives :{ upper : function (element,binding ){ console .log (element); console .log (binding); element.innerText = binding.value .toUpperCase (); } } })
执行函数打印结果,第一个参数是真实dom ,第二个参数是数据绑定的相关信息。包含value值等。
注意:指令函数定义的时候为多字符的时候通过横线分割,而不能用驼峰法表示
完整的指令函数 上面的写法是简单的写法,下面的完成的函数方法,可根据具体的指令的行为写对应的实现函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let vm = new Vue ({ el : "#app" , data : { info :{ userName : 'zhang@san' , } }, directives :{ upper :{ bind : function (element,binding ){ }, inserted : function (element,binding ){ }, update : function (element,binding ){ } } } })
局部指令和 全局指令 和过滤函数一样自定义指令也可以分为局部指令和全局指令。 创建Vue实例的时候指定的是局部的指令,通过Vue 声明的是全局的。
全局指令定义 https://v2.cn.vuejs.org/v2/api/#Vue-directive
1 2 3 4 5 6 7 8 9 Vue .directive ('my-directive' , { bind : function ( ) {}, inserted : function ( ) {}, update : function ( ) {}, componentUpdated : function ( ) {}, unbind : function ( ) {} })
Vue 的生命周期 vue 从创建到注册到消耗的一系列行为都有许多的回调函数,可以利用这些钩子函数做一些逻辑或处理。
init Events&Lifecycle 初始化生命周期事件,数据代理还未开始。
beforeCreate 创建数据代理和数据检测之前。此时数据代理还未完成,此时的this 还是window,因为vm还未初始化完。
init Injections & reactivity 初始化数据代理 数据监测
created 数据代理和数据监测完成
beforeMount 页面还未挂载,页面上的dom还未交给vue管理
mounted 页面的dom是经过vue编译的dom,可以做一些初始化的操作。
beforeUpdate 数据是新的,页面是旧的还未来的及更新
updated 数据和数据保持了同步
beforeDestroy vm中的各个data ,methods 都是可用状态,即将要销毁,一般做vm卸载的消耗操作。此时操作数据不会触发变更,因为都要销毁了,更新也没用了。
destroyed 已经被消耗了
比较重要的是: mounted beforeDestroy 这2个钩子函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <div id="app" > <input type ="text" v-model ="userName" /> <button @click ="des" > 销毁</button > </div> let vm = new Vue ({ el : "#app" , data : { userName : 'zhang@san' }, methods :{ des ( ){ this .$destroy(); } }, beforeCreate :function ( ){ console .log ("beforeCreate" ); console .log (arguments ); }, created : function ( ){ console .log ("created" ); console .log (arguments ); }, beforeMount : function ( ){ console .log ("beforeMount" ); console .log (arguments ); }, mounted : function ( ){ console .log ("mounted" ); console .log (arguments ); }, beforeUpdate : function ( ){ console .log ("beforeUpdate" ); console .log (arguments ); }, updated : function ( ){ console .log ("updated" ); console .log (arguments ); }, beforeDestroy : function ( ){ console .log ("beforeDestroy" ); console .log (arguments ); }, destroyed : function ( ){ console .log ("destroyed" ); console .log (arguments ); } })
组件 一个页面由多个块结构组成,可以把把每个拥有功能和逻辑的块结构看做一个组件。 组件是实现应用中局部功能代码的资源的整合。
页面的三要素为 html ,js ,css ,而一个组件同样包含这三个要素。
非单文件组件 在一个页面中定义多个组件元素。
通过 Vue.extend 定义一个组件 通过 components 选项注册组件,组件之间可以灵活嵌套 通过组件的名字标签来使用组件 组件名定义原则
不能和已有html标签的名称重名 单个单词可以首字母小写或首字母大写。 比如组件 users 或 Users 多个单词使用横线分割的方式或使用驼峰 my-table 或 MyTable 多个组件的使用示例
1 2 3 4 5 <div id ="app" > <tops > </tops > <br /> <bottoms > </bottoms > </div >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 let tops = Vue .extend ({ name :"tops" , template :`<div>{{info}}</div>` , data : function ( ){ return { info : '这是头部信息' } } }) let bottoms = Vue .extend ({ name :"bottoms" , template :`<div>{{info}}</div>` , data : function ( ){ return { info : '这是底部信息' } } }) new Vue ({ el : '#app' , components :{ tops, bottoms } })
注意点1: 组件和Vue的配置项很多都是相同的,不同点有组件不能有el 选项。 并且组件的data 必须是一个函数,不能是对象。因为组件是可以多次复用的,每次都是一个新的数据对象。
组件上也可以注册其他的组件,组件是可以无限嵌套的。
组件注册方式 局部注册:通过components 注册的方式是局部注册,只能在注册的组件上使用此组件,未注册的组件不能使用。 全局注册: 可以直接会用Vue 对象的 Vue.component(‘组件名’,组件) 注册全局组件,在所有的组件中都能用全局组件。 单文件组件 单文件组件是比较常用的一种组件方式,在实际使用中都是这种方式。
组件名规范:
单个单词 首字母大写 多个单词 首字母大写并且驼峰 组件示例结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <template> <div > {{info}} </div > </template> <script > export default { data ( ){ return { info : 'xx' } } } </script > <style > div { color : red; } </style >
对于一个组件文件来说。
html文件必须是template 为根标签 script 中通过export 暴露组件 最下方写对应的css文件。 其他页面中通过import 关键字来引入组件 import xx from ‘./xx.Vue’ 获取指定的组件实例 $ref 在vue的实例对象上有一个 $refs 属性,可以利用此属性获取特定的组件实例。
1 2 <HelloWorld ref ="he" msg ="Welcome to Your Vue.js App" />
1 2 console .log ("refs" ,this .$refs );console .log ("refs" ,this .$refs .he );
将一个组件上定义一个ref属性并指定名称。那么这个组件将被注册到Vue 实例上。 通过this.$refs 可以获取所有标注了ref 的组件实例对象。用于获取特定的组件对象。
Component 和Vue 的关系 定义的组件是什么? 通过let vc = Vue.extend({}) 声明的一个组件vc.本质上是一个构造函数,是一个VueComponent的构造函数。这个构造函数是写在Vue源码内部的。
我们只能定义了这个构造函数的一些行为和选项。当在页面上使用了组件标签的时候,Vue会把一个一个的组件标签实例化成一个一个的组件对象。每个组件对象都是一个
新的且独立的。组件中的相关函数 watch methods 的函数的this 指向是 组件的实例对象。
为什么Component 和Vue的选项基本相同? 对于一个对象和对象的实例对象有这么的一个关系。
对象的实例对象的__proto__ 等于对象的原型对象。
对象能从原型对象上继承属性和方法。同样对象实例对象也能从对象的原型对象上继承属性和方法。
而对于Vue来说。
Vue 实例对象 的__proto__ 等于 Vue的原型对象。Vue的原型对象的__proto__ 等于Object的原型对象。
而对于VueComponent来说。
VueComponent 实例对象的__proto__ 等于VueComponent的原型对象。而VueComponent的原型对象的 proto 原本应该也指向Object的原型对象,但是指向了 Vue的原型对象的。
这样就做到了 VueComponent 实例对象 跟着原型链拥有了Vue对象的属性和方法。
VueComponent.prototype.proto === Vue.prototype
脚手架 https://cli.vuejs.org/zh/guide/
安装脚手架vue-cli 查看可用版本
1 npm view vue-cli versions --json
设置npm加速地址
1 npm config set registry https://registry.npm.taobao.org
1 2 3 4 5 安装最新版本 npm install -g @vue/cli //安装指定版本 npm install vue-cli@2.9.6
安装完毕后就可以使用
等命令了
创建一个Vue项目 进入要创建项目的目录,执行命令
选择创建项目的选项 完成自定义项目的创建。
https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
babel JavaScript编译器 eslint 语法规则和代码风格检查工具 创建的项目结构
babel.config.js babel编译器的配置
jsconfig.json 指定根文件和JavaScript语言服务提供的功能选项。
package.json 定义了项目的基本信息 包管理信息 和启动 编译脚本等。
vue.config.js vue.config.js 是一个可选的配置文件,能覆盖默认的项目webpack的配置
App.vue 就是一个普通的组件,将其定义为一个根组件,其他的组件是它的子关系。
main.js 服务启动的时候执行此js。引入了Vue.js 引入了 App.vue 组件,实例化了Vue对象,并且挂载到app组件中的一个元素上。
main.js
1 2 3 4 5 6 7 8 9 import Vue from 'vue' import App from './App.vue' Vue .config .productionTip = false new Vue ({ render : h => h (App ), }).$mount('#app' )
创建Vue的时候有一个render选项。此选项为模板解析器选项。 此处完整写法为
1 2 3 new Vue ({ render : createElement => createElement (App ), }).$mount('#app' )
此处引入的Vue依赖包并不是一个完整的vue.js 还是针对生成运行环境精简的vue.js,只包含运行环境所必须的一些代码。而针对编译过程中对模板的解析的代码已经去除了, 因为对模板的解析在编译的时候只需要编译一次解析就可以了。所以没必要在生产运行的时候每次都解析。
这里配置的render 表示通过 vue-template-compiler 对app这个组件中的模板做解析。
package.json.devDependencies 中会包含一个 vue-template-compiler 依赖。
覆盖配置 在项目目录下执行命令行
1 vue inspect > default_config.js
将把当前项目的默认的webpack 配置输出到一个文件中。
这些配置是当前项目的webpack 的默认配置。
如果要覆默认配置,那么就将自定义的项目的配置写到 vue.config.js 中。
配置参考文档 https://cli.vuejs.org/zh/config/#vue-config-js
1 2 3 4 5 6 7 8 9 10 const { defineConfig } = require ('@vue/cli-service' )module .exports = defineConfig ({ transpileDependencies : true , pages :{ index :{ entry : 'src/main1.js' , } } })
覆盖默认启动执行文件。
配置代理 配置网络请求代理主要解决的是网络请求后端的跨域问题。 跨域请求浏览器会报has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource
配置1,将所有的请求都转向这个地址请求. 1 2 3 4 5 6 7 8 9 const { defineConfig } = require ('@vue/cli-service' )module .exports = defineConfig ({ transpileDependencies : true , devServer :{ proxy : 'http://localhost:8090' } })
复杂配置,配置不同的请求方式指向那个地址 推荐 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const { defineConfig } = require ('@vue/cli-service' )module .exports = defineConfig ({ transpileDependencies : true , devServer :{ proxy :{ '/data' : { target : 'http://localhost:8090' , ws : true , changeOrigin : true }, '/foo' : { target : 'http://localhost:8091' , ws : true , changeOrigin : true } } } })
target 转发到那个地址 ws 是否支持websocket changeOrigin 默认是true ,为true的时候服务端的host 就是服务端本身的地址,而如果为false ,那么服务端收到的host是代理服务的地址。这个是改变了网络请求中的host信息,让服务端最代理做到无感知。 深入组件 props 向组件内传值 通过组件内的props 选项可以在组件标签上使用 :xx 对组件内的属性传值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <template> <div > 用户名称{{userName}} <br /> 用户年龄{{ageNum}} <br /> 性别:{{sex}} </div > </template> <script > export default { name : "UserInfo" , data ( ){ return { ageNum : 0 } }, props : { userName :{ type : String , required : true , }, age :{ type : Number , required : true , }, sex :{ type : String , default : "男" } }, mounted : function ( ){ this .ageNum = this ._props .age ; } } </script >
1 2 3 4 5 6 7 8 9 <UserInfo :userName="userName" :age="10" ></UserInfo > data ( ){ return { userName :"李四" } },
type (规定数据类型) - String 字符串 - Number 数字 - Boolean 布尔 - Array 数组 - Object 对象 - Date 日期 - Function 函数 - Symbol 独一无二的值(es6)
如果只需要接收数据而不做一些限制,可以直接使用数组接收 props: [“xx”,”xx”,”xx”]
props 的属性能直接读取到,但是不能修改,如果需要修改,需要把属性赋值到data上的一个属性上。props的数据优先级会更高一些。
mixin 混入 组件的data methods watch 等选项,如果有多个组件的部分选项是完全一样的,那么把这部分公共配置抽取出来,达到复用的效果,就是mixin 混入。
将data 数据部分抽成一个js 并通过export暴露
1 2 3 4 5 6 7 export const userInfo = { data ( ){ return { name : "张三" } } }
组件中引入此js, 并通过mixins 选项使用
1 2 3 4 5 6 7 import {userInfo} from '../mixin/datamixin.js' ========================== mounted : function ( ){ this .ageNum = this ._props .age ; } ,mixins :[userInfo]
组件中所有的选项都可以抽到混合中,混合中的数据和配置和本地的数据合并,如果本地已存在的属性,以本地为准。
全局混入 上面的混入是局部混入,也可以配置一个全项目可用的混入,全局混入,其实就是让Vue 配置一下mixin选项
1 2 3 4 import {xx} from '../mixin/xx.js' Vue .mixin (xx);
style 作用域 默认情况下 Vue组件中写的 <style> 标签内的样式,会被应用到所有的组件,而为了达到各个组件内的样式只对各自组件生效,那么需要对组件内的样式加作用域。
style标签上加一个scoped 即可。
加了scoped 后,为css加了一个限定区域,默认Vue的每个组件都有一个随机的data-v 标识,并且是不同的,这样通过为组件内的样式也加这个限制达到只对组件内的元素有效果。
子组件和父组件通信 通过props传方法通信 父组件通过props传给子组件一个方法,子组件调用父组件的方法并带上参数,完成子组件对父组件的传值。
父组件定义一个method,并且支持传参数 1 2 3 4 5 6 7 8 methods : { updateCount (v ){ this .count = v; } }
子组件中声明可以接收函数的props 1 2 3 4 5 6 7 props : { updateCount :{ type :Function } }
父组件将自己的function 传递给子组件 1 2 <UserInfo :updateCount="updateCount" :userName="userName" :age="10" ></UserInfo >
子组件方法中可以调用父组件的函数 1 2 3 4 5 methods :{ upCount : function ( ){ this .updateCount (this .count ) } },
本质上就是父函数的引用传递给了子组件。
通过自定义事件通信 在子组件上绑定和监听子组件的自定义事件,子组件触发自定义事件让父组件接收到。
父组件使用 v-on 监听事件 或使用 $refs api的方式监听事件 子组件使用 $emit 触发事件 1 2 3 4 5 6 7 8 9 10 11 12 <UserInfo v-on :countSet="updateCount" ></UserInfo > methods :{ upCount : function ( ){ console .log ("==>" ); this .$emit("countSet" ,this .count ) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 mounted :function ( ){ this .$refs .userInfo .$on("countSet" ,this .updateCount ) }, `` `` ##### 解除事件 1. this .$off("eventName" ) this .$off(["eventName1" ,"eventName2" ])2. 组件消耗的时候也会解绑所有事件 this .$destory()### 全局总线 子父组件之间可能完成相互之间的通信,但是为了达到任意组件之间可以方便的进行通信。需要一个中间角色,这个中间角色是全局总线。 全局总线根本上就是一个绑定到Vue .prototype 上的一个属性,因为Vue component 也是继承Vue 的原型属性的,所以绑定到Vue 的原型对象上的属性组件和Vue 都是可以使用的。 而在Vue 的原型上绑定一个特定的Vue 实例,让此Vue 实例触发事件,其他的任意Vue 实例和Vue 组件都从原型属性中获取这个绑定的实例最终监听到事件。 1. 注册全局总线将new 出来的Vue 实例绑定到Vue 的原型上 `` `js new Vue({ render: h => h(App), beforeCreate:function(){ Vue.prototype.$bus = this; }, }).$mount('#app')
组件监听事件和销毁事件 1 2 3 4 5 6 7 8 9 mounted :function ( ){ this .$bus .$on("countSet" ,this .updateCount ) }, beforeDestroy ( ){ this .$bus .$off("countSet" ) }
触发事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 methods :{ upCount : function ( ){ this .$bus .$emit("countSet" ,this .count ) } } `` `` ### $nextTick 当一个函数内的所有代码执行完毕后,才会去渲染页面。 那么涉及到在一个函数内需要改了数据 然后 页面渲染完毕 再获取页面的相关信息的操作的时候将无法获取到。 $nextTick 回调函数是当前函数执行完,并且页面渲染完毕后才执行。 `` `js divShowGo(){ //divShow = true 后页面元素才会存在 this.divShow = true; this.$nextTick(()=>{ let inp = document.getElementById('inp'); console.log(inp); }) }
插槽 插槽的作用是在组件上定义一些可以放置数据的点,方便数据的展示。
普通插槽 在子组件的组件标签之间定义一些数据,默认情况下子组件中将不会出现,但是如果子组件定义了一个 slot 标签,那么组件标签之间的数据将被插入在slot位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <UserInfo > datainfo <UserInfo > <template > <div > <slot > </slot > </div > </template > `` `` 主要用于向子组件传递信息。 #### 具名插槽 默认普通插槽只能向子组件传递一个插槽信息,如果需要传递多个不同的信息,那么定义多个插槽并起个名字,能够区分,也叫做具名插槽。 - 子组件的多个slot 都有一个name 属性 - 父组件向子组件标签内写信息的时候加个slot 属性,属性的值为插槽的名称 `` `js <template> <div> <slot name="sl1"></slot> <slot name="sl2"></slot> </div> </template> ============================================== <button @click="divShowGo" slot="sl1" >按钮点击</button> <div slot="sl2">info</div> ` `` ` #### 作用域插槽 作用域插槽可用于子组件的值向外部传递。 > 一般用于处理子组件内数据的遍历 ` `` js <template> <slot :userInfo ="userInfo" > </slot > </template> <UserInfo > <template scope ="userInfo" > {{userInfo.userInfo.age}} </template > </UserInfo >
作用域插槽必须用template 包裹,并且指定scope取那个变量
插件 在创建Vue实例的时候,可以声明使用一些Vue插件,插件中定义了Vue的一些全局配置,一些自定义指令等等信息。
自定义插件 创建 plugins.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import {userInfo} from './mixin/datamixin.js' export default { install : function (Vue ){ Vue .mixin (userInfo); Vue .directive ("upper" ,function (element,binding ){ console .log (element); console .log (binding); element.innerText = binding.value .toUpperCase (); }); Vue .prototype .showMsg = ()=> { alert ("信息" ); } Vue .config .productionTip = false } }
使用插件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import Vue from 'vue' import App from './App.vue' import plugins from './plugins' Vue .use (plugins)new Vue ({ render : h => h (App ), }).$mount('#app' ) `` `` > 针对一些初始化对Vue 的一些配置和不同的定义可以放到不同的插件中。在执行的时候会按照顺序执行不同的插件中的代码。 ## Vuex Vue 官方的状态管理插件,用于多组件间的数据共享和访问。### vuex的模式 ;const actions = {};const mutations = {};const state = {};export default new Vuex .Store ({ actions, mutations, state })
将vuex应用到vue实例创建的位置 1 2 3 4 5 6 7 8 9 new Vue ({ render : h => h (App ), store, beforeCreate :function ( ){ Vue .prototype .$bus = this ; }, }).$mount('#app' )
基本使用 当注册了vuex 后,通过this.$store 可以获取相关的信息和操作。 获取state信息 this.$store.state
触发一个action函数 1 $store.dispatch('actionName',value);
提交一个 mutations 1 $store.commit('mutationName',value);
如果没有业务逻辑,可以绕过actions,直接通过commit提交,触发数据修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex );const actions = { addNumber (content,value ){ content.commit ('add' ,value); }, subtractNumber (content,value ){ content.commit ('sub' ,value); } }; const mutations = { add (state,value ){ console .log ("add" ,state,value,state.dataCount ); state.dataCount += value; }, sub (state,value ){ state.dataCount -= value; } }; const state = { dataCount : 0 }; export default new Vuex .Store ({ actions, mutations, state })
getter 配置 getter很像计算属性,通过函数的方式定义一个属性。
定义一个gettter并放到store的构造器上。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const getters = { dataCountFormat ( ){ return state.dataCount + "¥" ; } } export default new Vuex .Store ({ actions, mutations, state, getters })
直接通过$store.getters 获取对应的getter属性。 mapState,mapGetter,mapAction mapState 将store的属性映射到计算属性上。
写法1,传个数组。数组内定义要映射的属性。并且计算属性和原来的属性一致 1 2 3 4 5 6 import { mapState } from 'vuex' computed :{ ...mapState (['dataCount' ]) },
写法2,对象形式。支持一些灵活的定义。 1 2 3 4 5 6 7 8 9 import { mapState } from 'vuex' computed :{ ...mapState ({ c : 'dataCount' }) },
mapGetter 将getter的属性映射到计算属性上。
1 2 3 4 5 import { mapState ,mapGetters} from 'vuex' ...mapGetters (['dataCountFormat' ]) ...mapGetters ({format :'dataCountFormat' })
从vuex 中引入mapGetters,然后指定哪个属性进行映射即可
mapAction mapAction是一个函数,所以只能映射到具有函数功能的选项部分,只能映射到methods
1 2 3 4 5 import { mapState ,mapGetters,mapActions,mapMutations} from 'vuex' ...mapActions (['addNumber' ,'subtractNumber' ]),
需要注意的是当调用mapactions的方法的时候,要达到传参的目的,还需要其他的方法来调用它。
mapMutations mapMutations 也是映射到 methods 上。
1 2 3 4 5 6 7 import { mapState ,mapGetters,mapActions,mapMutations} from 'vuex' ...mapMutations ({addmu :'add' ,submu :'sub' }) ...mapMutations (['add' ,'sub' ])
Vuex 模块化 针对不同的模块或类型将不同的数据进行分类,方便更好的管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex );const countInfo = { namespaced :true , state :{ dataCount : 0 }, actions :{ addNumber (content,value ){ content.commit ('add' ,value); }, subtractNumber (content,value ){ content.commit ('sub' ,value); } }, mutations :{ add (state,value ){ console .log ("00" ,state); state.dataCount += value; }, sub (state,value ){ state.dataCount -= value; } }, getters :{ dataCountFormat ( ){ return countInfo.state .dataCount + "¥" ; } } } const userInfo = { namespaced :true , state :{ userName : '' }, actions :{ setUserName (content,value ){ content.commit ('set' ,value); }, }, mutations :{ set (state,value ){ state.userInfo .userName = value; }, }, getters :{ dataCountFormat ( ){ return userInfo.state .dataCount + "¥" ; } } } export default new Vuex .Store ({ modules :{ countInfo, userInfo } })
将每个模块进行分类,然后统一注册到 modules
通过map映射时
1 2 3 4 5 computed :{ ...mapState ('countInfo' ,['dataCount' ]), ...mapState ('userInfo' ,['userName' ]), ...mapGetters ('countInfo' ,{dataF :'dataCountFormat' }) },
mapActions
1 2 3 4 5 methods : { ...mapActions ('countInfo' ,['addNumber' ,'subtractNumber' ]), ...mapMutations ('countInfo' ,{addmu :'add' ,submu :'sub' }) },
使用map 映射的时候,只要首先第一个参数是模块名称即可。
如果使用$store来操作vuex,可能有一些区别。
1 $store.state .countInfo .dataCount
$store.state.模块名.属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $store.getters ['countInfo/dataCountFormat' ] $store.dispatch ('countInfo/dataCountFormat' ,value) $store.commit ('countInfo/dataCountFormat' ,value) `` `` > 通过getter dispatch commit 操作的时候,拼接模块名的时候用 模块名 + 斜线 ## 路由 ### 路由安装 vue2安装对应的router3版本 vue3安装对应的router4版本 `` `bash npm i vue-router@3
路由使用 src下创建router文件夹和index。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import VueRouter from 'vue-router' import Index from '../pages/Index' ;import Catagory from '../pages/Catagory' ;import Blog from '../pages/Blog' ;import Detail from '../pages/Blog-Detail' ;export default new VueRouter ({ routes :[ { path : '/index' , component : Index , },{ path : '/catagory' , component : Catagory } ] })
实例化VueRouter 内部配置path 和组件的对应关系
main.js use 路由插件,并且将router.js 文件放到vue实例上。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import Vue from 'vue' import App from './App.vue' import plugins from './plugins' import store from './store/index.js' import VueRouter from 'vue-router' import router from './router' Vue .use (plugins)Vue .use (VueRouter )new Vue ({ render : h => h (App ), store, beforeCreate :function ( ){ Vue .prototype .$bus = this ; }, router : router }).$mount('#app' )
Vue.use(VueRouter) router: router 注意: Vue.use 的是’vue-router’,放到构造中的是自定义的router文件
通过标签使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <div class ="topNav" > <ul > <li > <router-link to ="/index" > 首页</router-link > </li > <li > <router-link to ="/catagory" > 菜单</router-link > </li > </ul > </div> <div class ="content" > <router-view > </router-view > </div >
router-link 标签点击后触发路由切换 router-view 对应的路由的组件将显示到此位置,被切换掉的组件将被执行消耗,会触发自身的销毁生命周期函数 绑定路由的组件可以通过自身 this.$route 获取自身的路由。各个组件独立。所有组件都可以获取一个公共的 router this.$router 获取。
1 2 3 4 mounted ( ){ console .log ("mounted" ,this .$route ); console .log ("mounted" ,this .$router ); }
路由嵌套 路由上支持配置层级的子路由的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export default new VueRouter ({ routes :[ { path : '/index' , component : Index , },{ path : '/catagory' , component : Catagory }, ,{ path : '/blog' , component : Blog , children :[ { path : 'detail' , component : Detail } ] } ] })
1 2 3 4 5 6 7 8 9 10 11 <template> <div > <div > 博客信息 <router-link to ="/blog/detail" > 博客详情</router-link > </div > <div > <router-view > </router-view > </div > </div > </template>
多级路由如果使用路径的话,必须写完整路径
路由命名 给路由起一个名字,那么在定义router-link 跳转到哪个路径的时候,可以直接使用名字还不是路径。
1 2 3 4 5 6 { name :'index' , path : '/index' , component : Index , }
1 <router-link :to="{name:'index'}" >首页</router-link>
注意: to 前面要加: 才能后面的当做结构解析,不然只是当成普通字符串了。就可以直接跳到对应名称的路由了.
路由传参 通过query参数传参。 在router-link 上面加query参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <ul> <li v-for ="(item,k) in blogs" :key ="k" > <router-link :to ="{ path:'/blog/detail', query:{ title:item.title, content: item.content, author:item.author }}" > {{item.title}}</router-link > </li > </ul>
遍历文章列表,传递不同的参数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <template> <div > <h2 > {{$route.query.title}}</h2 > <hr /> <div > {{$route.query.author}}</div > <br /> <div > {{$route.query.content}}</div > </div > </template> `` `` 目标组件使用$route.query 来获取对应的数据。 > 通过query 传参,对应的参数会显示在地址栏上。 #### 通过params参数传参。 通过params 参数传递和query 差不多。 `` `js <ul> <li v-for="(item,k) in blogs" :key="k"> <router-link :to="{ name:'detail', params:{ title:item.title, content: item.content, author:item.author }}" >{{item.title}}</router-link> </li> </ul>
通过params 传参 不能使用path指定路由,必须 使用 name属性。
1 2 3 4 <h2>{{$route.params .title }}</h2> <hr /> <div > {{$route.params.author}}</div > <div > {{$route.params.content}}</div >
params 和 query 传参的区别
query 传参会显示在浏览器头部,而params不会 query 可以使用 路由路径或name 指定跳转的路由,而params 只能使用路由的名称 query 的参数在 $router.query 中 params 参数在 $router.params 中。 路由的props 路由选项上支持配置props属性,可以为boolean , 结构 ,函数
props :true 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ,{ name : 'blog' , path : '/blog' , component : Blog , children :[ { name : 'detail' , path : 'detail' , component : Detail , props : true } ] }
当配置路由的props 为true的时候,传递给此路由对应的组件的param 数据将可以props的形式传递。
注意:只能处理params 数据,queyr数据不能这么操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <template> <div > <h2 > {{title}}</h2 > <hr /> <div > {{author}}</div > <div > {{content}}</div > </div > </template> <script> export default { props : ['title' ,'author' ,'content' ] }
props 函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 children :[ { name : 'detail' , path : 'detail' , component : Detail , props : function ($route ){ return {title :$route.query .title , author :$route.query .author } } } ]
定义的函数内部的参数为$route ,而$route 中可以获取 query 和params ,所以可以利用这个函数做自定义的一些行为,比如传递query的prop参数。
写死的props数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 { name : 'detail' , path : 'detail' , component : Detail , props : {a :'b' ,c :'d' } } } `` `` props 可以直接写成对象结构的形式。props可以直接获取这里定义的数据。 ### router-link replace属性 默认情况下router-link 标签是没有replace属性的。当完成路由的切换后 ,可以通过浏览器的前后切换记录。 路由的切换被放到了浏览器的记录中。 而如果 router-link 上加了一个replace属性后,那么此切换将不会被记录到浏览器的历史记录上,而是替换了上一个浏览器记录。 `` `js <router-link replace :to="{name:'index'}">首页</router-link>
编程式路由 说白了,通过api的方式控制路由跳转。 核心就是几个api
$route.push 等同于
1 2 3 4 5 6 7 8 $route.push ({ name : 'xx' , params : { id : id } })
$route.replace 等同于
1 2 3 4 5 6 7 $route.replace ({ name : 'xx' , params : { id : id } })
$route.forward() 浏览器前进按钮一次
$route.back(); 浏览器后退按钮一次
$route.go(flag) flag 大于0 浏览器前进,flag小于0 浏览器后退
路由缓存组件 默认情况下路由的切换伴随着对应的组件的消耗,而针对频繁切换的场景,希望组件能够缓存起来而不是销毁。
可以在指定那些组件不马上销毁。
1 2 3 4 5 6 7 8 <keep-alive include="Catagory" > <router-view > </router-view > </keep-alive> ====================== <keep-alive include ="['Catagory']" > <router-view > </router-view > </keep-alive >
注意: include 的是组件名而不是路由名
注意: 未加include的组件会触发 beforeDestroy , destroyed 生命周期函数,而添加了的不会触发,只会触发beforeDestroy 函数。
activated() 和 deactivated() 函数 activated() 和 deactivated()是2个路由组件的生命周期函数。
注意:前提是组件被加入了缓存 才会触发此2个函数,如果未使用组件 缓存是不会触发的。
在被缓存的组件被切换和切走的时候触发。
如果只有一个组件加入缓存也不会触发,需要大于等于2个组件的时候才触发
路由守卫 路由守卫是路由切换的时候执行一些方法,可以通过路由守卫控制路由的行为和做一些操作。
全局路由守卫 通过VueRouter 的 beforeEach 和 afterEach 定义前置路由守卫和后置路由守卫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const router = new VueRouter ({ routes :[ { name :'index' , path : '/index' , component : Index , meta :{auth :true } },{ .....===== router.beforeEach ((to,from ,next )=> { console .log (to.meta ); next (); }) router.afterEach ((to,from )=> { console .log (to); }) export default router;
每个route定义上可以添加meta信息
前置路由守卫只要不调用next 函数,路由组件就不会执行切换。 后置路由是切换完毕后执行,处理一些路由切换完毕后的事情。
独享路由守卫 可以为每个路由配置定义独自的前置路由守卫(没有后置的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 children :[ { name : 'detail' , path : 'detail' , component : Detail , beforeEnter (to,from ,next ){ console .log (to); next (); }, props : function ($route ){ return {title :$route.query .title , author :$route.query .author } } } ]
当同时配置了全局路由守卫和独立路由守卫后,执行顺序是首先执行全局的,然后执行独立的
组件内路由守卫 组件内的守卫在组件内的2个函数beforeRouteEnter beforeRouteLeave
beforeRouteEnter 当前组件要执行路由切换之前调用,如果不调用next 将不完成切换到当前组件 beforeRouteLeave 当要切换到其他路由的时候调用,如果不调用next 将不切换到其他组件 最大的特点是既可以控制进来此组件,也可以控制离开此组件
1 2 3 4 5 6 7 8 9 10 11 12 13 <script> export default { beforeRouteEnter (to,from ,next ){ console .log ("beforeRouteEnter" ); next (); }, beforeRouteLeave (to,from ,next ){ console .log ("beforeRouteLeave" ); next (); } } </script>
路由的工作模式 hash 即地址栏 URL 中的 # 符号后面的信息 history 地址栏没有无关的信息,利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。 默认情况下是hash模式,也可以通过配置修改此选项。
1 2 3 4 const router = new VueRouter ({ mode : 'history' , })
当使用hash模式的时候前端能够处理404 的问题。而如果是history 如果存在不存在的页面需要后端进行处理。