3 Vue面试真题-237页
3 Vue面试真题-237页
Life Cycle
Cradle-to-Grave
Vue
Vue
this property
created: () => this.fetchTodos()
1
Vue
2
vue
watch event
vm.$el
el vm.$mount(el)
3
vm.el DOM
vm.el
vm.el el
el template render
view
beforeUpdate
view
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
data
Compile
Watcher 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 }
10
defineReactive key Dep
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
12
.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
$emit $emit
Chilfen.vue
15
1 this.$emit('add', good)
Father.vue
ref
ref
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 // 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
mutations state
19
props $emit ref
$bus $parent
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 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 }
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
/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
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
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 }
1 obj.foo
2 obj.foo = 'new'
obj
27
Vue
{any} value
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-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>
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
true
34
false
v-if
v-show
v-if
v-if v-show
template ast JS
vue
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 }
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
37
v-for key
1 <ul>
2 <li v-for="item in items" :key="item.id">...</li>
3 </ul>
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
40
v-for key
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
Vue
43
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 })
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
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
mixins
mergeField
Vue
48
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
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
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
53
Vue DOM
vue
input v-model
54
value change
parseFloat
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
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 v-bind
59
view-Box viewBox
1 <svg :viewBox="viewBox"></svg>
60
Vue DOM Vue
Html
vue
message
DOM
1 console.log(vm.$el.textContent) //
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 })
/src/core/util/next-tick.js
callbacks
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
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
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
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
}
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 }
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
}
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
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
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
}
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
$mount
81
mountComponent
updateComponent
render DOM
diff
diff
82
vue diff
VNode
83
diff
endIndex startIndex
diff
endIndex startInd
ex
84
startIndex startIndex endIndex
diff
startIndex startIndex
85
diff
startIndex
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 }
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
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
94
VNode
watcher patch DOM
isSameVnode patchVnode
patchVnode
dom el
el Vnode
oldVnode 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
vue
Vue.component
1 Vue.component('my-component-name', { /* ... */ })
98
components
Vue.use()
new Vue()
Vue.use
(Plugin) Vue
Vue
99
100
CORS HTTP
CORS
koa
Access-Control-Allow-Origin
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-
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
bind
inserted
unbind
el DOM
binding property
name v-
modifiers v-my-directive.foo.bar
{ foo: true, bar: true }
vnode Vue
106
oldVnode update componentUpdated
el
dataset
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 }
113
capitalize
message
filterA message
filterB filterA
filterB
JavaScript
filterA
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
114
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
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
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
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
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 }
this.$scopredSlots vm.slot
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
126
DOM DOM
DOM
vue DOM
DOM
1 <div id="app">
2 <p class="p"> </p>
3 <h3>{{ foo }}</h3>
4 </div>
vue
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))])])}})
DOM DOM
127
DOM div
DOM
DOM 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
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
132
Component
children VNode
normalizationType
render
normalizationType children
simpleNormalizeChildren render
normalizeChildren
render
slot v-for
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
axios HTTP
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 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
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 // .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
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
errorHandler
Vue
Vue API
undefined
console.error
v-on
errorCaptured
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
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
152
invokeWithErrorHandling
logError warn
axios
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 }
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
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
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';
request
165
1 Axios.prototype.request = function request(config) {
2 /*
3 mergeConfig ...
4 */
5 // Hook up interceptors middleware . dispatchRequest
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
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 }
172
173
4xx
174
jwt 401
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) //
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
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
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
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>
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
}
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 }
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
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
193
keep-alive actived
1 activated(){
2 this.getData() //
3 },
avtived
SPA
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.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
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 ]
Service Worker
localStorage
UI element-UI antd UI
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
206
gzip gzip
express compression
vue Nuxt.js
207
web
vue
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 }
web
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
211
Server-Side Rendering SSR
HTML
Web
dom
JS
#app
212
Vue SSR
Vue SSR
js
SSR
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
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
webpack
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
vuex
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
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
Teleport Vue
231
createRenderer vue
canvas
createRenderer
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
v-if v-for
v-bind="object"
functional (SFC)
defineAsyncComponent
235
API
$scopedSlots $slots
class
v-enter v-enter-from
v-leave v-leave-from
watch $watch
destroyed unmounted
beforeDestroy beforeUnmount
data
mixin data
attribute
class
$watch
keyCode v-on
236
filter
attribute
$destroy Vue
237