anu icon indicating copy to clipboard operation
anu copied to clipboard

nanachi新的H5转译loader

Open RubyLouvre opened this issue 5 years ago • 5 comments

首先从app.js开始转译

import React from '@react';
import './pages/index/index'; //pages页面全部变成config.pages中的字符串
import './pages/aaa/index';   // pages页面全部变成config.pages中的字符串
import './pages/bbb/index';   //pages页面全部变成config.pages中的字符串

class Global extends React.Component {
    globalData = {}
    static config = {
        window: {
            navigationBarBackgroundColor: '#00afc7',
            backgroundTextStyle: 'light',
            navigationBarTitleText: 'nanachi',
            navigationBarTextStyle: 'white'
        },
        tabBar: {
            list: [ {name: 'app',pagePath: "/pages/index"}]
        }
    };
    onLaunch() {
       
        //针对快应用的全局getApp补丁
        if (this.$data && typeof global === 'object') {
            var ref = Object.getPrototypeOf(global) || global;
            var _this = this;
            this.globalData = this.$def.globalData;
            ref.getApp = function() {
                return _this;
            };
        }
        console.log('App launched');//eslint-disable-line
    }
}
// eslint-disable-next-line
export default App(new Global());

转换成

import React from '@react';
import PageWrapper from '@component/PageWrapper/index';
import './pages/index/index'; //它们不会删掉,用于webpack打包
import './pages/aaa/index';   // 它们不会删掉,用于webpack打包
import './pages/bbb/index'; 
class Global extends React.Component {
    globalData = {}
    static config = {
        window: {
            navigationBarBackgroundColor: '#00afc7',
            backgroundTextStyle: 'light',
            navigationBarTitleText: 'nanachi',
            navigationBarTextStyle: 'white'
        },
        tabBar: {
            list: [ {name: 'app',pagePath: "/pages/index"}]
        },
       pages: [
              './pages/index/index',
              './pages/aaa/index',
              './pages/bbb/index';  
      ]
    };
    onLaunch() {
        console.log('App launched');//eslint-disable-line
    }
    componentWillMount(){
         React.api.registerApp(this);// redirectTo等路由方法会调用Global的实例进行setState
         this.onLaunch() ;
         React.api.redirectTo(Global.config.pages[0])
    }
    render(){
           return <PageWrapper app={this} path={this.state.path}  query={this.state.query} />
    }
}

window.onload = function (){
    ReactDOM.render( <Global />, document.querySelector("#root" ))
}

React.api.registerApp, React.api.redirectTo 的实现

React.api.registerApp = function(app){
    this.__app = app
}
React.api.redirectTo = function({url, success,  fail,  complete} ){
  var [path, query] = getQuery(url) //自己实现
  var appInstance = this._app;
  var  appConfig = appInstance.constructor.config;
  if(appConfig.pages.indexOf(path) === -1){
     throw "没有注册该页面: "+ path
  }
  appInstance.setState({
     path,
     query, 
     success, 
     fail, 
     complete
  })
}

RubyLouvre avatar May 10 '19 08:05 RubyLouvre

PageWrapper负责渲染titleBar, titleBar, 右上角菜单, 还应该监听页面的滚动

