当前位置: 首页 > Web前端 > HTML

你可以在没有反应的情况下使用jsx吗?

时间:2023-03-29 11:28:30 HTML

原创自定义jsx解析工厂函数问题我们一般在react项目中使用jsx。当我们习惯了jsx的语法后,可能离不开jsx。如果我们不在react中但是如果想在原生js项目中使用jsx怎么办?解决方案:React官方发布了新的jsx解析器,剥离jsx-runtime单独解析jsx,不使用react使用jsx,并与babel和typescript官方合作,使用babel或ts配置解析jsx,都提供了简洁的配置方案.或者,我们也可以自定义一个jsx解析函数。方案一:babel配置先安装依赖包npmupdate@babel/core@babel/preset-react然后配置babel.config.json文件{"presets":[["@babel/preset-react",{"runtime":"automatic"}]]}方案二:typescript配置需要在ts中使用jsx,即使用tsx。Typescript4.1支持React17的jsx和jsxs工厂函数的tsconfig.json配置在开发环境中使用"jsx":"react-jsxdev"在生产环境中使用"jsx":"react-jsx"例如://./src/tsconfig.json{"compilerOptions":{"module":"esnext","target":"es2015","jsx":"react-jsx","strict":true},"include":["./**/*"]}方案三:自定义tsx工厂函数自定义一套jsx解析工厂函数,了解jsx解析过程。下面演示一个ts版本的tsx解析函数。先定义一个jsxFactory.ts使用定义和导出工厂函数//---jsxFactory.ts---/*https://gist.github.com/borestad/eac42120613bc67a3714f115e8b485a7*Customjsxparser*json*tsconfig*{*"jsx":"react",*"jsxFactory":"h",*"lib":[*"es2017",*"dom",*"dom.iterable"*]*}**/interfaceentityMapData{[key:string]:string;}exportconstentityMap:entityMapData={"&":"amp","<":"lt",">":"gt",'"':"quot","'":"#39","/":"#x2F",};exportconstescapeHtml=(str:object[]|string)=>String(str).replace(/[&<>"'\/\\]/g,(s)=>`&${entityMap[s]};`);//为了与ReactDOM保持一定的一致性,让我们使用映射器//https://reactjs.org/docs/dom-elements.htmlexportconstAttributeMapper=(val:string)=>({tabIndex:"tabindex",className:"class",readOnly:"readonly",}[val]||val);//tslint:disable-next-line:no-default-exportexport函数DOMcreateElement(标签:功能|string,attrs?:{[key:string]:any},...children:(HTMLElement|string)[]):HTMLElement{attrs=attrs||}{};conststack:any[]=[...children];//支持组件(ish)if(typeoftag==="function"){attrs.children=stack;返回标签(属性);}constelm=document.createElement(标签);//添加属性for(let[name,val]ofObject.entries(attrs)){name=escapeHtml(AttributeMapper(name));if(name.startsWith("on")&&name.toLowerCase()inwindow){elm.addEventListener(name.toLowerCase().substr(2),val);}elseif(name==="ref"){val(elm);}elseif(name==="style"){Object.assign(elm.style,val);}elseif(val===true){elm.setAttribute(name,name);}elseif(val!==false&&val!=null){elm.setAttribute(name,escapeHtml(val));}elseif(val===false){elm.removeAttribute(name);}}//追加孩子while(stack.length){constchild=stack.shift();//孩子是一片叶子吗?if(!Array.isArray(child)){elm.appendChild((childasHTMLElement).nodeType==null?document.createTextNode(child.toString()):child);}else{stack.push(...child);}}returnelm;}exportconstDOMcreateFragment=(attrs?:{[key:string]:any},...children:(HTMLElement|string)[]):(HTMLElement|string)[]=>{返回孩子;};支持工厂函数d.ts声明文件//---jsxFactory.d.ts---declarenamespaceJSX{typeElement=string;interfaceIntrinsicElements{[eleName:string]:any;}}然后将jsx配置添加到tsconfig.json{"compilerOptions":{//...其他配置"jsx":"preserve","jsxFactory":"DOMcreateElement","jsxFragmentFactory":"DOMcreateFragment",}}比如下面的引用{"compilerOptions":{"rootDir":"src","outDir":"lib","target":"ESNext","useDefineForClassFields":true,"module":"ESNext",“lib”:[“ESNext”,“dom”,“dom.iterable"],"moduleResolution":"Node","strict":true,"sourceMap":true,"resolveJsonModule":true,"esModuleInterop":true,"noEmit":true,"noUnusedLocals":true,"noUnusedParameters":true,"noImplicitReturns":true,"declaration":true,"declarationDir":"./lib","declarationMap":true,"baseUrl":"./","jsx":"preserve","jsxFactory":"DOMcreateElement","jsxFragmentFactory":"DOMcreateFragment","allowJs":true},"include":["./src"]}一般来说,这个可以用在每个.tsx的末尾,引入即可DOMcreateElement在文件中,但是如果使用esbuild,也可以在esbuild配置中自动注入DOMcreateElement和DOMcreateFragment的引用esbuild配置,下面的例子中@/helper/jsxFactory是jsxFactory.ts所在的目录。:{jsxFactory:"DOMcreateElement",jsxFragment:"DOMcreateFragment",jsxInject:`import{DOMcreateElement,DOMcreateFragment}from'@/helper/jsxFactory';`,}解决方案4:简单jsxfactoryfunctionjsx简洁版,可以按照这个简单版自定义和扩展版一个constappendChild=(parent,child)=>{if(Array.isArray(child))child.forEach((nestedChild)=>appendChild(parent,nestedChild));elseparent.appendChild(child.nodeType?child:document.createTextNode(child));};exportconstDOMcreateElement=(tag,props,...children)=>{if(typeoftag==='function')返回标签(道具,孩子);constelement=document.createElement(标签);Object.entries(props||{}).forEach(([name,value])=>{if(name.startsWith('on')&&name.toLowerCase()inwindow){element.addEventListener(name.toLowerCase().substr(2),value);}else{element[name]=value;//element.setAttribute(name,value.toString());}});children.forEach((child)=>{appendChild(element,child);});returnelement;};exportconstDOMcreateFragment=(props,...children)=>{returnchildren;};版本二/***确保我们不会使用空值的辅助函数*@paramval*@paramfallback*/functionnonNull(val,fallback){returnBoolean(val)?val:fallback;}/***我们如何处理孩子名词孩子可以是:*1.调用DOMcreateElement,返回一个节点*2.文本内容,返回一个文本*@paramchildren*/functionDOMparseChildren(children){returnchildren.map((child)=>{if(typeofchild==='string'){returndocument.createTextNode(child);}returnchild;});}/***我们如何处理常规节点。*1.我们创建一个元素*2.我们将JSX的所有属性应用到这个DOM节点*3.如果可用,我们追加所有子元素。*@paramelement*@paramproperties*@paramchildren*/functionDOMparseNode(element,properties,children){constel=document.createElement(element);Object.keys(nonNull(properties,{})).forEach((key)=>{el[key]=properties[key];});DOMparseChildren(children).forEach((child)=>{el.appendChild(child);});returnel;}/***我们的入口函数。*1.元素是一个函数,而不是一个功能组件。*我们调用这个函数(传递props和children当然)*并返回结果。我们期望一个Node*2类型的返回值。如果元素是一个字符串,我们解析一个常规节点*@paramelement*@paramproperties*@paramchildren*/exportfunctionDOMcreateElement(element,properties,...children){if(typeofelement==='function'){returnelement({...nonNull(properties,{}),children,});}returnDOMparseNode(element,properties,children);}原创自定义jsx解析工厂函数参考介绍新的JSX转换React17JSXFactoriesBuildyourownReactYoudon'tneedReacttouseJSX