成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

FE.SRC-Vue實(shí)戰(zhàn)與原理筆記

wangjuntytl / 729人閱讀

摘要:超出此時間則渲染錯誤組件。元素節(jié)點(diǎn)總共有種類型,為表示是普通元素,為表示是表達(dá)式,為表示是純文本。

實(shí)戰(zhàn) - 插件 form-validate


  • {{ error }}
i18n


{{ $t("welcome-message") }}

實(shí)戰(zhàn) - 組件

Vue組件=Vue實(shí)例=new Vue(options)

屬性

自定義屬性 props:組件props中聲明的屬性

原生屬性 attrs:沒有聲明的屬性,默認(rèn)自動掛載到組件根元素上

特殊屬性 class,style:掛載到組件根元素上

事件

普通事件 @click,@input,@change,@xxx 通過this.$emit("xxx")觸發(fā)

修飾符事件 @input.trim @click.stop

插槽

v-slot:xxx

v-slot:xxx="props"

相同名稱的插槽替換

動態(tài)導(dǎo)入/延遲加載組件

基于路由拆分

const routes = [
  { path: /foo", component: () => import("./RouteComponent.vue") }
]
函數(shù)式組件

無狀態(tài) 、實(shí)例、this上下文、生命周期

臨時變量組件

  


批量渲染標(biāo)簽組件

封裝應(yīng)用實(shí)例化函數(shù)
function createApp ({ el, model, view, actions }) {
  const wrappedActions={}
  Object.keys(actions).forEach(key=>{
    const originalAction=actions[key]
    wrappedActions[key]=()=>{
      const nextModel=originalAction(model)
      vm.model=nextModel
    }
  })

  const vm=new Vue({
    el,
    data:{model},
    render(h){
      return view(h,this.model,wrappedActions)
    }
  })
}

createApp({
  el: "#app",
  model: {
    count: 0
  },
  actions: {
    inc: ({ count }) => ({ count: count + 1 }),
    dec: ({ count }) => ({ count: count - 1 })
  },
  view: (h, model, actions) => h("div", { attrs: { id: "app" }}, [
    model.count, " ",
    h("button", { on: { click: actions.inc }}, "+"),
    h("button", { on: { click: actions.dec }}, "-")
  ])
})
高階組件

這是一個具名插槽
異步組件
const AsyncComp = () => ({
  // 需要加載的組件。應(yīng)當(dāng)是一個 Promise
  component: import("./MyComp.vue"),
  // 加載中應(yīng)當(dāng)渲染的組件
  loading: LoadingComp,
  // 出錯時渲染的組件
  error: ErrorComp,
  // 渲染加載中組件前的等待時間。默認(rèn):200ms。
  delay: 200,
  // 最長等待時間。超出此時間則渲染錯誤組件。默認(rèn):Infinity
  timeout: 3000
})
Vue.component("async-example", AsyncComp)
實(shí)戰(zhàn) - 組件通信 父傳子props







子傳父組件$emit, $on, $off

在組件中,可以使用 $emit, $on, $off 分別來分發(fā)、監(jiān)聽、取消監(jiān)聽事件

// NewTodoInput ---------------------
// ...
methods: {
  addTodo: function () {
    eventHub.$emit("add-todo", { text: this.newTodoText })
    this.newTodoText = ""
  }
}
// DeleteTodoButton ---------------------
// ...
methods: {
  deleteTodo: function (id) {
    eventHub.$emit("delete-todo", id)
  }
}
// Todos ---------------------
// ...
created: function () {
  eventHub.$on("add-todo", this.addTodo)
  eventHub.$on("delete-todo", this.deleteTodo)
},
// 最好在組件銷毀前
// 清除事件監(jiān)聽
beforeDestroy: function () {
  eventHub.$off("add-todo", this.addTodo)
  eventHub.$off("delete-todo", this.deleteTodo)
},
methods: {
  addTodo: function (newTodo) {
    this.todos.push(newTodo)
  },
  deleteTodo: function (todoId) {
    this.todos = this.todos.filter(function (todo) {
      return todo.id !== todoId
    })
  }
}
內(nèi)置$parent、$children、$ref;$attrs和$listeners

$ref ref="xxx"
$parent / $children:訪問父 / 子實(shí)例


 
高階插件/組件庫 provide & inject(observable)
// 父級組件提供 "foo"
var Provider = {
  provide: {
    foo: "bar"
  },
  // ...
}

// 子組件注入 "foo"
var Child = {
  inject: ["foo"],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}
// 父級組件提供 "state"
var Provider = {
  provide: {
    state = Vue.observable({ count: 0 })
  },
  // ...
}

// 子組件注入 "foo"
var Child = {
  inject: ["state"],
  created () {
    console.log(this.state) // => { count: 0 }
  }
  // ...
}
全局對象 Event Bus
const state={count:0}

const Counter = {
  data(){return state},
  render:h=>h("div",state.count)
}

new Vue({
  el: "#app",
  components:{Counter},
  methods:{
    inc(){
      state.count++
    }
  }
})
//中央事件總線
var bus = new Vue();
var app = new Vue({
  el: "#app",
  template: `
            
` }); // 在組件 brother1 的 methods 方法中觸發(fā)事件 bus.$emit("say-hello", "world"); // 在組件 brother2 的 created 鉤子函數(shù)中監(jiān)聽事件 bus.$on("say-hello", function(arg) { console.log("hello " + arg); // hello world });
自實(shí)現(xiàn)boradcast和dispatch

**$dispatch 和 $broadcast 已經(jīng)被棄用,使用
Vuex代替**

以下自實(shí)現(xiàn)參考 iview/emitter.js at 2.0 · iview/iview -github

function broadcast(componentName, eventName, params) {
    this.$children.forEach(child => {
        const name = child.$options.name;

        if (name === componentName) {
            child.$emit.apply(child, [eventName].concat(params));
        } else {
            // todo 如果 params 是空數(shù)組,接收到的會是 undefined
            broadcast.apply(child, [componentName, eventName].concat([params]));
        }
    });
}
export default {
    methods: {
        dispatch(componentName, eventName, params) {
            let parent = this.$parent || this.$root;
            let name = parent.$options.name;

            while (parent && (!name || name !== componentName)) {
                parent = parent.$parent;

                if (parent) {
                    name = parent.$options.name;
                }
            }
            if (parent) {
                parent.$emit.apply(parent, [eventName].concat(params));
            }
        },
        broadcast(componentName, eventName, params) {
            broadcast.call(this, componentName, eventName, params);
        }
    }
};
原理 - 響應(yīng)式

單向數(shù)據(jù)流,雙向綁定語法糖

demo


原理

數(shù)據(jù)劫持+觀察訂閱模式:

在讀取屬性的時候依賴收集,在改變屬性值的時候觸發(fā)依賴更新

實(shí)現(xiàn)一個observer,劫持對象屬性

實(shí)現(xiàn)一個全局的訂閱器Dep,可以追加訂閱者,和通知依賴更新

在讀取屬性的時候追加當(dāng)前依賴到Dep中,在設(shè)置屬性的時候循環(huán)觸發(fā)依賴的更新

new Vue(options)創(chuàng)建實(shí)例的時候,initData()進(jìn)行數(shù)據(jù)劫持

通過Object.defineProperty(obj,key,desc)對data進(jìn)行數(shù)據(jù)劫持,即創(chuàng)建get/set函數(shù)

這里需要考慮對對象的以及對數(shù)組的數(shù)據(jù)劫持(支持 push,pop,shift,unshift,splice,sort,reverse,不支持 filter,concat,slice)

對象遞歸調(diào)用

數(shù)組變異方法的解決辦法:代理原型/實(shí)例方法

避免依賴重讀Observer

  // 全局的依賴收集器Dep
   window.Dep = class Dep {
       constructor() {this.subscribers = new Set()}
       depend() {activeUpdate&&this.subscribers.add(activeUpdate)}
       notify() {this.subscribers.forEach(sub =>sub())}
   }
   let activeUpdate
   function autorun(update) {
       function wrapperUpdate() {
           activeUpdate = wrapperUpdate
           update()
           activeUpdate = null
       }
       wrapperUpdate()
   }
   function observer(obj) {
       Object.keys(obj).forEach(key => {
           var dep = new Dep()
           let internalValue = obj[key]
           Object.defineProperty(obj, key, {
               get() {
                   // console.log(`getting key "${key}": ${internalValue}`)
                   // 將當(dāng)前正在運(yùn)行的更新函數(shù)追加進(jìn)訂閱者列表
                   activeUpdate&&dep.depend() //收集依賴
                   return internalValue
               },
               set(newVal) {
                //console.log(`setting key "${key}" to: ${internalValue}`)
                // 加個if判斷,數(shù)據(jù)發(fā)生變化再觸發(fā)更新
                if(internalValue !== newVal) {
                       internalValue = newVal
                       dep.notify() // 觸發(fā)依賴的更新
                }
               }
           })
       })
   }
   let state = {count:0}
   observer(state);
   autorun(() => {
       console.log("state.count發(fā)生變化了", state.count)
   })
   state.count = state.count + 5;
   // state.count發(fā)生變化了 0
   // state.count發(fā)生變化了 5
MVVM實(shí)現(xiàn)

實(shí)現(xiàn)mvvm-github

Model-View-ViewModel,其核心是提供對View 和 ViewModel 的雙向數(shù)據(jù)綁定,這使得ViewModel 的狀態(tài)改變可以自動傳遞給 View

observer

proxy 方法遍歷 data 的 key,把 data 上的屬性代理到 vm 實(shí)例上(通過Object.defineProperty 的 getter 和 setter )

observe(data, this) 給 data 對象添加 Observer做監(jiān)聽。

創(chuàng)建一個 Observer 對象

創(chuàng)建了一個 Dep 對象實(shí)例(觀察者模式)

遍歷data,convert(defineReactive) 方法使他們有g(shù)etter、setter

getter 和 setter 方法調(diào)用時會分別調(diào)用 dep.depend 方法和 dep.notify

depend:把當(dāng)前 Dep 的實(shí)例添加到當(dāng)前正在計算的Watcher 的依賴中

notify:遍歷了所有的訂閱 Watcher,調(diào)用它們的 update 方法

computed

computed初始化被遍歷computed,使用Object.defineProperty進(jìn)行處理,依賴收集到Dep.target

觸發(fā)data值時會觸發(fā)Watcher監(jiān)聽函數(shù)

computed值緩存 watcher.dirty決定了計算屬性值是否需要重新計算,默認(rèn)值為true,即第一次時會調(diào)用一次。每次執(zhí)行之后watcher.dirty會設(shè)置為false,只有依賴的data值改變時才會觸發(fā)

mixin

全局注冊的選項(xiàng),被引用到你的每個組件中
1、Vue.component 注冊的 【全局組件】
2、Vue.filter 注冊的 【全局過濾器】
3、Vue.directive 注冊的 【全局指令】
4、Vue.mixin 注冊的 【全局mixin】

合并權(quán)重
1、組件選項(xiàng)
2、組件 - mixin
3、組件 - mixin - mixin
4、.....
x、全局 選項(xiàng)
函數(shù)合并疊加(data,provide)
數(shù)組疊加(created,watch)
原型疊加(components,filters,directives)
兩個對象合并的時候,不會相互覆蓋,而是 權(quán)重小的 被放到 權(quán)重大 的 的原型上
覆蓋疊加(props,methods,computed,inject)
兩個對象合并,如果有重復(fù)key,權(quán)重大的覆蓋權(quán)重小的
直接替換(el,template,propData 等)

filter

something | myFilter 被解析成_f("myFilter")( something )

nextTick

Vue.js 在默認(rèn)情況下,每次觸發(fā)某個數(shù)據(jù)的 setter 方法后,對應(yīng)的 Watcher 對象其實(shí)會被 push 進(jìn)一個隊(duì)列 queue 中,在下一個 tick 的時候?qū)⑦@個隊(duì)列 queue 全部拿出來 run( Watcher 對象的一個方法,用來觸發(fā) patch 操作) 一遍。

