0% found this document useful (0 votes)
15 views237 pages

3 Vue面试真题-237页

Uploaded by

puerem
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views237 pages

3 Vue面试真题-237页

Uploaded by

puerem
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 237




Life Cycle
Cradle-to-Grave
Vue

Vue
this property
created: () => this.fetchTodos()



1

Vue

2


vue

watch event

methods dom computed


watch

vm.$el

el vm.$mount(el)

render template outerHTML

3
vm.el DOM

vm.el

vm.el el

vm.el vm.$el DOM el DOM

vm.el DOM vm.$el

el template render
view

beforeUpdate

view

updated beforeUpdate updated

4

created dom
mounted dom created mounte
d
mounted
dom
created





5
Model View JavaScript
Model View
View Model

View Model
Model View


Vue

MVVM

6




Vue Vue

 new Vue() data Observe

 data
Compile

 Watcher Watcher

 data key key Dep


Watcher

 Dep Watcher

7


data

1 class Vue {
2 constructor(options) {
3 this.$options = options;
4 this.$data = options.data;
5
6 // data
7 observe(this.$data);
8
9 // data vm
10 proxy(this);
11
12 //
13 new Compile(options.el, this);
14 }
15 }

data

8
1 function observe(obj) {
2 if (typeof obj !== "object" || obj == null) {
3 return;
4 }
5 new Observer(obj);
6 }
7
8 class Observer {
9 constructor(value) {
10 this.value = value;
11 this.walk(value);
12 }
13 walk(obj) {
14 Object.keys(obj).forEach((key) => {
15 defineReactive(obj, key, obj[key]);
16 });
17 }
18 }

 Compile

9
1 class Compile {
2 constructor(el, vm) {
3 this.$vm = vm;
4 this.$el = document.querySelector(el); // dom
5 if (this.$el) {
6 this.compile(this.$el);
7 }
8 }
9 compile(el) {
10 const childNodes = el.childNodes;
11 Array.from(childNodes).forEach((node) => { //
12 if (this.isElement(node)) { //
13 console.log(" " + node.nodeName);
14 } else if (this.isInterpolation(node)) {
15 console.log(" " + node.textContent); //
{{}}
16 }
17 if (node.childNodes && node.childNodes.length > 0) { //

18 this.compile(node); //
19 }
20 });
21 }
22 isElement(node) {
23 return node.nodeType == 1;
24 }
25 isInterpolation(node) {
26 return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
27 }
28 }



data key key


Watcher Watcher Dep
Dep

10
 defineReactive key Dep

 key name1 watcher1

 name1 getter watcher1 name1


 name1 setter Dep Watcher

1 //
2 class Watcher {
3 constructor(vm, key, updater) {
4 this.vm = vm
5 this.key = key
6 this.updaterFn = updater
7
8 // Dep.target
9 Dep.target = this
10 // key get
11 vm[key]
12 //
13 Dep.target = null
14 }
15
16 // dom dep
17 update() {
18 this.updaterFn.call(this.vm, this.vm[this.key])
19 }
20 }

Dep

11
1 class Dep {
2 constructor() {
3 this.deps = []; //
4 }
5 addDep(dep) {
6 this.deps.push(dep);
7 }
8 notify() {
9 this.deps.forEach((dep) => dep.update());
10 }
11 }

watcher getter

1 class Watcher {
2 constructor(vm, key, updateFn) {
3 Dep.target = this;
4 this.vm[this.key];
5 Dep.target = null;
6 }
7 }

Dep

1 function defineReactive(obj, key, val) {


2 this.observe(val);
3 const dep = new Dep();
4 Object.defineProperty(obj, key, {
5 get() {
6 Dep.target && dep.addDep(Dep.target);// Dep.target Watcher
7 return val;
8 },
9 set(newVal) {
10 if (newVal === val) return;
11 dep.notify(); // dep
12 },
13 });
14 }

12




vue vue .vue

.vue UI
table table



vue



13

vue









 parent







14
props

Children.vue

1 props:{
2 //
3 name:String //
4 //
5 age:{
6 type:Number, //
7 defaule:18, // 18
8 require:true // age
9 }
10 }

Father.vue

1 <Children name="jack" age=18 />



$emit $emit

Chilfen.vue

15
1 this.$emit('add', good)

Father.vue

1 <Children @add="cartAdd($event)" />



ref

ref

1 <Children ref="foo" />


2
3 this.$refs.foo //



EventBus

$emit $emit

$on

Bus.js

16
1 //
2 class Bus {
3 constructor() {
4 this.callbacks = {}; //
5 }
6 $on(name, fn) {
7 this.callbacks[name] = this.callbacks[name] || [];
8 this.callbacks[name].push(fn);
9 }
10 $emit(name, args) {
11 if (this.callbacks[name]) {
12 this.callbacks[name].forEach((cb) => cb(args));
13 }
14 }
15 }
16
17 // main.js
18 Vue.prototype.$bus = new Bus() // $bus vue
19 //
20 Vue.prototype.$bus = new Vue() // Vue Bus

Children1.vue

1 this.$bus.$emit('foo')

Children2.vue

1 this.$bus.$on('foo', this.handle)



$parent $root

this.$parent.on('add',this.add)

this.$parent.emit('add')

17


$attrs $listeners

prop

v-bind="$attrs"

1 // child props foo


2 <p>{{$attrs.foo}}</p>
3
4 // parent
5 <HelloWorld foo="foo"/>

1 // Grandson communication/index.vue
2 <Child2 msg="lalala" @some-event="onSomeEvent"></Child2>
3
4 // Child2
5 <Grandson v-bind="$attrs" v-on="$listeners"></Grandson>
6
7 // Grandson
8 <div @click="$emit('some-event', 'msg from grandson')">
9 {{msg}}
10 </div>



provide

inject

1 provide(){
2 return {
3 foo:'foo'
4 }
5 }

18
1 inject:['foo'] //

 vuex

Vuex

state

getter getter store

mutations state

actions action mutations

19

props $emit ref

$bus $parent

attrs listeners Provide Inject

vuex




vue data

20
1 const app = new Vue({
2 el:"#app",
3 //
4 data:{
5 foo:"foo"
6 },
7 //
8 data(){
9 return {
10 foo:"foo"
11 }
12 }
13 })

data

data

1 Vue.component('component1',{
2 template:`<div> </div>`,
3 data:{
4 foo:"foo"
5 }
6 })

data


data

vue Vue.extend()

data

21
1 function Component(){
2
3 }
4 Component.prototype.data = {
5 count : 0
6 }

1 const componentA = new Component()


2 const componentB = new Component()

componentA data componentB

1 console.log(componentB.data.count) // 0
2 componentA.data.count = 1
3 console.log(componentB.data.count) // 1

componentA
componentB

1 function Component(){
2 this.data = this.data()
3 }
4 Component.prototype.data = function (){
5 return {
6 count : 0
7 }
8 }

componentA data componentB

22
1 console.log(componentB.data.count) // 0
2 componentA.data.count = 1
3 console.log(componentB.data.count) // 0

vue data


vue data data

/vue-dev/src/core/instance/state.js

1 function initData (vm: Component) {


2 let data = vm.$options.data
3 data = vm._data = typeof data === 'function'
4 ? getData(data, vm)
5 : data || {}
6 ...
7 }

data object function

/vue-dev/src/core/util/options.js

mergeOptions

23
1 Vue.prototype._init = function (options?: Object) {
2 ...
3 // merge options
4 if (options && options._isComponent) {
5 // optimize internal component instantiation
6 // since dynamic options merging is pretty slow, and none of the
7 // internal component options needs special treatment.
8 initInternalComponent(vm, options)
9 } else {
10 vm.$options = mergeOptions(
11 resolveConstructorOptions(vm.constructor),
12 options || {},
13 vm
14 )
15 }
16 ...
17 }

data

/vue-dev/src/core/instance/init.js

vm undefined if data function

24
1 strats.data = function (
2 parentVal: any,
3 childVal: any,
4 vm?: Component
5 ): ?Function {
6 if (!vm) {
7 if (childVal && typeof childVal !== "function") {
8 process.env.NODE_ENV !== "production" &&
9 warn(
10 'The "data" option should be a function ' +
11 "that returns a per-instance value in component " +
12 "definitions.",
13 vm
14 );
15
16 return parentVal;
17 }
18 return mergeDataOrFn(parentVal, childVal);
19 }
20 return mergeDataOrFn(parentVal, childVal, vm);
21 };


data

data data
initData data



25


p v-for

botton

1 <p v-for="(value,key) in item" :key="key">


2 {{ value }}
3 </p>
4 <button @click="addProperty"> </button>

vue data methods

1 const app = new Vue({


2 el:"#app",
3 data:()=>{
4 item:{
5 oldProperty:" "
6 }
7 },
8 methods:{
9 addProperty(){
10 this.items.newProperty = " " // items
11 console.log(this.items) // newProperty items
12 }
13 }
14 })

26
console



vue2 Object.defineProperty

1 const obj = {}
2 Object.defineProperty(obj, 'foo', {
3 get() {
4 console.log(`get foo:${val}`);
5 return val
6 },
7 set(newVal) {
8 if (newVal !== val) {
9 console.log(`set foo:${newVal}`);
10 val = newVal
11 }
12 }
13 })
14 }

foo foo setter getter

1 obj.foo
2 obj.foo = 'new'

obj

1 obj.bar = ' '

obj foo bar Obje


ct.defineProperty



27
Vue



{Object | Array} target

{string | number} propertyName/index

{any} value

Vue.set property property

Vue.set

src\core\observer\index.js

1 function set (target: Array<any> | Object, key: any, val: any): any {
2 ...
3 defineReactive(ob.value, key, val)
4 ob.dep.notify()
5 return val
6 }

defineReactive

defineReactive Object.defineProperty

28
1 function defineReactive(obj, key, val) {
2 Object.defineProperty(obj, key, {
3 get() {
4 console.log(`get ${key}:${val}`);
5 return val
6 },
7 set(newVal) {
8 if (newVal !== val) {
9 console.log(`set ${key}:${newVal}`);
10 val = newVal
11 }
12 }
13 })
14 }



Object.assign()

1 this.someObject = Object.assign({},this.someObject,{newProperty1:1,newPrope
rty2:2 ...})



Vue

$forceUpdate Vue


Vue.set()

Object.assign()

$forceUpdate()

29
vue3 proxy




v-if true

v-for v-for item in items


items item

v-for key key diff

1 <Modal v-if="isShow" />


2
3 <li v-for="item in items" :key="item.id">
4 {{ item.label }}
5 </li>


v-if v-for vue

vue render

30


p v-if v-for

1 <div id="app">
2 <p v-if="isShow" v-for="item in items">
3 {{ item.title }}
4 </p>
5 </div>

vue isShow items

1 const app = new Vue({


2 el: "#app",
3 data() {
4 return {
5 items: [
6 { title: "foo" },
7 { title: "baz" }]
8 }
9 },
10 computed: {
11 isShow() {
12 return this.items && this.items.length > 0
13 }
14 }
15 })

render app.$options.render

1 ƒ anonymous() {
2 with (this) { return
3 _c('div', { attrs: { "id": "app" } },
4 _l((items), function (item)
5 { return (isShow) ? _c('p', [_v("\n" + _s(item.title) + "\n")]) : _e()
}), 0) }
6 }

_l vue if

v-for v-if

31
v-for v-if

1 <div id="app">
2 <template v-if="isShow">
3 <p v-for="item in items">{{item.title}}</p>
4 </template>
5 </div>

render

1 ƒ anonymous() {
2 with(this){return
3 _c('div',{attrs:{"id":"app"}},
4 [(isShow)?[_v("\n"),
5 _l((items),function(item){return _c('p',[_v(_s(item.title))])})]:_e()],
2)}
6 }

v-for v-if

vue

\vue-dev\src\compiler\codegen\index.js

32
1 export function genElement (el: ASTElement, state: CodegenState): string {
2 if (el.parent) {
3 el.pre = el.pre || el.parent.pre
4 }
5 if (el.staticRoot && !el.staticProcessed) {
6 return genStatic(el, state)
7 } else if (el.once && !el.onceProcessed) {
8 return genOnce(el, state)
9 } else if (el.for && !el.forProcessed) {
10 return genFor(el, state)
11 } else if (el.if && !el.ifProcessed) {
12 return genIf(el, state)
13 } else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
14 return genChildren(el, state) || 'void 0'
15 } else if (el.tag === 'slot') {
16 return genSlot(el, state)
17 } else {
18 // component or element
19 ...
20 }

if v-for v-if

v-for v-if


 v-if v-for

 template dom

1 <template v-if="isShow">
2 <p v-for="item in items">
3 </template>

 computed

33
1 computed: {
2 items: function() {
3 return this.list.filter(function (item) {
4 return item.isShow
5 })
6 }
7 }




vue v-show v-if

1 <Model v-show="isShow" />


2 <Model v-if="isShow" />

true

34
false



v-show css--display:none dom v-


if dom

v-if
v-show

v-if

v-show false true

v-if false true beforeCreate create beforeMo


unt mounted true false beforeDestory desto
ryed

v-if v-show



template ast JS

ast JS render staticRenderFns

render staticRenderFns VNODE DOM

vm.patch DOM VNODE DOM



vue

transition transition display

35
1 // https://github.com/vuejs/vue-next/blob/3cd30c5245da0733f9eb6f29d220f39c
46518162/packages/runtime-dom/src/directives/vShow.ts
2 export const vShow: ObjectDirective<VShowElement> = {
3 beforeMount(el, { value }, { transition }) {
4 el._vod = el.style.display === 'none' ? '' : el.style.display
5 if (transition && value) {
6 transition.beforeEnter(el)
7 } else {
8 setDisplay(el, value)
9 }
10 },
11 mounted(el, { value }, { transition }) {
12 if (transition && value) {
13 transition.enter(el)
14 }
15 },
16 updated(el, { value, oldValue }, { transition }) {
17 // ...
18 },
19 beforeUnmount(el, { value }) {
20 setDisplay(el, value)
21 }
22 }



v-if v-show else else-if


v-if

node render DOM

36
1 // https://github.com/vuejs/vue-next/blob/cdc9f336fd/packages/compiler-cor
e/src/transforms/vIf.ts
2 export const transformIf = createStructuralDirectiveTransform(
3 /^(if|else|else-if)$/,
4 (node, dir, context) => {
5 return processIf(node, dir, context, (ifNode, branch, isRoot) => {
6 // ...
7 return () => {
8 if (isRoot) {
9 ifNode.codegenNode = createCodegenNodeForBranch(
10 branch,
11 key,
12 context
13 ) as IfConditionalExpression
14 } else {
15 // attach this branch's codegen node to the v-if root.
16 const parentCondition = getParentCondition(ifNode.codegenNode!)
17 parentCondition.alternate = createCodegenNodeForBranch(
18 branch,
19 key + ifNode.branches.length - 1,
20 context
21 )
22 }
23 }
24 })
25 }
26 )


v-if v-show dom

v-if v-show dom



37


 v-for key

1 <ul>
2 <li v-for="item in items" :key="item.id">...</li>
3 </ul>

 +new Date() key

1 <Comp :key="+new Date()" />

key



v-for key

38
+new Date() key



items

1 <body>
2 <div id="demo">
3 <p v-for="item in items" :key="item">{{item}}</p>
4 </div>
5 <script src="../../dist/vue.js"></script>
6 <script>
7 //
8 const app = new Vue({
9 el: '#demo',
10 data: { items: ['a', 'b', 'c', 'd', 'e'] },
11 mounted () {
12 setTimeout(() => {
13 this.items.splice(2, 0, 'f') //
14 }, 2000);
15 },
16 });
17 </script>
18 </body>

key vue

39
patch dom

patch dom
patch dom

patch dom

