寫給跨端玩家:支撐淘寶上億日活的跨端框架—— Rax 的入門教程(附 TODO Demo)

一些廢話

沉寂了兩個月,我又回來了。javascript

跟大家猜的同樣,我已經到淘系實習了一段時間了,從上一篇文章以後就放了更多的心思在工做上。上篇文章發出去以後,我去騰訊實習了一段時間,等待阿里實習生入職流程開啓。css

收到淘系的實習生 offer 後,我買了人生中的第一張機票,第一次坐上了飛機,來到了一個陌生的城市——杭州。乾淨的街道、寬敞的瀝青馬路,吸引了我這個來自小城市的年輕人。html

博客好久沒更新了,不論是我的的網站仍是掘金,都不多有更新了,偶爾上掘金看看一些好文,總想更新一下,但又沒找到好的題材。如今實習了一段時間後,上手了淘系的開源跨端框架——Rax,新奇又好玩,也總結了一些很基礎的開發技巧,補充一下官方文檔的缺漏(官方文檔對於新手來說確實不太友好)。前端

基礎部分演示項目 Git 倉庫:Rax-TODOjava

進階技巧演示項目 Git 倉庫:Software-Engineering(同時也是個人課程設計,歡迎大夥點個 Starnode

前面的部分是針對只有一點 React 基礎的同窗的,高端玩家請點擊:進階技巧react

項目環境

  1. node >=10.3.0
  2. npm >= 6.1.0

前置知識

  1. JSX
  2. Hooks
  3. TypeScript(可選)
  4. Jest(可選)
  5. Enzyme(可選)

建立項目

初始化項目

這部分官網文檔比較詳細了,先看一下官網文檔:快速開始webpack

官網文檔提供的方案還挺多的,可能會選擇困難,咱們就從最基礎的 TODO 開始,使用 Rax 搭建一個 Web 項目:git

npm init rax todo-list
複製代碼

選擇項目類型

輸入上面的命令以後,會使用 npx 安裝 rax-cli 腳手架工具,安裝完成後會彈出這樣的界面:github

added 106 packages from 53 contributors in 10.5s
? What's your project type? (Use arrow keys)
❯ App (Build universal application)
  Component (Build universal component)
  API (Build universal API library)
  Plugin (Build plugin for miniapp)
複製代碼

使用 上下鍵 移動箭頭,後面的操做同理。

這裏咱們就直接使用默認的選項,建立一個 APP,按下回車。

選擇應用運行平臺

? Choose targets your project want to run? (Press <space> to select, <a> to togg
le all, <i> to invert selection)
❯◉ Web
 ◯ Weex
 ◯ Kraken (Flutter)
 ◯ Alibaba MiniApp
 ◯ WeChat MiniProgram
複製代碼

使用 空格鍵 選擇,按下 字母a 能夠全選,這裏咱們就先選擇 Web,後續若是有編譯爲 Weex小程序 的需求能夠在項目目錄的 build.json 中添加。

編譯爲 Flutter 應用的功能目前還不穩定,且坑比較多,建議動手能力強的玩家嘗試,小白就先繞道吧。

選擇應用類型

? What's your application type? (Only valid in target: Web/Weex/Kraken) (Use arr
ow keys)
❯ Single-page application (SPA)
  Multi-page application (MPA)
  Create lite application (The simplest project setup)
複製代碼

選擇 APP 的類型,是 SPA 仍是 MPA,這裏的選項只在 Web/Weex/Kraken(Flutter) 應用中有效。

咱們選擇默認的選項 SPA,不明白 SPAMPA 的區別的同窗,能夠移步文章:認識單頁應用(SPA)與多頁應用(MPA)

輸入做者名字

? What's author's name? (rax): 熾翎
複製代碼

選擇開發語言

? What type of language do you want to use? (Use arrow keys)
❯ JavaScript
  TypeScript
複製代碼

這裏咱們就先選擇 JavaScript,後續若是有引入 TypeScript 的需求,能夠手動引入。

開啓特性

? Do you want to enable these features? (Press <space> to select, <a> to toggle
all, <i> to invert selection)
❯◯ Server-side rendering (SSR) (Only valid in target: Web)
 ◯ Aliyun Function Compute (FaaS) (Only valid in target: Web)
 ◯ Compatibility with React
