没有webpack和脚手架的老项目用上React技术栈

不用 react 脚手架和 webpack,直接使用 react 写 demo

老项目使用 react

技术一直发展,可是总有项目来不及更新。当年 MVC 技术热度不减现在的 react 和 vue。几年过去了,前后端分离,各种前端框架的出现,使得部分老项目更加少人维护了,毕竟 MVC 项目的语法对前端实在是太不友好了

然而最近有一个项目就是 java web 的项目,可是新的需求的 UI 库又是基于 teambition 的,借此机会也顺便学习下 react。

react 可以像 vue 一样 “渐进式” 吗?

vue 可以直接引入 vue 的依赖文件,直接在项目中使用 new Vue() 来生成 vue 的实例,所以就算离开脚手架,也只是写法上的一点小区别
那 react 可以吗?可以!只是不是用 new React 的方式

React 的挂载方式

先看一段代码

class App extends React.Component {
    
    }
ReactDOM.render(<App />, document.querySelector('#app'))

这是 React 挂载的方式(当然也可以写 hook)。通过 ReactDOM.render 挂载

所以最重要的就是找到 ReactReactDom 的变量

CDN 找一下这 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>

运行发现报错了,原生的 js 并不认识 jsx 的语法

转义 JS

通过 webpack,我们可以把一些 JS 无法识别的东西,进过 loader 或者各种插件,转换为 JS 能识别的,那现在没 webpack 应该如何转换 jsx?

babel,而且是动态转义

老规矩在 CDN 找到 babel 的库。而且必须是找 离线包 babel-standalone

引入:

<script
  src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"
  crossorigin
></script>

既然引入了 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>

引入 UI 库

下面用 at 这个库作为演示
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>

文档中有一句话非常重要

这个库有导出一个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>

运行成功!

而且基于 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>

可以看出,<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>

使用 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>
  • 点击按钮前

  • 点击按钮,使用 updateMsg 更新(注意按钮的文案已经修改了)

之前我们用 import {useState} from 'React' 之类的语法,都可以翻译为 React.xxxx 来使用

使用更多的 UI 库

React 的库还有一个特别喜欢的就是 Ant Design

在官方文档上没给出 CDN 的地址,所以还是到 boot CDN 上找 bootcdn - antd

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>

自定义组件该如何引入呢

我们自定义了一个组件,并且接受传入的参数,外层按钮进行触发更新

  • myComponents.js
function MyCompoents(props) {
    
    
  return <div>{
    
    props.msg}</div>
}
  • 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>

填坑时间

全局变量的问题

一路写下来,是不是觉得没毛病?项目就这样用上了 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'))
})()

组件的全局变量问题如何解决?如果是公共的自定义组件,要通过 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>
  }
})()

使用的时候 <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>

更多的坑

到这一步,其实我的项目需求也写完了,毕竟老项目只是显示一下图表之类的,没有用到太多新颖的功能。那会不会还有更多的坑?应该是有的~等着自己探索和解决了

最后

感慨一下:旧项目能用新技术去写也是很幸福的一件事了,省去了 JQ 一个个 div 找到在赋值

另外一个博客地址。有空多多支持:Jioho/blog

猜你喜欢

转载自blog.csdn.net/Jioho_chen/article/details/111872375