patch dom

DOM

key vue

patch dom

patch dom

patch dom

patch dom

patch dom

key DOM diff



40
v-for key



key key key key und


efined undefined undefined

1 function sameVnode (a, b) {


2 return (
3 a.key === b.key && (
4 (
5 a.tag === b.tag &&
6 a.isComment === b.isComment &&
7 isDef(a.data) === isDef(b.data) &&
8 sameInputType(a, b)
9 ) || (
10 isTrue(a.isAsyncPlaceholder) &&
11 a.asyncFactory === b.asyncFactory &&
12 isUndef(b.asyncFactory.error)
13 )
14 )
15 )
16 }

updateChildren vnode diff


DOM

41
1 function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, remo
veOnly) {
2 ...
3 while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
4 if (isUndef(oldStartVnode)) {
5 ...
6 } else if (isUndef(oldEndVnode)) {
7 ...
8 } else if (sameVnode(oldStartVnode, newStartVnode)) {
9 ...
10 } else if (sameVnode(oldEndVnode, newEndVnode)) {
11 ...
12 } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode move
d right
13 ...
14 } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode move
d left
15 ...
16 } else {
17 if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldC
h, oldStartIdx, oldEndIdx)
18 idxInOld = isDef(newStartVnode.key)
19 ? oldKeyToIdx[newStartVnode.key]
20 : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndId
x)
21 if (isUndef(idxInOld)) { // New element
22 createElm(newStartVnode, insertedVnodeQueue, parentElm, ol
dStartVnode.elm, false, newCh, newStartIdx)
23 } else {
24 vnodeToMove = oldCh[idxInOld]
25 if (sameVnode(vnodeToMove, newStartVnode)) {
26 patchVnode(vnodeToMove, newStartVnode, insertedVnodeQu
eue, newCh, newStartIdx)
27 oldCh[idxInOld] = undefined
28 canMove && nodeOps.insertBefore(parentElm, vnodeToMove
.elm, oldStartVnode.elm)
29 } else {
30 // same key but different element. treat as new elemen
t
31 createElm(newStartVnode, insertedVnodeQueue, parentElm
, oldStartVnode.elm, false, newCh, newStartIdx)
32 }
33 }
34 newStartVnode = newCh[++newStartIdx]
35 }

42
36 }
37
...
38
}


Mixin mixin

Mixin



mixin Vue

js data components me
thods created computed

mixins mixins mixin


s

Vue

43


mixin options data methods

1 var myMixin = {
2 created: function () {
3 this.hello()
4 },
5 methods: {
6 hello: function () {
7 console.log('hello from mixin!')
8 }
9 }
10 }

mixins mixin

1 Vue.component('componentA',{
2 mixins: [myMixin]
3 })

mixin created hello



Vue.mixin()

1 Vue.mixin({
2 created: function () {
3 console.log(" ")
4 }
5 })

44


mixin mixin

mixin



Vue mixin

modal isShowing

1 const Modal = {
2 template: '#modal',
3 data() {
4 return {
5 isShowing: false
6 }
7 },
8 methods: {
9 toggleShow() {
10 this.isShowing = !this.isShowing;
11 }
12 }
13 }

tooltip isShowing

45
1 const Tooltip = {
2 template: '#tooltip',
3 data() {
4 return {
5 isShowing: false
6 }
7 },
8 methods: {
9 toggleShow() {
10 this.isShowing = !this.isShowing;
11 }
12 }
13 }

mixin

mixin

1 const toggle = {
2 data() {
3 return {
4 isShowing: false
5 }
6 },
7 methods: {
8 toggleShow() {
9 this.isShowing = !this.isShowing;
10 }
11 }
12 }

mixin

46
1 const Modal = {
2 template: '#modal',
3 mixins: [toggle]
4 };
5
6 const Tooltip = {
7 template: '#tooltip',
8 mixins: [toggle]
9 }

Mixin


Vue.mixin

1 export function initMixin (Vue: GlobalAPI) {


2 Vue.mixin = function (mixin: Object) {
3 this.options = mergeOptions(this.options, mixin)
4 return this
5 }
6 }

merOptions

47
1 export function mergeOptions (
2 parent: Object,
3 child: Object,
4 vm?: Component
5 ): Object {
6
7 if (child.mixins) { // mixin mixin mixin

8 for (let i = 0, l = child.mixins.length; i < l; i++) {


9 parent = mergeOptions(parent, child.mixins[i], vm)
10 }
11 }
12
13 const options = {}
14 let key
15 for (key in parent) {
16 mergeField(key) // parent key strats[XXX]
17 }
18 for (key in child) {
19 if (!hasOwn(parent, key)) { // parent key
20 mergeField(key) // child key parent key
21 }
22 }
23 function mergeField (key) {
24 const strat = strats[key] || defaultStrat
25 options[key] = strat(parent[key], child[key], vm, key) //
options strats
26 }
27 return options
28 }

mixins

parent key mergeField


options
child parent key mergeField
options

mergeField

Vue

48


props methods inject computed

1 strats.props =
2 strats.methods =
3 strats.inject =
4 strats.computed = function (
5 parentVal: ?Object,
6 childVal: ?Object,
7 vm?: Component,
8 key: string
9 ): ?Object {
10 if (!parentVal) return childVal // parentVal childVal
11 const ret = Object.create(null) // ret
12 extend(ret, parentVal) // extend parentVal ret
13 if (childVal) extend(ret, childVal) // childVal ret
14 return ret
15 }
16 strats.provide = mergeDataOrFn

props methods inject computed



data

49
1 strats.data = function(parentVal, childVal, vm) {
2 return mergeDataOrFn(
3 parentVal, childVal, vm
4 )
5 };
6
7 function mergeDataOrFn(parentVal, childVal, vm) {
8 return function mergedInstanceDataFn() {
9 var childData = childVal.call(vm, vm) // data
10 var parentData = parentVal.call(vm, vm)
11 if (childData) {
12 return mergeData(childData, parentData) // 2

13 } else {
14 return parentData // childData parentData
15 }
16 }
17 }
18
19 function mergeData(to, from) {
20 if (!from) return to
21 var key, toVal, fromVal;
22 var keys = Object.keys(from);
23 for (var i = 0; i < keys.length; i++) {
24 key = keys[i];
25 toVal = to[key];
26 fromVal = from[key];
27 //
28 if (!to.hasOwnProperty(key)) {
29 set(to, key, fromVal);
30 }
31 //
32 else if (typeof toVal =="object" && typeof fromVal =="object") {
33 mergeData(toVal, fromVal);
34 }
35 }
36 return to
37 }

mergeData

set

50


watch

51
1 function mergeHook (
2 parentVal: ?Array<Function>,
3 childVal: ?Function | ?Array<Function>
4 ): ?Array<Function> {
5 return childVal
6 ? parentVal
7 ? parentVal.concat(childVal)
8 : Array.isArray(childVal)
9 ? childVal
10 : [childVal]
11 : parentVal
12 }
13
14 LIFECYCLE_HOOKS.forEach(hook => {
15 strats[hook] = mergeHook
16 })
17
18 // watch
19 strats.watch = function (
20 parentVal,
21 childVal,
22 vm,
23 key
24 ) {
25 // work around Firefox's Object.prototype.watch...
26 if (parentVal === nativeWatch) { parentVal = undefined; }
27 if (childVal === nativeWatch) { childVal = undefined; }
28 /* istanbul ignore if */
29 if (!childVal) { return Object.create(parentVal || null) }
30 {
31 assertObjectType(key, childVal, vm);
32 }
33 if (!parentVal) { return childVal }
34 var ret = {};
35 extend(ret, parentVal);
36 for (var key$1 in childVal) {
37 var parent = ret[key$1];
38 var child = childVal[key$1];
39 if (parent && !Array.isArray(parent)) {
40 parent = [parent];
41 }
42 ret[key$1] = parent
43 ? parent.concat(child)
44 : Array.isArray(child) ? child : [child];
45 }

52
46 return ret
47
};

watch



component directives filters

1 strats.components=
2 strats.directives=
3
4 strats.filters = function mergeAssets(
5 parentVal, childVal, vm, key
6 ) {
7 var res = Object.create(parentVal || null);
8 if (childVal) {
9 for (var key in childVal) {
10 res[key] = childVal[key];
11 }
12 }
13 return res
14 }


props methods inject computed

data set

watch

component directives filters



53


Vue DOM

vue





input v-model

54


value change

1 <input type="text" v-model.lazy="value">


2 <p>{{value}}</p>



1 <input type="text" v-model.trim="value">



parseFloat

1 <input v-model.number="age" type="number">



55


event.stopPropagation

1 <div @click="shout(2)">
2 <button @click.stop="shout(1)">ok</button>
3 </div>
4 // 1



event.preventDefault

1 <form v-on:submit.prevent="onSubmit"></form>



event.target

1 <div v-on:click.self="doThat">...</div>

v-on:click.prevent.s
elf v-on:click.self.prevent



1 <button @click.once="shout(1)">ok</button>



56
1 <div @click.capture="shout(1)">
2 obj1
3 <div @click.capture="shout(2)">
4 obj2
5 <div @click="shout(3)">
6 obj3
7 <div @click="shout(4)">
8 obj4
9 </div>
10 </div>
11 </div>
12 </div>
13 // : 1 2 4 3



onscroll
onscroll .lazy

1 <!-- ( ) -->
2 <!-- `onScroll` -->
3 <!-- `event.preventDefault()` -->
4 <div v-on:scroll.passive="onScroll">...</div>

.passive .prevent .prevent

passive



html v-on

1 <my-component v-on:click.native="doSomething"></my-component>

57


1 <button @click.left="shout(1)">ok</button>
2 <button @click.right="shout(1)">ok</button>
3 <button @click.middle="shout(1)">ok</button>



onkeyup onkeydown

keyCode vue

1 // keyCode
2 <input type="text" @keyup.keyCode="shout()">

1 Vue.config.keyCodes.f2 = 113



58


props

1 //
2 <comp :myMessage.sync="bar"></comp>
3 //
4 this.$emit('update:myMessage',params);

1 //
2 <comp :myMessage="bar" @update:myMessage="func"></comp>
3 func(e){
4 this.bar = e;
5 }
6 // js
7 func2(){
8 this.$emit('update:myMessage',params);
9 }

async

sync update:value value


props

.sync v-bind

v-bind.sync v-bind.sync=”{ title: doc.title


}”



1 <input id="uid" title="title1" value="1" :index.prop="index">

59


view-Box viewBox

1 <svg :viewBox="viewBox"></svg>







60
Vue DOM Vue

Html

1 <div id="app"> {{ message }} </div>

vue

1 const vm = new Vue({


2 el: '#app',
3 data: {
4 message: ' '
5 }
6 })

message

1 this.message = ' 1'


2 this.message = ' 2'
3 this.message = ' 3'

DOM

1 console.log(vm.$el.textContent) //

message vue Dom

61
DOM



1 {{num}}
2 for(let i=0; i<100000; i++){
3 num = i
4 }

nextTick num
nextTick nextTick


DOM Vue.nextTick()

DOM

1 //
2 vm.message = ' '
3 // DOM
4 console.log(vm.$el.textContent) //
5 Vue.nextTick(function () {
6 // DOM
7 console.log(vm.$el.textContent) //
8 })

vm.$nextTick() this.$nextTick() t
his Vue

62
1 this.message = ' '
2 console.log(this.$el.textContent) // => ' '
3 this.$nextTick(function () {
4 console.log(this.$el.textContent) // => ' '
5 })

$nextTick() Promise async/await

1 this.message = ' '


2 console.log(this.$el.textContent) // => ' '
3 await this.$nextTick()
4 console.log(this.$el.textContent) // => ' '


/src/core/util/next-tick.js

callbacks

callbacks timerFunc pending

63
1 export function nextTick(cb?: Function, ctx?: Object) {
2 let _resolve;
3
4 // cb callbacks
5 callbacks.push(() => {
6 if (cb) {
7 // cb try-catch
8 try {
9 cb.call(ctx);
10 } catch (e) {
11 handleError(e, ctx, 'nextTick');
12 }
13 } else if (_resolve) {
14 _resolve(ctx);
15 }
16 });
17
18 // timerFunc
19 if (!pending) {
20 pending = true;
21 timerFunc();
22 }
23
24 // nextTick Promise
25 if (!cb && typeof Promise !== 'undefined') {
26 return new Promise(resolve => {
27 _resolve = resolve;
28 });
29 }
30 }

timerFunc

Promise.then MutationObserver setImmediate setTimeout

64
1 export let isUsingMicroTask = false
2 if (typeof Promise !== 'undefined' && isNative(Promise)) {
3 // 1 Promise
4 const p = Promise.resolve()
5 timerFunc = () => {
6 p.then(flushCallbacks)
7 if (isIOS) setTimeout(noop)
8 }
9 isUsingMicroTask = true
10 } else if (!isIE && typeof MutationObserver !== 'undefined' && (
11 isNative(MutationObserver) ||
12 MutationObserver.toString() === '[object MutationObserverConstructor]'
13 )) {
14 // 2 MutationObserver
15 let counter = 1
16 const observer = new MutationObserver(flushCallbacks)
17 const textNode = document.createTextNode(String(counter))
18 observer.observe(textNode, {
19 characterData: true
20 })
21 timerFunc = () => {
22 counter = (counter + 1) % 2
23 textNode.data = String(counter)
24 }
25 isUsingMicroTask = true
26 } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate))
{
27 // 3 setImmediate
28 timerFunc = () => {
29 setImmediate(flushCallbacks)
30 }
31 } else {
32 // 4 setTimeout
33 timerFunc = () => {
34 setTimeout(flushCallbacks, 0)
35 }
36 }

flushCallbacks

callbacks callbacks

callbacks

65
1 function flushCallbacks () {
2 pending = false
3 const copies = callbacks.slice(0)
4 callbacks.length = 0
5 for (let i = 0; i < copies.length; i++) {
6 copies[i]()
7 }
8 }










new Vue()

66

vue

1 function Vue (options) {


2 if (process.env.NODE_ENV !== 'production' &&
3 !(this instanceof Vue)
4 ) {
5 warn('Vue is a constructor and should be called with the `new` keyword'
)
6 }
7 this._init(options)
8 }

options data methods

vue _init

1 initMixin(Vue); // _init
2 stateMixin(Vue); // $set $get $delete $watch
3 eventsMixin(Vue); // $on $once $off $emit
4 lifecycleMixin(Vue);// _update $forceUpdate $destroy
5 renderMixin(Vue); // _render dom

initMixin Vue _init

67
1 Vue.prototype._init = function (options?: Object) {
2 const vm: Component = this
3 // a uid
4 vm._uid = uid++
5 let startTag, endTag
6 /* istanbul ignore if */
7 if (process.env.NODE_ENV !== 'production' && config.performance && mar
k) {
8 startTag = `vue-perf-start:${vm._uid}`
9 endTag = `vue-perf-end:${vm._uid}`
10 mark(startTag)
11 }
12
13 // a flag to avoid this being observed
14 vm._isVue = true
15 // merge options
16 // mixins extends
17 if (options && options._isComponent) {
18 // optimize internal component instantiation
19 // since dynamic options merging is pretty slow, and none of the
20 // internal component options needs special treatment.
21 initInternalComponent(vm, options)
22 } else { // vue
23 vm.$options = mergeOptions(
24 resolveConstructorOptions(vm.constructor),
25 options || {},
26 vm
27 )
28 }
29 /* istanbul ignore else */
30 if (process.env.NODE_ENV !== 'production') {
31 // proxy
32 initProxy(vm)
33 } else {
34 vm._renderProxy = vm
35 }
36 // expose real self
37 vm._self = vm
38 //
39 initLifecycle(vm)
40 //
41 initEvents(vm)
42 //
43 initRender(vm)
44 callHook(vm, 'beforeCreate')

68
45 // data props
46
initInjections(vm) // resolve injections before data/props
47
// props/data/method/watch/methods
48
initState(vm)
49
initProvide(vm) // resolve provide after data/props
50
callHook(vm, 'created')
51
52
/* istanbul ignore if */
53
if (process.env.NODE_ENV !== 'production' && config.performance && mar
k) {
54
vm._name = formatComponentName(vm, false)
55
mark(endTag)
56
measure(`vue ${vm._name} init`, startTag, endTag)
57
}
58
//
59
if (vm.$options.el) {
60
vm.$mount(vm.$options.el)
61
}
62
}

