IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 关于react、vue的一些问题 -> 正文阅读

[JavaScript知识库]关于react、vue的一些问题

DVA整合reducer,initialState、action、saga

app.model({
  namespace: 'products',
  state: {
    list: [],
    loading: false,
  },
    在dom ready之后运行
  subscriptions: [
    function(dispatch) {
      dispatch({type: 'products/query'});
    },
  ],
  effects: {
    ['products/query']: function*() {
      yield call(delay(800));
      yield put({
        type: 'products/query/success',
        payload: ['ant-tool', 'roof'],
      });
    },
  },
  reducers: {
    ['products/query'](state) {
      return { ...state, loading: true, };
    },
    ['products/query/success'](state, { payload }) {
      return { ...state, loading: false, list: payload };
    },
  },
});

介绍下这些 model 的 key :(假设你已经熟悉了 redux, redux-saga 这一套应用架构)

  • namespace - 对应 reducer 在 combine 到 rootReducer 时的 key 值
  • state - 对应 reducer 的 initialState
  • subscription - elm@0.17 的新概念,在 dom ready 后执行,这里不展开解释,详见:A Farewell to FRP
  • effects - 对应 saga,并简化了使用
  • reducers

虚拟DOM

使用普通js来描述DOM结构

在react、vue等技术出现之前,我们要改变页面内容只能通过遍历查询dom树的方式找到要修改的dom然后修改样式或结构来达到更新dom的目的,这种方式非常消耗资源(操作dom会引起浏览器的回流和重绘)。但是如果建立一个与dom树相应的虚拟dom对象(js对象),对象嵌套的方式表示dom树及其层级结构,这样对dom的更改就变成了对js对象的操作,性能开销也变小。

Vue和React的区别

  • vue提供一些便捷的指令和属性

  • React使用JSX模板,允许HTML和js混写,Vue基于html的模板语法

  • 在响应式方面,Vue的数据改动时,界面会自动更新,React需要调动setState方法。

    ? 把两者称为Push-basedPull-based。所谓Push-based就是说,改动数据之后,数据本身会把这个改动推送出去,告知渲染系统自动进行渲染。在React里面,它是一个Pull的形式,用户要给系统一个明确的信号说明现在需要重新渲染了,这个系统才会重新渲染

React

React响应式原理

  • 开发者只需要关注数据,当状态改变时,React框架会自动根据新的状态重新渲染构建UI。
  • React框架在接受到状态改变时,会根据当前渲染树,结合最新的状态改变通过Diff算法计算出树中改变的部分,只更新变化的部分,从而避免整棵树重构,提高性能。状态改变后React不会立即去计算并渲染DOM树变化的部分,react会在DOM的基础上建立一个抽象层,即虚拟DOM树,对数据和状态的任何变化都会自动且高效的同步到虚拟DOM,最后在批量同步到真实DOM上,而不是每次改变都去操作DOM。

React中的setState

? React中的state不能直接修改,需要调用setState()的方法来修改。

? setState执行的时候可以简单认为属于原生js执行的空间,那么属于同步,而被react处理过的空间属于异步空间,但是如果多次使用setState修改state,react为了提高性能会按批次更新state然后render即异步操作,所以setState()后立即取state中的值并不是更新之后的状态,如果想要取得更新之后的state可以在setState()的第二个参数callback中取得新值。setState()有两个参数,第一个参数可以是Object或者函数,当参数是Object时,对应的key,value就是更新后的值,如果参数是函数,setState()会将上一次更新的结果作为参数传入这个函数中。

React组件通信的方式

  • 父组件向子组件传递props
  • 父组件将一个回调函数传入子组件,子组件调用该函数可以向父组件通信
  • 使用context可以避免组件嵌套传参,provider生产者产生数据,consumer消费者在各个组件中传递参数
  • 利用组件共同的父组件通信
  • redux,dva,mobx 在view中触发action,改变state,进而改变其他组件的view
  • 引用ref可以获得子组件的实例
  • Render Props 渲染的细节由父组件控制