原理 - virtaul DOM

真實(shí)DOM操作昂貴,虛擬DOM就是js對象,操作代價小

模板編譯&渲染

平時開發(fā)寫vue文件都是用模板template的方法寫html,模板會被編譯成render函數(shù),流程如下:

初始化的時候

- 模板會被編譯成render函數(shù)
- render函數(shù)返回虛擬DOM
- 生成真正的DOM

數(shù)據(jù)更新的時候

- render函數(shù)返回新的virtual Dom
- 新的virtual Dom和舊的virtual Dom做diff
- 將差異運(yùn)用到真實(shí)DOM

render API

    //template, jsx, render本質(zhì)都是一樣的, 都是一種dom和數(shù)據(jù)狀態(tài)之間關(guān)系的表示
    render(h) {
        h(tag, data, children)
    }
    // tag可以是原生的html標(biāo)簽
    render(h) {
        return h("div", { attrs: {}}, [])
    }
    // 也可以是一個vue component
    import vueComponent from "..."
    render(h) {
        h(vueComponent, {
            props: {} 
        })
    }

偏邏輯用render,偏視圖用template

compile

compile 編譯可以分成 parse、 optimize 與 generate 三個階段,最終需要得到 render function。

parse:會用正則等方式解析 template 模板中的指令、class、style 等數(shù)據(jù),形成 AST。

