比如要把:<span>test</span>
這段代碼當做文本原樣輸出在頁面上,如果按照正常的方式,肯定會被轉義,在頁面上只能看到 text。那么要想達到預想的效果,應該怎么辦呢?
在學習 html 標簽時,知道如果要把 代碼 原樣輸出,可以用標簽 pre
+ code
處理。但這種方式不能處理:html 標簽。
1. 借助 vue
框架,可以這么做實現:
<template>
<p v-html="html" />
</template>
<script>
export default {
data () {
return {
html: '<span>test</span>'
}
}
}
</script>
2. 借助 react
框架,可以這么實現:
import React, { PureComponent } from 'react'
class Test extends PureComponent {
constructor (props) {
super(props)
this.state = {
html: '<span>test</span>'
}
}
render () {
const { content } = this.state
return (
<div>
<p dangerouslySetInnerHTML={{__html: content}} />
</div>
)
}
}
3. 如果想只用一個 html 標簽就實現,可能嗎?答案是可能的,可以用 xmp
標簽。這個標簽的作用:會將內容當做字符串輸出。
<xmp><span>test</span></xmp>
不過,這個標簽被W3C廢棄了,但各大瀏覽器依然支持該標簽。為什么被廢棄呢?被廢棄,肯定有被廢棄的緣由的。如果一定要用這個標簽,需注意:
- 若模板中包含標簽會造成標簽結束符混亂的問題,因此通過該方式存放模板時,不能包含結束標簽;
- xmp元素必須作為body的子孫元素。
4. 那么不借助xmp
標簽或框架的能力,如何自己實現呢?
// html 轉義處理
function htmlEncode (text) {
var isHtml = /[\x00`><\"'&]/;
var htmlEncode = /[\x00`><"'&]/g;
return text != null ? isHtml.test(text) && ("" + text).replace(htmlEncode, getCharEntity) || text : "";
function getCharEntity (ch) {
var charEntities = {
"&": "&",
"<": "<",
">": ">",
"\x00": "�",
"'": "'",
'"': """,
"`": "`"
};
return charEntities[ch] || (charEntities[ch] = "&#" + ch.charCodeAt(0) + ";");
}
};
let htmlCon = '<span>test</span>';
document.querySelector('#html_con').innerHTML = htmlEncode(htmlCon);
5. 特地去看了 vue
,react
源碼,想看看它們都是怎么實現的。但只找到 react
中是怎么實現的,vue
目錄太多也比較繞,沒找到。
node-modeles/react-dom/cjs/react-dom-server.browser.development.js 中 631 行。
// code copied and modified from escape-html
/**
* Module variables.
* @private
*/
var matchHtmlRegExp = /["'&<>]/;
/**
* Escapes special characters and HTML entities in a given html string.
*
* @param {string} string HTML string to escape for later insertion
* @return {string}
* @public
*/
function escapeHtml(string) {
var str = '' + string;
var match = matchHtmlRegExp.exec(str);
if (!match) {
return str;
}
var escape = void 0;
var html = '';
var index = 0;
var lastIndex = 0;
for (index = match.index; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34:
// "
escape = '"';
break;
case 38:
// &
escape = '&';
break;
case 39:
// '
escape = '''; // modified from escape-html; used to be '''
break;
case 60:
// <
escape = '<';
break;
case 62:
// >
escape = '>';
break;
default:
continue;
}
if (lastIndex !== index) {
html += str.substring(lastIndex, index);
}
lastIndex = index + 1;
html += escape;
}
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}
// end code copied and modified from escape-html
/**
* Escapes text to prevent scripting attacks.
*
* @param {*} text Text value to escape.
* @return {string} An escaped string.
*/
function escapeTextForBrowser(text) {
if (typeof text === 'boolean' || typeof text === 'number') {
// this shortcircuit helps perf for types that we know will never have
// special characters, especially given that this function is used often
// for numeric dom ids.
return '' + text;
}
return escapeHtml(text);
}
/**
* Escapes attribute value to prevent scripting attacks.
*
* @param {*} value Value to escape.
* @return {string} An escaped string.
*/
function quoteAttributeValueForBrowser(value) {
return '"' + escapeTextForBrowser(value) + '"';
}
這部分源碼,還是比如容易看懂呢。
總結:html 轉義,主要就是將 "'&<>
這幾個特殊字符轉換為 html 實體。
延伸:預防 xss 攻擊:
- 對用戶輸入進行轉義
- 獲取內容后,反轉義並domParse,過濾不安全標簽及屬性,進行xss攔截
- 不安全標簽:style、link、script、iframe、frame、img
- 不安全屬性:onerror、onclick等