yehanzhou / blog Goto Github PK
View Code? Open in Web Editor NEW大多数是关于前端的~
大多数是关于前端的~
在函数执行时,this 总是指向调用该函数的对象。
要判断 this 的指向,其实就是判断 this 所在的函数属于谁。
在《javaScript语言精粹》这本书中,把 this出现的场景分为四类,简单的说就是:
有对象就指向调用对象,没调用对象就指向全局对象,用new构造就指向新对象,
通过 apply 或 call 或 bind 来改变 this 的所指。
函数有所属对象时,通常通过 . 表达式调用,这时 this自然指向所属对象。比如下面的例子:
var myObject = {value: 100};
myObject.getValue = function () {
console.log(this.value); // 输出 100
console.log(this);// 输出 { value: 100, getValue: [Function] },
// 其实就是 myObject 对象本身
return this.value;
};
console.log(myObject.getValue()); // => 100
getValue() 属于对象 myObject,并由 myOjbect 进行 . 调用,因此 this 指向对象 myObject。
var myObject = {value: 100};
myObject.getValue = function () {
var foo = function () {
console.log(this);// 输出全局对象 global
console.log(this.value) // => undefined
};
foo();
return this.value;
};
console.log(myObject.getValue()); // => 100
在上述代码块中,foo 函数虽然定义在 getValue的函数体内,但实际上它既不属于 getValue 也不属于 myObject。foo 并没有被绑定在任何对象上,所以当调用时,它的 this 指针指向了全局对象 global。据说这是个设计错误。
js 中,我们通过 new 关键词来调用构造函数,此时 this会绑定在该新对象上。
var SomeClass = function(){
this.value = 100;
}
var myCreate = new SomeClass();
console.log(myCreate.value); // 输出100
顺便说一句,在 js 中,构造函数、普通函数、对象方法、闭包,这四者没有明确界线。界线都在人的心中。
apply() 方法接受两个参数第一个是函数运行的作用域,另外一个是一个参数数组(arguments)。
call() 方法第一个参数的意义与apply()方法相同,只是其他的参数需要一个个列举出来。
简单来说,call 的方式更接近我们平时调用函数,而 apply 需要我们传递 Array 形式的数组给它。它们是可以互相转换的。
var myObject = {value: 100};
var foo = function(){
console.log(this);
};
foo(); // 全局变量 global
foo.apply(myObject); // { value: 100 }
foo.call(myObject); // { value: 100 }
var newFoo = foo.bind(myObject);
newFoo(); // { value: 100 }
之前理解的css选择器优先级是这样的
!important >内联样式> id > class > 元素选择器 > 伪元素
看了一下w3c的文档规范,发现压根没有我想的这么简单
!important > 内联样式
没有变化
先说说有哪些选择器:
这些元素的特异性如下
连接abc为一个三位数,计算他们的优先级:
* /* a=0 b=0 c=0 -> 优先级 = 0 */
LI /* a=0 b=0 c=1 -> 优先级 = 1 */
UL LI /* a=0 b=0 c=2 -> 优先级 = 2 */
UL OL+LI /* a=0 b=0 c=3 -> 优先级 = 3 */
H1 + *[REL=up] /* a=0 b=1 c=1 -> 优先级 = 11 */
UL OL LI.red /* a=0 b=1 c=3 -> 优先级 = 13 */
LI.red.level /* a=0 b=2 c=1 -> 优先级 = 21 */
#x34y /* a=1 b=0 c=0 -> 优先级 = 100 */
#s12:not(FOO) /* a=1 b=0 c=1 -> 优先级 = 101 */
参考链接:
css3选择器w3c标准
css2选择器w3c标准
MDN
先在github上新建一个仓库,取名,例test
本地新建文件夹
打开命令行工具cd进这个文件夹
执行git init
,执行后会在该文件夹下生成一个名为.git的文件夹,里面有有关git的配置
关联到远程仓库
git remote add origin https://[email protected]/YehanZhou/test.git
添加所有文件到暂存区
git add .
提交
git commit -m "description"
推送
git push -u origin master
参考:
Diff 的出现,就是为了减少更新量,找到最小差异部分DOM,只更新差异部分DOM就好了
这样消耗就会小一些
数据变化一下,没必要把其他没有涉及的没有变化的DOM 也替换了
精确复用所有节点就必须付出高昂的代价:时间复杂度 O(n³) 的 diff 算法
也可以说成是
只有两个新旧节点是相同节点的时候,才会去比较他们各自的子节点
最大的根节点一开始可以直接比较
这也叫做 同层级比较
如图所示,只按层比较,就可以将时间复杂度降低为 O(n)。
这样做确实非常高效,但代价就是,判断的有点傻,比如 ac 明明是一个移动操作,却被误识别为删除 + 新增。
好在跨 DOM 复用在实际业务场景中很少出现,因此这种笨拙出现的频率实际上非常低。
所有的 新旧节点 指的都是 Vnode 节点,Vue 只会比较 Vnode 节点,而不是比较 DOM
因为 Vnode 是 JS 对象,不受平台限制,所以以它作为比较基础,代码逻辑后期不需要改动
拿到比较结果后,根据不同平台调用相应的方法进行处理就好了
比如下图出现的 四次比较(从 first 到 fouth),他们的共同特点都是有 相同的父节点
比如 蓝色方的比较,新旧子节点的父节点是相同节点 1
比如 红色方的比较,新旧子节点的父节点都是 2
所以他们才有比较的机会
而下图中,只有两次比较,就是因为在 蓝色方 比较中,并没有相同节点,所以不会再进行下级子节点比较
Diff 比较的内核是 节点复用,所以 Diff 比较就是为了在 新旧节点中 找到 相同的节点
这个的比较逻辑是建立在上一步说过的同层比较基础之上的
所以说,节点复用,找到相同节点并不是无限制递归查找
比如下图中,的确 旧节点树 和 新节点树 中有相同节点 6,但是它们不在同一层,旧节点6并不会被复用
就算在同一层级,然而父节点不一样,依旧不会被复用
只有这种情况的节点会被复用,相同父节点 8
在新旧节点中
其实这里存在 三种树,一个是 页面DOM 树,一个是 旧VNode 树,一个是 新 Vnode 树
页面DOM 树 和 旧VNode 树 节点一一对应的
而 新Vnode 树则是表示更新后 页面DOM 树 该有的样子
这里把 旧Vnode 树 和 新Vnode树 进行比较的过程中
不会对这两棵Vode树进行修改,而是以比较的结果直接对 真实DOM 进行修改
比如说,在 旧 Vnode 树同一层中,找到 和 新Vnode 树 中一样但位置不一样节点
此时需要移动这个节点,但是不是移动 旧 Vnode 树 中的节点
而是 直接移动 DOM
总的来说,新旧 Vnode 树是拿来比较的,页面DOM 树是拿来根据比较结果修改的
因为父节点都是 1,所以开始比较他们的子节点
按照我们上面的比较逻辑,所以先找 相同且不需移动 的点,遂找到 2
拿到比较结果,这里不用修改DOM,所以 DOM 保留在原地
然后,没有 相同且不需移动 的节点 了
只能第二个方案,开始找相同的点
找到 节点5,相同但是位置不同,所以需要移动
拿到比较结果,页面 DOM 树需要移动DOM 了,不修改,原样移动
继续,相同节点也没了,没得办法了,只能创建了
所以要根据 新Vnode 中没找到的节点去创建并且插入
然后旧Vnode 中有些节点不存在 新VNode 中,所以要删除
于是开始创建节点 6 和 9,并且删除节点 4 和 5
React 采用了 仅右移策略
PS:最新版 React Dom diff 算法可能有更新,因为这种算法看来不如 Vue 的高效。
function Vue() {
... 已省略其他
new Watcher(function() {
vm._update(vm._render());
})
... 已省略其他
}
Watcher 的源码
funciton Watcher(expOrFn){
this.getter = expOrFn;
this.get();
}
Watcher.prototype.get = function () {
this.getter()
}
生成页面模板对应的 Vnode 树, 比如
<div>
<span></span>
{{num}}
</div>
生成的 Vnode 树是( 其中num的值是111 )
{
tag: "div",
children:[{
tag: "span"
},{
tag: undefined,
text: "111"
}]
}
这一步是通过 compile 生成的, 下次分享讲
比较 旧Vnode 树 和 vm._render 生成的新 Vnode 树 进行比较
比较完后,更新页面的DOM,从而完成更新
ok,我们看下源码
Vue.prototype._update = function(vnode) {
var vm = this;
var prevEl = vm.$el;
var prevVnode = vm._vnode;
vm._vnode = vnode;
if (!prevVnode) {
// 不存在旧根节点,直接创建新的DOM树
vm.$el = vm.__patch__(
vm.$el, vnode,
vm.$options._parentElm,
vm.$options._refElm
);
}
else {
// 存在旧节点,比较vnode和新vnode,更新DOM树
vm.$el = vm.__patch__(
prevVnode, vnode
);
}
};
解释其中几个点
vm._vnode
这个属性保存的就是当前 Vnode 树
当页面开始更新,而生成了新的 Vnode 树之后
这个属性则会替换成新的Vnode
所以保存在这里,是为了方便拿到 旧 Vnode 树
vm.patch
这个东西就是 Diff 的主要内容
var patch = createPatchFunction();
Vue.prototype.__patch__ = patch ;
function createPatchFunction() {
return function patch(
oldVnode, vnode, parentElm, refElm
) {
// 没有旧节点,直接生成新节点
if (!oldVnode) {
createElm(vnode, parentElm, refElm);
} else { // 有旧节点
// 新旧是一样Vnode
if (sameVnode(oldVnode, vnode)) {
// 比较他们的子节点
patchVnode(oldVnode, vnode);
} else {
// 新旧是不一样Vnode
// 替换存在的元素
var oldElm = oldVnode.elm;
var _parentElm = oldElm.parentNode
// 创建新节点
createElm(vnode, _parentElm, oldElm.nextSibling);
// 销毁旧节点
if (_parentElm) {
removeVnodes([oldVnode], 0, 0);
}
}
}
return vnode.elm
}
}
它的作用是判断两个节点是否相同
这里说的相同,并不是完全一样,而是关键属性一样,可以先看下源码
function sameVnode(a, b) {
return (
a.key === b.key &&
a.tag === b.tag &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
)
}
function sameInputType(a, b) {
if (a.tag !== 'input') return true
var i;
var types = [
'text','number','password',
'search','email','tel','url'
]
var typeA = (i = a.data) && (i = i.attrs) && i.type;
var typeB = (i = b.data) && (i = i.attrs) && i.type;
// input 的类型一样,或者都属于基本input类型
return (
typeA === typeB ||
types.indexOf(typeA)>-1 &&
types.indexOf(typeB)>-1
)
}
判断的依据主要是 三点:key、tag、是否存在 data
这里判断的节点是只是相对于 节点本身,并不包括 children 在内。
也就是说,就算data不一样,children 不一样,两个节点还是可能一样。
有一种特殊情况,就是 input 节点。
input 需要额外判断:
两个节点的 type 是否相同
或者
两个节点的类型可以不同,但是必须属于那些 input 类型。
为什么只判断是否存在 data?
因为DOM属性的值是可能是动态绑定动态更新变化的,所以变化前后的 两个 vnode,相应的 data 肯定不一样,但是其实他们是同一个 Vnode,所以 data 不在判断范畴
但是 data 在新旧节点中,必须都定义,或者都不定义
该函数的其中的一个作用是 比较两个Vnode 的子节点
function patchVnode(oldVnode, vnode) {
if (oldVnode === vnode) return
var elm = vnode.elm = oldVnode.elm;
var oldCh = oldVnode.children;
var ch = vnode.children;
// 更新children
if (!vnode.text) {
// 存在 oldCh 和 ch
if (oldCh && ch) {
if (oldCh !== ch)
// 新旧节点同时存在,比较它们
updateChildren(elm, oldCh, ch); // 是 Diff 的核心模块,蕴含着 Diff 的**
}
// 存在 newCh 时,oldCh 只能是不存在,如果存在,就跳到上面的条件了
else if (ch) {
// 只有新节点,不存在旧节点,创建出所有新DOM,并且添加进父节点
if (oldVnode.text) elm.textContent = '';
for (var i = 0; i <= ch.length - 1; ++i) {
createElm(
ch[i], elm, null
);
}
}
else if (oldCh) {
// 只有旧节点而没有新节点,就是把所有的旧节点删除,也就是直接把DOM 删除
for (var i = 0; i<= oldCh.length - 1; ++i) {
oldCh[i].parentNode.removeChild(el);
}
}
else if (oldVnode.text) {
elm.textContent = '';
}
}
// Vnode 是文本节点,则更新文本(文本节点不存在子节点)
else if (oldVnode.text !== vnode.text) {
elm.textContent = vnode.text;
}
}
diff核心算法,从两边往中间遍历新旧节点
function updateChildren(parentElm, oldCh, newCh) {
var oldStartIdx = 0;
var oldEndIdx = oldCh.length - 1;
var oldStartVnode = oldCh[0];
var oldEndVnode = oldCh[oldEndIdx];
var newStartIdx = 0;
var newEndIdx = newCh.length - 1;
var newStartVnode = newCh[0];
var newEndVnode = newCh[newEndIdx];
var oldKeyToIdx, idxInOld, vnodeToMove, refElm;
// 不断地更新 OldIndex 和 OldVnode ,newIndex 和 newVnode
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (!oldStartVnode) {
oldStartVnode = oldCh[++oldStartIdx];
} else if (!oldEndVnode) {
oldEndVnode = oldCh[--oldEndIdx];
}
// 旧头 和新头 比较
else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode); // 比较它们的子节点
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
}
// 旧尾 和新尾 比较
else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
}
// 旧头 和 新尾 比较
else if (sameVnode(oldStartVnode, newEndVnode)) {
patchVnode(oldStartVnode, newEndVnode);
// oldStartVnode 放到 oldEndVnode 后面,还要找到 oldEndVnode 后面的节点
parentElm.insertBefore(
oldStartVnode.elm,
oldEndVnode.elm.nextSibling
);
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
}
// 旧尾 和新头 比较
else if (sameVnode(oldEndVnode, newStartVnode)) {
patchVnode(oldEndVnode, newStartVnode);
// oldEndVnode 放到 oldStartVnode 前面
parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
}
// 如果不属于以上四种情况,就进行常规的循环比对patch
else {
// oldKeyToIdx 是一个 把 Vnode 的 key 和 index 转换的 map
if (!oldKeyToIdx) {
oldKeyToIdx = createKeyToOldIdx(
oldCh, oldStartIdx, oldEndIdx
);
}
// 使用 newStartVnode 去 OldMap 中寻找 相同节点,默认key存在
idxInOld = oldKeyToIdx[newStartVnode.key]
// 新孩子中,存在一个新节点,老节点中没有,需要新建
if (!idxInOld) {
// 把 newStartVnode 插入 oldStartVnode 的前面
createElm(
newStartVnode,
parentElm,
oldStartVnode.elm
);
} else {
// 找到 oldCh 中 和 newStartVnode 一样的节点
vnodeToMove = oldCh[idxInOld];
if (sameVnode(vnodeToMove, newStartVnode)) {
patchVnode(vnodeToMove, newStartVnode);
// 删除这个 index
oldCh[idxInOld] = undefined;
// 把 vnodeToMove 移动到 oldStartVnode 前面
parentElm.insertBefore(
vnodeToMove.elm,
oldStartVnode.elm
);
}
// 只能创建一个新节点插入到 parentElm 的子节点中
else {
// same key but different element. treat as new element
createElm(
newStartVnode,
parentElm,
oldStartVnode.elm
);
}
}
// 这个新子节点更新完毕,更新 newStartIdx,开始比较下一个
newStartVnode = newCh[++newStartIdx];
}
}
// 处理剩下的节点
if (oldStartIdx > oldEndIdx) {
var newEnd = newCh[newEndIdx + 1]
refElm = newEnd ? newEnd.elm :null;
for (; newStartIdx <= newEndIdx; ++newStartIdx) {
createElm(
newCh[newStartIdx], parentElm, refElm
);
}
}
// 说明新节点比对完了,老节点可能还有,需要删除剩余的老节点
else if (newStartIdx > newEndIdx) {
for (; oldStartIdx<=oldEndIdx; ++oldStartIdx) {
oldCh[oldStartIdx].parentNode.removeChild(el);
}
}
}
遵循理论篇第三节比较逻辑宗旨:
1.先找一样的不需要移动的
2.再找一样的要移动的
3.都找不到了,则新建删除节点
用patchVnode比较它们的子节点
更新索引
以 新子节点的位置 来移动的,旧头 在新子节点的 末尾
所以把 oldStartVnode 的 dom 放到 oldEndVnode 的后面
把 oldEndVnode DOM 直接放到 当前 oldStartVnode.elm 的前面
这个map 表的作用,就主要是判断存在什么旧子节点
比如你的旧子节点数组是
[{
tag:"div", key:1
},{
tag:"strong", key:2
},{
tag:"span", key:4
}]
经过 createKeyToOldIdx 生成一个 map 表 oldKeyToIdx
oldKeyToIdx = {
1:0,
2:1,
4:2
}
oldKeyToIdx[newStartVnode.key]
不存在旧子节点数组: 直接创建DOM,并插入oldStartVnode 前面
createElm(newStartVnode, parentElm, oldStartVnode.elm);
存在旧子节点数组中:
找到这个旧子节点,然后判断和新子节点是否 sameVnode
如果相同,直接移动到 oldStartVnode 前面
如果不同,直接创建插入 oldStartVnode 前面
在updateChildren 中,比较完新旧两个数组之后,可能某个数组会剩下部分节点没有被处理过,所以这里需要统一处理
newStartIdx > newEndIdx
新子节点遍历完毕,旧子节点可能还有剩
所以我们要对可能剩下的旧节点进行 批量删除!
就是遍历剩下的节点,逐个删除DOM
for (; oldStartIdx <= oldEndIdx; ++oldStartIdx) {
oldCh[oldStartIdx]
.parentNode
.removeChild(el);
}
oldStartIdx > oldEndIdx
旧子节点遍历完毕,新子节点可能有剩
所以要对剩余的新子节点处理
很明显,剩余的新子节点不存在 旧子节点中,所以全部新建
但是新建有一个问题,就是插在哪里?
来看下refElm的代码:
var newEnd = newCh[newEndIdx + 1]
refElm = newEnd ? newEnd.elm :null;
refElm 获取的是 newEndIdx 后一位的节点
当前没有处理的节点是 newEndIdx
也就是说 newEndIdx+1 的节点如果存在的话,肯定被处理过了
如果 newEndIdx 没有移动过,一直是最后一位,那么就不存在 newCh[newEndIdx + 1]
newStartIdx++ while循环结束
newStartIdx > newEndIdx
剩余旧节点4,将其删除
Math.random()方法返回大于等于 0 小于 1 的一个随机数。对于某些站点来说,这个方法非常实用,因为可以利用它来随机显示一些名人名言和新闻事件。
值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)
例:产生1-10的随机数
var rand1 = Math.floor(Math.random() * 10 + 1);
编写产生startNumber至endNumber随机数的函数
function selectFrom(startNumber, endNumber) {
var choice = endNumber - startNumber + 1;
return Math.floor(Math.random() * choice + startNumber)
}
var rand2 = selectFrom(2,8);//产生2至8的随机数
例:随机产生2或4中的一个数
var rand3 = Math.random() < 0.5 ? 2 : 4;
结合函数参数数组,可编写在不相邻的多个整数中产生一个随机值的函数
function selectFromMess() {
return arguments[Math.floor(Math.random() * arguments.length)]
}
//随机产生1、6、8中的一个数
var rand4 = selectFromMess(1, 6, 8);
//也可随机产生文本
var randomTxt1 = selectFromMess("安慰奖", "二等奖", "一等奖");
每次要输入这么多参数比较麻烦,可以改写一下函数
function selectFromMessArray(arr) {
return arr[Math.floor(Math.random() * arr.length)]
}
var arrayTxt=["一","二","三","四","五"];
var randTxt2 = selectFromMessArray(arrayTxt);
或者不改变原有方法,可以利用apply()这个方法传递数组参数
var randTxt3 = selectFromMess.apply(null,arrayTxt);
关于apply方法的使用可以看这里
ECMAScript 5以及以前的版本:
let options = {
repeat: true,
save: false
};
// extract data from the object
let repeat = options.repeat,
save = options.save;
虽然这段代码看上去也挺简单的,但想象一下如果你要给大量的变量赋值,你得一个一个的赋值。
或者你需要取一个嵌套结构数据的某个值,也许你得遍历整个解构。
如果你能把数据解构成一些小小的片段,那获取信息将会更加容易。
let node = {
type: "Identifier",
name: "foo"
};
let { type, name } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
注意: 必须初始化
// syntax error!
var { type, name };
// syntax error!
let { type, name };
// syntax error!
const { type, name };
可以赋值给已经定义过的变量:
let node = {
type: "Identifier",
name: "foo"
},
type = "Literal",
name = 5;
// assign different values using destructuring
({ type, name } = node);
console.log(type); // "Identifier"
console.log(name); // "foo"
let node = {
type: "Identifier",
name: "foo"
};
let { type, name, value = true } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // true
let node = {
type: "Identifier",
name: "foo"
};
let { type: localType, name: localName } = node;
console.log(localType); // "Identifier"
console.log(localName); // "foo"
let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
}
};
let { loc: { start }} = node;
console.log(start.line); // 1
console.log(start.column); // 1
let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
只取你需要的部分
let colors = [ "red", "green", "blue" ];
let [ , , thirdColor ] = colors;
console.log(thirdColor); // "blue"
注意: 和对象的解构一样,必须初始化
可以赋值给已经定义过的变量:
let colors = [ "red", "green", "blue" ],
firstColor = "black",
secondColor = "purple";
[ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
在ECMAScript 5 中交换变量值
let a = 1,
b = 2, tmp;
tmp = a;
a = b;
b = tmp;
console.log(a); // 2
console.log(b); // 1
在ECMAScript 6 中交换变量值
let a = 1,
b = 2;
[ a, b ] = [ b, a ];
console.log(a); // 2
console.log(b); // 1
let colors = [ "red" ];
let [ firstColor, secondColor = "green" ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
let colors = [ "red", [ "green", "lightgreen" ], "blue" ];
// later
let [ firstColor, [ secondColor ] ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
let colors = [ "red", "green", "blue" ];
let [ firstColor, ...restColors ] = colors;
console.log(firstColor); // "red"
console.log(restColors.length); // 2
console.log(restColors[0]); // "green"
console.log(restColors[1]); // "blue"
数组的第一个值赋给了firstColor,剩下的值组成了一个新的数组赋给了restColors。
ECMAScript 5克隆一个数组:
var colors = [ "red", "green", "blue" ];
var clonedColors = colors.concat();
console.log(clonedColors); // "[red,green,blue]"
ECMAScript 6克隆一个数组:
let colors = [ "red", "green", "blue" ];
let [ ...clonedColors ] = colors;
console.log(clonedColors); // "[red,green,blue]"
注意: 剩余的元素必须是解构数组的最后一个元素,后面不能有逗号。
对象与数组嵌套混合的解构:
let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1 },
end: {
line: 1,
column: 4 }
},
range: [0, 3]
};
let {
loc: { start },
range: [ startIndex ]
} = node;
console.log(start.line); // 1
console.log(start.column); // 1
console.log(startIndex); // 0
function setCookie(name, value, { secure, path, domain, expires }) {
// code to set the cookie
}
setCookie("type", "js", {
secure: true,
expires: 60000
});
// error!
setCookie("type", "js");
它实际上是这样运行的:
function setCookie(name, value, options) {
let { secure, path, domain, expires } = options;
// code to set the cookie
}
当解构赋值的右边是null或者undefined,就会抛出错误。
如果你希望解构参数是可选的,你可以这样写:
function setCookie(name, value, { secure, path, domain, expires } = {}) {
// empty }
function setCookie(name, value,
{
secure = false,
path = "/",
domain = "example.com",
expires = new Date(Date.now() + 360000000)
} = {} ){
// empty }
vue cli 3抽象化了webpack配置,所以没有了build文件,但你还是可以通过vue inspect
查找相应配置,并通过添加vue.config.js修改相应配置。
本文列举如何将markdown文件当做组件引入,
vue.config.js
const marked = require('marked')
const renderer = new marked.Renderer()
module.exports = {
chainWebpack: config => {
config.module
.rule('md')
.test(/\.md$/)
.use('html-loader')
.loader('html-loader')
.end()
.use('markdown-loader')
.loader('markdown-loader')
.options({
pedantic: true,
renderer
})
}
}
配置完后可以通过命令vue inspect --rule md
来审查项目的 webpack 配置有没有生效
home.vue
<template>
<div class="home">
<div class="md-content" v-html="content"></div>
</div>
</template>
<script>
import intro from '@/md/intro.md'
export default {
data() {
return {
intro
}
}
}
</script>
参考官方文档
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.