关于jsx:为什么浏览器无法读取JSX
浏览器只能解决 JavaScript 对象,而不能读取惯例 JavaScript 对象中的 JSX。所以为了使浏览器可能读取 JSX,首先,须要用像 Babel 这样的 JSX 转换器将 JSX 文件转换为 JavaScript 对象,而后再将其传给浏览器。
浏览器只能解决 JavaScript 对象,而不能读取惯例 JavaScript 对象中的 JSX。所以为了使浏览器可能读取 JSX,首先,须要用像 Babel 这样的 JSX 转换器将 JSX 文件转换为 JavaScript 对象,而后再将其传给浏览器。
前言JSX中添加属性有什么要注意的?以及JSX中的子元素是怎么操作的?组件的大小写问题,使用拓展运算符,以及怎么循坏遍历一个对象JSX中的prop指的是什么?以及表单的labe应该要注意什么?以上问题即使自己很清楚,但是否有时却总是道不清,说不明?那么读完本文,就豁然开朗了 JSX添加特定属性自定义标签拓展了原生HTML标签不具备的能力,最大的一个用处就是属性传值,标签的属性值,可以是字符串,变量对象 例如:如下所示 const element = <div divIndex ="0"></div>当然也可以使用下面这种方式,是等价的,用一个大括号{ }将变量包裹起来 const element = <div divIndex={"0"}></div>至于更多插值表达式内容,你可以看上一节 React学习(2)-深入浅出JSX这里要提一下,在属性中嵌入javascript表达式,不要在双大括号外面加上引号,例如,下面的是错误的 const element = <div divIndex="{ variable }"></div>也就是说,对于字符串或者双大括号中的表达式,对于同一属性,不能同时使用这两种符号 注意 JSX语法是更接近Javascript而不是HTML,只是长得像而已,对于Reat中自定义组件的属性名称,使用 camelCase驼峰式命名来定义属性的名称,例如:定义JSX里的class属性className,而divindex变成 divIndex JSX中的子元素在原生HTML标签中,要是对于DOM结构树熟悉的话,理解JSX的子元素也是比较容易的 原生HTML的标签叫做节点,节点有节点属性,以及节点的内容 如果一个标签或者React组件没有内容,你是可以使用 />,单标签来闭合的,就像XML语法一样,例如如下所示 const element = <img src={ user.avatarUrl} />JSX标签里面能够包含很多个子元素 例如:如下所示 const element = ( <div> <h1 title="我是子h1元素属性的内容">我是子h1元素的节点内容</h1> <h2>欢迎关注微信itclanCoder公众号</h2> <h3>川川是全宇宙最帅的小伙子</h3> </div>)包含在开始和结束标签之间的 JSX 表达式内容将会被作为特定属性 props.children传递给外层组件 其中,有下面几种不同的方法来传递子元素 字符串字面量你可以将字符串放在开始和结束标签之间,此时 props.children就只是该字符串,对于内置的HTML元素是很有用的,但同时要注意怎么接收这个内容 <MyComponent>itclanCoder</MyComponent>上面的JSX,MyComponent的子元素内容是itclanCoder,可以通过 props.children来获取,它是一个没有转移的字符串 itclanCoder JSX会移除首尾行以及空行,与标签相邻的空行都会被删除,文本字符串之间的新航都会被压缩一个空格 所以下面的这几种写法都是等价的,但是笔者建议,该换行就换行的 <div>itclanCoder</div><div> itclanCoder</div><div> 川川 itclanCoder</div><div> itclanCoder</div>JSX子元素嵌套在React中,子元素允许由多个JSX元素组成,组件可以嵌套组件,例如:如下所示 <MyContainer> <Header /> <Navigator /> <Aside /> <Footer /></MyContainer>在React中,是可以将不同类型的子元素混合在一起的,这跟在以前写HTML是一样的 ...
背景由于表格数据比较同构,有时候列配置是动态的,此时希望使用v-for来配置column <template><el-table :data="tableData"> <el-table-column v-for="col in columns" :key="col.dataIndex" :label="col.title" :prop="col.dataIndex" :formatter="col.render" /></el-table></template><script>const PHONE_COLS = [ { title: '录音id', dataIndex: 'attach_id' }, { title: '接听客服', dataIndex: 'handle_user_info' }, { title: '呼叫类型', dataIndex: 'type_desc' }, { title: '通话时长', dataIndex: 'duration' },];const WORK_COLS = [ { title: '工单id', dataIndex: 'attach_id' }, { title: '创建客服', dataIndex: 'handle_user_info' }, { title: '创建时间', dataIndex: 'c_t' }, { title: '工单来源', dataIndex: 'source' }, { title: '工单分类', dataIndex: 'ws_type', render: (row, column, cell) => (cell || []).join(',') }];export default { data () { return { columns: PHONE_COLS, tableData: [], tableTotal: 0, } },}</script>可以看到代码比直接使用template更加简洁,另外也可以利用formatter函数处理简单的自定义字符串 ...
前言:文章不介绍任务背景知识,没有原理说明,偏向于实践的总结和经验分享。文章所有的代码是基于Vue CLI 3.x版本,不会涉及到一步步通过Webpack来配置JSX所需要的知识点。 在使用Vue开发项目时绝大多数情况下都是使用模板来写HTML,但是有些时候页面复杂又存在各种条件判断来显示/隐藏和拼凑页面内容,或者页面中很多部分存在部分DOM结构一样的时候就略显捉襟见肘,会写大量重复的代码,会出现单个.vue文件过长的情况,这个时候我们就需要更多的代码控制,这时候可以使用渲染函数。 渲染函数想必平时几乎没有人去写,因为写起来很痛苦(本人也没有写过)。更多的是在Vue中使用JSX语法。写法上和在React中差不多,但是功能上还是没有React中那么完善。 在写JSX的过程中不得考虑一个样式的问题,虽然可以直接在.vue文件中不写<tempate>部分,只写<script>和<style>部分,而不用担心样式作用域问题。但是更多的时候还是推荐直接使用.js的方式来写组件,这个时候就涉及到样式作用域的问题了。 在React的生态中,有很多CSS-IN-JS的解决方案,比如styled-jsx、emotion、styled-components等,目前最活跃和用户量最多的是styled-components,目前已经拥有良好的生态圈子。如果需要在样式中作一些像Sass/Less中的颜色计算,可以使用polished来实现,当然不止这么简单的功能。但是在Vue中可使用的方案就太少了,因为Vue使用模板来写HTML本身是开箱即用的样式scoped,在使用JSX写组件的时候就面临着样式问题,一种方案是在组件包裹<div>中取一个特殊的名字,然后样式都嵌套写在这个class下面,但是难免会遇到命名冲突的情况,而且每次还得变着花样取名称。此外,就是引入CSS-IN-JS在Vue对应的实现,但目前来看Styled-components官方提供了一个Vue版本的叫vue-styled-components和emotion的vue-emotion,但是用的人实在太少。像styled-components进行了重大更新和变化,但是Vue版本的还是最初的版本,而且有时候还出现样式不生效的情况。 接下来进入正题,从简单语法到经验分享(大牛请绕行) 基本用法首先需要约定一下,使用JSX组件命名采用首字母大写的驼峰命名方式,样式可以少的可以直接基于vue-styled-components写在同一个文件中,复杂的建议放在单独的_Styles.js_文件中,当然也可以不采用CSS-IN-JS的方式,使用Less/Sass来写,然后在文件中import进来。 下面是一个通用的骨架: import styled from 'vue-styled-components'const Container = styled.div` heigth: 100%;`const Dashboard = { name: 'Dashboard', render() { return ( <Container>内容</Container> ) }}export default Dashboard插值在JSX中使用单个括号来绑定文本插值 <span>Message: {this.messsage}</span><!-- 类似于v-html --><div domPropsInnerHTML={this.dangerHtml}/><!-- v-model --><el-input v-model={this.vm.name} />在jsx中不需要把v-model分成事件绑定和赋值二部分分开来写,因为有相应的babel插件来专门处理。 样式在JSX中可以直接使用class="xx"来指定样式类,内联样式可以直接写成style="xxx" <div class="btn btn-default" style="font-size: 12px;">Button</div><!-- 动态指定 --><div class={`btn btn-${this.isDefault ? 'default' : ''}`}></div><div class={{'btn-default': this.isDefault, 'btn-primary': this.isPrimary}}></div><div style={{color: 'red', fontSize: '14px'}}></div>遍历在JSX中没有v-for和v-if等指令的存在,这些全部需要采用Js的方式来实现 {/* 类似于v-if */}{this.withTitle && <Title />}{/* 类似于v-if 加 v-else */}{this.isSubTitle ? <SubTitle /> : <Title />}{/* 类似于v-for */}{this.options.map(option => { <div>{option.title}</div>})}事件绑定事件绑定需要在事件名称前端加上on前缀,原生事件添加nativeOn ...
React社区一直在探寻使用React语法开发小程序的方式,其中比较著名的项目有Taro,nanachi。而使用React语法开发小程序的难点主要就是在JSX语法上,JSX本质上是JS,相比于小程序静态模版来说太灵活。本文所说的新思路就是在处理JSX语法上的新思路,这是一种更加动态的处理思路,相比于现有方案,基本上不会限制任何JSX的写法,让你以真正的React方式处理小程序,希望这个新思路可以给任何有志于用React开发小程序的人带来启发。 现有思路的局限在介绍新的思路之前,我们先来看下Taro(最新版1.3),nanachi是怎么在小程序端处理JSX语法的。简单来说,主要是通过在编译阶段把JSX转化为等效的小程序wxml来把React代码运行在小程序端的。 举个例子,比如React逻辑表达式: xx && <Text>Hello</Text>将会被转化为等效的小程序wx:if指令: <Text wx:if="{{xx}}">Hello</Text>这种方式把对JSX的处理,主要放在了编译阶段,他依赖于编译阶段的信息收集,以上面为例,它必须识别出逻辑表达式,然后做对应的wx:if转换处理。 那编译阶段有什么问题和局限呢?我们以下面的例子说明: class App extends React.Component { render () { const a = <Text>Hello</Text> const b = a return ( <View> {b} </View> ) }}首先我们声明 const a = <Text>Hello</Text>,然后把a赋值给了b,我们看下最新版本Taro 1.3的转换,如下图: 这个例子不是特别复杂,却报错了。 要想理解上面的代码为什么报错,我们首先要理解编译阶段。本质上来说在编译阶段,代码其实就是‘字符串’,而编译阶段处理方案,就需要从这个‘字符串’中分析出必要的信息(通过AST,正则等方式)然后做对应的等效转换处理。 而对于上面的例子,需要做什么等效处理呢?需要我们在编译阶段分析出b是JSX片段:b = a = <Text>Hello</Text>,然后把<View>{b}</View>中的{b}等效替换为<Text>Hello</Text>。然而在编译阶段要想确定b的值是很困难的,有人说可以往前追溯来确定b的值,也不是不可以,但是考虑一下 由于b = a,那么就先要确定a的值,这个a的值怎么确定呢?需要在b可以访问到的作用域链中确定a,然而a可能又是由其他变量赋值而来,循环往复,期间一旦出现不是简单赋值的情况,比如函数调用,三元判断等运行时信息,追溯就宣告失败,要是a本身就是挂在全局对象上的变量,追溯就更加无从谈起。 所以在编译阶段 是无法简单确定b的值的。 我们再仔细看下上图的报错信息:a is not defined。 为什么说a未定义呢?这是涉及到另外一个问题,我们知道<Text>Hello</Text>,其实等效于React.createElement(Text, null, 'Hello'),而React.createElement方法的返回值就是一个普通JS对象,形如 // ReactElement对象{ tag: Text, props: null, children: 'Hello' ...}所以上面那一段代码在JS环境真正运行的时候,大概等效如下: ...
react中基本都使用JSX来开发,但JSX其实是javascript的一种语法糖。 什么是语法糖?语法糖就是提供了一种全新的方式书写代码,但是其实现原理与之前的写法相同。语法糖可以说是广泛存在于各种计算机代码中,包括C语言中的a[i]其实就是*a+i的语法糖。而今天对于我们来说,a[i]其实已经很普遍和常用了,所以也没有人提这是语法糖这回事了。因为终极来说,所有语言都是汇编语言的语法糖:) 简单说,JSX是一种更简便书写javascript的方式由于DOM结构被我们写到了javascript文件里,由javascript来生成DOM结构如果一直使用javascript来写DOM结构,那么render函数里就是一堆React.createElement这样既不美观也不实用。 但是我们必须知道,JSX本质上就是javascript在编译的时候,会由babel将JSX转化为javascript。 比如 <div className="aaa"> <span>222</span> <span>333</span></div>生成了 "use strict";React.createElement("div", { className: "aaa"}, React.createElement("span", null, "222"), React.createElement("span", null, "333"));比如 function Comp(){ return <div className='test'>test</div>}<Comp className="test2">222</Comp>生成 "use strict";function Comp() { return React.createElement("div", { className: "test" }, "test");}React.createElement(Comp, { className: "test2"}, "222");了解JSX的本质,只需要记住:JSX本质就是javascript 附录babel提供的一个在线转换JSX为javascript的地址https://babeljs.io/repl/
React.createElement语法糖JSX是一种JavaScript的语法拓展,可以使用它来进行UI的展示:const element = <h1>Hello, world!</h1>;我们一般会在组件的render方法里使用JSX进行布局和事件绑定:class Home extends Component { render() { return ( <div onClick={() => console.log(‘hello’)}> <h1>Hello, world!</h1> <Blog title=“deepred” /> </div> ); }}React的核心机制之一就是可以创建虚拟的DOM元素,利用虚拟DOM来减少对实际DOM的操作从而提升性能,JSX正是为了虚拟DOM而存在的语法糖我们在平时的组件编写中,通常都这么写:import React, { Component } from ‘react’;class Demo extends Component { render() { return ( <h1>Hello, world!</h1> ) }}然而代码里面并没有用到React,为什么要引入这个变量呢?因为JSX是React.createElement这个方法的语法糖:const element = <h1 id=“container” className=“home”>Hello</h1>;// 等价于const element = React.createElement(“h1”, { id: “container”, className: “home”}, “Hello”);推荐大家在babeljs.io上看下JSX编译后的实际效果React.createElement有三个参数:React.createElement( type, // dom类型,比如div,h1 [props], // dom属性,比如id,class,事件 […children] // 子节点,字符串或者React.createElement生成的一个对象)JSX用一种类似HTML的语法替代了比较繁琐的React.createElement纯JS方法,而@babel/preset-react插件就起到了最关键的一步:负责在webpack编译时,把所有的JSX都改成React.createElement:class Home extends Component { render() { return ( <div onClick={() => console.log(‘hello’)}> <h1>Hello, world!</h1> <Blog title=“deepred” /> </div> ); }}编译后:class Home extends Component { render() { return React.createElement(“div”, { onClick: () => console.log(‘hello’) }, React.createElement(“h1”, null, “Hello, world!”), React.createElement(Blog, { title: “deepred” })); }}在开发中,有了JSX后我们基本不怎么需要用到createElement方法,但如果我们需要实现这样一个组件:// 根据传入的type属性,渲染成相应的html元素<Tag type=“h1” id=“hello” onClick={() => console.log(‘hello’)}>this is a h1</Tag><Tag type=“p”>this is a p</Tag>我们不太可能根据type的属性,一个个if else去判断对应的标签:function Tag(props) { const { type, …other } = props; if (type === ‘h1’) { return <h1 {…other}>{props.children}</h1> } if (type === ‘p’) { return <p {…other}>{props.children}</p> }}这时,就需要用到底层的api了:function Tag(props) { const { type, …other } = props; return React.createElement(type, other, props.children);}自己实现一个JSX渲染器虚拟dom本质就是一个js对象:const vnode = { tag: ‘div’, attrs: { className: ‘container’ }, children: [ { tag: ‘img’, attrs: { src: ‘1.png’ }, children: [] }, { tag: ‘h3’, attrs: {}, children: [‘hello’] } ]}可以通过在每个文件的上方添加/** @jsx h /来告诉@babel/preset-react用h方法名代替JSX(默认方法是React.createElement)/* @jsx h /const element = <h1 id=“container” className=“home”>Hello</h1>;/* @jsx h /const element = h(“h1”, { id: “container”, className: “home”}, “Hello”);现在让我们开始创建自己的h函数吧!function h(nodeName, attributes, …args) { // 使用concat是为了扁平化args,因为args数组里面的元素可能也是数组 // h(‘div’, {}, [1, 2, 3]) h(’d’, {}, 1, 2, 3) 都是合法的调用 const children = args.length ? [].concat(…args) : null; return { nodeName, attributes, children };}const vnode = h(“div”, { id: “urusai”}, “Hello!”);// 返回// {// “nodeName”: “div”,// “attributes”: {// “id”: “urusai”// },// “children”: [// “Hello!”// ]// }h的作用就是返回一个vnode,有了vnode,我们还需要把vnode转成真实的dom:function render(vnode) { if (typeof vnode === ‘string’) { // 生成文本节点 return document.createTextNode(vnode); } // 生成元素节点并设置属性 const node = document.createElement(vnode.nodeName); const attributes = vnode.attributes || {}; Object.keys(attributes).forEach(key => node.setAttribute(key, attributes[key])); if (vnode.children) { // 递归调用render生成子节点 vnode.children.forEach(child => node.appendChild(render(child))); } return node;}现在让我们使用这两个方法吧:/* @jsx h /const vnode = <div id=“urusai”>Hello!</div>;const node = render(vnode);document.body.appendChild(node);编译转码后:/* @jsx h /const vnode = h(“div”, { id: “urusai”}, “Hello!”);const node = render(vnode);document.body.appendChild(node);我们还可以遍历数组:/* @jsx h /const items = [‘baga’, ‘hentai’, ‘urusai’];const vnode = <ul>{items.map((item, index) => <li key={index}>{item}</li>)}</ul>;const list = render(vnode);document.body.appendChild(list);编译转码后:/* @jsx h */const items = [‘baga’, ‘hentai’, ‘urusai’];const vnode = h(“ul”, null, items.map((item, index) => h(“li”, { key: index}, item)));const list = render(vnode);document.body.appendChild(list);通过h render两个函数,我们就实现了一个很简单的JSX渲染器!!!参考WTF is JSXJSX In DepthReact Without JSX ...
我在使用egg.js时,他用的模板引擎是Nunjucks,其中有个地方需要用到正则,可是官方文档基本上写了跟没写一样,官方的正则表达式。于是我便去找例子了。正则表达式在Nunjucks中使用正则表达式的示例:{% set regExp = r/^foo.*/g %}{% if regExp.test(‘foo’) %} Foo in the house!{% endif %}那么这个就会被正常显示。其他的表达式也是可以的。例如:<!– 有个后台存储的未验证的手机号码(mobile)在前端显示,如果格式正确则显示,不正确则显示“暂无” –>{% set regExp = r/^\d{11}$/g %}<span>号码:{{mobile if regExp.test(mobile) else ‘暂无’}}</span>这两个例子应该看得懂吧。正则这块我并没有看源码,因为搜索出来了,我这里参考的regex exmaple?后来发现其实很多方法文档并没有写出来,这时候可能真的需要看看源码了,有兴趣的话可以阅读下filter的源码https://github.com/mozilla/nu…