複製代碼

這裏咱們就什麼都不選。

固然,若是有須要咱們能夠選擇 Compatibility with React 配置與 React 的兼容,也能夠爲咱們的應用開啓 服務端渲染(SSR)。若是有需求開啓 Serverless,也能夠選擇開啓 功能即服務(FaaS)SSRFaaS 都只能在 Web 應用中有效。

不懂 SSRServerless 的同窗請自行百度(Google)哈。

自動安裝依賴

? Do you want to install dependences automatically after initialization? (Y/n)
複製代碼

這裏就直接回車,這個選項的意思是詢問是否在初始化完成後自動安裝依賴。默認是 Yes,就不用咱們手動進入項目文件夾執行 npm install 了。

等待依賴安裝完成

To run your app:
   cd todo-list
   npm start
複製代碼

當終端出現這一段文字的時候,就說明依賴安裝完了,咱們能夠進入到項目的文件夾下,執行 npm start 跑起咱們的項目。

啓動項目

在執行 npm start 後,終端會顯示:

Rax development server has been started:

[Web] Development server at:
    http://localhost:3333/

複製代碼

這時候咱們就能夠在瀏覽器輸入 Dev Server 的地址,查看咱們的項目了。

Home

開始開發

隨便選擇一款本身喜歡的 IDE,進入項目的文件夾,咱們會看到這樣的一個目錄結構:

.
├── README.md                   # 項目說明
├── build.json                  # 項目構建配置
├── package.json
└── src                         # 源碼目錄
    ├── app.js                  # 應用入口文件
    ├── app.json                # 應用配置,包括路由配置,小程序 window 配置等
    ├── public                  # (可選)靜態資源目錄,會拷貝內容至 build 目錄
    ├── components              # 應用的公共組件
    │   └── Logo                # 組件
    │       ├── index.css       # Logo 組件的樣式文件
    │       └── index.jsx       # Logo 組件 JSX 源碼
    ├── document                # 頁面的 HTML 模板
    │   └── index.jsx
    └── pages                   # 頁面
        └── Home                # home 頁面
            └── index.jsx
複製代碼

開發以前,咱們須要瞭解一些 Rax 下的一些小規矩:

  • rpx:默認以 750rpx 爲屏幕寬度,即 1rpx = 1/750 * 屏幕寬度
  • 樣式簡寫:在 Rax 中,因爲目前兼容性的問題,不支持 部分 樣式簡寫,例如:在寫 border 樣式時,應該將各個部分分開:border-widthborder-styleborder-color。在遇到屬性簡寫在非 Web 平臺不生效的問題時,嘗試將屬性分開或許就能解決問題。

刪掉初始頁面

咱們要作的第一件事是刪除初試化後默認的 Home 頁面中的 Logo 組件,在 src/components 文件夾下,整個刪除。

刪除以後,src/pages/Home/index.jsx 確定會報錯,咱們先稍做修改,改爲 Hello World!

// index.jsx
import { createElement } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';

import './index.css';

export default function Home() {
  return (
    <View className="home"> <Text>Hello World!</Text> </View>
  );
}
複製代碼

這個時候會發現頁面變成了 Hello World!,後面應該不用講解太多,就是使用 React 應用開發的方式,開始愉快的 coding 過程。

可能你會發現,爲何在這裏咱們只能用 Rax 提供的 ViewText 組件?

由於爲了使跨端顯示效果一致,Rax 爲開發者抹平了不一樣平臺之間樣式顯示不一致的問題。

固然,若是你只是想用 Rax 作一個 Web 應用,那你可使用 HTML 標籤(汗)。

View 組件和 Text 組件的用法能夠移步文檔:基礎組件-View基礎組件-Text

建立 Item 組件

按照 耦合度,咱們應該將組件放在 src/pages/Home 文件夾下。

在這個文件夾下,咱們建立一個這樣的目錄:

src/pages/Home
└── components                  # 組件文件夾
    └── ListItem                # ListItem 組件
        ├── index.css           # CSS
        └── index.jsx           # JSX
複製代碼

