vue考题最新版本

VUE学习整理
http://doc.liangxinghua.com/vue-family/1.html
http://doc.liangxinghua.com/vue-family/1.3.html

vue的优点
低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。

可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
vue和react对比
两者本质的区别:模板和组件化的区别

Vue本质是MVVM框架,由MVC发展而来;
React是前端组件化框架,由后端组件化发展而来;
Vue使用模板
React使用JSX
React本身就是组件化
Vue是在MVVM上扩展的

共同点:
都支持组件化,都是数据驱动视图

上面主要梳理了react和vue的4点不同:
1.数据是不是可变的
2.通过js操作一切还是各自的处理方式
3.什么功能内置,什么交给社区去做
react整体的思路就是函数式,所以推崇纯组件,数据不可变,单向数据流,当然需要双向的地方也可以做到,比如结合redux-form,而vue是基于可变数据的,支持双向绑定。react组件的扩展一般是通过高阶组件,而vue组件会使用mixin。vue内置了很多功能,而react做的很少,很多都是由社区来完成的,vue追求的是开发的简单,而react更在乎方式是否正确

监听数据变化的实现原理不同
Vue 通过 getter/setter 以及一些函数的劫持,能精确快速的计算出 Virtual DOM 的差异。这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
React 默认是通过比较引用的方式进行的,如果不优化,每当应用的状态被改变时,全部子组件都会重新渲染,可能导致大量不必要的 VDOM 的重新渲染。
Vue 不需要特别的优化就能达到很好的性能,而对于 React 而言,需要通过 PureComponent/shouldComponentUpdate 这个生命周期方法来进行控制。如果你的应用中,交互复杂,需要处理大量的 UI 变化,那么使用 Virtual DOM 是一个好主意。如果你更新元素并不频繁,那么 Virtual DOM 并不一定适用,性能很可能还不如直接操控 DOM。
为什么 React 不精确监听数据变化呢?
这是因为 Vue 和 React 设计理念上的区别,Vue 使用的是可变数据,而 React 更强调数据的不可变。
数据流的不同
Vue 中默认支持双向绑定,组件与 DOM 之间可以通过 v-model 双向绑定。但是,父子组件之间,props 在 2.x 版本是单向数据流
React 一直提倡的是单向数据流,他称之为 onChange/setState()模式。
不过由于我们一般都会用 Vuex 以及 Redux 等单向数据流的状态管理框架,因此很多时候我们感受不到这一点的区别了。

模板渲染方式的不同
在表层上, 模板的语法不同
React 是通过 JSX 渲染模板
而 Vue 是通过一种拓展的 HTML 语法进行渲染
在深层上,模板的原理不同,这才是他们的本质区别:

React 是在组件 JS 代码中,通过原生 JS 实现模板中的常见语法,比如插值,条件,循环等,都是通过 JS 语法实现的
Vue 是在和组件 JS 代码分离的单独的模板中,通过指令来实现的,比如条件语句就需要 v-if 来实现
对这一点,我个人比较喜欢 React 的做法,因为他更加纯粹更加原生,而 Vue 的做法显得有些独特,会把 HTML 弄得很乱。举个例子,说明 React 的好处:react 中 render 函数是支持闭包特性的,所以我们 import 的组件在 render 中可以直接调用。但是在 Vue 中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以我们 import 一个组件完了之后,还需要在 components 中再声明下,这样显然是很奇怪但又不得不这样的做法。

vue全家桶
vue + vue-router + vuex + axios
https://www.cnblogs.com/Nutrient-rich/p/7063058.html
组件里面, data必须是一个函数
类比引用数据类型 Object是引用数据类型, 每个组件的data 都是内存的同一个地址,一个数据改变了其他也改变了;

那么用什么方法可以使每个组件的data相互独立,不受影响呢?

当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。
vue中的nextTick
methods/computed/watch
计算属性:自动监听依赖值的变化,从而动态返回内容,主要目的是简化模板内的复杂运算,只需要动态值的时候,用计算属性,计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值,不支持异步,当computed内有异步操作时无效,无法监听数据的变化,适用场景:
重新计算开销很大的话,选computed; 不希望有缓存的选methods
watch:需要知道值改变后执行业务逻辑用watch,watch有新旧值两个参数, 计算属性没有,但是计算属性可以从setter获得新值,Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的
methods:是一个方法,可以接受参数,计算属性不能,计算属性是可以缓存的

在执行简单的计算操作时,computed比watch更简洁易读。

总结:
1.如果一个数据依赖于其他数据的简易计算处理的,那么使用computed比较合适。
2.如果需要在某个数据变化时做一些事情,使用watch来观察这个数据变化
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。这是和computed最大的区别

关于computed
对于计算属性要特别说明一点: vue的计算属性computed默认只有getter,需要使用getter的时候需要自己加一个setter

export default {
    data () {
        return {
            firstName: '张',
            lastName: '三',
        };
    },
    computed: {
        fullName() {
              return this.firstName + ' ' + this.lastName
        },
    },
    methods: {
        changeFullName () {
            this.fullName = '李 四';
        }
    },
};
 
其中computed里的代码完整写法是  
 
computed: {
   fullName: {
        // getter
        get: function () {
          return this.firstName + ' ' + this.lastName
        },
   }    
},

执行 changeFullName 发现报错[Vue warn]: Computed property “fullame” was assigned to but it has no setter.

我们需要给计算属性fullName添加一个setter

computed: {
   fullName: {
        // getter
        get: function () {
          return this.firstName + ' ' + this.lastName
        },
        // setter
        set: function (newValue) {
          var names = newValue.split(' ')
          this.firstName = names[0]
          this.lastName = names[names.length - 1]
        }
  }    
}

vue中如何监控某个属性值的变化
比如现在需要监控data中,obj.a 的变化。Vue中监控对象属性的变化你可以这样:

watch: {  
      obj: {  
      handler (newValue, oldValue) {  
        console.log('obj changed')  
      },  
      deep: true  
    }  
  } 
deep属性表示深层遍历,但是这么写会监控obj的所有属性变化,并不是我们想要的效果,所以做点修改:

watch: {  
   'obj.a': {  
      handler (newName, oldName) {  
        console.log('obj.a changed')  
      }  
   }  
  } 
还有一种方法,可以通过computed 来实现,只需要:

computed: {  
    a1 () {  
      return this.obj.a  
    }  
} 
利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。

vue中组件通信
—props emit bus
—vuex slot都可以传值

Vue 组件间通信六种方式
props/$emit
emit/ emit/emit/on
vuex
attrs/ attrs/attrs/listeners
provide/inject
parent/ parent/parent/children 与 ref
非父子通信
简单情况下我们可以通过使用一个空的Vue实例作为中央事件总线

 在main.js文件夹下建一个vue空实例

        let bus = new Vue()

        Vue.prototype.bus = bus

        

        在一个组件中定义this.bus.$emit('toChangeTitle','从首页来')

            toChangeTitle自定义事件名称,后面是要传的值

        

        在另一个组件中的接收

        mounted () {

                    this.bus.$on('toChangeTitle', function (title) {

                    toChangeTitle //前一个组件定义的自定义事件名

                    title //值 把他赋值给data里的变量就可以在模板中使用了

                             console.log(title)

                     })

          }

