摘要:超出此時間則渲染錯誤組件。元素節(jié)點(diǎn)總共有種類型,為表示是普通元素,為表示是表達(dá)式,為表示是純文本。
實(shí)戰(zhàn) - 插件 form-validate
i18n
實(shí)戰(zhàn) - 組件{{ $t("welcome-message") }}
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)簽組件{{ var1 }} {{ var2 }}
封裝應(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
- {{item}}
在組件中,可以使用 $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: `自實(shí)現(xiàn)boradcast和dispatch` }); // 在組件 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 });
**$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原理(phoneInfo=val)" :zip-code="zipCode" @update:zipCode="val=>(zipCode=val)" />
數(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ā)生變化了 5MVVM實(shí)現(xiàn)
實(shí)現(xiàn)mvvm-github
Model-View-ViewModel,其核心是提供對View 和 ViewModel 的雙向數(shù)據(jù)綁定,這使得ViewModel 的狀態(tài)改變可以自動傳遞給 View
observerproxy 方法遍歷 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 方法
computedcomputed初始化被遍歷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 等)
something | myFilter 被解析成_f("myFilter")( something )
nextTickVue.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
compilecompile 編譯可以分成 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模板編譯前后:
`
對比react的jsx編譯前后
`diffHello World` h("div",{ id: "1", "class": "li-1" },"Hello World", h(MyComp, null) )
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)。所以不需要重新渲染整個組件樹。vdom實(shí)現(xià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...
類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: `原理 - props(屬性)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
父組件的模板 會被解析成一個 模板渲染函數(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 僅僅是作為 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)。
原理 - SSRVue.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
摘要:純分享直接上干貨操作系統(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)存管...
摘要:異步實(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...
摘要:請回復(fù)這個帖子并注明組織個人信息來申請加入。版筆記等到中文字幕翻譯完畢后再整理。數(shù)量超過個,在所有組織中排名前。網(wǎng)站日超過,排名的峰值為。主頁歸檔社區(qū)自媒體平臺微博知乎專欄公眾號博客園簡書合作侵權(quán),請聯(lián)系請抄送一份到贊助我們 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1...
摘要:主頁暫時下線社區(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 知識庫 自媒體平臺 ...
閱讀 667·2021-11-23 09:51
閱讀 3314·2021-10-11 10:58
閱讀 15488·2021-09-29 09:47
閱讀 3581·2021-09-01 11:42
閱讀 1297·2019-08-29 16:43
閱讀 1841·2019-08-29 15:37
閱讀 2121·2019-08-29 12:56
閱讀 1732·2019-08-28 18:21