首先咱們應該構思一下這個 ListItem 應該暴露給父組件哪些接口:

  1. 每一項對應的 id
  2. 每一項的內容
  3. 每一項的完成狀態
  4. 每一項的 onClick 事件

明確了組件應該暴露的接口以後,就能夠開始寫代碼了:

// index.jsx
import { createElement } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';

import './index.css';

const ListItem = (props) => {
  // 解構 id 完成狀態 內容 onClick 事件
  const { id, done, content, onClick } = props;

  // 完成項文字樣式
  const style = {
    fontSize: '64rpx',
    lineHeight: '96rpx',
    textDecoration: done && 'line-through'
  };

  return (
    <View className="list-item" onClick={() => onClick(id)}> <View className="list-dot"></View> <Text style={style}>{content}</Text> </View>
  );
};

export default ListItem;
複製代碼
/* index.css */
.list-item {
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
  width: 100%;
  height: 100rpx;
}

.list-dot {
  width: 20rpx;
  height: 20rpx;
  margin-right: 20rpx;
  border-radius: 10rpx;
  background-color: #333;
}
複製代碼

建立 List 組件

仍是同樣,先建立文件:

src/pages/Home
└── components                  # 組件文件夾
    ├── List                    # List 組件
    │   ├── index.css           # CSS
    │   └── index.jsx           # JSX
    └── ListItem                # ListItem 組件
        ├── index.css           # CSS
        └── index.jsx           # JSX
複製代碼

建立一個 List 組件做爲 ListItem 的容器管理全部的 Item,實現 添加/刪除 的功能。

既然要作 添加 功能,那輸入框確定是必不可少的,在咱們建立的項目裏,默認是沒有輸入框組件的依賴的,因此咱們要先安裝 Rax 提供的輸入框組件:

npm install rax-textinput --save
複製代碼

組件的用法能夠轉戰文檔:基礎組件-TextInput

// index.jsx
import { createElement, useState } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import TextInput from 'rax-textinput';

import ListItem from '../ListItem';
import './index.css';

const List = () => {
  // 初始化 itemId 每次添加新列表項就 +1
  const [itemId, setItemId] = useState(0);
  // 初始化列表
  const [list, setList] = useState([]);
  // 初始化 TextInput 內容
  const [inputValue, setInputValue] = useState('');

  // 輸入框輸入事件
  const handleUserInput = (e) => {
    setInputValue(e.target.value);
  };

  // 添加按鈕點擊事件
  const handleAddButtonClick = () => {
    // 構造列表項數據結構
    const item = {
      id: itemId,
      content: inputValue,
      done: false
    };
    // immutable 思想 生成新的引用
    const newList = [...list, item];
    setList(newList);
    // 清空輸入框
    setInputValue('');
    // itemId ++
    setItemId(itemId + 1);
  };

  // 列表項點擊事件
  const handleItemClick = (id) => {
    // 遍歷列表 當事件未完成時標記爲已完成 當事件已完成時刪除
    const newList = list.filter((item) => {
      if (item.id === id) {
        if (item.done) {
          return false;
        } else {
          item.done = true;
        }
      }
      return true;
    });
    setList(newList);
  };

  return (
    <View className="list"> <View className="list-input-wrapper"> <TextInput className="list-input" value={inputValue} onInput={handleUserInput} /> <View className="list-add-button" onClick={handleAddButtonClick}> <Text>添加</Text> </View> </View> <View className="list-item-wrapper"> {list.map((item) => ( <ListItem key={item.id} id={item.id} content={item.content} done={item.done} onClick={handleItemClick} /> ))} </View> </View>
  );
};

export default List;
複製代碼
.list {
  align-items: center;
}

.list-input-wrapper {
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
}

.list-add-button {
  justify-content: center;
  align-items: center;
  width: 150rpx;
  height: 100rpx;
  margin-left: 20rpx;
  border-radius: 10px;
  background-color: #dcdcdc;
}

.list-input {
  width: 100%;
  height: 100rpx;
  line-height: 96rpx;
  font-size: 64rpx;
  border-width: 1px;
  border-color: #dcdcdc;
}

.list-item-wrapper {
  width: 100%;
}
複製代碼

至此,組件的基本功能就完成了。

