老项目使用react
不用 react 脚手架和 webpack,直接使用 react 写 demo
# 老项目使用 react
技术一直发展,可是总有项目来不及更新。当年 MVC 技术热度不减现在的 react 和 vue。几年过去了,前后端分离,各种前端框架的出现,使得部分老项目更加少人维护了,毕竟 MVC 项目的语法对前端实在是太不友好了
然而最近有一个项目就是 java web 的项目,可是新的需求的 UI 库又是基于 teambition (opens new window) 的,借此机会也顺便学习下 react。
# react 可以像 vue 一样 "渐进式" 吗?
vue 可以直接引入 vue 的依赖文件,直接在项目中使用 new Vue()
来生成 vue 的实例,所以就算离开脚手架,也只是写法上的一点小区别
那 react 可以吗?可以!只是不是用 new React 的方式
# React 的挂载方式
先看一段代码
class App extends React.Component {}
ReactDOM.render(<App />, document.querySelector('#app'))
2
这是 React 挂载的方式(当然也可以写 hook)。通过 ReactDOM.render
挂载
所以最重要的就是找到 React
和 ReactDom
的变量
用 CDN (opens new window) 找一下这 2 个库
值得一提的是:CDN 有非常多的 JS 提供选择,自己可以留意下前缀,和 js 名,调试模式下用
development.js
可以获得一些错误信息。上线的时候改用production.min.js
库会更小,一些警告之类的也不会出现。
最后我选择了 umd 的
新建一个页面,引入这 2 个库试下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React demo</title>
<script
src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.js"
crossorigin
></script>
<script
src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"
crossorigin
></script>
</head>
<body>
<div id="app"></div>
</body>
<script>
class App extends React.Component {
render() {
return <div>hello React</div>
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
</script>
</html>
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
运行发现报错了,原生的 js 并不认识 jsx 的语法
# 转义 JS
通过 webpack,我们可以把一些 JS 无法识别的东西,进过 loader 或者各种插件,转换为 JS 能识别的,那现在没 webpack 应该如何转换 jsx?
用 babel
,而且是动态转义
老规矩在 CDN 找到 babel
的库。而且必须是找 离线包 babel-standalone (opens new window)
引入:
<script
src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"
crossorigin
></script>
2
3
4
既然引入了 babel
。那 script 标签必须改成 <script type="text/babel">
才能识别
最后代码如下(多加了一些 state 之类的语法):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React demo</title>
<script
src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.js"
crossorigin
></script>
<script
src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"
crossorigin
></script>
<script
src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"
crossorigin
></script>
</head>
<body>
<div id="app"></div>
</body>
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
msg: 'hello React'
}
}
render() {
let { msg } = this.state
return <div>{msg}</div>
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
</script>
</html>
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
# 引入 UI 库
下面用 at (opens new window) 这个库作为演示
AT
这个 UI 库非常贴心的提供了 cdn 资源
直接引入
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@txdfe/at/build/teambition.min.css" />
<script src="https://cdn.jsdelivr.net/npm/@txdfe/at/build/at.min.js"></script>
2
文档中有一句话非常重要
这个库有导出一个AT
的全局变量
# 使用组件
先试下 button 组件把
这段代码肯定不能直接用在我们的项目中,毕竟还有 import
。我们也没用 nodejs 安装依赖
不过反过来想,既然 UI 库导出了一个名为 AT
的全局变量。import
也只是到 node_modules 里面帮我们找到对应的库,如果粗暴点翻译为 ES6 长啥样?更加 ES6 解构赋值的语法:
import { Button } from '@txdfe/at';
=== const { Button } = AT
<script type="text/babel">
const { Button } = AT
class App extends React.Component {
constructor() {
super()
this.state = {
msg: 'hello React'
}
}
render() {
let { msg } = this.state
return <Button>{msg}</Button>
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行成功!
而且基于 React 的语法。我们甚至都不用每次都声明,可以把上面的代码改成这样(为了做对比,我新找了一个组件):
<script type="text/babel">
const { Button } = AT
class App extends React.Component {
constructor() {
super()
this.state = {
msg: 'hello React'
}
}
onChange(checked) {
console.log(`switch to ${checked}`)
}
render() {
let { msg } = this.state
return (
<div>
<Button>{msg}</Button>
<AT.Switch onChange={this.onChange} />
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
</script>
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
可以看出,<AT.Switch>
标签可以直接用,而且各种事件也是可以兼容
接下来的写法就和 React 一样了
附上完整的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React demo</title>
<script
src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.js"
crossorigin
></script>
<script
src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"
crossorigin
></script>
<script
src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"
crossorigin
></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@txdfe/at/build/teambition.min.css" />
<script src="https://cdn.jsdelivr.net/npm/@txdfe/at/build/at.min.js"></script>
</head>
<body>
<div id="app"></div>
</body>
<script type="text/babel">
const { Button } = AT
class App extends React.Component {
constructor() {
super()
this.state = {
msg: 'hello React'
}
}
onChange(checked) {
console.log(`switch to ${checked}`)
}
render() {
let { msg } = this.state
return (
<div>
<Button>{msg}</Button>
<AT.Switch onChange={this.onChange} />
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#app'))
</script>
</html>
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
# 使用 hook 语法 和 useState
既然 React 能运行起来,hook 语法和 useState 是不是一样可以兼容?
<!-- 新增一个hook挂载节点 -->
<div id="hook"></div>
<!-- 使用hook的语法和 useState -->
<script type="text/babel">
function App2() {
const [msg, updateMsg] = React.useState('App2 Demo')
return (
<div>
<AT.Button onClick={() => updateMsg('click btn')}>{msg}</AT.Button>
<AT.Switch />
</div>
)
}
ReactDOM.render(<App2 />, document.querySelector('#hook'))
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
点击按钮前
点击按钮,使用 updateMsg 更新(注意按钮的文案已经修改了)
之前我们用 import {useState} from 'React'
之类的语法,都可以翻译为 React.xxxx
来使用
# 使用更多的 UI 库
React 的库还有一个特别喜欢的就是 Ant Design (opens new window) 了
在官方文档上没给出 CDN 的地址,所以还是到 boot CDN 上找 bootcdn - antd (opens new window)
CDN 上也是有非常多的资源,其中包括不少样式库等,我们先只引入最基础的 js 和 css
引入后,我们打印一下 window.antxxx(这时候浏览器会有提示的,根据提示我们知道了是 window.antd)
看下我们发现了什么,虽然 antd 是个 Module 对象(应该是给 import 那些用的)。可是这并不影响我们取组件!老规矩来一段 Button
<script type="text/babel">
function AntdDemo() {
return (
<div>
<antd.Button type="primary">Primary Button</antd.Button>
<antd.Button>Default Button</antd.Button>
<antd.Button type="dashed">Dashed Button</antd.Button>
<br />
<antd.Button type="text">Text Button</antd.Button>
<antd.Button type="link">Link Button</antd.Button>
</div>
)
}
ReactDOM.render(<AntdDemo />, document.querySelector('#antddemo'))
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 自定义组件该如何引入呢
我们自定义了一个组件,并且接受传入的参数,外层按钮进行触发更新
- myComponents.js
function MyCompoents(props) {
return <div>{props.msg}</div>
}
2
3
- demo 的写法:
<!-- 引入我们的组件 -->
<script src="./myComponents.js" type="text/babel"></script>
<!-- 在demo中更新 -->
<script type="text/babel">
function AntdDemo() {
const [msg, updateMsg] = React.useState('App2 Demo')
return (
<div>
<antd.Button type="primary">Primary Button</antd.Button>
<antd.Button>Default Button</antd.Button>
<antd.Button type="dashed">Dashed Button</antd.Button>
<br />
<antd.Button type="text">Text Button</antd.Button>
<antd.Button type="link">Link Button</antd.Button>
<br />
<br />
<br />
{/* 这就是我们自定义的组件 */}
<MyCompoents msg={msg}></MyCompoents>
<antd.Button type="primary" onClick={() => updateMsg('更新组件props')}>
更新组件文案
</antd.Button>
</div>
)
}
ReactDOM.render(<AntdDemo />, document.querySelector('#antddemo'))
</script>
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
# 填坑时间
# 全局变量的问题
一路写下来,是不是觉得没毛病?项目就这样用上了 React
!!
不,还漏了一个最重要的东西,全局变量
无论我们写的 hook
还是 class
的方式,我们都是直接声明,这意味着这是一个全局的变量。如果用脚手架或者 webpack,我们还能通过 export
的方法去相互隔离,但是这里没有啊~
想解决也很简单,只需要包一层方法,让他们相互隔离
像这样,使用一个立即执行函数,让 App
只留在这个立即执行的函数内部,这样就不会污染全局变量了。
;(function() {
function App() {
const [msg, updateMsg] = React.useState('App2 Demo')
return (
<div>
<antd.Button type="primary">Primary Button</antd.Button>
<antd.Button>Default Button</antd.Button>
<antd.Button type="dashed">Dashed Button</antd.Button>
<br />
<antd.Button type="text">Text Button</antd.Button>
<antd.Button type="link">Link Button</antd.Button>
<br />
<br />
<br />
<MyCompoents msg={msg}></MyCompoents>
<antd.Button type="primary" onClick={() => updateMsg('更新组件props')}>
更新组件文案
</antd.Button>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('#antddemo'))
})()
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
# 组件的全局变量问题如何解决?如果是公共的自定义组件,要通过 script 引入啊
是的,公共组件需要通过 script 变量引入,如果这时候用函数在包裹着组件,那我们引入 JS 后也拿不到组件
这时候就可以参考 UI 库的做法,我们只暴露一个全局的名称:比如 AT
,或者 antd
。那自己项目规定好名字就行
注意这里,我们把全局的组件都放在
My
变量中。而且每次 JS 引入的时候,都判断My
是否存在 然后在往My
中添加我们的组件MyCompoents
window.My = window.My || {}
;(function() {
My.MyCompoents = function(props) {
return <div>{props.msg}</div>
}
})()
2
3
4
5
6
使用的时候 <My.MyCompoents></My.MyCompoents>
<script type="text/babel">
function AntdDemo() {
const [msg, updateMsg] = React.useState('App2 Demo')
return (
<div>
<My.MyCompoents msg={msg}></My.MyCompoents>
<antd.Button type="primary" onClick={() => updateMsg('更新组件props')}>
更新组件文案
</antd.Button>
</div>
)
}
ReactDOM.render(<AntdDemo />, document.querySelector('#antddemo'))
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 更多的坑
到这一步,其实我的项目需求也写完了,毕竟老项目只是显示一下图表之类的,没有用到太多新颖的功能。那会不会还有更多的坑?应该是有的~等着自己探索和解决了
# 最后
感慨一下:旧项目能用新技术去写也是很幸福的一件事了,省去了 JQ 一个个 div 找到在赋值