最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue?parseHTML函數(shù)源碼解析start鉤子函數(shù)

 更新時間:2022年07月13日 17:40:04   作者:李李  
這篇文章主要為大家介紹了vue?parseHTML函數(shù)源碼解析start鉤子函數(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

接上章節(jié):parseHTML 函數(shù)源碼解析 AST 預(yù)備知識

現(xiàn)在我們就可以愉快的進入到Vue start鉤子函數(shù)源碼部分了。

start: function start(tag, attrs, unary) {
	// check namespace.
	// inherit parent ns if there is one
	var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag);
	// handle IE svg bug
	/* istanbul ignore if */
	if (isIE && ns === 'svg') {
		attrs = guardIESVGBug(attrs);
	}
	var element = createASTElement(tag, attrs, currentParent);
	if (ns) {
		element.ns = ns;
	}
	if (isForbiddenTag(element) && !isServerRendering()) {
		element.forbidden = true;
		warn$2(
			'Templates should only be responsible for mapping the state to the ' +
			'UI. Avoid placing tags with side-effects in your templates, such as ' +
			"<" + tag + ">" + ', as they will not be parsed.'
		);
	}
	// apply pre-transforms
	for (var i = 0; i < preTransforms.length; i++) {
		element = preTransforms[i](element, options) || element;
	}
	if (!inVPre) {
		processPre(element);
		if (element.pre) {
			inVPre = true;
		}
	}
	if (platformIsPreTag(element.tag)) {
		inPre = true;
	}
	if (inVPre) {
		processRawAttrs(element);
	} else if (!element.processed) {
		// structural directives
		processFor(element);
		processIf(element);
		processOnce(element);
		// element-scope stuff
		processElement(element, options);
	}
	function checkRootConstraints(el) {
		{
			if (el.tag === 'slot' || el.tag === 'template') {
				warnOnce(
					"Cannot use <" + (el.tag) + "> as component root element because it may " +
					'contain multiple nodes.'
				);
			}
			if (el.attrsMap.hasOwnProperty('v-for')) {
				warnOnce(
					'Cannot use v-for on stateful component root element because ' +
					'it renders multiple elements.'
				);
			}
		}
	}
	// tree management
	if (!root) {
		root = element;
		checkRootConstraints(root);
	} else if (!stack.length) {
		// allow root elements with v-if, v-else-if and v-else
		if (root.if && (element.elseif || element.else)) {
			checkRootConstraints(element);
			addIfCondition(root, {
				exp: element.elseif,
				block: element
			});
		} else {
			warnOnce(
				"Component template should contain exactly one root element. " +
				"If you are using v-if on multiple elements, " +
				"use v-else-if to chain them instead."
			);
		}
	}
	if (currentParent && !element.forbidden) {
		if (element.elseif || element.else) {
			processIfConditions(element, currentParent);
		} else if (element.slotScope) { // scoped slot
			currentParent.plain = false;
			var name = element.slotTarget || '"default"';
			(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
		} else {
			currentParent.children.push(element);
			element.parent = currentParent;
		}
	}
	if (!unary) {
		currentParent = element;
		stack.push(element);
	} else {
		closeElement(element);
	}
}

如上代碼start 鉤子函數(shù)接受三個參數(shù),這三個參數(shù)分別是標簽名字 tag,該標簽的屬性數(shù)組attrs,以及代表著該標簽是否是一元標簽的標識 unary。

接下來別害怕看不懂,我們一點點來分析它函數(shù)體中的代碼。

var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag);

開頭定義了 ns 變量,它的值為標簽的命名空間,如何獲取當前元素的命名空間呢?首先檢測currentParent 變量是否存在,我們知道 currentParent 變量為當前元素的父級元素描述對象,如果當前元素存在父級并且父級元素存在命名空間,則使用父級的命名空間作為當前元素的命名空間。

如果父級元素不存在或父級元素沒有命名空間那么會調(diào)用platformGetTagNamespace函數(shù),platformGetTagNamespace 函數(shù)只會獲取 svg 和 math 這兩個標簽的命名空間,但這兩個標簽的所有子標簽都會繼承它們兩個的命名空間。

