-
五、Vue\
- vue的特点:virtual dom tree(虚拟dom树)、dom tree(真实dom树)、diff算法,以及双向数据绑定\
- Object.defineProperty是一个无法被shim的属性,就是说它无法被降级使用,这也是vuejs不支持ie8以下的根本原因。\
-
✔render函数\
- template标签在vue内部会被编译成render函数\
- 使用render函数的好处在于我们可以动态的创建标签\
-
render函数的参数是createElement函数\
-
createElement有三个参数\
- 第一个参数,必需,string或object或function,是要渲染的html标签\
- 第二个参数,可选,object,html标签的各种属性\
- 第三个参数,可选,string或array,子虚拟节点(Vnodes),当前html标签的子元素\
-
渲染过程:\
- render => newVnode(虚拟DOM) => patch(vnode, newVnode) diff算法 => 视图更新\
-
render(createElement){\
- return createElement(h1, {}, '我是h1')\
- }\
-
-
✔props:{}\
- Heading 子组件中:props: { tag: String }\
- 从父组件获得参数:\
- 子组件中调用this.tag\
-
✔vue怎么实现更新的(render)\
- 更新的过程:render => newVnode(虚拟DOM) => patch(vnode, newVnode) diff算法 => 视图更新\
-
首次渲染:\
- 1.render函数,给组件里定义的数据设置响应式(get方法、set方法)\
- 2.通过模板编译出来的render函数使用到的数据,通过touch去获取数据\
- 3.生成渲染树,渲染到页面上,这样就完成了首次渲染\
-
更新:\
- 1.touch之后,data属性被作为dependency依赖,被watcher观察起来(每个组件的实例都会有watcher实例)\
- 2.watcher记录所有被touch过的属性\
- 3.模板没有使用到的属性,没有被touch,所以不会被watcher观察记录起来\
-
4.触发重新渲染:\
- 当被watcher观察的数据修改时,会触发重新渲染(re-render)\
- 触发重新渲染时,所有被观察的属性watcher会看一下是不是被修改了\
- 最后重新进行render的过程,生成渲染树\
-
✔mvc和mvvm的区别\
-
二者区别:\
- 1.mvvm各部分的通信是双向的,而mvc各部分通信是单向的\
- 2.mvvm是真正将页面与数据逻辑分离放到js里实现,而mvc里面未分离\
-
mvc概念:\
- 用户操作会请求服务端路由,路由会调用对应的控制器来处理,控制器会获取数据。将结果返回给前端,页面重新渲染\
-
mvvm概念:\
- 不需要用户收到操作 dom 元素,将数据绑定到 viewModel 层上,会自动将数据渲染到页面中,视图变化会通知 viewModel层 更新数据\
- ViewModel 就是我们 MVVM 模式中的桥梁\
-
-
***虚拟dom,diff算法(vue的和传统的区别)\
- snabbdom是著名的虚拟DOM库,是diff算法的鼻祖,Vue源码借鉴了snabbdom\
-
虚拟DOM\
- 把真实DOM变成JS对象\
- 真实dom性能低\
- 提升性能\
-
diff算法\
- 最小量更新\
- 会同层比较,不会跨层比较\
- diff算法的作用:进行精细化比对,实现最小量更新\
-
vue2中的diff\
-
递归+双指针\
- 1.判断是不是同一个元素,不是就直接替换\
- 2.是同一个元素,比对属性,比对儿子\
-
-
vue3中的diff\
- 采用最长递归子序列\
- diff优化的策略\
- diff更新子节点的策略\
- 手写diff 原理\
- 公共的bus\
-
✔父子组件通信(props:{} + attrs、ref + this.$refs)\
-
1.通过props:{} + $emit()的方式\
-
父向子props:[]\
- 父组件中<Child :message="message" @changeMessage="message = $event" />\
- 子组件中props: ["message"]\
-
子向父$emit()\
- 父组件中<Child :message="message" @changeMessage="message = $event" />\
- 子组件中写一个方法handleClick修改this.$emit("changeMessage", "Bye")\
- 注意:event能接收参数Bye\
-
-
2.使用回调函数的方式\
-
子向父\
-
父组件中\
- 有一个methods为changeMsgFn修改this.message = "Byee";\
- 子组件中props: ["message", "changeMsgFn"],\
-
-
-
3.children的方式(vue3中无效)\
- 父组件:this.$children[0].number;\
- 子组件:this.$parent.message\
-
4.provide:{} + inject:{} 的方式\
- 父组件中provide:{ provideMsg: "Hello World" }\
- 子组件中inject: ["provideMsg"]\
-
5.listeners 的方式\
-
子组件中使用$attrs会包含父组件传递的所有参数,直接调用即可\
- 注意:此时Child组件中的props不能包含孙子组件要获取的属性\
- 子组件中使用$listeners会包含父组件给子组件的事件\
-
-
6.ref="XXX" + this.$refs.XXX属性的方式\
-
父组件h5中使用注册子组件\
- 在script中使用this.refs.childComp.changeAge\
- 子组件中定义age数据和changeAge方法\
-
-
- 兄弟组件通信、跨层级组件通信\
-
✔vue生命周期、父子生命周期的顺序\
- 生命周期钩子=生命周期函数=生命周期事件\
- 这给了用户在不同阶段添加自己的代码的机会。\
-
生命周期过程:\
-
1.创建期间\
- beforeCreate(vue实例的挂载元素el和数据对象data都为undefined ,还未初始化 ,也就是说此时我们还拿不到数据)\
- created(vue实例的数据对象data有了 ,但是实例对象el还没有 ,也就是说可以拿到数据 ,但是无法获取Dom节点,此时data 和 methods已经可以使用 但是页面还没有渲染出来)\
- beforeMount(vue实例的$el和data都初始化了 ,但还是挂载之前的虚拟DOM节点 ,此时实例尚未挂载完成 ,此时页面上还看不到真实数据 只是一个模板页面而已)\
- mounted(vue实例挂载完成 ,页面成功渲染数据已经真实渲染到页面上 在这个钩子函数里面我们可以使用一些第三方的插件)\
-
2.运行期间\
- beforeUpdate(在实例销毁之前调用 ,在这一步,实例仍然完全可用)\
- updated(实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁)\
-
3.销毁期间\
- beforeDestroy\
- destroyed\
-
-
用途\
- beforeCreate 此时页面开始创建 ,可以加loading……事件 ,加载实列时触发\
- created 一些数据异步请求的调用 ,loading……事件结束等\
- mounted 获取Dom节点操作DOM ,在这个钩子函数里面我们可以使用一些第三方的插件实例写在这个函数内\
- updated 做一些数据统一更新处理的相应函数\
-
父子生命周期的顺序\
- 父先创建,子再创建\
- 子先挂载,父再挂载\
-
✔Vue双向绑定(v-model)\
- Vue 2将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter\
-
v-model\
- 原理 :会将组件的 v-model 默认转化成value+input\
- 在表单 、 及 元素上创建双向数据绑定\
-
组件的v-model 就是value+input的语法糖\
- <el-checkbox :value="" @input="">\
- 等价于\
- \
-
✔Vuex\
-
什么是Vuex?\
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理插件。它采用集中式存储管理应用的所有组件的状态,而更改状态的唯一方法是提交mutation\
-
Vuex解决了什么问题?\
- 多个组件依赖于同一状态时。多层嵌套的组件的传参将会非常繁琐\
- 来自不同组件的行为需要变更同一状态。以往采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码\
-
什么时候用Vuex?\
- 多个组件依赖于同一状态时\
- 来自不同组件的行为需要变更同一状态\
-
Vuex的5个核心属性是什么?\
- 分别是 state、getters、mutations(可以写异步,但是插件比较差)、actions(可以写异步)、modules\
-
-
✔vue-router 实现懒加载(路由懒加载)\
- 如果直接import引入路由,则在一开始全部加载完\
- 路由懒加载后,访问到路由时才会下载下来\
- 作用:缩短了首屏渲染的时间,性能优化\
-
附加:\
- /* webpackChunkName: "about" */决定了懒加载js文件的名字\
- 不写,则成为0.js\
-
1.vue路由懒加载作用?写法?实现原理?\
-
作用:\
- 1.让打包后的代码变小,打包成多个文件\
- 2.并且当访问到对应的路由时,才去加载对应的JS代码,可以加快页面打开速度\
-
怎么实现的:\
- 定义路由时,把import静态导入改成import()函数动态导入\
- 改完之后,webpack读到这些代码时,webpack编译的时候会打包成单独的文件\
- vue-router访问对应路径的时候会请求加载对应的JS文件\
-
-
2.E6 import()动态导入\
- import函数是ES6提供的动态导入的函数(import()返回是一个Promise练习的对象,其实可以.then())\
- import()函数返回是一个Promise练习的对象,也是一个性能优化\
- webpack发现import动态导入的时候,会把导入的文件单独打包生成一个独立文件xxx.js\
- 3.webpack代码切割\
-
性能优化:\
- 1.import导入用花括号,用到webpack 5的自动用导\
- 2.import()函数返回是一个Promise对象\
-
✔JavaScript图片懒加载\
-
方法1:scroll\
- 在h5中设置img属性data-src\
- addEventListener绑定scroll事件\
- 获取const imageTop = image.getBoundingClientRect().top\
- 如果小于imageTop < window.innerHeight\
-
执行:修改img的属性data-src为src\
- const data_src = image.getAttribute('data-src')\
- image.setAttribute('src', data_src)\
-
方法2:intersectionObserver(交叉观察)\
- 是浏览器提供的构造函数,可以直接使用\
- 观察目标元素和窗口的交叉区域,发生什么事件,触发什么方法\
-
1.const observer = new IntersectionObserver(callback回调函数)\
- 开始观察observer.observe(DOM节点)\
- 结束观察observer.unobserve(DOM节点)\
- 2.对DOM元素images调用forEach方法每个绑定observer.observe(DOM元素)\
-
3.在callback回调函数中接收参数entries(数组)\
- 遍历entries数组绑定形参entry\
- 如果entry元素的isIntersecting为true,则看见了图片(DOM元素)\
- 通过entry.target获取相应的图片节点\
- 获取自定义属性data-src,修改为src,同时结束观察observer.unobserve(DOM元素)\
-
-
✔slot(插槽)和slot-scope\
-
slot作用:\
- 让用户可以拓展组件,去更好地复用组件和对其做定制化处理\
- 父组件中测试,会被插入到子组件的当中\
-
默认插槽\
- 在父组件的template模板里面只能访问父组件的数据,不能访问子组件的数据\
- 具名插槽\
-
作用域插槽\
- 子组件v-bind绑定数据,父组件v-slot获取数据\
-
-
✔🍗Hash路由 和 History路由的区别和原理\
-
区别:\
- 最明显的区别在于URL上是否有# 号,有# 代表使用hash模式,没有# 代表使用history模式\
- URL显示:hash有#;history没有#\
- 回车刷新:hash可以加载到hash值对应的页面;history一般就是404了(history的问题)\
- 支持版本:hash支持低版本IE;history是HTML5新出的API\
-
原理:\
-
HashRouter :\
- hash 模式是一种把前端路由的路径用井号 # 拼接在真实 url 后面的模式\
- 当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 onhashchange 事件。\
- 监听hash值的变化,重新渲染视图\
-
HistoryRouter:\
- history API 是 H5 提供的新特性,允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求。\
-
-
HashRouter路由\
-
hash的改变有三种情况:\
- 1.点击时改变\
- 2.手动的改变window.onhashchange\
- 3.点击按钮时通过JS修改location.href\
- 页面刚开始时打印hash值:监听DOMContentLoaded\
- hash的好处:不会刷新页面,用户可以在不刷新页面的前提下看到新页面,用户体验更好\
-
-
HistoryRouter路由\
- 切换路由history.pushState(state, '', 'user')\
- 监听浏览器前进后退的事件window.onpopstate\
- vue-router默认 hash 模式,还有一种是history模式。\
-
-
webpack和webpack-dev-server\
-
作用:\
- webpack提供模块化开发打包功能(import/export模块化编程)\
- webpack-dev-server提供代码热更新,让JS在网页上进行调试\
-
为什么要使用webpack?\
- 1.解决面向过程中代码不可维护的问题\
- 2.解决面向对象中下载多个js文件消耗时间的问题\
- 3.解决了面向对象不清楚来源,难以查错的问题\
-
如何使用webpack-dev-server?\
-
为什么要使用webpack-dev-server?\
- 实时热更新,提高开发效率\
-
-
-
前端工程化\
- Webpack、Gulp\