引言
Q&A
补充
动态加载 js, css
参考:
动态加载css,js,动态创建link标签,script标签_小龚的博客-CSDN博客_动态加载script标签
JS动态引入js、CSS动态创建script/link/style标签 - 岁月淡忘了谁 - 博客园
如何实现JavaScript动态加载CSS和JS文件_javascript技巧_脚本之家
utils.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 var utils = {}; utils.dynamicLoad = { css: (href, onloadCallbackStr) => { let loadStr = ` (function () { var hm = document.createElement("link"); hm.href = "{{hbeSeoContent}}#123;href}"; // 注意: 需要设置以下两项, 浏览器才会加载 hm.setAttribute("rel", "stylesheet"); hm.setAttribute("type", "text/css"); hm.onload = () => { {{hbeSeoContent}}#123;onloadCallbackStr}; // 加载完就删除 // hm.remove(); }; var heads = document.getElementsByTagName("head"); if(heads.length) heads[0].appendChild(hm); else document.documentElement.appendChild(hm); })();`; return loadStr; }, js: (src, onloadCallbackStr) => { let loadStr = ` (function () { var hm = document.createElement("script"); hm.src = "{{hbeSeoContent}}#123;src}"; hm.onload = () => { {{hbeSeoContent}}#123;onloadCallbackStr}; // 加载完就删除 // hm.remove(); }; var heads = document.getElementsByTagName("head"); if(heads.length) heads[0].appendChild(hm); else document.documentElement.appendChild(hm); })();`; return loadStr; } }; export default utils;
JavaScript 查找 注释节点: <!-- 注释节点值 -->
参考:
在整个body中找到所有的注释(通过JS来操作节点寻找)-CSDN社区
1 2 3 4 5 6 7 8 9 // 从 body 开始向内查找 var bodyElement = document.getElementsByTagName("body")[0]; for (var i=0; i<bodyElement.childNodes.length; i++) { // 注释节点 类型 为 8 if (bodyElement.childNodes[i].nodeType == 8) { console.log(bodyElement.childNodes[i].nodeValue); } }
但是这个只能找到body的子节点是注释的情况,
当然,就这样一直循环下去也可以找到body子节点的子节点是注释的情况,但是如果在很深的DOM节点里,用这种循环的方式就很不好书写。
所以我想到用递归来写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div> <div> <div> <!-- note1 --> </div> </div> <div> <div></div> </div> </div> <div> <div> <p> <!-- note2 --> <p> </div> <div> <div></div> </div> </div> <!-- note3 --> <div> <div> <div></div> </div> <!-- note4 --> <div> <div></div> </div> </div> <script type="text/javascript"> function eachComment(ele) { for (var i=0; i<ele.childNodes.length; i++) { var child = ele.childNodes[i]; if (child.nodeType == 8) { console.log(child.nodeValue); } else if (child.childNodes) { eachComment(child); } } } var bodyElement = document.getElementsByTagName("body")[0]; eachComment(bodyElement); </script> </body> </html>
注意: <!-- PluginCore.IPlugins.IPluginWidget --> 用 console.log(child.nodeValue); 得到的值 是 PluginCore.IPlugins.IPluginWidget 两边有空格,
也 nodeValue 就是内部内容, 不会去除两边空格的
JavaScript Element.replaceWith(...nodes)
参考:
在JS中使用replaceWith将元素替换为DOMstring或多个元素 - 我爱学习网
Element.replaceWith() - Web API 接口参考 | MDN
Element.replaceWith() - Web APIs | MDN - 英文 参考 更详细
补充
经过测试, 注释节点也可以用
1 2 3 4 5 6 7 const span = document.createElement("span"); 注释节点.replaceWith(span) // 注意: 注释节点不变, 注释节点 依然指向原来的 , 而不是 replaceWith 后的 // 注意: 经测试: 不同节点.replaceWith(node1) 时, node1 对象不要重复使用, 否则可能导致想象外效果 temp1.replaceWith("<h1>aaa</h1>") // 这样会替换为 nodeValue: '<h1>aaa</h1>' 的文本节点
正确做法
1 2 3 c = parseDom("<h1>ccc</h1>") // 注意: 不要省略 "..." temp4.replaceWith(...c)
语法
我可以使用replaceWith将任意一个子跨度与多个元素和文本节点交换吗
Element.replaceWith()的签名接受数量可变的Node或DOMString参数。。。
Syntax
案例
Using replaceWith()
1 2 3 4 5 6 7 8 9 var parent = document.createElement("div"); var child = document.createElement("p"); parent.appendChild(child); var span = document.createElement("span"); child.replaceWith(span); console.log(parent.outerHTML); // "<div><span></span></div>"
Browser compatibility
Node.nodeType
参考:
Node.nodeType - Web API 接口参考 | MDN
只读属性 Node.nodeType 表示的是该节点的类型。
nodeType 属性可用来区分不同类型的节点,比如
元素 ,
文本 和
注释 。
1 var type = node.nodeType;
返回一个整数,其代表的是节点类型。其所有可能的值请参考
节点类型常量 .
例子
不同的节点类型
1 2 3 4 5 6 7 8 9 10 11 document.nodeType === Node.DOCUMENT_NODE; // true document.doctype.nodeType === Node.DOCUMENT_TYPE_NODE; // true var fragment = document.createDocumentFragment(); fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE; // true var p = document.createElement("p"); p.textContent = "很久很久以前..."; p.nodeType === Node.ELEMENT_NODE; // true p.firstChild.nodeType === Node.TEXT_NODE; // true
注释
1 2 3 4 // 该示例会检查 document 下第一个节点是不是注释,如果不是,则会提醒。 var node = document.documentElement.firstChild; if (node.nodeType != Node.COMMENT_NODE) console.log("你应该认真编写代码注释!");
Element.remove()
删除 Element 节点
字符串转 DOM 对象
参考:
javascript转换字符串为dom对象(字符串动态创建dom)_javascript技巧_脚本之家
1 2 3 4 5 function parseDom(arg) { var objE = document.createElement("div"); objE.innerHTML = arg; return objE.childNodes; };
axios
参考:
Axios
axios/axios: Promise based HTTP client for the browser and node.js
Promise based HTTP client for the browser and node.js
Using npm:
Using unpkg CDN:
1 <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
1 const axios = require('axios');
Axios API
1 2 3 4 5 6 7 8 9 // Send a POST request axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } });
1 2 3 4 5 6 7 8 9 // GET request for remote image in node.js axios({ method: 'get', url: 'http://bit.ly/2mTM3nY', responseType: 'stream' }) .then(function (response) { response.data.pipe(fs.createWriteStream('ada_lovelace.jpg')) });
The Axios Instance
1 2 3 4 5 const instance = axios.create({ baseURL: 'https://some-domain.com/api/', timeout: 1000, headers: {'X-Custom-Header': 'foobar'} });
Request Config
1 2 3 4 // `responseType` indicates the type of data that the server will respond with // options are: 'arraybuffer', 'document', 'json', 'text', 'stream' // browser only: 'blob' responseType: 'json', // default
Config Defaults
Global axios defaults
1 2 3 axios.defaults.baseURL = 'https://api.example.com'; axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
Custom instance defaults
1 2 3 4 5 6 7 8 9 10 // Set config defaults when creating the instance const instance = axios.create({ baseURL: 'https://api.example.com' }); // Alter defaults after instance has been created instance.defaults.headers.common['Authorization'] = AUTH_TOKEN; // 注意: timeout 的单位 是 ms, 因此这里是 2.5 seconds instance.defaults.timeout = 2500;
axios 例子: request.js
utils/request.js
request.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 import axios from "axios"; import qs from "qs"; // build 环境 // axios.defaults.baseURL = 'http://api.tikotiko.fun'; // axios.defaults.baseURL = 'http://localhost:4530/'; axios.defaults.baseURL = process.env.VUE_APP_API; // axios 请求拦截 - 在发送请求之前做某件事 axios.interceptors.request.use( function(request) { // 解决跨域 post 变 OPTIONS,导致 404 if (request.method === "post") { request.headers["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8"; request.data = qs.stringify({ ...request.data }); } // 如果有登录状态token的话,则添加到请求头 if (localStorage.token) { // 在 headers 中设置 Authorization 属性放token,token是存在缓存中的 request.headers.Authorization = `Bearer {{hbeSeoContent}}#123;localStorage.token}`; } return request; }, function(error) { return Promise.reject(error); } ); /** * 发送http请求 * @param {String} url 目标rl * @param {String} method 方法get,post,put etc. * @param {Object} params 对象参数 * @returns {Promise<unknown>} */ const request = function(url, method, params) { return new Promise((resolve, reject) => { if (method == "get") { axios({ url: url, method: "get", params: params }) .then(res => { res.data.status = res.status; resolve(res.data); }) .catch(err => { err.data.status = err.status; reject(err.data); }); } else { axios({ url: url, method: method, data: params }) .then(res => { res.data.status = res.status; resolve(res.data); }) .catch(err => { err.data.status = err.status; reject(err.data); }); } }); }; export default request;
使用 request.js
api/plugins.js
1 2 3 4 5 import request from "@/utils/request.js"; export page => request("/article/last", "get", { page: page }); export pluginId => request("/plugincore/admin/plugins/install", "post", { pluginId: pluginId });
axios 例子: 请求 html 字符串
参考:
axios里的responseType - 简书
关于axios 的responseType类型的设置 - 鲁班快跑 - 博客园
responseType
XMLHttpRequest
XMLHttpRequest 本身就是支持responseType
""
responseType 为空字符串时,采用默认类型 DOMString,与设置为 text 相同。
arraybuffer
response 是一个包含二进制数据的 JavaScript ArrayBuffer。
blob
response 是一个包含二进制数据的 Blob 对象 。
document
response 是一个 HTML Document 或 XML XMLDocument,这取决于接收到的数据的 MIME 类型。请参阅 XMLHttpRequest 中的 HTML 以了解使用 XHR 获取 HTML 内容的更多信息。
json
response 是一个 JavaScript 对象。这个对象是通过将接收到的数据类型视为 JSON 解析得到的。
text
response 是一个以 DOMString 对象表示的文本。
ms-stream
response 是下载流的一部分;此响应类型仅允许下载请求,并且仅受 Internet Explorer 支持。
补充:
默认 xhr 请求 会 自动 带上 Cookie
动态创建 script
F12 直接修改html页面加script标签,这样方式浏览器只会解析,不会执行js, network中也没有加载 script.src
F12 Console 通过执行 js createElement 动态创建 script 标签形式,这种方式就能加载js并执行
补充:
动态插入JS可以不阻碍 DOMContentLoad
前端存储 cookie、sessionStorage、localStorage、websql、indexeddb
参考:
前端存储cookie、sessionStorage、localStorage、websql与indexeddb_奋斗的小猪-CSDN博客
cookie
Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie。
正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,比如test1.mcrwayfun.com和test2.mcrwayfun.com,因为二者的域名不完全相同。如果想要mcrwayfun.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为.mcrwayfun.com,这样使用test1.mcrwayfun.com和test2.mcrwayfun.com就能访问同一个cookie。
一级域名又称为顶级域名,一般由字符串+后缀组成。熟悉的一级域名有baidu.com,qq.com。com,cn,net等均是常见的后缀。
二级域名是在一级域名下衍生的,比如有个一级域名为mcrfun.com,则blog.mcrfun.com和www.mcrfun.com均是其衍生出来的二级域名。
path属性决定允许访问Cookie的路径。比如,设置为"/"表示允许所有路径都可以使用Cookie。
sessionStorage
访问限制性
不同于cookie,sessionStorage的访问限制更高一些,只有当前设定sessionStorage的域下才能访问,而且不同的两个tab之间不能互通。例,我在www.qq.com下种下了sessionStorage,在wx.qq.com下是,无法访问的;
在新开的tab下,或者关闭本TAB再打开后(也是www.qq.com),也是无法访问到之前种的sessionStorage的;
而本tab刷新的时候,sessionStorage确是可以访问的。
以上种种,证明sessionStorage里面的session,并不同于cookie,是以tab为级别的session。
localStorage
访问的限制性
localStorage与sessionStorage虽然相似,但是访问限制却不尽相同,localStorage的访问域默认设定为设置localStorage的当前域,其他域名不可以取。
这点与sessionStorage相同,但是与sessionStorage不同的是,localStorage设定后,新开tab是可以访问到的。
存储时间
localStorage理论上讲是 永久性质的存储。但是,免不了用户会使用浏览器清除数据,或者浏览器有时候为了节省,去清除数据。
websql 与 indexeddb
参考
感谢帮助!