Redux和Vuex的区别

  • Vuex改进了Redux中的action和reducer函数,用mutations变化函数取代reducer,无需switch,只需要在对应的mutation函数改变state值就可以。
  • Vuex由于Vue的自动重新渲染特性,不需要重新订阅渲染函数render,只要生成新的state就可以。
  • Vuex数据流的顺序是View调用store.commit提交对应的请求到Store中对应的mutation函数 ,让store改变state(vue检测到数据变化自动渲染)

redux-saga中遇到的坑

? saga是一个系列的工作文件,拦截action执行,执行异步操作,put出一个新的action改变reducer

  • saga不会强迫我们捕获异常
  • generator调试环境糟糕,babel的source-map会错位
  • action定义谨慎,避免action在saga和reducer之间重复触发

Hooks和redux

Hooks

? Hooks是在react16.8版本中出现的一个新功能,是一种函数,它可以实现组件的逻辑复用,可以让我们在函数组件中使用类组件中的状态和生命周期等功能,hook都是以use开头

  1. useState 创建状态 返回数组,第一个参数是状态,第二个是改变状态的函数
  2. useEffect 副作用函数(数据获取,页面渲染结束后执行的操作)第一个参数是执行的操作,第二个参数是依赖列表,如果没有第二个参数,则会在第一个渲染结束和每次更新渲染页面的时候都会执行该函数
  3. useRef 创建ref ,此索引在整个生命周期都不会变,可以获取组件或者元素的实例,可以做输入框聚焦和动画
  4. useMemo 可以做函数组件优化,只有当依赖改变时才会触发,和usCallback作用类似
  5. useCallback 类似useMemo
  6. useContext 获取上下文注入值 可以结构出provider和consumer两个方法 provider生产者传递数据,consumer消费者可以获取数据
  7. useLayoutEffect 和useEffect类似,在DOM更新之后执行,执行顺序早于useEfffect,DOM更新完以后才会渲染。

redux

redux是一个可预测的状态容器,实质是单向数据流的思想。

redux三大特性:单一数据流、状态state是只读的、状态修改事由纯函数完成

redux由action、reducer、store组成

  • action是view发出的通知state发生变化的动作
  • reducer 接收action和当前的state作为参数,是改变state的具体计算过程
  • store 是保存数据的地方。 redux提供createStore这个函数来生成store
    • store.getState 获取当前时刻的state
    • store.dispatch view派发 action的方法
    • store.subscribe 设置监听函数 可以监听state,当state改变时可以做相应的操作。

Fiber

? Fiber是React16中新的协调引擎,主要目的是使虚拟DOM进行增量式渲染,可以类比generator函数。fiber可以认为是最小的工作单元,提升对于动画,布局,手势等场景的适用性,核心功能:增量渲染,将渲染工作分解为多个区块并将其散落到每一帧中。

  • 暂停工作,并回来
  • 为不同类型任务分配等级
  • 重用以前完成的工作
  • 不需要的时候终止任务

为什么不建议在componentWillMount做Ajax操作?

在React16之后将会废弃这个钩子,虽然可以使用,但是会有警告,而且会增加渲染的时间出现白屏现象。在React16之后采用了Fiber架构,只有componentDidMount钩子是确定执行一次的,componentWillMount钩子有可能会执行多次。在render阶段,可能会被暂停,中止或重启。

React的diff算法规则是什么