beforeCreate data props

created data props


dom dom
vm.$mount

initState props/data/method/watch/methods

69
1 export function initState (vm: Component) {
2 // watcher
3 vm._watchers = []
4 const opts = vm.$options
5 // props
6 if (opts.props) initProps(vm, opts.props)
7 // methods
8 if (opts.methods) initMethods(vm, opts.methods)
9 if (opts.data) {
10 // data
11 initData(vm)
12 } else {
13 observe(vm._data = {}, true /* asRootData */)
14 }
15 if (opts.computed) initComputed(vm, opts.computed)
16 if (opts.watch && opts.watch !== nativeWatch) {
17 initWatch(vm, opts.watch)
18 }
19 }

data initData initState

70
1 function initData (vm: Component) {
2 let data = vm.$options.data
3 // data
4 data = vm._data = typeof data === 'function'
5 ? getData(data, vm)
6 : data || {}
7 if (!isPlainObject(data)) {
8 data = {}
9 process.env.NODE_ENV !== 'production' && warn(
10 'data functions should return an object:\n' +
11 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function'
,
12 vm
13 )
14 }
15 // proxy data on instance
16 const keys = Object.keys(data)
17 const props = vm.$options.props
18 const methods = vm.$options.methods
19 let i = keys.length
20 while (i--) {
21 const key = keys[i]
22 if (process.env.NODE_ENV !== 'production') {
23 //
24 if (methods && hasOwn(methods, key)) {
25 warn(
26 `Method "${key}" has already been defined as a data property.`,
27 vm
28 )
29 }
30 }
31 // state
32 if (props && hasOwn(props, key)) {
33 process.env.NODE_ENV !== 'production' && warn(
34 `The data property "${key}" is already declared as a prop. ` +
35 `Use prop default value instead.`,
36 vm
37 )
38 } else if (!isReserved(key)) { // key
39 // _data vm , this.xxx
40 proxy(vm, `_data`, key)
41 }
42 }
43 // observe data
44 // data

71
45 observe(data, true /* asRootData */)
46
}

props methods data

data

vm.$mount

72
1 Vue.prototype.$mount = function (
2 el?: string | Element,
3 hydrating?: boolean
4 ): Component {
5 //
6 el = el && query(el)
7
8 /* istanbul ignore if */
9 // vue body
10 if (el === document.body || el === document.documentElement) {
11 process.env.NODE_ENV !== 'production' && warn(
12 `Do not mount Vue to <html> or <body> - mount to normal elements ins
tead.`
13 )
14 return this
15 }
16
17 const options = this.$options
18 // resolve template/el and convert to render function
19 if (!options.render) {
20 let template = options.template
21 // template vue
22 if (template) {
23 if (typeof template === 'string') {
24 if (template.charAt(0) === '#') {
25 template = idToTemplate(template)
26 /* istanbul ignore if */
27 if (process.env.NODE_ENV !== 'production' && !template) {
28 warn(
29 `Template element not found or is empty: ${options.template}
`,
30 this
31 )
32 }
33 }
34 } else if (template.nodeType) {
35 template = template.innerHTML
36 } else {
37 if (process.env.NODE_ENV !== 'production') {
38 warn('invalid template option:' + template, this)
39 }
40 return this
41 }
42 } else if (el) {
43 //

73
44 template = getOuterHTML(el)
45
}
46
if (template) {
47
/* istanbul ignore if */
48
if (process.env.NODE_ENV !== 'production' && config.performance && m
ark) {
49
mark('compile')
50
}
51
/**
52
* 1. temmplate ast tree
53
* 2. ast tree render
54
* 3. render
55
*/
56
const { render, staticRenderFns } = compileToFunctions(template, {
57
outputSourceRange: process.env.NODE_ENV !== 'production',
58
shouldDecodeNewlines,
59
shouldDecodeNewlinesForHref,
60
delimiters: options.delimiters,
61
comments: options.comments
62
}, this)
63
options.render = render
64
options.staticRenderFns = staticRenderFns
65
66
/* istanbul ignore if */
67
if (process.env.NODE_ENV !== 'production' && config.performance && m
ark) {
68
mark('compile end')
69
measure(`vue ${this._name} compile`, 'compile', 'compile end')
70
}
71
}
72
}
73
return mount.call(this, el, hydrating)
74
}

body html

template/render template el
render compileToFunctions template rend
er

template

html ast

ast

render

74
render vm mount

1 // public mount method


2 Vue.prototype.$mount = function (
3 el?: string | Element,
4 hydrating?: boolean
5 ): Component {
6 el = el && inBrowser ? query(el) : undefined
7 //
8 return mountComponent(this, el, hydrating)
9 }

mountComponent

75
1 export function mountComponent (
2 vm: Component,
3 el: ?Element,
4 hydrating?: boolean
5 ): Component {
6 vm.$el = el
7 // render
8 // render
9 if (!vm.$options.render) {
10 vm.$options.render = createEmptyVNode
11 if (process.env.NODE_ENV !== 'production') {
12 /* istanbul ignore if */
13 if ((vm.$options.template && vm.$options.template.charAt(0) !== '#')
||
14 vm.$options.el || el) {
15 warn(
16 'You are using the runtime-only build of Vue where the template
' +
17 'compiler is not available. Either pre-compile the templates int
o ' +
18 'render functions, or use the compiler-included build.',
19 vm
20 )
21 } else {
22 // vue
23 warn(
24 'Failed to mount component: template or render function not defi
ned.',
25 vm
26 )
27 }
28 }
29 }
30 // beforeMount
31 callHook(vm, 'beforeMount')
32
33 let updateComponent
34 /* istanbul ignore if */
35 if (process.env.NODE_ENV !== 'production' && config.performance && mark)
{
36 updateComponent = () => {
37 const name = vm._name
38 const id = vm._uid
39 const startTag = `vue-perf-start:${id}`
40 const endTag = `vue-perf-end:${id}`

76
41
42
mark(startTag)
43
const vnode = vm._render()
44
mark(endTag)
45
measure(`vue ${name} render`, startTag, endTag)
46
47
mark(startTag)
48
vm._update(vnode, hydrating)
49
mark(endTag)
50
measure(`vue ${name} patch`, startTag, endTag)
51
}
52
} else {
53
//
54
updateComponent = () => {
55
// lifeCycleMixin _update renderMixin _render
56
vm._update(vm._render(), hydrating)
57
}
58
}
59
// we set this to vm._watcher inside the watcher's constructor
60
// since the watcher's initial patch may call $forceUpdate (e.g. inside
child
61
// component's mounted hook), which relies on vm._watcher being already
defined
62
//
63
new Watcher(vm, updateComponent, noop, {
64
before () {
65
if (vm._isMounted && !vm._isDestroyed) {
66
//
67
callHook(vm, 'beforeUpdate')
68
}
69
}
70
}, true /* isRenderWatcher */)
71
hydrating = false
72
73
// manually mounted instance, call mounted on self
74
// mounted is called for render-created child components in its inserte
d hook
75
if (vm.$vnode == null) {
76
vm._isMounted = true
77
callHook(vm, 'mounted')
78
}
79
return vm
80
}

beforeCreate

77
updateComponent

beforeUpdate

updateComponent vue render update

render vnode

78
1 // vue render
2 Vue.prototype._render = function (): VNode {
3 const vm: Component = this
4 // render option
5 const { render, _parentVnode } = vm.$options
6
7 if (_parentVnode) {
8 vm.$scopedSlots = normalizeScopedSlots(
9 _parentVnode.data.scopedSlots,
10 vm.$slots,
11 vm.$scopedSlots
12 )
13 }
14
15 // set parent vnode. this allows render functions to have access
16 // to the data on the placeholder node.
17 vm.$vnode = _parentVnode
18 // render self
19 let vnode
20 try {
21 // There's no need to maintain a stack because all render fns are
called
22 // separately from one another. Nested component's render fns are
called
23 // when parent component is patched.
24 currentRenderingInstance = vm
25 // render render createElement vN
ode
26 vnode = render.call(vm._renderProxy, vm.$createElement)
27 } catch (e) {
28 handleError(e, vm, `render`)
29 // return error render result,
30 // or previous vnode to prevent render error causing blank compone
nt
31 /* istanbul ignore else */
32 if (process.env.NODE_ENV !== 'production' && vm.$options.renderErr
or) {
33 try {
34 vnode = vm.$options.renderError.call(vm._renderProxy, vm.
$createElement, e)
35 } catch (e) {
36 handleError(e, vm, `renderError`)
37 vnode = vm._vnode
38 }
39 } else {

79
40 vnode = vm._vnode
41
}
42
} finally {
43
currentRenderingInstance = null
44
}
45
// if the returned array contains only a single node, allow it
46
if (Array.isArray(vnode) && vnode.length === 1) {
47
vnode = vnode[0]
48
}
49
// return empty vnode in case the render function errored out
50
if (!(vnode instanceof VNode)) {
51
if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode))
{
52
warn(
53
'Multiple root nodes returned from render function. Rende
r function ' +
54
'should return a single root node.',
55
vm
56
)
57
}
58
vnode = createEmptyVNode()
59
}
60
// set parent
61
vnode.parent = _parentVnode
62
return vnode
63
}

_update patch vnode DOM

80
1 Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
2 const vm: Component = this
3 const prevEl = vm.$el
4 const prevVnode = vm._vnode
5 //
6 const restoreActiveInstance = setActiveInstance(vm)
7 vm._vnode = vnode
8 // Vue.prototype.__patch__ is injected in entry points
9 // based on the rendering backend used.
10 if (!prevVnode) {
11 // initial render
12 //
13 vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly
*/)
14 } else {
15 // updates
16 vm.$el = vm.__patch__(prevVnode, vnode)
17 }
18 restoreActiveInstance()
19 // update __vue__ reference
20 if (prevEl) {
21 prevEl.__vue__ = null
22 }
23 if (vm.$el) {
24 vm.$el.__vue__ = vm
25 }
26 // if parent is an HOC, update its $el as well
27 if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
28 vm.$parent.$el = vm.$el
29 }
30 // updated hook is called by the scheduler to ensure that children are
31 // updated in a parent's updated hook.
32 }


new Vue _init

$set $get $delete $watch

$on $off $emit $off

_update $forceUpdate $destroy

$mount

81
mountComponent

updateComponent

render DOM

_update DOM DOM




diff

diff vue dom dom VN


ode


diff



82


vue diff

VNode

83
diff
endIndex startIndex

diff
endIndex startInd
ex

84
startIndex startIndex endIndex

diff
startIndex startIndex

85
diff
startIndex

startIndex endIndex newStartIdx newEndIdx

86

set Dep.notify Watcher p
atch DOM

87
1 function patch(oldVnode, vnode, hydrating, removeOnly) {
2 if (isUndef(vnode)) { // destory
3 if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
4 return
5 }
6
7 let isInitialPatch = false
8 const insertedVnodeQueue = []
9
10 if (isUndef(oldVnode)) {
11 isInitialPatch = true
12 createElm(vnode, insertedVnodeQueue) // do
m
13 } else {
14 const isRealElement = isDef(oldVnode.nodeType)
15 if (!isRealElement && sameVnode(oldVnode, vnode)) {
16 // patchVnode
17 patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, re
moveOnly)
18 } else {
19 // dom
20 if (isRealElement) {
21
22 if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_A
TTR)) {
23 oldVnode.removeAttribute(SSR_ATTR)
24 hydrating = true
25 }
26 if (isTrue(hydrating)) {
27 if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
28 invokeInsertHook(vnode, insertedVnodeQueue, true)
29 return oldVnode
30 }
31 }
32 oldVnode = emptyNodeAt(oldVnode)
33 }
34 return vnode.elm
35 }
36 }
37 }

patch oldVnode Vnode

destory

88
createElm

sameVnode patchVno
de

patchVnode

89
1 function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
2 //
3 if (oldVnode === vnode) {
4 return
5 }
6
7 // vnode.el dom el vnode.el
8 const elm = vnode.elm = oldVnode.elm
9
10 //
11 if (isTrue(oldVnode.isAsyncPlaceholder)) {
12 if (isDef(vnode.asyncFactory.resolved)) {
13 hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
14 } else {
15 vnode.isAsyncPlaceholder = true
16 }
17 return
18 }
19 // key
20 // vnode v-once oldVnode.elm oldVnod
e.child vnode
21 //
22 if (isTrue(vnode.isStatic) &&
23 isTrue(oldVnode.isStatic) &&
24 vnode.key === oldVnode.key &&
25 (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
26 ) {
27 vnode.componentInstance = oldVnode.componentInstance
28 return
29 }
30
31 let i
32 const data = vnode.data
33 if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
34 i(oldVnode, vnode)
35 }
36
37 const oldCh = oldVnode.children
38 const ch = vnode.children
39 if (isDef(data) && isPatchable(vnode)) {
40 for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnod
e)
41 if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
42 }
43 // vnode

90
44 if (isUndef(vnode.text)) {
45
//
46
if (isDef(oldCh) && isDef(ch)) {
47
// updateChildren
48
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueu
e, removeOnly)
49
50
// vnode
51
} else if (isDef(ch)) {
52
if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
53
// elm dom dom
54
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
55
56
// vnode vnode oldCh
57
} else if (isDef(oldCh)) {
58
removeVnodes(elm, oldCh, 0, oldCh.length - 1)
59
60
//
61
} else if (isDef(oldVnode.text)) {
62
nodeOps.setTextContent(elm, '')
63
}
64
65
// vnode vnode
66
// vnode.text != oldVnode.text vnode.elm
67
} else if (oldVnode.text !== vnode.text) {
68
nodeOps.setTextContent(elm, vnode.text)
69
}
70
if (isDef(data)) {
71
if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnod
e)
72
}
73
}

patchVnode

dom

DOM

DOM

updateChildren

91
1 function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, rem
oveOnly) {
2 let oldStartIdx = 0 //
3 let newStartIdx = 0 //
4 let oldEndIdx = oldCh.length - 1 //
5 let newEndIdx = newCh.length - 1 //
6 let oldStartVnode = oldCh[0] // oldVnode child
7 let oldEndVnode = oldCh[oldEndIdx] // oldVnode child
8 let newStartVnode = newCh[0] // newVnode child
9 let newEndVnode = newCh[newEndIdx] // newVnode child
10 let oldKeyToIdx, idxInOld, vnodeToMove, refElm
11
12 // removeOnly is a special flag used only by <transition-group>
13 // to ensure removed elements stay in correct relative positions
14 // during leaving transitions
15 const canMove = !removeOnly
16
17 // oldStartVnode oldEndVnode diff

18 while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {


19 // oldVnode child
20 if (isUndef(oldStartVnode)) {
21 // oldStart
22 oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
23
24 // oldVnode child
25 } else if (isUndef(oldEndVnode)) {
26 // oldEnd
27 oldEndVnode = oldCh[--oldEndIdx]
28
29 // oldStartVnode newStartVnode
30 } else if (sameVnode(oldStartVnode, newStartVnode)) {
31 // patch oldStartVnode newStartVnode
32 patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
33 oldStartVnode = oldCh[++oldStartIdx]
34 newStartVnode = newCh[++newStartIdx]
35
36 // oldEndVnode newEndVnode
37 } else if (sameVnode(oldEndVnode, newEndVnode)) {
38 // patch oldEndVnode newEndVnode
39 patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
40 oldEndVnode = oldCh[--oldEndIdx]
41 newEndVnode = newCh[--newEndIdx]
42
43 // oldStartVnode newEndVnode

92
44 } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved
right
45
// patch oldStartVnode newEndVnode
46
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
47
// removeOnly false oldStartVnode.eml oldEndVnode.elm

48
canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nod
eOps.nextSibling(oldEndVnode.elm))
49
// oldStart newEnd
50
oldStartVnode = oldCh[++oldStartIdx]
51
newEndVnode = newCh[--newEndIdx]
52
53
// oldEndVnode newStartVnode
54
} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved
left
55
// patch oldEndVnode newStartVnode
56
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
57
// removeOnly false oldEndVnode.elm oldStartVnode.elm

