一、什么是路由,路由的实现原理
路由:根据URL地址,跳转到相应页面,vue中通过使用路由进行组件切换
路由填充位(也叫做路由占位符):<router-view></router-view>
将来通过路由规则匹配到的组件,将会被渲染到 router-view
所在的位置
路由导航守卫都有那些钩子函数?说明在什么场景下会触发这些钩子函数。
vue-router
的导航钩子,主要用来作用是拦截导航,让他完成跳转或取消。
全局守卫
router.beforeEach
:全局前置守卫,进入路由之前
router.afterEach
:全局后置钩子,进入路由之后
路由组件内的守卫
beforeRouteEnter()
:进入路由前
beforeRouteUpdate()
:路由复用同一个组件时
beforeRouteLeave()
:离开当前路由时
独享守卫
beforeEnter:
进入路由之前
$route
和 $router
的区别
router
为VueRouter
的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象,例如history
对象。经常用的跳转链接就可以用this.$router.push
,和router-link
跳转一样。。。
route
相当于当前正在跳转的路由对象。可以从里面获取name,path,params,query
等。。
vue-router
的两种模式
1.hash模式 :URL
的hash
是以#
开头,是基于location.hash
来实现的。Location.hash
的值就是URL中#
后面的内容。当hash
改变时,页面不会因此刷新,浏览器也不会请求服务器。
同时,hash
改变时,并触出发相应的hashchange
事件,所以,hash
很适合被用来做前端路由。当hash
路由发生跳转,便会触发hashchange
回调,回调里面就可以实现页面的更新操作,从而达到跳转页面的效果。
2.HTML的history
模式:history
丢掉了#,操作中不怕前进和后退,就怕刷新,如果没有服务端的支持,刷新之后就会去请求服务器,由于找不到相应的支持响应或者资源,就会报出404页面。
history api
可以分为两大部分:切换和修改
1、切换历史状态包括back、forward、go
这三个方法,对应浏览器的前进,后退,跳转操作;(跳转操作:在前进后退上长按鼠标,会出来所有当前窗口的历史记录,从而可以跳转)
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进
2、修改历史状态包括了 pushState, replaceState
两个方法
这两个方法接收三个参数:stateObj, title, url
window.history.pushState(state, title, url)
// state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
// title:标题,基本没用,一般传 null
// url:设定新的历史记录的 url。新的 url 与当前 url 的 origin 必须是一樣的,否则会抛出错误。url可以是绝对路径,也可以是相对路径。
//如 当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, './qq/'),则变成 https://www.baidu.com/a/qq/,
//执行history.pushState(null, null, '/qq/'),则变成 https://www.baidu.com/qq/
window.history.replaceState(state, title, url)
// 与 pushState 基本相同,但她是修改当前历史记录,而 pushState 是创建新的历史记录
window.addEventListener("popstate", function() {
// 监听浏览器前进后退事件,pushState 与 replaceState 方法不会触发
});
使用history模式出现404解决方法:
使用nginx
服务器
配置nginx
这里的配置意思是将所有的文件都指向Index.html
文件,然后再给一个404的错误提示页面
server {
listen 8089; // 端口
server_name localhost; // 访问地址,默认是本机
location / {
try_files $uri $uri/ @router; // 需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
index index.html index.htm;
}
// 对应上面的@router,主要原因是路由的路径资源并不是一个真实的路径,所以无法找到具体的文件
#因此需要rewrite到index.html中,然后交给路由在处理请求资源
location @router {
rewrite ^.*$ /index.html last;
}
两种模式的区别
1. history
设置的新url可以是与当前url同源的任意值;hash
只可修改 # 后面的部分,实质上是同文档的url;
2. history
设置的新url可以与当前url相同,依然会把记录添加到栈中;hash
必须不一样才会添加;
3. history
通过stateObject参数可以添加任意类型的数据到记录中;hash
只可添加短字符串;
4.history
可额外设置title属性;
路由懒加载的三种方法
1.vue异步组件
/* vue异步组件技术 */
{
path: '/home',
name: 'home',
component: resolve => require(['@/components/home'],resolve)
},{
path: '/index',
name: 'Index',
component: resolve => require(['@/components/index'],resolve)
}
2.es提案的import()
const List = () => import('@/components/list.vue')
const router = new VueRouter({
routes: [
{ path: '/list', component: List }
]
})
3.webpack
的require,ensure()
/* 组件懒加载方案三: webpack提供的require.ensure() */
{
path: '/home',
name: 'home',
component: r => require.ensure([], () => r(require('@/components/home')), 'demo')
}, {
path: '/index',
name: 'Index',
component: r => require.ensure([], () => r(require('@/components/index')), 'demo')
}
一、CSS性能优化
1.尽量使用缩写:p { margin: 1px 2px 3px 4px; }
2.查找并删除未使用的 CSS
3. 文件压缩
4.避免需要性能要求的属性(border-radius box-shadow transform filter :nth-child position: fixed;)
5. 优化重排与重绘
避免不必要的重绘:当元素的外观(如color,background,visibility
等属性)发生改变时,会触发重绘。在网站的使用过程中,重绘是无法避免的。不过,浏览器对此做了优化,它会将多次的重排、重绘操作合并为一次执行。
不过我们仍需要避免不必要的重绘,如页面滚动时触发的hover事件,可以在滚动的时候禁用hover事件,这样页面在滚动时会更加流畅。
6.减少使用 @import, 而建议使用link, 因为后者在页面加载时一起加载,前者是等待页面加载完成之后再进行加载;
1、首先,使用@import
引入CSS会影响浏览器的并行下载。使用@import
引用的CSS文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,然后下载后开始解析、构建render tree等一系列操作。
这就导致浏览器无法并行下载所需的样式文件。
2、其次,多个@import
会导致下载顺序紊乱。在IE中,@import
会引发资源文件的下载顺序被打乱,即排列在@import
后面的js文件先于@import
下载,并且打乱甚至破坏@import
自身的并行下载。
二、javascript定时器,取消定时器,及js定时器优化方法
一般不用setInterval,而用setTimeout的延时递归来代替interval。
setInterval会产生回调堆积,特别是时间很短的时候。
三、v-model的原理,还有没有其他方法实现?
v-model
只是一个语法糖,等于:value+@input
,真正的实现靠的还是: v-bind:绑定响应式数据,触发 input 事件并传递数据 (核心和重点)
<input v-model="something">
其实和下面一样的
<input :value="something" @input="something = $event.target.value">
因此,对于一个带有 v-model 的输入框组件,它应该:接收一个 value prop,触发 input 事件,并传入新值
自定义myInput组件
<template>
<div style="padding-top: 100px;">
<button @click="minu" class="btn">-</button>
<input type="text" :value="currentValue" @input="handleInput" />
<button @click="plus" class="btn">+</button>
</div>
</template>
<script>
export default {
props:['value'],
data () {
return {}
},
computed:{
currentValue:function(){
return this.value
}
},
methods:{
handleInput:function(event){
this.$emit('input', event.target.value); //触发 input 事件,并传入新值
},
minu:function(){
this.$emit('input', this.currentValue - 1 );
},
plus:function(){
this.$emit('input', this.currentValue + 1 );
},
}
}
</script>
<style type="text/css">
.btn{
width: 60px;
}
</style>
总结:
自定义的组件内部一般包含原生的表单组件(当然非表单的组件也可以),然后给原生控件绑定事件,捕捉到原生组件的值,利用$emit
方法,触发input
方法,组件监听到input
事件然后把值传入
v-model
在内部为不同的输入元素使用不同的属性并抛出不同的事件:
text
和textarea
元素使用value
属性和input
事件;
checkbox
和radio
使用checked
属性和change
事件;
select
字段将value
作为prop
并将change
作为事件。
六、Vue中的created和mounted的区别
created
:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted
:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
八、splice,slice,substr,substring区别总结
1.
splice()
:删除、添加原数组;数组使用,会改变原数组;参数一:要操作的位置下标(即在该下标开始进行添加或删除);参数二:要删除的数组个数;参数三:在下标为参数一的位置添加的内容;返回值为删除各项所组成的新数组,无删除时则返回空数组。
var str = ["as","dfg","hj"];
var str_splice = str.splice(1,0,"hh");
console.log(str); // ["as","hh","dfg","hj"]
console.log(str_splice); //[]
2.
slice():
数组、字符串位置截取;数组、字符串均可使用;参数一:开始截取的下标;参数二:截取的截止位置但不包含该下标;返回截取的新数组。
var str = ["as","hh","dfg","hj"];
var str_slice = str.slice(1,3); //["hh","dfg"]
3.
substr()
截取指定长度字符串;字符串使用,用于截取指定长度字符串;参数一:开始截取的下标位置;参数二:要截取的字符串长度;返回截取的新字符串。
var str = "as-dfg-hj";
var str_substr = str.substr(1,4); //s-df
4.
substring()
字符串位置截取;字符串使用;参数一:开始截取位置;参数二:结束截取下标,但不包含该下标字符;返回截取的新字符串;(第二个参数应该大于第一个参数,如果出现第一个参数大于第二个参数的情况,substring
方法会自动更换两个参数的位置)
var str = "as-dfg-hj";
var str_substring = str.substring(1,4); //s-d
十、vuex
-
State:所有共享的数据都要统一放到 Store 的 State 中进行存储
组件访问 State 中数据的:this.$store.state.全局数据名称
组件访问 State 中数据的第二种方式:从 vuex 中按需导入 mapState 函数 -
Mutation
用于变更 Store中 的数据。
① 只能通过mutation
变更Store
数据,不可以直接操作Store
中的数据。
② 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。
this.$store.commit()
是触发mutations
的方法 -
Action
用于处理异步任务。
如果通过异步操作变更数据,必须通过Action
,而不能使用Mutation
,但是在Action
中还是要通过触发Mutation
的方式间接变更数据。
this.$store.dispatch()
是触发actions
的方法
-
Getter
用于对Store
中的数据进行加工处理形成新的数据。
①Getter
可以对Store
中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性。
②Store
中数据发生变化,Getter
的数据也会跟着变化。
使用方法:this.$store.getters
.名称
十一、为什么 vue 组件中 data 必须是一个函数?
对象为引用类型,当复用组件时,由于数据对象都指向同一个 data 对象,当在一个组件中修改 data 时,其他重用的组件中的 data 会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object 的实例),引用地址不同,则不会出现这个问题。
十五、Vue中子组件修改父组件传递过来的props,并不影响父组件的原始数据
这仅限于props为非数组及对象等引用类型数据,譬如字符串,数字等
如果props是对象或数组的话,在子组件内修改props的话,父组件是不会报错的。
那么要怎么解决修改props传的值而不污染父组件的值:
1,可以使用ES6提供的Object.assign({}, prop)的返回值就是一个全新的对象,操作这个新对象不会影响旧对象。如果不用ES6就自己递归实现拷贝器
2,可以给对象重新赋值:(给对象里的每一项重新赋值)
十六、vuex传递数据页面刷新
页面刷新
store.state
中的数据消失是不可避免的,那么使用localStorage
来避免这个问题。发现问题的时候我就考虑到存数据在localStorage
里,但是一个一个数据添加实在是太蠢了。那么就需要一个全局的方法来,将store
的数据存储在localStorage
里。具体的方法也是百度的很好用,也很方便。
在App.vue中,created
初始化生命周期中写入以下方法
//在页面刷新时将vuex里的信息保存到localStorage里
window.addEventListener("beforeunload",()=>{
localStorage.setItem("messageStore",JSON.stringify(this.$store.state))
})
//在页面加载时读取localStorage里的状态信息
localStorage.getItem("messageStore") && this.$store.replaceState(Object.assign(this.$store.state,JSON.parse(localStorage.getItem("messageStore"))));
replaceState
这个方法呢,查了api,就是替换 store
的根状态,然后通过对象赋值assign
将localStorage
进行赋值
beforeunload
这个方法是在页面刷新时触发的,将store
中的信息存入localStorage
这样就通过localStorage
来避免vuex
刷新数据消失的问题了。
十八、原生JS获取HTML DOM元素的8种方法
十九、获取url参数转为对象
把字符串转为对象
// 把字符串转化为对象
var str = 'id=1&name=zhangsan&age=18&className=2003'
// {id:1,name:zhangsan,age:18,className:2003}
// 先把字符串转化为 数组
var arr = str.split('&');
// arr = ['id=1','name=zhangsan','age=18','className=2003']
var obj = {};
for (var i = 0; i < arr.length; i++) {
// arr[i].split('=') ['id',1]
var newArr = arr[i].split('=');
// ['id','1'] ['name':'zhangsan']
obj[newArr[0]] = newArr[1];
}
console.log(obj);
把对象转为字符串
// 把对象转化为字符串
var obj1 = {
id: 1,
name: 'zhangsan',
age: 18,
className: 2003
}
// 'id=1&name=zhangsan&age=18&className=2003'
var str1 = '';
// 遍历对象
for (var key in obj1) {
str1 += key + '=' + obj1[key] + '&';
}
// 去掉最后一个 &
console.log(str1.substr(0, str1.length - 1));
封装字符串转为对象函数
var str = 'id=1&name=fqniu&age=25&workName=frontEnd'
// strUrlObj 为字符串转为对象的函数名
function strUrlObj(string) {
// 去掉字符串中的 & 的数组
let strArray = string.split('&')
//console.log(strArray) //["id=1", "name=fqniu", "age=25", "workName=frontEnd"]
// 定义一个空对象
let obj = {}
for (let i = 0; i < strArray.length; i++) {
// 定义一个新的数组来接收 去掉字符串中的 = 的数组
let newArray = strArray[i].split('=')
// console.log(newArray)
// 然后遍历添加每个数组中对应的数据 添加到对象中
obj[newArray[0]] = newArray[1]
}
return obj
}
console.log(strUrlObj(str))
//{id: "1", name: "fqniu", age: "18",workName: "frontEnd"}
二十、for…in与for…of的区别
for in
返回数组的下标(key);
for of
返回数组的元素;
for of
不可以遍历普通对象,想要遍历对象的属性,可以用for in循环, 或Object.keys()
方法。
二十一、Vue 常见性能优化方式
合理使用
v-show
和v-if
合理使用computed
v-for 时要加key
,以及避免和v-if
同时使用
自定义事件、DOM 事件及时销毁
合理使用异步组件
合理使用keep-alive
data
层级不要太深(因为深度监听一次性监听到底)
使用vue-loader
在开发环境做模板编译(预编译)
webpack
层面的优化
二十二、 target、currentTarget 的区别?
currentTarget
当前所绑定事件的元素
target
当前被点击的元素
二十三、 用 js 递归的方式写 1 到 100 求和?
function add(num1,num2){
var num = num1+num2;
if(num2+1>100){
return num;
} else {
return add(num,num2+1)
}
}
var sum =add(1,2);
console.log(sum)