引入首頁

如今咱們要將組件引入首頁 Home 中:

import { createElement } from 'rax';
import View from 'rax-view';

import List from './components/List';
import './index.css';

export default function Home() {
  return (
    <View className="home"> <List /> </View>
  );
}
複製代碼

如今就能看到一個能完成基本功能的 TODO 應用了。

進階技巧

我猜,有些高玩看到我前面寫的部分,確定會說:

啊~ 你這個這麼簡單,不是有手就行嘛~

有一說一,確實有手就行(誤)。

前面的部分只是寫給不愛讀官方文檔或者只有一點 React 基礎的同窗的,從這個部分開始,會開始引入一些高級的內容。

修改路由配置

在前面建立的項目中,咱們會看到項目的文件夾下有一個 app.json 文件,打開它你會看到:

{
  "routes": [
    {
      "path": "/",
      "source": "pages/Home/index"
    }
  ],
  "window": {
    "title": "Rax App"
  }
}
複製代碼

這個文件就是用來控制項目路由,若是後續的開發增長了更多的頁面,就須要在 routes 下添加對象。path 就是你新加入的頁面的訪問路徑(URL),source 就是你新添加的頁面所在的文件目錄。

修改成多頁應用(MPA)

在項目文件夾下還有一個 build.json 文件,裏面的內容是這樣的:

{
  "plugins": [
    [
      "build-plugin-rax-app",
      {
        "targets": ["web"]
      }
    ]
  ]
}
複製代碼

由於咱們以前建立項目的時候,選擇的是單頁應用(SPA),咱們想改爲多頁應用怎麼辦? 很簡單:

先安裝多頁應用依賴:

npm install build-plugin-rax-multi-pages --save-dev
複製代碼

而後在 targets 所在的對象下,增長一個鍵 "type",值爲 "mpa",像這樣:

{
  "plugins": [
    [
      "build-plugin-rax-app",
      {
        "targets": ["web"],
        "type": "mpa"
      }
    ]
  ]
}
複製代碼

重啓 Dev Server 後會發現頁面變成了這樣:

MPA

添加跨端支持

咱們在最開始建立項目的時候,並無選擇多平臺,可是 Rax 做爲一個跨端開發框架,不添加跨端支持怎麼行,這裏會給一個教程教你們添加跨端支持(固然一開始就選擇好更方便)。

仍是 build.json 文件,有一個 targets 數組,想要添加跨端支持,就在數組中添加對應的內容便可:

{
  "plugins": [
    [
      "build-plugin-rax-app",
      {
        "targets": ["web", "weex", "miniapp", "wechat-miniprogram"],
        "type": "mpa"
      }
    ]
  ]
}
複製代碼

weex 對應 Weex 平臺,miniappwechat-miniprogram 對應 阿里小程序微信小程序

查看 Weex 效果

咱們在 build.json 中使用了上面的配置後,頁面會變成這個樣子:

Weex

點開 Weex Preview 後,你會發現是一堆 JavaScript 代碼,那怎麼預覽?

有一個稍微麻煩一點的方法,就是先肯定你本機在內網的 IP 地址,將 URL 中的 localhost 改爲內網 IP,而後將 URL 轉換爲二維碼(Chrome 有不少插件能夠實現)。

在手機上下載 Weex Playground 而後掃描二維碼,就能在手機上預覽到 APP 的效果。

Weex Playground 下載地址

TypeScript 支持

在前面的項目裏,咱們沒有引入 TypeScript,那若是想引入應該怎麼辦?

很簡單,在官網的文檔中也有講解:項目開發- TypeScript 支持

只須要先安裝 rax-typestypescript

npm install rax-types typescript --save-dev
複製代碼

在項目文件夾下建立 tsconfig.json 文件,使用如下配置:

{
  "compilerOptions": {
    "module": "esNext",
    "target": "es2015",
    "outDir": "build",
    "jsx": "preserve",
    "jsxFactory": "createElement",
    "moduleResolution": "node",
    "sourceMap": true,
    "alwaysStrict": true,
    "baseUrl": ".",
    "paths": {
      "rax": ["node_modules/rax-types"]
    }
  },
  "include": ["./src/**/*"]
}
複製代碼