diff算法即差异查找算法,对于HTML DOM结构就是tree的差异查找算法,只有在更新阶段才会有diff算法的应用,react为了降低时间复杂度是按照层比较新旧两个虚拟dom树。

  1. tree diff 新旧两颗dom树,逐层对比的过程就是tree diff,当整颗dom树逐层对比完成后,那必然会找到需要更新的元素

  2. component diff 在进行tree diff 的时候每一层都有自己的组件,组件级别的对比就是component diff ,如果对比前后组件的类型不同,则需要移除旧组件创建新的组件渲染到页面。react只会匹配类型相同的组件,如果A组件被B替换,react会直接删除A然后创建B组件;如果A组件转移到同层B组件上,那么A会 被销毁,在B组件下重新生成,以A为根节点的树整个都被重建,虽然会浪费性能,但实际上很少跨层移动dom节点,一般都是同层横向移动。

  3. element diff 在进行组件对比时,如果两个组件类型相同,需要进行元素级别的对比,叫做element diff

    对于列表渲染,react在创建时需要为每一项输入一个独一无二的key,方便进行diff运算,在比较新旧子元素的时候是通过key值来精确判断两个节点是否是同一个,如果没有key见谁就更新谁,消耗性能

styled-component的使用

首先引入styled  import styled from  ‘styled-compont’

在文件中通过 const  name=styled.div`
		样式
`
styled.dom标签名
例:
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;
render(
    <Title>
      Hello styled-component
    </Title>
);

React状态管理工具都有哪些,Redux actionCreator都有什么?

mobx、react等

Redux actionCreator用来创建action,通常只用来返回action对象

Vue和React中Diff算法的区别

都是忽略跨级比较,只做同级比较。vue diff时调动patch函数,参数是vnode和oldVnode,分别代表新旧节点。

  • vue对比节点,当节点元素相同但是classname不同时就会被认为是不同类型的元素,会删除重建,react认为是同类型节点只是修改属性。
  • vue的列表对比,采用的是两端到中间的对比方式,而react采用的是从左到右依次对比的方式。当一个集合只是把最后一个节点移动到第一个,react会把前面的节点依次移动,vue只会把最后一个节点移到第一个。总体上说vue更加高效。

为什么有时连续setState只有一次生效

如果连续setState同一个值,批量更新策略会对其进行覆盖,取最后一次执行,如果同时setState多个不同的值,在更新时会对其进行合并批量更新。如果想对上次setState后的结果进行下一次setState,可以让setState接收一个函数,这个函数用上一个state作为第一个参数,这次被更新的props作为第二个参数

React JSX组件渲染过程

  • 使用JSX编写的组件后所有JSX代码通过babel转化为React.createELement执行
  • createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,对传入的子节点进行处理,最终构成一个ReactElement对象,即所谓的虚拟DOM
  • ReactDOM.render将生成好的虚拟DOM渲染到指定的容器上,采用了批处理、事务等机制并对特定浏览器进行性能优化最终转换为真实DOM

Redux中间件原理?异步中间件thunk和saga如何使用

redux添加中间件 applyMiddleware

redux中间件的执行时机在action发出和reducer执行之间,中间件的实质就是对redux中store.dispatch函数的再封装。(要保持reducer的纯净)

thunk

import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'

export default createStore(
    rootReducer,
    applyMiddleware(thunk)
)   

redux-thunk是把异步操作放在action里面,而saga是把异步逻辑拆分出来放在一个异步文件里面。

thunk检查action的类型,如果是函数就执行这个action,并把dispatch、getState, extraArgument 作为参数传递进去,否则就调用 next 让下一个中间件继续处理 action。

saga

redux-saga比redux-thunk更加强大,不止可以充当中间件的作用,可以实现多种代码效果

import $http from '@/utils/http'
import { put,takeEvery,call } from 'redux-saga/effects'
import {LOADLIST,GETLIST,LOADSHOES,GETSHOES,LOADCLOTHES,GETCLOTHES} from './actionType'

//saga中间件中做axios请求
function* foo(){
    let recommend=yield call($http.get,{
        url:'recommend.json'
    })
    let shoes=yield call($http.get,{
        url:'shoes.json'
    })
    let clothes=yield call($http.get,{
        url:'clothes.json'
    })
    yield put({
        type:LOADLIST,
        list:recommend.data.data.hotList
    })
    yield put({
        type:LOADSHOES,
        shoes:shoes.data.data.list
    })
    yield put({
        type:LOADCLOTHES,
        clothes:clothes.data.data.list
    })

    // console.log(shoes);
}