58
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldSt
artVnode.elm)
59
// oldEnd newStart
60
oldEndVnode = oldCh[--oldEndIdx]
61
newStartVnode = newCh[++newStartIdx]
62
63
//
64
} else {
65
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh,
oldStartIdx, oldEndIdx)
66
67
// oldChildren newStartVnode key Vnode
68
idxInOld = isDef(newStartVnode.key)
69
? oldKeyToIdx[newStartVnode.key]
70
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
71
72
// newStartVnode
73
if (isUndef(idxInOld)) { // New element
74
// Vnode
75
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStar
tVnode.elm)
76
77
// newStartVnodej key Vnode vnodeToMove
78
} else {
79
vnodeToMove = oldCh[idxInOld]
80
/* istanbul ignore if */
81
if (process.env.NODE_ENV !== 'production' && !vnodeToMove) {
82
warn(
83

93
'It seems there are duplicate keys that is causing an updat
84
e error. ' +
85
'Make sure each v-for item has a unique key.'
86
)
87
}
88
89
// key
// key newCh oldCh key
key oldKeyToIdx key
90
dom
91
if (sameVnode(vnodeToMove, newStartVnode)) {
92
// patch vnodeToMove newStartVnode
93
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
94
//
95
oldCh[idxInOld] = undefined
// removeOnly false newStartVnodej key
96
Vnode vnodeToMove.elm
97
// oldStartVnode.elm
canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, o
98
ldStartVnode.elm)
99
100
// key
101
} else {
102
// same key but different element. treat as new element
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldSt
103
artVnode.elm)
104
}
105
}
106
107
//
108
newStartVnode = newCh[++newStartIdx]
109
}
}

while

VNode start patchVnode VNode

VNode end patchVnode VNode

VNode start VNode end patchVnode


dom oldEndVnode VNode
VNode

VNode end VNode start patchVnode


dom oldStartVnode VNode

94
VNode

VNode key index value newSt


artVnode key VNode patchVnode d
om oldStartVnode dom

createElm dom newStartIdx


watcher patch DOM

isSameVnode patchVnode

patchVnode
dom el

el Vnode

oldVnode VNode el

oldVnode VNode VNode el

updateChildren

updateChildren

VNode

patchVnode patch
createElem key VNode



95


Vue
.vue


Vue

vue-custom-element

vue-touch

vue-router

Vue Vue.prototype

API vue-router



96




vue .vue

vue

1 <template>
2 </template>
3 <script>
4 export default{
5 ...
6 }
7 </script>
8 <style>
9 </style>

template
template template

1 <template id="testComponent"> //
2 <div>component!</div>
3 </template>
4
5 Vue.component('componentA',{
6 template: '#testComponent'
7 template: `<div>component</div>` //
8 })



97
vue install Vue

1 MyPlugin.install = function (Vue, options) {


2 // 1. property
3 Vue.myGlobalMethod = function () {
4 // ...
5 }
6
7 // 2.
8 Vue.directive('my-directive', {
9 bind (el, binding, vnode, oldVnode) {
10 // ...
11 }
12 ...
13 })
14
15 // 3.
16 Vue.mixin({
17 created: function () {
18 // ...
19 }
20 ...
21 })
22
23 // 4.
24 Vue.prototype.$myMethod = function (methodOptions) {
25 // ...
26 }
27 }





vue

Vue.component

1 Vue.component('my-component-name', { /* ... */ })

98
components

1 const component1 = {...} //


2
3 export default {
4 components:{
5 component1 //
6 }
7 }



Vue.use()

1 Vue.use( ,{ /* ... */} )

new Vue()

Vue.use



(Component) App App.vue

(Plugin) Vue

Vue



99




vue CORS Proxy



100
CORS HTTP

CORS

koa

Access-Control-Allow-Origin

1 app.use(async (ctx, next)=> {


2 ctx.set('Access-Control-Allow-Origin', '*');
3 ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, A
uthorization, Accept, X-Requested-With , yourHeaderFeild');
4 ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTION
S');
5 if (ctx.method == 'OPTIONS') {
6 ctx.body = 200;
7 } else {
8 await next();
9 }
10 })

Access-Control-Allow-Origin
Access-Control-Allow-Origin host



101
vue-cli webpack

vue.config.js

1 amodule.exports = {
2 devServer: {
3 host: '127.0.0.1',
4 port: 8084,
5 open: true,// vue
6 proxy: {
7 '/api': { // '/api' node url /api

8 target: "http://xxx.xxx.xx.xx:8080", //

9 changeOrigin: true, //
10 pathRewrite: { // pathRewrite Request Url '/
api' ""
11 '^/api': ""
12 }
13 }
14 }
15 }
16 }

axios

1 axios.defaults.baseURL = '/api'

express

102
1 var express = require('express');
2 const proxy = require('http-proxy-middleware')
3 const app = express()
4 app.use(express.static(__dirname + '/'))
5 app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: fals
e
6 }));
7 module.exports = app

nginx

1 server {
2 listen 80;
3 # server_name www.josephxia.com;
4 location / {
5 root /var/www/html;
6 index index.html index.htm;
7 try_files $uri $uri/ /index.html;
8 }
9 location /api {
10 proxy_pass http://127.0.0.1:3000;
11 proxy_redirect off;
12 proxy_set_header Host $host;
13 proxy_set_header X-Real-IP $remote_addr;
14 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
15 }
16 }



103


vue

v-

v-model v-show Vue

104
1 //
2 `v-xxx`
3
4 // --
5 `v-xxx="value"`
6
7 // -- `v-html="'<p> </p>'"`
8 `v-xxx="'string'"`
9
10 // -- `arg` `v-bind:class="className"`
11 `v-xxx:arg="value"`
12
13 // -- `modifier`
14 `v-xxx:arg.modifier="value"`



Vue.directive

Vue.directive v-

1 // `v-focus`
2 Vue.directive('focus', {
3 // DOM ……
4 inserted: function (el) {
5 //
6 el.focus() //
7 }
8 })

options directive

105
1 directives: {
2 focus: {
3 //
4 inserted: function (el) {
5 el.focus() //
6 }
7 }
8 }

v-focus

1 <input v-focus />

bind

inserted

update VNode VNode

componentUpdated VNode VNode

unbind

el DOM

binding property
name v-

value v-my-directive="1 + 1" 2

oldValue update componentUpdated

expression v-my-directive="1 + 1"


"1 + 1"

arg v-my-directive:foo "foo"

modifiers v-my-directive.foo.bar
{ foo: true, bar: true }

vnode Vue

106
oldVnode update componentUpdated

el
dataset

1 <div v-demo="{ color: 'white', text: 'hello!' }"></div>


2 <script>
3 Vue.directive('demo', function (el, binding) {
4 console.log(binding.value.color) // "white"
5 console.log(binding.value.text) // "hello!"
6 })
7 </script>





v-throttle

107
1 // 1. v-throttle
2 Vue.directive('throttle', {
3 bind: (el, binding) => {
4 let throttleTime = binding.value; //
5 if (!throttleTime) { // 2s
6 throttleTime = 2000;
7 }
8 let cbFun;
9 el.addEventListener('click', event => {
10 if (!cbFun) { //
11 cbFun = setTimeout(() => {
12 cbFun = null;
13 }, throttleTime);
14 } else {
15 event && event.stopImmediatePropagation();
16 }
17 }, true);
18 },
19 });
20 // 2. button v-throttle
21 <button @click="sayHello" v-throttle> </button>



v-lazy

108
1 const LazyLoad = {
2 // install
3 install(Vue,options){
4 // loading
5 let defaultSrc = options.default;
6 Vue.directive('lazy',{
7 bind(el,binding){
8 LazyLoad.init(el,binding.value,defaultSrc);
9 },
10 inserted(el){
11 //
12 if('IntersectionObserver' in window){
13 LazyLoad.observe(el);
14 }else{
15 LazyLoad.listenerScroll(el);
16 }
17
18 },
19 })
20 },
21 //
22 init(el,val,def){
23 // data-src src
24 el.setAttribute('data-src',val);
25 // src loading
26 el.setAttribute('src',def);
27 },
28 // IntersectionObserver el
29 observe(el){
30 let io = new IntersectionObserver(entries => {
31 let realSrc = el.dataset.src;
32 if(entries[0].isIntersecting){
33 if(realSrc){
34 el.src = realSrc;
35 el.removeAttribute('data-src');
36 }
37 }
38 });
39 io.observe(el);
40 },
41 // scroll
42 listenerScroll(el){
43 let handler = LazyLoad.throttle(LazyLoad.load,300);
44 LazyLoad.load(el);
45 window.addEventListener('scroll',() => {

109
46 handler(el);
47
});
48
},
49
//
50
load(el){
51
let windowHeight = document.documentElement.clientHeight
52
let elTop = el.getBoundingClientRect().top;
53
let elBtm = el.getBoundingClientRect().bottom;
54
let realSrc = el.dataset.src;
55
if(elTop - windowHeight<0&&elBtm > 0){
56
if(realSrc){
57
el.src = realSrc;
58
el.removeAttribute('data-src');
59
}
60
}
61
},
62
//
63
throttle(fn,delay){
64
let timer;
65
let prevTime;
66
return function(...args){
67
let currTime = Date.now();
68
let context = this;
69
if(!prevTime) prevTime = currTime;
70
clearTimeout(timer);
71
72
if(currTime - prevTime > delay){
73
prevTime = currTime;
74
fn.apply(context,args);
75
clearTimeout(timer);
76
return;
77
}
78
79
timer = setTimeout(function(){
80
prevTime = Date.now();
81
timer = null;
82
fn.apply(context,args);
83
},delay);
84
}
85
}
86
87
}
88
export default LazyLoad;



110
1 import { Message } from 'ant-design-vue';
2
3 const vCopy = { //
4 /*
5 bind
6 el: dom
7 value: copy
8 */
9 bind(el, { value }) {
10 el.$value = value; //

11 el.handler = () => {
12 if (!el.$value) {
13 // ant-design-vue
14 Message.warning(' ');
15 return;
16 }
17 // textarea
18 const textarea = document.createElement('textarea');
19 // textarea readonly iOS textarea

20 textarea.readOnly = 'readonly';
21 textarea.style.position = 'absolute';
22 textarea.style.left = '-9999px';
23 // copy textarea value
24 textarea.value = el.$value;
25 // textarea body
26 document.body.appendChild(textarea);
27 //
28 textarea.select();
29 // textarea.setSelectionRange(0, textarea.value.length);
30 const result = document.execCommand('Copy');
31 if (result) {
32 Message.success(' ');
33 }
34 document.body.removeChild(textarea);
35 };
36 // copy
37 el.addEventListener('click', el.handler);
38 },
39 //
40 componentUpdated(el, { value }) {
41 el.$value = value;
42 },
43 //

111
44 unbind(el) {
45
el.removeEventListener('click', el.handler);
46
},
47
};
48
49
export default vCopy;




filter

Vue

Vue3 filter

112

vue v-bind Java
Script

1 <!-- -->
2 {{ message | capitalize }}
3
4 <!-- `v-bind` -->
5 <div v-bind:id="rawId | formatId"></div>



1 filters: {
2 capitalize: function (value) {
3 if (!value) return ''
4 value = value.toString()
5 return value.charAt(0).toUpperCase() + value.slice(1)
6 }
7 }

1 Vue.filter('capitalize', function (value) {


2 if (!value) return ''
3 value = value.toString()
4 return value.charAt(0).toUpperCase() + value.slice(1)
5 })
6
7 new Vue({
8 // ...
9 })

113
capitalize
message

1 {{ message | filterA | filterB }}

filterA message
filterB filterA
filterB

JavaScript

1 {{ message | filterA('arg1', arg2) }}

filterA

message 'arg1' arg2

1 <div id="app">
2 <p>{{ msg | msgFormat(' ','--')}}</p>
3 </div>
4
5 <script>
6 // Vue msgFormat
7 Vue.filter('msgFormat', function(msg, arg, arg2) {
8 // replace

9 return msg.replace(/ /g, arg+arg2)


10 })
11 </script>



114


1 Vue.filter('toThousandFilter', function (value) {


2 if (!value) return ''
3 value = value.toString()
4 return .replace(str.indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)
(?=(?:\d{3})+$)/g, '$1,')
5 })



1 {{ message | capitalize }}

parseFilters

1 _s(_f('filterFormat')(message))

_f

resolveFilter this.$options.filters

1 //
2 this.$options.filters['filterFormat'](message) // message

resolveFilter

115
1 import { indentity,resolveAsset } from 'core/util/index'
2
3 export function resolveFilter(id){
4 return resolveAsset(this.$options,'filters',id,true) || identity
5 }

resolveAsset option id

resolveAsset

1 export function resolveAsset(options,type,id,warnMissing){ //


resolveFilter type 'filters',

2 if(typeof id !== 'string'){ // id

3 return
4 }
5 const assets = options[type] //
6 // id assets
7 if(hasOwn(assets,id)) return assets[id] //
8 //
9 const camelizedId = camelize(id) //
10 if(hasOwn(assets,camelizedId)) return assets[camelizedId]
11 //
12 const PascalCaseId = capitalize(camelizedId) //
13 if(hasOwn(assets,PascalCaseId)) return assets[PascalCaseId]
14 // ( )
15 const result = assets[id] || assets[camelizedId] || assets[PascalCaseI
d]
16 //
17 if(process.env.NODE_ENV !== 'production' && warnMissing && !result){
18 warn('Failed to resolve ' + type.slice(0,-1) + ': ' + id, options)
19 }
20 //
21 return result
22 }

_s

_s toString toString to
String Vnode

116
1 function toString(value){
2 return value == null
3 ? ''
4 : typeof value === 'object'
5 ? JSON.stringify(value,null,2)// JSON.stringify()

6 : String(value)
7 }

parseFilters

1 function parseFilters (filter) {


2 let filters = filter.split('|')
3 let expression = filters.shift().trim() // shift()

4 let i
5 if (filters) {
6 for(i = 0;i < filters.length;i++){
7 experssion = warpFilter(expression,filters[i].trim()) //
expression
8 }
9 }
10 return expression
11 }
12 // warpFilter
13 function warpFilter(exp,filter){
14 //
15 const i = filter.indexof('(')
16 if(i<0){ //
17 return `_f("${filter}")(${exp})`
18 }else{
19 const name = filter.slice(0,i) //
20 const args = filter.slice(i+1) // ‘)’
21 return `_f('${name}')(${exp},${args}` // ')'
22 }
23 }



117
parseFilters

resolveFilter

toString toString Vnode


text




slot Web Components

118
1 <template id="element-details-template">
2 <slot name="element-name">Slot template</slot>
3 </template>
4 <element-details>
5 <span slot="element-name">1</span>
6 </element-details>
7 <element-details>
8 <span slot="element-name">2</span>
9 </element-details>