data赋值与computed赋值的区别:
data赋值:data:{return {aaa: this.aaa}如果是在data中进行赋值,当父组件的aaa值发生改变时,不会在重新赋给子组件中的aaa。

computed赋值:如果想让子组件跟着父组件修改,需要将赋值操作写在computed中。computed:{aaa(){return this.aaa}
vue中操作dom,获取dom

//选择器获取                                                                                                                                  <template>
    <div>
        <canvas id='cvs' >
    </div>
</template>
<script>
    export default{
        mounted(){
            let canvas=document.querySelector('#cvs');
        }
    }
</script>

ref获取                                                                                                                                            <template>
    <div>
        <canvas ref='cvs' >
    </div>
</template>
<script>
    export default{
        mounted(){
            let canvas=this.$refs.cvs;
        }
    }
</script>

在watch或者created里面操作dom,用this.$nextTick(function(){
xxxx
})

vue中数据双向绑定原理及如何实现
原理:
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调
1、实现一个数据监听器Obverser,对data中的数据进行监听,若有变化,通知相应的订阅者。
2、实现一个指令解析器Compile,对于每个元素上的指令进行解析,根据指令替换数据,更新视图。
3、实现一个Watcher,用来连接Obverser和Compile, 并为每个属性绑定相应的订阅者,当数据发生变化时,执行相应的回调函数,从而更新视图。
4、构造函数 (new MVue({}))

实现思路:

 <div id="app">
        <input type="text" v-model="text">
        输入的值为:{{text}}
        <div>
            <input type="text" v-model="text">
        </div>
    </div>
    <script>
        var vm = new MVue({
            el: '#app',
            data: {
                text: 'hello world'
            }
        })
    </script>
1、输入框以及文本节点和data中的数据进行绑定
2、输入框内容变化时,data中的对应数据同步变化,即 view => model
3、data中数据变化时,对应的文本节点内容同步变化 即 model => view

vue过滤器作用和使用场景
https://011.vuejs.org/api/filters.html
https://www.cnblogs.com/yanwuming/p/10603058.html
https://www.cnblogs.com/xuqp/p/9395269.html
https://blog.csdn.net/Shauna_Wu/article/details/79496101
Vue过滤器的作用和使用场景
​ 主要用来实现数据的筛选、过滤、格式化。通常用于时间戳转时间,价格标签的转换等。过滤器分为全局过滤器和局部过滤器;

Vue 1.x 版本借鉴了 angular , 提供 10 个过滤器, 包括有: 日期 小数点位数保留 货币 大小写 等
使用特点
(1)可串联
{{ message | filterA | filterB }}
含义:message作为参数传到过滤器A,过滤器A的结果传到过滤器B。
(2)多个参数
{{ message | filterA(‘arg1’, arg2) }}
含义: 过滤器A有message,‘arg1’, ‘arg2’三个参数

过滤器代码实现
全局过滤器的定义语法

Vue.filter('过滤器的名称',function(){})

项目中的具体使用,局部过滤器为例

先定义个局部过滤器

filters: { //这个定义方式和上面的过滤器定义同理,这里formatDate是从JS中引入进来的,将时间戳转为时间格式
    formatDate (time) {
      if (!time) {
        return;
      }
      var date = new Date(time);
      return formatDate(date, 'yyyy-MM-dd hh:mm:ss');
    }
  },

在template中使用该过滤器

{{item.createTime  | formatDate}}   {{ 需要过滤的参数| 过滤器}}

如果在main.js中定义一个这样的全局过滤器,这样在任何地方需要用到这个过滤器,都可以用这个方式使用

1.3、使用过滤器需要注意事项
一、过滤器要想获得我们的数据,要通过一个叫做 ‘管道符 | ’ 来获取数据
二、过滤器是对已经有的数据进行格式化,也就是必须先有数据,在去格式化(如果支持空数据的形式,可自己在过滤器进行处置)

1.4、多个过滤器和多个参数的过滤器如何使用
直接看代码:

首先说全局,多个值的过滤器

Vue.filter('demo',function(val,first,second){  //多个参数的过滤器定义
				return val + first + second;
			})
<!--这里把其他参数括号括起来-->
{{message | demo('canshu1','canshu2')}} //多个参数的过滤器使用
全局传多个过滤器

Vue.filter('demo',function(val){  //多个过滤器定义
	return val + ' demo1 ';
})
Vue.filter('demo2',function(val){
    return val + ' demo2!';
})
             <!--多个全局过滤器使用-->
			{{message | demo1 | demo2}

是这样的,很多时候我们在项目中会需要从后台获取到时间这个字段,像带有评论的会有用户的评论时间,发表文章的会有发表文章的时间,虽然都是时间,但是有的时候需求不会完全统一。有的只需要年月日,有的要精确到时分秒。

还有一个是路径的问题,比如,我们页面上的图片,音频,视频等这些外部资源,目前我们从后台上传的都不是全路径的,所以这个需要我们自己拼接,但是有的时候又是全路径的,不需要拼接,以图片为例,也就是同一个img标签的src它不紧要支持全路径还要支持非全路径的资源,但是这个东西后台一般不会给你做区分处理,前端可以获取到之后自己做处理,如果用传统方式解决就要加各种判断,如果页面上既有图片又有音频,甚至还有视频等,每种类型都要判断,很繁琐。但是vue给我们提供了一个非常好的工具filters,可以让我们很简单的解决这种问题

过滤器是可以叠加的,后面过滤器接收前面过滤器的返回值
需要用v-if进行判断的都可以用过滤器来实现

filter的使用场景,一般为当后端返回数据为判断显示某个内容时,原来通常使用v-if现在改用filter便于管理

过滤器的作用:实现数据的筛选、过滤、格式化
使用场景:

1.时间过滤器
在项目中使用到的经常用到过滤器,比如时间,数据截取等过滤器,如果在每个.vue中都可以复制同一个过滤器,这可以达到目的,但是遇到方法有bug时就需要诸葛修改进入不同的页面修改,这样既费时又费力,优先可以考虑注册全局的过滤器。
定义方法如下:

新建filters/index.js

const isNullOrEmpty = function(val) {
    if (val == null || val == "" || typeof(val) == undefined) {
        return true;
    } else {
        return false;
    }
}
 
const timeFormat = (value, format) => {
    let date = new Date(value);
    let y = date.getFullYear();
    let m = date.getMonth() + 1;
    let d = date.getDate();
    let h = date.getHours();
    let min = date.getMinutes();
    let s = date.getSeconds();
    let result = "";
    if (format == undefined) {
        result = `${y}-${m < 10 ? "0" + m : m}-${d < 10 ? "0" + d : d} ${
        h < 10 ? "0" + h : h
      }:${min < 10 ? "0" + min : min}:${s < 10 ? "0" + s : s}`;
    }
    if (format == "yyyy-mm-dd") {
        result = `${y}-${m < 10 ? "0" + m : m}-${d < 10 ? "0" + d : d}`;
    }
    if (format == "yyyy-mm") {
        result = `${y}-${m < 10 ? "0" + m : m}`;
    }
    if (format == "mm-dd") {
        result = ` ${mm < 10 ? "0" + mm : mm}:${ddmin < 10 ? "0" + dd : dd}`;
    }
    if (format == "hh:mm") {
        result = ` ${h < 10 ? "0" + h : h}:${min < 10 ? "0" + min : min}`;
    }
    if (format == "yyyy") {
        result = `${y}`;
    }
    return result;
};
 
 
export {
    isNullOrEmpty,
    timeFormat
}

在main.js中引入和注册全局过滤器

import * as filters from '../filters/index'
Object.keys(filters).forEach(key => {
    Vue.filter(key, filters[key])
})

此时就可以在不同的.vue中使用定义的全局过滤器了

{{date|isNullOrEmpty}}是否为空<br/>
        {{date|timeFormat('yyyy-mm-dd')}} 时间过滤器<br>
        {{date|timeFormat('yyyy-mm')}} 时间过滤器yyyy-mm<br>
        {{date|timeFormat('hh:mm')}} 时间过滤器hh:mm<br>
        {{date|timeFormat('yyyy')}} 时间过滤器yyyy<br>
     {{date|timeFormat}} 时间过滤器yyyy<br>

在这里插入图片描述
2.
1.创建filter文件

import Vue from 'vue';

/*订单页面详情页面,状态栏*/
Vue.filter('settleAccountsType',(val)=>{
  if(val==4){
    return `订单已取消!`
  }else if(val==5) {
    return `订单已作废!`
  }
  else if(val==6) {
    return `订单申请退款`
  }else{
    return ``
  }
});
export default {

}

在main中挂载


import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App'
import router from './router'
import $ from 'jquery'
import axios from 'axios'//引入axios
import Vuex from 'vuex'
import store from './store/store';
import filter from '@/assets/js/filter.js';//过滤器全局

Vue.config.productionTip = false;

Vue.use(ElementUI);
Vue.use(Vuex);
// Vue.use(axios);
Vue.prototype.axios = axios;
Vue.use(filter);//过滤器全局挂载

/* eslint-disable no-new */

new Vue({
  el: '#app',
  router,
  filter,//过滤器全局挂载
  store,//使用store
  components: { App },
  template: '<App/>'
});

3.在页面使用

<!--过滤器使用-->
      <div class="statusIf">{{ status | settleAccountsType }}</div>
解释:status :vue页面绑定的数据,就是原来v-if判断的数据
         settleAccountsType :过滤器js中的方法名

3.例如一个购物车界面,读取json文件获取购物车内物品的相关信息。对于单价的变量,读取后需要加美元符号或者加小数点,可以使用过滤器

1、局部过滤器

  在js代码中

var vm = new Vue({
  el:"#app",
  data:{
    totalMoney:0,
    productList:[]
  },
  filters:{
    formatMoney:function(value){
      return "¥"+value.toFixed(2)
    }
  },
  ...

  在HTML代码中:

 <div class="cart-tab-2">
      <div class="item-price">{{item.productPrice | formatMoney}}</div>
 </div>

在这里插入图片描述
4.路径拼接过滤器
路径过滤(涉及传参的形式):
需要自己拼接的写法html:

<img src="../assets/image/img_article.png" alt=""  
v-if="subject.image===''||subject.image==='null'"/>
<img :src="imgUrl+subject.image" alt="" v-if="subject.image !== ''&&subject.image!=='null'"/>

对应的js:

data() {
            return {
                imgUrl:" http://app.chuxinketing.com/api/",
            };
        },

在这种写法上,如果后台返回的图片本身就是一个全路径的链接,比如:http://app.chuxinketing.com/api/123.jpg,在拼上imgUrl,就变成了http://app.chuxinketing.com/api/http://app.chuxinketing.com/api/123.jpg。这种是无法正确打开图片的,你可以自己在浏览器中打开尝试一下,所以这时候,过滤器又变成了了一个化繁为简解决问题的小能手。我们只需要这么做:

<img src="../assets/image/img_avatar.png" alt="" 
v-if="item.image===''||item.image==='null'" />
<img :src="item.image|urlFilter(imgUrl)" alt="" v-if="item.image !== ''&&item.image!=='null'"/>

在filters中添加一个urlFilter方法:


filters:{
          urlFilter(value,imgUrl){
                const self = this;
                if (value) {
                    let include = value.indexOf("http");
                    if (include > -1){
                        return value;
                    } else {
                        return imgUrl+value;
                    }
                }
            },
}

注意,urlFilter()方法中的value其实就是后台返回的被过滤对象。这个大伙可以自己用chrome等调试工具断点调试哦,这样是不是就就轻松解决啦。另外,细心的小伙伴可能会发现为什么,要写两个img标签呢,其实,仔细看就会发现,第一个img标签的src前面没有“:”,而且图片路径也是一个固定的图片,其实这是为了提高用户体验所做的优化,如果后台没有返回图片,像一些需要用户上传头像的地方,如果用户没上传之类的,就可以用一张默认图片展示了。
vue过滤器中如何使用vue-i18n进行多语言的国际化翻译

//单个过滤起的文件,在main.js中引入,全局使用
import Vue from ‘vue’
import moment from ‘moment’
import accounting from ‘accounting’
import i18n from ‘…/i18n/i18n’ //引入国际化语言的语言包

Vue.filter(‘projectDate’, date => moment(date).format(‘YYYY-MM-DD’))
Vue.filter(‘logDate’, date => moment(date).format(‘YYYY-MM-DD HH:mm:ss’))
Vue.filter(‘developerStatus’, state => {
switch (state) {
case 4:
return i18n.t(‘register.recruit’) // 语言包中定义的json对象
case 5:
return i18n.t(‘register.develop’) //其他书写方式一致
case 51:
return ‘测试中’
case 6:
return ‘质保中’
case 7:
return ‘已完成’
case 8:
return ‘已取消’
case 9:
return ‘已中止’
default:
return ‘’
}
})
Vue.filter(‘teamSize’, state => {
switch (state) {
case 1:
return ‘1-3人’
case 2:
return ‘4-6人’
case 3:
return ‘7-9人’
case 4:
return ‘10人以上’
default:
return ‘’
}
})
Vue.filter(‘budget’, money => accounting.formatNumber(money))
Vue.filter(‘totalPrice’, money => accounting.formatMoney(money, ‘’, 2))
Vue.filter(‘payment’, state => {
switch (state) {
case 1:
return ‘线下转账’
case 2:
return ‘支付宝’
case 3:
return ‘微信支付’
default:
return ‘’
}
})

//使用示例

{{detail.developerStatus | developerStatus}}过滤器的名字


**vue中生命周期函数**
**什么是vue生命周期**
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期
 
**vue生命周期的作用是什么**
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
**created阶段的ajax请求与mounted请求的区别**
前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态
mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染
完毕,可以用 vm.$nextTick

vue2.0之后主动调用$destroy()不会移除dom节点,作者不推荐直接destroy这种做法,如果实在需要这样用可以在这个生命周期钩子中手动移除dom节点
**vue获取后端数据应该在created还是mounted**
看情况了,一般放到created里面就可以了,这样可以及早发请求获取数据,如果有依赖dom必须存在的情况,就放到mounted(){this.$nextTick(() => { /* code */ })}里面

**第一次页面加载会触发哪几个钩子**
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子

**DOM 渲染在 哪个周期中就已经完成**
DOM 渲染在 mounted 中就已经完成了

**简单描述每个周期具体适合哪些场景**

答:生命周期钩子的一些使用方法:

beforecreate : 可以在这加个loading事件,在加载实例时触发
实例初始化之后,this指向创建的实例,不能访问到data、computed、watch、methods上的方法和数据
常用于初始化非响应式变量

created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
实例创建完成,可访问data、computed、watch、methods上的方法和数据,未挂载到DOM,不能访问到el属性, el属性,el属性,ref属性内容为空数组
常用于简单的ajax请求,页面的初始化

mounted : 挂载元素,获取到DOM节点
$ref属性可以访问
常用于获取VNode信息和操作,ajax请求

updated : 如果对数据统一处理,在这里写上相应函数
虚拟 DOM 重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作
避免在这个钩子函数中操作数据,可能陷入死循环

beforeDestroy : 可以做一个确认停止事件的确认框
实例销毁之前调用。这一步,实例仍然完全可用,this仍能获取到实例
常用于销毁定时器、解绑全局事件、销毁插件对象等操作

destroyed
实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁

nextTick : 更新数据后立即操作dom

Document

beforeCreated: el和data并未初始化 created: 完成data数据的初始化,el没有 beforeMount: 完成了el和data初始化 mounted: 完成挂载

 **什么时候从后台获取数据组好?**
其实Vue并没有规定什么时候获取数据最好,我们从每个钩子函数定义便可知,只要在Vue实例创建之后,也就是created中及以后的所有钩子函数里都可以从后台获取数据。但是,这里我建议大家还是在created钩子函数就获取数据,然后进行数据操作。原因嘛就是,从beforeCreate到mounted,这中间是一个流程,而且是不受任何东西影响的,并且数据请求是个异步的过程,而生命周期并不是等待数据返回再接着运行。举个例子:

data(){ return{ source:[] } }, created(){ ...从后台获取数据 this.source = response//将data里数据赋值为获取到的数据 }, mounted(){ //假如有一个数组项值为'Alan' console.log(this.$refs.Alan) //这里可能会报错,此时的refs显示为未定义的Object的属性 } ``` 为什么可能会报错呢?其实,在大多数网络情况下,是肯定会报错。因为数据请求是个异步的操作,取决于网络情况和数据量的大小,谁都不知道什么时候才会返回完整的数据。而Vue的周期函数不会等待数据全部返回完毕后再接着从created往下走,而是一旦created了,就会接着beforeMount,然后mounted。所以,当我们在mounted里试图通过$refs属性获取v-for渲染的DOM时就会报错了,因为在mounted阶段,说不准数据还没返回完呢 那虚拟DOM就还没完全渲染出真实DOM。当然,只有通过v-for渲染的DOM才会受影响。其余的我们自己写的正常html标签都可以正常获取… 那么,应该怎样获取我们通过后台返回数据而渲染的DOM节点呢?

updated是个好东西
通过上文即可知道,updated是不需要渲染真实DOM之后才能调用的钩子。也就是说,我们不需要担心什么时候真实DOM才会被渲染完成。上述代码改成这样:

	<ul>
    <li v-for="item of source" :key="item" ref="item"></li>
</ul>
	data(){
    return{
        source:[]
    }
},
created(){
    ...从后台获取数据
    this.source = response//将data里数据赋值为获取到的数据
},
updated(){
    this.$nextTick(()=>{
        //假如有一个数组项值为'Alan'
        console.log(this.$refs.Alan)
        //这里可能会报错,此时的refs显示为未定义的Object的属性
    })
}

只要原始数据source发生了改变,就可以在updated里执行代码。这里要注意的是,我使用了一个Vue的自带方法 n e x t T i c k D O M V u e D O M ( ) D O M ( ) nextTick。这个方法的意思是把回调函数内的操作延迟到下一个DOM更新循环之后。这又是个啥玩意,其实Vue本身会将所有DOM更新的操作放入一个队列里,然后根据实际情况(应该会考虑到性能)一个接一个执行真实的DOM更新(局部更新), nextTick就是会将方法内回调函数的操作延迟到队列里下一个DOM更新后执行。也就是说,会等到source获取完毕,然后真实DOM渲染完成后才执行。 n e x t T i c k V u e 便 D O M 使 nextTick非常常用,只要涉及到数据更新,就应该执行这个方法。 **总结** Vue的生命周期函数为开发者提供了非常便利的操作。但是善用生命周期才会不踩坑,一定需要注意数据获取是个异步过程,而生命周期函数的运行是独立的!只要涉及到DOM更新的操作,一定要使用 nextTick!

vue中keep-alive

vue中slot
vue中的vuex
https://blog.csdn.net/weixin_40402192/article/details/80052887
https://blog.csdn.net/Evan_QB/article/details/81358352
https://blog.csdn.net/awake720/article/details/79657719
https://blog.csdn.net/weixin_38483133/article/details/89327362
https://www.csdn.net/gather_29/MtTaAg3sOTg3Mi1ibG9n.html
https://www.jianshu.com/p/4547221704d8

原理:

vuex的方案是,在vue中构建一个用于存储state、定义操作state方法的仓库(即store)。通过在多个(不一定是全部)组件中引用需要的state、调用“操作state的方法”来实现对给共享变量的处理。且由于各个组件对state是引用的,单个组件改变了某个state后,其他组件可以实时的响应变化。

Vuex 是什么?
是专门针对vue提供的状态管理模式。
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

为什么要用vuex?

为了更好的管理状态,在之前组件内部状态的应用中发现,每个组件的状态都是独立的,如果存在好多组件公用的状态通讯起来比较麻烦

vuex的出现,他是将所有数据状态集中式存储管理,变成了一个,在组件中以相应的规则去读取改变。
vuex的核心概念
state: 唯一数据源, 定义在store中的state选项,在组件中可以通过this.$store.state读取,一半都写在计算属性中,可以通过mapState()函数生成计算属性

getters:相当于组件中的计算属性, 定义在store中getters选项中,在组件中通过$store.getters获取,一半都写在计算属性中,可以通过mapGetters()函数生成计算属性

getters: {

计算属性名称 (state, getters) => {

return 返回值

}

}

mutation: 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,

并且它会接受 state 作为第一个参数, 其他参数为第二个, 通过commit可以执行他

注意:Mutation 必须是同步函数

因为mutation中除了改变state之外还将state改变的过程快照记录了下来,如果在mutation中写异步逻辑的话就无法记录快照了

actions: 用来提交mutations,可以处理异步逻辑,但是不能直接修改state,只能通过commit(‘mutation’)的形式改变state,actions函数接收一个context对象(类似于store实例)
vuex实现的作用
数据共享机制,通过统一的数据中心store维护状态数据,每个组件进行更新的时候,通知数据中心 store。再由stroe将共享的状态,触发每一个调用它的组件的更新。
vuex的流程
页面通过mapAction异步提交事件到action。action通过commit把对应参数同步提交到mutation。mutation会修改state中对于的值。
最后通过getter把对应值跑出去,在页面的计算属性中,通过mapGetter来动态获取state中的值

vuex的Mutation特性是?
一、Action 类似于 mutation,不同在于:
二、Action 提交的是 mutation,而不是直接变更状态。
三、Action 可以包含任意异步操作
vuex的优势
状态管理工具 核心是响应式的做到数据管理
一个页面发生数据变化。动态的改变对应的页面
相比使用localStorage ,localstorge只能纯属字符串数据格式,因此还得封装自己的写入写出,localstorage的优势是永久存储,兄弟之间组件有大量通信的,建议一定要用VUEX,不管大项目和小项目
vuex的工作流程
1.在vue组件里面,通过dispatch来触发actions提交修改数据的操作。

2.然后再通过actions的commit来触发mutations来修改数据。

3.mutations接收到commit的请求,就会自动通过Mutate来修改state(数据中心里面的数据状态)里面的数据。

4.最后由store触发每一个调用它的组件的更新
store.js中的基本结构是

import Vue from 'vue'

        mport Vuex from 'vuex'

        

        Vue.use(Vuex)

        

        const state = {   //状态 在视图里面渲染的基本都放在state里

                list:[],

                num:0

            //存放共享组件

        }

        const getters = { //计算过滤操作

            state中的变量:(state)=>{

                return  state.变量名+一些列的操作

            }

        }

        const mutation = {  //同步改变状态

            add(state){

                state.num++

            } 

            //可以放一些改变state的方法

        }

        const actions ={    //异步改变状态 在视图中调用actions里的方法一般使用  

                                        this,$store.dispatch('方法名')

            方法名(context){    //这里的context是一个上下文对象  相当于当前的store    也可以直接                 

                                            使用{commit}

                context.commit('调用mutation里的方法',自由参数)

            }

        }

        

        

        export default new Vuex.store({

            state,

            mutations,

            actions,

            getters,

            

        })



视图应用

        如果想把state里的数据映射在视图中,如liste:[]需要以下操作

        第一种:可以直接在组件中{{this.$store.state.list}}  //注意list是个数组需要循环

        第二种 可以在视图中引入mapState方法  

import  {mapState}  from 'Vuex'

                    然后在视图计算属性中

                    computed:{

                        ...mapState(['list'])    //es6的扩展符

                    }

                    然后在组件中直接使用    {{list}} //注意list是个数组需要循环

                    

               

             如果想把mutation里的方法想应用在视图中,如add需要以下操作

            第一种:可以直接在组件中事件的函数中“this.$store.commit("add",自由参)”  

            第二种 可以在视图中引入mapMutation方法  

                    import  {mapMutation}  from 'Vuex'

                然后在视图的方法中

                methods:{

                    ...mapMutation(['add'])

                }

                可以直接在组件中事件的函数中"add",自由参

            actions的应用与mutation类似

           getters的应用就是把state引进修改后return出去就好

vue导航钩子
vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。有多种方式可以在路由导航发生时执行钩子:全局的, 单个路由独享的, 或者组件级的。

全局钩子
const router = new VueRouter({ … }) router.beforeEach((to, from, next) => { // do something next(); }); router.afterEach((to, from, next) => { console.log(to.path); });

每个钩子方法接收三个参数:

to: Route : 即将要进入的目标 [路由对象]
from: Route : 当前导航正要离开的路由
next: Function : 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next方法的调用参数。
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed (确认的)。

next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from路由对应的地址。

next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

确保要调用 next方法,否则钩子就不会被 resolved。

组件内的钩子
你可以在路由组件内直接定义以下路由导航钩子:


beforeRouteEnter beforeRouteUpdate (2.2 新增) beforeRouteLeave

const Foo = {   template: `...`,   beforeRouteEnter (to, from, next) {    
 // 在渲染该组件的对应路由被 confirm 前调用    
  // 不!能!获取组件实例 `this`     
  // 因为当钩子执行前,组件实例还没被创建   
  },   
  beforeRouteUpdate (to, from, next) {    
   // 在当前路由改变,但是该组件被复用时调用    
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,     
   // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。    
   // 可以访问组件实例 `this`   
   },   
   beforeRouteLeave (to, from, next) {     
   // 导航离开该组件的对应路由时调用     
   // 可以访问组件实例 `this`   } }

beforeRouteEnter 钩子 不能 访问 this,因为钩子在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {  
 next(vm => {     // 通过 `vm` 访问组件实例   }) }

你可以 在 beforeRouteLeave 中直接访问 this。这个 leave 钩子通常用来禁止用户在还未保存修改前突然离开。可以通过 next(false) 来取消导航。

history和hash区别

需要使用 history模式,才能使用 scrollBehavior

实现的原理:

hash 模式的原理是 onhashchange 事件,可以在 window 对象上监听这个事件。

history :hashchange 只能改变 # 后面的代码片段,history api (pushState、replaceState、go、back、forward) 则给了前端完全的自由,通过在window对象上监听popState()事件。
前端路由的核心是:改变视图的同时不会向后端发出请求。

pushState()、replaceState() 方法接收三个参数:stateObj、title、url。
 
// 设置状态
history.pushState({color: "red"}, "red", "red");
 
// 监听状态
window.onpopstate = function(event){
    console.log(event.state);
    if(event.state && event.state.color === "red"){
        document.body.style.color = "red";
    }
}
 
// 改变状态
history.back();
history.forward();复制代码

应用场景:

通过 pushState 把页面的状态保存在 state 对象中,当页面的 url 再变回到这个 url 时,可以通过 event.state 取到这个 state 对象,从而可以对页面状态进行还原,如页面滚动条的位置、阅读进度、组件的开关等。

调用 history.pushState() 比使用 hash 存在的优势:

pushState 设置的 url 可以是同源下的任意 url ;而 hash 只能修改 # 后面的部分,因此只能设置当前 url 同文档的 url
pushState 设置的新的 url 可以与当前 url 一样,这样也会把记录添加到栈中;hash 设置的新值不能与原来的一样,一样的值不会触发动作将记录添加到栈中
pushState 通过 stateObject 参数可以将任何数据类型添加到记录中;hash 只能添加短字符串
pushState 可以设置额外的 title 属性供后续使用
劣势:

history 在刷新页面时,如果服务器中没有相应的响应或资源,就会出现404。因此,如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面
hash 模式下,仅 # 之前的内容包含在 http 请求中,对后端来说,即使没有对路由做到全面覆盖,也不会报 404

history模式的问题
通过history api,我们丢掉了丑陋的#,但是它也有个问题:不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的。
在hash模式下,前端路由修改的是#中的信息,而浏览器请求时不会将 # 后面的数据发送到后台,所以没有问题。但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,则会刷新出来404页面。

hash模式原理是利用了window可以监听onhashchange事件,也就是说url中的哈希值
如果变化了,前端可以做到监听并做一些响应,这样,即使前端没有发起http请求,也能够找到对应页面代码进行按需加载
hash 虽然出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面
hisory模式,有5个api,go,back,forward,pushState,replaceState,其中
pushstate和replace作用是可以将url替换并且不刷新页面,http也并没有去请求服务器该
路径下的资源,

history 利用了 html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈,在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求
但是history模式下刷新会报404,需要在服务器那端,将不存在的路径请求重定向到入口文件
总之,pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应

总结
传统的路由指的是:当用户访问一个url时,对应的服务器会接收这个请求,然后解析url中的路径,从而执行对应的处理逻辑。这样就完成了一次路由分发。

而前端路由是不涉及服务器的,是前端利用hash或者HTML5的history API来实现的,一般用于不同内容的展示和切换。

----------------------------- 补充 -----------------------------

history模式下,build之后本地 index.html 打开是无效的。

hash模式下,build之后本地 index.html 打开正常!

大牛解答:hash模式url里面永远带着#号,我们在开发当中默认使用这个模式。那么什么时候要用history模式呢?如果用户考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url适合推广宣传。当然其功能也有区别,比如我们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react做的,咱们把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式,但是使用history模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404错误,那么就需要和后端人配合让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok啦

vue路由钩子函数
https://juejin.im/post/5b10b46df265da6e2a08a724
全局前置守卫 router.beforeEach
全局解析守卫 router.beforeResolve
全局后置钩子 router.afterEach
路由独享的守卫 beforeEnter

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内的守卫 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不能获取组件实例 `this`
    // 因为当钩子执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

beforeRouteEnter 钩子 不能 访问 this,因为钩子在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

你可以 在 beforeRouteLeave 中直接访问 this。这个 leave 钩子通常用来禁止用户在还未保存修改前突然离开。可以通过 next(false) 来取消导航。

同时注意必须有这个next(),相当于一个按钮开启一样。

1、beforeRouteEnter(to,from,next)

    beforeRouteEnter 函数内部 this 是undefined,这是因为在执行路由钩子函数beforRouteEnter时候,组件还没有被创建出来;先执行beforRouteEnter,再执行组件周期钩子函数beforeCreate。我们可以通过 next 获取组件的实例对象,如:next( (vm)=>{} ),参数vm就是组件的实例化对象。
    使用场景:
    比如在进入列表页前需要提前对列表页的组件路由进行判断,判断是否需要刷新数据,获取新的列表内容,如果滚动行为是undefined(说明是刷新页面或是第一次进入页面)或者null(说明是点击了导航连接),此时需要刷新数据,获取新的列表内容,否则的话什么都不需要做

router/index.js 的配置如下:

import Vue from 'vue';
import Router from 'vue-router';
// import HelloWorld from '@/views/HelloWorld';Vue.use(Router);

scrollBehavior (to, from, savedPosition) { 
// 保存到 meta 中,备用 
to.meta.savedPosition = savedPosition;
 if (savedPosition) {  
 return { x: 0, y: 0 }; 
 } 
 return {};
  }
  });

list.vue 代码如下

<template> <div class="hello"> <h1>vue</h1> <h2>{{msg}}</h2> <router-link to="/detail">跳转到detail页</router-link> </div></template> <script>export default { name: 'helloworld', data () { return {  msg: 'Welcome to Your Vue.js App' }; }, methods: { ajaxRequest() {  const obj = {  'aa': 1  };  Promise.all([this.$store.dispatch('testUrl', obj)]).then((res) => {  console.log(res);  }); } }, 
beforeRouteEnter(to, from, next) { 
next(vm => { 
 /*
   如果 to.meta.savedPosition === undefined 说明是刷新页面或可以叫第一次进入页面 需要刷新数据  如果savedPosition === null, 那么说明是点击了导航链接;  此时需要刷新数据,获取新的列表内容。  否则的话 什么都不做,直接使用 keep-alive中的缓存  */  
   if (to.meta.savedPosition === undefined) { 
   vm.ajaxRequest();  
   } 
    if (to.meta.savedPosition === null) { 
     vm.ajaxRequest();  
     }
      }) 
      }
      };
    </script>

2、beforeRouteUpdate(to,from,next)

    About组件是有二级导航的,在切换二级导航的时候,对应的内容是在变化的;但是about组件是复用的,只会生成一次,切换二级导航的时,如何知道导航在更新呢?

    一个组件有二级导航的时候,点击二级导航的时候导航路径更新了,会触发路由钩子函数beforeRouteUpdate。

3、beforeRouteLeave(to,from,next)

    当在about切换到user时,about页面有些数据还没有加载完成,这时候我们不让它切换到user。
<template>
    <div>
        我是about
        <hr>
        <ul class="subnave f-cb">
            <!-- a标签中href属性不需要写地址,页面编译完成后自动会在href中加入对应的路劲 -->
            <router-link :to='{name:"About"}' exact tag="li">
                <a>study</a>
            </router-link>
            <router-link :to='{name:"Work"}' tag="li">
                <a>work</a>
            </router-link>
            <router-link :to='{name:"Hobby"}' tag="li">
                <a>hobby</a>
            </router-link>
        </ul>
        测试数据:{{test}}
        <router-view></router-view>
    </div>
</template>

<script>
    export default {
        data(){
            return {
                test:'改变之前'
            }
        },
        beforeCreate(){//组件生命周期函数
            console.log('beforeCreate')
        },
        //当进入组件之前,执行 beforRouteEnter 路由钩子函数
        beforeRouteEnter(to,from,next){
            console.log('beforRouteEnter')
            console.log(this) // 结果为undefined,因为在执行beforRouteEnter时候,组件还没有被创建出来;先执行beforRouteEnter,再执行beforeCreate
            next((vm)=>{ //参数vm就是当前组件的实例。
                vm.test = '我被改变了'
            })
        },
        beforeRouteUpdate(to,from,next){
            console.log('beforeRouteUpdate')
            next()
        },
        beforeRouteLeave(to,from,next){//离开组件的时候触发
            //什么都不写的时候,不会离开(走下一步)
            next()
        }
    }
</script>

路由钩子在实际开发中的应用场景
  路由钩子在实际的开发过程中使用较少, 我在实际的项目中只在组件内使用过beforeRouteLeave, 使用场景分别为一下三类情况:

1、清除当前组件中的定时器

当一个组件中有一个定时器时, 在路由进行切换的时候, 可使用beforeRouteLeave将定时器进行清楚, 以免占用内存:


beforeRouteLeave (to, from, next) {
 window.clearInterval(this.timer) //清除定时器
 next()
}

2、当页面中有未关闭的窗口, 或未保存的内容时, 阻止页面跳转

如果页面内有重要的信息需要用户保存后才能进行跳转, 或者有弹出框的情况. 应该阻止用户跳转

beforeRouteLeave (to, from, next) {
 //判断是否弹出框的状态和保存信息与否
 if (this.dialogVisibility === true) {
  this.dialogVisibility = false //关闭弹出框
  next(false) //回到当前页面, 阻止页面跳转
 }else if(this.saveMessage === false) {
  //弹出警告
  next(false) //回到当前页面, 阻止页面跳转
 }else {
  next() //否则允许跳转
 }
}

3、保存相关内容到Vuex中或Session中

当用户需要关闭页面时, 可以将公用的信息保存到session或Vuex中

beforeRouteLeave (to, from, next) {
  localStorage.setItem(name, content); //保存到localStorage中
  next()
}

go,replace,push区别
route 和 router 的区别是什么?
route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
router是“路由实例对象”,包括了路由的跳转方法(push、replace),钩子函数等。
vue指令
v-text

解释:更新元素的 textContent
v-html

解释:更新元素的 innerHTML

v-bind
作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
语法:v-bind:title=“msg”
简写::title=“msg”

v-on

作用:绑定事件
语法:v-on:click=“say” or v-on:click=“say(‘参数’, $event)”
简写:@click=“say”
说明:绑定的事件从methods中获取

事件修饰符

.stop 阻止冒泡,调用 event.stopPropagation()
.prevent 阻止默认事件,调用 event.preventDefault()
.capture 添加事件侦听器时使用事件捕获模式
.self 只当事件在该元素本身(比如不是子元素)触发时触发回调
.once 事件只触发一次

v-model

作用:在表单元素上创建双向数据绑定
说明:监听用户的输入事件以更新数据

v-for

作用:基于源数据多次渲染元素或模板块

v-for循环key属性
vue中的v-for循环最好加上key属性,否则在高版本(2.2.0+)的vue中控制台会报错。

key属性需要唯一,理想的 key 值是每项都有唯一 id,全局不需唯一,但在一个循环中需要唯一

为什么使用key?
当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容

key属性

推荐:使用 v-for 的时候提供 key 属性,以获得性能提升。
说明:使用 key,VUE会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

样式处理 -class和style

说明:这两个都是HTML元素的属性,使用v-bind,只需要通过表达式计算出字符串结果即可
表达式的类型:字符串、数组、对象
语法:

===>
为什么避免 v-if 和 v-for 用在一起
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。

v-if 和 v-show
条件渲染
v-if:根据表达式的值的真假条件,销毁或重建元素
v-show:根据表达式之真假值,切换元素的 display CSS 属性

不同点:

1 . v-if 当值为 true时,显示div ,

当值为false时,改元素消失,代码也会消失,

相当于将代码删除了,当在为true时,页面会重新渲染div; 

支持加在<template>标签上



而v-show 控制的隐藏出现,

只是将css属性设为了display:none 或block;

不支持加在标签上

2.v-if 后还有 v-else 和 v-else-if 条件渲染,

这里需要注意的是v-else 必须紧跟 v-if 或v-else-if

3.v-if是真真正正的条件渲染;

然而他是惰性的,

在初始值是false的时候,他就什么都不做,

在为真的时候才会开始局部变异

相比之下v-show则是更简单一下,仅仅是css上的切换

所以,v-if有更高的切换消耗,

而v-show有更高的初始渲染消耗;

因此,如果是频繁切换,就用v-show;

在条件很难改变,比如某个模块在用户a出显示,就用v-if

提升用户体验:v-cloak

这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
防止刷新页面,网速慢的情况下出现{{ message }}等数据格式

{{ message }}
提升性能:v-pre

说明:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
{{ this will not be compiled }}

提升性能:v-once

说明:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
This will never change: {{msg}}
vue框架的理解
vue是(渐进式表现:)声明式渲染—组件系统—客户端路由—大数据状态管理—构建工具的一款渐进式框架

vue的两个核心点
(1)响应式数据绑定
当数据发生变化的时候,视图自动更新,即双向数据同步,原理利用了ES6中的 Object.definedProperty 中的setter/getter 代理数据,监控对数据的操作。
(2)组合的视图组件
即页面最终映射为一个组件树,采用树形数据结构进行设计,方便维护,重用

vue路由params和query区别
传参可以使用params和query两种方式。
使用params传参只能用name来引入路由,即push里面只能是name:’xxxx’,不能是path:’/xxx’,因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!!。
使用query传参使用path来引入路由。
params是路由的一部分,必须要在路由后面添加参数名。query是拼接在url后面的参数,没有也没关系。
二者还有点区别,直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会再地址栏中显示。
https://blog.csdn.net/mf_717714/article/details/81945218
vue-diff算法-虚拟dom
为什么使用 diff 算法?
  1、页面结构庞大时,DOM 操作代价太高,可维护性差,因此要减少 DOM 操作;

2、虚拟 DOM 很轻量,对虚拟 DOM 操作快;

3、diff 算法 是找出本次 DOM 需要更新的节点进行更新,
  其余不更新,对 DOM 进行原地复用,减少 DOM 创建性能耗费,
  可以减少浏览器页面的重绘。
diff算法就是虚拟dom

1.虚拟DOM
virtual DOM和真实DOM的区别?
虚拟dom是将真实dom数据抽取出来,以对象的形式模拟树形结构
比如:
真实dom是这样的

<div>
    <p>123</p>
</div>

对应的虚拟dom:

var Vnode = {
     tag:'div'
     children:[
          {
       tag:'p',text:'123'
}
]
}

渲染真实dom的开销很大,比如有时候我们修改了某个数据,如果直接渲染到真实dom上,会
引起整个dom数的重绘和重排,有没有可能我们只更新我们修改的那一小块dom而不要更新整个dom呢,diff算法可以实现

我们先根据真实dom生成一个虚拟dom,当虚拟dom某个节点的数据改变后会生成一个新的vnode,然后vnode和oldvnode对比,发现有不一样的地方就直接修改在真实的dom上,然后使oldvode的值为vnode

diff的过程就是调用名为patch的函数,比较新旧节点,一遍比较一边给真实的dom打补丁

二、diff算法
传统的diff算法
传统的Diff算法通过循环递归对节点进行比较,然后判断每个节点的状态以及要做的操作(add,remove,change),最后 根据Virtual DOM进行DOM的渲染。

Vue的Diff算法与上面的思路大体相似,只比较同级的节点,若找不到与新节点类型相同的节点,则插入一个新节点,若有相同类型的节点则进行节点属性的更新,最后删除新节点列表中不包含的旧节点。具体的实现在vue源码的src/core/vdom/patch.js中的updateChildren方法中,由于代码较长,下面简单说一下整个的比较流程

三、vue中diff实现
vnode分类
EmptyVNode: 没有内容的注释节点
TextVNode:文本节点
ElementVNode: 普通元素节点
ComponentVNode: 组件节点
CloneVNode: 克隆节点,可以是以上任意类型的节点,唯一的区别在于isCloned属性为true

** patch函数接收6个参数:**

oldVnode: 旧的虚拟节点或旧的真实dom节点
vnode: 新的虚拟节点
hydrating: 是否要跟真实dom混合
removeOnly: 特殊的flag,用于
parentElm: 父节点
refElm: 新节点将插入到refElm之前
代码逻辑

如果vnode不存在,但是oldVnode存在,说明是需要销毁旧节点,则调用invokeDestroyHook(oldVnode)来销毁oldVnode。
如果vnode存在,但是oldVnode不存在,说明是需要创建新节点,则调用createElm来创建新节点。
当vnode和oldVnode都存在时
oldVnode和vnode是同一个节点,就调用patchVnode来进行patch
当vnode和oldVnode不是同一个节点时, 如果oldVnode是元素节点,需要用hydrate函数将虚拟dom和真是dom进行映射
如果oldVnode是真实节点时或vnode和oldVnode不是同一节点时,vnode替换oldVnode。如果组件根节点被替换,遍历更新父节点element。然后移除旧节点。
createElm 创建真实的 DOM 对象
vnode 根据vnode的数据结构创建真实的dom节点,如果vnode有children则会遍历这些子节点,递归调用createElm方法
insertedVnodeQueue记录子节点创建顺序的队列,每创建一个dom元素就会往队列中插入当前的vnode,当整个vnode对象全部转换成为真实的dom 树时,会依次调用这个队列中vnode hook的insert方法
新节点将插入到refElm之前

** 代码逻辑 **
从分析patch方法中,我们知道当vnode和oldVnode都存在,并且vnode和oldVnode是同一节点时,才会调用patchVnode进行patch。
下面来看来patchVnode执行原理

如果oldVnode和vnode完全一致,则可认为没有变化,return;
如果oldVnode的isAsyncPlaceholder属性为true时,跳过检查异步组件,return;
如果oldVnode跟vnode都是静态节点(实例不会发生变化),且具有相同的key,并且当vnode是克隆节点或是v-once指令控制的节点时,只需要把oldVnode.elm和oldVnode.child都复制到vnode上,也不用再有其他操作,return;
否则,如果vnode不是文本节点或注释节点
如果vnode和oldVnode都有子节点并且两者的子节点不一致时,就调用updateChildren更新子节点
如果只有vnode有子节点,则调用addVnodes创建子节点
如果只有oldVnode有子节点,则调用removeVnodes把这些子节点都删除
如果vnode文本为undefined,则清空vnode.elm文本;
如果vnode是文本节点但是vnode.text != oldVnode.text时只需要更新vnode.elm的文本内容就可以。

key的作用
不设key,newCh和oldCh只会进行头尾两端的相互比较,设key后,除了头尾两端的比较外,还会从用key生成的对象oldKeyToIdx中查找匹配的节点,所以为节点设置key可以更高效的利用dom。
举个例子:
我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的

在这里插入图片描述

在这里插入图片描述

vue中的key属性
MVVM模式和MVC模式
什么是MVVM呢?

答:M:后端实体,V:前端HTML,VM:前端实体。

后端M用来从数据库装载数据给前端VM,前端VM用来替换V中插值表达式来填充数据,同时V的变化可以动态更新到VM,V再加上指令,就可以基本不用操作DOM,靠前端MVVM框架通过指令去渲染页面了。

MVVM 是 Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式, 其核心是提供对 View 和 ViewModel 的双向数据绑定,这使得 ViewModel 的状态改变可以自动传递给 View,即所谓的数据双向绑定。
M:Model(数据层,也就是指数据(前端是js))
V:View ( 也就是指DOM层 或用户界面 )
VM : ViewModel (处理数据和界面的中间层,也就是指Vue) Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。

在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?
主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。

区别:vue数据驱动,通过数据来显示视图层而不是节点操作。

场景:数据操作比较多的场景,更加便捷

VNode是什么?虚拟 DOM是什么?
Vue在 页面上渲染的节点,及其子节点称为“虚拟节点 (Virtual Node)”,简写为“VNode”。“虚拟 DOM”是由 Vue 组件树建立起来的整个 VNode 树的称呼。
vue-loader是什么?使用它的用途有哪些?
解析.vue文件的一个加载器。

用途:js可以写es6、style样式可以scss或less、template可以加jade等
vue中 key 值的作用
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

所以概括来说key的作用主要是为了高效准确的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

使用key来给每个节点做一个唯一标识

Vue 组件中 data 为什么必须是函数
在 new Vue() 中,data 是可以作为一个对象进行操作的,然而在 component 中,data 只能以函数的形式存在,不能直接将对象赋值给它。
当data选项是一个函数的时候,每个实例可以维护一份被返回对象的独立的拷贝,这样各个实例中的data不会相互影响,是独立的

v-for 与 v-if 的优先级
v-for的优先级比v-if高
计算属性的 set get 如何使用

每一个计算属性都包含一个getter 和一个setter ;
绝大多数情况下,我们只会用默认的getter 方法来读取一个计算属性,在业务中很少用到setter,所以在声明一个计算属性时,可以直接使用默认的写法,不必将getter 和setter 都声明。
但在你需要时,也可以提供一个setter 函数, 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发setter 函数,

计算属性和watch的区别
在我们运用vue的时候一定少不了用计算属性computed和watch
computed计算属性是用来声明式的描述一个值依赖了其它的值。当你在模板里把数据绑定到一个计算属性上时,Vue 会在其依赖的任何值导致该计算属性改变时更新 DOM。这个功能非常强大,它可以让你的代码更加声明式、数据驱动并且易于维护。
watch监听的是你定义的变量,当你定义的变量的值发生变化时,调用对应的方法。
就好在div写一个表达式name,data里写入num和lastname,firstname,在watch里当num的值发生变化时,就会调用num的方法,方法里面的形参对应的是num的新值和旧值,
而计算属性computed,计算的是Name依赖的值,它不能计算在data中已经定义过的变量

Vue中如何在组件内部实现一个双向数据绑定?
假设有一个输入框组件,用户输入时,同步父组件页面中的数据
具体思路:父组件通过 props 传值给子组件,子组件通过 $emit 来通知父组件修改相应的props值,具体实现如下

解决Vue同路由跳转后数据不更新
场景:需要根据不同的 $route.query 请求不同的数据

问题:由于使用 VueRouter 跳转时组件实例会被复用,跳转后无法拿到最新的 $route.query,并且地址栏的 url 不会变化

解决方法1
此方法一劳永逸,但是页面渲染速度会降低

指定页面

<!-- :key="$route.fullPath" 解决同路由但不同查询参数跳转后数据不更新 -->
<router-view
  v-if="$route.name === 'match_id-match'"
  :key="$route.fullPath"
/>
<router-view v-else />

所有页面

<!-- :key="$route.fullPath" 解决同路由但不同查询参数跳转后数据不更新 -->
<router-view :key="$route.fullPath" />

解决方法2

beforeRouteUpdate()
使用 VueRouter 的导航守卫钩子 beforeRouteUpdate(),在里面写上路由更新前你需要重新执行的代码

示例:

beforeRouteUpdate(to, from, next) {
    this.zoneId = to.query.zone_id || null // 赛区id
    this._getMatchZones() // 根据this.zoneId,请求不同数据
    next()
},

本人决定使用了解决方法2,因为不想降低页面渲染速度

vue监听路由的变化,跳转到同一个页面时,Url改变但视图未重新加载问题
解决方法:

添加路由监听,路由改变时执行监听方法

methods:{
      fetchData(){
           console.log('路由发送变化doing...');
     }
  },
  created() {
        var self = this;
        self.fetchData();
  },
  watch:{
      '$route':'fetchData'
  },

vue-router在同一个路由下切换,取不到变化的路由参数
最近用vue写项目的时候碰到一个问题,在同一个页面下跳转,路由地址不变,路由参数有变化,一开始只是在data里取路由的参数,发现根本取不到变化的路由参数。

在网上查找了一番后发现可以这样写:


watch: {
  '$route' (to, from) {
  //这样就可以获取到变化的参数了,然后执行参数变化后相应的逻辑就行了
    console.log(this.$route.query)
  }
}

https://www.cnblogs.com/sophie_wang/p/7880261.html

https://www.jb51.net/article/144337.htm

https://www.jb51.net/article/131110.htm

发布了45 篇原创文章 · 获赞 4 · 访问量 1050

猜你喜欢

转载自blog.csdn.net/weixin_44990056/article/details/104317364