遇事不慌-手擼一個Vue-markdown-loader!

Created By JishuBao on 2019-04-22 12:38:22
Recently revised in 2019-04-23 20:59css

 

  歡迎你們來到技術寶的掘金世界,您的star是我寫文章最大的動力!GitHub地址   
   
  最近新公司在制定一套UI規範,說是爲了之後前端「即託即用」作準備。   
前端

  原本前端人數就很少的我和個人小郝哥開始抓緊了節奏,準備開始寫一些UI規範,可是因爲咱們倆都沒有完整的瞭解過相關的方面的製做,咱們在文檔上煞費苦心,在製做UI規範文檔中,咱們發如今Vue中並不能直接像markdown同樣寫一些代碼註釋等。致使須要這樣寫,你們感覺下:    vue

其實咱們的心裏是崩潰的!若是每寫一個都這樣的話,咋整!也不是說難,就是太繁瑣,咱也不敢想!因此技術寶以爲這樣不行,而後靈機一動想到平時所用的ui框架vant,人家的官網作的是多麼的好看,而後我打開了vant的源碼,一開始我是沒怎麼看懂,後來我發現他用的是md文檔,當時我心想,這麼so easy的嗎?git

而後我直接引入了markdown文檔格式文件md文件,而後...github

what the fuck 什麼鬼?

明明別人的就能引入不報錯,而後我發現那些Ui框架裏面的package.json文件裏面基本上都有一個vue-markdown-loader,機智的我很快就反應過來,他不識別md格式,就和不加vue-loader不能使用Vue同樣,因此咱們仍是要加上這個vue-markdown-loader,技術寶這個時候忽然萌生了一個想法,若是本身寫一個loader呢,這樣以來,既能理解,又可以對於這個loader瞭如指掌。話很少啥,開始啓動咱們的教程吧。正則表達式

1、前置知識

你須要知道loader是幹嗎用的,其實Loader就至關於一個「處理器」,將ts文件轉化爲js文件,將圖像轉化成內聯data url等...json

咱們來寫一個簡單的loader瀏覽器

module.exports = function (src) {
  const res = (
    `<template>\n` +
    `<h1>hello world</h1>\n` +
    `</template>`
  )
  return res
}
複製代碼

我認可這個loader長的確實是有一點智障的,可是這個Loader確實也作到了大部分Loader所作的事。緩存

  1. 你須要知道loader是幹嗎用的。
  2. 須要安裝相對應的輔助工具包。
包名稱 功能說明
markdown-it 渲染markdown基本語法
markdown-it-anchor 爲各級標題添加瞄點
markdown-it-container 用於建立自定義的塊級容器
markdown-it-emoji 渲染emoji
markdown-it-table-of-contents 自動生成目錄
highlight.js 代碼高亮
hash-sum 產生一個基於的四字節十六進制哈希value
lru-cache 一個緩存對象,用於刪除最近最少使用的項目

3、項目書寫中遇到的問題/知識點

1.md文檔中h1/h2標籤大小一致

發現已是解析成h1了爲啥還會不同呢?經過我機智的觀察,咱們在控制檯發現了這麼一坨代碼!

這是谷歌瀏覽器默認樣式,不一樣的瀏覽器都有對應不一樣的默認樣式,那麼咱們如何取消默認樣式呢?因爲瀏覽器的默認樣式優先級都會比較低,那麼咱們只須要在reset.css裏面重置樣式便可。

section h1{
    font-size:2em;
}
複製代碼

參考連接:h1和h2大小一致如何去掉瀏覽器的默認樣式markdown

2.js方法replace方法

語法

stringObject.replace(regexp/substr,replacement)

參數

參數 參數類型 描述 是否必需
stringObject 字符串 一個字符串類型的值
regexp/substr 正則對象/字符串 規定子字符串或要替換的模式的 RegExp 對象。值得注意的是若是該值是一個字符串,則將它做爲要檢索的直接量文本模式,而不是首先被轉換爲 RegExp 對象。
replacement 一個字符串值 規定了替換文本或生成替換文本的函數

返回值:

一個新的字符串,是用 replacement 替換了 regexp 的第一次匹配或全部匹配以後獲得的。

說明:

字符串 stringObject 的 replace() 方法執行的是查找並替換的操做。它將在 stringObject 中查找與 regexp 相匹配的子字符串,而後用replacement來替換這些子串。若是 regexp 具備全局標誌 g,那麼 replace() 方法將替換全部匹配的子串。不然,它只替換第一個匹配子串。replacement 能夠是字符串,也能夠是函數。若是它是字符串,那麼每一個匹配都將由字符串替換。可是 replacement 中的 $ 字符具備特定的含義。以下表所示,它說明從模式匹配獲得的字符串將用於替換。

字符 替換文本
$一、$二、...、$99 與 regexp 中的第 1 到第 99 個子表達式相匹配的文本。
$& 與 regexp 相匹配的子串。
$` 位於匹配子串左側的文本。
$' 位於匹配子串右側的文本。
$$ 直接量符號。

實例:

  1. 正則表達式第一個參數是字符串。
"Jsbao是是是男神".replace('是','不是');//Jsbao不是是是男神(只匹配第一個子串)
複製代碼

2.正則表達式第一個參數是正則對象。

"Jsbao是是是男神".replace(/是/,'不是');//Jsbao不是是是男神(只匹配第一個子串)
複製代碼

3.正則表達式第一個參數是正則對象且有g全局標誌。

"Jsbao是是是男神".replace(/是/g,'不是');//Jsbao不是否是不是男神(匹配全部子串)
複製代碼

4.正則表達式第二個參數是帶有特殊含義的符號

"Jsbao是是是男神".replace(/(是)/g,'不$1');//Jsbao不是否是不是男神(匹配全部子串)
複製代碼

注意$1此類特殊符號表明匹配的子項,故正則須要加(),表明匹配子項

5.正則表達式第二個參數是函數。

"Jsbao是男神".replace(/(是|男)/g,function(value){return value==='是'?'不是':'女'});//Jsbao不是女神
複製代碼

此文中遇到的replace

str.replace(/(<pre|<code)/g,'$1 v-pre');//意思應該是將<pre或者<code替換成<pre v-pre或<code pre的形式
複製代碼

參考連接:replace函數的做用

3.js方法Object.defineProperty()

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。

語法

Object.defineProperty(obj, prop, descriptor)

參數

參數 描述
obj 要在其上定義屬性的對象
prop 要定義或修改的屬性的名稱
descriptor 將定義或修改的屬性描述符

返回值

被傳遞給函數的對象。

在ES6中,因爲 Symbol類型的特殊性,用Symbol類型的值來作對象的key與常規的定義或修改不一樣,而Object.defineProperty 是定義key爲Symbol的屬性的方法之一。

描述

該方法容許精確添加或修改對象的屬性。經過賦值操做添加的普通屬性是可枚舉的,可以在屬性枚舉期間呈現出來(for...in 或 Object.keys 方法), 這些屬性的值能夠被改變,也能夠被刪除。這個方法容許修改默認的額外選項(或配置)。默認狀況下,使用 Object.defineProperty() 添加的屬性值是不可修改的。

屬性描述符

對象裏目前存在的屬性描述符有兩種主要形式:數據描述符和存取描述符。數據描述符是一個具備值的屬性,該值多是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是二者。

屬性 描述 是不是數據描述符屬於的鍵值 是不是存取描述符屬於的鍵值
configurable 當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。
enumerable 當且僅當該屬性的enumerable爲true時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。
value 該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined。
writable 當且僅當該屬性的writable爲true時,value才能被賦值運算符改變。默認爲 false。
get 一個給屬性提供 getter 的方法,若是沒有 getter則爲undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,可是會傳入this對象(因爲繼承關係,這裏的this並不必定是定義該屬性的對象)。默認爲 undefined。
set 一個給屬性提供 setter 的方法,若是沒有 setter 則爲undefined。當屬性值修改時,觸發執行該方法。該方法將接受惟一參數,即該屬性新的參數值。默認爲 undefined。

若是一個描述符不具備value,writable,get 和 set 任意一個關鍵字,那麼它將被認爲是一個數據描述符。若是一個描述符同時有(value或writable)和(get或set)關鍵字,將會產生一個異常。

記住,這些選項不必定是自身屬性,若是是繼承來的也要考慮。爲了確認保留這些默認值,你可能要在這以前凍結 Object.prototype,明確指定全部的選項,或者經過 Object.create(null)將__proto__屬性指向null

// 使用 __proto__
var obj = {};
var descriptor = Object.create(null); // 沒有繼承的屬性
// 默認沒有 enumerable,沒有 configurable,沒有 writable
descriptor.value = 'static';
Object.defineProperty(obj, 'key', descriptor);

// 顯式
Object.defineProperty(obj, "key", {
  enumerable: false,
  configurable: false,
  writable: false,
  value: "static"
});

// 循環使用同一對象
function withValue(value) {
  var d = withValue.d || (
    withValue.d = {
      enumerable: false,
      writable: false,
      configurable: false,
      value: null
    }
  );
  d.value = value;
  return d;
}
// ... 而且 ...
Object.defineProperty(obj, "key", withValue("static"));

// 若是 freeze 可用, 防止代碼添加或刪除對象原型的屬性
// (value, get, set, enumerable, writable, configurable)
(Object.freeze||Object)(Object.prototype);
複製代碼

用法

  1. 建立屬性:

若是對象中不存在指定的屬性,Object.defineProperty()就建立這個屬性。當描述符中省略某些字段時,這些字段將使用它們的默認值。擁有布爾值的字段的默認值都是false。value,get和set字段的默認值爲undefined。一個沒有get/set/value/writable定義的屬性被稱爲「通用的」,並被「鍵入」爲一個數據描述符。

var o = {}; // 建立一個新對象

// 在對象中添加一個屬性與數據描述符的示例
Object.defineProperty(o, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});

// 對象o擁有了屬性a,值爲37

// 在對象中添加一個屬性與存取描述符的示例
var bValue;
Object.defineProperty(o, "b", {
  get : function(){
    return bValue;
  },
  set : function(newValue){
    bValue = newValue;
  },
  enumerable : true,
  configurable : true
});

o.b = 38;
// 對象o擁有了屬性b,值爲38

// o.b的值如今老是與bValue相同,除非從新定義o.b

// 數據描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
  value: 0x9f91102, 
  get: function() { 
    return 0xdeadbeef; 
  } 
});
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors
複製代碼
  1. 修改屬性

若是屬性已經存在,Object.defineProperty()將嘗試根據描述符中的值以及對象當前的配置來修改這個屬性。若是舊描述符將其configurable 屬性設置爲false,則該屬性被認爲是「不可配置的」,而且沒有屬性能夠被改變(除了單向改變 writable 爲 false)。當屬性不可配置時,不能在數據和訪問器屬性類型之間切換。

當試圖改變不可配置屬性(除了value和writable 屬性以外)的值時會拋出TypeError,除非當前值和新值相同。

Writable屬性

當writable屬性設置爲false時,該屬性被稱爲「不可寫」。它不能被從新分配。

var o = {}; // Creates a new object

Object.defineProperty(o, 'a', {
  value: 37,
  writable: false
});

console.log(o.a); // logs 37
o.a = 25; // No error thrown
// (it would throw in strict mode,
// even if the value had been the same)
console.log(o.a); // logs 37. The assignment didn't work.

// strict mode
(function() {
 'use strict';
  var o = {};
  Object.defineProperty(o, 'b', {
    value: 2,
    writable: false
  });
  o.b = 3; // throws TypeError: "b" is read-only
  return o.b; // returns 2 without the line above
}());
複製代碼

Enumerable 特性

enumerable定義了對象的屬性是否能夠在 for...in 循環和 Object.keys() 中被枚舉。

var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // 若是使用直接賦值的方式建立對象的屬性,則這個屬性的enumerable爲true

for (var i in o) {    
  console.log(i);  
}
// 打印 'a' 和 'd' (in undefined order)

Object.keys(o); // ["a", "d"]

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
複製代碼

Configurable 特性

configurable特性表示對象的屬性是否能夠被刪除,以及除value和writable特性外的其餘特性是否能夠被修改。

var o = {};
Object.defineProperty(o, "a", { get : function(){return 1;}, 
                                configurable : false } );

// throws a TypeError
Object.defineProperty(o, "a", {configurable : true}); 
// throws a TypeError
Object.defineProperty(o, "a", {enumerable : true}); 
// throws a TypeError (set was undefined previously) 
Object.defineProperty(o, "a", {set : function(){}}); 
// throws a TypeError (even though the new get does exactly the same thing) 
Object.defineProperty(o, "a", {get : function(){return 1;}});
// throws a TypeError
Object.defineProperty(o, "a", {value : 12});

console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1
複製代碼

參考連接:Object.defineProperty詳解

相關文章
相關標籤/搜索