template DOM

1 customElements.define('element-details',
2 class extends HTMLElement {
3 constructor() {
4 super();
5 const template = document
6 .getElementById('element-details-template')
7 .content;
8 const shadowRoot = this.attachShadow({mode: 'open'})
9 .appendChild(template.cloneNode(true));
10 }
11 })

Vue

Slot solt
slot

119


slot


slot

120


<slot> DOM
DOM

Child.vue

1 <template>
2 <slot>
3 <p> </p>
4 </slot>
5 </template>

1 <Child>
2 <div> </div>
3 </Child>



name

slot name

Child.vue

1 <template>
2 <slot> </slot>
3 <slot name="content"> </slot>
4 </template>

121
1 <child>
2 <template v-slot:default> </template>
3 <!-- -->
4 <template v-slot:content> ...</template>
5 </child>



v-slot

v-slot:

Child.vue

1 <template>
2 <slot name="footer" testProps=" ">
3 <h3> footer </h3>
4 </slot>
5 </template>

1 <child>
2 <!-- v-slot -->
3 <template v-slot:default="slotProps">
4 {{slotProps.testProps}}
5 </template>
6 <template #default="slotProps">
7 {{slotProps.testProps}}
8 </template>
9 </child>



v-slot <template>

default v-slot

122
# #default

v-slot={user} v-slot="{user: newName}"


v-slot="{user = ' '}"


slot VNode Vue templa
te -> render function -> VNode -> DOM slot

buttonCounter

1 Vue.component('button-counter', {
2 template: '<div> <slot> </slot></div>'
3 })

1 new Vue({
2 el: '#app',
3 template: '<button-counter><span> slot </span></button-counter
>',
4 components:{buttonCounter}
5 })

buttonCounter

1 (function anonymous(
2 ) {
3 with(this){return _c('div',[_t("default",[_v(" ")])],2)}
4 })

_v _t

renderSlot

123
1 function renderSlot (
2 name,
3 fallback,
4 props,
5 bindObject
6 ) {
7 //
8 var scopedSlotFn = this.$scopedSlots[name];
9 var nodes;
10 // nodes
11 //
12 nodes = scopedSlotFn(props) || fallback;
13 return nodes;
14 }

name default fallback slot

this.$scopredSlots vm.slot

1 function initRender (vm) {


2 ...
3 vm.$slots = resolveSlots(options._renderChildren, renderContext);
4 ...
5 }

resolveSlots children slots

124
1 function resolveSlots (
2 children,
3 context
4 ) {
5 if (!children || !children.length) {
6 return {}
7 }
8 var slots = {};
9 for (var i = 0, l = children.length; i < l; i++) {
10 var child = children[i];
11 var data = child.data;
12 // remove slot attribute if the node is resolved as a Vue slot node
13 if (data && data.attrs && data.attrs.slot) {
14 delete data.attrs.slot;
15 }
16 // named slots should only be respected if the vnode was rendered i
n the
17 // same context.
18 if ((child.context === context || child.fnContext === context) &&
19 data && data.slot != null
20 ) {
21 // slot (slot="header") key
22 var name = data.slot;
23 var slot = (slots[name] || (slots[name] = []));
24 // tempalte template children
template
25 if (child.tag === 'template') {
26 slot.push.apply(slot, child.children || []);
27 } else {
28 slot.push(child);
29 }
30 } else {
31 // default
32 (slots.default || (slots.default = [])).push(child);
33 }
34 }
35 // ignore slots that contains only whitespace
36 for (var name$1 in slots) {
37 if (slots[name$1].every(isWhitespace)) {
38 delete slots[name$1];
39 }
40 }
41 return slots
42 }

125
_render normalizeScopedSlots vm.$scopedSlots

1 vm.$scopedSlots = normalizeScopedSlots(
2 _parentVnode.data.scopedSlots,
3 vm.$slots,
4 vm.$scopedSlots
5 );

renderSlot props
_t

 

 
Virtual DOM React Vue DOM
React-Native Weex

DOM JavaScript VNode

Javascript DOM Object tag


attrs children

126
DOM DOM
DOM

vue DOM

DOM

1 <div id="app">
2 <p class="p"> </p>
3 <h3>{{ foo }}</h3>
4 </div>

vue

1 const app = new Vue({


2 el:"#app",
3 data:{
4 foo:"foo"
5 }
6 })

render render DOM

1 (function anonymous(
2 ) {
3 with(this){return _c('div',{attrs:{"id":"app"}},[_c('p',{staticClass:"p"}
,
4 [_v(" ")]),_v(" "),_c('h3',[_v(_s(foo))])])}})

VNode vue diff


dom

 
DOM DOM

127
DOM div

DOM

api jQuery DOM DOM

DOM DOM

VNode DOM DOM DOM di


ff js js attach DOM

 
vue VNode

128
1 export default class VNode {
2 tag: string | void;
3 data: VNodeData | void;
4 children: ?Array<VNode>;
5 text: string | void;
6 elm: Node | void;
7 ns: string | void;
8 context: Component | void; // rendered in this component's scope
9 functionalContext: Component | void; // only for functional component ro
ot nodes
10 key: string | number | void;
11 componentOptions: VNodeComponentOptions | void;
12 componentInstance: Component | void; // component instance
13 parent: VNode | void; // component placeholder node
14 raw: boolean; // contains raw HTML? (server only)
15 isStatic: boolean; // hoisted static node
16 isRootInsert: boolean; // necessary for enter transition check
17 isComment: boolean; // empty comment placeholder?
18 isCloned: boolean; // is a cloned node?
19 isOnce: boolean; // is a v-once node?
20
21 constructor (
22 tag?: string,
23 data?: VNodeData,
24 children?: ?Array<VNode>,
25 text?: string,
26 elm?: Node,
27 context?: Component,
28 componentOptions?: VNodeComponentOptions
29 ) {
30 /* */
31 this.tag = tag
32 /* VNodeData VNod
eData */
33 this.data = data
34 /* */
35 this.children = children
36 /* */
37 this.text = text
38 /* dom */
39 this.elm = elm
40 /* */
41 this.ns = undefined
42 /* */
43 this.context = context

129
44 /* */
45
this.functionalContext = undefined
46
/* key */
47
this.key = data && data.key
48
/* option */
49
this.componentOptions = componentOptions
50
/* */
51
this.componentInstance = undefined
52
/* */
53
this.parent = undefined
54
/* HTML innerHTML true textContent
false*/
55
this.raw = false
56
/* */
57
this.isStatic = false
58
/* */
59
this.isRootInsert = true
60
/* */
61
this.isComment = false
62
/* */
63
this.isCloned = false
64
/* v-once */
65
this.isOnce = false
66
}
67
68
// DEPRECATED: alias for componentInstance for backwards compat.
69
/* istanbul ignore next https://github.com/answershuto/learnVue*/
70
get child (): Component | void {
71
return this.componentInstance
72
}
73
}

VNode

context Vue

elm DOM

vue createElement VNode

130
1 export function createElement (
2 context: Component,
3 tag: any,
4 data: any,
5 children: any,
6 normalizationType: any,
7 alwaysNormalize: boolean
8 ): VNode | Array<VNode> {
9 if (Array.isArray(data) || isPrimitive(data)) {
10 normalizationType = children
11 children = data
12 data = undefined
13 }
14 if (isTrue(alwaysNormalize)) {
15 normalizationType = ALWAYS_NORMALIZE
16 }
17 return _createElement(context, tag, data, children, normalizationType)
18 }

createElement _createElement

