• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當(dāng)前位置: 首頁 - 科技 - 知識(shí)百科 - 正文

    vue操作數(shù)據(jù)改變更新視圖

    來源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 19:50:45
    文檔

    vue操作數(shù)據(jù)改變更新視圖

    vue操作數(shù)據(jù)改變更新視圖:這次給大家?guī)韛ue操作數(shù)據(jù)改變更新視圖,vue操作數(shù)據(jù)改變更新視圖的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。前記三個(gè)月前看了vue源碼來分析如何做到響應(yīng)式數(shù)據(jù)的, 文章名字叫vue源碼之響應(yīng)式數(shù)據(jù), 最后分析到, 數(shù)據(jù)變化后會(huì)調(diào)用Watcher的u
    推薦度:
    導(dǎo)讀vue操作數(shù)據(jù)改變更新視圖:這次給大家?guī)韛ue操作數(shù)據(jù)改變更新視圖,vue操作數(shù)據(jù)改變更新視圖的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。前記三個(gè)月前看了vue源碼來分析如何做到響應(yīng)式數(shù)據(jù)的, 文章名字叫vue源碼之響應(yīng)式數(shù)據(jù), 最后分析到, 數(shù)據(jù)變化后會(huì)調(diào)用Watcher的u

    這次給大家?guī)韛ue操作數(shù)據(jù)改變更新視圖,vue操作數(shù)據(jù)改變更新視圖的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。

    前記

    三個(gè)月前看了vue源碼來分析如何做到響應(yīng)式數(shù)據(jù)的, 文章名字叫vue源碼之響應(yīng)式數(shù)據(jù), 最后分析到, 數(shù)據(jù)變化后會(huì)調(diào)用Watcher的update()方法. 那么時(shí)隔三月讓我們繼續(xù)看看update()做了什么. (這三個(gè)月用react-native做了個(gè)項(xiàng)目, 也無心總結(jié)了, 因?yàn)楹孟裉唵瘟?.

    本文敘事方式為樹藤摸瓜, 順著看源碼的邏輯走一遍, 查看的vue的版本為2.5.2. 我fork了一份源碼用來記錄注釋.

    目的

    明確調(diào)查方向才能直至目標(biāo), 先說一下目標(biāo)行為: 數(shù)據(jù)變化以后執(zhí)行了什么方法來更新視圖的. 那么準(zhǔn)備開始以這個(gè)方向?yàn)槟繕?biāo)從vue源碼的入口開始找答案.

    從之前的結(jié)論開始

    先來復(fù)習(xí)一下之前的結(jié)論:

    vue構(gòu)造的時(shí)候會(huì)在data(和一些別的字段)上建立Observer對(duì)象, getter和setter被做了攔截, getter觸發(fā)依賴收集, setter觸發(fā)notify.

    另一個(gè)對(duì)象是Watcher, 注冊watch的時(shí)候會(huì)調(diào)用一次watch的對(duì)象, 這樣觸發(fā)了watch對(duì)象的getter, 把依賴收集到當(dāng)前Watcher的deps里, 當(dāng)任何dep的setter被觸發(fā)就會(huì)notify當(dāng)前Watcher來調(diào)用Watcher的update()方法.

    那么這里就從注冊渲染相關(guān)的Watcher開始.

    找到了文件在src/core/instance/lifecycle.js中.

    new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)

    mountComponent

    渲染相關(guān)的Watcher是在mountComponent()這個(gè)方法中調(diào)用的, 那么我們搜一下這個(gè)方法是在哪里調(diào)用的. 只有2處, 分別是src/platforms/web/runtime/index.js和src/platforms/weex/runtime/index.js, 以web為例:

    Vue.prototype.$mount = function (
     el?: string | Element,
     hydrating?: boolean
    ): Component {
     el = el && inBrowser ? query(el) : undefined
     return mountComponent(this, el, hydrating)
    }

    原來如此, 是$mount()方法調(diào)用了mountComponent(), (或者在vue構(gòu)造時(shí)指定el字段也會(huì)自動(dòng)調(diào)用$mount()方法), 因?yàn)閣eb和weex(什么是weex?之前別的文章介紹過)渲染的標(biāo)的物不同, 所以在發(fā)布的時(shí)候應(yīng)該引入了不同的文件最后發(fā)不成不同的dist(這個(gè)問題留給之后來研究vue的整個(gè)流程).

    下面是mountComponent方法:

    export function mountComponent (
     vm: Component,
     el: ?Element,
     hydrating?: boolean
    ): Component {
     vm.$el = el // 放一份el到自己的屬性里
     if (!vm.$options.render) { // render應(yīng)該經(jīng)過處理了, 因?yàn)槲覀兘?jīng)常都是用template或者vue文件
     // 判斷是否存在render函數(shù), 如果沒有就把render函數(shù)寫成空VNode來避免紅錯(cuò), 并報(bào)出黃錯(cuò)
     vm.$options.render = createEmptyVNode
     if (process.env.NODE_ENV !== 'production') {
     /* istanbul ignore if */
     if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
     vm.$options.el || el) {
     warn(
     'You are using the runtime-only build of Vue where the template ' +
     'compiler is not available. Either pre-compile the templates into ' +
     'render functions, or use the compiler-included build.',
     vm
     )
     } else {
     warn(
     'Failed to mount component: template or render function not defined.',
     vm
     )
     }
     }
     }
     callHook(vm, 'beforeMount')
     let updateComponent
     /* istanbul ignore if */
     if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
     // 不看這里的代碼了, 直接看else里的, 行為是一樣的
     updateComponent = () => {
     const name = vm._name
     const id = vm._uid
     const startTag = `vue-perf-start:${id}`
     const endTag = `vue-perf-end:${id}`
     mark(startTag)
     const vnode = vm._render()
     mark(endTag)
     measure(`vue ${name} render`, startTag, endTag)
     mark(startTag)
     vm._update(vnode, hydrating)
     mark(endTag)
     measure(`vue ${name} patch`, startTag, endTag)
     }
     } else {
     updateComponent = () => {
     vm._update(vm._render(), hydrating)
     }
     }
     // we set this to vm._watcher inside the watcher's constructor
     // since the watcher's initial patch may call $forceUpdate (e.g. inside child
     // component's mounted hook), which relies on vm._watcher being already defined
     // 注冊一個(gè)Watcher
     new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
     hydrating = false
     // manually mounted instance, call mounted on self
     // mounted is called for render-created child components in its inserted hook
     if (vm.$vnode == null) {
     vm._isMounted = true
     callHook(vm, 'mounted')
     }
     return vm
    }

    這段代碼其實(shí)只做了3件事:

  • 調(diào)用beforeMount鉤子

  • 建立Watcher

  • 調(diào)用mounted鉤子

  • (哈哈哈)那么其實(shí)核心就是建立Watcher了.

    看一下Watcher的參數(shù): vm是this, updateComponent是一個(gè)函數(shù), noop是空, null是空, true代表是RenderWatcher.

    在Watcher里看了isRenderWatcher:

    if (isRenderWatcher) {
     vm._watcher = this
     }

    是的, 只是復(fù)制了一份用來在watcher第一次patch的時(shí)候判斷一些東西(從注釋里看的, 我現(xiàn)在還不知道是干嘛的).

    那么只有一個(gè)問題沒解決就是updateComponent是個(gè)什么東西.

    updateComponent

    在Watcher的構(gòu)造函數(shù)的第二個(gè)參數(shù)傳了function, 那么這個(gè)函數(shù)就成了watcher的getter. 聰明的你應(yīng)該已經(jīng)猜到, 在這個(gè)updateComponent里一定調(diào)用了視圖中所有的數(shù)據(jù)的getter, 才能在watcher中建立依賴從而讓視圖響應(yīng)數(shù)據(jù)的變化.

    updateComponent = () => {
     vm._update(vm._render(), hydrating)
     }

    那么就去找vm._update()和vm._render().

    在src/core/instance/render.js找到了._render()方法.

    Vue.prototype._render = function (): VNode {
     const vm: Component = this
     const { render, _parentVnode } = vm.$options // todo: render和_parentVnode的由來
     // reset _rendered flag on slots for duplicate slot check
     if (process.env.NODE_ENV !== 'production') {
     for (const key in vm.$slots) {
     // $flow-disable-line
     vm.$slots[key]._rendered = false
     }
     }
     if (_parentVnode) {
     vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject
     }
     // set parent vnode. this allows render functions to have access
     // to the data on the placeholder node.
     vm.$vnode = _parentVnode
     // render self
     let vnode
     try {
     vnode = render.call(vm._renderProxy, vm.$createElement)
     } catch (e) {
     // catch其實(shí)不需要看了, 都是做異常處理, _vnode是在vm._update的時(shí)候保存的, 也就是上次的狀態(tài)或是null(init的時(shí)候給的)
     handleError(e, vm, `render`)
     // return error render result,
     // or previous vnode to prevent render error causing blank component
     /* istanbul ignore else */
     if (process.env.NODE_ENV !== 'production') {
     if (vm.$options.renderError) {
     try {
     vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
     } catch (e) {
     handleError(e, vm, `renderError`)
     vnode = vm._vnode
     }
     } else {
     vnode = vm._vnode
     }
     } else {
     vnode = vm._vnode
     }
     }
     // return empty vnode in case the render function errored out
     if (!(vnode instanceof VNode)) {
     if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
     warn(
     'Multiple root nodes returned from render function. Render function ' +
     'should return a single root node.',
     vm
     )
     }
     vnode = createEmptyVNode()
     }
     // set parent
     vnode.parent = _parentVnode
     return vnode
     }
    }

    這個(gè)方法做了:

  • 根據(jù)當(dāng)前vm的render方法來生成VNode. (render方法可能是根據(jù)template或vue文件編譯而來, 所以推論直接寫render方法效率最高)

  • 如果render方法有問題, 那么首先調(diào)用renderError方法, 再不行就讀取上次的vnode或是null.

  • 如果有父節(jié)點(diǎn)就放到自己的.parent屬性里.

  • 最后返回VNode

  • 所以核心是這句:

    vnode = render.call(vm._renderProxy, vm.$createElement)

    其中的render(), vm._renderProxy, vm.$createElement都不知道是什么.

    先看vm._renderProxy: 是initMixin()的時(shí)候設(shè)置的, 在生產(chǎn)環(huán)境返回vm, 開發(fā)環(huán)境返回代理, 那么我們認(rèn)為他是一個(gè)可以debug的vm(就是vm), 細(xì)節(jié)之后再看.

    vm.$createElement的代碼在vdom文件夾下, 看了下是一個(gè)方法, 返回值一個(gè)VNode.

    render有點(diǎn)復(fù)雜, 能不能以后研究, 總之就是把template或者vue單文件和mount目標(biāo)parse成render函數(shù).

    小總結(jié): vm._render()的返回值是VNode, 根據(jù)當(dāng)前vm的render函數(shù)

    接下來看vm._update()

    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
     const vm: Component = this
     if (vm._isMounted) {
     callHook(vm, 'beforeUpdate')
     }
     // 記錄update之前的狀態(tài)
     const prevEl = vm.$el
     const prevVnode = vm._vnode
     const prevActiveInstance = activeInstance
     activeInstance = vm
     vm._vnode = vnode
     // Vue.prototype.patch is injected in entry points
     // based on the rendering backend used.
     if (!prevVnode) { // 初次加載, 只有_update方法更新vm._vnode, 初始化是null
     // initial render
     vm.$el = vm.patch( // patch創(chuàng)建新dom
     vm.$el, vnode, hydrating, false /* removeOnly */,
     vm.$options._parentElm,
     vm.$options._refElm
     )
     // no need for the ref nodes after initial patch
     // this prevents keeping a detached DOM tree in memory (#5851)
     vm.$options._parentElm = vm.$options._refElm = null
     } else {
     // updates
     vm.$el = vm.patch(prevVnode, vnode) // patch更新dom
     }
     activeInstance = prevActiveInstance
     // update vue reference
     if (prevEl) {
     prevEl.vue = null
     }
     if (vm.$el) {
     vm.$el.vue = vm
     }
     // if parent is an HOC, update its $el as well
     if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
     vm.$parent.$el = vm.$el
     }
     // updated hook is called by the scheduler to ensure that children are
     // updated in a parent's updated hook.
     }

    我們關(guān)心的部分其實(shí)就是patch()的部分, patch()做了對(duì)dom的操作, 在_update()里判斷了是否是初次調(diào)用, 如果是的話創(chuàng)建新dom, 不是的話傳入新舊node進(jìn)行比較再操作.

    結(jié)論

    vue的視圖渲染是一種特殊的Watcher, watch的內(nèi)容是一個(gè)函數(shù), 函數(shù)運(yùn)行的過程調(diào)用了render函數(shù), render又是由template或者el的dom編譯成的(template中含有一些被observe的數(shù)據(jù)). 所以template中被observe的數(shù)據(jù)有變化觸發(fā)Watcher的update()方法就會(huì)重新渲染視圖.

    遺留

    render函數(shù)是在哪里被編譯的
    vue源碼發(fā)布時(shí)引入不同平臺(tái)最后打成dist的流程是什么
    patch和VNode的分析

    相信看了本文案例你已經(jīng)掌握了方法,更多精彩請(qǐng)關(guān)注Gxl網(wǎng)其它相關(guān)文章!

    推薦閱讀:

    jQuery實(shí)現(xiàn)輸入文字超過規(guī)定行數(shù)時(shí)自動(dòng)添加省略號(hào)

    ElTableColumn添加搜索歸納功能

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    vue操作數(shù)據(jù)改變更新視圖

    vue操作數(shù)據(jù)改變更新視圖:這次給大家?guī)韛ue操作數(shù)據(jù)改變更新視圖,vue操作數(shù)據(jù)改變更新視圖的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。前記三個(gè)月前看了vue源碼來分析如何做到響應(yīng)式數(shù)據(jù)的, 文章名字叫vue源碼之響應(yīng)式數(shù)據(jù), 最后分析到, 數(shù)據(jù)變化后會(huì)調(diào)用Watcher的u
    推薦度:
    標(biāo)簽: 操作 修改 使用
    • 熱門焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 免费精品国自产拍在线播放| 亚洲国产婷婷综合在线精品| 日韩精品一区二区三区在线观看 | 亚洲高清国产拍精品26U| 久久精品国内一区二区三区| 欧美精品VIDEOSSEX少妇| 鲸鱼传媒绿头鱼实验室之炮机测评日韩精品一级毛 | 精品一区二区三区中文字幕| 亚洲av永久无码精品秋霞电影影院| 国产欧美日本亚洲精品一5| 国产精品久久久久影视不卡| 精品人妻无码一区二区色欲产成人| 偷拍精品视频一区二区三区| 精品99又大又爽又硬少妇毛片| 97久人人做人人妻人人玩精品| 国产精品电影在线观看| 2021国产成人精品国产| 国产精品日韩欧美一区二区三区| 无码人妻精品一区二区三区久久 | 伊人 久久 精品| 精品亚洲成α人无码成α在线观看 | 亚洲欧洲精品成人久久曰影片| 国产农村妇女毛片精品久久| 国产精品成人精品久久久| 99RE8这里有精品热视频| 久久免费精品一区二区| 久久99国产精品久久99| 国产在线拍揄自揄视精品不卡| 3D动漫精品一区二区三区| 91在线视频精品| 国产精品久久久久久久| 秋霞午夜鲁丝片午夜精品久| 91国内揄拍国内精品对白不卡| 最新国产精品亚洲| 高清在线国产午夜精品| 国产精品亚洲不卡一区二区三区| 国产精品麻豆入口| 久久se精品一区二区影院 | 国产亚洲午夜高清国产拍精品| 国产VA免费精品高清在线| 国产精品 日韩欧美|