platformGetTagNamespace 源碼

function getTagNamespace(tag) {
	if (isSVG(tag)) {
		return "svg"
	}
	if (tag === "math") {
		return "math"
	}
}

接下來源碼:

if (isIE && ns === "svg") {
	attrs = guardIESVGBug(attrs);
}

這里通過isIE來判斷宿主環(huán)境是不是IE瀏覽器,并且前元素的命名空間為svg, 如果是通過guardIESVGBug處理當前元素的屬性數(shù)組attrs,并使用處理后的結(jié)果重新賦值給attrs變量,該問題是svg標簽中渲染多余的屬性,如下svg標簽:

<svg xmlns:feature="http://www.openplans.org/topp"></svg>

被渲染為:

<svg xmlns:NS1="" NS1:xmlns:feature="http://www.openplans.org/topp"></svg>

標簽中多了 'xmlns:NS1="" NS1:' 這段字符串,解決辦法也很簡單,將整個多余的字符串去掉即可。而 guardIESVGBug 函數(shù)就是用來修改NS1:xmlns:feature屬性并移除xmlns:NS1="" 屬性的。

接下來源碼:

var element = createASTElement(tag, attrs, currentParent);
if (ns) {
	element.ns = ns;
}

在上章節(jié)聊過,createASTElement 它將生成當前標簽的元素描述對象并且賦值給 element 變量。緊接著檢查當前元素是否存在命名空間 ns ,如果存在則在元素對象上添加 ns 屬性,其值為命名空間的值。

接下來源碼:

if (isForbiddenTag(element) && !isServerRendering()) {
	element.forbidden = true;
	warn$2(
		'Templates should only be responsible for mapping the state to the ' +
		'UI. Avoid placing tags with side-effects in your templates, such as ' +
		"<" + tag + ">" + ', as they will not be parsed.'
	);
}

這里的作用就是判斷在非服務(wù)端渲染情況下,當前解析的開始標簽是否是禁止在模板中使用的標簽。哪些是禁止的呢?

 isForbiddenTag 函數(shù)

function isForbiddenTag(el) {
	return (
		el.tag === 'style' ||
		(el.tag === 'script' &amp;&amp; (
			!el.attrsMap.type ||
			el.attrsMap.type === 'text/javascript'
		))
	)
}

可以看到,style,script 都是在禁止名單中,但通過isForbiddenTag 也發(fā)現(xiàn)一個彩蛋。

<script type="text/x-template" id="hello-world-template">
  <p>Hello hello hello</p>
</script>

當定義模板的方式如上,在 <script> 元素上添加 type="text/x-template" 屬性。 此時的script不會被禁止。

最后還會在當前元素的描述對象上添加 element.forbidden 屬性,并將其值設(shè)置為true。

接下來源碼:

for (var i = 0; i < preTransforms.length; i++) {
	element = preTransforms[i](element, options) || element;
}

如上代碼中使用 for 循環(huán)遍歷了preTransforms 數(shù)組,preTransforms 是通過pluckModuleFunction 函數(shù)從options.modules 選項中篩選出名字為preTransformNode 函數(shù)所組成的數(shù)組。實際上 preTransforms 數(shù)組中只有一個 preTransformNode 函數(shù)該函數(shù)只用來處理 input 標簽我們在后面章節(jié)會來講它。

接下來源碼:

if (!inVPre) {
	processPre(element);
	if (element.pre) {
		inVPre = true;
	}
}
if (platformIsPreTag(element.tag)) {
	inPre = true;
}
if (inVPre) {
	processRawAttrs(element);
} else if (!element.processed) {
	// structural directives
	processFor(element);
	processIf(element);
	processOnce(element);
	// element-scope stuff
	processElement(element, options);
}