131
1 export function _createElement(
2 context: Component,
3 tag?: string | Class<Component> | Function | Object,
4 data?: VNodeData,
5 children?: any,
6 normalizationType?: number
7 ): VNode | Array<VNode> {
8 if (isDef(data) && isDef((data: any).__ob__)) {
9 process.env.NODE_ENV !== 'production' && warn(
10 `Avoid using observed data object as vnode data: ${JSON.string
ify(data)}\n` +
11 'Always create fresh vnode data objects in each render!',
12 context`
13 )
14 return createEmptyVNode()
15 }
16 // object syntax in v-bind
17 if (isDef(data) && isDef(data.is)) {
18 tag = data.is
19 }
20 if (!tag) {
21 // in case of component :is set to falsy value
22 return createEmptyVNode()
23 }
24 ...
25 // support single function children as default scoped slot
26 if (Array.isArray(children) &&
27 typeof children[0] === 'function'
28 ) {
29 data = data || {}
30 data.scopedSlots = { default: children[0] }
31 children.length = 0
32 }
33 if (normalizationType === ALWAYS_NORMALIZE) {
34 children = normalizeChildren(children)
35 } else if ( === SIMPLE_NORMALIZE) {
36 children = simpleNormalizeChildren(children)
37 }
38 // VNode
39 ...
40 }

_createElement

context VNode Component

132
Component

data VNode VNodeData

children VNode

normalizationType
render

normalizationType children

1 if (normalizationType === ALWAYS_NORMALIZE) {


2 children = normalizeChildren(children)
3 } else if ( === SIMPLE_NORMALIZE) {
4 children = simpleNormalizeChildren(children)
5 }

simpleNormalizeChildren render

normalizeChildren

render

slot v-for

simpleNormalizeChildren normalizeChildren children


children VNode Array

children

children VNode

133
1 let vnode, ns
2 // tag
3 if (typeof tag === 'string') {
4 let Ctor
5 ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag
)
6 if (config.isReservedTag(tag)) {
7 // VNode
8 vnode = new VNode(
9 config.parsePlatformTagName(tag), data, children,
10 undefined, undefined, context
11 )
12 } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag
))) {
13 // component
14 // component createComponent VNode
15 vnode = createComponent(Ctor, data, context, children, tag)
16 } else {
17 vnode = new VNode(
18 tag, data, children,
19 undefined, undefined, context
20 )
21 }
22 } else {
23 // direct component options / constructor
24 vnode = createComponent(tag, data, context, children)
25 }

createComponent VNode

134
1 export function createComponent (
2 Ctor: Class<Component> | Function | Object | void,
3 data: ?VNodeData,
4 context: Component,
5 children: ?Array<VNode>,
6 tag?: string
7 ): VNode | Array<VNode> | void {
8 if (isUndef(Ctor)) {
9 return
10 }
11 //
12 const baseCtor = context.$options._base
13
14 // plain options object: turn it into a constructor
15 if (isObject(Ctor)) {
16 Ctor = baseCtor.extend(Ctor)
17 }
18
19 // if at this stage it's not a constructor or an async component factor
y,
20 // reject.
21 if (typeof Ctor !== 'function') {
22 if (process.env.NODE_ENV !== 'production') {
23 warn(`Invalid Component definition: ${String(Ctor)}`, context)
24 }
25 return
26 }
27
28 // async component
29 let asyncFactory
30 if (isUndef(Ctor.cid)) {
31 asyncFactory = Ctor
32 Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
33 if (Ctor === undefined) {
34 return createAsyncPlaceholder(
35 asyncFactory,
36 data,
37 context,
38 children,
39 tag
40 )
41 }
42 }
43
44 data = data || {}

135
45
46
// resolve constructor options in case global mixins are applied after
47
// component constructor creation
48
resolveConstructorOptions(Ctor)
49
50
// transform component v-model data into props & events
51
if (isDef(data.model)) {
52
transformModel(Ctor.options, data)
53
}
54
55
// extract props
56
const propsData = extractPropsFromVNodeData(data, Ctor, tag)
57
58
// functional component
59
if (isTrue(Ctor.options.functional)) {
60
return createFunctionalComponent(Ctor, propsData, data, context, child
ren)
61
}
62
63
// extract listeners, since these needs to be treated as
64
// child component listeners instead of DOM listeners
65
const listeners = data.on
66
// replace with listeners with .native modifier
67
// so it gets processed during parent component patch.
68
data.on = data.nativeOn
69
70
if (isTrue(Ctor.options.abstract)) {
71
const slot = data.slot
72
data = {}
73
if (slot) {
74
data.slot = slot
75
}
76
}
77
78
// data.hook
79
installComponentHooks(data)
80
81
// VNode VNode children
82
const name = Ctor.options.name || tag
83
const vnode = new VNode(
84
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
85
data, undefined, undefined, undefined, context,
86
{ Ctor, propsData, listeners, tag, children },
87
asyncFactory
88
)
89
if (__WEEX__ && isRecyclableComponent(vnode)) {
90
return renderRecyclableComponentTemplate(vnode)
91
}

136
92
93
return vnode
94
}

createComponent VNode

Ctor

installComponentHooks
vnode

 

createElement VNode VNode children children


VNode DOM




axios HTTP

XMLHttpRequest HTTP Promise


Node.js Vue vue-resource a
xios axios Vue



137
XMLHttpRequests

node.js http

Promise

JSON

XSRF



1 //
2 npm install axios --S
3 // cdn
4 <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

1 import axios from 'axios'

1 axios({
2 url:'xxx', //
3 method:"GET", //
4 params:{ // get params , post data
5 type: '',
6 page: 1
7 }
8 }).then(res => {
9 // res
10 console.log(res);
11 })

138
axios.all([])

1 function getUserAccount() {
2 return axios.get('/user/12345');
3 }
4
5 function getUserPermissions() {
6 return axios.get('/user/12345/permissions');
7 }
8
9 axios.all([getUserAccount(), getUserPermissions()])
10 .then(axios.spread(function (res1, res2) {
11 // res1 res2
12 //
13 }));


axios

HTTP

axios

139
1 axios('http://localhost:3000/data', {
2 //
3 method: 'GET',
4 timeout: 1000,
5 withCredentials: true,
6 headers: {
7 'Content-Type': 'application/json',
8 Authorization: 'xxx',
9 },
10 transformRequest: [function (data, headers) {
11 return data;
12 }],
13 // ...
14 })
15 .then((data) => {
16 // todo:
17 console.log(data);
18 }, (err) => {
19 //
20 if (err.response.status === 401) {
21 // handle authorization error
22 }
23 if (err.response.status === 403) {
24 // handle server forbidden error
25 }
26 // .....
27 console.log(err);
28 });

axios



status

get post

140


node

1 if (process.env.NODE_ENV === 'development') {


2 axios.defaults.baseURL = 'http://dev.xxx.com'
3 } else if (process.env.NODE_ENV === 'production') {
4 axios.defaults.baseURL = 'http://prod.xxx.com'
5 }

vue.config.js devServer

1 devServer: {
2 proxy: {
3 '/proxyApi': {
4 target: 'http://dev.xxx.com',
5 changeOrigin: true,
6 pathRewrite: {
7 '/proxyApi': ''
8 }
9 }
10 }
11 }



141
1 const service = axios.create({
2 ...
3 timeout: 30000, // 30s
4 headers: {
5 get: {
6 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-
8'
7 //
8 },
9 post: {
10 'Content-Type': 'application/json;charset=utf-8'
11 //
12 }
13 },
14 })



142
1 // get
2 export function httpGet({
3 url,
4 params = {}
5 }) {
6 return new Promise((resolve, reject) => {
7 axios.get(url, {
8 params
9 }).then((res) => {
10 resolve(res.data)
11 }).catch(err => {
12 reject(err)
13 })
14 })
15 }
16
17 // post
18 // post
19 export function httpPost({
20 url,
21 data = {},
22 params = {}
23 }) {
24 return new Promise((resolve, reject) => {
25 axios({
26 url,
27 method: 'post',
28 transformRequest: [function (data) {
29 let ret = ''
30 for (let it in data) {
31 ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it
]) + '&'
32 }
33 return ret
34 }],
35 //
36 data,
37 // url
38 params
39
40 }).then(res => {
41 resolve(res.data)
42 })
43 })
44 }

143
api.js

1 import { httpGet, httpPost } from './http'


2 export const getorglist = (params = {}) => httpGet({ url: 'apps/api/org/lis
t', params })

1 // .vue
2 import { getorglist } from '@/assets/js/api'
3
4 getorglist({ id: 200 }).then(res => {
5 console.log(res)
6 })

api api.js



1 //
2 axios.interceptors.request.use(
3 config => {
4 // token
5 // http header token token
token localstorage
6 token && (config.headers.Authorization = token)
7 return config
8 },
9 error => {
10 return Promise.error(error)
11 })



144
1 //
2 axios.interceptors.response.use(response => {
3 // 200
4 //
5 if (response.status === 200) {
6 if (response.data.code === 511) {
7 //
8 } else if (response.data.code === 510) {
9 //
10 } else {
11 return Promise.resolve(response)
12 }
13 } else {
14 return Promise.reject(response)
15 }
16 }, error => {
17 //
18 if (error.response.status) {
19 //
20 //
21 return Promise.reject(error.response)
22 }
23 })


axios

axios



145


Vue





axios interceptor response

1 apiClient.interceptors.response.use(
2 response => {
3 return response;
4 },
5 error => {
6 if (error.response.status == 401) {
7 router.push({ name: "Login" });
8 } else {
9 message.error(" ");
10 return Promise.reject(error);
11 }
12 }
13 );

146




1 Vue.config.errorHandler = function (err, vm, info) {


2 // handle error
3 // `info` Vue
4 // 2.2.0+
5 }

errorHandler
Vue

Vue API

undefined
console.error

v-on



errorCaptured

1 (err: Error, vm: Component, info: string) => ?boolean

false

config.errorHandler

errorCaptured

147
errorCaptured
config.errorHandler

errorCaptured false
errorCaptured
config.errorHandler

cat

1 Vue.component('cat', {
2 template:`
3 <div>
4 <h1>Cat: </h1>
5 <slot></slot>
6 </div>`,
7 props:{
8 name:{
9 required:true,
10 type:String
11 }
12 },
13 errorCaptured(err,vm,info) {
14 console.log(`cat EC: ${err.toString()}\ninfo: ${info}`);
15 return false;
16 }
17
18 });

kitten dontexist()

1 Vue.component('kitten', {
2 template:'<div><h1>Kitten: {{ dontexist() }}</h1></div>',
3 props:{
4 name:{
5 required:true,
6 type:String
7 }
8 }
9 });

148
1 <div id="app" v-cloak>
2 <cat name="my cat">
3 <kitten></kitten>
4 </cat>
5 </div>

errorCaptured

1 cat EC: TypeError: dontexist is not a function


2 info: render



149
1 // Vue , Vue.config
2 import config from '../config'
3 import { warn } from './debug'
4 //
5 import { inBrowser, inWeex } from './env'
6 // Promise val.then === 'function' && val.catch === 'functio
n', val === null && val !== undefined
7 import { isPromise } from 'shared/util'
8 // deps infinite rendering
9 // https://github.com/vuejs/vuex/issues/1505
10 import { pushTarget, popTarget } from '../observer/dep'
11
12 export function handleError (err: Error, vm: any, info: string) {
13 // Deactivate deps tracking while processing error handler to avoid p
ossible infinite rendering.
14 pushTarget()
15 try {
16 // vm
17 if (vm) {
18 let cur = vm
19 // errorCap
tured
20 // errorCaptured errorCaptured
globalHandleError
21 while ((cur = cur.$parent)) {
22 const hooks = cur.$options.errorCaptured
23 // errorCaptured
24 if (hooks) {
25 //
26 for (let i = 0; i < hooks.length; i++) {
27 // errorCaptured
28 // try{}catch{}
config.errorHandler
29 // globalHandleError
30 try {
31 // errorCaptured false
32 // false capture = true
errorCaptured config.errorHandler
33 // true capture = fale
errorCaptured
34 //
35 const capture = hooks[i].call(cur, err, vm, i
nfo) === false
36 if (capture) return
37 } catch (e) {

150
38 globalHandleError(e, cur, 'errorCaptured hoo
k')
39
}
40
}
41
}
42
}
43
}
44
//
45
globalHandleError(err, vm, info)
46
} finally {
47
popTarget()
48
}
49
}
50
//
51
export function invokeWithErrorHandling (
52
handler: Function,
53
context: any,
54
args: null | any[],
55
vm: any,
56
info: string
57
) {
58
let res
59
try {
60
// handle
61
res = args ? handler.apply(context, args) : handler.call(
context)
62
// handle
63
// res._isVue an flag to avoid this being observed
_isVue ture ( Vue ) observer
64
// isPromise(res) val.then === 'function' && val.catc
h === 'function', val === null && val !== undefined
65
// !res._handled _handle Promise
false onFulfilled,onRejected
66
if (res && !res._isVue && isPromise(res) && !res._handled
) {
67
res.catch(e => handleError(e, vm, info + ` (Promise/a
sync)`))
68
// avoid catch triggering multiple times when nested
calls
69
// catch
70
res._handled = true
71
}
72
} catch (e) {
73
//
74
handleError(e, vm, info)
75
}
76
return res
77
}

151
78
79
//
80
function globalHandleError (err, vm, info) {
81
// undefined
82
//
83
if (config.errorHandler) {
84
// try{}catch{}
85
try {
86
// handle error
87
return config.errorHandler.call(null, err, vm, info)
88
} catch (e) {
89
// errorHandler throw err
90
// err log
91
// throw err Error(' ') log
92
if (e !== err) {
93
logError(e, null, 'config.errorHandler')
94
}
95
}
96
}
97
// log
98
logError(err, vm, info)
99
}
100
101
//
102
function logError (err, vm, info) {
103
if (process.env.NODE_ENV !== 'production') {
104
warn(`Error in ${info}: "${err.toString()}"`, vm)
105
}
106
/* istanbul ignore else */
107
if ((inBrowser || inWeex) && typeof console !== 'undefined') {
108
console.error(err)
109
} else {
110
throw err
111
}
112
}


handleError
errorCaptured errorCaptured error
Captured globalHandleError

globalHandleError errorHandler logError

152
invokeWithErrorHandling

logError warn




axios

1 import axios from 'axios';


2
3 axios(config) //
4 axios(url[, config]) // url
5 axios[method](url[, option]) // url
6 axios[method](url[, data[, option]]) // data url

7 axios.request(option) // request
8
9 const axiosInstance = axios.create(config)
10 // axiosInstance axios
11
12 axios.all([axiosInstance1, axiosInstance2]).then(axios.spread(response1, r
esponse2))
13 // all spread

153
1 axios.interceptors.request.use(function (config) {
2 //
3 return config;
4 }, function (error) {
5 //
6 return Promise.reject(error);
7 });

1 axios.interceptors.response.use(function (response) {
2 //
3 return response;
4 }, function (error) {
5 //
6 return Promise.reject(error);
7 });

1 //
2 const CancelToken = axios.CancelToken;
3 const source = CancelToken.source();
4
5 axios.get('xxxx', {
6 cancelToken: source.token
7 })
8 // ( )
9 source.cancel(' ');
10
11 //
12 const CancelToken = axios.CancelToken;
13 let cancel;
14
15 axios.get('xxxx', {
16 cancelToken: new CancelToken(function executor(c) {
17 cancel = c;
18 })
19 });
20 cancel(' ');

154

Axios request

1 class Axios {
2 constructor() {
3
4 }
5
6 request(config) {
7 return new Promise(resolve => {
8 const {url = '', method = 'get', data = {}} = config;
9 // ajax
10 const xhr = new XMLHttpRequest();
11 xhr.open(method, url, true);
12 xhr.onload = function() {
13 console.log(xhr.responseText)
14 resolve(xhr.responseText);
15 }
16 xhr.send(data);
17 })
18 }
19 }

axios

1 // axios request
2 function CreateAxiosFn() {
3 let axios = new Axios();
4 let req = axios.request.bind(axios);
5 return req;
6 }
7
8 // axios
9 let axios = CreateAxiosFn();

axios({ })

axios.method()

155
1 // get,post... Axios
2 const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'p
ost'];
3 methodsArr.forEach(met => {
4 Axios.prototype[met] = function() {
5 console.log(' '+met+' ');
6 //
7 if (['get', 'delete', 'head', 'options'].includes(met)) { // 2
(url[, config])
8 return this.request({
9 method: met,
10 url: arguments[0],
11 ...arguments[1] || {}
12 })
13 } else { // 3 (url[,data[,config]])
14 return this.request({
15 method: met,
16 url: arguments[0],
17 data: arguments[1] || {},
18 ...arguments[2] || {}
19 })
20 }
21
22 }
23 })

Axios.prototype request

b a this

156
1 const utils = {
2 extend(a,b, context) {
3 for(let key in b) {
4 if (b.hasOwnProperty(key)) {
5 if (typeof b[key] === 'function') {
6 a[key] = b[key].bind(context);
7 } else {
8 a[key] = b[key]
9 }
10 }
11
12 }
13 }
14 }

1 function CreateAxiosFn() {
2 let axios = new Axios();
3
4 let req = axios.request.bind(axios);
5 //
6 utils.extend(req, Axios.prototype, axios)
7
8 return req;
9 }

157
1 class InterceptorsManage {
2 constructor() {
3 this.handlers = [];
4 }
5
6 use(fullfield, rejected) {
7 this.handlers.push({
8 fullfield,
9 rejected
10 })
11 }
12 }

axios.interceptors.response.use axios.interceptors.request.use

1 class Axios {
2 constructor() {
3 //
4 this.interceptors = {
5 request: new InterceptorsManage,
6 response: new InterceptorsManage
7 }
8 }
9
10 request(config) {
11 ...
12 }
13 }

axios.interceptors.response.use axios.interceptors.request.use
axios interceptors response request
use

Axios request

158
1 function CreateAxiosFn() {
2 let axios = new Axios();
3
4 let req = axios.request.bind(axios);
5 // axios request get,post...
6 utils.extend(req, Axios.prototype, axios)
7 //
8 utils.extend(req, axios)
9 return req;
10 }

request interceptors request ha


ndlers

ajax

1 request(config) {
2 this.sendAjax(config)
3 }
4 sendAjax(config){
5 return new Promise(resolve => {
6 const {url = '', method = 'get', data = {}} = config;
7 // ajax
8 console.log(config);
9 const xhr = new XMLHttpRequest();
10 xhr.open(method, url, true);
11 xhr.onload = function() {
12 console.log(xhr.responseText)
13 resolve(xhr.responseText);
14 };
15 xhr.send(data);
16 })
17 }

handlers

159
1 request(config) {
2 //
3 let chain = [this.sendAjax.bind(this), undefined] //

4
5 //
6 this.interceptors.request.handlers.forEach(interceptor => {
7 chain.unshift(interceptor.fullfield, interceptor.rejected)
8 })
9
10 //
11 this.interceptors.response.handlers.forEach(interceptor => {
12 chain.push(interceptor.fullfield, interceptor.rejected)
13 })
14
15 // promise
16 let promise = Promise.resolve(config);
17 while(chain.length > 0) {
18 promise = promise.then(chain.shift(), chain.shift())
19 }
20 return promise;
21 }

chains ['fulfilled1','reject1','fulfilled2','reject2','this.sendAja
x','undefined','fulfilled2','reject2','fulfilled1','reject1']



160
axios axios.js

161
1 function createInstance(defaultConfig) {
2 var context = new Axios(defaultConfig);
3
4 // instance request context instance(opt
ion)
5 // Axios.prototype.request instance
(url, option)
6 var instance = bind(Axios.prototype.request, context);
7
8 // Axios.prototype instance
9 // context Axios this context
10 utils.extend(instance, Axios.prototype, context);
11
12 // Copy context to instance
13 // context instance
14 // extend forEach for in

15 // instance defaults interceptors


16 utils.extend(instance, context);
17 return instance;
18 }
19
20 // Create the default instance to be exported axios

21 var axios = createInstance(defaults);


22
23 // Factory for creating new instances axios.create crea
teInstance
24 axios.create = function create(instanceConfig) {
25 return createInstance(mergeConfig(axios.defaults, instanceConfig));
26 };
27
28 // Expose all/spread
29 axios.all = function all(promises) {
30 return Promise.all(promises);
31 };
32
33 axios.spread = function spread(callback) {
34 return function wrap(arr) {
35 return callback.apply(null, arr);
36 };
37 };
38 module.exports = axios;

162
Axios.prototype.request request
request

163
1 Axios.prototype.request = function request(config) {
2 // Allow for axios('example/url'[, config]) a la fetch API
3 // config URL c
onfig
4 if (typeof config === 'string') {
5 config = arguments[1] || {};
6 // url config mergeConfig
7 config.url = arguments[0];
8 } else {
9 // config config
10 config = config || {};
11 }
12 //
13 config = mergeConfig(this.defaults, config);
14 //
15 config.method = config.method ? config.method.toLowerCase() : 'get';
16 /*
17 something...
18 */
19 };
20
21 // Axios 'delete', 'get', 'head', 'options'
request
22 utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethod
NoData(method) {
23 Axios.prototype[method] = function(url, config) {
24 return this.request(utils.merge(config || {}, {
25 method: method,
26 url: url
27 }));
28 };
29 });
30
31 // Axios 'post', 'put', 'patch'
request
32 utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(met
hod) {
33 Axios.prototype[method] = function(url, data, config) {
34 return this.request(utils.merge(config || {}, {
35 method: method,
36 url: url,
37 data: data
38 }));
39 };
40 });

164
request config config axios

axios config

defaults.js

config.method get

createInstance axios config

request config

1 // axios.js
2 // axios
3 var axios = createInstance(defaults);
4
5 // axios.create createInstance
6 axios.create = function create(instanceConfig) {
7 return createInstance(mergeConfig(axios.defaults, instanceConfig));
8 };
9
10 // Axios.js
11 //
12 config = mergeConfig(this.defaults, config);
13 //
14 config.method = config.method ? config.method.toLowerCase() : 'get';

default method:get Axios thi


s.default request

request

165
1 Axios.prototype.request = function request(config) {
2 /*
3 mergeConfig ...
4 */
5 // Hook up interceptors middleware . dispatchRequest

6 var chain = [dispatchRequest, undefined];


7
8 // push interceptor.fulfilled interceptor.rejected
undefined
9 this.interceptors.request.forEach(function unshiftRequestInterceptors(in
terceptor) {
10 // forEach forEach
11 chain.unshift(interceptor.fulfilled, interceptor.rejected);
12 });
13
14 this.interceptors.response.forEach(function pushResponseInterceptors(int
erceptor) {
15 // forEach forEach
16 chain.push(interceptor.fulfilled, interceptor.rejected);
17 });
18
19 // promise resolved config
20 var promise = Promise.resolve(config);
21
22 //
23 while (chain.length) {
24 promise = promise.then(chain.shift(), chain.shift()); //

25 }
26 // promise
27 return promise;
28 };

interceptors axios

166
1 function Axios(instanceConfig) {
2 this.defaults = instanceConfig;
3 this.interceptors = {
4 request: new InterceptorManager(), //
5 response: new InterceptorManager() //
6 };
7 }

InterceptorManager

1 //
2 function InterceptorManager() {
3 this.handlers = [];
4 }
5
6 // use push
7 InterceptorManager.prototype.use = function use(fulfilled, rejected) {
8 this.handlers.push({
9 fulfilled: fulfilled,
10 rejected: rejected
11 });
12 return this.handlers.length - 1;
13 };
14
15 // use ID null
16 // splice slice id

17 InterceptorManager.prototype.eject = function eject(id) {


18 if (this.handlers[id]) {
19 this.handlers[id] = null;
20 }
21 };
22
23 // Axios request forEach
24 InterceptorManager.prototype.forEach = function forEach(fn) {
25 utils.forEach(this.handlers, function forEachHandler(h) {
26 if (h !== null) {
27 fn(h);
28 }
29 });
30 }

167
unshift push
dispatchRequest promise

168
1 var utils = require('./../utils');
2 var transformData = require('./transformData');
3 var isCancel = require('../cancel/isCancel');
4 var defaults = require('../defaults');
5 var isAbsoluteURL = require('./../helpers/isAbsoluteURL');
6 var combineURLs = require('./../helpers/combineURLs');
7
8 //
9 function throwIfCancellationRequested(config) {
10 if (config.cancelToken) {
11 config.cancelToken.throwIfRequested();
12 }
13 }
14
15 module.exports = function dispatchRequest(config) {
16 throwIfCancellationRequested(config);
17
18 // baseUrl, config.url baseUrl config.url
19 if (config.baseURL && !isAbsoluteURL(config.url)) {
20 // baseURL url
21 config.url = combineURLs(config.baseURL, config.url);
22 }
23
24 config.headers = config.headers || {};
25
26 // /lib/defaults.js transformRequest config.headers config.d
ata
27 // headers Accept Content-Type
28 // Object JSON application/json;cha
rset=utf-8 Content-Type
29 //
30 config.data = transformData(
31 config.data,
32 config.headers,
33 config.transformRequest
34 );
35
36 // headers config.headers
37 config.headers = utils.merge(
38 config.headers.common || {},
39 config.headers[config.method] || {},
40 config.headers || {}
41 );
42
43 // headers method

169
44 utils.forEach(
45
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
46
function cleanHeaderConfig(method) {
47
delete config.headers[method];
48
}
49
);
50
51
// config adapter config adapter
52
var adapter = config.adapter || defaults.adapter;
53
54
// adapter adapter Node
55
return adapter(config).then(
56
//
57
function onAdapterResolution(response) {
58
//
59
throwIfCancellationRequested(config);
60
61
// /lib/defaults.js transformResponse

62
// JSON.parse
63
response.data = transformData(
64
response.data,
65
response.headers,
66
config.transformResponse
67
);
68
69
return response;
70
},
71
//
72
function onAdapterRejection(reason) {
73
if (!isCancel(reason)) {
74
throwIfCancellationRequested(config);
75
76
if (reason && reason.response) {
77
reason.response.data = transformData(
78
reason.response.data,
79
reason.response.headers,
80
config.transformResponse
81
);
82
}
83
}
84
return Promise.reject(reason);
85
}
86
);
87
};

axios CancelToken.js

170
1 function CancelToken(executor) {
2 if (typeof executor !== 'function') {
3 throw new TypeError('executor must be a function.');
4 }
5 // CancelToken pending promise resolve
resolvePromise
6 var resolvePromise;
7 this.promise = new Promise(function promiseExecutor(resolve) {
8 resolvePromise = resolve;
9 });
10
11 var token = this;
12 // executor cancel
13 // resolvePromise promise resolve promise
resolve
14 // xhr CancelToken.promise.then , xhr
15 executor(function cancel(message) {
16 //
17 if (token.reason) {
18 return;
19 }
20 token.reason = new Cancel(message);
21 resolvePromise(token.reason);
22 });
23 }
24
25 CancelToken.source = function source() {
26 // source CancelToken new CancelToken

27 var cancel;
28 var token = new CancelToken(function executor(c) {
29 cancel = c;
30 });
31 // CancelToken
32 return {
33 token: token,
34 cancel: cancel
35 };
36 };

xhr.js

171
1 if (config.cancelToken) {
2 config.cancelToken.promise.then(function onCanceled(cancel) {
3 if (!request) {
4 return;
5 }
6 //
7 request.abort();
8 reject(cancel);
9 });
10 }

CancelToken executor resolve prom


ise



172


173


4xx

174




jwt 401

token token axios


token

1 axios.interceptors.request.use(config => {
2 config.headers['token'] = cookie.get('token')
3 return config
4 })
5 axios.interceptors.response.use(res=>{},{response}=>{
6 if (response.data.code === 40099 || response.data.code === 40098) { //t
oken
7 router.push('/login')
8 }
9 })



175
1 const routerMap = [
2 {
3 path: '/permission',
4 component: Layout,
5 redirect: '/permission/index',
6 alwaysShow: true, // will always show the root menu
7 meta: {
8 title: 'permission',
9 icon: 'lock',
10 roles: ['admin', 'editor'] // you can set roles in root nav
11 },
12 children: [{
13 path: 'page',
14 component: () => import('@/views/permission/page'),
15 name: 'pagePermission',
16 meta: {
17 title: 'pagePermission',
18 roles: ['admin'] // or you can only set roles in sub nav
19 }
20 }, {
21 path: 'directive',
22 component: () => import('@/views/permission/directive'),
23 name: 'directivePermission',
24 meta: {
25 title: 'directivePermission'
26 // if do not set roles, means: this page does not require permissi
on
27 }
28 }]
29 }]

176
addRoutes

177
1 import router from './router'
2 import store from './store'
3 import { Message } from 'element-ui'
4 import NProgress from 'nprogress' // progress bar
5 import 'nprogress/nprogress.css'// progress bar style
6 import { getToken } from '@/utils/auth' // getToken from cookie
7
8 NProgress.configure({ showSpinner: false })// NProgress Configuration
9
10 // permission judge function
11 function hasPermission(roles, permissionRoles) {
12 if (roles.indexOf('admin') >= 0) return true // admin permission passed
directly
13 if (!permissionRoles) return true
14 return roles.some(role => permissionRoles.indexOf(role) >= 0)
15 }
16
17 const whiteList = ['/login', '/authredirect']// no redirect whitelist
18
19 router.beforeEach((to, from, next) => {
20 NProgress.start() // start progress bar
21 if (getToken()) { // determine if there has token
22 /* has token*/
23 if (to.path === '/login') {
24 next({ path: '/' })
25 NProgress.done() // if current page is dashboard will not trigger af
terEach hook, so manually handle it
26 } else {
27 if (store.getters.roles.length === 0) { // user_
info
28 store.dispatch('GetUserInfo').then(res => { // user_info
29 const roles = res.data.roles // note: roles must be a array! suc
h as: ['editor','develop']
30 store.dispatch('GenerateRoutes', { roles }).then(() => { //
roles
31 router.addRoutes(store.getters.addRouters) //

32 next({ ...to, replace: true }) // hack addRoutes ,


set the replace: true so the navigation will not leave a history record
33 })
34 }).catch((err) => {
35 store.dispatch('FedLogOut').then(() => {
36 Message.error(err || 'Verification failed, please login again'
)
37 next({ path: '/' })

178
38 })
39
})
40
} else {
41
// next() ↓
42
if (hasPermission(store.getters.roles, to.meta.roles)) {
43
next()//
44
} else {
45
next({ path: '/401', replace: true, query: { noGoBack: true }})
46
}
47
// ↑
48
}
49
}
50
} else {
51
/* has no token*/
52
if (whiteList.indexOf(to.path) !== -1) { //
53
next()
54
} else {
55
next('/login') //
56
NProgress.done() // if current page is login will not trigger afterE
ach hook, so manually handle it
57
}
58
}
59
})
60
61
router.afterEach(() => {
62
NProgress.done() // finish progress bar
63
})





179
1 {
2 name: "login",
3 path: "/login",
4 component: () => import("@/pages/Login.vue")
5 }

name
name

180
1 function hasPermission(router, accessMenu) {
2 if (whiteList.indexOf(router.path) !== -1) {
3 return true;
4 }
5 let menu = Util.getMenuByName(router.name, accessMenu);
6 if (menu.name) {
7 return true;
8 }
9 return false;
10
11 }
12
13 Router.beforeEach(async (to, from, next) => {
14 if (getToken()) {
15 let userInfo = store.state.user.userInfo;
16 if (!userInfo.name) {
17 try {
18 await store.dispatch("GetUserInfo")
19 await store.dispatch('updateAccessMenu')
20 if (to.path === '/login') {
21 next({ name: 'home_index' })
22 } else {
23 //Util.toDefaultPage([...routers], to.name, router, next);
24 next({ ...to, replace: true })// ,
25 }
26 }
27 catch (e) {
28 if (whiteList.indexOf(to.path) !== -1) { //
29 next()
30 } else {
31 next('/login')
32 }
33 }
34 } else {
35 if (to.path === '/login') {
36 next({ name: 'home_index' })
37 } else {
38 if (hasPermission(to, store.getters.accessMenu)) {
39 Util.toDefaultPage(store.getters.accessMenu,to, routes, next);
40 } else {
41 next({ path: '/403',replace:true })
42 }
43 }
44 }
45 } else {

181
46 if (whiteList.indexOf(to.path) !== -1) { //
47
next()
48
} else {
49
next('/login')
50
}
51
}
52
let menu = Util.getMenuByName(to.name, store.getters.accessMenu);
53
Util.title(menu.title);
54
});
55
56
Router.afterEach((to) => {
57
window.scrollTo(0, 0);
58
});

name name

name

addRoutes



1 const Home = () => import("../pages/Home.vue");


2 const UserInfo = () => import("../pages/UserInfo.vue");
3 export default {
4 home: Home,
5 userInfo: UserInfo
6 };

182
1 [
2 {
3 name: "home",
4 path: "/",
5 component: "home"
6 },
7 {
8 name: "home",
9 path: "/userinfo",
10 component: "userInfo"
11 }
12 ]

addRoutes component





v-if

role
meta.btnPermissions



183
1 {
2 path: '/permission',
3 component: Layout,
4 name: ' ',
5 meta: {
6 btnPermissions: ['admin', 'supper', 'normal']
7 },
8 //
9 children: [{
10 path: 'supper',
11 component: _import('system/supper'),
12 name: ' ',
13 meta: {
14 btnPermissions: ['admin', 'supper']
15 } //
16 },
17 {
18 path: 'normal',
19 component: _import('system/normal'),
20 name: ' ',
21 meta: {
22 btnPermissions: ['admin']
23 } //
24 }]
25 }

184
1 import Vue from 'vue'
2 /** **/
3 const has = Vue.directive('has', {
4 bind: function (el, binding, vnode) {
5 //
6 let btnPermissionsArr = [];
7 if(binding.value){
8 //
9 btnPermissionsArr = Array.of(binding.value);
10 }else{
11 // btnPermissionsArr

12 btnPermissionsArr = vnode.context.$route.meta.btnPermissions;
13 }
14 if (!Vue.prototype.$_has(btnPermissionsArr)) {
15 el.parentNode.removeChild(el);
16 }
17 }
18 });
19 //
20 Vue.prototype.$_has = function (value) {
21 let isExist = false;
22 //
23 let btnPermissionsStr = sessionStorage.getItem("btnPermissions");
24 if (btnPermissionsStr == undefined || btnPermissionsStr == null) {
25 return false;
26 }
27 if (value.indexOf(btnPermissionsStr) > -1) {
28 isExist = true;
29 }
30 return isExist;
31 };
32 export {has}

v-has

1 <el-button @click='editClick' type="primary" v-has> </el-button>



185



keep-alive vue DOM

keep-alive

keep-alive props

include

exclude

max

keep-alive

1 <keep-alive>
2 <component :is="view"></component>
3 </keep-alive>

includes exclude

186
1 <keep-alive include="a,b">
2 <component :is="view"></component>
3 </keep-alive>
4
5 <!-- ( `v-bind`) -->
6 <keep-alive :include="/a|b/">
7 <component :is="view"></component>
8 </keep-alive>
9
10 <!-- ( `v-bind`) -->
11 <keep-alive :include="['a', 'b']">
12 <component :is="view"></component>
13 </keep-alive>

name name
components

activated deactivated

beforeRouteEnter beforeCreate created mounted a


ctivated beforeRouteLeave deactivated

beforeRouteEnter activated beforeRouteLeave


deactivated


keepalive

keep-alive

( ) ( )
( ) keep-alive

keepAlive

187
1 {
2 path: 'list',
3 name: 'itemList', //
4 component (resolve) {
5 require(['@/pages/item/list'], resolve)
6 },
7 meta: {
8 keepAlive: true,
9 title: ' '
10 }
11 }

<keep-alive>

1 <div id="app" class='wrapper'>


2 <keep-alive>
3 <!-- -->
4 <router-view v-if="$route.meta.keepAlive"></router-view>
5 </keep-alive>
6 <!-- -->
7 <router-view v-if="!$route.meta.keepAlive"></router-view>
8 </div>


keep-alive vue

188
1 export default {
2 name: 'keep-alive',
3 abstract: true,
4
5 props: {
6 include: [String, RegExp, Array],
7 exclude: [String, RegExp, Array],
8 max: [String, Number]
9 },
10
11 created () {
12 this.cache = Object.create(null)
13 this.keys = []
14 },
15
16 destroyed () {
17 for (const key in this.cache) {
18 pruneCacheEntry(this.cache, key, this.keys)
19 }
20 },
21
22 mounted () {
23 this.$watch('include', val => {
24 pruneCache(this, name => matches(val, name))
25 })
26 this.$watch('exclude', val => {
27 pruneCache(this, name => !matches(val, name))
28 })
29 },
30
31 render() {
32 /* */
33 const slot = this.$slots.default
34 const vnode = getFirstComponentChild(slot)
35 /* componentOptions */
36 const componentOptions = vnode && vnode.componentOptions
37
38 if (componentOptions) {
39 /* name name tag
*/
40 const name = getComponentName(componentOptions)
41
42 const { include, exclude } = this
43 /* name inlcude exlude vnode */
44 if (

189
45 (include && (!name || !matches(include, name))) ||
46
// excluded
47
(exclude && name && matches(exclude, name))
48
) {
49
return vnode
50
}
51
52
const { cache, keys } = this
53
/* key */
54
const key = vnode.key == null
55
// same constructor may get registered as different local componen
ts
56
// so cid alone is not enough (#3269)
57
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${compone
ntOptions.tag}` : '')
58
: vnode.key
59
/* key this.cache
*/
60
if (cache[key]) {
61
vnode.componentInstance = cache[key].componentInstance
62
// make current key freshest
63
remove(keys, key)
64
keys.push(key)
65
}
66
/* */
67
else {
68
cache[key] = vnode
69
keys.push(key)
70
// prune oldest entry
71
/* max this.max */
72
if (this.max && keys.length > parseInt(this.max)) {
73
pruneCacheEntry(cache, keys[0], keys, this._vnode)
74
}
75
}
76
77
vnode.data.keepAlive = true
78
}
79
return vnode || (slot && slot[0])
80
}
81
}