transclude(el, option) 把 template 編譯成一段 document fragment

compileNode(el, options) 深度遍歷DOM,正則解析指令

vm.bindDir(descriptor, node, host, scope) 根據(jù) descriptor 實(shí)例化不同的 Directive 對象

Directive 在初始化時通過 extend(this, def) 擴(kuò)展 bind 和 update,創(chuàng)建了 Watcher關(guān)聯(lián)update

解析模板字符串生成 AST `const ast = parse(template.trim(), options)
循環(huán)解析 template,利用正則表達(dá)式順序解析模板,當(dāng)解析到開始標(biāo)簽、閉合標(biāo)簽、文本的時候都會分別執(zhí)行對應(yīng)的回調(diào)函數(shù),來達(dá)到構(gòu)造 AST 樹的目的。 AST 元素節(jié)點(diǎn)總共有 3 種類型,type 為 1 表示是普通元素,為 2 表示是表達(dá)式,為 3 表示是純文本。

optimize:標(biāo)記 static 靜態(tài)節(jié)點(diǎn),而減少了比較的過程 等優(yōu)化

優(yōu)化語法樹 optimize(ast, options)
深度遍歷這個 AST 樹,去檢測它的每一顆子樹是不是靜態(tài)節(jié)點(diǎn),如果是靜態(tài)節(jié)點(diǎn)則它們生成 DOM 永遠(yuǎn)不需要改變(標(biāo)記靜態(tài)節(jié)點(diǎn) markStatic(root);標(biāo)記靜態(tài)根 markStaticRoots(root, false))

generate:是將 AST 轉(zhuǎn)化成 render function 字符串

AST轉(zhuǎn)可執(zhí)行的代碼 const code = generate(ast, options)

vue模板編譯前后:

`
  • {{item}}:{{index}}
