snabbdom介绍
- snabbdom是虚拟dom库,是diff算法的鼻祖,vue源码借鉴了snabbdom;
官方git-ts版本
snabbdom-study尝试使用
npm i -D snabbdom
手写Vue源码
let vnode = {
sel: "div",,
data: {},
children: [{sel: 'a',
data:{},
text: '1',
key: undifined,
elm: undifined,
}],
text: "我是一个盒子"
key:undifined,
elm: undifined,
}
function h(sel, data, c) {
if (arguments.length != 3)
throw new Error('对不起,h函数必须传入3个参数,我们是低配版h函数');
if (typeof c == 'string' || typeof c == 'number') {
return vnode(sel, data, undefined, c, undefined);
} else if (Array.isArray(c)) {
let children = [];
for (let i = 0; i < c.length; i++) {
if (!(typeof c[i] == 'object' && c[i].hasOwnProperty('sel')))
throw new Error('传入的数组参数中有项不是h函数');
children.push(c[i]);
}
return vnode(sel, data, children, undefined, undefined);
} else if (typeof c == 'object' && c.hasOwnProperty('sel')) {
let children = [c];
return vnode(sel, data, children, undefined, undefined);
} else {
throw new Error('传入的第三个参数类型不对');
}
};
手写diff算法
function patch(oldVnode, newVnode) {
if (oldVnode.sel == '' || oldVnode.sel == undefined) {
oldVnode = vnode(oldVnode.tagName.toLowerCase(), {}, [], undefined, oldVnode);
}
if (oldVnode.key == newVnode.key && oldVnode.sel == newVnode.sel) {
console.log('是同一个节点');
patchVnode(oldVnode, newVnode);
} else {
console.log('不是同一个节点,暴力插入新的,删除旧的');
let newVnodeElm = createElement(newVnode);
if (oldVnode.elm.parentNode && newVnodeElm) {
oldVnode.elm.parentNode.insertBefore(newVnodeElm, oldVnode.elm);
}
oldVnode.elm.parentNode.removeChild(oldVnode.elm);
}
};
封装createElement函数
function createElement(vnode) {
let domNode = document.createElement(vnode.sel);
if (vnode.text != '' && (vnode.children == undefined || vnode.children.length == 0)) {
domNode.innerText = vnode.text;
} else if (Array.isArray(vnode.children) && vnode.children.length > 0) {
for (let i = 0; i < vnode.children.length; i++) {
let ch = vnode.children[i];
let chDOM = createElement(ch);
domNode.appendChild(chDOM);
}
}
vnode.elm = domNode;
return vnode.elm;
};
封装patchVnode
function patchVnode(oldVnode, newVnode) {
if (oldVnode === newVnode) return;
if (newVnode.text != undefined && (newVnode.children == undefined || newVnode.children.length == 0)) {
console.log('新vnode有text属性');
if (newVnode.text != oldVnode.text) {
oldVnode.elm.innerText = newVnode.text;
}
} else {
console.log('新vnode没有text属性');
if (oldVnode.children != undefined && oldVnode.children.length > 0) {
updateChildren(oldVnode.elm, oldVnode.children, newVnode.children);
} else {
oldVnode.elm.innerHTML = '';
for (let i = 0; i < newVnode.children.length; i++) {
let dom = createElement(newVnode.children[i]);
oldVnode.elm.appendChild(dom);
}
}
}
}
vue-cli3配置文件
- webpack的位置文件是新建
webpack.config.js
- vue的配置文件是新建
vue.config.js
publicPath会将此参数,传递到process.env.BASE_URL
- 创建 .env文件设置变量名
VUE_APP_{变量名}, 也会传递到process.env.VUE_APP_{变量名}
module.exports = {
publicPath: '/subapp/sub-vue',
transpileDependencies: ['common'],
chainWebpack: config => config.resolve.symlinks(false),
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`
}
},
devServer: {
port: process.env.VUE_APP_PORT,
headers: {
'Access-Control-Allow-Origin': '*'
}
}
}
- 封装updateChildren函数
- 四种命中查找 - 四个指针;循环条件:新前小于新后,旧前小于旧后。
- 新前与旧前
- 新后与旧后
- 新后与旧前
- 新前与旧后
- 如果都没有命中需要循环寻找
function updateChildren(parentElm, oldCh, newCh) {
console.log('我是updateChildren');
console.log(oldCh, newCh);
let oldStartIdx = 0;
let newStartIdx = 0;
let oldEndIdx = oldCh.length - 1;
let newEndIdx = newCh.length - 1;
let oldStartVnode = oldCh[0];
let oldEndVnode = oldCh[oldEndIdx];
let newStartVnode = newCh[0];
let newEndVnode = newCh[newEndIdx];
let keyMap = null;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
console.log('★');
if (oldStartVnode == null || oldCh[oldStartIdx] == undefined) {
oldStartVnode = oldCh[++oldStartIdx];
} else if (oldEndVnode == null || oldCh[oldEndIdx] == undefined) {
oldEndVnode = oldCh[--oldEndIdx];
} else if (newStartVnode == null || newCh[newStartIdx] == undefined) {
newStartVnode = newCh[++newStartIdx];
} else if (newEndVnode == null || newCh[newEndIdx] == undefined) {
newEndVnode = newCh[--newEndIdx];
} else if (checkSameVnode(oldStartVnode, newStartVnode)) {
console.log('①新前和旧前命中');
patchVnode(oldStartVnode, newStartVnode);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (checkSameVnode(oldEndVnode, newEndVnode)) {
console.log('②新后和旧后命中');
patchVnode(oldEndVnode, newEndVnode);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (checkSameVnode(oldStartVnode, newEndVnode)) {
console.log('③新后和旧前命中');
patchVnode(oldStartVnode, newEndVnode);
parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
} else if (checkSameVnode(oldEndVnode, newStartVnode)) {
console.log('④新前和旧后命中');
patchVnode(oldEndVnode, newStartVnode);
parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
} else {
if (!keyMap) {
keyMap = {};
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
const key = oldCh[i].key;
if (key != undefined) {
keyMap[key] = i;
}
}
}
console.log(keyMap);
const idxInOld = keyMap[newStartVnode.key];
console.log(idxInOld);
if (idxInOld == undefined) {
parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm);
} else {
const elmToMove = oldCh[idxInOld];
patchVnode(elmToMove, newStartVnode);
oldCh[idxInOld] = undefined;
parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm);
}
newStartVnode = newCh[++newStartIdx];
}
}
if (newStartIdx <= newEndIdx) {
console.log('new还有剩余节点没有处理,要加项。要把所有剩余的节点,都要插入到oldStartIdx之前');
for (let i = newStartIdx; i <= newEndIdx; i++) {
parentElm.insertBefore(createElement(newCh[i]), oldCh[oldStartIdx].elm);
}
} else if (oldStartIdx <= oldEndIdx) {
console.log('old还有剩余节点没有处理,要删除项');
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
if (oldCh[i]) {
parentElm.removeChild(oldCh[i].elm);
}
}
}
};
添加element-ui
- 执行命令
vue add element
- 有俩个选项 1.全局引入 2.按需引入
Fully import
Import on demand
- 我选择的是第二种按需引入,会
自动修改我们的配置文件
1. 多个plugins文件
2. bael.config.js文件修改
3. mian.js自动引入