template render render

this.cache

190
1 this.cache = {
2 'key1':' 1',
3 'key2':' 2',
4 // ...
5 }

pruneCacheEntry

1 function pruneCacheEntry (
2 cache: VNodeCache,
3 key: string,
4 keys: Array<string>,
5 current?: VNode
6 ) {
7 const cached = cache[key]
8 /* */
9 if (cached && (!current || cached.tag !== current.tag)) {
10 cached.componentInstance.$destroy()
11 }
12 cache[key] = null
13 remove(keys, key)
14 }

mounted include exclude

1 mounted () {
2 this.$watch('include', val => {
3 pruneCache(this, name => matches(val, name))
4 })
5 this.$watch('exclude', val => {
6 pruneCache(this, name => !matches(val, name))
7 })
8 }

include exclude
pruneCache

191
1 function pruneCache (keepAliveInstance, filter) {
2 const { cache, keys, _vnode } = keepAliveInstance
3 for (const key in cache) {
4 const cachedNode = cache[key]
5 if (cachedNode) {
6 const name = getComponentName(cachedNode.componentOptions)
7 if (name && !filter(name)) {
8 pruneCacheEntry(cache, key, keys, _vnode)
9 }
10 }
11 }
12 }

this.cache name
pruneCacheEntry
this.cache