可以看到這里會有大量的process*的函數(shù),這些函數(shù)是做什么用的呢?實際上process* 系列函數(shù)的作用就是對元素描述對象做進一步處理,比如其中一個函數(shù)叫做 processPre,這個函數(shù)的作用就是用來檢測元素是否擁有v-pre 屬性,如果有v-pre 屬性則會在 element 描述對象上添加一個 pre 屬性,如下:

{
  type: 1,
  tag,
  attrsList: attrs,
  attrsMap: makeAttrsMap(attrs),
  parent,
  children: [],
  pre: true
}

總結(jié):所有process* 系列函數(shù)的作用都是為了讓一個元素的描述對象更加充實,使這個對象能更加詳細地描述一個元素, 不過我們本節(jié)主要總結(jié)解析一個開始標簽需要做的事情,所以稍后去看這些代碼的實現(xiàn)。

接下來源碼:

function checkRootConstraints(el) {
	{
		if (el.tag === 'slot' || el.tag === 'template') {
			warnOnce(
				"Cannot use <" + (el.tag) + "> as component root element because it may " +
				'contain multiple nodes.'
			);
		}
		if (el.attrsMap.hasOwnProperty('v-for')) {
			warnOnce(
				'Cannot use v-for on stateful component root element because ' +
				'it renders multiple elements.'
			);
		}
	}
}

我們知道在編寫 Vue 模板的時候會受到兩種約束,首先模板必須有且僅有一個被渲染的根元素,第二不能使用 slot 標簽和 template 標簽作為模板的根元素。

checkRootConstraints 函數(shù)內(nèi)部首先通過判斷 el.tag === 'slot' || el.tag === 'template' 來判斷根元素是否是slot 標簽或 template 標簽,如果是則打印警告信息。接著又判斷當前元素是否使用了 v-for 指令,因為v-for 指令會渲染多個節(jié)點所以根元素是不允許使用 v-for 指令的。

接下來源碼:

if (!root) {
	root = element;
	checkRootConstraints(root);
} else if (!stack.length) {
	// allow root elements with v-if, v-else-if and v-else
	if (root.if &amp;&amp; (element.elseif || element.else)) {
		checkRootConstraints(element);
		addIfCondition(root, {
			exp: element.elseif,
			block: element
		});
	} else {
		warnOnce(
			"Component template should contain exactly one root element. " +
			"If you are using v-if on multiple elements, " +
			"use v-else-if to chain them instead."
		);
	}
}

這個 if 語句先檢測 root 是否存在!我們知道 root 變量在一開始是不存在的,如果 root 不存在那說明當前元素應(yīng)該就是根元素,所以在 if 語句塊內(nèi)直接把當前元素的描述對象 element 賦值給 root 變量,同時會調(diào)用 checkRootConstraints函數(shù)檢查根元素是否符合要求。

再來看 else if 語句的條件,當 stack 為空的情況下會執(zhí)行 else if 語句塊內(nèi)的代碼, 那stack 什么情況下才為空呢?前面已經(jīng)多次提到每當遇到一個非一元標簽時就會將該標簽的描述對象放進數(shù)組,并且每當遇到一個結(jié)束標簽時都會將該標簽的描述對象從 stack 數(shù)組中拿掉,那也就是說在只有一個根元素的情況下,正常解析完成一段 html 代碼后 stack 數(shù)組應(yīng)該為空,或者換個說法,即當 stack 數(shù)組被清空后則說明整個模板字符串已經(jīng)解析完畢了,但此時 start 鉤子函數(shù)仍然被調(diào)用了,這說明模板中存在多個根元素,這時 else if 語句塊內(nèi)的代碼將被執(zhí)行:

接下來源碼:

if (root.if &amp;&amp; (element.elseif || element.else)) {
	checkRootConstraints(element);
	addIfCondition(root, {
		exp: element.elseif,
		block: element
	});
} else {
	warnOnce(
		"Component template should contain exactly one root element. " +
		"If you are using v-if on multiple elements, " +
		"use v-else-if to chain them instead."
	);
}

想要能看懂這個代碼,你需要懂一些前置知識。

