<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <p> {{ message }}</p> <input type="text" v-model="message"> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) </script> </body> </html>
因为js的限制,Vue 不能检测以上数组的变更,以及对象的添加/删除,不少人会由于像上面这样操做,出现视图没有更新的问题。javascript
this.$set(你要改变的数组/对象,你要改变的位置/key,你要改为什么value)html
this.$set(this.arr, 0, "aa"); // 改变数组 this.$set(this.obj, "c", "cc"); // 改变对象
数组原生方法触发视图更新:
Vue能够监测到数组变化的,数组原生方法:前端
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <p>arr:{{arr}}</p> <p>obj:{{obj}}</p> <button @click="arrFn1">修改数组</button> <button @click="arrFn2">改变数组</button> <button @click="objFn1">增长和删除对象</button> <button @click="objFn2">修改对象</button> <button @click="this$set">this.$set</button> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data() { return { arr: [1, 2, 3], obj: { a: 1, b: 2 } } }, methods: { objFn1() { this.obj.c = 'c' //增长对象属性 delete this.obj.a; //删除对象属性 console.log(this.obj) //数据变化,视图没有变化 }, objFn2() { this.obj.a = 'aa' console.log(this.obj) //数据变化,视图变化 }, arrFn1() { this.arr[0] = '11'; //修改数组 this.arr.length = 1; //修改数组 console.log(this.arr) //数据变化,视图没有变化 }, arrFn2() { // splice()、 push()、pop()、shift()、unshift()、sort()、reverse() this.arr.push('3') console.log(this.arr) //数据变化,视图变化 }, this$set() { this.$set(this.arr, 0, "11"); // 改变数组 this.$set(this.obj, "c", "11"); // 改变对象 } } }) </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <div>{{message | filterTest}}</div> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data() { return { message: 1 } }, filters: { filterTest(value) { // value在这里是message的值 // return value + '%'; return `${value}%`; } } }) </script> </body> </html>
v-if尽可能不要与v-for在同一节点使用,由于v-for 的优先级比 v-if 更高,若是它们处于同一节点的话,那么每个循环都会运行一遍v-if
若是你想根据循环中的每一项的数据来判断是否渲染,那么你这样作是对的:vue
<li v-for="todo in todos" v-if="todo.type===1"> {{ todo }} </li>
若是你想要根据某些条件跳过循环,而又跟将要渲染的每一项数据没有关系的话,你能够将v-if放在v-for的父节点:html5
// 数组是否有数据 跟每一个元素没有关系 <ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
正确使用v-for与v-if优先级的关系,能够为你节省大量的性能。java
<!DOCTYPE html> <html> <head> <title></title> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> </head> <body> <div id="app"> <p>{{ message }}</p> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { message: "xuxiao is boy" }, beforeCreate: function() { console.group('beforeCreate 建立前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //undefined console.log("%c%s", "color:red", "message: " + this.message) }, created: function() { console.group('created 建立完毕状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, beforeMount: function() { console.group('beforeMount 挂载前状态===============》'); console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, mounted: function() { console.group('mounted 挂载结束状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, beforeUpdate: function() { console.group('beforeUpdate 更新前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, updated: function() { console.group('updated 更新完成状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, beforeDestroy: function() { console.group('beforeDestroy 销毁前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, destroyed: function() { console.group('destroyed 销毁完成状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message) } }) </script> </body> </html>
另外在标绿处,咱们能发现el仍是 {{message}},这里就是应用的 Virtual DOM(虚拟Dom)技术,先把坑占住了。到后面mounted挂载的时候再把值渲染进去。node
有关于销毁,暂时还不是很清楚。咱们在console里执行下命令对 vue实例进行销毁。销毁完成后,咱们再从新改变message的值,vue再也不对此动做进行响应了。可是原先生成的dom元素还存在,能够这么理解,执行了destroy操做,后续就再也不受vue控制了。git
https://segmentfault.com/a/11...
Vue的生命周期函数和beforeRouteEnter()/beforeRouteLeave()的函数github
2.触发屡次浏览器重绘及回流:使用 vnode ,至关于加了一个缓冲,让一次数据变更所带来的全部 node 变化,先在 vnode 中进行修改,而后 diff 以后对全部产生差别的节点集中一次对 DOM tree 进行修改,以减小浏览器的重绘及回流vue-router
例子
{ tag: 'div', /*说明这是一个div标签*/ children: [ /*存放该标签的子节点*/ { tag: 'a', /*说明这是一个a标签*/ text: 'click me' /*标签的内容*/ } ] }
渲染后能够获得
<div> <a>click me</a> </div>
当一个组件被定义, data 必须声明为返回一个初始数据对象的函数,由于组件可能被用来建立多个实例。若是 data 仍然是一个纯粹的对象,则全部的实例将共享引用同一个数据对象!经过提供 data 函数,每次建立一个新实例后,咱们可以调用 data 函数,从而返回初始数据的一个全新副本数据对象。
为何在组件中用js动态建立的dom,添加样式不生效
<template> <div class="test"></div> </template> <script> let a=document.querySelector('.test'); let newDom=document.createElement("div"); // 建立dom newDom.setAttribute("class","testAdd" ); // 添加样式 a.appendChild(newDom); // 插入dom </script> <style scoped> .test{ background:blue; height:100px; width:100px; } .testAdd{ background:red; height:100px; width:100px; } </style>
结果
// test生效 testAdd 不生效 <div data-v-1b971ada class="test"><div class="testAdd"></div></div> .test[data-v-1b971ada]{ // 注意data-v-1b971ada background:blue; height:100px; width:100px; }
缘由
当 <style> 标签有 scoped 属性时,它的 CSS 只做用于当前组件中的元素。
它会为组件中全部的标签和class样式添加一个scoped标识,就像上面结果中的data-v-1b971ada。
因此缘由就很清楚了:由于动态添加的dom没有scoped添加的标识,没有跟testAdd的样式匹配起来,致使样式失效。
解决办法:
去掉scoped便可
SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是: 更新视图而不从新请求页面;vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据mode参数来决定采用哪种方式。
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,因而当 URL 改变时,页面不会从新加载。 hash(#)是URL 的锚点,表明的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会从新加载网页,也就是说hash 出如今 URL 中,但不会被包含在 http 请求中,对后端彻底没有影响,所以改变 hash 不会从新加载页面;同时每一次改变#后的部分,都会在浏览器的访问历史中增长一个记录,使用”后退”按钮,就能够回到上一个位置;因此说Hash模式经过锚点值的改变,根据不一样的值,渲染指定DOM位置的不一样数据。hash 模式的原理是 onhashchange 事件(监测hash值变化),能够在 window 对象上监听这个事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> ul { height: 1000px; border-bottom: 1px solid #000; } </style> </head> <body> <ul> <li class="a"> <a href="#a">a</a> </li> <li class="b"> <a href="#b">b</a> </li> </ul> <div> <div id="a">a</div> <div id="b">b</div> </div> </body> </html>
因为hash模式会在url中自带#,若是不想要很丑的 hash,咱们能够用路由的 history 模式,只须要在配置路由规则时,加入"mode: 'history'",这种模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈,在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会当即向后端发送请求
const router = new VueRouter({ mode: 'history', routes: [...] })
当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,比较好看!
不过这种模式要玩好,还须要后台配置支持。由于咱们的应用是个单页客户端应用,若是后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就很差看了。
因此呢,你要在服务端增长一个覆盖全部状况的候选资源:若是 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
export const routes = [ {path: "/", name: "homeLink", component:Home} {path: "/register", name: "registerLink", component: Register}, {path: "/login", name: "loginLink", component: Login}, {path: "*", redirect: "/"}]
此处就设置若是URL输入错误或者是URL 匹配不到任何静态资源,就自动跳到到Home页面
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app" class="demo"> <!-- 全局注册 --> <input type="text" placeholder="我是全局自定义指令" v-focus> </div> <script> Vue.directive("focus", { inserted: function(el) { el.focus(); } }) //new Vue要放在后面 new Vue({ el: "#app" }) </script> </body> </html>
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app" class="demo"> <!-- 局部注册 --> <input type="text" placeholder="我是局部自定义指令" v-focus2> </div> <script> new Vue({ el: "#app", directives: { focus2: { inserted: function(el) { el.focus(); } } } }) </script> </body> </html>
一个指令定义对象能够提供以下几个钩子函数 (均为可选)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="container"> <!-- 准备实现需求: 在h1标签上面,加上一个按钮,当点击按钮时候,对count实现一次 自增操做,当count等于5的时候,在控制台输出‘it is a test’ --> <button @click="handleClick">clickMe</button> <h1 v-if="count < 6" v-change="count">it is a custom directive</h1> </div> <script> //directive new Vue({ el: '#container', data: { count: 0, color: '#ff0000' }, methods: { handleClick: function() { //按钮单击,count自增 this.count++; } }, directives: { change: { bind: function(el, bindings) { console.log('指令已经绑定到元素了'); console.log(el); console.log(bindings); //准备将传递来的参数 // 显示在调用该指令的元素的innerHTML el.innerHTML = bindings.value; }, update: function(el, bindings) { console.log('指令的数据有所变化'); console.log(el); console.log(bindings); el.innerHTML = bindings.value; if (bindings.value == 5) { console.log(' it is a test'); } }, unbind: function() { console.log('解除绑定了'); } } } }) </script> </body> </html>
https://www.cnblogs.com/wangr...
https://juejin.im/post/5a3933...
v-if按照条件是否渲染,v-show是display的block或none
key的做用主要是为了高效的更新虚拟DOM。
https://www.zhihu.com/questio...
在下次 DOM 更新循环结束以后执行延迟回调。在修改数据以后当即使用这个方法,获取更新后的 DOM。
注意:重点是获取更新后的DOM 就是在开发过程当中有个需求是须要在created阶段操做数据更新后的节点 这时候就须要用到Vue.nextTick()
$nextTick就是用来知道何时DOM更新完成
在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操做无异于徒劳,因此在created中必定要将DOM操做的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted()钩子函数,由于该钩子函数执行时全部的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操做都不会有问题
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app"> <div ref="msg1">{{msg1}}</div> <div>{{msg2}}</div> <button @click="changeMsg">点击我</button> </div> <script> new Vue({ el: '#app', data() { return { msg1: "11", msg2: "22" } }, methods: { changeMsg() { this.msg1 = "33" this.msg2 = this.$refs.msg1.textContent; console.log('DOM并未渲染完成' + this.$refs.msg1.textContent) //11 this.$nextTick(function() { console.log('DOM已经何渲染完成' + this.$refs.msg1.textContent) //33 }) } } }) </script> </body> </html>
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app"> <div ref="msg" id="msg" v-if="isShow">{{msg}}</div> <button @click="changeMsg">点击我</button> </div> <script> new Vue({ el: '#app', data() { return { msg: "aa", isShow: false } }, methods: { changeMsg() { this.isShow = true console.log(document.getElementById("msg")) //null this.$nextTick(function() { console.log(document.getElementById("msg").innerHTML) //aa }) } } }) </script> </body> </html>
https://juejin.im/post/5b6a60...
首先在定义路由的时候配置 meta 字段,自定义一个KeepAlive字段做为该页面是否缓存的标记
routes:[{ path: '/search', name: 'search', component: search, meta: { title: '搜索列表页', keepAlive: true // 标记列表页须要被缓存 } }, { path: '/detail', name: 'detail', component: detail, meta: { title: '详情页', // 详情页不须要作缓存,因此不加keepAlive标记 } }]
因为<keep-alive>组件不支持v-if指令,因此咱们在App.vue中采用两个<router-view>的写法,经过当前路由的keepAlive字段来判断是否对页面进行缓存:
<div id="app"> <keep-alive> <router-view v-if="$route.meta.keepAlive" /> </keep-alive> <router-view v-if="!$route.meta.keepAlive" /> </div>
使用<keep-alive>提供的 exclude 或者 include 选项,此处咱们使用 exclude ,在App.vue中:
<div id="app"> <keep-alive exclude="detail"> <router-view /> </keep-alive> </div>
须要注意的是,必定要给页面组件加上相应的name,例如在detail.vue中:
<script>
<script> export default { name: 'detail', // 这个name要和keep-alive中的exclude选项值一致 ... } </script>
这么写就表明了在项目中除了name为detail的页面组件外,其他页面都将进行缓存。
父子通讯:
父向子传递数据是经过 props,子向父是经过 events($emit);经过父链 / 子链也能够通讯($parent / $children);ref 也能够访问组件实例;provide / inject API;$attrs/$listeners
兄弟通讯:
Bus;Vuex
跨级通讯:
Bus;Vuex;provide / inject API、$attrs/$listeners
https://juejin.im/post/5bd97e...
https://github.com/ljianshu/B...
Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为何在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
data:{ //普通属性 msg:'aa', }, computed:{ //计算属性 reverseMsg:function(){ // 该函数必须有返回值,用来获取属性,称为get函数 //能够包含逻辑处理操做,同时reverseMsg依赖于msg,一旦msg发生变化,reverseMsg也会跟着变化 return this.msg.split(' ').reverse().join(' '); } }
<template> <button @click="obj.a = 2">修改</button> </template> <script> export default { data() { return { obj: { a: 1, } } }, watch: { obj: { handler: function(newVal, oldVal) { console.log(newVal); }, deep: true, immediate: true } } } </script>