keep-alive render

key

1 const key = vnode.key == null?


2 componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.t
ag}` : '')
3 : vnode.key

key this.cache

1 /* vnode */
2 if (cache[key]) {
3 vnode.componentInstance = cache[key].componentInstance
4 /* key */
5 remove(keys, key)
6 keys.push(key)
7 }

vnode key
this.keys

192
this.cache key

1 /* */
2 else {
3 cache[key] = vnode
4 keys.push(key)
5 /* max this.max */
6 if (this.max && keys.length > parseInt(this.max)) {
7 pruneCacheEntry(cache, keys[0], keys, this._vnode)
8 }
9 }

key vnode
this.cache key this.keys

this.keys this.max





beforeRouteEnter

1 beforeRouteEnter(to, from, next){


2 next(vm=>{
3 console.log(vm)
4 //
5 vm.getData() //
6 })
7 },



193
keep-alive actived

1 activated(){
2 this.getData() //
3 },

avtived




SPA

HTML JavaScript CSS

194
react vue angular ember SPA



MPA
html css js



195






 hash

 pushsate

196


 hash

url hash

197
1 // Router
2 class Router {
3 constructor () {
4 this.routes = {}; // path callback
5 this.currentUrl = '';
6
7 // change
8 window.addEventListener('load', this.refresh, false);
9 window.addEventListener('hashchange', this.refresh, false);
10 }
11
12 route(path, callback){
13 this.routes[path] = callback;
14 }
15
16 push(path) {
17 this.routes[path] && this.routes[path]()
18 }
19 }
20
21 // router
22 window.miniRouter = new Router();
23 miniRouter.route('/', () => console.log('page1'))
24 miniRouter.route('/page2', () => console.log('page2'))
25
26 miniRouter.push('/') // page1
27 miniRouter.push('/page2') // page2



history HTML5 history api api router

history.pushState

history.replaceState

history.popState history

198
1 // Router
2 class Router {
3 constructor () {
4 this.routes = {};
5 this.listerPopState()
6 }
7
8 init(path) {
9 history.replaceState({path: path}, null, path);
10 this.routes[path] && this.routes[path]();
11 }
12
13 route(path, callback){
14 this.routes[path] = callback;
15 }
16
17 push(path) {
18 history.pushState({path: path}, null, path);
19 this.routes[path] && this.routes[path]();
20 }
21
22 listerPopState () {
23 window.addEventListener('popstate' , e => {
24 const path = e.state && e.state.path;
25 this.routers[path] && this.routers[path]()
26 })
27 }
28 }
29
30 // Router
31
32 window.miniRouter = new Router();
33 miniRouter.route('/', ()=> console.log('page1'))
34 miniRouter.route('/page2', ()=> console.log('page2'))
35
36 //
37 miniRouter.push('/page2') // page2



Vue SPA SEO



199
nuxt.js



URL Rewrite

 Phantomjs

Nginx nod
e server PhantomJS HTML

200


201




performance.timing

202
DOMContentLoad performance

1 //
2 document.addEventListener('DOMContentLoaded', (event) => {
3 console.log('first contentful painting');
4 });
5 //
6 performance.getEntriesByName("first-contentful-paint")[0].startTime
7
8 // performance.getEntriesByName("first-contentful-paint")[0]
9 // PerformancePaintTiming
10 {
11 name: "first-contentful-paint",
12 entryType: "paint",
13 startTime: 507.80000002123415,
14 duration: 0,
15 };



203




vue-router

204
1 routes:[
2 path: 'Blogs',
3 name: 'ShowBlogs',
4 component: () => import('./components/ShowBlogs.vue')
5 ]



HTTP Cache-Control Last-Modified Etag

Service Worker

localStorage



UI element-UI antd UI

1 import ElementUI from 'element-ui'


2 Vue.use(ElementUI)

1 import { Button, Input, Pagination, Table, TableColumn, MessageBox } from


'element-ui';
2 Vue.use(Button)
3 Vue.use(Input)
4 Vue.use(Pagination)



A.js A.js

205
webpack config CommonsChunkPlugin

1 minChunks: 3

minChunks



icon
http



gzip compression-webpack-plugin

1 cnmp i compression-webpack-plugin -D

vue.congig.js webpack

1 const CompressionPlugin = require('compression-webpack-plugin')


2
3 configureWebpack: (config) => {
4 if (process.env.NODE_ENV === 'production') {
5 // ...
6 config.mode = 'production'
7 return {
8 plugins: [new CompressionPlugin({
9 test: /\.js$|\.html$|\.css/, //
10 threshold: 10240, // 10k
11 deleteOriginalAssets: false //
12 })]
13 }
14 }

206
gzip gzip
express compression

1 const compression = require('compression')


2 app.use(compression()) //



vue Nuxt.js





207


web

vue

1 // scp user host ip, xx web


2 scp dist.zip user@host:/xx/xx/xx

web nginx

1 server {
2 listen 80;
3 server_name www.xxx.com;
4
5 location / {
6 index /data/dist/index.html;
7 }
8 }

nginx

208
1 //
2 nginx -t
3
4 //
5 nginx -s reload



vue

history



Vue

SPA
index.html

nginx

209
1 server {
2 listen 80;
3 server_name www.xxx.com;
4
5 location / {
6 index /data/dist/index.html;
7 }
8 }

nginx www.xxx.com dist


index.html www.xxx.com/login

website.com/login nginx location



router hash website.com/#/login hash


#/login

hash URL HTTP


hash

hash hash website.com/#/login


website.com location



web

index.html

nginx .conf try_files $uri $uri/ /index.html;

210
1 server {
2 listen 80;
3 server_name www.xxx.com;
4
5 location / {
6 index /data/dist/index.html;
7 try_files $uri $uri/ /index.html;
8 }
9 }

1 nginx -s reload

index.html

Vue

1 const router = new VueRouter({


2 mode: 'history',
3 routes: [
4 { path: '*', component: NotFoundComponent }
5 ]
6 })



211

Server-Side Rendering SSR

HTML

Web



dom



JS

#app

212


SSR dom spa


spa

Vue SSR

Vue SSR SPA

Vue SSR

Vue SSR SPA



HTML ssr HTM


L seo

js

SSR

node serve nginx

cpu spa

213
SSR

 SEO




webpack

bundle bundle

vue

1 src
2 ├── router
3 ├────── index.js #
4 ├── store
5 ├────── index.js #
6 ├── main.js # vue
7 ├── entry-client.js # “ ”
8 └── entry-server.js #

214
1 import Vue from "vue";
2 import Router from "vue-router";
3
4 Vue.use(Router);
5 //
6
7 export function createRouter() {
8 return new Router({
9 mode: 'history',
10 routes: [
11 //
12 { path: "/", component: { render: h => h('div', 'index page')
} },
13 { path: "/detail", component: { render: h => h('div', 'detail
page') } }
14 ]
15 });
16 }

vue vue

1 import Vue from "vue";


2 import App from "./App.vue";
3 import { createRouter } from "./router";
4 // Vue
5 // vue
6 export function createApp(context) {
7 const router = createRouter();
8 const app = new Vue({
9 router,
10 context,
11 render: h => h(App)
12 });
13 return { app, router };
14 }

src/entry-server.js

Vue url

215
1 import { createApp } from "./main";
2 // vue
3 export default context => {
4 // Promise
5 return new Promise((resolve, reject) => {
6 const { app, router } = createApp(context);
7 //
8 router.push(context.url);
9 //
10 router.onReady(() => {
11 resolve(app);
12 }, reject);
13 });
14 };

entry-client.js

vue

1 import { createApp } from "./main";


2 // vue router
3 const { app, router } = createApp();
4 //
5 router.onReady(() => {
6 app.$mount("#app");
7 });

webpack

1 npm install webpack-node-externals lodash.merge -D

vue.config.js

216
1 //
2 const VueSSRServerPlugin = require("vue-server-renderer/server-plugin");
3 const VueSSRClientPlugin = require("vue-server-renderer/client-plugin");
4 const nodeExternals = require("webpack-node-externals");
5 const merge = require("lodash.merge");
6 //
7 const TARGET_NODE = process.env.WEBPACK_TARGET === "node";
8 const target = TARGET_NODE ? "server" : "client";
9 module.exports = {
10 css: {
11 extract: false
12 },
13 outputDir: './dist/'+target,
14 configureWebpack: () => ({
15 // entry server / client
16 entry: `./src/entry-${target}.js`,
17 // bundle renderer source map
18 devtool: 'source-map',
19 // target node webpack Node
20 // Vue `vue-loader`
21 target: TARGET_NODE ? "node" : "web",
22 // node
23 node: TARGET_NODE ? undefined : false,
24 output: {
25 // Node
26 libraryTarget: TARGET_NODE ? "commonjs2" : undefined
27 },
28 // https://webpack.js.org/configuration/externals/#function
29 // https://github.com/liady/webpack-node-externals
30 //
31 externals: TARGET_NODE
32 ? nodeExternals({
33 // webpack
34 // *.vue
35 // `global` polyfill
36 whitelist: [/\.css$/]
37 })
38 : undefined,
39 optimization: {
40 splitChunks: undefined
41 },
42 // JSON
43 // `vue-ssr-server-bundle.json`
44 // `vue-ssr-client-manifest.json`
45 plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new

217
46 VueSSRClientPlugin()]
47
}),
48
chainWebpack: config => {
49
// cli4
50
if (TARGET_NODE) {
51
config.optimization.delete('splitChunks')
52
}
53
54
config.module
55
.rule("vue")
56
.use("vue-loader")
57
.tap(options => {
58
merge(options, {
59
optimizeSSR: false
60
});
61
});
62
}
63
};

1 npm i cross-env -D

package.json

1 "scripts": {
2 "build:client": "vue-cli-service build",
3 "build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build",
4 "build": "npm run build:server && npm run build:client"
5 }

/public/index.html

218
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width,initial-scale=1.
0">
7 <title>Document</title>
8 </head>
9 <body>
10 <!--vue-ssr-outlet-->
11 </body>
12 </html>

vuex

1 npm install -S vuex

vuex

1 import Vue from 'vue'


2 import Vuex from 'vuex'
3 Vue.use(Vuex)
4 export function createStore () {
5 return new Vuex.Store({
6 state: {
7 count:108
8 },
9 mutations: {
10 add(state){
11 state.count += 1;
12 }
13 }
14 })
15 }

main.js store

219
1 import { createStore } from './store'
2 export function createApp (context) {
3 //
4 const store = createStore()
5 const app = new Vue({
6 store, //
7 render: h => h(App)
8 })
9 return { app, router, store }
10 }

store

1 export function createStore() {


2 return new Vuex.Store({
3 mutations: {
4 //
5 init(state, count) {
6 state.count = count;
7 },
8 },
9 actions: {
10 // count action
11 getCount({ commit }) {
12 return new Promise(resolve => {
13 setTimeout(() => {
14 commit("init", Math.random() * 100);
15 resolve();
16 }, 1000);
17 });
18 },
19 },
20 });
21 }

220
1 export default {
2 asyncData({ store, route }) { // asyncData
3 // action Promise
4 return store.dispatch("getCount");
5 }
6 };

entry-server.js

221
1 import { createApp } from "./app";
2 export default context => {
3 return new Promise((resolve, reject) => {
4 // store router
5 const { app, router, store } = createApp(context);
6 router.push(context.url);
7 router.onReady(() => {
8 //
9 const matchedComponents = router.getMatchedComponents();
10
11 //
12 if (!matchedComponents.length) {
13 return reject({ code: 404 });
14 }
15
16 // `asyncData()`
17 Promise.all(
18 matchedComponents.map(Component => {
19 if (Component.asyncData) {
20 return Component.asyncData({
21 store,
22 route: router.currentRoute,
23 });
24 }
25 }),
26 )
27 .then(() => {
28 // resolve
29 // store
30 // `template` renderer
31 // `window.__INITIAL_STATE__` HTML
32 context.state = store.state;
33
34 resolve(app);
35 })
36 .catch(reject);
37 }, reject);
38 });
39 };

store entry-client.js

222
1 // store
2 const { app, router, store } = createApp();
3 // template context.state window.__INITIAL_STATE__
HTML
4 // store
5 if (window.__INITIAL_STATE__) {
6 store.replaceState(window.__INITIAL_STATE__);
7 }

main.js

1 Vue.mixin({
2 beforeMount() {
3 const { asyncData } = this.$options;
4 if (asyncData) {
5 // promise
6 //
7 // `this.dataPromise.then(...)`
8 this.dataPromise = asyncData({
9 store: this.$store,
10 route: this.$route,
11 });
12 }
13 },
14 });

223
1 //
2 const resolve = dir => require('path').resolve(__dirname, dir)
3 // 1 dist/client index
4 app.use(express.static(resolve('../dist/client'), {index: false}))
5 // 2 createBundleRenderer
6 const { createBundleRenderer } = require("vue-server-renderer");
7 // 3
8 const bundle = resolve("../dist/server/vue-ssr-server-bundle.json");
9 // 4
10 const renderer = createBundleRenderer(bundle, {
11 runInNewContext: false, // https://ssr.vuejs.org/zh/api/#runinnewconte
xt
12 template: require('fs').readFileSync(resolve("../public/index.html"),
"utf8"), //
13 clientManifest: require(resolve("../dist/client/vue-ssr-clientmanifes
t.json")) //
14 });
15 app.get('*', async (req,res)=>{
16 // url title
17 const context = {
18 title:'ssr test',
19 url:req.url
20 }
21 const html = await renderer.renderToString(context);
22 res.send(html)
23 })


ssr vue

ssr

asyncData

mixin beforeMount

 

224
 
vue3

 

225
Vue3

 

vue3 vue2

Dom

undate

SSR

226
 

webpack tree-shaking

tree-shaking

vue

vue vue

 

227
 

Options API

Vue3

 

VUE3 typescipt

 

228
 

 

Api

229
230
 

 

Vue3.x

1 <!-- Layout.vue -->


2 <template>
3 <header>...</header>
4 <main v-bind="$attrs">...</main>
5 <footer>...</footer>
6 </template>

 

Teleport DOM Vue app

vue2 modals toast Vue


z-index

Teleport Vue

1 <button @click="showToast" class="btn"> toast</button>


2 <!-- to -->
3 <teleport to="#teleport-target">
4 <div v-if="visible" class="toast-wrap">
5 <div class="toast-msg"> Toast </div>
6 </div>
7 </teleport>

231
 

createRenderer vue

canvas

createRenderer

1 import { createRenderer } from '@vue/runtime-core'


2
3 const { render, createApp } = createRenderer({
4 patchProp,
5 insert,
6 remove,
7 createElement,
8 // ...
9 })
10
11 export { render, createApp }
12
13 export * from '@vue/runtime-core'

 

api

232
compositon api

233
234
1 export default {
2 setup() {
3 const count = ref(0)
4 const double = computed(() => count.value * 2)
5 function increment() {
6 count.value++
7 }
8 onMounted(() => console.log('component mounted!'))
9 return {
10 count,
11 double,
12 increment
13 }
14 }
15 }

 

 

Vue API

API tree-shakable

 

v-model

<template v-for> v-for key

v-if v-for

v-bind="object"

v-for ref ref

 

functional (SFC)

defineAsyncComponent

235
  

API

$scopedSlots $slots

class

v-enter v-enter-from

v-leave v-leave-from
watch $watch

Vue 2.x outerHTML


VUE3.x innerHTML

 

destroyed unmounted

beforeDestroy beforeUnmount

[prop default this

data

mixin data

attribute

class

$watch

<template> v-if/else-if/else v-for v-slot


<template>
Vue 2.x outerHTML
Vue 3.x innerHTML

 

keyCode v-on

$on $off $once

236
filter

attribute

$destroy Vue

237

You might also like