/*
  .tabBar {
    border-style: solid;
    border-color: #ddd;
    border-top-width: 1px;
    position: fixed;
    bottom: 0;
    width: 100%;
    height: 110px;
  }

  .tabBar .tab {
    flex: 1;
    margin: 10px;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }

  .tabBar .tab image {
    width: 45px;
    height: 45px;
  }

  .tabBar .tab a {
    margin: 10px;
    border: 1px solid #eeeeee;
  }
*/
  import React from '../../ReactH5.js';
  class PageWrapper extends React.Component{
      constructor(props){
          super(props);
          this.$app = props.app;
         
          this.state = {
            tabBar: {
                list: []
            },
            titleBar: { },
            showTitleBar: true,
            pagePath: "",
            backgroundColor: "#ffffff"
          }
          setTitleAndTabs(this, this.$app);
      }
      componentWillUpdate(){
         setTitleAndTabs(this, this.$app);
      }
      onSelected(item) {
        if(item.selected){
          return false;
        }
        var page = React.api.getCurrentPage();
        if (page.onTabItemTap) {
          page.onTabItemTap(item)
        }
        React.api.navigateTo({
          url: item.pagePath
        })
      }
      render(){
          return (<div style={{
            width:"100%",
            paddingBottom: this.state.tabBar.list.length? '110px': '0px',
            backgroundColor: this.state.backgroundColor}}>
            <header style={{display: this.state.showTitleBar ? 'block': 'none'}}>
                <div class="title" style={{ 
                    color: this.state.titleBar.textColor,
                    backgroundColor: this.state.titleBar.backgroundColor
                    }} >
                  {this.state.titleBar.text}
                </div>
                <div class="menu"></div>
            </header>
          { !this.state.tabBar.list.length ? 
            null:
            <main class="tabBar" style={{backgroundColor: this.state.tabBar.backgroundColor}}>
            { this.state.tabBar.list.map(
                function(item ){
                  return  <div class="tab" onClick={ this.onSelected.bind(this,item)}>
                    <img src={ item.selected ? item.selectedIconPath : item.iconPath } />
                    <span style={{
                           color: item.selected ? tabBar.selectedColor: tabBar.color ,
                           fontSize: "20px"
                         }}>
                      {item.text}
                    </span>
                    </div>
            })
        }</main> }
        </div>)
      }
  }

  function setTitleAndTabs(instance, app) {
      var path = app.state.path;
      var PageClass = React.api.__pages[path];
      var appConfig =  app.constructor.config || {}
      var pageConfig = PageClass.config || {}
      var mixin = Object.assign({
        navigationBarTitleText: "",
        navigationBarTextStyle: "white",
        navigationBarBackgroundColor: "#000000"
      }, appConfig, pageConfig);

      instance.setState({
          showTitleBar: mixin.navigationStyle !== "custom",
          backgroundColor: mixin.backgroundColor || "#ffffff",
          titleBar: {
            text: mixin.navigationBarTitleText,
            textColor: mixin.navigationBarTextStyle,
            backgroundColor: mixin.navigationBarBackgroundColor,
          }
      })

      var tabBar = pageConfig.tabBar || appConfig.tabBar;
      if (tabBar && tabBar.list && tabBar.list.length) {
          tabBar.backgroundColor = tabBar.backgroundColor || "#f9faf5";
          tabBar.color = tabBar.color || "#000";
          tabBar.selectedColor = tabBar.selectedColor || "#48adc4";
          tabBar.list.forEach(function(el, i){
            if(!el.pagePath){
               console.warn(`tabBar.list[${i}] miss pagePath`, el);//eslint-disable-line
               return
            }
            el.selected = trimPagePath(el.pagePath) === trimPagePath(instance.pagePath);
          })
          instance.setState({
            tabBar: tabBar
          })
      }   
  }
  export default PageWrapper

RubyLouvre avatar May 10 '19 09:05 RubyLouvre

页面的转换

import React from '@react';
//import Welcome from '@components/Welcome/index';
import {GlobalTheme} from '@common/GlobalTheme';
import Layout from '@components/Layout/index';
import AnotherComponent from '@components/AnotherComponent/index';
import './index.scss';
class P extends React.Component {
    componentDidMount() {
        // eslint-disable-next-line
        console.log('page did mount!');
    }
    render() {
        this.props.anyVar = {color:'red'}
        return  <div class = 'page' >
                 <GlobalTheme.Provider value={this.props.anyVar}>
                   <Layout>
                       <AnotherComponent />
                   </Layout>
                </GlobalTheme.Provider>
                </div>
    }
}

export default P;

变成

import React from '@react';
import {GlobalTheme} from '@common/GlobalTheme';
import Layout from '@components/Layout/index';
import AnotherComponent from '@components/AnotherComponent/index';
import './index.scss';
class P extends React.Component {
    componentDidMount() {
        // eslint-disable-next-line
        console.log('page did mount!');
    }
    render() {
        this.props.anyVar = {color:'red'}
        return  <div class = 'page' >
                 <GlobalTheme.Provider value={this.props.anyVar}>
                   <Layout>
                       <AnotherComponent />
                   </Layout>
                </GlobalTheme.Provider>
                </div>
    }
}

export default P;

转换成

import React from "../../ReactH5.js";
import { GlobalTheme } from "../../common/GlobalTheme.js";
import Layout from '@components/Layout/index';
import AnotherComponent from '@components/AnotherComponent/index';
import './index.scss';
function P() {}

P = React.toClass(P, React.Component, {
  componentDidMount: function () {
    console.log('page did mount!');
  },
  render: function () {
      return <div class = 'page' >
                 <GlobalTheme.Provider value={this.props.anyVar}>
                   <Layout>
                       <AnotherComponent />
                   </Layout>
                </GlobalTheme.Provider>
                </div>
  },
  classUid: "c1004"
}, {});
//Page(React.registerPage(P, "pages/index/index"));
React.registerPage(P, "pages/index/index")
export default P;

React.registerPage 的实现。

React.registerPage = function( PageClass, path){
    this.api.__pages[path] = PageClass;
    return PageClass;
}

组件不需要处理

RubyLouvre avatar May 10 '19 09:05 RubyLouvre

我们需要动态生成一个页面来处理加webpack打包出来的js

RubyLouvre avatar May 10 '19 09:05 RubyLouvre

页面组件与业务组件的事件都不用再处理

RubyLouvre avatar May 10 '19 09:05 RubyLouvre

this.props.App.initAppConfig(AppConfig.config.window);

h5 AppConfig is not defined

ExcellentRay avatar Jul 09 '19 07:07 ExcellentRay