UNPKG

hm-react-cli

Version:

Create a Huimei React project by module

330 lines (316 loc) 9.69 kB
import { NAMESPACE } from "./browser"; import { patchStyle } from "./style"; import { eventAction, rform } from "./event"; import { typeNumber, emptyObject, noop } from "react-core/util"; //import { duplexAction } from './duplex'; import { DUPLEX } from "react-fiber/effectTag"; //布尔属性的值末必为true,false //https://github.com/facebook/react/issues/10589 let isSpecialAttr = { style: 1, autoFocus: 1, innerHTML: 1, dangerouslySetInnerHTML: 1 }; let svgCache = {}; let strategyCache = {}; /** * 仅匹配 svg 属性名中的第一个驼峰处,如 viewBox 中的 wB, * 数字表示该特征在属性列表中重复的次数 * -1 表示用 ":" 隔开的属性 (xlink:href, xlink:title 等) * https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute */ let svgCamelCase = { w: { r: 1, b: 1, t: 1 }, e: { n: 1, t: 1, f: 1, p: 1, c: 1, m: 1, a: 2, u: 1, s: 1, v: 1 }, o: { r: 1 }, c: { m: 1 }, p: { p: 1 }, t: { s: 2, t: 1, u: 1, c: 1, d: 1, o: 1, x: 1, y: 1, l: 1 }, l: { r: 1, m: 1, u: 1, b: -1, l: -1, s: -1 }, r: { r: 1, u: 2, h: 1, w: 1, c: 1, e: 1 }, h: { r: 1, a: 1, l: 1, t: 1 }, y: { p: 1, s: 1, t: 1, c: 1 }, g: { c: 1 }, k: { a: -1, h: -1, r: -1, s: -1, t: -1, c: 1, u: 1 }, m: { o: 1, l: 1, a: 1 }, n: { c: 1, t: 1, u: 1 }, s: { a: 3 }, f: { x: 1, y: 1 }, d: { e: 1, f: 1, m: 1, d: 1 }, x: { c: 1 } }; // SVG 属性列表中驼峰命名和短横线分隔命名特征值有重复 // 列出了重复特征中的短横线命名的属性名 let specialSVGPropertyName = { "overline-thickness": 2, "underline-thickness": 2, "overline-position": 2, "underline-position": 2, "stroke-miterlimit": 2, "baseline-shift": 2, "clip-path": 2, "font-size": 2, "font-size-adjust": 2, "font-stretch": 2, "font-style": 2, "text-decoration": 2, "vert-origin-x": 2, "vert-origin-y": 2, "paint-order": 2, "fill-rule": 2, "color-rendering": 2, "marker-end": 2, "pointer-events": 2, "units-per-em": 2, "strikethrough-thickness": 2, "lighting-color": 2 }; // 重复属性名的特征值列表 let repeatedKey = [ "et", "ep", "em", "es", "pp", "ts", "td", "to", "lr", "rr", "re", "ht", "gc" ]; function createRepaceFn(split) { return function(match) { return match.slice(0, 1) + split + match.slice(1).toLowerCase(); }; } let rhump = /([a-z])([A-Z])/; let toHyphen = createRepaceFn("-"); let toColon = createRepaceFn(":"); function getSVGAttributeName(name) { if (svgCache[name]) { return svgCache[name]; } const match = name.match(rhump); if (!match) { return (svgCache[name] = name); } const prefix = match[1]; const postfix = match[2].toLowerCase(); const orig = name; const count = svgCamelCase[prefix] && svgCamelCase[prefix][postfix]; if (count) { if (count === -1) { return (svgCache[orig] = { name: name.replace(rhump, toColon), ifSpecial: true }); } if (~repeatedKey.indexOf(prefix + postfix)) { const dashName = name.replace(rhump, toHyphen); if (specialSVGPropertyName[dashName]) { name = dashName; } } } else { name = name.replace(rhump, toHyphen); } return (svgCache[orig] = name); } export function diffProps(dom, lastProps, nextProps, fiber) { let isSVG = fiber.namespaceURI === NAMESPACE.svg; let tag = fiber.type; let continueProps = skipProps; if (!isSVG && rform.test(fiber.type)) { continueProps = duplexProps; if (!("onChange" in nextProps)) { eventAction(dom, "onChange", noop, lastProps, fiber); } fiber.effectTag *= DUPLEX; fiber.onDuplex = continueProps.onDuplex; } //eslint-disable-next-line for (let name in nextProps) { if (continueProps[name]) { continue; } let val = nextProps[name]; if (val !== lastProps[name]) { let which = tag + isSVG + name; let action = strategyCache[which]; if (!action) { action = strategyCache[which] = getPropAction(dom, name, isSVG); } actionStrategy[action](dom, name, val, lastProps, fiber); } } //如果旧属性在新属性对象不存在,那么移除DOM eslint-disable-next-line for (let name in lastProps) { if (continueProps[name]) { continue; } if (!nextProps.hasOwnProperty(name)) { let which = tag + isSVG + name; let action = strategyCache[which]; if (!action) { continue; } actionStrategy[action](dom, name, false, lastProps, fiber); } } // fiber.onDuplex = continueProps.onDuplex // continueProps.onDuplex(dom, fiber, nextProps, lastProps); } function isBooleanAttr(dom, name) { let val = dom[name]; return val === true || val === false; } function isEventName(name) { return /^on[A-Z]/.test(name); } /** * 根据一个属性所在的元素或元素的文档类型,就可以永久决定该使用什么策略操作它 * * @param {any} dom 元素节点 * @param {any} name 属性名 * @param {any} isSVG * @returns */ function getPropAction(dom, name, isSVG) { if (isSVG && name === "className") { return "svgClass"; } if (isSpecialAttr[name]) { return name; } if (isEventName(name)) { return "event"; } if (isSVG) { return "svgAttr"; } //img.width = '100px'时,取img.width为0,必须用setAttribute if (name === "width" || name === "height") { return "attribute"; } if (isBooleanAttr(dom, name)) { return "booleanAttr"; } return name.indexOf("data-") === 0 || dom[name] === void 666 ? "attribute" : "property"; } let builtinStringProps = { className: 1, title: 1, name: 1, type: 1, alt: 1, lang: 1 }; var skipProps = { innerHTML: 1, children: 1, onDuplex: noop }; var duplexProps = { // onDuplex: duplexAction, value: 1, defaultValue: 1, checked: 1, // defaultChecked: 1, innerHTML: 1, children: 1 }; export let actionStrategy = { style: function(dom, _, val, lastProps) { patchStyle(dom, lastProps.style || emptyObject, val || emptyObject); }, autoFocus: function(dom) { if ( /input|text/i.test(dom.nodeName) || dom.contentEditable === "true" ) { dom.focus(); } }, svgClass: function(dom, name, val) { if (!val) { dom.removeAttribute("class"); } else { dom.setAttribute("class", val); } }, svgAttr: function(dom, name, val) { // http://www.w3school.com.cn/xlink/xlink_reference.asp // https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#notable-enh // a ncements xlinkActuate, xlinkArcrole, xlinkHref, xlinkRole, xlinkShow, // xlinkTitle, xlinkType eslint-disable-next-line let method = typeNumber(val) < 3 && !val ? "removeAttribute" : "setAttribute"; let nameRes = getSVGAttributeName(name); if (nameRes.ifSpecial) { let prefix = nameRes.name.split(":")[0]; // 将xlinkHref 转换为 xlink:href dom[method + "NS"](NAMESPACE[prefix], nameRes.name, val || ""); } else { dom[method](nameRes, typeNumber(val) !== 3 && !val ? "" : val); } }, booleanAttr: function(dom, name, val) { // 布尔属性必须使用el.xxx = true|false方式设值 如果为false, IE全系列下相当于setAttribute(xxx,""), // 会影响到样式,需要进一步处理 eslint-disable-next-line dom[name] = !!val; if (dom[name] === false) { dom.removeAttribute(name); } else if (dom[name] === "false") { //字符串属性会将它转换为false dom[name] = ""; } }, attribute: function(dom, name, val) { if (val == null || val === false) { return dom.removeAttribute(name); } try { dom.setAttribute(name, val); } catch (e) { console.warn("setAttribute error", name, val); // eslint-disable-line } }, property: function(dom, name, val) { // 尝试直接赋值,部分情况下会失败,如给 input 元素的 size 属性赋值 0 或字符串 // 这时如果用 setAttribute 则会静默失败 try { if (!val && val !== 0) { //如果是假值但不是0,就改成“”,alt不能removeAttribute if (builtinStringProps[name]) { dom[name] = ""; } else { dom.removeAttribute(name); } } else { dom[name] = val; } } catch (e) { try { //修改type会引发多次报错 dom.setAttribute(name, val); } catch (e) { /*ignore*/ } } }, event: eventAction, dangerouslySetInnerHTML: function(dom, name, val, lastProps) { let oldhtml = lastProps[name] && lastProps[name].__html; let html = val && val.__html; html = html == null ? "" : html; if (html !== oldhtml) { dom.innerHTML = html; } } };