返回介绍

Rax 0.x 开发工具

发布于 2019-12-27 01:10:52 字数 8056 浏览 1715 评论 0 收藏 0

工欲善其事必先利其器。在Rax开发相关的整条链路上,厂内的小伙伴们已经积累了大量的实践经验与相应工具产出。使用这些工具相信能在一定程度上提升你的开发效率。当前作为一个前端多面手知道如何使用工具还远远不够,了解其背后的工作原理也是十分重要的。

本文将从工具开发原理入手介绍如何从0开始搭建rax业务及其组件的开发环境,顺便安利几款开发工具供大家使用。

官方工具库

  • babel-preset-rax

组合了包括babel-preset-stage-0 ~ es2017 等常用的babel preset 及 plugin。

  • rax-webpack-plugin

顾名思义转为rax进行webpack打包的插件,打出能够被客户端识别的jsbundle全靠它。

  • stylesheet-loader

使用stylesheet-loader让css in js 也变得好用起来。

  • rax-hot-loader & rax-hot-module-replacement-webpack-plugin

weex一样可以支持代码热更新,开发效率double不是问题。

  • weex-devtool

weex调试工具

对于对webpack比较了解的前端开发者来说,利用以上工具库可以自由组合打造自己的weex开发环境。下面让我们尝试组合这些工具搭建一个小型的业务开发环境

业务开发

打包一个weex bundle

难度等级:✨

假设我们的项目目录是酱式儿的:

├── build
├── package.json
├── src/index.jsx

页面只有一个入口index.jsx,要构建一个三端可用的页面需要做如下操作:

  • 增加webpack.config.js 文件,并安装webpack、babel-core、babel-loader等相应依赖到devDependencies。
  • 配置babel-loader、rax-webpack-plugin

见配置文件:

const RaxPlugin = require('rax-webpack-plugin');
const webpack  = require('webpack');
const babelConfig = {
    presets:[require.resolve('babel-preset-rax')]
}
const config = {
    entry:{
        index:'./src/index'
    },
    output:{
        filename:'[name].js',
        publicPath:'build'
    },
    module:{
        rules:[{
            test:/\.js|\.jsx?,
            include:[new RegExp(/node_modules\/.*@.*/)]
            use:`${require.resolve('babel-loader')}?${JSON.stringify(babelConfig)}`
        }]
    },
    plugins:[
        new RaxWebpackPlugin({
          target: 'bundle'
        })
    ]
}
moduex.exports = config;

然后就可以准备src/index.jsx的源代码了:

/** @jsx createElement */
import { createElement, Component, render } from 'rax';
import { View, Text } from 'weex-nuke';

class App extends Component {
  constructor() {
    super();
  }
  render() {
    return (
      <View>
        <Text>Hello World</Text>
      </View>
    );
  }
};
render(<App />);

PS:没有用过preact等类react框架的同学肯定会对/** @jsx createElement / 和无缘无故的import createElement有些奇怪。这是因为JSX扩展语法是从 React 独立出来的语法,Babel/Bublé这些编译器默认会把调用React.createElement()来编译 JSX. babel-preset-rax增加了对/@jsx createElement**/的默认设置,使用babel-preset-rax而非自定义组合babel preset和plugin进行编译的开发者可以不写这行特别的注释。参考这行代码:_ babel-preset-rax

加些语法糖

难度等级:✨

增加scss或者less的支持,增加await、async的语法支持。

const babelConfig = {
    presets:[require.resolve('babel-preset-rax')],
    plugins:[
        require.resolve('babel-plugin-transform-runtime'),
        require.resolve('babel-plugin-transform-regenerator')
    ]
};
module:{
    rules:[{
        test:/\.less?,
        use:[require.resolve('stylesheet-loader'),require.resolve('less-loader'),]
    },{
        test:/\.js|\.jsx?,
        include:[new RegExp(/node_modules\/.*@.*/)]
        use:`${require.resolve('babel-loader')}?${JSON.stringify(babelConfig)}`
    }]
 },

PS:weex rax 包括react-native这些技术栈都不存在css的概念。为了让开发者可以使用css的方式写此类技术栈的样式, css in js 技术应用而生。从技术原理上一版都是讲css 语法编译成js object,最终同jsbundle打包在一起。

开发与生产环境隔离

难度等级:✨✨✨

最直接的做法是抽取webpack中的公用配置,在开发与生产环境通过不同的构建命令与环境变量区分。在上述完成的基本配置的基础上,我们通过 process.env.NODE_ENV 来进行不同环境的 webpack 配置。最终可以在 npm scripts中维护以下两个脚本:

"scripts": {
    "dev": "webpack-dev-server --config webpack.config.js NODE_ENV=development",
    "dist": "webpack --config webpack.config.js NODE_ENV=production"
  },

PS:process.env.NODE_ENV 的设置意义不光是区分开发者自己的开发环境,对于一些三方打包相关loader或插件来说在开发和生产环境也有些表现上的差别。eg:stylesheet-loader这款loader会在开发环境输出weex不支持的样式警告,而在生产环境不会有任何输出。

webpack.config.js

if(process.env.NODE_ENV === 'development'){
    // 为开发环境增加单独配置
}
if(process.env.NODE_ENV === 'production'){
    // 为生产环境增加单独配置
}

代码压缩

if(process.env.NODE_ENV === 'prodution'){
    config.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
          mangle: { keep_fnames: true }, 
          minimize: false,
          compress: { warnings: false, drop_console: false, keep_fnames: true },
          output: { comments: false },
        })
    )
}

代码热更新与热替换

const RaxHotModuleReplacementPlugin = require('rax-hot-module-replacement-webpack-plugin');
if(process.env.NODE_ENV === 'developement'){
    config.entry.forEach((point)=>{
         webpackConfigDev.module.loaders.forEach((loader) => {
          if (loader.test.toString() === /\.js|\.jsx?$/.toString()) {
             const appIndexjs = `${path.resolve(cwd, `src/${config.entry[point][0]}`)}.jsx`;
             loader.loaders.push(`${require.resolve('nuke-hot-dev-utils/module-hot-accept')}?appIndex=${appIndexjs}`);
         }
     });
    config.entry[point].unshift(require.resolve('rax-hot-loader/patch'));
    config.entry[point].unshift(require.resolve('nuke-hot-dev-utils/webpackHotDevClient'));
    })
    config.plugins.push(new RaxHotModuleReplacementPlugin())
}

同时,文件组织的方式也需要做稍许变更,如下图:

![image.png | center | 300x106](https://gw.alipayobjects.com/zos/skylark/b7a84d9f-617e-4c02-9767-574d47b4b2df/2018/png/6e34b458-5ea4-4c54-8877-22a5d7f67763.png]
热更新的原理本文不再赘述了,感兴趣的同学可以移步这里 https://www.atatech.org/articles/81931。

增加单测

推荐使用 jest 并结合rax-test-renderer 对项目进行单元测试。测试demo如下所示:

import renderer from 'rax-test-renderer';
import { Component, createElement } from 'rax';
import Button from '../src/index';

describe('Button', () => {
  it('should render a button', () => {
    const component = renderer.create(<Button />);
    const tree = component.toJSON();
    expect(tree.tagName).toEqual('DIV');
  }
});

在安装完成jest后,在package.json中增加npm srcipt :

script:{
    "test": "jest"
}

调试

weex 官方文档中已经给出了比较清晰的介绍。不清楚流程的同学可以参考最新版本的 weex-toolkit 2.0。

降级

构建出的jsbundle在降级情况下需要html模板来承载。这时候我们需要在jsbundle前引用 web-rax-framework 的cdn资源,补充web下的polyfill,从而实现大部分浏览器下的兼容。与之对应的在客户端内还有 weex-rax-framework ,为我们提供了包括window在内的各种rax全局API。阿里系集成aliweex的客户端均已支持。在依赖以上两个framework之后,此时在构建过程中可以选择把rax external掉,以减少代码体积。类似下面这样的代码:

组件开发

组件开发需要依赖于项目开发的环境进行调试,先在项目开发环境的基础上进行一些目录改造:

├── demo/basic.jsx         // 组件demo 
├── package.json 
├── lib                    // 存放babel后的代码  
├── src/index.jsx          // 组件源码

demo/basic.jsx的主要作用是在组件调试阶段引用组件并render到页面上,demo的构建过程与业务开发完全相同,只需将webpack entry字段的页面入口调整为demo/basic.jsx即可。

与业务开发不同的是,组件开发一般只需将源码进行babel转换而无需将进行打包。因此在发布前需要使用babel-preset-rax 对src目录的js文件进行一次babel,并将所有的文件拷贝到lib目录。

PS:关于是否将组件源码还是babel后的代码暴露给其他开发者,在社区依旧存在一些争论。目前比较普遍的做法是在packcage.json中声明两个字段,以根据开发者自己的需求进行构建配置。redux等类库都采用了这种方式:

{ 
    main:"lib/index",
    module:"src/index"
}

以上就搭建完成了一个最最基础的组件开发环境。当然,对于体系化的组件开发来说,还需要考虑到组件API文档、demo、单元测试、自动化测试等一系列功能。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文