便可添加 TypeScript 支持。

引入自動化測試

這是一個比較大的坑,以致於讓我摸索了一個上午才解決。

Rax 官方文檔中並無自動化測試相關配置的教程,以致於我只能直接在釘釘找負責 Rax 維護的師兄求助,歷經千辛萬苦終於將測試用例跑通。

首先,咱們須要安裝幾個依賴,數量有點多,建議分開安裝:

npm install jest --save-dev
npm install @types/jest --save-dev
npm install @babel/preset-env --save-dev
npm install babel-jest --save-dev
npm install rax-test-renderer --save-dev
npm install @babel/plugin-proposal-class-properties --save-dev
npm install @babel/plugin-proposal-decorators --save-dev
npm install @babel/plugin-proposal-export-default-from --save-dev
npm install @babel/preset-flow --save-dev
npm install @babel/preset-react --save-dev
npm install babel-plugin-transform-jsx-stylesheet --save-dev
複製代碼

依賴安裝好以後,在項目的目錄下建立 .babelrc.jsBabel 進行配置:

module.exports = function (api) {
  // Cache the returned value forever and don't call this function again.
  if (api) api.cache(true);

  return {
    presets: [
      '@babel/preset-flow',
      [
        '@babel/preset-env',
        {
          loose: true
        }
      ],
      [
        '@babel/preset-react',
        {
          pragma: 'createElement'
        }
      ]
    ],
    plugins: [
      '@babel/plugin-proposal-export-default-from',
      ['@babel/plugin-proposal-class-properties', { loose: false }],
      'babel-plugin-transform-jsx-stylesheet',
      ['@babel/plugin-proposal-decorators', { legacy: true }]
    ],
    ignore: ['build', 'coverage', 'node_modules']
  };
};
複製代碼

配置好以後,Jest 仍是沒法跑通的,要在 package.json 中添加啓動腳本:

{
  "scripts": {
    "build": "build-scripts build",
    "start": "build-scripts start",
    "lint": "eslint --ext .js --ext .jsx ./",
    // 添加 jest 啓動腳本
    "test": "jest"
  }
}
複製代碼

同時,還要對 Jest 進行配置:

{
  "jest": {
    "collectCoverage": true,
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
    }
  }
}
複製代碼

配置添加完成以後,在項目的目錄下建立一個文件夾:

.
└── __mocks__
    ├── fileMock.js          # 靜態資源處理
    └── styleMock.js         # 樣式處理
複製代碼

分別在兩個文件中加入添加代碼:

// fileMock.js
module.exports = 'test-file-stub';
複製代碼
// styleMock.js
module.exports = {};
複製代碼

配置這個部分的目的是讓 Jest 處理樣式和靜態資源時到 mock 的文件夾下找。由於在測試的時候,測試腳本並不會真的去訪問引入的靜態資源,遇到須要引用靜態資源的地方就引導到 __mocks__ 文件夾下查找,就不會由於找不到資源而報錯。配置來源於 Jest 官網處理靜態文件

一切配置完成後,就能夠在一個須要作測試的組件下建立 __test__ 文件夾並將測試腳本放在文件夾下了,或者你也能夠直接使用 xxx.test.jsx 爲文件命名,這些文件都會被 Jest 識別爲測試腳本。

除了 Jest,Rax 團隊還爲 Rax 提供了 Enzyme 適配器,示例項目:raxjs/enzyme-adapter-rax,提供了 Enzyme 的使用示例。

總結

得益於淘系強大的技術實力,Rax 的相關生態相對比較完善。同時,性能也很是強大,這裏我就不過多介紹了。

Rax 在小程序上的性能甚至優於市面上其餘小程序框架:Rax ——完美融合編譯時與運行時的雙引擎小程序框架

其餘的一些介紹 Rax 的文章能夠移步官網的博客

文章的最後,推薦一下本身的公衆號:Hello FE,是我和跟我一塊兒實習的小夥伴們一塊兒運營的,會按期分享一些乾貨。

同時,關注公衆號還有前端書籍大全一份贈送,歡迎你們關注,也歡迎你們進羣交流:

公衆號

相關文章
相關標籤/搜索