vue入门
vue渐进式框架:
渐进式意味着你可以将Vue作为应用的一部分嵌入其中,带来更丰富的交互体验。或者如果希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统。比如Core+Vue-router÷ Vuex,也可以满足各种各样的需求。
MVVM:
1、数据与界面分离
<div id='app'> <h2>{{message}}</h2> </div> <ul id='app2'> <li v-for='item in datas'>{{item}}</li> </ul> <script> let app = new Vue({ //options el: '#app', //挂载绑定元素 data: { //绑定数据,实时更新,响应式,更有利于更新从服务器获取的数据 message: 'hello' } }) let app2 = new Vue({ el: 'ul', data: { datas: [1, 2, 3] //服务器增删数据,自动修改 } })
data需要说明一下:组件的高复用性导致其可能在多个页面使用,需要将data相互隔离,一般组件内的data需要作为函数return包装,为了防止污染全局。若为对象的data,不包裹,会造成全局污染
2、生命周期:诞生到消亡的整个过程,与其中间各个阶段的操作(可以提取其回调函数执行一系列操作)(钩子函数)
3、基本语法:
3.1:插值(文本赋值):mustache(双大括号)、v-once、v-html、v-text(不常用)、v-pre、v-cloak
<!-- v-cloak:vue解析前与解析后 --> <div id='app' v-cloak> <!-- mustache --> <h2 v-once>{{message + loc}}</h2> <h2>{{num*2}}</h2> <h2 v-html='url'></h2> <h2 v-text='message'></h2> <h2 v-pre>{{message + loc}}</h2> <h2>{{message + loc}}</h2> </div> <script> let app = new Vue({ //options el: '#app', //挂载绑定元素 data: { //绑定数据,实时更新,响应式,更有利于更新从服务器获取的数据 message: 'hello', loc: 'world', num: 100, url:'<a href="http://www.baidu.com">百度一下</a>' } })
3.2:动态绑定(属性赋值)。v-bind(语法糖 :)
<div id='app'> <!-- v-bind给属性绑定获取的数据 --> <img v-bind:src="img" alt=""> <!-- :为其语法糖 --> <a v-bind:href="url">百度一下</a> </div> <script> let app = new Vue({ el: '#app', data: { img: 'http://bpic.588ku.com/element_origin_min_pic/16/10/29/2ac8e99273bc079e40a8dc079ca11b1f.jpg', url: 'www.baidu.com' } })
3.2.1:v-bind绑定class,可以另外定义其他class,最后会合并
使用对象语法,class内以键值对出现,末尾为boolean: {key: value}
使用数组语法,用得比较少,因为不能定状态,何不将其放在别的class内
<div id='app'> <a :href="url" class="title" :class="{red:isRed,black:isBlack}">百度一下</a> <a :href="url" class="title" :class="getColor()">百度一下</a> <button v-on:click='turnColor'>变色</button> </div> <script> let app = new Vue({ el: '#app', data: { url: 'www.baidu.com', isRed: true, isBlack: false }, methods: { turnColor: function () { //取反即可 this.isRed = !this.isRed, this.isBlack = !this.isBlack }, getColor: function () { return { red: this.isRed, black: this.isBlack } } } })
3.2.2:v-bind绑定style。也是对象语法、数组语法
<div id='app'> <h2 :style="{color:'red',fontSize:'15px'}">hello</h2> <h2 :style="{color:'red',fontSize:finalSize}">hello</h2> <h2 :style="getStyle()">hello</h2> <h2 :style="[baseStyle]">hello</h2> </div> <script> let app = new Vue({ el: '#app', data: { finalSize: "50px", baseStyle: { color: 'red', fontSize: '50px' } }, methods: { getStyle: function () { return { color: 'red', fontSize: this.finalSize } }, } })
3.2.3:计算属性(也可以理解为methods的语法糖,但是比methods高效),主要是为了展示某些数据,让其名称更明确,更利于理解
<div id='app'> <h2>{{message+loc}}</h2> <h2>{{getMsg()}}</h2> <h2>{{Msg}}</h2> </div> <script> let app = new Vue({ //options el: '#app', //挂载绑定元素 data: { //绑定数据,实时更新,响应式,更有利于更新从服务器获取的数据 message: 'hello', loc: 'world' }, methods: { getMsg: function () { return this.message + this.loc } }, computed: { Msg: function () { return this.message + this.loc } } })
计算属性的本质是getter和setter,所以完整的计算属性应该为
Msg2:{ //一般来说set不需要实现,计算属性主要为只读属性,不可写,所以一般省略set,再简写,即为上面的Msg set: function(){ }, get: function(){ return this.message + this.loc } },
计算属性和methods的本质区别:缓存。若要做重复操作,使用methods会重复调用,而计算属性会缓存计算内容,只执行一次
3.2.4:事件监听:v-on(语法糖@)
v-on参数传递:如果不传参,可以不写小括号;如果要默认传事件参数,在调用时实参不加括号,但是在执行代码内形参为e;如果需要传参,还需要事件,则$event,不可简写,必须是$event
<div id='app'> <button @click=onClick>点击</button> //不加参数 <button @click=onClick2(10,$event)>点击</button> </div> <script> const app = new Vue({ //options el: '#app', //挂载绑定元素 methods: { onClick(e) { console.log(e); }, onClick2(a,e){ console.log(a,e); } } })
v-on修饰:
.stop-调用event.stopPropagation(),停止冒泡;.prevent-调用event.preventDefault(),阻止默认行为;;.native -监听组件根元素的原生事件;.once -只触发一次回调;.capture事件捕获;.self自身内部;.passive立刻触发,比如配合scroll使用的时候,不必等到scroll完成才调用,可以大幅度提高移动端的性能。
<div id='app'> <div @click=divClick> <!-- stop阻止冒泡 --> <button @click.stop=onClick>点击</button> </div> <form action="baidu.com"> <!-- prevent阻止默认行为,如submit的跳转 --> <input type="submit" name="" id="" @click.prevent='submitClick' value="提交"> </form> </div> <script> const app = new Vue({ //options el: '#app', //挂载绑定元素 methods: { onClick(e) { console.log(e); }, divClick() { console.log('盒子被点击了'); }, submitClick() { console.log('点击提交'); } } })
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击
3.2.5:条件判断v-if、v-else-if、v-else,vue中可以根据值的状态在dom中渲染或销毁(不出现该标签)元素或组件。可以作为判断显示
<div id='app'> <h2 v-if='score>90'>优秀</h2> <h2 v-if='score>80'>良好</h2> <h2 v-if='score>60'>及格</h2> </div> <script> const app = new Vue({ //options el: '#app', //挂载绑定元素 data: { score: 70, } })
3.2.6:v-show,是否渲染。与v-if的区别:如果false,v-if下dom中不存在该元素,而v-show只是将其display设为none,不占位。所以当需要频繁切换时,使用v-show,一次切换可用v-if。
注意:v-show不支持 <template>
元素,也不支持 v-else
3.2.7:循环:v-for,主要用来遍历数组。如果需要索引 v-for=(item,index) in item。index即为原数组索引。使用v-for时,最好给对应的元素或组件添加上key属性,主要是为了高效的更新虚拟dom。vue内的虚拟dom和diff算法
<div id='app'> <ul> <li v-for='item in movie' :key='item'>{{item}}</li> //有key时,优先对应内容,提高插入等操作的效率,这里的key值有要求,不能随便写index,应该用唯一标识,item或者id <li v-for='(item,index) in movie'>{{index}}.{{item}}</li> //索引与数 <li v-for='item in info'>{{item}}</li> //遍历,输出各个值 <li v-for='(value,key,index) in info'>{{index}}.{{key}}.{{value}}</li> //索引,键值 </ul> </div> <script> const app = new Vue({ //options el: '#app', //挂载绑定元素 data: { movie: ['银河', '异形'], info: { id: 1, name: 'jacky' } } })
如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
3.2.8:检测数组更新。由于vue是响应式,所以以下数组操作会触发视图更新:push()添加到最后,pop()删除最后一个,shift()删除第一个,unshift()数组最前面添加元素,splice()添加、删除、替换,sort()排序,reverse()翻转,set()修改值。有的不会触发更新,比如直接根据下标替换内容:this.arr[0] = 修改后的值,不会更新
3.2.9:v-model表单绑定:在input / textarea/select内绑定属性,实现与data的双向绑定:data内容修改,input其值修改;input其值修改,data内容修改。
区别于一般的单向响应式,如mustache,或者input的v-bind:value
双向绑定v-model思路:首先view获取data用value等v-bind方式显示,然后再添加事件,如v-on等event事件获取输入内容,改变data数据
<div id='app'> <!-- <input type="text" v-model='message'> --> <input type="text" :value='message' v-on:input='valueChange'> <!-- <input type="text" :value='message' @input = '$event.target.value'> --> <p>{{message}}</p> </div> <script> const app = new Vue({ //options el: '#app', //挂载绑定元素 data:{ message: '你好' }, methods: { valueChange(event){ this.message = event.target.value } } })
其他的如绑定radio(单选)、checkbox(复选,多个值可以放在一个数组里)、select等输入值
v-model修饰:lazy(相当于防抖)、number(类型改成number型)、trim(去除左右空格)
v-model本质只是个语法糖,监听用户输入通过响应式更新数据,这里的数据指的是Vue实例的数据,也就是data,会忽略表单的value、checked、selected等初始值
3.2.10:值绑定:当所需要选择的内容不确定个数时,v-for遍历动态数组,不需要写死。其实就循环遍历后再绑定值给input
<div id="app"> <label v-for='item in oHobbies' > <input type="checkbox" :value='item' v-model='hobbies'>{{item}}</br> </label> <p>{{hobbies}}</p> </div> <script> const app = new Vue({ //options el: '#app', //挂载绑定元素 data: { hobbies: [], oHobbies: ['篮球', '乒乓球', '羽毛球', '足球'] }, })
3.2.11:v-once、v-html
4、组件化:将一个页面拆分成一个个小功能模块,每个块完成属于自己的部分独立功能,便于后期管理维护
在vue中,可以构建独立可重复的小组件来构建应用,任何应用都会被抽象成一颗组件树。
三步骤:创建组件构造器 Vue.extend()、注册组件 Vue.component()、使用组件Vue实例。
4.1:全局组件:通过Vue.component()注册,可以在多个Vue实例内使用,
局部组件:在指定Vue实例内注册使用
<div id='app'> <mycpn></mycpn> </div> <script> const cpnConstructor = Vue.extend({ template: `<div> <p>hello</p> </div>` }) //全局组件,意味着可以在多个Vue实例中使用 // Vue.component('my-cpn', cpnConstrucstor) const app = new Vue({ //options el: '#app', //挂载绑定元素 components: { //cpn使用组件时的标签名 mycpn: cpnConstructor } })
4.2:父子组件:包含关系,在父组件内注册子组件,并在template引入
const cpnC1 = Vue.extend({ template: `<div> <p>hello1</p> </div>` }) const cpnC2 = Vue.extend({ template: `<div> <p>hello2</p> <cpn1></cpn1> </div>`, components: { cpn1: cpnC1 } }) const app = new Vue({ //options el: '#app', //挂载绑定元素 components: { cpn2: cpnC2 } })
4.3:语法糖(推荐用法)把extend的内容放在注册Vue.component内
<div id='app'> <cpn1></cpn1> <cpn2></cpn2> </div> <script> //全局组件语法糖 Vue.component('cpn1', { template: `<div> <p>hello1</p> </div>` }) const app = new Vue({ el: '#app', //局部组件语法糖 components: { cpn2: { template: `<div> <p>hello2</p> </div>` } } })
4.4:模版分离:将模版写入<script type='text/x-template' id='cpn'></script> / <template id='cpn'></template>标签内
4.5:组件访问Vue实例对象数据:组件内创建data函数,而不是data对象,可以返回一个对象:必须是一个函数是因为让Vue每个组件对象都范湖一个新的对象,如果是同一个对象,组件在多次使用后会出现问题。举个例子,计数器组件内的counter,如果不是返回新对象,当我创建三个计数器时会共享这个counter,导致三个一起++或者--,连锁反应
Vue.component('cpn1', { template: ` <div> <p>{{title}}</p> </div>`, //组件内放置data,但是这个data是函数,不是对象 data() { return { title: 'abc' } } })
4.6:父子组件通讯:大组件进行网络请求,获取全部数据后将部分数据传递给小组件
4.6.1:通过props,夫向子组件传递数据:props数组形式、对象形式(可以做类型验证),props为单向下行绑定,不能往上传,防止数据混乱。每次父级数据变化,props传下去的子也会变化
4.6.2:通过事件,子向父组件发送消息:子组件$emit('方法',参数)发射事件给父组件,父组件v-on监听事件
4.7:父子组件访问方式:
4.7.1:父对子:$children:返回数组,获取时需要指定数组下标,即第几个子组件,所以会有问题:真正开发的时候不知道所需要的下标是第几个,所以使用$refs
$refs:指定子组件标签内添加 ref 值(id名),在$refs后添加其名称访问指定子组件
4.7.2:子对直系父:$parent。但是为了避免耦合,一般不这样做
4.7.3:访问根组件:$root,根部Vue实例
4.8:非父子组件通信,Vue2.x提供中央事件总线,也就是一个中介来解决。但是这个方案不如直接使用Vuex
4.9:插槽slot(匿名插槽):组件提高扩展性,让用户决定展示什么,比如一个底板内分为三个部分,我可以展示这三个,也可以那三个,还可以混搭,反正有三个插槽
抽取共性,保留不同:共性抽取到组件中,不同暴露为插槽:模版指定位置暴露插槽<slot></slot>,根组件内插入需要定制内容;也可以指定默认slot,根据需要修改。
4.9.1:具名插槽:一个模版可能有多个插槽,给每个插槽指定名称,插入的时候可以指定位置
//具名插槽 <div id='app'> <cpn1><button slot="top">插槽按钮</button></cpn1> </div> <template id="cpnC1"> <div> <slot name='top'>顶</slot> <p>------------</p> <slot name='bottom'>底</slot> </div> </template>
4.9.2:编译作用域:父组件模版的所有东西(数据)都会在父级作用域内编译,子组件模版的所有东西(数据)都会在子级作用域内编译,调用组件显示的时候也是在父组件作用域内编译
<my-cpn v-show="isShow"></my-cpn> //最终该子组件是可以显示的,因为是在父组件内使用
作用域插槽:子组件的内容、数据由父组件替换插槽标签展示。简单来说就是父组件拿子组件的数据进行不同风格的展示
4.9.3:解构插槽:将插槽内容包裹在一个拥有单个参数的函数里
4.10:is:
4.10.1:动态切换组件
<component v-bind:is="currentTabComponent"></component> //currentTabComponent为组件名
4.10.2:解析DOM模版,解除html的元素限制,比如ul只能包含li
<ul> <li is="your-component"></li> </ul>
5、模块化:CommonJS、AMD、CMD、ES6的Modules等
export{参数1、参数2...} //指定导出 import * as info from './info.js' //全导入,别名
5.1:Vue中webpack:
5.1.1:打包执行顺序:先本地node_modules/.bin中webpack,如果没有,再去全局找
5.1.2:Vue使用webpack打包编译的时候注意环境配置:
runtime+compiler:template -> 解析成ast -> 编译成render ->再创建虚拟dom
runtime-only:只需要用到render去createElement,转换成虚拟dom树,再直接生成UI,在这之前已经使用vue-template-compiler处理template。性能高,代码量少
5.2:Vue组件打包成 .vue文件,分离成三个部分:template、script、style。再在main.js中引入,webpack中使用vue-lodaer,安装相应loader
5.3:配置分离:将基础配置设为base.config.js,生产配置和开发配置再单独设置文件,修改json文件指定新配置文件
6、Vue Cli:
创建项目:1、create;2、init;3、启动配置服务器Vue UI
配置:1、Vue UI配置;2、隐藏的配置文件@Vue下cli-service设置;3、自定义配置文件并添加
7、Vue router路由
路由:决定数据包来源到目的地的路径,内部有一核心概念叫路由表,本质为一个映射表,决定了数据包的指向
后端渲染:jsp,css+html+java
前端渲染:前后端分离。静态资源服务器(HTML+CSS+JS) + 后端数据服务器(异步API请求数据,再渲染)
前端路由:URL映射,改变URL,响应指定组件,页面可不整体刷新
7.1:前端路由规则(改变URL页面不刷新):1、修改URL的hash(#);2、HTML5的history.pushState() / .replaceState() / .go / .forward() / .back
7.2:Vue-router:
使用:导入路由对象,调用Vue.use(VueRouter);创建路由实例,传入映射配置;挂载
配置:一个组件对应一个路径。router-link引用,渲染成一个a标签;router-view渲染显示组件;路由切换是切换router-view
7.3:router-link:
7.3.1:to跳转、tag指定渲染成什么组件、replace不留下history、active-class可设置一个router-link-active的class,一般用来设置高亮css类
7.3.2:代码跳转:点击或者自定义其他事件,设置方法将其 this.$router.push('/about') 或者replace 跳转,主要是可扩大事件与标签范围,不局限于a等其他标签
7.4:动态路由:根据返回数据将URl修改,如登录后URl会在结尾加上用户名:路由内配置路径path: 'user/:id',组件内实现跳转 :to='/user/返回用户名';若组件要获取路由信息,则可在组件内定义计算属性,返回 this.$route.params.id($route不是router,拿回当前活跃route,id为路由配置内的 :id)
7.5:路由懒加载:避免将大型js包打包成一个文件,当网络拥堵时造成用户页面空白,懒加载就是将路由对应组件打包成一个个代码块,路由被访问时才加载对应组件
7.6:嵌套路由:创建对应子组件,在路由映射中父组件路由下配置对应子路由children;再父组件内router-link指向带父组件地址的url,如 /home/msg ,再添加router-view指定显示位置
7.7:传参:params、query
7.7.1:params配置路由:/router/:id,通过path后面对应的值传参,获取用$route.params.name,适合简单传递
7.7.2:query普通配置:/router/?id=query,query的key传参,获取也是用 $route.query.name,适合大量传递,因为传递的是对象
7.8:导航守卫:监听路由的进入和离开,然后执行相应操作(比如切换路由浏览器标题切换):beforeEach和afterEach钩子函数:
7.8.1:全局守卫:单个监听可以在每个组件下的created生命周期添加dom操作,如document.title,但是很麻烦,所以使用全局导航守卫:在router页面定义钩子函数,在钩子函数内执行
//前置钩子,跳转前回调 router.beforeEach(function (to, from, next) { //from从哪来(即将离开的路由),to到哪去(跳转到的路由),各个路由配置内配置meta元数据(描述数据的数据) document.title = to.meta.title //必须执行下一步,可以是跳转下一个钩子,也可以执行别的操作 next() }) //后置钩子,跳转后回调,不需要next router.afterEach()
7.8.2:路由独享守卫:在配置路由的时候内部配置钩子函数beforeEnter等,和全局守卫类似
7.8.3:还有组件守卫等
7.9:keep-alive:Vue内置的组件,可以将包含的组件保留其状态,避免重新渲染,include属性,匹配的组件会被缓存;exclude属性,匹配的组件不会被缓存
router-view如果被包含在keep-alive里,所有路径匹配到的视图组件都会被缓存。keep-alive定义在根App.vue内才生效,因为属性include需要查询各个组件的name
8、Vex:状态管理模式,集中式存储管理,其实就是将多个组件(多个页面)的共享变量放在一个对象里,并将其放置于顶层Vue实力内,让其他组件可访问。Vuex最大特点:响应式:
8.1:单页面管理:
8.2:Vuex管理:按照规则执行,不可绕过,绕过的话devtools无法跟踪(如,组件不可直接改变state)
8.3:Vuex核心:State、Getter、Mutation、Action、Module
8.3.1:State单一状态树(单一数据源) ,方便管理,维护,为了安全
8.3.2:Getters,类似计算属性,只是把计算属性公用了。getters不能随便传参,自定义传参的话可以返回一个函数,让函数传参。在组件中可以使用mapGetters映射,将所需要的getters结构传入
8.3.3:Mutation,用来更新state的唯一方式(更新时,需要调用commit方法才能更新)。包含事件类型和回调函数。
Mutation响应式规则:1、如果是对象,提前在store内初始化好所需属性;2、如果要添加新属性,必须使用Vue.set(obj, 属性名, 值),或者用新对象重新赋值。这样才可以响应式,否则view视图不会刷新
mut(state) { //非响应式,该方法也不在数组响应式方法内 // state.students2['age'] = 18 // delete state.students2.id //set在 Vue.set(state.students2, 'age', 18) Vue.delete(state.students2, 'id') //或者赋予新对象 state.students2={ ...state.students2,'age':18 } }
Mutation的常量类型:将方法名设置为常亮,将其放于一个映射文件内,便于后期维护
8.3.4:Action:Mutation的devtools监控同步函数,异步使用action(调用dispatch更新)。注意:该处可以理解为增加了一个环节,修改属性必须还是通过mutation,而不能将action替代掉mutation而直接在action内修改state属性,理解Vuex流程图。因为是异步,所以可以考虑promise,灵活使用then执行函数:比如提交成功或失败用Promise回馈。同理actions也可以用映射mapActions
//index.js actions: { //context可理解为store acUpdateStu(context) { return new Promise((resolve, reject) => { setTimeout(() => { context.commit('mut') resolve('成功') reject('失败') }, 1000) }) //以下的then也可以在客户端内函数执行,让用户决定promise内容 // }).then(res=>console.log(res)).catch(rej=>console.log(rej)) } }, //App.vue muts() { this.$store .dispatch("acUpdateStu") .then((res) => console.log(res)) .catch((rej) => console.log(rej)); },
8.3.5:Module:将store分割成多个模块,每个模块都有store的内容,如state,actions等。
Modules内的异步action调用根属性的时候可以使用rootState,rootGetters等,这些都包含在context内
8.3.6:项目结构:
9、关于事件总线:
Vue2.x中new一个eventBus
Vue3中,事件总线模式可以被替换为使用外部的、实现了事件触发器接口的库,例如 mitt 或 tiny-emitter
但是不鼓励使用,短期可以解决问题,但是长期来看不利于维护,有如下替代方案:
1、prop逐级穿透传递父子组件通讯,但是有时候远距离通信过于麻烦,会在某些组件中传递不必要的prop
2、provide和inject,可以远距离通信
3、Vuex
10、Mixin混入(部分转载https://www.cnblogs.com/dengyao-blogs/p/11589962.html)
混入也是一个封装,封装的是Vue组件的生命周期所要执行的函数。引入mixin分局部引用和全局引用
创建了混入对象之后,我们自定义的方法或者变量可以很轻松的挂载在Vue实例上
1、mixin混入对象的变量是不会共享的,多个组件引用,一个发生了变化,并不会通知mixin进行实时刷新数据,发生的变化只会在page1.vue中生效,不影响其他组件
2、如果是同名钩子函数将合并为一个数组,因此都被调用,但是混入对象的钩子将在自身实例钩子之前触发
3、值为对象的选项,例如methods,components等如果变量名和mixin混入对象的变量名发生冲突,将会以组件优先并进行递归合并,相当于组件数据直接覆盖了mixin中的同名数据;
4、若想自定义合并:Vue.config.optionMergeStrategies
5、全局引用会影响所有组件的使用,在名称定义需要注意
和Vuex的区别:Vuex是状态共享管理,所以Vuex中的所有变量和方法都是可以读取和更改并相互影响的;mixin可以定义公用的变量或方法,但是mixin中的数据是不共享的,也就是每个组件中的mixin实例都是不一样的,都是单独存在的个体,不存在相互影响的;
12、$nextTick
mounted是DOM渲染完毕,但是不代表图片等资源渲染完毕。
如要等图片等渲染完毕再操作,可以使用$nextTick等下一次DOM更新循环结束后延迟回调,比如数据修改后、图片加载完毕后立即执行等操作。
$nextTick所放置的位置需要注意,要确定所放位置的下一帧是否满足要求,比如放在图片加载完成之后,DOM刷新
上面为codewhy视频总结。以下为自行阅读Vue官方文档后补充的内容:
13、Vue2.6之后的动态参数
2.6之后添加了动态参数,可以动态的改变标签参数
//attributeName可以是href,或者别的 <a v-bind:[attributeName]="url"> ... </a> //eventName可以是focus <a v-on:[eventName]="doSomething"> ... </a>
动态参数预期会求出一个字符串,异常情况下值为 null
。这个特殊的 null
值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。
14、有关key:
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。但有时候会带来问题:比如两个input切换的时候,一个input的内容可能会复用到另一个input上,而我们是想切换的时候换成一个空的input。如果不需要复用,则在input上绑定一个唯一key
15、Vue组件处理边界情况:
15.1:访问根实例:$root
15.2:访问父级组件实例:$parent
15.3:访问子组件ref:$refs只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——应该避免在模板或计算属性中访问$refs
15.4:依赖注入:当子组件需要访问父组件的方法时,父组件可以provide将方法/函数提供出去
provide() { return { getMap: this.getMap } }
子组件使用inject接收
inject: ['getMap']
可以将其认为是一个更大范围的prop
15.5:程序化的事件侦听器:比如执行一次的函数,利用once自动清理
- 通过
$on(eventName, eventHandler)
侦听一个事件 - 通过
$once(eventName, eventHandler)
一次性侦听一个事件 - 通过
$off(eventName, eventHandler)
停止侦听一个事件
15.6:组件循环套用:A中有B,B中有A(这样会造成一个问题,Vue不知道怎么不经过一个引用另一个):beforeCreate创建;webpack异步import
16、过渡&动画(单纯举例,详细的可到Vue官网查看文档 https://cn.vuejs.org/v2/guide/transitions.html)
16.1:transition过渡
<transition name="fade">
<p v-if="show">hello</p>
</transition>
属性name和追加的类名一致:.fade-enter-active, .fade-leave-active等
16.2:css过渡:和transiton类似,也是name上设置如slide,css内定义slide属性
<transition name="slide-fade">
<p v-if="show">hello</p>
</transition>
name都是自定义的
16.3:JS的钩子函数:before-enter等,在过渡生命周期期间执行相应操作
17、自定义指令
全局:
// 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() } })
局部:
directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } } }
相关钩子函数:bind、inserted、update、componentUpdated、unbind
-
bind():当指令绑定在 HTML 元素上时触发
-
inserted():当指令绑定的元素插入到父节点中的时候触发
-
update():当指令绑定的元素状态/样式、内容(这里指元素绑定的 vue 数据) 发生改变时触发
-
componentUpdated():当 update() 执行完毕之后触发
-
unbind():当指令绑定的元素从 dom 中删除时触发
18、渲染函数render和JSX
18.1、render渲染元素,有时候比创建组件来得方便,比如根据type来展示不同的 h级标签
//组件 <script type="text/x-template" id="anchored-heading-template"> <h1 v-if="level === 1"> <slot></slot> </h1> <h2 v-else-if="level === 2"> <slot></slot> </h2> <h3 v-else-if="level === 3"> <slot></slot> </h3> ................ props: { level: { type: Number, required: true } } //render函数 render: function (createElement) { return createElement( 'h' + this.level, // 标签名称 this.$slots.default // 子节点数组 ) }, props: { level: { type: Number, required: true } }
18.2、虚拟DOM:createElement => createNodeDescription,VNode即虚拟dom节点
18.3、注意,深入底层自定义指令后,v-model、v-if等就无法使用,只能使用原生自己实现这些功能
18.4:JSX:Babel插件,javascript+xml,直接return一个html模版,减少书写量,更直观
18.5:函数式组件(将functional设置为true):定义那些没有响应数据,也不需要有任何生命周期的场景,它只接受一些props 来显示组件
19、插件:
-
添加全局方法或者 property。如:vue-custom-element
-
添加全局资源:指令/过滤器/过渡等。如 vue-touch
-
通过全局混入来添加一些组件选项。如 vue-router
-
添加 Vue 实例方法,通过把它们添加到
Vue.prototype
上实现。 -
一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
vue的注意事项:
1、由于vue采用虚拟dom,会造成一个复用的情况,在某些条件下,如多个input,当切换的时候,虚拟dom会复用前一个dom,会造成dom内value不清空,此时可以创建一个key,让vue区分哪些需要复用,哪些不需要复用。
<div id="app"> <span v-if="isUser"> <label for="username">用户账号</label> <input type="text" id="username" placeholder="用户账号" key="username"> </span> <span v-else> <label for="email">用户邮箱</label> <input type="text" id="email" placeholder="用户邮箱" key="email"> </span> <button @click="isUser = !isUser">切换类型</button> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { isUser: true } })
2、过滤器(vue3移除,使用计算属性),格式化通用文本
3、驼峰:在component的props内,用驼峰的话,template就要换成相应的驼峰,否则不能识别。但是在实例下HTML标签内的传输v-bind要换成-连接,不能也用驼峰,不然会报错
<div id='app'> <cpn1 :c-msg='msg'></cpn1> </div> <script> const app = new Vue({ el: '#app', data: { msg: 'hello', }, components: { cpn1: { template: ` <div> <p>{{cMsg}}</p> </div>`, props: { //此处用驼峰的话,上面的template就要换成相应的驼峰,否则不能识别。但是在实例下HTML标签内的传输v-bind要换成-连接,不能也用驼峰,不然会报错 cMsg: String } } } })
4、Vue2.6后slot和slot-scope统一为v-slot,语法糖为#
5、Vue原生开发不包含webpack,命名时需要注意大小写问题
6、Vue与webpack打包编译的时候注意运行环境配置版本:compiler和runtime。打包前指定webpack配置环境
//在webpack.config.js中 resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' } }
不过后期使用手脚架不需要配置那么多
7、当打开首页的时候,默认没有显示首页的组件,需要用户点击,体验不好。这时可以让路径默认跳转默认路由:新建一个映射,将 '/' redirect到主页,如 '/home' 上
const routes = [{ path: '/', redirect: '/home' }]
8、关于路由懒加载:不可以在路由配置上直接import组件,否则其不会执行懒加载,需要指定,ES6写法:
component: () => import('../views/About.vue')
还有别的如AMD写法
9、使用插槽的时候,尽量外面再包一层其他标签如div,一些判断,如是否展示 v-if 等写在该标签内,而不写在插槽内,防止插槽被替换而不起效果
10、别名:cli4下起别名需要手动创建vue.config.js,重新run后再使用别名。在属性内的位置需要在别名前加 ~ ,import可不加
11、获取组件元素,首先在父组件上给子组件定义ref,然后引用的时候使用 this.$refs.子组件ref名称.$el.方法,如 offsetTop
12、要理解Vue的核心,数据响应的原理,当修改props值的时候,其实父的本值也被改变了,导致其他使用该值的组件内的值也改变了
13、利用Vue.use自定义插件,如toast
14、当v-for和v-if同时出现时,Vue2优先处理v-for,Vue3优先处理v-if,但是建议永远不要两个放在一起用,讲要判断的值换做计算属性返回后在v-for
15、$listeners:当子组件为一个被包装过的input子组件,.native不能如期生效,可以在子组件上使用该对象,将事件监听指向某个特定元素
16、router-linl的exact-active-class,当组件被激活时,默认加上该类,可配置精准匹配激活类,就不用在多个router-link上添加v-if判断再添加类了
exact-active-class="active" //只有一个router-link会被激活
当然,也有模糊匹配,适合配置父子路由,都需要激活某类的时候
active-class="active" //父子同体系下可能会有多个router-link会被激活
Vue3需要注意模糊匹配:路由路径包含 且 需要有路由嵌套关系,才可模糊激活
{ path: "/member", component: MemberLayout, children:[ { path: "/member", component: MemberHome, },{ path: "/member/order", component: MemberOrder, }, { path: "/member/order/:id", component: MemberOrderDetail, }, ] }, /member 和/member/other 不是嵌套路由,不会加上这个类 router-link-active
修改:
{ path: '/member/order/', // vue3.0 需要有嵌套关系才能模糊匹配 component: { render: () => h(<RouterView/>) }, children: [ { path: '', component: MemberOrder }, { path: ':id', component: MemberOrderDetail } ] }