[ Vue條件渲染 ] (https://cn.vuejs.org/v2/guide/conditional.html)

我們知道在編寫 Vue 模板時的約束是必須有且僅有一個被渲染的根元素,但你可以定義多個根元素,只要能夠保證最終只渲染其中一個元素即可,能夠達到這個目的的方式只有一種,那就是在多個根元素之間使用 v-if 或 v-else-if 或 v-else 。

示例代碼:

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

在回歸到代碼部分。

if (root.if && (element.elseif || element.else))

root 對象中的 .if 屬性、.elseif 屬性以及 .else 屬性都是哪里來的,它們是在通過 processIf 函數(shù)處理元素描述對象時,如果發(fā)現(xiàn)元素的屬性中有 v-if 或 v-else-if 或 v-else ,則會在元素描述對象上添加相應(yīng)的屬性作為標識。

上面代碼如果第一個根元素上有 .if 的屬性,而非第一個根元素 element 有 .elseif 屬性或者 .else 屬性,這說明根元素都是由 v-if、v-else-if、v-else 指令控制的,同時也保證了被渲染的根元素只有一個。

接下來繼續(xù)看:

if (root.if && (element.elseif || element.else)) {
	checkRootConstraints(element);
	addIfCondition(root, {
		exp: element.elseif,
		block: element
	});
} else {
	warnOnce(
		"Component template should contain exactly one root element. " +
		"If you are using v-if on multiple elements, " +
		"use v-else-if to chain them instead."
	);
}

checkRootConstraints 函數(shù)檢查當前元素是否符合作為根元素的要求,這都能理解。

addIfCondition是什么

看下它的源代碼。

function addIfCondition(el, condition) {
	if (!el.ifConditions) {
		el.ifConditions = [];
	}
	el.ifConditions.push(condition);
}

代碼很簡單,調(diào)用addIfCondition 傳遞的參數(shù) root 對象,在函數(shù)體中擴展一個屬性addIfCondition, root.addIfCondition 屬性值是一個對象。 此對象中有兩個屬性exp、block。實際上該函數(shù)是一個通用的函數(shù),不僅僅用在根元素中,它用在任何由 v-if、v-else-if 以及 v-else 組成的條件渲染的模板中。

通過如上分析我們可以發(fā)現(xiàn),具有 v-else-if 或 v-else 屬性的元素的描述對象會被添加到具有 v-if 屬性的元素描述對象的 .ifConnditions 數(shù)組中。

舉個例子,如下模板:

<div v-if="A"></div>
<div v-else-if="B"></div>
<div v-else-if="C"></div>
<div v-else></div>

解析后生成的 AST 如下(簡化版):

{
  type: 1,
  tag: 'div',
  ifConditions: [
    {
      exp: 'A',
      block: { type: 1, tag: 'div' /* 省略其他屬性 */ }
    },
    {
      exp: 'B',
      block: { type: 1, tag: 'div' /* 省略其他屬性 */ }
    },
    {
      exp: 'C',
      block: { type: 1, tag: 'div' /* 省略其他屬性 */ }
    },
    {
      exp: 'undefined',
      block: { type: 1, tag: 'div' /* 省略其他屬性 */ }
    }
  ]
  // 省略其他屬性...
}

假如當前元素不滿足條件:root.if && (element.elseif || element.else) ,那么在非生產(chǎn)環(huán)境下會打印了警告信息。

接下來源碼:

if (currentParent && !element.forbidden) {
	if (element.elseif || element.else) {
		processIfConditions(element, currentParent);
	} else if (element.slotScope) { // scoped slot
		currentParent.plain = false;
		var name = element.slotTarget || '"default"';
		(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
	} else {
		currentParent.children.push(element);
		element.parent = currentParent;
	}
}
if (!unary) {
	currentParent = element;
	stack.push(element);
} else {
	closeElement(element);
}

我們先從下往上講, 為什么呢?原因是在解析根元素的時候currentParent并沒有賦值。

!unary 表示解析的是非一元標簽,此時把該元素的描述對象添加到stack 棧中,并且將 currentParent 變量的值更新為當前元素的描述對象。如果一個元素是一元標簽,那么應(yīng)該調(diào)用 closeElement 函數(shù)閉合該元素。

老生常談的總結(jié):每當遇到一個非一元標簽都會將該元素的描述對象添加到stack數(shù)組,并且currentParent 始終存儲的是 stack 棧頂?shù)脑?,即當前解析元素的父級?/p>

if (currentParent && !element.forbidden) {
	if (element.elseif || element.else) {
		processIfConditions(element, currentParent);
	} else if (element.slotScope) { // scoped slot
		currentParent.plain = false;
		var name = element.slotTarget || '"default"';
		(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
	} else {
		currentParent.children.push(element);
		element.parent = currentParent;
	}
}

這里的條件要成立,則說明當前元素存在父級( currentParent ),并且當前元素不是被禁止的元素。

常見的情況如下:

if (currentParent && !element.forbidden) {
        if (element.elseif || element.else) {
         //...
	} else if (element.slotScope) { // scoped slot
	 //...
	} else {
		currentParent.children.push(element);
		element.parent = currentParent;
	}
}

在 else 語句塊內(nèi),會把當前元素描述對象添加到父級元素描述對象 ( currentParent ) 的children 數(shù)組中,同時將當前元素對象的 parent 屬性指向父級元素對象,這樣就建立了元素描述對象間的父子級關(guān)系。

如果一個標簽使用 v-else-if 或 v-else 指令,那么該元素的描述對象實際上會被添加到對應(yīng)的v-if 元素描述對象的 ifConditions 數(shù)組中,而非作為一個獨立的子節(jié)點,這個工作就是由如下代碼完成:

if (currentParent && !element.forbidden) {
	if (element.elseif || element.else) {
		processIfConditions(element, currentParent);
	} else if (element.slotScope) { // scoped slot
		currentParent.plain = false;
		var name = element.slotTarget || '"default"';
		(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
	} else {
	  //...
	}
}

如當前解析的元素使用了 v-else-if 或 v-else 指令,則會調(diào)用 processIfConditions 函數(shù),同時將當前元素描述對象 element 和父級元素的描述對象 currentParent 作為參數(shù)傳遞:

processIfConditions 源碼

function processIfConditions(el, parent) {
	var prev = findPrevElement(parent.children);
	if (prev && prev.if) {
		addIfCondition(prev, {
			exp: el.elseif,
			block: el
		});
	} else {
		warn$2(
			"v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " +
			"used on element <" + (el.tag) + "> without corresponding v-if."
		);
	}
}

findPrevElement 函數(shù)是去查找到當前元素的前一個元素描述對象,并將其賦值給 prev 常量,addIfCondition 不用多說如果prev 、prev.if 存在,調(diào)用 addIfCondition 函數(shù)在當前元素描述對象添加 ifConditions 屬性,傳入的對象存儲相關(guān)信息。

如果當前元素沒有使用 v-else-if 或 v-else 指令,那么還會判斷當前元素是否使用了 slot-scope 特性,如下:

if (currentParent && !element.forbidden) {
	if (element.elseif || element.else) {
          //...
	} else if (element.slotScope) { // scoped slot
		currentParent.plain = false;
		var name = element.slotTarget || '"default"';
		(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
	} else {
	  //...
	}
}

如果一個元素使用了 slot-scope 特性,那么該元素的描述對象會被添加到父級元素的scopedSlots 對象下,也就是說使用了 slot-scope 特性的元素與使用了v-else-if 或 v-else 指令的元素一樣,他們都不會作為父級元素的子節(jié)點,對于使用了 slot-scope 特性的元素來講它們將被添加到父級元素描述對象的 scopedSlots 對象下。

自 2.6.0 起有所更新。已廢棄的使用slot-scope 特性的語法在這里。所以此塊內(nèi)容就不鋪開來講了,有興趣的同學(xué)可以去了解下,更多關(guān)于vue parseHTML start鉤子函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue自定義多選組件使用詳解

    Vue自定義多選組件使用詳解

    這篇文章主要為大家詳細介紹了Vue自定義多選組件的使用,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • vue3封裝echarts組件的實現(xiàn)步驟

    vue3封裝echarts組件的實現(xiàn)步驟

    這篇文章主要介紹了如何在Vue3中封裝一個高效、可復(fù)用的ECharts組件TChart,該組件支持響應(yīng)式圖表、空數(shù)據(jù)展示、事件監(jiān)聽、主題切換和性能優(yōu)化等功能,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-01-01
  • vite項目配置less全局樣式的實現(xiàn)步驟

    vite項目配置less全局樣式的實現(xiàn)步驟

    最近想實現(xiàn)個項目,需要配置全局less,本文主要介紹了vite項目配置less全局樣式的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • 基于vite2+Vue3編寫一個在線幫助文檔工具

    基于vite2+Vue3編寫一個在線幫助文檔工具

    提起幫助文檔,想必大家都會想到?VuePress等。但是VuePress是“靜態(tài)網(wǎng)站生成器”,需要我們自行編寫文檔,然后交給VuePress變成網(wǎng)站。因此,本文將用vite2+Vue3編寫一個在線幫助文檔工具,需要的可以參考一下
    2022-03-03
  • 詳解vue3中watch監(jiān)聽的幾種情況

    詳解vue3中watch監(jiān)聽的幾種情況

    watch是CompositionAPI的一部分,用于監(jiān)聽響應(yīng)式數(shù)據(jù)的變化并執(zhí)行相應(yīng)的操作,本文主要介紹了詳解vue3中watch監(jiān)聽的幾種情況,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • vue echarts實現(xiàn)綁定事件和解綁事件

    vue echarts實現(xiàn)綁定事件和解綁事件

    這篇文章主要介紹了vue echarts實現(xiàn)綁定事件和解綁事件方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Vue微信小程序和uniapp配置環(huán)境地址

    Vue微信小程序和uniapp配置環(huán)境地址

    在微信小程序中,可以使用全局配置和使用開發(fā)、體驗、生產(chǎn)環(huán)境的地址,這篇文章主要介紹了Vue微信和uniapp配置環(huán)境地址,需要的朋友可以參考下
    2023-07-07
  • electron實現(xiàn)打印功能支持靜默打印、無感打印

    electron實現(xiàn)打印功能支持靜默打印、無感打印

    使用electron開發(fā)應(yīng)用遇到了打印小票的功能,實現(xiàn)途中還是幾經(jīng)波折,下面這篇文章主要給大家介紹了關(guān)于electron實現(xiàn)打印功能支持靜默打印、無感打印的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • vue 如何添加全局函數(shù)或全局變量以及單頁面的title設(shè)置總結(jié)

    vue 如何添加全局函數(shù)或全局變量以及單頁面的title設(shè)置總結(jié)

    本篇文章主要介紹了vue 如何添加全局函數(shù)或全局變量以及單頁面的title設(shè)置總結(jié),非常具有實用價值,需要的朋友可以參考下
    2017-06-06
  • Vue.js實現(xiàn)簡單動態(tài)數(shù)據(jù)處理

    Vue.js實現(xiàn)簡單動態(tài)數(shù)據(jù)處理

    本篇文章主要介紹了Vue.js實現(xiàn)簡單動態(tài)數(shù)據(jù)處理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02

最新評論

夏津县| 宁河县| 大城县| 吕梁市| 黎川县| 罗田县| 日喀则市| 麻阳| 泰顺县| 乌兰察布市| 东台市| 武清区| 义乌市| 枞阳县| 奎屯市| 安康市| 始兴县| 黑河市| 绥江县| 宁津县| 崇礼县| 新田县| 甘孜| 普安县| 海伦市| 那坡县| 南宫市| 汝阳县| 西贡区| 广丰县| 历史| 赤水市| 沙河市| 浦东新区| 民和| 深水埗区| 台州市| 靖江市| 大田县| 淮安市| 桐城市|