function* saga(){
    yield takeEvery(GETLIST,foo)
    yield takeEvery(GETSHOES,foo)
    // yield takeEvery(GETCLOTHES,foo)
}
export default saga
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { helloSaga } from './sagas'
import reducer from 'xxxx'

const sagaMiddleware = createSagaMiddleware()

const store = createStore(
  reducer,	// 这个reducer是用combineReducers综合导出的reducer
  applyMiddleware(sagaMiddleware)	// 声明使用saga中间件
)

sagaMiddleware.run(helloSaga)

异步调用

  • sagas被实现为Genetator函数,他会yield对象到 redux-saga middleware
  • yield的对象都是一类指令,可被middleware解释执行
  • 当middleware取得一个yield后的Promise,middleware会暂停saga,直到该promise完成
  • 一旦promise被reslove,middleware会恢复saga执行,直到遇到下一个yield

takeEvery可以执行多个saga

put 使用功能和dispatch一样,一般只在saga函数内部使用。

call和fork

这两个函数主要都是用来发起异步操作的(如发送请求),不同的是 call 发起的是阻塞操作,即必须等待该异步操作完全完成才能进行后续的操作,而 fork 是非阻塞的,因此可以使用 fork 来实现多个并发的请求操作(fork相当于生成了一个task——一个在后台运行的进程)

call(fn, …args):第一个参数是一个generator函数(也可以是一个返回Promise或任意其它值的普通函数),后面的参数都是依次传入这个函数的形参数值,返回值是promise的resolve结果

call默认是接收到promise成功标志时解除阻塞,但是如果是普通函数的话,可能达不到阻塞的效果。

React加载组件的方式

子组件、children、render函数

为什么使用自定义hooks

hooks都是use开头的函数,可以把公用的逻辑封装起来复用,缩减嵌套,实现代码拆分

设置父组件传值的类型

可以使用props-types这个插件

传值校验
子组件名.propTypes= {
	avname= PropTypes.number.isRequired
	content:PropTypes.string,
	index:PRopTypes.number
}
设置默认值
子组件名.defaultProps = {
	avname:'松岛枫’
}

React顶层DOM对象

  • React.Children 组件中的节点
  • React.cloneElement type(ReactElement),[props(object)],[children(ReactElement)] 克隆并返回一个新的ReactElement(内部子元素也会跟着克隆),新返回的元素会保留有旧元素的props,ref,key,也会集成新的props(只要在第二个参数中有定义)
  • React.createElement…

根据原来的组件生成新的组件,添加属性

  • HOC高阶组件
  • 顶层API React.cloneEmelent

Mobx和redux的区别

Mobx

创建store,对store中的数据进行监听

 constructor(title) {
    makeObservable(this, {
      finished: observable,
      title: observable,
      changeFinished: action
    })
    this.title = title
  }

mobx是面向对象编程的状态管理库,在action中定义改变状态的函数,在store中集中管理state和action。mobx状态可变,可以直接修改。mobx没有一个全局状态树,状态分散在各个独立的store中。

mobx流程:view中触发action,在action中修改state,通过computed拿到state的计算值,自动触发对应的reactions,这里包含autorun,渲染视图等。

原理:使用Object.defineProperty拦截数据的访问,一旦值发生变化,将会调用react的render方法重新渲染视图或者触发autorun。

autorun

当autorun函数中依赖的可观察属性发生改变时,就会自动触发autorun函数的执行,可以执行一些副作用操作。它不同于computed,不会产生一个新值,可以用来打印日志等。

observable

observable装饰器可以装饰一个属性,当这个属性发生改变时就会触发相应的动作,这个属性的类型可以包括string、boolean、array、object等

oberver