` with(this){ return (isShow) ? _c("ul", { staticClass: "list", class: bindCls }, _l((data), function(item, index) { return _c("li", { on: { "click": function($event) { clickItem(index) } } }, [_v(_s(item) + ":" + _s(index))]) }) ) : _e() }

對比react的jsx編譯前后

`
Hello World
` h("div",{ id: "1", "class": "li-1" },"Hello World", h(MyComp, null) )

diff

vnode

{
  el:  div  //對真實(shí)的節(jié)點(diǎn)的引用,本例中就是document.querySelector("#id.classA")
  tagName: "DIV",   //節(jié)點(diǎn)的標(biāo)簽
  sel: "div#v.classA"  //節(jié)點(diǎn)的選擇器
  data: null,       // 一個存儲節(jié)點(diǎn)屬性的對象,對應(yīng)節(jié)點(diǎn)的el[prop]屬性,例如onclick , style
  children: [], //存儲子節(jié)點(diǎn)的數(shù)組,每個子節(jié)點(diǎn)也是vnode結(jié)構(gòu)
  text: null,    //如果是文本節(jié)點(diǎn),對應(yīng)文本節(jié)點(diǎn)的textContent,否則為null
}

核心 patch (oldVnode, vnode)

key和sel相同才去比較,否則新替舊

patchVnode (oldVnode, vnode)節(jié)點(diǎn)比較5種情況

if (oldVnode === vnode),他們的引用一致,可以認(rèn)為沒有變化

if(oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text),文本節(jié)點(diǎn)的比較,需要修改,則會調(diào)用Node.textContent = vnode.text

if( oldCh && ch && oldCh !== ch ), 兩個節(jié)點(diǎn)都有子節(jié)點(diǎn),而且它們不一樣,這樣我們會調(diào)用updateChildren函數(shù)比較子節(jié)點(diǎn)

else if (ch),只有新的節(jié)點(diǎn)有子節(jié)點(diǎn),調(diào)用createEle(vnode),vnode.el已經(jīng)引用了老的dom節(jié)點(diǎn),createEle函數(shù)會在老dom節(jié)點(diǎn)上添加子節(jié)點(diǎn)

else if (oldCh),新節(jié)點(diǎn)沒有子節(jié)點(diǎn),老節(jié)點(diǎn)有子節(jié)點(diǎn),直接刪除老節(jié)點(diǎn)

同層比較作用:將一棵樹轉(zhuǎn)換成另一棵樹的最小操作次數(shù)是O(n^3),同層是O(1)

key的作用:

為了在數(shù)據(jù)變化時強(qiáng)制更新組件,以避免“原地復(fù)用”帶來的副作用。

在交叉對比沒有結(jié)果(列表數(shù)據(jù)的重新排序,插,刪)的時候會采用key來提高這個diff速度(不設(shè)key,newCh和oldCh只會進(jìn)行頭尾兩端的相互比較,設(shè)key后,除了頭尾兩端的比較外,還會從用key生成的對象oldKeyToIdx中查找匹配的節(jié)點(diǎn),從而移動dom而不是銷毀再創(chuàng)建)

vue&react vdom區(qū)別
Vue 很“ 囂張 ”,它宣稱可以更快地計算出Virtual DOM的差異,這是由于它在渲染過程中,由于vue會跟蹤每一個組件的依賴收集,通過setter / getter 以及一些函數(shù)的劫持,能夠精確地知道變化,并在編譯過程標(biāo)記了static靜態(tài)節(jié)點(diǎn),在接下來新的Virtual DOM 并且和原來舊的 Virtual DOM進(jìn)行比較時候,跳過static靜態(tài)節(jié)點(diǎn)。所以不需要重新渲染整個組件樹。

React默認(rèn)是通過比較引用的方式進(jìn)行,當(dāng)某個組件的狀態(tài)發(fā)生變化時,它會以該組件為根,重新渲染整個組件子樹。如果想避免不必要的子組件重新渲染,你需要在所有可能的地方使用PureComponent,或者手動實(shí)現(xiàn)shouldComponentUpdate方法。但是Vue中,你可以認(rèn)定它是默認(rèn)的優(yōu)化。

摘自 https://juejin.im/post/5b6178...

vdom實(shí)現(xiàn)

類vue vdom

snabbdom-github

snabbdom源碼閱讀分析

Vue 2.0 的 virtual-dom 實(shí)現(xiàn)簡析

類react vdom

preact-github

preact工作原理

原理 - Router(路由)

vue插件,通過hash /history 2中方式實(shí)現(xiàn)可配路由
Hash

push(): 設(shè)置新的路由添加歷史記錄并更新視圖,常用情況是直接點(diǎn)擊切換視圖,調(diào)用流程:

$router.push() 顯式調(diào)用方法

HashHistory.push() 根據(jù)hash模式調(diào)用,設(shè)置hash并添加到瀏覽器歷史記錄(window.location.hash= XXX)

History.transitionTo() 開始更新

History.updateRoute() 更新路由

app._route= route

vm.render() 更新視圖

replace: 替換當(dāng)前路由并更新視圖,常用情況是地址欄直接輸入新地址,流程與push基本一致

但流程2變?yōu)樘鎿Q當(dāng)前hash (window.location.replace= XXX)
3.監(jiān)聽地址欄變化:在setupListeners中監(jiān)聽hash變化(window.onhashchange)并調(diào)用replace

History

push:與hash模式類似,只是將window.hash改為history.pushState

replace:與hash模式類似,只是將window.replace改為history.replaceState

監(jiān)聽地址變化:在HTML5History的構(gòu)造函數(shù)中監(jiān)聽popState(window.onpopstate)

實(shí)戰(zhàn)

const Foo = {
    props: ["id"],
    template: `