当可观察属性改变时,调用render方法重新渲染视图

makeObservable

手动为store中的属性添加类型,有两个参数,第一个是当前环境的this,第二个参数是对象,在这个对象中为属性添加响应式或者指定action和computed,

key为属性,value是要指定的类型。

 makeObservable(this, {
      finished: observable,
      title: observable,
      changeFinished: action
    })

makeAutoObservable

自动为store中的属性指定observable和action等

一个参数就是当前环境的this

 constructor(todos) {
    makeAutoObservable(this)
    this.todos = todos
  }

Redux和mobx的区别

  • redux将数据保存在单一的store中,mobx的数据分散在多个store中
  • redux使用 plainobject保存数据,需要手动出炉变化后的操作,mobxobservable保存数据,并且数据变化后自动处理响应操作
  • redux状态不可变,不能直接修改状态,应该返回一个新的状态,使用纯函数;mobx状态可变,也可以直接修改
  • mobx面向对象的编程,redux面向函数式编程。

为什么redux的reducer为什么要设计成不能直接修改原状态,而返回修新状态对象

  • 源码上来说redux会对reducer返回的状态进行引用地址的比较,不同才更新,直接修改源码不会更新
  • 设计角度,要知道redux返回的状态是否有改变,必须对状态对象进行深度比较,但是这样比较消耗性能,所以仅进行状态对象引用地址的比较,有开发者决定是否更新,返回新状态才更新。

React优化

  • 使用纯组件
  • 使用React.memo进行组件记忆(高阶组件)对于相同的输入不重复执行
  • 在类组件中可以使用shouldComponentUpdate生命周期函数,决定是否重新渲染组件,使用immutable对象
  • 不使用内联函数和内联样式属性,内联函数会在每次重新渲染时创建一个新的函数
  • 列表函数添加key值
  • 函数组件中可以使用useCallback和useMemo进行组件优化,依赖不改变不重复执行

React的事件代理

React 基于 Virtual DOM 实现了一个 SyntheticEvent (合成事件)层,我们所定义的事件处理器会接收到一个 SyntheticEvent 对象的实例,它完全符合 W3C 标准,不会存在任何 IE 标准的兼容性问题。并且与原生的浏览器事件一样拥有同样的接口,同样支持事件的冒泡机制,我们可以使用 stopPropagation() 和 preventDefault() 来中断它。

react-router里的标签和标签有什么区别

link的跳转行为只会触发相匹配的页面内容更新,而不会刷新整个页面,他做了三件事情:

  1. 有onclick就执行onclick
  2. click的时候阻止a标签默认事件
  3. 根据href(就是to),用history跳转,只是连接改变,但是没有刷新页面。

a标签就是普通的超链接,用于从当前页面挑战到href指向的另一个页面

a标签默认事件禁止后做什么猜实现跳转

DOM操作获取所有的a标签,循环列表为每一个a添加click事件

let domArr=document.getElementsByTagName(‘a’)
[…domArr].forEach(item=>{
item.addEventListener(‘click’,function(){
location.href=this.href;
})
})

使用import时,webpack对node_modules里的依赖会做什么

  • import只是一个引用,在没有用到的时候不会执行
  • 需要执行的时候去模块里面取值,webpack根据引入方式去判断模块的类型,进行相关转译,babel会默认把ES6的模块转译为CommonJS规范,然后把node_modues里面的依赖打包成自执行函数,模块会传到数组里面,函数运作之后将模块通过module.exports导出

Vue

Object.defineProperty、Proxy

? vue2使用Object.defineProperty里面的setter和getter方法的观察者模式来实现响应式,但是Object.Property存在以下几种缺陷

  • 无法检测到对象属性的新增或删除

  • 无法检测数组的变化,只能通过重新构造数组的部分方法来实现响应式(push、pop、shift、splice、sort、reverse)

    vue3使用Proxy来代替Object.defineProperty实现了数据响应,Proxy的优势如下:

  • Proxy可以直接监听对象而不是属性,可以直接监听数组的变化

  • Proxy有多大13种拦截方法,不限于apply、ownKyes、deleteProperty、has等,这些是Object.defineProperty不具有的。

  • Proxy返回的是一个新对象,我们可以只操作新对象达到目的,而Object.defineProperty只能遍历对象的属性直接修改属性

    但是Proxy存在浏览器兼容的问题并且无法使用垫片(polyfill)弥补

Vue响应式数据原理

? vue的响应式主要利用了Object.defineProperty中的getter和setter方法的观察者模式来实现,在组件初始化时会给每一个data属性注册getter和setter,然后new一个自己的watcher对象,这时watcher会立即调用组件的render函数去生成虚拟DOM。调用render时就会用到data属性值,此时会触发getter函数,将当前的watcher函数注册进sub。当data属性值改变时会遍历sub里面的所有watcher对象,通知他们去重新渲染组件。

computed和watch区别

computed

  • 支持缓存,只有当依赖数据发生改变才会重新计算
  • 不支持异步,有异步操作时操作无效,无法监听数据变化
  • 如果一个属性是由其他属性计算而来的,这属性依赖其他属性,是一个一对多或者一对一,一般用computed
  • 如果computed属性值是函数,默认会走get方法,函数返回值就是属性的属性值;在computed中属性都有一个get和set方法,当数据变化时调用set方法。