foo with id: {{id}}
` } const Bar = { template: `
bar
` } const NotFound = { template: `
not found
` } const routeTable = { "/foo/:id": Foo, "/bar": Bar, } const compiledRoutes = []; Object.keys(routeTable).forEach(path => { const dynamicSegments = [] const regex = pathToRegexp(path, dynamicSegments) const component = routeTable[path] compiledRoutes.push({ component, regex, dynamicSegments }) }) window.addEventListener("hashchange", () => { app.url = window.location.hash.slice(1); }) const app = new Vue({ el: "#app", data() { return { url: window.location.hash.slice(1) } }, render(h) { const url = "/" + this.url let componentToRender let props = {} compiledRoutes.some(route => { const match = route.regex.exec(url) if (match) { componentToRender = route.component route.dynamicSegments.forEach((segment,index) => { props[segment.name] = match[index+1] }) } }) return h("div", [ h("a", { attrs: { href: "#foo/123" } }, "foo123"), "|", h("a", { attrs: { href: "#foo/234" } }, "foo234"), "|", h("a", { attrs: { href: "#bar" } }, "bar"), h(componentToRender || NotFound, { props }) ]) } })
原理 - props(屬性)

父組件怎么傳值給子組件的 props

父組件的模板 會被解析成一個 模板渲染函數(shù),執(zhí)行時會綁定 父組件為作用域

(function() {
    with(this){ 
        return _c("div",{staticClass:"a"},[
            _c("testb",{attrs:{"child-name":parentName}})
        ],1)
    }
})

所以渲染函數(shù)內(nèi)部所有的變量,都會從父組件對象 上去獲取

組件怎么讀取 props

子組件拿到父組件賦值過后的 attr,篩選出 props,然后保存到實(shí)例的_props 中,并逐一復(fù)制到實(shí)例上,響應(yīng)式的。

父組件數(shù)據(jù)變化,子組件props如何更新
父組件數(shù)據(jù)變化,觸發(fā)set,從而通知依賴收集器的watcher重新渲染

原理 - Vuex

vuex 僅僅是作為 vue 的一個插件而存在,不像 Redux,MobX 等庫可以應(yīng)用于所有框架,vuex 只能使用在 vue 上,很大的程度是因?yàn)槠涓叨纫蕾囉?vue 的 computed 依賴檢測系統(tǒng)以及其插件系統(tǒng),

vuex 整體思想誕生于 flux,可其的實(shí)現(xiàn)方式完完全全的使用了 vue 自身的響應(yīng)式設(shè)計,依賴監(jiān)聽、依賴收集都屬于 vue 對對象 Property set get 方法的代理劫持。vuex 中的 store 本質(zhì)就是沒有 template 的隱藏著的 vue 組件;

state

this.$store.state.xxx 取值

提供一個響應(yīng)式數(shù)據(jù)

vuex 就是一個倉庫,倉庫里放了很多對象。其中 state 就是數(shù)據(jù)源存放地,對應(yīng)于一般 vue 對象里面的 data

state 里面存放的數(shù)據(jù)是響應(yīng)式的,vue 組件從 store 讀取數(shù)據(jù),若是 store 中的數(shù)據(jù)發(fā)生改變,依賴這相數(shù)據(jù)的組件也會發(fā)生更新

它通過 mapState 把全局的 state 和 getters 映射到當(dāng)前組件的 computed 計算屬性

getter

this.$store.getters.xxx 取值

借助vue計算屬性computed實(shí)現(xiàn)緩存

getter 可以對 state 進(jìn)行計算操作,它就是 store 的計算屬性

雖然在組件內(nèi)也可以做計算屬性,但是 getters 可以在多給件之間復(fù)用

如果一個狀態(tài)只在一個組件內(nèi)使用,是可以不用 getters

mutaion

this.$store.commit("xxx") 賦值

更改state方法

action 類似于 muation, 不同在于:action 提交的是 mutation,而不是直接變更狀態(tài)

action 可以包含任意異步操作

action

this.$store.dispatch("xxx") 賦值

觸發(fā)mutation方法

module

Vue.set動態(tài)添加state到響應(yīng)式數(shù)據(jù)中

簡單版Vuex實(shí)現(xiàn)

//min-vuex
import Vue from "vue"
const Store = function Store (options = {}) {
  const {state = {}, mutations={}} = options
  this._vm = new Vue({
    data: {
      $$state: state
    },
  })
  this._mutations = mutations
}
Store.prototype.commit = function(type, payload){
  if(this._mutations[type]) {
    this._mutations[type](this.state, payload)
  }
}
Object.defineProperties(Store.prototype, { 
  state: { 
    get: function(){
      return this._vm._data.$$state
    }
  }
});
export default {Store}

使用 Vuex 只需執(zhí)行 Vue.use(Vuex),并在 Vue 的配置中傳入一個 store 對象的示例,store 是如何實(shí)現(xiàn)注入的?

Vue.use(Vuex) 方法執(zhí)行的是 install 方法,它實(shí)現(xiàn)了 Vue 實(shí)例對象的 init 方法封裝和注入,使傳入的 store 對象被設(shè)置到 Vue 上下文環(huán)境的store中。因此在VueComponent任意地方都能夠通過this.store 訪問到該 store。

state 內(nèi)部支持模塊配置和模塊嵌套,如何實(shí)現(xiàn)的?

在 store 構(gòu)造方法中有 makeLocalContext 方法,所有 module 都會有一個 local context,根據(jù)配置時的 path 進(jìn)行匹配。所以執(zhí)行如 dispatch("submitOrder", payload)這類 action 時,默認(rèn)的拿到都是 module 的 local state,如果要訪問最外層或者是其他 module 的 state,只能從 rootState 按照 path 路徑逐步進(jìn)行訪問。

在執(zhí)行 dispatch 觸發(fā) action(commit 同理)的時候,只需傳入(type, payload),action 執(zhí)行函數(shù)中第一個參數(shù) store 從哪里獲取的?

store 初始化時,所有配置的 action 和 mutation 以及 getters 均被封裝過。在執(zhí)行如 dispatch("submitOrder", payload)的時候,actions 中 type 為 submitOrder 的所有處理方法都是被封裝后的,其第一個參數(shù)為當(dāng)前的 store 對象,所以能夠獲取到 { dispatch, commit, state, rootState } 等數(shù)據(jù)。

Vuex 如何區(qū)分 state 是外部直接修改,還是通過 mutation 方法修改的?

Vuex 中修改 state 的唯一渠道就是執(zhí)行 commit("xx", payload) 方法,其底層通過執(zhí)行 this._withCommit(fn) 設(shè)置_committing 標(biāo)志變量為 true,然后才能修改 state,修改完畢還需要還原_committing 變量。外部修改雖然能夠直接修改 state,但是并沒有修改_committing 標(biāo)志位,所以只要 watch 一下 state,state change 時判斷是否_committing 值為 true,即可判斷修改的合法性。

調(diào)試時的"時空穿梭"功能是如何實(shí)現(xiàn)的?

devtoolPlugin 中提供了此功能。因?yàn)?dev 模式下所有的 state change 都會被記錄下來,"時空穿梭" 功能其實(shí)就是將當(dāng)前的 state 替換為記錄中某個時刻的 state 狀態(tài),利用 store.replaceState(targetState) 方法將執(zhí)行 this._vm.state = state 實(shí)現(xiàn)。

原理 - SSR

Vue.js 是構(gòu)建客戶端應(yīng)用程序的框架。默認(rèn)情況下,可以在瀏覽器中輸出 Vue 組件,進(jìn)行生成 DOM 和操作 DOM。

然而,也可以將同一個組件渲染為服務(wù)器端的 HTML 字符串,將它們直接發(fā)送到瀏覽器,最后將靜態(tài)標(biāo)記"混合"為客戶端上完全交互的應(yīng)用程序。

服務(wù)器渲染的 Vue.js 應(yīng)用程序也可以被認(rèn)為是"同構(gòu)"或"通用",因?yàn)閼?yīng)用程序的大部分代碼都可以在服務(wù)器和客戶端上運(yùn)行。

服務(wù)端渲染的核心就在于:通過vue-server-renderer插件的renderToString()方法,將Vue實(shí)例轉(zhuǎn)換為字符串插入到html文件

其他

vue 企業(yè)級應(yīng)用模板-github

VueConf 2018 杭州(第二屆Vue開發(fā)者大會)

Vue 3.0 進(jìn)展 - 尤雨溪

參考資料

深入響應(yīng)式原理 —— Vue.js官網(wǎng)

Advanced Vue.js Features from the Ground Up - 尤雨溪

Vue 源碼解析:深入響應(yīng)式原理 - 黃軼

Vue 源碼研究會 - 神仙朱

vue組件之間8種組件通信方式總結(jié) - zhoulu_hp

Vue問得最多的面試題 - yangcheng

剖析 Vue.js 內(nèi)部運(yùn)行機(jī)制 - 染陌

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109787.html

相關(guān)文章

  • 阿里 2021 版最全 Java 并發(fā)編程筆記,看完我才懂了“內(nèi)卷”的真正意義

    摘要:純分享直接上干貨操作系統(tǒng)并發(fā)支持進(jìn)程管理內(nèi)存管理文件系統(tǒng)系統(tǒng)進(jìn)程間通信網(wǎng)絡(luò)通信阻塞隊(duì)列數(shù)組有界隊(duì)列鏈表無界隊(duì)列優(yōu)先級有限無界隊(duì)列延時無界隊(duì)列同步隊(duì)列隊(duì)列內(nèi)存模型線程通信機(jī)制內(nèi)存共享消息傳遞內(nèi)存模型順序一致性指令重排序原則內(nèi)存語義線程 純分享 , 直接上干貨! 操作系統(tǒng)并發(fā)支持 進(jìn)程管理內(nèi)存管...

    不知名網(wǎng)友 評論0 收藏0
  • FE.SRC-React實(shí)戰(zhàn)原理筆記

    摘要:異步實(shí)戰(zhàn)狀態(tài)管理與組件通信組件通信其他狀態(tài)管理當(dāng)需要改變應(yīng)用的狀態(tài)或有需要更新時,你需要觸發(fā)一個把和載荷封裝成一個。的行為是同步的。所有的狀態(tài)變化必須通過通道。前端路由實(shí)現(xiàn)與源碼分析設(shè)計思想應(yīng)用是一個狀態(tài)機(jī),視圖與狀態(tài)是一一對應(yīng)的。 React實(shí)戰(zhàn)與原理筆記 概念與工具集 jsx語法糖;cli;state管理;jest單元測試; webpack-bundle-analyzer Sto...

    PumpkinDylan 評論0 收藏0
  • ApacheCN 編程/大數(shù)據(jù)/數(shù)據(jù)科學(xué)/人工智能學(xué)習(xí)資源 2019.5

    摘要:請回復(fù)這個帖子并注明組織個人信息來申請加入。版筆記等到中文字幕翻譯完畢后再整理。數(shù)量超過個,在所有組織中排名前。網(wǎng)站日超過,排名的峰值為。主頁歸檔社區(qū)自媒體平臺微博知乎專欄公眾號博客園簡書合作侵權(quán),請聯(lián)系請抄送一份到贊助我們 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1...

    zhonghanwen 評論0 收藏0
  • ApacheCN 學(xué)習(xí)資源匯總 2019.3

    摘要:主頁暫時下線社區(qū)暫時下線知識庫自媒體平臺微博知乎簡書博客園合作侵權(quán),請聯(lián)系請抄送一份到特色項(xiàng)目中文文檔和教程與機(jī)器學(xué)習(xí)實(shí)用指南人工智能機(jī)器學(xué)習(xí)數(shù)據(jù)科學(xué)比賽系列項(xiàng)目實(shí)戰(zhàn)教程文檔代碼視頻數(shù)據(jù)科學(xué)比賽收集平臺,,劍指,經(jīng)典算法實(shí)現(xiàn)系列課本課本描述 【主頁】 apachecn.org 【Github】@ApacheCN 暫時下線: 社區(qū) 暫時下線: cwiki 知識庫 自媒體平臺 ...

    array_huang 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<