watch

  • 不支持缓存,数据变化直接触发相应的操作

  • 支持异步函数

  • 监听的函数接收两个参数,第一个是最新的值,第二个是输入之前的值

  • 监听数据必须是data中声明过或者是父组件传递过来的props中的数据,watch有两个参数

    ? immediate:

    ? 这样使用watch时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。

    比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true

    new Vue({
      el: '#root',
      data: {
        cityName: ''
      },
      watch: {
        cityName: {
          handler(newName, oldName) {
            // ...      },
          immediate: true
        }
      } 
    
    

    deep

    ? 当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。

    ? 数组(一维、多维)的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。

    new Vue({
      el: '#root',
      data: {
        cityName: {id: 1, name: 'shanghai'}
      },
      watch: {
        cityName: {
          handler(newName, oldName) {      // ...    },
        deep: true,
        immediate: true
        }
      } 
    
    

    设置deep: true 则可以监听到cityName.name的变化,此时会给cityName的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性:

    watch: {
    'cityName.name': {
          handler(newName, oldName) { 
         // ...    
      },
          deep: true,
          immediate: true
        }
    
    

    watch工作原理

    watch在一开始初始化时,会读取一遍监听的数据的值,这个数据会被收集到watch的watcher,然后给watch设置的handler会被放进watcher的更新函数中,当数据改变时,通知watch的watcher进行更新,设置的handler就会被调用进行更新操作。

Vue组件间通信

  • 父组件向子组件传值通过props

  • 子组件向父组件传值 通过事件的形式 $emit

    ? 子组件

    ?

    changeTitle() {
          this.$emit("titleChanged","子向父组件传值");//自定义事件  传递值“子向父组件传值”
        }
    

    父组件

    <app-header v-on:titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致
       // updateTitle($event)接受传递过来的文字
    
     methods:{
        updateTitle(e){   //声明这个函数
          this.title = e;
        }
      },
    
  • 中央总线(bus总线)$emit / $on 通过一个空的vue实例作为中央事件总线,用它触发事件和监听事件,可以实现任何组件间通信。

    ?

    <div id="itany">
        <my-a></my-a>
        <my-b></my-b>
        <my-c></my-c>
    </div>
    <template id="a">
      <div>
        <h3>A组件:{{name}}</h3>
        <button @click="send">将数据发送给C组件</button>
      </div>
    </template>
    <template id="b">
      <div>
        <h3>B组件:{{age}}</h3>
        <button @click="send">将数组发送给C组件</button>
      </div>
    </template>
    <template id="c">
      <div>
        <h3>C组件:{{name}}{{age}}</h3>
      </div>
    </template>
    <script>
    var Event = new Vue();//定义一个空的Vue实例
    var A = {
        template: '#a',
        data() {
          return {
            name: 'tom'
          }
        },
        methods: {
          send() {
            Event.$emit('data-a', this.name);
          }
        }
    }
    var B = {
        template: '#b',
        data() {
          return {
            age: 20
          }
        },
        methods: {
          send() {
            Event.$emit('data-b', this.age);
          }
        }
    }
    var C = {
        template: '#c',
        data() {
          return {
            name: '',
            age: ""
          }
        },
        mounted() {//在模板编译完成后执行
         Event.$on('data-a',name => {
             this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
         })
         Event.$on('data-b',age => {
             this.age = age;
         })
        }
    }
    var vm = new Vue({
        el: '#itany',
        components: {
          'my-a': A,
          'my-b': B,
          'my-c': C
        }
    }); 
    </script>
    

    ?

  • vuex

  • $attr $listeners 组件间嵌套传递参数

  • provide inject

  • $parent $children 访问父/子实例

  • ref 获取组件实例

Vue-router原理

通过改变url在不重新请求页面的情况下,更新视图。有两种方式实现

  1. Hash 通过hashChange监听url的变化,每次改变hash都会在浏览器访问历史中增加一个记录

  2. History H5新增的方法 pushState与replaceState 可以将url替换并且不刷新页面

    ? pushState将新路由添加到浏览器访问历史的栈顶

    ? replaceState 将新路由添加到浏览器访问历史的栈顶,而是替换掉当前的路由

Vue组件懒加载

  • 异步组件实现路由懒加载
    component: resolve=>(require(["@/components/HelloWorld"],resolve))
    
  • es提出的import(推荐)
    const HelloWorld = ()=>import("@/components/HelloWorld")
    

Vue的动态组件和异步组件

动态组件: Vue提供了一个特殊元素component用来挂在不同的组件,通过改变属性is的值来选择要挂载的组件。

<component :is="currentView"></component>

异步组件:在组件被需要时才开始加载渲染,并把结果缓存起来用于后再再次渲染

Vue.component(
  'async-webpack-example',
  // 这个动态导入会返回一个 `Promise` 对象。
  () => import('./my-async-component')
)

Vue-router懒加载

const Foo = () => import('./Foo.vue')
  
const router = new VueRouter({
 routes: [{ path: '/foo', component: Foo }]
})

Vue运行机制

  1. 初始化 调用原型的_init()进行初始化,初始化生命周期,props,data,methods,computed,watch等,利用Object.definePropty()对data中的属性设置setter和getter函数,实现响应式和依赖收集

  2. 挂载组件 初始化之后调用$mount挂载组件 如果是运行时编译即不存在render函数但存在template需要进行编译步骤

  3. 编译 parse(解析)、optimize(标记静态节点做优化)、generate(转换成字符串)

    parse:利用正则将模板转换为抽象语法树(AST)

    optimize:标记静态节点,以后update的时候,diff算法可以跳过静态节点

    generate:将静态语法树(AST)转换成字符串,供render去渲染DOM

    经过这个步骤组件中就会存在render函数

  4. 响应式:在进行render渲染时会对data进行数据读取,会触发getter函数,把data中的属性进行依赖收集,将这些属性放到观察者(watcher)的观察队列中,当进行属性修改时就会触发setter函数,setter告诉观察者(watcher)数据变化,重新渲染视图,观察者调用update更新视图

  5. 虚拟DOM:render function转换为虚拟DOM,虚拟dom就是一个js对象。render function被转换为VNODe节点。

  6. 更新视图: 在updata时,执行patch将oldNODE传进去,通过diff算法跟VNODE进行比较算出差异然后更新视图。

template解析过程

  • template模板经过parse()生成AST抽象语法树(使用正则解析截取),optimize对静态节点(和数据没有关系,不需要每次更新)优化(diff跳过静态节点,优化patch性能),generate生成render字符串,调用render函数生成DOM
  • 调用new Watcher函数监听数据变化,调用render函数生成vnode
  • 数据更新vnode会与oldnode做diff比较,然后更新dom

nextTick

Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。

Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM,

Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。

Vue.nextTick(callback) 使用原理:

原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你设置 vm.someData = ‘new value’,DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。

@2.5后版本 $nextTick was using setIwmmediate > MessageChannel > setTimeout

@2.5前版本 $nextTick was using Promise > MutationObserver > setTimeout

https://www.cnblogs.com/purple-windbells/p/11731919.html

Vue优化

  • 路由懒加载
  • 开启服务器Gzip压缩技术,webpack 提供的compression-webpack-plugin进行压缩
  • 使用cdn加速
  • v-if和v-show、computed和watch
  • 列表渲染添加key值
  • 使用keep-aive进行组件缓存
  • 第三方库按需引入 插件babel-plugin-import
  • 服务端渲染 ssr

路由守卫

  • 全局守卫

    ? beforeEach、afterEach

  • 路由独享守卫

    ? beforeEnter

  • 组件内守卫

    beforeRouterEnter、beforeRouterUpdate、beforeRouterLeave

v-model原理

本质上是v-bind和v-on的结合体,绑定一个value通过v-on触发来更新数据。

主要依赖与Object.defineProperty()这个函数监听get和set事件

img

实现过程

首先对数据进行劫持监听,设置一个监听器Observer,监听所有的属性。如果属性发生变化就告诉订阅者Watcher是否需要更新。还要有一个消息订阅器Dep收集这些订阅者,在监听器Observer和订阅者Watcher之间统一管理。还需要指令解析器Compile对每个节点元素进行解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定的函数,Watcher接收到属性变化就执行相关函数更新视图。

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

流程图如下:

717034f25ee385b09e9dee53b2988cae.png

function objServer(obj){
    let keys = Object.keys(obj);
    keys.forEach((item)=>{
        definedActive(obj,item,obj[item])
     })
    returnobj;
}

function definedActive(obj,item,val){
    Object.defineProperty(obj,item,{
        get(){
        	console.log(`${item}获取了`)
        },
    set(newVlaue){
        val = newVlaue;
        console.log(`${item}修改了`)
    	}
    })
}

let obj = objServer({
a:1,
b:2
})
obj.a
obj.b
obj.a = 2;
obj.b = 3;

Vue3 proxy的原理

主要通过proxy对对象进行绑定监听处理,通过es6的 new map(自带set和get)对对象的属性进行处理,将要执行的函数匹配到存到对应的prop上,通过每次的访问触发get方法,进行存方法 的操作,通过修改可以触发set方法,接着执行回调监听的函数,达到修改视图和数据。

手写emit on发布订阅

let obj = {};
const $on = (name,fn)=>{
    if(!obj[name]){
        obj[name] = [];
    }
    obj[name].push(fn);
}

const $emit = (name,val)=>{
    if(obj[name]){
        obj[name].map((fn)=>{
            fn(val);
        });
    }
}

const $off = (name,fn)=>{
    if(obj[name]){
        if(fn){
            let index = obj[name].indexOf(fn);
            if(index > -1){
                obj[name].splice(index,1);
            }           
        }else{
            obj[name].length = 0;
            //设长度为0比obj[name] = []更优,因为如果是空数组则又开辟了一个新空间,设长度为0则不必开辟新空间
        }  
    }
}

export default {
    $on,
    $emit,
    $off
}

服务器端渲染 SSR

半静态 后端解析字符串

nuxt 约定式路由

_name.vue动态路由

动态路由 越具体越先匹配

asyncData 异步加载数据返回的值会跟data合并

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 01:25:52  更:2021-08-17 01:26:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/26 16:26:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计